面试常出这种题:
const obj = {
name: '张三',
say: function() {
return this.name;
}
};
const say = obj.say;
console.log(obj.say()); // 张三
console.log(say()); // undefined 还是报错?
同一个函数,不同的调用方式,this 指向就完全不同。
一、this 是什么
核心真相:this 不是在函数定义时决定的,而是在函数调用时决定的。
这句话是理解 this 的总纲,先记住。
同一个函数,不同的调用方式,this 就不同 —— 这就是所有 this 相关题目的本质。
二、4 种绑定规则(按优先级从低到高)
规则 1:默认绑定(优先级最低)
触发条件:函数独立调用
this 指向:
- 非严格模式:全局对象(浏览器是
window) - 严格模式:
undefined
function fn() {
console.log(this);
}
fn();
// 非严格模式:window
// 严格模式:undefined
常见陷阱:
const obj = {
name: '张三',
say: function() {
return this.name;
}
};
const say = obj.say; // 把方法赋值给变量
say(); // undefined —— 独立调用,this 是 window,window.name 是 undefined
踩坑点:函数被赋值给变量后独立调用,this 就丢了。
规则 2:隐式绑定
触发条件:通过对象点语法调用 obj.fn()。
this 指向:点号前的对象。
const user = {
name: '张三',
say: function() {
return this.name;
}
};
user.say(); // "张三" —— this 是 user
关键:只看调用位置的最近一层。
const parent = {
name: '父',
child: {
name: '子',
say: function() {
return this.name;
}
}
};
parent.child.say(); // "子" —— this 是 child,不是 parent
常见陷阱:回调函数丢 this
const user = {
name: '张三',
say: function() {
return this.name;
}
};
setTimeout(user.say, 1000); // undefined —— 传参时 say 变成独立函数
setTimeout(user.say, 1000) 相当于:
const fn = user.say;
setTimeout(fn, 1000);
fn(); // 独立调用,this 丢了
规则 3:显式绑定
触发条件:通过 call / apply / bind 手动指定 this。
this 指向:你传的第一个参数。
function say() {
return this.name;
}
const user = { name: '张三' };
say.call(user); // "张三"
say.apply(user); // "张三"
say.bind(user)(); // "张三"
差别:
call/apply:立即执行bind:返回新函数,后续调用
const user = { name: '张三' };
const other = { name: '李四' };
user.say = function() { return this.name; };
user.say.call(other); // "李四" —— call 覆盖了隐式绑定
规则 4:new 绑定(优先级最高)
触发条件:用 new 调用函数。
this 指向:new 出来的新对象。
function Person(name) {
this.name = name;
}
const p = new Person('张三');
console.log(p.name); // "张三"
详细原理看[手写 new 那篇]
function Person(name) {
this.name = name;
}
const bound = Person.bind({ name: '李四' });
const p = new bound('张三');
console.log(p.name); // "张三" —— new 覆盖了 bind
即使 bind 到"李四",new bound() 还是会创建新对象,把 this 绑到新对象上。
三、优先级总结
new 绑定 > 显式绑定 (call/apply/bind) > 隐式绑定 (obj.fn) > 默认绑定 (fn)
四、箭头函数特例(没有 this)
箭头函数没有自己的 this,继承定义时外层作用域的 this。
const user = {
name: '张三',
say: function() {
const inner = () => {
return this.name;
};
return inner();
}
};
user.say(); // "张三"
关键:inner 是箭头函数,它的 this 继承自 say 的 this(即 user)。
对比:如果 inner 是普通函数
const user = {
name: '张三',
say: function() {
const inner = function() {
return this.name;
};
return inner();
}
};
user.say(); // undefined —— inner 独立调用,默认绑定,window.name 是 undefined
这是箭头函数最有用的地方:解决回调里 this 丢失的问题。
箭头函数无法被改变 this
call/apply/bind 对箭头函数无效:
const fn = () => this.name;
const user = { name: '张三' };
fn.call(user); // undefined —— call 无法改变箭头函数的 this
五、面试题一览(输出什么?)
题 1:
const obj = {
name: '张三',
say: function() {
return this.name;
}
};
const say = obj.say;
say(); // ???
答案:undefined(默认绑定,this 是 window)
题 2:
const obj = {
name: '张三',
say: function() {
return () => this.name;
}
};
const fn = obj.say();
fn(); // ???
答案:"张三"
obj.say()隐式绑定,this 是 obj- 返回的箭头函数继承 say 的 this,还是 obj
题 3:
function Person(name) {
this.name = name;
return { name: '李四' };
}
const p = new Person('张三');
console.log(p.name); // ???
答案:"李四"
- 构造函数显式返回对象时,new 会用返回的对象
六、面试怎么答
面试官问:"this 指向怎么判断?"
满分答法:
- 总纲:this 不是定义时决定,是调用时决定
- 4 种规则 + 优先级:new > 显式 > 隐式 > 默认
- 箭头函数特例:没有自己的 this,继承外层
- 实际判断:从高到低检查函数调用形式
new xxx() → new 绑定
xxx.call/apply/bind → 显式绑定
obj.xxx() → 隐式绑定
xxx() → 默认绑定
总结
this 4 规则:
- 默认绑定:独立调用,window/undefined
- 隐式绑定:obj.fn(),this 是 obj
- 显式绑定:call/apply/bind,指定 this
- new 绑定:new 出的新对象
优先级:new > 显式 > 隐式 > 默认
箭头函数:没有 this,继承外层