在 JavaScript 中,this 是一个至关重要且有些复杂的概念。它与函数的运行环境紧密相关,当我们对内存(栈内存和堆内存)有透彻的理解时,就能更好地把握 this 的行为机制。
首先,让我们回顾一些相关的概念,如调用栈、执行上下文、作用域以及作用域链(outer)。在这些概念体系中,其实还缺少一个指向函数调用对象的指针,而这个指针就是 this。this 的值并非固定不变,它在函数执行的瞬间由函数的调用方式所决定
调用栈、执行上下文和作用域
JavaScript 代码的执行环境可以分为多个层次,包括全局执行上下文、函数执行上下文以及 eval 执行上下文等。每个执行上下文都有其对应的变量对象,而 this 就是在这个环境中指向某个特定对象的指针。
全局执行上下文
在全局执行上下文中,this 指向全局对象,在浏览器中就是 window 对象。
console.log(this === window); // true
函数执行上下文
当函数被执行时,会创建一个新的函数执行上下文,其中 this 的值由函数的调用方式决定。
对象的方法调用
当函数作为对象的方法被调用时,this 指向该对象。
var obj = {
x: 1,
foo: function() {
console.log(this);
console.log(this.x);
}
};
obj.foo(); // 输出: {x: 1, foo: ƒ}, 1
普通函数调用
当函数作为普通函数被调用时,this 默认指向全局对象(非严格模式下),而在严格模式下则为 undefined。
"use strict";
var x = 2;
var obj = {
x: 1,
foo: function() {
console.log(this);
console.log(this.x);
}
};
// 提取方法到独立变量
var foo = obj.foo;
// 直接调用
foo(); // 输出: undefined, TypeError: Cannot read property 'x' of undefined
构造函数调用
当使用 new 关键字调用构造函数时,this 指向新创建的对象。
function Person(name) {
this.name = name;
}
var person = new Person("Alice");
console.log(person.name); // 输出: Alice
显式指定 this
还可以使用 call、apply 或 bind 方法显式地指定 this 的值。
var name = "刀郎";
var a = {
name: "天外来物",
func1: function() {
console.log(this.name);
},
func2: function() {
setTimeout(function() {
// 使用 call 显式绑定 this 到对象 a
this.func1.call(a);
}, 1000);
}
};
a.func2();
在这个例子中,setTimeout 内部的匿名函数默认情况下 this 指向全局对象。通过使用 .call(a) 我们可以将 this 绑定到对象 a 上,从而正确输出 "天外来物"。
总结
- 对象的方法调用:
this指向该对象。 - 普通函数调用:非严格模式下
this指向全局对象;严格模式下this为undefined。 - 构造函数调用:
this指向新创建的对象实例。 - 显式指定
this:可以通过call、apply或bind方法显式地指定this的值。