轻松搞懂 apply、call、bind:JavaScript 函数调用的魔法秘籍
嘿,前端小伙伴们😎!今天咱们要来一场有趣的 JavaScript 函数之旅,探秘 apply、call、bind 这三个神秘方法的奇妙世界🧙♂️。它们可是 JavaScript 函数里的超级小助手,能帮我们改变函数中 this 的指向,就像给函数戴上了不同的魔法眼镜,让它看到不同的世界哦😜。
一、神秘的“this”指向
在 JavaScript 中,函数的 this 就像一个调皮的小精灵🧚♂️,它会根据函数的调用方式和所在位置改变自己的指向。有时候它指向全局对象(在浏览器里是 window,在 Node.js 环境中是 global),有时候又指向某个特定的对象。这可让很多小伙伴头疼不已,不过别怕,apply、call、bind 就是来拯救我们的超级英雄🦸♂️!
二、三个方法的初登场
(一)call 方法:参数逐个传
call 方法就像是一个严格的指挥官👨✈️,要求我们把参数一个一个地列出来,用逗号隔开。它会立即执行函数,让函数以指定的对象为 this 来运行。
function foo(a, b) {
console.log(a + b);
console.log(this.name);
}
let obj = {
name: 'jennry',
age: '23'
};
foo.call(obj, 1, 2); 在这里,foo 函数原本的 this 可能指向全局对象,但通过 call 方法,我们让它把 obj 当作 this,所以在函数里就能访问到 obj 的 name 属性啦。
(二)apply 方法:参数打包送
apply 方法则像是一个贴心的小助手👩✈️,它允许我们把参数打包成一个数组传进去。和 call 一样,它也会马上执行函数哦。
function foo(a, b) {
console.log(a + b);
console.log(this.name);
}
let obj = {
name: 'jennry',
age: '23'
};
foo.apply(obj, [1, 2]); 看,我们把 1 和 2 放在一个数组里传给 apply,函数就能正常使用这些参数进行计算啦。
(三)bind 方法:延迟执行的魔法
bind 方法就像一个神秘的魔法师🧙♂️,它不会立刻执行函数,而是返回一个新的函数。这个新函数就像是被施了魔法一样,已经绑定了指定的 this 和参数,等我们什么时候想调用它,它就会以绑定好的状态执行。
function foo(a, b) {
console.log(a + b);
console.log(this.name);
}
let obj = {
name: 'jennry',
age: '23'
};
let boundFoo = foo.bind(obj, 1, 3);
boundFoo(); 这里我们先通过 bind 得到了一个新的函数 boundFoo,它已经记住了 obj 是它的 this,以及参数 1 和 3。当我们调用 boundFoo 时,它就会按照设定好的状态执行函数。
三、它们的区别大揭秘
(一)参数传递方式
call方法:参数是一个一个列出来的,就像排队的小朋友,规规矩矩,用逗号隔开。apply方法:参数都放在一个数组里,像一群小伙伴手拉手一起走。bind方法:和call类似,也是一个一个传参数,但它返回的是一个待执行的新函数。
(二)执行时机
call和apply:都是立即执行函数,迫不及待地要展示自己的功能。bind:比较有耐心,先准备好一切,等我们召唤它的时候才执行。
四、手写 JavaScript 实现它们的魔法
(一)模拟 call 方法
function myCall(fn, obj,...args) {
// 如果没有指定对象,那就让 this 指向全局对象(这里用 globalThis,兼容性更好哦)
if (!obj) {
obj = globalThis;
}
// 把要调用的函数绑定到指定对象上,就像给对象穿上了一件带函数的新衣服
obj.temp = fn;
// 让对象穿上新衣服后执行函数,把参数都传给它
const result = obj.temp(...args);
// 函数执行完了,把这件衣服脱下来(删除临时属性)
delete obj.temp;
// 返回函数执行的结果
return result;
}
// 测试一下我们的 myCall 方法
window.name = "tony";
let obj = { name: "jenny", age: 22 };
function foo(a, b) {
console.log(this.name, this);
console.log(a + b);
}
myCall(foo, obj, 1, 2); (二)模拟 apply 方法
function myApply(fn, obj, args) {
// 同样,如果没有对象,this 就指向全局对象
if (!obj) {
obj = globalThis;
}
// 把函数绑定到对象上
obj.temp = fn;
// 执行函数,这里要注意用扩展运算符把数组参数展开哦
const result = obj.temp(...args);
// 脱掉函数衣服
delete obj.temp;
// 返回结果
return result;
}
window.name = "tony";
let obj = { name: "jenny", age: 22 };
function foo(a, b) {
console.log(this.name, this);
console.log(a + b);
}
myApply(foo, obj, [1, 2]); (三)模拟 bind 方法
function myBind(fn, obj,...args) {
return function() {
// 这里我们调用之前写的 myCall 方法,把函数和对象以及参数都传进去
return myCall(fn, obj,...args);
};
}
window.name = "tony";
let obj = { name: "jenny", age: 22 };
function foo(a, b) {
console.log(this.name, this);
console.log(a + b);
}
let boundFoo = myBind(foo, obj, 1, 2);
boundFoo(); 通过手写实现这三个方法,我们就像揭开了它们神秘的面纱,更加深入地理解了它们的工作原理。以后在使用它们的时候,就像拥有了魔法咒语一样得心应手啦😎!希望小伙伴们都能轻松掌握这三个 JavaScript 函数的小魔法,写出更厉害的代码哦💪!如果在实现过程中有什么问题或者有更好的想法,欢迎大家一起交流分享呀🤗。
