手寫call、apply、bind函數

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; 
}
複製代碼
相關文章
相關標籤/搜索