JavaScript 中的 this 关键字
在JavaScript编程中,this
关键字无疑是最令人困惑的概念之一。与PHP、Java等面向对象语言不同,JavaScript中的this
具有更灵活也更复杂的绑定规则。本文将全面解析JavaScript中this
的各种绑定场景,通过全新示例代码和详细注释帮助你彻底掌握这一重要概念。
一、全局上下文中的this
在全局执行环境中(任何函数体外),this
指向全局对象。在浏览器中,全局对象就是window
。
// 全局变量
var globalVar = '我是全局变量';console.log(this.globalVar); // 输出: "我是全局变量"
console.log(window.globalVar); // 输出: "我是全局变量"
// 在浏览器环境中,this === window 结果为true
中文备注:在全局作用域中,this默认指向window对象,全局变量会自动成为window对象的属性。
二、函数调用中的this
1. 普通函数调用
当函数作为普通函数调用时,其中的this
在非严格模式下指向全局对象,在严格模式下为undefined
。
function showThis() {console.log(this); // 在浏览器中输出Window对象
}showThis(); // 普通函数调用// 严格模式下的表现
function strictShowThis() {'use strict';console.log(this); // 输出: undefined
}strictShowThis();
中文备注:普通函数调用时,this的指向取决于是否使用严格模式。
2. 对象方法中的this
当函数作为对象的方法调用时,this
指向调用该方法的对象。
const car = {brand: 'Toyota',showBrand: function() {console.log(`这辆车的品牌是: ${this.brand}`);}
};car.showBrand(); // 输出: "这辆车的品牌是: Toyota"// 方法赋值给变量后调用
const showFn = car.showBrand;
showFn(); // 输出: "这辆车的品牌是: undefined" (this指向改变)
中文备注:方法中的this指向调用它的对象,但将方法赋值给变量后调用会改变this的指向。
三、构造函数中的this
当函数作为构造函数使用(通过new
调用)时,this
指向新创建的对象实例。
function Person(name, age) {this.name = name;this.age = age;this.introduce = function() {console.log(`大家好,我是${this.name},今年${this.age}岁。`);};
}const person1 = new Person('张三', 25);
person1.introduce(); // 输出: "大家好,我是张三,今年25岁。"const person2 = new Person('李四', 30);
person2.introduce(); // 输出: "大家好,我是李四,今年30岁。"
中文备注:构造函数中的this指向新创建的实例对象,通过new调用时会自动创建新对象并将this绑定到该对象。
四、显式绑定:call、apply和bind
我们可以使用call
、apply
和bind
方法显式地设置this
的值。
1. call和apply方法
function greet(greeting, punctuation) {console.log(`${greeting}, ${this.name}${punctuation}`);
}const user = { name: '王五' };// 使用call
greet.call(user, '你好', '!'); // 输出: "你好, 王五!"// 使用apply
greet.apply(user, ['嗨', '~']); // 输出: "嗨, 王五~"
中文备注:call和apply都能立即调用函数并指定this值,区别在于参数传递方式不同。
2. bind方法
const employee = {name: '赵六',position: '工程师'
};function showInfo(department) {console.log(`${this.name}是${department}的${this.position}`);
}// 使用bind创建新函数
const boundShowInfo = showInfo.bind(employee);
boundShowInfo('技术部'); // 输出: "赵六是技术部的工程师"
中文备注:bind方法会创建一个新函数,其this值永久绑定到指定的对象。
五、箭头函数中的this
箭头函数没有自己的this
,它会捕获所在上下文的this
值。
const team = {name: '开发团队',members: ['钱七', '孙八', '周九'],showMembers: function() {this.members.forEach(member => {// 箭头函数中的this继承自showMembersconsole.log(`${member}属于${this.name}`);});},showMembersError: function() {this.members.forEach(function(member) {// 普通函数中的this指向全局对象或undefinedconsole.log(`${member}属于${this.name}`); // 输出错误});}
};team.showMembers(); // 正确输出每个成员所属团队
team.showMembersError(); // 输出错误,this.name为undefined
中文备注:箭头函数中的this由定义时的上下文决定,不会因调用方式改变。
六、this绑定优先级
JavaScript中this
绑定的优先级从高到低如下:
- new绑定:
new Foo()
- 显式绑定:
call
、apply
、bind
- 隐式绑定:
obj.foo()
- 默认绑定:
foo()
graph TDA[this绑定规则] --> B[new绑定]A --> C[显式绑定]A --> D[隐式绑定]A --> E[默认绑定]B -->|优先级最高| F[new Constructor()]C --> G[call/apply/bind]D --> H[obj.method()]E -->|优先级最低| I[直接调用function()]
七、常见问题与解决方案
1. 回调函数中的this丢失
const timer = {message: '时间到!',start: function() {// 错误做法:this指向改变setTimeout(function() {console.log(this.message); // undefined}, 1000);// 解决方案1:使用箭头函数setTimeout(() => {console.log(this.message); // "时间到!"}, 1000);// 解决方案2:使用bindsetTimeout(function() {console.log(this.message); // "时间到!"}.bind(this), 1000);// 解决方案3:保存this引用const self = this;setTimeout(function() {console.log(self.message); // "时间到!"}, 1000);}
};timer.start();
2. 类方法中的this问题
class Calculator {constructor() {this.value = 0;}add(num) {this.value += num;return this;}// 错误示例:在回调中丢失thisdoubleAfterDelay() {setTimeout(function() {this.value *= 2; // 错误:this指向错误console.log(this.value);}, 1000);}// 正确示例:使用箭头函数correctDoubleAfterDelay() {setTimeout(() => {this.value *= 2;console.log(this.value);}, 1000);}
}const calc = new Calculator().add(5);
calc.doubleAfterDelay(); // 输出NaN或报错
calc.correctDoubleAfterDelay(); // 正确输出10
八、总结
JavaScript中的this
绑定规则可以归纳为以下几点:
- 默认情况下,
this
指向全局对象(浏览器中是window
),严格模式下为undefined
- 当函数作为对象方法调用时,
this
指向该对象 - 使用
new
调用构造函数时,this
指向新创建的对象实例 - 使用
call
、apply
或bind
可以显式设置this
的值 - 箭头函数不绑定自己的
this
,而是继承外层函数的this
值
理解这些规则后,你将能够准确预测代码中this
的行为,避免常见的陷阱。
英语学习查找表
单词(短语) | 音标 | 词性 | 词根/词缀 | 释义 | 搭配 | 例子 |
---|---|---|---|---|---|---|
this | /ðɪs/ | 代词 | 古英语þis | 这个,这 | this keyword, this value | this refers to the object |
context | /ˈkɒntekst/ | 名词 | con- + text | 上下文,环境 | execution context | The function’s context determines this |
binding | /ˈbaɪndɪŋ/ | 名词 | bind + -ing | 绑定 | this binding | Arrow functions have lexical binding |
constructor | /kənˈstrʌktər/ | 名词 | construct + -or | 构造函数 | constructor function | The new keyword calls the constructor |
arrow function | /ˈærəʊ ˈfʌŋkʃən/ | 名词短语 | - | 箭头函数 | define arrow function | Arrow functions don’t have their own this |
explicit | /ɪkˈsplɪsɪt/ | 形容词 | ex- + plicit | 明确的,显式的 | explicit binding | call and apply provide explicit this binding |
implicit | /ɪmˈplɪsɪt/ | 形容词 | im- + plicit | 隐含的,隐式的 | implicit binding | Method invocation has implicit this binding |
scope | /skəʊp/ | 名词 | 希腊语skopos | 作用域 | lexical scope | this is scoped differently in arrow functions |
invoke | /ɪnˈvəʊk/ | 动词 | in- + vocare | 调用 | invoke a function | How you invoke a function affects this |
callback | /ˈkɔːlbæk/ | 名词 | call + back | 回调函数 | pass a callback | this is often lost in callbacks |