1.arguments & callee
arguments 对象,它是一个类数组对象,包含调用函数时传入的所有参数。这个对象只有以function关键字定义函数(相比于使用箭头函数)时才会有。这里说一下arguments对象的callee属性,是一个指向arguments对象所在函数的指针。
示例:一个经典的阶乘函数
1 function factorial(n){ 2 if(n <=1 ){ 3 return 1 4 }else{ 5 return n * factorial(n-1) 6 } 7 } 8 9 factorial(5) // 120
这样写的问题是:紧耦合。如果这个函数的名称改变了,函数内部的代码也需要改变。使用arguments.callee可以让函数逻辑与函数名解耦:
1 function factorial(n){ 2 if(n <=1 ){ 3 return 1 4 }else{ 5 return n * arguments.callee(n-1) 6 } 7 } 8 9 factorial(5) // 120
2.this
函数中的另一个特殊对象是this,它在标准函数和箭头函数中有不同的行为。
(1)标准函数中
在标准函数中,this 引用的是把函数当成方法调用的上下文对象,这时候通常称其为 this 值(在网页的全局上下文中调用函数时,this 指向 windows)。
1 window.n =1 2 3 function fn(){ 4 console.log(this.n); 5 } 6 7 fn() // 1
定义在全局上下文中的函数 sayColor()引用了 this 对象。这个 this 到底引用哪个对象必须到函数被调用时才能确定。因此这个值在代码执行的过程中可能会变。
(2)箭头函数中
在箭头函数中,this引用的是定义箭头函数的上下文。
1 window.n =1 2 let o = { 3 n : 9 4 } 5 6 let fn = ()=> console.log(this.n); 7 fn() // 1 8 o.fn = fn 9 fn() // 1
在事件回调或定时回调中调用某个函数时,this指向的并非想要的对象。此时将回调函数写成箭头函数就可以解决问题。因为箭头函数中的this会保留定义该函数时的山下文:
1 window.uname = "王五" 2 3 function People(){ 4 this.uname = '张三' 5 setTimeout(() => console.log(this.uname) ,0) 6 } 7 8 function Obj(){ 9 this.uname = "李四" 10 setTimeout(function(){console.log(this.uname)} ,0) 11 } 12 13 new People() // 张三 14 new Obj() // 王五
注意:
- 函数名只是保存指针的变量。因此全局定义的 sayColor()函数和 o.sayColor()是同一个函数,只不过执行的上下文不同。
3.caller
ES5在函数对象上也添加了一个属性:caller。这个属性引用的是调用当前函数的函数,或者
1 function outer(){ 2 inner() 3 console.log(123); 4 } 5 6 function inner(){ 7 console.log(inner.caller); 8 } 9 10 outer() 11 /** 12 ƒ outer(){ 13 inner() 14 console.log(123); 15 } 16 */
以上会显示outer()函数的源代码。这是因为outer()调用了inner()inner.caller指向outer().。如果要降低耦合度,则可以通过arguments.callee.caller来引用同样的值:
1 function outer(){ 2 inner() 3 console.log(123); 4 } 5 6 function inner(){ 7 console.log(arguments.callee.caller); 8 } 9 10 outer() 11 /** 12 ƒ outer(){ 13 inner() 14 console.log(123); 15 } 16 */
注意:严格模式下禁用了arguments.callee
4.new.target
JS中的函数始终可以作为构造函数实例化一个新对象,也可以作为普通函数被调用。ECMAScript 6 新增了检测函数是否使用 new 关键字调用的 new.target 属性。如果函数是正常调用的,则 new.target 的值是 undefined;如果是使用 new 关键字调用的,则 new.target 将引用被调用的构造函数。
1 function King() { 2 if (!new.target) { 3 throw 'King must be instantiated using "new"' 4 } 5 console.log('King instantiated using "new"'); 6 } 7 new King(); // King instantiated using "new" 8 King(); // Error: King must be instantiated using "new"