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

手写call、apply、bind函数

程序员文章站 2022-03-08 09:20:10
...

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; 
}
复制代码

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