函数内部属性 — this & arguments
函数调用时,JS引擎会在函数体内为本地作用域添加两个特殊变量:this、arguments。
this 对象
[ 执行环境和作用域?!] 函数体内,this 是指:函数的执行环境对象( 也叫 context,上下文对象 )。
一般来说,this 指向调用函数的对象,如果没有就是全局对象,在浏览器中就是 window 对象。
var sum = function(){
// 严格模式下是 undefined
// 非严格模式下是 Window
console.log(this);
}
var obj = {
sum:function(){
// this 为 obj 对象
console.log(this);
}
}
obj.sum();
[ this 绑定机制?!] 实际上,函数调用不同模式之间的本质区在于:函数本地作用域上的 this 变量的指向上。
- 函数调用模式:this 指向全局对象
- 方法调用模式:this 指向调用者
- 构造函数调用模式:this 指向被构造的对象
- 间接调用模式:this 指向第一个参数
arguments 对象
JS 中的参数在内部用一个数组表示。函数接收到的始终都是这个数组,而不关心数组中包含哪些参数。
在函数体内,可以通过arguments 对象来访问这个参数数组,从而获取传递给函数的每一个参数(实参)。
简单讲,函数体内,arguments 表示实参列表对象,是一个类数组对象,可通过下标访问对象的实参值。
// arguments对象并不是Array的实例,它是一个类数组对象,可以使用方括号语法访问它的每一个元素。
function sum(a,b){
return arguments[0] + arguments[1];
}
console.log(sum(1,2)); // 3
[ callee 属性 ] arguments 对象的 callee 属性,是一个指针,指向拥有这个 arguments 对象的函数。
下面是经典的阶乘函数
function factorial(num){
if(num <=1){
return 1;
}else{
return num* factorial(num-1);
}
}
console.log(factorial(5)); // 120
但是,上面这个函数的执行与函数名紧紧耦合在了一起,可以使用arguments.callee可以消除函数解耦
function factorial(num){
if(num <=1){
return 1;
}else{
return num* arguments.callee(num-1);
}
}
console.log(factorial(5)); // 120
但在严格模式下,访问这个属性会抛出TypeError错误
function factorial(num){
'use strict';
if(num <=1){
return 1;
}else{
return num* arguments.callee(num-1);
}
}
//TypeError: 'caller', 'callee', and 'arguments' properties may not be accessed on strict mode functions or the arguments objects for calls to them
console.log(factorial(5));
这时,可以使用具名的函数表达式
var factorial = function fn(num){
if(num <=1){
return 1;
}else{
return num*fn(num-1);
}
};
console.log(factorial(5));//120
[ caller属性 ] // 有点像 super,实际上有两个 caller 属性
- 函数的caller:保存着调用当前函数的函数的引用,如果是在全局作用域中调用当前函数,它的值是null
function outer(){ inner(); } function inner(){ console.log(inner.caller);//outer(){inner();} } outer(); function inner(){ console.log(inner.caller);//null } inner(); 在严格模式下,访问这个属性会抛出TypeError错误 function inner(){ 'use strict'; //TypeError: 'caller' and 'arguments' are restricted function properties and cannot be accessed in this context console.log(inner.caller); } inner();
- arguments 对象的 caller
该属性始终是 undefined,定义这个属性是为了分清 arguments.caller 和函数的 caller 属性;同样地,在严格模式下,访问这个属性会抛出 TypeError 错误。
// 拓展理解 — 函数重载:JS 函数不能像传统意义上那样实现重载。
在其他语言中,可以为一个函数编写两个定义,只要这两个定义的签名( 接受的参数的类型和数量 )不同即可。
JS函数没有签名,因为其参数是由包含0或多个值的数组来表示的,而没有函数签名,真正的重载是不可能做到的。
// 后面的声明覆盖了前面的声明 function addSomeNumber(num){ return num + 100; } function addSomeNumber(num){ return num + 200; } var result = addSomeNumber(100);//300只能通过检查传入函数中参数的类型和数量并作出不同的反应,来模仿方法的重载。
function doAdd(){ if(arguments.length == 1){ alert(arguments[0] + 10); }else if(arguments.length == 2){ alert(arguments[0] + arguments[1]); } } doAdd(10);//20 doAdd(30,20);//50