引言
大家好,我是刚被面试官用this
连环拷打的大三前端菜狗🐶。今天遇到一道堪称"this界修罗场"的题目,让我彻底明白------在JavaScript的世界里,this
就是个"变色龙"!(别打我,看完你就懂)
一、先上"刑场真题" 🚨
(建议先不要看答案,自己脑补输出结果)
javascript
var a = 10;
var foo = {
a: 20,
b: function () {
var a = 30;
return this.a;
},
c: () => {
var a = 40;
return this.a;
},
}
var d = { a: 50 };
console.log(a); // ?
console.log(foo.b()); // ?
console.log(foo.c()); // ?
console.log(foo.b.bind(d)()); // ?
console.log(foo.c.bind(d)()); // ?
答案揭晓前,先带大家修炼"this心法"!
二、this四大定律 📜
1️⃣ 默认绑定:孤狼模式 🐺
当函数"裸奔"调用时,this
默认指向全局对象:
- 浏览器 :
window
- Node环境 :
global
(但模块作用域中默认是undefined
!)
javascript
function showThis() {
console.log(this === window);
}
showThis(); // 浏览器中输出 true 🪟
⚠️ 陷阱警告:
javascript
'use strict'; // 严格模式下全局this是undefined!
function strictThis() {
console.log(this);
}
strictThis(); // undefined 🚫
2️⃣ 隐式绑定:谁调用就认谁做爹 👨👦
当函数被对象"领养"时,this
指向调用它的最近对象:
javascript
const family = {
father: '👨',
say: function() {
console.log(this.father);
}
};
family.say(); // 输出 👨(this指向family)
但! 如果把方法赋值给变量...
javascript
const lostChild = family.say;
lostChild(); // 输出 undefined 😭(this指向全局)
3️⃣ 显式绑定:强行认亲之术 🤜
用call
、apply
、bind
强行改变this
指向:
javascript
const orphan = { father: '🏠孤儿院' };
family.say.call(orphan); // 🏠孤儿院
family.say.apply(orphan); // 🏠孤儿院
const boundSay = family.say.bind(orphan);
boundSay(); // 🏠孤儿院
🔍 区别小课堂:
call
:参数一个个传(thisArg, arg1, arg2...)
apply
:参数用数组传(thisArg, [args])
bind
:返回新函数,需要再次调用
4️⃣ new绑定:无中生有造对象 🧙♂️
构造函数中,this
指向新创建的实例:
javascript
function Person(name) {
this.name = name;
}
const baby = new Person('👶');
console.log(baby.name); // 👶
三、箭头函数------this界的佛系青年 🧘♂️
箭头函数的this
在定义时就确定了,像"刻在DNA里"一样无法改变!
🚫 三大禁忌:
- 不能用
call
/apply
/bind
修改this
- 不能作为构造函数
- 没有自己的
arguments
javascript
const obj = {
a: 1,
normalFunc: function() {
console.log(this.a); // 正常人的this 😎
},
arrowFunc: () => {
console.log(this.a); // 佛系青年的this 🧘♂️
}
};
obj.normalFunc(); // 1(this指向obj)
obj.arrowFunc(); // undefined(this指向外层全局)
四、浏览器 vs Node环境差异 🌐
全局变量差异:
- 浏览器 :
var a = 1
→ 挂载到window
- Node :每个文件都是模块,
var a = 1
不属于global
!
javascript
// test.js
var a = 1;
console.log(global.a); // Node输出 undefined
console.log(window.a); // 浏览器输出 1
箭头函数实战差异:
javascript
const obj = {
arrow: () => console.log(this)
};
obj.arrow();
// 浏览器:Window(外层是全局)
// Node:{}(外层是模块作用域的this,空对象)
五、终极解密面试题 🕵️♂️
现在带着心法回头看题目:
javascript
// 全局a=10(浏览器挂载到window)
var a = 10;
var foo = {
a: 20,
b: function () { // 普通函数,this看调用者
var a = 30; // 烟雾弹!和this无关
return this.a;
},
c: () => { // 箭头函数,this继承定义时的外层(全局)
var a = 40; // 烟雾弹×2
return this.a;
},
};
var d = { a: 50 };
console.log(a); // 10 ✅(直接访问全局a)
console.log(foo.b()); // 20 ✅(this指向调用者foo)
console.log(foo.c()); // 10(浏览器)或undefined(Node)✅
// 箭头函数this指向全局,浏览器中window.a=10,Node无全局a
console.log(foo.b.bind(d)()); // 50 ✅(bind强行指向d)
console.log(foo.c.bind(d)()); // 同上结果,bind对箭头函数无效!✅
六、防坑指南 🚧
- 避免在对象方法中使用箭头函数(除非刻意指向全局)
- 回调函数中的this陷阱 (用箭头函数或
bind
固定this) - 严格模式警惕默认绑定(全局this变undefined)
七、灵魂总结 💡
"普通函数看调用,箭头函数看定义,显式绑定显神通,new个对象最安心"
下次面试再遇到this
,请优雅地甩出这篇文章,深藏功与名~ 🎩💨