只需三步实现 call、apply!

225 阅读2分钟

1、什么是 call、apply ?

  • 我们要想理解怎么实现 call 和 apply 方法就得先知道它们是什么。

  • call 和 apply 其实在功能上是高度相似的(基本上一样),那它们实现了什么呢?

  • apply 和 call 允许我们借用函数以及在函数调用中指定 this 指向.

  • 除此外, apply 函数允许我们在执行函数时传入一个参数数组, 以此使函数在执行可变参数的函数时可以将每个参数单独的传入函数并得到处理.

形象点来说,打个比方:

对象函数(方法)
吃鱼()、喵喵叫()
滑板鞋摩擦()、跳跃()

正常的情况下,应该是猫想吃鱼了就 猫.吃鱼("黄花鱼", "鲈鱼"),猫想叫了就 猫.喵喵叫();

但是有一天滑板鞋想吃鱼怎么办。

那就 :

猫.吃鱼().call(滑板鞋, "黄花鱼", "鲈鱼")
或者
猫.吃鱼().apply(滑板鞋, ["黄花鱼", "鲈鱼"])

call 和 apply 在使用上的不同也就是参数上有些区别。

2、那么实现这样的功能是个怎么样的过程呢?

其实 call 和 apply 改变了 this 的指向,让滑板鞋临时性的有了吃鱼这个功能,并且传递参数,把黄花鱼、鲈鱼给吃了。

具体过程大概是这样的:

  1. 将函数设置为对象的属性(把猫吃鱼设置为滑板鞋的属性)
  2. 在对象中执行该函数(让滑板鞋吃鱼)
  3. 删除该属性(滑板鞋嘴没了,不能吃鱼了)

其中还有参数的传递,传递给要执行的函数

3、实现 call

根据上面的过程,就很好理解了,上代码:

Function.prototype.newCall = function(context, ...args) {
    // 将函数设置为对象的属性(把猫吃鱼设置为滑板鞋的属性)
    context.fn = this;
    // 在对象中执行该函数(让滑板鞋吃鱼)
    let result = context.fn(...args);
    // 删除该属性(滑板鞋嘴没了,不能吃鱼了)
    delete context.fn;
    return result;
}

// 以上是核心代码,下面调用测试

let cat = {
    name: "加菲猫",
    eatFish: function(...fishType) {
        for (let fish of fishType) {
            console.log(this.name + " 吃 " + fish);
        }
    }
}

let shoes = {
    name: "滑板鞋"
}

cat.eatFish("黄花鱼", "鲈鱼");
cat.eatFish.newCall(shoes, "黄花鱼", "鲈鱼");

4、实现 apply

功能一样,就修改一下参数的传递方式就好了。

Function.prototype.newApply = function(context, args) {
    // 将函数设置为对象的属性(把猫吃鱼设置为滑板鞋的属性)
    context.fn = this;
    // 在对象中执行该函数(让滑板鞋吃鱼)
    let result = context.fn(...args);
    // 删除该属性(滑板鞋嘴没了,不能吃鱼了)
    delete context.fn;
    return result;
}

// 以上是核心代码,下面调用测试

let cat = {
    name: "加菲猫",
    eatFish: function(...fishType) {
        for (let fish of fishType) {
            console.log(this.name + " 吃 " + fish);
        }
    }
}

let shoes = {
    name: "滑板鞋"
}

cat.eatFish("黄花鱼", "鲈鱼");
cat.eatFish.newApply(shoes, ["黄花鱼", "鲈鱼"]);

喜欢的话点个赞再走吧〈( ^.^)ノ