在JavaScript中,函数内的this关键字的行为非常依赖于函数是如何被调用的。在user, user2, 和 user3的例子中,sayHi函数被定义了三种不同的方式,这导致this的行为也存在差异。
user对象
这里使用了一个名为sayHi的普通函数表达式:
let user = {
name: "Tom",
sayHi: function () {
console.log('Hi, I am ' + this.name);
}
}
在这个例子中,sayHi是一个普通的函数属性。当它作为方法(即通过对象引用调用时,例如user.sayHi())被调用时,this指的是调用它的对象,即user。
user2对象
这里使用了一个ES6中的方法简写:
let user2 = {
name: "Jack",
sayHi() {
console.log('Hi, I am ' + this.name);
}
}
user2例子中的sayHi定义和user的很相似,唯一的区别是它使用了ES6的简化语法来创建方法。这并不改变this的行为:作为user2对象的方法被调用时,this同样指向user2。
user3对象
这里使用了箭头函数:
let user3 = {
name: "Ben",
sayHi: () => {
console.log('Hi, I am ' + this.name);
}
}
user3.sayHi使用了一个箭头函数,箭头函数不绑定this,相反,它们捕获定义时上下文的this值。在全局代码中定义的箭头函数中的this指向全局对象,在浏览器中通常是window。因此,无论你何时何地调用user3.sayHi,它打印出来的this.name总会引用定义时捕获的this的name属性,而不是user3的name属性。如果user3.sayHi在全局范围内定义,如在浏览器中使用<script>
直接定义,则this.name很可能是undefined。
关于setTimeout
当你直接传递方法给setTimeout,如setTimeout(user.sayHi, 1000),你传递的是函数本身,而不是函数的调用。结果是,当setTimeout执行这个函数时,它没有了对象上下文,因此this不再引用原始的对象,除非像user和user2示例中一样使用.bind()方法明确地绑定this。
总结下三者间的区别:
user和user2展示了在对象内部定义方法的两种方式,都可以使用sayHi作为方法调用,其中this指向调用的对象。
user3的箭头函数导致this在定义时就固定下来,对箭头函数使用bind是无效的,因为箭头函数不绑定this。
使用.bind()可以修正setTimeout调用中this的指向,确保当方法在将来某个时间点执行时,this指向正确的对象。