this的指向取决于函数调用方式,其规则按优先级分为:箭头函数继承外层作用域this;new绑定指向新实例;显式绑定(call/apply/bind)指定this值;隐式绑定指向调用对象;默认绑定指向全局或undefined。

JavaScript中的
this关键字,说白了,它就是一个函数在执行时,指向的那个“上下文对象”。它的值不是固定不变的,而是完全取决于函数被调用的方式。理解这一点,是掌握
this的关键。
解决方案
要深入理解
this,我们需要看清它在不同调用模式下的表现。这就像是
this有几张不同的面孔,每次函数被调用,它就根据当前的面具来决定自己是谁。
最常见的几种绑定规则包括:
-
默认绑定 (Default Binding): 当函数作为独立函数被调用,没有明确的上下文对象时,
this
会指向全局对象(在浏览器中是window
,在Node.js中是global
)。但在严格模式下,this
会是undefined
,这通常是为了防止意外地修改全局对象。立即学习“Java免费学习笔记(深入)”;
function showThis() { console.log(this); } showThis(); // 在浏览器中输出 Window 对象,在严格模式下输出 undefined -
隐式绑定 (Implicit Binding): 当函数作为对象的方法被调用时,
this
会指向调用它的那个对象。这是我们最常遇到的情况。const person = { name: 'Alice', greet: function() { console.log(`Hello, my name is ${this.name}`); } }; person.greet(); // 输出 "Hello, my name is Alice",因为 greet 是通过 person 对象调用的这里有个常见的陷阱:如果把
person.greet
赋值给一个变量,再通过变量调用,就会失去隐式绑定,退化为默认绑定。const sayHello = person.greet; sayHello(); // 在非严格模式下输出 "Hello, my name is undefined" (或指向全局对象的 name),因为此时 greet 失去了 person 的上下文
-
显式绑定 (Explicit Binding): 我们可以强制改变
this
的指向,使用call()
,apply()
,bind()
这三个方法。call()
和apply()
会立即执行函数,并接受第一个参数作为this
的值。call()
接受参数列表,apply()
接受参数数组。bind()
则会创建一个新函数,这个新函数的this
永远被绑定到bind()
的第一个参数上,它不会立即执行。
function introduce(age, city) { console.log(`I'm ${this.name}, ${age} years old, from ${city}.`); } const user = { name: 'Bob' }; introduce.call(user, 30, 'New York'); // I'm Bob, 30 years old, from New York. introduce.apply(user, [25, 'London']); // I'm Bob, 25 years old, from London. const boundIntroduce = introduce.bind(user, 40); boundIntroduce('Paris'); // I'm Bob, 40 years old, from Paris. (注意这里 bind 也可以预设部分参数) -
new
绑定 (New Binding): 当函数作为构造函数,使用new
关键字调用时,this
会指向新创建的实例对象。function Car(make, model) { this.make = make; this.model = model; } const myCar = new Car('Honda', 'Civic'); console.log(myCar.make); // Honda console.log(myCar.model); // Civic在这个场景下,
this
指向的是new
操作符创建的那个空对象,然后构造函数会往这个空对象上添加属性。 -
箭头函数 (Arrow Functions): 这是ES6引入的,它彻底改变了
this
的绑定方式。箭头函数没有自己的this
,它会捕获其所在词法作用域(即定义时所处的外部作用域)的this
值。一旦确定,this
就不会再改变。const person = { name: 'Charlie', sayLater: function() { setTimeout(function() { // 这里的 this 默认指向全局对象 (Window),因为 setTimeout 的回调函数是独立调用的 console.log(`Regular function: ${this.name}`); // undefined 或全局对象的 name }, 100); }, sayLaterArrow: function() { setTimeout(() => { // 箭头函数捕获了 sayLaterArrow 定义时的 this,也就是 person 对象 console.log(`Arrow function: ${this.name}`); // Charlie }, 100); } }; person.sayLater(); person.sayLaterArrow();箭头函数在处理回调函数时尤其方便,因为它避免了
this
上下文丢失的问题,省去了我们手动bind
或_this = this
的麻烦。
为什么JavaScript中的this总是让人困惑不解?
我记得刚接触JavaScript时,
this简直就是个谜团。它不像Java或C++那样,
this总是指向当前实例。JavaScript的
this更像一个变色龙,它的颜色(指向)完全取决于它被“放在”哪个环境里(如何被调用)。这种动态性是其困惑的根源。
首先,多重绑定规则是主要原因。我们有默认绑定、隐式绑定、显式绑定、
new绑定,以及后来出现的箭头函数带来的词法绑定。这些规则之间存在优先级,比如显式绑定通常高于隐式绑定,
new绑定又高于显式绑定。而箭头函数则完全不走寻常路,它直接跳过了所有这些规则,去父级作用域找
this。这种复杂的优先级和不同寻常的绑定机制,让开发者很难形成一个统一的心智模型。
其次,回调函数的上下文丢失是另一个常见的痛点。在异步操作(如
setTimeout、事件监听器)或数组方法(如
forEach、
map)中,如果直接传入一个包含
this的普通函数作为回调,
this往往会退化为默认绑定,指向全局对象(或
undefined),而不是我们期望的那个对象。这导致很多初学者不得不使用
const self = this或者
.bind(this)来“固定”
this,这本身就说明了其复杂性。
再者,严格模式的影响也增加了理解难度。在非严格模式下,默认绑定会指向全局对象,这有时会掩盖问题。但在严格模式下,默认绑定会是
undefined,这会让错误暴露得更早,但也可能让不熟悉规则的开发者感到更困惑。
对我来说,真正理解
this,是从放弃“
this是一个固定指针”的观念开始的。它更像是一个“运行时上下文引用”,每次函数执行前,JavaScript引擎都会计算出它应该指向谁。
如何在不同场景下正确判断this的指向?
判断
this的指向,其实可以遵循一个相对清晰的“决策树”或者说“优先级列表”。当你看到一个函数调用时,可以这样一步步地问自己:
酷纬企业网站管理系统Kuwebs是酷纬信息开发的为企业网站提供解决方案而开发的营销型网站系统。在线留言模块、常见问题模块、友情链接模块。前台采用DIV+CSS,遵循SEO标准。 1.支持中文、英文两种版本,后台可以在不同的环境下编辑中英文。 3.程序和界面分离,提供通用的PHP标准语法字段供前台调用,可以为不同的页面设置不同的风格。 5.支持google地图生成、自定义标题、自定义关键词、自定义描
-
是不是箭头函数?
- 如果是,那么
this
就不是由调用方式决定的。它会向上寻找定义它的那个最近的非箭头函数作用域(或者全局作用域),并继承那个作用域的this
。一旦确定,永不改变。 - 如果不是,继续下一步。
- 如果是,那么
-
是不是通过
new
关键字调用的?- 如果是(例如
new MyConstructor()
),那么this
会指向新创建的那个对象实例。 - 如果不是,继续下一步。
- 如果是(例如
-
是不是通过
call()
、apply()
或bind()
显式调用的?- 如果是(例如
myFunction.call(someObject, ...)
),那么this
会指向传入的第一个参数(someObject
)。如果传入null
或undefined
,则会退化为默认绑定(指向全局对象或undefined
)。 - 如果不是,继续下一步。
- 如果是(例如
-
是不是作为对象的方法调用的?
- 如果是(例如
myObject.myMethod()
),那么this
会指向调用这个方法的那个对象(myObject
)。注意,这里只看.
前面的那个对象。 - 如果不是,继续下一步。
- 如果是(例如
-
默认绑定:
- 如果以上都不是,那么就是默认绑定。在非严格模式下,
this
指向全局对象(浏览器中的window
,Node.js中的global
)。在严格模式下,this
是undefined
。
- 如果以上都不是,那么就是默认绑定。在非严格模式下,
这个流程图能帮助我们覆盖绝大多数情况。举个例子:
const obj = {
name: 'Tester',
method: function() {
console.log(this.name);
},
arrowMethod: () => {
console.log(this.name); // 这里的 this 捕获的是 obj 定义时的全局 this,所以是 undefined 或 Window.name
}
};
obj.method(); // 步骤4:隐式绑定,this 指向 obj -> 'Tester'
const func = obj.method;
func(); // 步骤5:默认绑定,this 指向全局对象 -> undefined 或 Window.name
const anotherObj = { name: 'Another' };
obj.method.call(anotherObj); // 步骤3:显式绑定,this 指向 anotherObj -> 'Another'
obj.arrowMethod(); // 步骤1:箭头函数,this 捕获定义时的全局 this -> undefined 或 Window.name通过这种逐层判断,你可以更系统地分析
this的指向。
箭头函数如何改变了this的工作方式?
箭头函数是ES6带来的一项革命性特性,它在
this的处理上采取了一种全新的策略:词法绑定。这意味着箭头函数没有自己的
this,它的
this值完全取决于它被定义时所处的外部(词法)作用域。一旦定义,
this的指向就固定了,不会因为函数后续的调用方式而改变。
这与传统的
function关键字定义的函数形成了鲜明对比。普通函数在执行时,
this是动态绑定的,它的值由函数被调用的方式决定。而箭头函数则像一个“旁观者”,它不参与
this的绑定过程,只是“借用”了父级作用域的
this。
核心改变:
-
解决了回调函数中的
this
丢失问题: 这是箭头函数最显著的优势之一。在ES5及之前,我们经常需要在异步回调(如setTimeout
)、事件处理器或数组迭代方法中,手动保存this
的引用(例如var self = this;
),或者使用bind()
方法来确保this
指向正确。箭头函数通过词法绑定,自然而然地解决了这个问题。class Greeter { constructor(name) { this.name = name; } // 传统函数,this 在 setTimeout 回调中会丢失 greetOld() { setTimeout(function() { console.log(`Hello from old way, ${this.name}`); // this 指向 Window/undefined }, 100); } // 箭头函数,this 捕获了 greetNew 方法定义时的 this (即 Greeter 实例) greetNew() { setTimeout(() => { console.log(`Hello from new way, ${this.name}`); // this 指向 Greeter 实例 }, 100); } // 也可以直接将类方法定义为箭头函数,这样它的 this 总是指向实例 greetMethod = () => { console.log(`Hello from arrow method, ${this.name}`); } } const myGreeter = new Greeter('World'); myGreeter.greetOld(); // 输出 "Hello from old way, undefined" myGreeter.greetNew(); // 输出 "Hello from new way, World" myGreeter.greetMethod(); // 输出 "Hello from arrow method, World" setTimeout(myGreeter.greetMethod, 200); // 即使作为回调,this 也保持不变 更简洁的代码: 避免了冗余的
bind
调用或self = this
的赋值,代码更加简洁易读。不能作为构造函数: 箭头函数不能使用
new
关键字调用,因为它没有自己的this
,也就无法为新对象绑定this
。没有
arguments
对象: 箭头函数也没有自己的arguments
对象,它会从父级作用域继承arguments
。-
不适用于定义对象方法: 如果你希望对象方法中的
this
指向该对象本身,那么使用普通函数定义方法是更合适的。如果使用箭头函数,this
会指向对象定义时所处的外部作用域(通常是全局对象),这可能不是你想要的。const myObject = { value: 42, getValue: function() { return this.value; // this 指向 myObject }, getArrowValue: () => { return this.value; // this 指向全局对象(Window/undefined),而非 myObject } }; console.log(myObject.getValue()); // 42 console.log(myObject.getArrowValue()); // undefined (或 Window.value)
总的来说,箭头函数提供了一种更可预测、更稳定的
this行为,极大地简化了JavaScript中处理上下文绑定的复杂性,尤其是在涉及回调和高阶函数时。但理解它的工作原理,以及何时应该使用它,何时不应该,仍然是关键。









