轻松搞懂 applycallbind:JavaScript 函数调用的魔法秘籍

嘿,前端小伙伴们😎!今天咱们要来一场有趣的 JavaScript 函数之旅,探秘 applycallbind 这三个神秘方法的奇妙世界🧙‍♂️。它们可是 JavaScript 函数里的超级小助手,能帮我们改变函数中 this 的指向,就像给函数戴上了不同的魔法眼镜,让它看到不同的世界哦😜。

一、神秘的“this”指向

在 JavaScript 中,函数的 this 就像一个调皮的小精灵🧚‍♂️,它会根据函数的调用方式和所在位置改变自己的指向。有时候它指向全局对象(在浏览器里是 window,在 Node.js 环境中是 global),有时候又指向某个特定的对象。这可让很多小伙伴头疼不已,不过别怕,applycallbind 就是来拯救我们的超级英雄🦸‍♂️!

二、三个方法的初登场

(一)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,所以在函数里就能访问到 objname 属性啦。

(二)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]); 

看,我们把 12 放在一个数组里传给 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,以及参数 13。当我们调用 boundFoo 时,它就会按照设定好的状态执行函数。

三、它们的区别大揭秘

(一)参数传递方式

  1. call 方法:参数是一个一个列出来的,就像排队的小朋友,规规矩矩,用逗号隔开。
  2. apply 方法:参数都放在一个数组里,像一群小伙伴手拉手一起走。
  3. bind 方法:和 call 类似,也是一个一个传参数,但它返回的是一个待执行的新函数。

(二)执行时机

  1. callapply:都是立即执行函数,迫不及待地要展示自己的功能。
  2. 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 函数的小魔法,写出更厉害的代码哦💪!如果在实现过程中有什么问题或者有更好的想法,欢迎大家一起交流分享呀🤗。

最后修改:2024 年 12 月 11 日
如果觉得我的文章对你有用,请随意赞赏