【SH手寫系列】實現一個call apply bind

首先我要先搞懂這三個方法怎麼用數組

1. call( )
語法app

fun.call(thisArg, arg1, arg2, ...)

第一個參數理解爲要在哪裏執行fun(運行時指定的this值,他不必定是該函數執行時真正的this值,若是這個函數處於非嚴格模式下,則指定爲null和undefined的this值會自動指向全局對象),後面的一串參數意思是執行fun時的傳參函數

返回值是你調用的方法(fun)的返回值,若該方法沒有返回值,則返回undefinedthis

實現call開始prototype

// 首先沒有給一個傳入參數形式context
Function.prototype._call(context){
    // 就要判斷實際調用有沒有這個參數
    let context = context ? context : window
    // 此時這個context就替代了上面的target
    context.func = this
    let result = context.func(args)   
    // 拿參數這步省略了,無錯,按照我本身的寫法這樣也能夠
    /*
    let [target, ...args] = [...arguments]
    判斷了傳入的目標對象是null等狀況,再賦值window實際也能成功
    if(target == null || undefined){
        target = window;
    }
    */
    return result
    // 別忘記刪除方法,這句寫在return前面
    delete context.func
}

// 最終我本身不看答案寫出的結果以下,實測能夠 不知道有沒有潛在問題

Function.prototype._call = function(){
    // let cont = context ? context : window
    let [target, ...args] = [...arguments]

    if(target == null || undefined){
        target = window;
    }
    target.fn = this
    let result = target.fn(...args)

    delete target.fn
    return result
}

2.apply( )
語法指針

fun.apply(obj,args)

第一個參數仍然理解爲要在哪裏執行fun,第二個參數是一個數組。code

兩種方法意思是同樣的,只是傳參方式不一樣。call後面能夠給不少,而apply就只能給一個數組,在實際狀況中按需使用吧。(我今天看懂繼承再看看能多說點什麼,還有試一下apply的返回是否是同樣)對象

實現apply開始繼承

Function.prototype._apply = function(){
    // 一切和call都差很少
   let [target, ...args] = [...arguments]
   
   // 考慮不傳參 做用域的狀況
   if(target == null || undefined){
        target = window
   }
   target.fn = this
   let result
   // 考慮參數存在狀況。
   if(args){
    // 這裏踩坑了,實際調用過程仍是會把數組轉化成非數組的 要加上...
        result = target.fn(...args)
   }else{
        result = target.fn()
   }
   
   delete target.fn
   return result
   
   // 有了call作鋪墊基本還行,主要是判斷args是否存在,還有參數是 數組的問題
}

3.bind( ) ip

bind 是返回新的函數,以便稍後調用;apply 、call 則是當即調用原函數。也就是說 要定義一個新的變量 把bind返回的方法賦予給這個變量再去執行

語法

fun.bind(thisArg[, arg1[, arg2[, ...]]])

第一個參數我已經能夠理解了,這裏有一個新特性,就是若是bind返回的函數以new的形式去調用,第一個參數會被忽略,this仍然指向被調用的函數(fun)

arg1, arg2, … (可選)當綁定函數被調用時,要將這些參數(若是有的話)做爲bind()的參數寫在this後面。當綁定函數被調用時,這些參數會被插入到目標函數的參數列表的開始位置,傳遞給綁定函數的參數會跟在它們後面。
也就是說bind能夠實現預設一些函數參數的功能(有啥用)

如下有兩個例子來表達上面兩點,搜來的:

function original(x){
  this.a=1;
  this.b =function(){return this.a + x}
}
var obj={
  a:10
}
var  newObj = original.bind(obj,2) //傳入了一個實參2
var newObjChild = new newObj()
console.log(newObj.a)  //輸出 1, 說明返回的函數用做構造函數時obj(this的值)被忽略了

console.log(newObj.b()) //輸出3 ,說明傳入的實參2傳入了原函數original

var sum = function(x,y) { return x + y }; 

var succ = sum.bind(null, 1); //讓this指向null,其後的實參也會做爲實參傳入被綁定的函數sum

succ(2) // => 3:  能夠看到1綁定到了sum函數中的x

另外的,bind和函數柯里化好像有點關係,一下子回家討論下

實現bind開始
bind好難我本身寫不出來,只好一遍一遍看了

Function.prototype._bind = function(context){
    if(typeof this !== 'function'){
        throw new TypeError('被綁定的對象須要是函數')
    }
    var self = this   // 保存起原指針(做用域)

    var args = [].slice.call(arguments, 1)  
    console.log(arguments)  
    // =====> bind被調用時的傳入參數

    fBound = function(){ //  this instanceof fBound === true時,說明返回的fBound被當作new的構造函數調用

        console.log(arguments)  
        // =====> bind返回的函數fBound被調用時傳入的參數

        return self.apply(this instanceof fBound ? this : context, args.concat([].slice.call(arguments)))
        /* 若是被Fbound被用做了構造函數,
        構造出的實例必定是Fbound類型,
        若是沒有,那麼當前的this和Fbound沒有任何關係。
        參數爲bind被調用時和Fbound被調用時的參數聯合 */
    }

    // -----------------------------------------------
    
    var func = function(){}    // 這個究竟是幹嗎的??
    //維護原型關係
    console.log(this.prototype)   // 一個constructor指向原函數
    console.log(func.prototype)    // 什麼都沒有
    if(this.prototype){
        func.prototype = this.prototype
    }
    //使fBound.prototype是func的實例,返回的fBound若做爲new的構造函數,新對象的__proto__就是func的實例
    fBound.prototype = new func()
    
    // ------------------------------框住的我先不看了 不能理解

    return fBound
}

留一個最新的MDN的polyfill

Function.prototype.bind = function() {
    var thatFunc = this, thatArg = arguments[0];
    var args = slice.call(arguments, 1);
    if (typeof thatFunc !== 'function') {
      // closest thing possible to the ECMAScript 5
      // internal IsCallable function
      throw new TypeError('Function.prototype.bind - ' +
             'what is trying to be bound is not callable');
    }
    return function(){
      var funcArgs = args.concat(slice.call(arguments))
      return thatFunc.apply(thatArg, funcArgs);
    };
  };
相關文章
相關標籤/搜索