函数内部
ES5中,函数内部存在两个特殊的对象:arguments和this。ES6新增了new.target属性。
arguments
arguments对象其实还有一个callee属性,是一个指向arguments对象所在函数的指针。
function fb(num){ if(num<=1){ return 1; }else{ return num * fb(num-1); } } /*这个函数要正确执行就必须保证函数名是 factorial,从而导致 了紧密耦合。使用 arguments.callee 就可以让函数逻辑与函数名解耦*/ function factorial(num) { if (num <= 1) { return 1; } else { return num * arguments.callee(num - 1); } }
this
在标准函数中,this引用的是把函数当成方法调用的上下文对象,这时候通常称其为this值(在网页的全局上下文中调用函数时,this指向window)。
window.color = 'red'; let o = { color:'pink'; }; function sayColor(){ console.log(this.color); } sayColor();//red o.sayColor = sayColor; o.sayColor();//pink
在箭头函数中,this引用的是定义箭头函数的上下文。
window.color = 'red'; let o = { color: 'blue' }; let sayColor = () => console.log(this.color); sayColor(); // 'red' o.sayColor = sayColor; o.sayColor(); // 'red'
caller
caller属性引用的是调用当前函数的函数,或者如果是在全局作用域中调用的则为null。
function outer() { inner(); } function inner() { console.log(inner.caller); } outer(); //以上代码会显示 outer()函数的源代码
new.target
ES6新增了检测函数是否使用new关键字调用的new.target属性。如果函数是正常调用的,则new.target的值是undefined;如果使用new关键字调用的,则new.target将引用被调用的构造函数。
function King() { if (!new.target) { throw 'King must be instantiated using "new"' } console.log('King instantiated using "new"'); } new King(); // King instantiated using "new" King(); // Error: King must be instantiated using "new"
函数属性和方法
每个函数都有两个属性:length和prototype。length属性保存函数定义的命名参数的个数。
function sayName(name) { console.log(name); } function sum(num1, num2) { return num1 + num2; } function sayHi() { console.log("hi"); } console.log(sayName.length); // 1 console.log(sum.length); // 2 console.log(sayHi.length); // 0
prototype是保存引用类型所有实例方法的地方,这意味着toString()、valueOf()等方法实际上都保存在prototype上,进而由所有实例共享。
函数还有两个方法:apply()和call().这两个方法都会以指定的this值来调用函数,即会设置调用函数时函数体内this对象的值。
apply()方法接收两个参数:函数内this的值和一个参数组。第二个参数可以是Array的实例,但也可以是arguments对象。
function sum(num1, num2) { return num1 + num2; } function callSum1(num1, num2) { return sum.apply(this, arguments); // 传入 arguments 对象 } function callSum2(num1, num2) { return sum.apply(this, [num1, num2]); // 传入数组 } console.log(callSum1(10, 10)); // 20 console.log(callSum2(10, 10)); // 20
call()方法与 apply()的作用一样,只是传参的形式不同。第一个参数跟 apply()一样,也是 this 值,而剩下的要传给被调用函数的参数则是逐个传递的。换句话说,通过 call()向函数传参时,必须将参数一个一个地列出来function sum(num1, num2) { return num1 + num2; } function callSum(num1, num2) { return sum.call(this, num1, num2); } console.log(callSum(10, 10)); // 20
递归
递归函数通常的形式是一个函数通过名称调用自己
if (num <= 1) { return 1; } else { return num * factorial(num - 1); } }
尾调用优化
ES6尾调用优化的关键:如果函数的逻辑允许基于尾调用将其销毁,则引擎就会那么做。
什么是尾调用:即外部函数的返回值是一个内部函数的返回值。
function outerFunction() { return innerFunction(); // 尾调用 }
尾调用优化的条件:
- 代码在严格模式下执行
- 外部函数的返回值是对尾调用函数的调用
- 尾调用函数返回后不需要执行额外的逻辑
- 尾调用函数不是引用外部函数作用域中自由变量的闭包
闭包
闭包指的是那些引用了另一个函数作用域中变量的函数,通常是在嵌套函数中实现的。
function createComparisonFunction(propertyName) { return function(object1, object2) { let value1 = object1[propertyName]; let value2 = object2[propertyName]; if (value1 < value2) { return -1; } else if (value1 > value2) { return 1; } else { return 0; } }; } let compare = createComparisonFunction('name'); let result = compare({ name: 'Nicholas' }, { name: 'Matt' });
this对象
在比保重使用this会让代码变复杂。如果内部函数没有使用箭头函数定义,则this对象会在运行时绑定到执行函数的上下文。如果在全局函数中调用,则this在非严格模式下等于window,在严格模式下等于undefined。如果作为某个对象的方法调用,则this等于这个对象。
立即调用的函数表达式
立即调用的匿名函数又被称为立即调用的函数表达式。
(function()){ })()