[Medium] 📄 this Binding
1. What is this in JavaScript?
JavaScript 中的
this是什么?
this 是 JavaScript 中的一个关键字,它指向函数执行时的上下文对象。this 的值取决于函数如何被调用,而不是在哪里定义。
this 的绑定规则
JavaScript 中 this 的绑定有四种规则(按优先级从高到低):
- new 绑定:使用
new关键字调用构造函数 - 显式绑定:使用
call、apply、bind明确指定this - 隐式绑定:通过对象方法调用
- 默认绑定:其他情况下的默认行为
2. Please explain the difference of this in different contexts
请解释
this在不同情境下的差异
1. 全局环境中的 this
console.log(this); // 浏览器:window,Node.js:global
function globalFunction() {
console.log(this); // 非严格模式:window/global,严格模式:undefined
}
globalFunction();
'use strict';
function strictFunction() {
console.log(this); // undefined
}
strictFunction();
2. 普通函数(Function)中的 this
普通函数的 this 取决于调用方式:
function regularFunction() {
console.log(this);
}
// 直接调用:this 指向全局对象(非严格模式)或 undefined(严格模式)
regularFunction(); // window (非严格模式) 或 undefined (严格模式)
// 作为对象方法调用:this 指向该对象
const obj = {
method: regularFunction,
};
obj.method(); // obj
// 使用 call/apply/bind:this 指向指定的对象
const customObj = { name: 'Custom' };
regularFunction.call(customObj); // customObj
3. 箭头函数(Arrow Function)中的 this
箭头函数没有自己的 this,它会继承外层作用域的 this(词法作用域)。
const obj = {
name: 'Object',
// 普通函数
regularMethod: function () {
console.log('regularMethod this:', this); // obj
// 内部普通函数:this 会改变
function innerRegular() {
console.log('innerRegular this:', this); // window/undefined
}
innerRegular();
// 内部箭头函数:this 继承外层
const innerArrow = () => {
console.log('innerArrow this:', this); // obj
};
innerArrow();
},
// 箭头函数
arrowMethod: () => {
console.log('arrowMethod this:', this); // window(继承全局)
},
};
obj.regularMethod();
obj.arrowMethod();
4. 对象方法中的 this
const person = {
name: 'John',
age: 30,
// 普通函数:this 指向 person
greet: function () {
console.log(`Hello, I'm ${this.name}`); // "Hello, I'm John"
},
// ES6 简写方法:this 指向 person
introduce() {
console.log(`I'm ${this.name}, ${this.age} years old`);
},
// 箭头函数:this 继承外层(这里是全局)
arrowGreet: () => {
console.log(`Hello, I'm ${this.name}`); // "Hello, I'm undefined"
},
};
person.greet(); // "Hello, I'm John"
person.introduce(); // "I'm John, 30 years old"
person.arrowGreet(); // "Hello, I'm undefined"
5. 构造函数中的 this
function Person(name, age) {
this.name = name;
this.age = age;
this.greet = function () {
console.log(`Hello, I'm ${this.name}`);
};
}
const john = new Person('John', 30);
john.greet(); // "Hello, I'm John"
console.log(john.name); // "John"
6. Class 中的 this
class Person {
constructor(name) {
this.name = name;
}
// 普通方法:this 指向实例
greet() {
console.log(`Hello, I'm ${this.name}`);
}
// 箭头函数属性:this 绑定到实例
arrowGreet = () => {
console.log(`Hi, I'm ${this.name}`);
};
}
const john = new Person('John');
john.greet(); // "Hello, I'm John"
// 方法赋值给变量会失去 this 绑定
const greet = john.greet;
greet(); // 错误:Cannot read property 'name' of undefined
// 箭头函数属性不会失去 this 绑定
const arrowGreet = john.arrowGreet;
arrowGreet(); // "Hi, I'm John"
3. Quiz: What will be printed?
测验题:以下代码会打印什么?
题目 1:对象方法与箭头函数
const obj = {
name: 'Object',
regularFunc: function () {
console.log('A:', this.name);
},
arrowFunc: () => {
console.log('B:', this.name);
},
};
obj.regularFunc();
obj.arrowFunc();
点击查看答案
// A: Object
// B: undefined
解释:
regularFunc是普通函数,通过obj.regularFunc()调用,this指向obj,所以打印"A: Object"arrowFunc是箭头函数,没有自己的this,继承外层(全局)的this,全局没有name属性,所以打印"B: undefined"
题目 2:函数作为参数传递
const person = {
name: 'John',
greet: function () {
console.log(`Hello, ${this.name}`);
},
};
person.greet(); // 1
const greet = person.greet;
greet(); // 2
setTimeout(person.greet, 1000); // 3
点击查看答案
// 1: "Hello, John"
// 2: "Hello, undefined" 或错误(严格模式)
// 3: "Hello, undefined" 或错误(严格模式)
解释:
person.greet()- 通过对象调用,this指向persongreet()- 将方法赋值给变量后直接调用,this丢失,指向全局或undefinedsetTimeout(person.greet, 1000)- 方法作为回调函数传递,this丢失
题目 3:嵌套函数
const obj = {
name: 'Outer',
method: function () {
console.log('A:', this.name);
function inner() {
console.log('B:', this.name);
}
inner();
const arrow = () => {
console.log('C:', this.name);
};
arrow();
},
};
obj.method();
点击查看答案
// A: Outer
// B: undefined
// C: Outer
解释:
A-method通过obj调用,this指向objB-inner是普通函数,直接调用,this指向全局或undefinedC-arrow是箭头函数,继承外层method的this,指向obj
题目 4:setTimeout 与箭头函数
const obj = {
name: 'Object',
method1: function () {
setTimeout(function () {
console.log('A:', this.name);
}, 100);
},
method2: function () {
setTimeout(() => {
console.log('B:', this.name);
}, 100);
},
};
obj.method1();
obj.method2();
点击查看答案
// A: undefined
// B: Object
解释:
A-setTimeout的回调是普通函数,执行时this指向全局B-setTimeout的回调是箭头函数,继承外层method2的this,指向obj
题目 5:复杂的 this 绑定
const obj1 = {
name: 'obj1',
getThis: function () {
return this;
},
};
const obj2 = {
name: 'obj2',
};
console.log('A:', obj1.getThis().name);
const getThis = obj1.getThis;
console.log('B:', getThis() === window); // 假设在浏览器环境
obj2.getThis = obj1.getThis;
console.log('C:', obj2.getThis().name);
const boundGetThis = obj1.getThis.bind(obj2);
console.log('D:', boundGetThis().name);
点击查看答案
// A: obj1
// B: true
// C: obj2
// D: obj2
解释:
A- 通过obj1调用,this指向obj1B- 直接调用,this指向全局(window)C- 通过obj2调用,this指向obj2D- 使用bind绑定this为obj2
题目 6:构造函数与原型
function Person(name) {
this.name = name;
}
Person.prototype.greet = function () {
console.log(`Hello, I'm ${this.name}`);
};
Person.prototype.delayedGreet = function () {
setTimeout(function () {
console.log('A:', this.name);
}, 100);
};
Person.prototype.arrowDelayedGreet = function () {
setTimeout(() => {
console.log('B:', this.name);
}, 100);
};
const john = new Person('John');
john.delayedGreet();
john.arrowDelayedGreet();
点击查看答案
// A: undefined
// B: John
解释:
A-setTimeout的普通函数回调,this指向全局B-setTimeout的箭头函数回调,继承外层arrowDelayedGreet的this,指向john
题目 7:全局变量 vs 对象属性
var name = 'jjjj';
var obj = {
a: function () {
name = 'john';
console.log(this.name);
},
};
obj.a();
点击查看答案
// undefined
解释:
这道题的关键在于理解全局变量和对象属性的差异:
-
obj.a()的this指向:- 通过
obj.a()调用,this指向obj
- 通过
-
name = 'john'修改的是全局变量:name = 'john'; // 没有 var/let/const,修改全局 name
// 等同于
window.name = 'john'; // 浏览器环境 -
this.name访问的是对象属性:console.log(this.name); // 等同于 console.log(obj.name) -
obj对象没有name属性