调用位置
如果要从函数对象内部引用它自身,那只使用 this 是不够的。一般来说你需要通过一个指向函数对象的词法标识符(变量)来引用它。
具名函数,在它内部可以使用
函数标识符
来引用自身。或使用
this
替代标识符
来引用函数对象(一般是没有名称标识符的匿名函数)。在 匿名函数对象 内部引用自身中 也可以使用 使用
arguments.callee
来引用当前正在运行的 函数对象 。
function foo(num) {
console.log( "foo: " + num );
// 记录 foo 被调用的次数
// 注意,在当前的调用方式下(参见下方代码),this 确实指向 foo
this.count++;
}
foo.count = 0;
var i;
for (i=0; i<10; i++) {
if (i > 5) {
// 使用 call(..) 可以确保 this 指向函数对象 foo 本身
foo.call( foo, i );
}
}
// foo: 6
// foo: 7
// foo: 8
// foo: 9
需要明确的是,this 在任何情况下都不指向函数的词法作用域。在 JavaScript 内部,作用域确实和对象类似,可见的标识符都是它的属性。但是作用域“对象”无法通过 JavaScript代码访问,它存在于 JavaScript 引擎内部。
this 指向函数的作用域 , 在某种情况下它是正确的,但是在其他情况下它却是错误的。
this
是在运行时进行绑定的,并不是在编写时绑定,它的上下文取决于函数调用时的各种条件。this
的绑定和函数声明的位置没有任何关系,只取决于函数的调用方式。
当一个函数被调用时,会创建一个活动记录(有时候也称为执行上下文)。这个记录会包含函数在哪里被调用(调用栈)、函数的调用方法、传入的参数等信息。this 就是记录的其中一个属性,会在函数执行的过程中用到。
this 实际上是在函数被调用时发生的绑定,它指向什么完全取决于函数在哪里被调用。
调用位置就是函数在代码中被调用的位置(而不是声明的位置)。
this
的绑定取决于调用栈中真正的调用位置的。
绑定规则
- 默认绑定
最常用的函数调用类型:独立函数调用。
声明在全局作用域中的变量(比如 var a = 2)就是全局对象的一个同名属性。
function foo() {
console.log( this.a );
}
var a = 2;
foo(); // 2
可以看到当调用 foo() 时,this.a 被解析成了全局变量 a。函数调用时应用了 this 的默认绑定,因此 this 指向全局对象。 在代码中,foo()
是直接使用不带任何修饰的函数引用进行调用的,因此只能使用默认绑定
,无法应用其他规则。
如果使用严格模式(strict mode
),那么全局对象将无法使用默认绑定,因此 this
会绑定
到 undefined
:
function foo() {
"use strict";
console.log( this.a );
}
var a = 2;
foo(); // TypeError: this is undefined
虽然 this
的绑定规则完全取决于调用位置,但是只有 foo()
运行在非 strict mode
下时,默认绑定才能绑定到全局对象;严格模式下与 foo()
的调用位置无关:
function foo() {
console.log( this.a );
}
var a = 2;
(function(){
"use strict";
foo(); // 2
})();
通常来说你不应该在代码中混合使用 strict mode 和 non-strict mode。整个程序要么严格要么非严格。然而,有时候你可能会用到第三方库,其严格程度和你的代码有所不同,因此一定要注意这类兼容性细节。
- 隐式绑定
调用位置是否有上下文对象,或者说是否被某个对象拥有或者包含,不过这种说法可能会造成一些误导。