📕 重学JavaScript:如何实现一个call
/apply
?
嗨,大家好!这里是道长王jj
~ 🎩🧙♂️
这次要说的 call
/apply
方法其实和 bind
极其类似。
它们可以让你改变一个函数的 this 指向,也就是让这个函数执行时,里面的 this 指向你想要的对象。😮
❓ 什么是call
/apply
?
比如,你有一个函数叫 fn ,它会打印出 this 的 z 属性和两个参数 x 和 y 的和,就像这样:
function fn(x, y) {
console.log(this.z);
console.log(x + y);
}
然后,你有一个对象叫 obj ,它有一个 z 属性,就像这样:
var obj = {
z: 1
};
现在,你想让 fn 的 this 指向 obj ,而且给 fn 传递两个参数 2 和 3 ,就可以使用 call
/apply
方法了!😊
你只要这样写:
fn.call(obj, 2, 3); // call 方法是一个一个传入参数
fn.apply(obj, [2, 3]); // apply 方法是通过一个数组传入参数
就会打印出:
1
5
他们俩区别就是传递参数的方式不同。call 方法是一个一个传入参数,而 apply 方法是通过一个数组传入参数。😁
那么,现在我们要自己实现call
/apply
,该怎么办呢?🤔
🔰 思路分析
其实核心套路其实和bind
差不多,都是就两件事情。
- 让一个普通函数的 this 指向你想要的对象。
- 让一个构造函数的原型对象上的属性不会丢失。
确定好思路之后,我们就可以进行伪代码环节了。
💫 伪代码思路
1. 把第一个参数作为新的 this ,把剩下的参数作为新函数的初始参数,放在一个数组里。
2. 需要注意 call 方法是一个一个传入参数,而 apply 方法是通过一个数组传入参数。
3. 用 eval 函数执行原来的函数,把新的 this 和参数传进去。
4. 返回执行结果
⭕ 代码实现
function myCall(obj) {
// 如果 obj 参数为空,则默认为 window 对象
obj = obj || window;
// 使用 Symbol 函数创建一个唯一的标识符
const fnSymbol = Symbol();
// 把原来的函数赋值给 obj 的 fnSymbol 属性
obj[fnSymbol] = this;
// 获取剩余参数
const args = [...arguments].slice(1);
// 构造字符串代码
let code = `obj[fnSymbol](${
args})`;
// 执行代码并返回结果
let result = eval(code);
// 删除 obj 的 fnSymbol 属性
delete obj[fnSymbol];
// 返回结果
return result;
};
function myApply(obj) {
// 如果 obj 参数为空,则默认为 window 对象
obj = obj || window;
// 使用 Symbol 函数创建一个唯一的标识符
const fnSymbol = Symbol();
// 把原来的函数赋值给 obj 的 fnSymbol 属性
obj[fnSymbol] = this;
// 获取剩余参数(数组)
const args = arguments[1];
// 构造字符串代码
let code = `obj[fnSymbol](${
args})`;
// 执行代码并返回结果
let result = eval(code);
// 删除 obj 的 fnSymbol 属性
delete obj[fnSymbol];
// 返回结果
return result;
};
💥 测试一下看看有没有问题
function sayHello(name) {
return "Hello, " + this.greeting + " " + name;
}
sayHello.myCall = myCall;
sayHello.myApply = myApply;
var obj = {
greeting: "你好" };
// 用 myCall 调用函数
var result1 = sayHello.myCall(obj, "小明");
// 用 myApply 调用函数
var result2 = sayHello.myApply(obj, ["小红"]);
console.log(result1); // Hello, 你好 小明
console.log(result2); // Hello, 你好 小红
🎉 你觉得怎么样?这篇文章可以给你带来帮助吗?如果你有任何疑问或者想进一步讨论相关话题,请随时发表评论分享您的想法,让其他人从中受益。🚀✨