本文详解JavaScript中this关键字的工作原理,分析对象方法调用、全局调用及嵌套函数中的this指向差异。重点解析.call、.apply和.bind三种方法如何改变this绑定,并探讨作用域链中this丢失的常见陷阱及通过变量引用或.bind修复的解决方案。
this的基本工作原理
理解JavaScript中this关键字的工作机制是掌握该语言的关键。
当函数作为对象的方法被调用时,this会自动指向该对象实例。
示例代码如下:
var parent = {
method: function () {
console.log(this);
}
};
parent.method();
// 输出结果为 parent 对象
需要注意的是,这种绑定行为相对“脆弱”。如果仅获取方法的引用并在其他地方调用,this将不再指向原对象,而是指向全局对象(在浏览器环境中为window)。这一特性常令初学者感到困惑。
相关代码示例如下:
ThisClownCar();
// 输出结果为 Window 对象
使用call、apply和bind修改this
通过特定方法修改this指向
.call、.apply和.bind这三个方法允许开发者显式控制函数调用时的this值,同时还能指定传递给函数的参数。
Function.prototype.call接受任意数量的参数,其中第一个参数被指定为this的值,后续参数则传递给被调用的函数。
代码示例如下:
Array.prototype.slice.call([1, 2, 3], 1, 2)
// 返回结果为 [2]
Function.prototype.apply的行为与.call类似,区别在于它接收的参数是一个数组,而非独立的参数列表。
String.prototype.split.apply('13.12.02', ['.'])
// 返回结果为 ['13', '12', '02']
Function.prototype.bind创建一个新函数,该函数永久绑定到指定的this值,并支持部分参数应用(partial application),从而生成原函数的柯里化(curried)版本。
代码示例如下:
var arr = [1, 2];
var add = Array.prototype.push.bind(arr, 3);
// 此时add()等效于执行 arr.push(3)
add();
// 此时add(4)等效于执行 arr.push(3, 4)
add(4);
console.log(arr);
// 最终输出结果为 [1, 2, 3, 3, 4]作用域链中的this陷阱与解决方案
作用域链中的this指向问题
在以下示例中,this在作用域链中无法保持预期的一致性。这是JavaScript语言规则的一个特性,常导致初级开发者产生误解。
代码示例如下:
function scoping () {
console.log(this);
return function () {
console.log(this);
};
}
scoping()();
// 外层函数输出 Window
// 内层函数输出 Window
一种常见的解决方案是创建一个局部变量来保存对this的引用,并确保在子作用域中没有同名变量覆盖该引用。若子作用域中存在同名变量,它将覆盖父作用域中对this的引用。
相关代码示例如下:
function retaining () {
var self = this;
return function () {
console.log(self);
};
}
retaining()();
// 输出结果为 Window
除非确实需要同时访问父作用域的this和当前作用域的this,否则我倾向于使用.bind方法。该方法可将父作用域的this绑定到子函数中。
代码示例如下:
function bound () {
return function () {
console.log(this);
}.bind(this);
}
bound()();
// 输出结果为 Window