JavaScript專題之模擬實現bind

本文共 1100 字,讀完只需 4 分鐘

概述

前一篇文章咱們嘗試模擬實現了 call 和 apply 方法,其實 bind 函數也能夠用來改變 this 的指向。bind 和 call和 apply 二者的區別在於,bind 會返回一個被改變了 this 指向的函數。javascript

本文介紹如何模擬實現 bind 函數: java

首先觀察 bind 函數有什麼特色:數組

var person = {
    name: 'jayChou'
}

function say(age, sex) {
    console.log(this.name, age, sex);
}

var foo = say.bind(person, '男', 39);

foo();  // jayChou 男 39
  1. 返回一個函數
  2. 函數參數以逗號的形式傳入
  3. 改變了 this 的指向

1、call 簡單實現

既然 bind 內部也要用改變 this 指向,咱們能夠用現成的 call 函數來實現這一功能。app

Function.prototype.newBind = function(context) {
    if(typeof this !== 'function') {
        throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable');
    }
    var self = this;
    return function () {
        return self.call(context)
      }
}

說明一下:
if 判斷是爲了校驗,只能讓函數來調用此方法,並拋出錯誤。
第二個 return 是爲了讓返回的函數有返回值的功能。函數

測試一下:測試

var person = {
    name: 'jayChou'
};

var say = function() {
    console.log(this.name);
}

var foo = say.newBind(person);
foo();  // jayChou

成功啦。this

2、傳遞參數

剛纔的函數是沒有傳遞參數,固然不行,因此咱們想辦法把函數的參數也傳遞進去。 spa

bind 函數有個特色,就是在綁定的時候能夠傳參,返回的函數還能夠繼續傳參。prototype

var person = {
    name: 'jayChou'
};

var say = function(p1, p2) {
    console.log(this.name, p1, p2);
}

var foo = say.bind(person, 18);
foo(20);  // jayChou 18 20

還能夠寫成:code

say.bind(person, 18)(20); // jayChou 18 20

好,進入正題,考慮傳參的事。在前面的文章,咱們講過 arguments 類數組對象的一些特性,不能直接調用數組的方法,可是能夠用原型方法間接來調用,咱們採用 apply 的方式來傳遞參數。

Function.prototype.newBind = function(context) {
    if(typeof this !== 'function') {
        throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable');
    }
    var self = this;
    var args = Array.prototype.slice.call(arguments, 1);  // 間接調用數組方法,獲取第一次傳的參數
    
    return function () {
        var innerArgs = Array.prototype.slice.call(arguments);
        return self.apply(context, args.concat(innerArgs))
      }
}


var person = {
    name: 'jayChou'
};

var say = function(p1, p2) {
    console.log(this.name, p1, p2);
}

var foo = say.newBind(person, 18);  // 第一次傳參
foo(20);  // 第二次傳參

最後輸出:

jayChou 18 20

結果正確,以上就完成了 bind 函數基本功能的實現。

可是!

3、做爲構造函數時?

bind 函數其實還有一個很是重要的特色:

bind返回的函數若是做爲構造函數,搭配new關鍵字出現的話,咱們的綁定this就須要「被忽略」。

意思就是指:當使用 nuw 關鍵字把 bind 返回的函數做爲構造函數,以前改變了指向的 this 就失效了。返回的函數的 this 就關聯到了構造函數的實例對象上。

針對這個特色,須要再作一些修改:

Function.prototype.newBind = function(context) {
    if(typeof this !== 'function') {
        throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable');
    }
    var self = this;
    var args = Array.prototype.slice.call(arguments, 1);  // 間接調用數組方法,獲取第一次傳的參數
    
    let tempFn = function {};  // 利用一個空函數做爲中轉
    
    tempFn.prototype = this.prototype;  // 修改返回函數的 prototype 爲綁定函數的 prototype,實例就能夠繼承綁定函數的原型中的值
    
    var resultFn = function () {
        var innerArgs = Array.prototype.slice.call(arguments);
        if (this instanceof tempFn) {  // 若是 返回函數被當作構造函數後,生成的對象是 tempFn 的實例,此時應該將 this 的指向指向建立的實例。
            return self.apply(this, args.concat(innerArgs));
        } else {
            return self.apply(context, args.concat(innerArgs))
        }
      }
      
    resultFn = new tempFn();
    return resultFn;
}

總結

本文嘗試模擬實現了 bind 函數,bind 函數與 call,apply 函數的區別在於,bind 函數返回一個指定了 this 的函數,函數並未執行。其次,當返回的函數做爲構造函數時,以前綁定的 this 會失效。

歡迎關注個人我的公衆號「謝南波」,專一分享原創文章。

相關文章
相關標籤/搜索