手写一个call、apply、bind

136 阅读2分钟

公共函数

function isFunction (fn) {
  return typeof fn === 'function'
}

let obj = {}

function fn (name, age) {
  this.name = name;
  this.age = age;
}

call

Function.prototype.myCall = function (obj, ...arg) {
  if (!isFunction(this)) return;

  const fn =  Symbol('fn');
  obj = Object(obj);
  obj[fn] = this;
  const result = obj[fn](...arg);
  delete obj[fn];
  return result
}

Function.prototype.call = function(target, ...arg) {
  // 分析,我们平时使用的话,是obj1.fn.call(obj2, ...arg);
  // 那么执行call方法内部的this 就是obj1.fn,所以一定要是一个函数,而且就是我们要执行的函数
  // this === obj1.fn ; this 必须是个函数
  if (!this instanceof Function) {
    throw new Error('不是个函数')
  }
  let fn = Symbol("fn"); // 防止变量相同,导致函数重写
  target = new Object(target) // 防止target不是一个引用类型
  target[fn] = this; // 使fn存在target对象上,方便引用
  const result = target[fn](...arg); // 执行fn函数,this指向target,且缓存执行后的结果
  Reflect.deleteProperty(target, fn) // es6优化写法
  // delete target[fn]; // 删除fn属性,防止改变target对象
  return result; // 返回结果
}

apply

Function.prototype.myApply = function(obj, argArray) {
  if (!isFunction(this)) return;
  if (Array.isArray(argArray)) {
    return this.myCall(obj, ...argArray);
  } else {
    return;
  }
}

Function.prototype.apply = function(target, argArray) {
  // 分析,我们平时使用的话,是obj1.fn.apply(obj2, argArray);
  // 那么执行call方法内部的this 就是obj1.fn,所以一定要是一个函数,而且就是我们要执行的函数
  // this === obj1.fn ; this 必须是个函数
  if (!this instanceof Function) {
    throw new Error('不是个函数')
  }
  if (Array.isArray(argArray)) {
    return this.call(target, ...argArray)
  } else {
    throw new Error('apply的第二个参数必须是个数组')
  }
}

bind

Function.prototype.myBind = function (obj, ...arg) {
  if (!isFunction(this)) return;
  
  let bound,
    self = this;

  bound = function (...argB) {
    let _self = this instanceof self ? this : obj;
    return self.apply(_self, [...arg, ...argB]);
  }

  bound.prototype = Object.create(self.prototype);
  bound.constructor = self;
  return bound;
}

Function.prototype.bind = function(target, ...arg1) {
  // 分析,我们平时使用的话,是fn = obj1.fn.bind(obj2, ...arg1); 
  // fn(...arg2)
  // 执行的是obj1.fn函数,this指向的是obj2
  // 所以第一步需要缓存obj1.fn函数,也就是bind函数内部的this;
  // 第二步,把执行时的this指向到obj2上
  // 判断this是不是function
  if (!this instanceof Function) {
    throw new Error('不是个函数')
  }
  // 内部的this会重写外部的this,所以用self缓存this;
  let self = this; // 缓存 obj1.fn函数

  const bound = function(...arg2) {
    const _self =  this instanceof self ? this : target;
    return self.apply(_self, [...arg1, ...arg2]);
  }

  bound.prototype = Object.create(self.prototype);
  bound.constructor = self;

  return 
}

测试

fn.myCall(obj,  'swg', 26);
// 多轮测试
// fn.myApply(obj, [], 'swg', 26);
// fn.myApply(obj,  ['swg', 26]);
console.log(obj)