🔥 第一章:this的“灵魂归属”之旅
🧠 普通函数调用——“你是谁?我是全局变量!”
var name = "王老板"; // 全局变量
function fn() {
var name = "杜老板"; // 局部变量
console.log(this.name); // 输出:王老板(灵魂归属全局对象!)
}
fn(); // 普通函数调用,this指向window
幽默注释:
this就像一个“社牛”,在普通函数调用时,它会自动认领全局对象(浏览器中是window)为父。但如果你把它从对象方法中“偷跑”出来,它可能会迷失方向,变成“王老板”而不是“杜老板”!😅
🎯 构造函数调用——“我是新生命,我主宰自己!”
function Person(name, age) {
this.name = name; // 新生命拥有自己的name
this.age = age; // 新生命拥有自己的age
}
Person.prototype.sayHi = function() {
console.log(`你好,我叫${this.name},我今年${this.age}岁`);
};
const p1 = new Person("杜老板", 20);
p1.sayHi(); // 输出:你好,我叫杜老板,我今年20岁
幽默注释:
构造函数就像一个“造人机器”,用new关键字启动后,this会自动指向新创建的对象。此时它不再是“王老板”,而是“杜老板”!🎉
⚡ 事件处理中的this——“谁触发我,我就属于谁!”
const btn = document.getElementById('btn');
btn.addEventListener('click', function() {
console.log(this); // this指向触发事件的DOM元素(即按钮)
});
幽默注释:
点击按钮时,this就像个“追星族”,谁触发了事件,它就疯狂绑定谁的灵魂!🔥
🧊 第二章:箭头函数——闭包界的“铁憨憨”
❄️ 无this动态绑定,继承父作用域
var a = {
name: "杜老板",
func1: () => {
console.log(this.name); // 输出:王老板(继承自父作用域)
}
};
a.func1();
幽默注释:
箭头函数就像一个“铁憨憨”学霸,它从不自己思考this的归属,只会死板地继承父作用域的上下文!😅
🔄 在事件处理和组件封装中的“稳定发挥”
// button.js
Button.prototype.bindEvent = function () {
this.element.addEventListener('click', () => {
this.element.style.backgroundColor = 'lightblue'; // this始终指向Button实例
});
};
幽默注释:
箭头函数是“稳定型选手”,在组件封装中永远知道自己的定位,不会像传统函数那样“叛逆期发飙”!🎉
🧩 第三章:call/apply/bind——this的“外挂神器”
🎯 call vs apply:传参方式的“霸道总裁”
var a = {
name: '杜',
fn: function(a, b) {
console.log(this.name); // 输出:杜
console.log(a, b); // 输出:1 2
}
};
a.fn.call(a, 1, 2); // 单独传参(call的“一对一约会”)
a.fn.apply(a, [1, 2]); // 集体传参(apply的“团建派对”)
幽默注释:
call是“单独约会”,参数逐个传递;apply是“集体团建”,参数用数组打包。它们都能强行指定this的归属,但传参方式不同!🔥
⏱️ bind的“预绑定”特性——延迟执行的“契约”
const func2 = a.fn.bind(a, 1, 2);
func2(); // 输出:杜 + 1 2
幽默注释:
bind就像签“婚约”,提前锁定this的归属,等你准备好再调用,稳如老狗!🎉
🛠️ 实战场景:call/apply/bind的典型用途
1. 修改函数内部的this指向
const person = { name: "杜" };
function sayHi(age) {
console.log(`你好,我叫${this.name},我今年${age}岁`);
}
sayHi.call(person, 20); // 输出:你好,我叫杜,我今年20岁
2. 函数借用(Function Borrowing)
const obj1 = { values: [3, 1, 4] };
const obj2 = { values: [1, 5, 9] };
Math.max.apply(null, obj1.values); // 4
Math.min.apply(null, obj2.values); // 1
3. 函数柯里化(Currying)
function multiply(a, b) {
return a * b;
}
const double = multiply.bind(null, 2); // 固定a=2
console.log(double(5)); // 10
幽默注释:
bind还能变身“函数柯里化大师”,把两个参数的函数变成只接受一个参数的“懒人模式”!😄
🛠️ 第四章:实战演练——从脑海想象到真实场景
🧨 this丢失的“社死”时刻
// button.js
Button.prototype.bindEvent = function () {
this.element.addEventListener('click', function() {
this.style.backgroundColor = 'lightblue'; // this指向按钮,而非Button实例!
});
};
幽默注释:
这是程序员的“社死”现场——this突然指向按钮,导致代码崩溃!😱
✅ 解决this丢失的四种方法
| 方法 | 实现方式 | 幽默比喻 |
|---|---|---|
bind | this.setBgColor.bind(this) | 给this签“终身契约” |
| 箭头函数 | () => this.setBgColor() | “铁憨憨”学霸永不叛变 |
| 保存引用 | var _this = this; | 把this藏进“保险箱” |
call/apply | setBgColor.call(this) | 直接“强制绑定” |
🧪 完整解决方案示例
Button.prototype.bindEvent = function () {
// 方法1:bind预绑定
this.element.addEventListener('click', this.setBgColor.bind(this));
// 方法2:箭头函数
this.element.addEventListener('click', () => this.setBgColor());
// 方法3:保存引用
const _this = this;
this.element.addEventListener('click', function() {
_this.setBgColor();
});
// 方法4:call/apply
this.element.addEventListener('click', function() {
this.setBgColor.call(this);
});
};
幽默注释:
四种方法就像四个“保镖”,任选其一都能护送this安全回家!🎉
📊 表格总结:this的“灵魂归属”规则
| 调用方式 | this指向 | 生活化比喻 |
|---|---|---|
| 普通函数 | window(非严格模式) | 社牛认领全局对象 |
| 对象方法 | 调用该方法的对象 | 服从“直属上司” |
| 构造函数 | 新创建的对象 | “造人机器”的产物 |
| 事件处理 | 触发事件的DOM元素 | 追星族绑定触发者 |
| 箭头函数 | 父作用域的this | 铁憨憨学霸死板继承 |
🎉 结语:
JavaScript的this机制就像一场“灵魂归属”的冒险,看似复杂,但只要掌握规则和技巧,就能轻松驾驭!希望这篇博客能帮你从“小白”进阶为“this大师”!🔥