手写call、apply、bind函数

2,855 阅读1分钟

call函数

  • 特点:
    • 1)可以改变我们当前函数的this指向
    • 2)还会让当前函数执行
Function.prototype.call = function (context) {
 if (typeof this !== 'function') {
    throw new TypeError(`${this} is not a function`)
  }
  context = Object(context) || window;
  context.fn = this;
  let args = [];
  for (let i = 1; i < arguments.length; i++) {
    args.push('arguments['+i+']'); 
  }
  let r = eval('context.fn('+args+')');
  delete context.fn;
  return r;
}

题目自测

function fn1() {
  console.log(this,arguments);
  console.log(1);
  
}
function fn2() {
  console.log(this,arguments);
  console.log(2);
}
fn1.call(fn2,1,2); // fn2 [1,2] 1
fn1.call.call.call.call.call(fn2,1,2); // {number:1} [2] 2
  • 思路解析
    • fn1.call(fn2,1,2)
      • 1)call执行传入fn2,1,2三个参数
      • 2)call函数内部context = Object(fn2) = fn2
      • 3)fn2.fn = fn1
      • 4)args=['arguments[1]','arguments[2]']=[1,2]
      • 5)eval('context.fn('+args+')') = fn2.fn(1,2) = fn2.fn1(1,2)
    • fn1.call.call.call.call.call(fn2,1,2)
      • 1)call执行传入fn2,1,2三个参数
      • 2)call函数内部context = Object(fn2) = fn2
      • 3)fn2.fn = call
      • 4)args=['arguments[1]',arguments[2]]=[1,2]
      • 5)eval('context.fn('+args+')') = fn2.fn(1,2) = fn2.call(1,2)
      • 6)call执行传入1,2两个参数
      • 7)call函数内部context = Object(1) = Number{1}
      • 8)Number{1}.fn = fn2
      • 9)args=['arguments[1]']=[1]
      • 10)eval('context.fn('+args+')') = Number{1}.fn(2) = Number{1}.fn2(2)
      • 注:多次调用call的时候其实是call执行的时候内部又调用了一次call,总共调用两次

apply函数

  • 特点:
    • 1)可以改变我们当前函数的this指向
    • 2)还会让当前函数执行
Function.prototype.apply = function (context,args) {
  if (typeof this !== 'function') {
     throw new TypeError(`${this} is not a function`)
   }
   context = Object(context) || window;
   context.fn = this;
   if(!args){
    return context.fn();
   }
   let r = eval('context.fn('+args+')');
   delete context.fn;
   return r;
 }

new操作符

  • 特点
    • 新生成了对象
    • 链接到原型
    • 绑定this
    • 返回一个对象
/*
 *基本使用 
 */
function Animal(type) {
  this.type = type ; 
}
Animal.prototype.say = function () {
  console.log('say');
}
let tiger = new Animal('tiger');
console.log(tiger);
--------------------------------------------------------
/*
 *方法实现
 */
function mockNew(){
    let Constructor = [].shift.call(arguments);
    let obj = {}; // 新生成了对象
    obj.__proto__ = Constructor.prototype; // 链接到原型
    let r = Constructor.apply(obj,arguments) // 绑定`this`
    return r instance Object ? r : obj; //返回一个对象
}
let tiger = mockNew(Animal,'tiger');
console.log(tiger);

bind函数

  • 特点
    • 绑定this指向
    • 返回一个绑定后的函数(高阶函数原理)
    • 如果绑定的函数被new执行 ,当前函数的this就是当前的实例
    • new出来的结果可以找到原有类的原型
Function.prototype.bind = function (context) {
  if (typeof this !== 'function') {
    throw new TypeError(`${this} is not a function`)
  }
  let that = this;
  let bindArgs = Array.prototype.slice.call(arguments, 1);
  function Fn() { };
  function bindFn() {
    let args = Array.prototype.slice.call(arguments);
    /*
     * 绑定`this`指向
     * 如果绑定的函数被`new`执行 ,当前函数的`this`就是当前的实例
     */
    that.apply(this instanceof bindFn ? this : context, bindArgs.concat(args)); 
  }
  /*`new`出来的结果可以找到原有类的原型*/
  Fn.prototype = that.prototype; 
  bindFn.prototype = new Fn();
  /*返回一个绑定后的函数*/
  return bindFn; 
}