0%

js 中 this 的工作原理

在 javascript 中,this 关键字是一个特殊的对象标识符,它指向当前执行上下文的对象。与其他编程语言不同,javascript 的 this 不是在编写时绑定,而是在运行时根据函数的调用方式动态绑定的。理解 this 的指向是掌握 javascript 的关键,也是许多开发者容易混淆的地方。

全局上下文中的 this

在全局执行环境(浏览器中是 window,Node.js 中是 global)中,this 指向全局对象。

1
2
3
4
5
// 浏览器环境
console.log(this === window); // true

var name = 'globalName';
console.log(this.name); // 'globalName'

函数调用中的 this

非严格模式

在普通函数调用中,this 指向全局对象。

1
2
3
4
function showThis() {
console.log(this);
}
showThis(); // 浏览器中输出 window

严格模式

如果函数处于严格模式(‘use strict’),this 会保持为 undefined,因为严格模式下禁止默认指向全局。

1
2
3
4
5
'use strict';
function showThisStrict() {
console.log(this);
}
showThisStrict(); // undefined

方法调用中的 this

当函数作为对象的方法被调用时,this 指向调用该方法的对象。

1
2
3
4
5
6
7
8
9
10
11
const person = {
name: 'Alice',
greet: function() {
console.log('Hello, ' + this.name);
}
};
person.greet(); // Hello, Alice

// 如果将方法赋值给变量再调用,this 会丢失
const greetFn = person.greet;
greetFn(); // Hello, undefined (非严格模式下 this 指向 window)

构造函数中的 this

使用 new 关键字调用函数时,会创建一个新对象,并将该对象绑定到函数的 this 上。构造函数通常首字母大写。

1
2
3
4
5
function Person(name) {
this.name = name;
}
const alice = new Person('Alice');
console.log(alice.name); // Alice

如果构造函数显式返回一个对象,则 this 会被替换;如果返回基本类型,则忽略,依然返回 this 指向的新对象。

箭头函数中的 this

箭头函数没有自己的 this,它捕获其所在(定义时)上下文的 this 值。箭头函数的 this 在定义时就确定了,之后不会改变。

1
2
3
4
5
6
7
8
9
10
11
12
const obj = {
name: 'obj',
normalFunc: function() {
console.log(this.name);
},
arrowFunc: () => {
console.log(this.name);
}
};

obj.normalFunc(); // 'obj'
obj.arrowFunc(); // undefined(箭头函数的 this 继承自全局/外层,此处外层是全局,所以 this 指向 window)

箭头函数的 this 不能被 call、apply、bind 改变。

事件处理中的 this

在 DOM 事件处理函数中,this 通常指向绑定事件的元素。

1
2
3
4
5
6
7
<button id="btn">Click me</button>
<script>
const btn = document.getElementById('btn');
btn.addEventListener('click', function() {
console.log(this); // <button id="btn">Click me</button>
});
</script>

如果使用箭头函数,this 会继承外层上下文(比如 window),可能不是期望的元素。

改变 this 指向:call、apply、bind

我们可以使用 call、apply 或 bind 显式指定函数调用时的 this。

  1. call(thisArg, arg1, arg2, …):立即调用函数,并指定 this。
  2. apply(thisArg, [argsArray]):与 call 类似,但参数以数组形式传递。
  3. bind(thisArg, arg1, arg2, …):返回一个新函数,其 this 永久绑定到指定对象,不会立即执行。
1
2
3
4
5
6
7
8
9
10
11
function introduce(age, city) {
console.log(`I'm ${this.name}, ${age} years old, from ${city}.`);
}

const user = { name: 'Bob' };

introduce.call(user, 25, 'New York'); // I'm Bob, 25 years old, from New York.
introduce.apply(user, [30, 'London']); // I'm Bob, 30 years old, from London.

const boundIntroduce = introduce.bind(user, 28);
boundIntroduce('Paris'); // I'm Bob, 28 years old, from Paris.

常见陷阱与注意事项

  1. 回调函数中的 this 丢失:将对象方法作为回调传递时,常常丢失 this。
1
2
3
4
5
6
7
8
9
10
const counter = {
count: 0,
increment: function() {
console.log(this.count);
}
};
setTimeout(counter.increment, 1000); // 输出 undefined(this 指向全局)
// 解决方法:使用箭头函数、bind 或包装函数
setTimeout(() => counter.increment(), 1000); // 0
setTimeout(counter.increment.bind(counter), 1000); // 0
  1. 嵌套函数中的 this:在方法内部定义普通函数时,该函数的 this 会指向全局(非严格模式)或 undefined(严格模式)。
1
2
3
4
5
6
7
8
9
10
11
12
const obj = {
name: 'obj',
outer: function() {
console.log('outer this:', this.name); // 'obj'
function inner() {
console.log('inner this:', this.name); // undefined(非严格模式 window.name)
}
inner();
}
};
obj.outer();
// 解决:使用箭头函数、保存 this 变量(const self = this)、bind

总结

  1. this 的指向取决于函数的调用方式,而不是定义位置。
  2. 全局函数调用 → 指向全局对象(严格模式下 undefined)。
  3. 对象方法调用 → 指向调用该方法的对象。
  4. 构造函数调用(new)→ 指向新创建的实例。
  5. 箭头函数 → 继承定义时外层上下文的 this。
  6. 事件处理 → 指向绑定事件的元素。
  7. 可以使用 call、apply、bind 显式绑定 this。