🎉 JavaScript中的this与函数绑定:call/apply/bind与箭头函数全解析!

200 阅读4分钟

🔥 第一章:this的“灵魂归属”之旅

image.png

🧠 普通函数调用——“你是谁?我是全局变量!”

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就像个“追星族”,谁触发了事件,它就疯狂绑定谁的灵魂!🔥


🧊 第二章:箭头函数——闭包界的“铁憨憨”

image.png

❄️ 无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的“外挂神器”

image.png

🎯 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还能变身“函数柯里化大师”,把两个参数的函数变成只接受一个参数的“懒人模式”!😄


🛠️ 第四章:实战演练——从脑海想象到真实场景

image.png

🧨 this丢失的“社死”时刻

// button.js
Button.prototype.bindEvent = function () {
    this.element.addEventListener('click', function() {
        this.style.backgroundColor = 'lightblue'; // this指向按钮,而非Button实例!
    });
};

幽默注释
这是程序员的“社死”现场——this突然指向按钮,导致代码崩溃!😱


✅ 解决this丢失的四种方法

方法实现方式幽默比喻
bindthis.setBgColor.bind(this)this签“终身契约”
箭头函数() => this.setBgColor()“铁憨憨”学霸永不叛变
保存引用var _this = this;this藏进“保险箱”
call/applysetBgColor.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大师”!🔥