JS中This指向问腿
在 JavaScript 中,this 是一个特殊关键字,其指向并非在函数定义时确定,而是在函数调用时动态绑定,取决于调用方式。理解 this 的指向是 JS 面试的高频考点,以下从核心场景、特殊情况和面试题三个维度详细讲解。
一、this 指向的核心场景
1. 全局作用域中的 this
- 非严格模式:全局作用域中
this指向全局对象(浏览器中是window,Node.js 中是global)。 - 严格模式:全局作用域中
this仍指向全局对象(严格模式仅影响函数内的this)。
console.log(this === window); // 浏览器中:true(非严格模式)
'use strict';
console.log(this === window); // 仍为 true(严格模式不改变全局 this)2. 普通函数调用(最容易混淆)
函数独立调用时,this 指向由是否为严格模式决定:
- 非严格模式:
this指向全局对象(window/global)。 - 严格模式:
this为undefined。
// 非严格模式
function foo() {
console.log(this === window); // true
}
foo(); // 独立调用
// 严格模式
'use strict';
function bar() {
console.log(this); // undefined
}
bar(); // 独立调用3. 对象方法调用
函数作为对象的方法被调用时,this 指向调用该方法的对象(即方法所属的对象)。
const obj = {
name: '张三',
sayHi() {
console.log(this.name); // 指向 obj
}
};
obj.sayHi(); // 输出:张三
// 注意:方法赋值后独立调用,this 会改变
const func = obj.sayHi;
func(); // 非严格模式下:this 指向 window(输出:undefined)4. 构造函数调用(new 关键字)
用 new 调用构造函数时,this 指向新创建的实例对象。
function Person(name) {
this.name = name; // this 指向新实例
}
const p = new Person('李四');
console.log(p.name); // 输出:李四特殊情况:若构造函数返回一个对象类型(非原始值),this 会指向返回的对象(而非实例):
function Person(name) {
this.name = name;
return { age: 18 }; // 返回对象
}
const p = new Person('李四');
console.log(p.name); // undefined(this 指向返回的 {age:18})5. 箭头函数中的 this
箭头函数没有自己的 this,其 this 继承自外层作用域的 this(定义时确定,而非调用时)。
const obj = {
name: '王五',
sayHi: () => {
console.log(this.name); // this 继承自外层(全局),指向 window
},
delaySay() {
setTimeout(() => {
console.log(this.name); // 继承自 delaySay 的 this(即 obj)
}, 100);
}
};
obj.sayHi(); // 输出:undefined(window 无 name)
obj.delaySay(); // 输出:王五(正确)6. call/apply/bind 改变 this
这三个方法可强制绑定函数的 this 指向:
func.call(thisArg, arg1, arg2, ...):立即调用函数,参数逐个传入。func.apply(thisArg, [arg1, arg2, ...]):立即调用函数,参数以数组传入。func.bind(thisArg, arg1, arg2, ...):返回新函数(不立即调用),this永久绑定。
function sayName() {
console.log(this.name);
}
const obj1 = { name: '赵六' };
const obj2 = { name: '钱七' };
sayName.call(obj1); // 输出:赵六(this 指向 obj1)
sayName.apply(obj2); // 输出:钱七(this 指向 obj2)
const boundFunc = sayName.bind(obj1);
boundFunc(); // 输出:赵六(绑定后无法修改)
boundFunc.call(obj2); // 仍输出:赵六(bind 优先级最高)7. DOM 事件处理中的 this
DOM 事件回调函数中,this 指向触发事件的 DOM 元素(与 event.target 类似,但有区别)。
<button id="btn">点击</button>
<script>
const btn = document.getElementById('btn');
btn.onclick = function() {
console.log(this === btn); // true(this 指向触发事件的按钮)
};
</script>二、常见误区总结
- 箭头函数不能作为构造函数:箭头函数没有
this,用new调用会报错。 - 对象方法中的嵌套函数:嵌套函数独立调用时,
this指向全局(非严格模式),而非外层对象:javascriptconst obj = { name: '孙八', outer() { function inner() { console.log(this.name); // 非严格模式:window.name(可能为 undefined) } inner(); // 独立调用,this 不指向 obj } }; obj.outer(); // 输出:undefined bind绑定后无法被覆盖:call/apply无法修改bind已绑定的this。
三、面试题及解析
题目 1:基础判断
var name = '全局';
const obj = {
name: '对象',
say() {
console.log(this.name);
}
};
const func = obj.say;
obj.say(); // ?
func(); // ?答案:obj.say() 输出 对象(方法调用,this 指向 obj);func() 输出 全局(独立调用,this 指向 window,var 声明的 name 挂载到 window)。
题目 2:箭头函数与普通函数对比
var age = 10;
const person = {
age: 20,
getAge1: function() {
return this.age;
},
getAge2: () => {
return this.age;
}
};
console.log(person.getAge1()); // ?
console.log(person.getAge2()); // ?答案:person.getAge1() 输出 20(普通函数,this 指向 person);person.getAge2() 输出 10(箭头函数,this 继承自全局,指向 window)。
题目 3:构造函数与返回值
function Student(name) {
this.name = name;
return name; // 返回原始值
}
const s1 = new Student('小明');
console.log(s1.name); // ?
function Teacher(name) {
this.name = name;
return { name: '老师' }; // 返回对象
}
const t1 = new Teacher('小红');
console.log(t1.name); // ?答案:s1.name 输出 小明(构造函数返回原始值,this 仍指向实例);t1.name 输出 老师(构造函数返回对象,this 指向返回的对象)。
题目 4:call 与嵌套函数
const obj = {
a: 1,
foo() {
const bar = function() {
console.log(this.a);
};
bar.call(this); // 强制绑定 this 为 obj
}
};
obj.foo(); // ?答案:输出 1(bar.call(this) 中 this 是 foo 的 this(即 obj),故 bar 的 this 指向 obj)。
题目 5:综合场景
var x = 10;
const obj = {
x: 20,
fn() {
console.log(this.x);
return function() {
console.log(this.x);
};
}
};
obj.fn()(); // 输出什么?答案:先输出 20(obj.fn() 中 this 指向 obj),再输出 10(返回的函数独立调用,this 指向 window)。
总结
判断 this 指向的优先级:
new调用 >bind绑定 >call/apply> 对象方法调用;- 普通函数独立调用:非严格模式指向全局,严格模式为
undefined; - 箭头函数:继承外层作用域的
this,与调用方式无关。
掌握这些规则,就能应对绝大多数 this 指向的面试题。