在 JavaScript 中,this 是一个重要的上下文关键词,它的指向取决于函数的调用方式。this 的行为可能会让人困惑,特别是当它在不同的场景下表现不同的时候。本文将围绕五种常见的 this 绑定方式进行深入分析,帮助你理解和掌握 this 的工作机制。
1. 箭头函数:this 是静态绑定的
箭头函数的一个独特特点是 this 绑定静态,即它不会自己拥有 this。相反,箭头函数的 this 会 继承外层函数的 this。这意味着在箭头函数中,this 是静态的,它的指向是由定义箭头函数时所在的上下文决定的,而不是由调用它的方式决定的。即在编译阶段的语义分析阶段已经断定其上下文的this指向
例子:
function Outer() {
this.name = 'Outer';
// 箭头函数
this.sayName = () => {
console.log(this.name);
return this.name;
};
}
const obj = new Outer();
obj.sayName(); // 输出: Outer
const obj2 = { name: 'obj2' };
obj2.sayName = obj.sayName;
obj2.sayName(); // 输出: Outer
在上面的代码中,Outer 是一个构造函数,this 被绑定到新创建的 obj 实例对象。箭头函数 sayName 中的 this 会继承 Outer 函数的 this,因此它指向了 Outer 中的 this,即 obj。
重要概念:
- 箭头函数没有自己的
this。 - 它总是使用外部函数上下文的
this。 - 无法通过
call、apply或bind改变this,因为它是静态绑定的。
例子(无法修改 this):
const obj = { name: 'Object' };
const arrowFunction = () => {
console.log(this.name);
};
arrowFunction.call(obj); // 输出: undefined (全局对象的 `name`)
在这个例子中,虽然我们使用 call 显式指定 this,但是由于 arrowFunction 是一个箭头函数,它的 this 会继承外部作用域的 this。因此,它指向了全局对象,无法通过 call 修改 this。
2. 构造函数(new):this 指向新创建的实例对象
构造函数是 JavaScript 中用来创建对象的特殊函数。当一个函数通过 new 操作符调用时,this 会被绑定到新创建的对象。new 操作符的作用包括:
- 创建一个新的空对象。
- 将新对象的
__proto__指向构造函数的prototype。 - 绑定
this到新对象。 - 如果构造函数没有显式返回一个对象,
this会被自动返回。
例子:
function Person() {
this.getName = function() { return this.name || 'person'; };
}
const person = new Person('Alice');
console.log(person.getName()); // 输出: person
const NewPerson = Person.bind({ name: 'newPerson' })
const newPerson = new NewPerson();
console.log(person.getName()); // 输出: person
在这个例子中,new Person('Alice') 创建了一个新对象,并将 this 绑定到新对象,因此 this.name = 'Alice' 会在新对象上创建 name 属性。即使使用bind绑定别的this,还是会返回新对象的this
总结:new 操作符的 this 总是指向新创建的实例对象。
3. 对象方法调用(隐式绑定):this 指向调用该方法的对象
当一个函数作为对象的方法被调用时,this 会指向调用该方法的对象。这种行为被称为 隐式绑定。在隐式绑定中,this 会根据方法的调用对象来绑定。
例子:
const person = {
name: 'Bob',
greet: function() {
console.log(`Hello, ${this.name}`);
return `Hello, ${this.name}`;
}
};
person.greet(); // 输出: Hello, Bob
const newPerson = { name: 'Alice' };
newPerson.greet = person.greet;
newPerson.greet(); // 输出: Hello, Alice
newPerson.greet.call(person); // 输出: Hello, Bob
在这个例子中,greet 方法是 person 对象的一个方法。当我们调用 person.greet() 时,this 会自动指向 person 对象。优先级低于显式绑定
注意:当对象的方法调用时,this 指向该对象。但如果你改变方法的调用方式(例如通过 call 或 apply),this 会改变。
4. 显式绑定(call, apply, bind):显式指定 this
JavaScript 提供了 call、apply 和 bind 方法来显式地指定 this。这些方法允许你手动绑定 this 到任何你想要的对象,从而控制函数内部的 this 指向。
call:立即调用函数并传递this和参数。apply:类似于call,但是参数是作为数组传递。bind:返回一个新函数,永久绑定指定的this,并且不会立即执行。
call 示例:
function greet() {
console.log(`Hello, ${this.name}`);
}
const person = { name: 'Charlie' };
greet.call(person); // 输出: Hello, Charlie
bind 示例:
const person = { name: 'David' };
function greet() {
console.log(`Hello, ${this.name}`);
}
const boundGreet = greet.bind(person);
boundGreet(); // 输出: Hello, David
总结:
call和apply会立即执行函数并改变this。bind不会立即执行函数,它返回一个新的函数,this永远绑定到指定对象。
5. 隐式全局绑定:this 默认指向全局对象
如果在函数调用时没有显式指定 this,并且函数没有在任何对象上调用,this 会指向全局对象(在浏览器中是 window,在 Node.js 中是 global)。这种情况通常发生在函数作为普通函数调用时,而不是作为对象方法调用。
例子(浏览器环境):
function showThis() {
console.log(this);
}
showThis(); // 输出: window (在浏览器中,`this` 指向 window 对象)
如果你在非严格模式下调用一个普通函数,this 默认指向全局对象。注意:在严格模式下,this 将是 undefined,而不是全局对象。
'use strict';
function showThis() {
console.log(this);
}
showThis(); // 输出: undefined (严格模式下,`this` 是 undefined)
总结:this 绑定的五种方式
- 箭头函数:
this是静态绑定的,它会继承外层作用域的this,并且无法通过call、apply或bind改变。 - 构造函数(
new) :this指向新创建的实例对象。 - 对象方法调用(隐式绑定) :
this指向调用该方法的对象。 - 显式绑定(
call,apply,bind) :使用call、apply或bind显式指定this,强制this指向给定的对象。 - 隐式全局绑定(
window) :如果没有显式绑定,this默认指向全局对象(在浏览器中是window,在 Node.js 中是global)。