手写bind,call,apply

179 阅读1分钟

apply

Function.prototype.myApply = function(context, args) {
  if (context == null) {
    //排除context==''的情况
    context = window;
  } else if (typeof context != "object") {
    context = Object(context); //// 值为原始值(数字,字符串,布尔值)的 this 会指向该原始值的实例对象
  }
  //   给context新增一个独一无二的属性以免覆盖原有属性
  const key = Symbol();
  context[key] = this; //这里的this是testFun
  /* 
  context={
      ....context,
      Symbol():this
  }
  =>context={
      ...context:
      fun:this=>fun:function(){}
  }
  context[key]=>context[Symbol]=>context.Symbol()=>context.fun()
  */
  const result = context[key](...args);
  //带走产生的副作用
  delete context[key]; //要删除给context新增的属性key
  return result;
};

call

Function.prototype.myCall = function(context, ...args) {
  if (context == null) {
    context = window;
  }
  let key = Symbol();
  context[key] = this;
  let result = context[key](...args);
  delete context[key];
  return result;
};

bind

Function.prototype.myBind = function(objThis, ...args) {
  const _this = this;
  let newBind = function(...paras) {
    const isNew = this instanceof newBind; // this是否是newBind的实例,也就是返回的newBind是否通过new调用
    const context = isNew ? this : Object(objThis); //new调用的就绑定到this上,否则就绑定到传入的objThis上
    return _this.call(context, ...args, ...paras);
  };
  if (this.prototype) {
    newBind.prototype = Object.create(this.prototype);
  }

  return newBind;
};

测试代码


var name = 'window'

function testFun(...args){
    console.log(this.name,...args);
}

const testObj = {
    name:'testObj'
}

const test = testFun.myBind(testObj);
const t = new test(); //undefined
console.log(t.name); //undefined
function Animal(name, color) {
  this.name = name;
  this.color = color;
}

Animal.prototype.say = function() {
  return `I'm a ${this.color} ${this.name}`;
};

const Cat = Animal.bind(null, "cat");
const cat = new Cat("white");

if (
  cat.say() === "I'm a white cat" &&
  cat instanceof Cat &&
  cat instanceof Animal
) {
  console.log("success");
}

参考链接
【THE LAST TIME】this:call、apply、bind