欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

手写call,apply,bind

程序员文章站 2022-03-08 09:17:45
...
call() 方法调用一个函数, 其具有一个指定的this值和分别地提供的参数(参数列表)。
apply() 方法调用一个具有给定this值的函数,以及作为一个数组(或类数组对象)提供的参数。
bind() 方法创建一个新的函数,在调用时设置this关键字为提供的值。并在调用新函数时,将给定参数列表作为原函数的参数序列的前若干项。

以上内容源自MDN

下面我用自己的理解说一下call,apply及bind都做了啥

foo.call(obj,argument1,augument2,argument3)复制代码

以上代码的作用:

执行函数foo,并将其this指定为obj,后面的参数为调用foo时传入的参数

foo.apply(obj,[argument1,augument2,argument3])复制代码

以上代码的作用:

执行函数foo,并将其this指定为obj并将数组

[
argument1,augument2,argument3]中的每一项作为调用foo时传入的参数

let bar = foo.bind(obj,argument1,augument2,argument3);
bar();复制代码

以上代码的作用:

返回一个绑定了this为obj,参数为argument1,augument2,argument3的函数,所以如果想要执行foo,需要把返回结果再执行一下

了解了方法的功能和原理,接下来让我们分别实现它们

call的实现:

首先实现一个简单的call方法

Function.prototype.call2 = function(obj){
  obj.fn = this;
  if(arguments.length>1){
    obj.fn(...([...arguments].slice(1)));
  }else{
    obj.fn();
  }
  delete obj.fn;
}复制代码

以上代码仅仅实现了this的绑定以及函数调用传参,但是忽略了函数的返回值,而且默认把当前函数赋给了obj.fn,而没有考虑obj.fn存在的情况,改进后的代码如下:

Function.prototype.call2 = function(obj){
  let _fn = "fn",result;
  while (obj.hasOwnProperty(_fn)) {
    _fn = "fn" + Math.random(); // 循环判断并重新赋值
  }
  obj[_fn] = this;
  if(arguments.length>1){
    result = obj[_fn](...([...arguments].slice(1)));
  }else{
    result = obj[_fn]();
  }
  delete obj[_fn];
  return result;
}
复制代码

这样似乎完善了一些,但是还有一种情况需要考虑,即调用call方法没有传参的情况,或者第一个参数为null或者undefined

var name = 'window.name';
function foo(){
  console.log(this);
  console.log(this.name)
};
foo.call(); //  window window.name
foo.call(1); // Number {1}  undefined复制代码

通过上面的代码我们可以得出结论,如果调用call方法没有传入参数或者第一个参数为null或者undefined,会将函数的this绑定到window对象(注意如上测试代码,不可以使用let声明name哦),而如果传入的参数为基本数据类型,会将其转换为对象

继续完善后的call方法如下:

Function.prototype.call2 = function(obj){
  obj = obj?Object(obj):window;
  let _fn = "fn",result;
  while (obj.hasOwnProperty(_fn)) {
    _fn = "fn" + Math.random(); // 循环判断并重新赋值
  }
  obj[_fn] = this;
  if(arguments.length>1){
    result = obj[_fn](...([...arguments].slice(1)));
  }else{
    result = obj[_fn]();
  }
  delete obj[_fn];
  return result;
}
复制代码

而有了如上思考过程,实现apply就很简单了,代码如下:

Function.prototype.apply2 = function(obj,arr){
  obj = obj?Object(obj):window;
  let _fn = "fn",result;
  while (obj.hasOwnProperty(_fn)) {
    _fn = "fn" + Math.random(); // 循环判断并重新赋值
  }
  obj[_fn] = this;
  if(arr){
    result = obj[_fn](...arr);
  }else{
    result = obj[_fn]();
  }
  delete obj[_fn];
  return result;
}
复制代码

bind实现:

Function.prototype.bind2 = function(obj){
  obj = obj?Object(obj):window;
  var myArguments = arguments,self = this;
  if(arguments.length>1){
    return function(){
      self.apply(obj,[...myArguments].slice(1))
    };
  }else{
    return function(){
      self.apply(obj);
    };
  }
}
复制代码

bind的实现借用了apply方法,有兴趣的朋友可以尝试不借用apply实现bind

如果有错误或者不严谨的地方,请给予指正,十分感谢!


转载于:https://juejin.im/post/5c90d4525188256ee169bdea