手動封裝es5中 bind apply call

1.call(不傳參版本)es6

// 函數原型上添加 myCall方法 來模擬call
Function.prototype.myCall = function(obj){
  if(obj === null || obj === undefined){
    obj = window;
  }else{
    obj = object(obj);
  }
//咱們要讓傳入的obj成爲, 函數調用時的this值. obj._fn_ = this; //在obj上添加_fn_屬性,值是this(要調用此方法的那個函數對象)。 val = obj._fn_(...arg); //不能直接return obj._fn_(...arg) 這樣就不delete屬性了 obj._fn_(); //在obj上調用函數,那函數的this值就是obj. delete obj._fn_; // 再刪除obj的_fn_屬性,去除影響. //_fn_ 只是個屬性名 你能夠隨意起名,可是要注意可能會覆蓋obj上原本就有的屬性 return val }

 

若是要傳參的話能夠借用es6裏面的拓展運算符 (...)(省略了其餘的判斷)數組

Function.prototype.myCall = function(obj,...arg){
    obj._fn_ = this;
    obj._fn_(...arg);
    delete obj._fn_;
}

測試一下app

let test = {
    name:'test'
}
let o = {
    name:'o',
    fn:function(){
        console.log(this.name, ...arguments);  //這裏把參數顯示一下
    }
}
o.fn(1,2,3) // "o" 1 2 3
o.fn.call(test,1,2,3) // "test" 1 2 3
o.fn.myCall(test,1,2,3) // "test" 1 2 3

不用es6能夠用eval函數

Function.prototype.myCall = function(obj){
    let arg = [];
   let val;
for(let i = 1 ; i<arguments.length ; i++){ arg.push( 'arguments[' + i + ']' ) ; // 這裏要push 這行字符串 而不是直接push 值 // 由於直接push值會致使一些問題 // 例如: push一個數組 [1,2,3] // 在下面👇 eval調用時,進行字符串拼接,JS爲了將數組轉換爲字符串 , // 會去調用數組的toString()方法,變爲 '1,2,3' 就不是一個數組了,至關因而3個參數. // 而push這行字符串,eval方法,運行代碼會自動去arguments裏獲取值 } obj._fn_ = this; val = eval( 'obj._fn_(' + arg + ')' ) // 字符串拼接,JS會調用arg數組的toString()方法,這樣就傳入了全部參數 delete obj._fn_;
   return val }
//測試 let test = { name:'test' } let o = { name:'o', fn:function(){ console.log(this.name, ...arguments); //這裏把參數顯示一下 } } o.fn(1,['a','b'],3) // "o" 1 ["a","b"] 3 o.fn.call(test,1,['a','b'],3) // "test" 1 ["a","b"] 3 o.fn.myCall(test,1,['a','b'],3) // "test" 1 ["a","b"] 3

2.apply(用上面的mycall版本)測試

// ES6
Function.prototype.myApply = function(obj,arr){
    let args = [];
    for(let i = 0 ; i<arr.length; i++){
        args.push( arr[i] );
    }
    // 其實直接 ...arr 傳參也能夠 可是效果就和aplly有微小差異了
    return this.myCall(obj, ...args);
}
// eval
Function.prototype.myApply = function(obj,arr){
    let args = [];
    for(let i = 0 ; i<arr.length; i++){
        args.push( 'arr[' + i + ']' );  // 這裏也是push 字符串
    }
    return eval( 'this.myCall(obj,' + args + ')' );
}

不用mycall版本this

Function.prototype.myApply = function(obj,arr){
    let args = [];
    let val ;
    for(let i = 0 ; i<arr.length ; i++){
        args.push( 'arr[' + i + ']' ) ;
    }
    obj._fn_ = this;
    val = eval( 'obj._fn_(' + args + ')' ) 
    delete obj._fn_;
    return val
}

3.bind(借用apply,call)es5

 Function.prototype.bind = function(son){
        var that = this;
        var parent = Array.prototype.slice.call(arguments);//具備length屬性的對象轉成數組
        return function() {
            return that.apply(son, parent.slice(1));
        }
    }

es5spa

Function.prototype.myBind = function(obj){
    let _this = this;
    let argArr = [];
    let arg1 = [];
    for(let i = 1 ; i<arguments.length ; i++){ // 從1開始 
        arg1.push( arguments[i] ); // 這裏用arg1數組收集下參數
        // 獲取arguments是從1開始, 但arg1要從 0(i-1)開始
        // 如果用Array.prototype.slice.call(argument)就方便多了
        argArr.push( 'arg1[' + (i - 1)  + ']' ) ; // 若是用arguments在返回的函數裏運行 會獲取不到這個函數裏的參數了
    }
    return function(){
        let val ;
        for(let i = 0 ; i<arguments.length ; i++){ // 從0開始
            argArr.push( 'arguments[' + i + ']' ) ;
        }
        obj._fn_ = _this;
        val = eval( 'obj._fn_(' + argArr + ')' ) ;
        delete obj._fn_;
        return val
    };
}

 

es6prototype

Function.prototype.myBind = function(obj,...arg1){
    return (...arg2) => { 
        let args = arg1.concat(arg2);
        let val ;
        obj._fn_ = this;
        val = obj._fn_( ...args ); 
        delete obj._fn_;
        return val
    }
}
相關文章
相關標籤/搜索