js中bind的實現


眾所周知,bind、call、apply都是用來改變this指向的,其中bind是返回一個函數,你需要再次手動調用。

舉例:

var obj = {
        a: 1,
        func: function() {
            console.log(this.a)
        }
    }

// 需要在最后加個括號手動執行func方法
obj.func.bind({a:2})()  // 2

實現方式很簡單:

Function.prototype.bind = function(context){
  // 取出bind方法中傳入的除第一個參數(第一個參數是需要綁定的this)外的其余參數存在args數組中
  var args = Array.prototype.slice.call(arguments, 1),
  // 這里的this是指調用bind方法的函數
  self = this;
  return function(){
      // 獲取執行bind函數傳入的參數
      var innerArgs = Array.prototype.slice.call(arguments);
      // 將第二個括號中的參數concat進args得到除第一個參數外所有傳入的參數(這里有兩個知識點:1、因為閉包args參數的值一直存在在內存中;2、偏函數(和函數柯里化相似但有點不同))
      var finalArgs = args.concat(innerArgs);
      // 調用apply方法,return函數結果
      return self.apply(context,finalArgs);
  };
};

想必上面的實現代碼大家都能看懂,我們再看一個構造函數調用bind后執行的結果:

function Point(x, y) {
  this.x = x;
  this.y = y;
}

Point.prototype.toString = function() { 
  return this.x + ',' + this.y; 
};

var YAxisPoint = Point.bind(null, 0);   // 第1行
var axisPoint = new YAxisPoint(5);  // 第2行
axisPoint.toString(); // '0,5'   第3行

axisPoint instanceof Point; // true    第4行
axisPoint instanceof YAxisPoint; // true    第5行
new Point(17, 42) instanceof YAxisPoint; // true    第6行

其中,第5行代碼不難理解,因為axisPoint是YAxisPoint new出來的對象,理所當然是YAxisPoint的實例。

但是第4行axisPoint也是Point的實例,那就說明YAxisPoint的原型和Point的原型是繼承關系或者說他們的原型指向同一個原型。

再看第6行,Point的實例指向YAxisPoint(即第4行和第6行有點相互牽制的意思),所以說明YAxisPoint的原型和Point的原型指向同一個原型,因為如果是繼承關系的話,第4行和第6行總有一個會是false。

最終實現:

Function.prototype.bind = function (context) {
        // 如果調用bind的不是一個function類型,直接報錯,不再向下執行
        if (typeof this !== "function") {
            throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");
        }
        var args = Array.prototype.slice.call(arguments, 1),
        self = this,
        bound = function () {
            // 這里和上面一版實現不同的是,apply的this指向做了一點改動,如果是構造函數調用,那么apply傳入的第一個參數會被忽略,其余參數仍然可用(這里為什么這么寫,其實沒有太明白)
            return self.apply(
                this instanceof self ? this : context || window,
                args.concat(Array.prototype.slice.call(arguments))
            );
        };
        // 針對bind調用的函數是構造函數的場景,通過上面分析,調用bind的構造函數的原型和bind返回的函數的原型指向同一個原型,即將this.prototype賦值給bound.prototype
        bound.prototype = this.prototype;
        return bound;
    }; 

參考文章:

https://www.cnblogs.com/heshan1992/p/6667596.html

偏函數與柯里化區別:

  柯里化是將一個多參數函數轉換成多個單參數函數,也就是將一個 n 元函數轉換成 n 個一元函數。

  偏函數則是固定一個函數的一個或者多個參數,也就是將一個 n 元函數轉換成一個 n - x 元函數。

       n元就是n個參數

 

文章如有理解錯誤之處,不吝賜教~


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM