本文详细解析 JavaScript 中 this 关键字的五种绑定规则,包括全局作用域、函数调用、方法调用、构造函数调用及显式绑定。通过具体代码示例说明 this 在不同上下文中的指向差异,并探讨 call 和 apply 方法对 this 的重定向作用,帮助开发者准确掌握 this 的工作机制。
全局作用域下的 this
全局作用域下
在全局作用域中使用 this,它指向全局对象。接下来详细介绍全局对象的概念:
全局对象(Global object)是在进入任何执行上下文之前就已经创建好的对象。该对象在整个程序中只存在一份,其属性可以在程序的任何地方被访问,全局对象的生命周期随着程序的退出而终止。在全局对象初始创建阶段,会将 Math、String、Date、parseInt 等作为其自身属性。此外,全局对象还可以包含其他额外创建的对象作为属性(这些对象可以指向全局对象自身)。例如,在 DOM 环境中,全局对象的 window 属性就引用了全局对象本身。因此,在控制台(console)中输入 window 和 this.window 的结果是相同的。
函数与方法调用中的 this
调用一个函数时
执行 foo(); 时,这里的 this 同样指向全局对象。
调用一个方法时
执行 test.foo();
在上述例子中,this 将会指向 test 对象。
构造函数中的 this
调用一个构造函数时
执行 new foo();
当一个函数被调用且使用了关键字 new 时,该函数被称为构造函数。此时在函数内部,this 指向新创建的对象。
显式绑定 this 的方法
显式设置时
function foo(a, b, c) {}//
var bar = {};
foo.apply(bar, [1, 2, 3]); // array will expand to the below
foo.call(bar, 1, 2, 3); // results in a = 1, b = 2, c = 3
当使用 Function.prototype 的 apply 和 call 方法时,this 的值被显式设置为该方法的第一个参数。因此,与直接调用函数的规则不同,上述示例中 this 指向了 bar。
这里介绍下 call 和 apply 方法:
call 方法:
语法:call([thisObj[,arg1[, arg2[, [,.argN]]]]]) 定义:调用一个对象的一个方法,以另一个对象替换当前对象。
apply 方法:
语法:apply([thisObj[,argArray]]) 定义:应用某一对象的一个方法,用另一个对象替换当前对象。 在这里我们要注意一点,在对象的字面声明时,this 不能用来指向对象本身。如下:
var obj = {me: this}
这里,this 不会指向 obj,this 的应用只限于以上五种情形。
this 绑定的总结与常见问题
总结
尽管上述情形在大多时候是有意义的,但是第二种情形(即调用一个函数时)的 this 实际上是很少有用途的,这被认为是 Javascript 设计上的另一个错误。
Foo.method = function() {
function test() {
// this is set to the global object
}
test();
}
根据我们上面所述,这里的 this 将会指向全局对象,而不是 Foo 函数。 为了在 test 中获得指向 Foo 的途径,我们需要在 method 内部创建一个局部变量指向 Foo。
Foo.method = function() {
var that = this;
function test() {
// Use that instead of this here
}
test();
}
that 只是普通的变量名,但是它经常被用来指向外部的 this。 还有一个比较有意思的地方与函数别名相关,即将一个方法赋值给一个变量时。
var test = someObject.methodTest;
test();
上例中,test 将会被当做一个普通函数看待,所以根据第二种情形(即调用一个函数时),其内部的 this 将会指向全局变量,而不是 someObject。 尽管,this 晚绑定初看上去是个不好的决定,但实际上这是原型式继承工作的基础。
function Foo() {}
Foo.prototype.method = function() {};
function Bar() {}
Bar.prototype = Foo.prototype;
new Bar().method();
此时,当 method 被调用时,它将指向 Bar 的实例对象。