看完這篇文章還不會call/apply/bind的來找我。

先從一個小題目開始吧:javascript

要實現一個加法函數,這個時候向函數當中傳遞個數大於0的若干個整形數據,求全部這些數據的和。java

  • Function.prototype.callsegmentfault

  • Function.prototype.apply數組

  • Function.prototype.bindapp

其中call方法:異步

var personA = {
        name: 'XL',
        sayName: function (hobby){
            console.log(this.name + ' likes ' + hobby);
        } 
    };
    
    personA.sayName('basketball');   // 'XL likes basketball'
    
    var personB = {
        name: 'xl'
    }
    
    personA.sayName.call(personB, 'basketball');  // 'xl likes basketball'
    personA.sayName.apply(personB, ['basketball']); // 'xl likes basketball'

call和apply的區別就在於傳遞方式的不一樣,call在接收指定參數的形式是someMethod.call(obj, arg1, arg2);而apply在接收指定參數時的形式是someMethod.apply(obj, [arg1, arg2]).或者someMethod.apply(obj, arg1),可是這個arg1必須是一個類數組對象函數

其實想要真正掌握call/apply包括bind方法,首先必須搞清楚當一個函數/方法被調用的時候this的指向問題。 關於this的指向的問題請參照個人學習筆記學習

那麼在這裏call,apply,bind事實上都改變了函數/方法被調用時this的指向。this

仍是拿上面的例子來講:prototype

personA.sayName(‘basketball’); //調用sayName()這個方法的對象是personA,所以sayName()內部的this指向就是personA對象

換一種寫法

var sayName = personA.sayName('basketball');
    //這裏將sayName方法掛載到了window對象上,即window.sayName = person.sayName();  這個時候調用sayName().此時this指向就是window對象

使用call/apply

personA.sayName.call(personB, 'basketball');
    //原本sayName方法的this指向是personA對象,可是調用call後,this對象指向了personB對象。

若是你們這種寫法看不習慣,那就換種方式來看:

personA.sayName.call(personB, 'basketball') ===> personB.sayName('basketball');
    //從前面的一個形式變爲後面一種形式,此時,sayName方法的this指向是personB對象了。

換一種方式書寫後你們應該看的很清晰明瞭了吧?之後碰到call/apply調用的時候,換一種形式去理解,這樣就很清晰了。

再好比你們常常看到的一種對於函數的arguments類數組對象的處理方式:

function fn() {
        var args = Array.prototype.slice.apply(arguments); //這裏將arguments這個類數組對象轉化爲一個數組 
    }
    
    //我們再來轉化下:
Array.prototype.slice.apply(arguments); ===>>> arguments.slice(); 
//由於arguments是類數組對象的緣由,所以它能夠直接調用slice方法;若是要截取數組的從第幾位到第幾位的數

Array.prototype.slice.apply(arguments, [0, 2]); ===>>> arguments.slice(0, 2);

握草,感受編不下去了- -

其實將call/apply,換一種形式去看,是否是就和普通的方法調用同樣同樣的。

bind方法呢,起的做用和call,apply同樣,都是改變函數/方法執行時,this的指向,確保這個函數/方法運行時this指向保持一致。
好比你們常常用到的setTimeout異步函數:

var person = {
        name: 'XL',
        sayName: function() {
            setTimeout(function() {
                console.log(this.name);
            }, 0);
        }
    }
    
    person.sayName();   //最後輸出: undefined

這是由於setTimeout()這個異步函數調用的時候,內部的回調函數this的指向是window.可是在window對象上並未掛載name屬性,所以最後輸出undefined.

添加一行代碼

var name = 'XLLLL';
    var person = {
        name: 'XL',
        sayName: function() {
            setTimeout(function() {
                console.log(this.name);
            }, 0);
        }
    }
    
    person.sayName();   //輸出  ‘XLLLL’

爲了不在回調函數當中,this指向發生變化,因此你們都會這樣處理:

var person = {
        name: 'XL',
        sayName: function() {
            setTimeout(function() {
                console.log(this.name);
            }.bind(this), 0);   //經過bind方法將this對象綁定爲person。那麼回調函數在執行的時候,this指向仍是person。
        }
    }

能夠用下面這段代碼來簡單模擬下bind方法內部的操做:

Function.prototype.bind = function(obj) {
        var method = this;
        return function() {
            method.apply(obj, arguments);
        }
    }

還記得剛纔給你們講的將apply進行變換的形式嗎?

Function.prototype.bind = function(obj) {
        var method = this;
        return function() {
            obj.method(arguments);
        }
    }

你們應該看到了bindapply/call的區別了吧? bind方法是返回一個新的函數,可是這個函數比較特殊,這個函數的this對象已經被bind方法傳入的第一個參數給綁定了.

好比咱們可使用bind方法來簡寫一個方法:

function fn() {
        var hasOwnKey = Function.call.bind(Object.hasOwnProperty);
        
        for(var key in obj) {
            if(hasOwnKey(obj, key)) {
                //xxxx
            }
        }
    }

唉,真的編不下去了。你們看完以後應該已經懂了把? - -

仍是不懂的話在評論區留言,我給你們解答。

哦,一開始那個題目的一種寫法

//要實現一個加法函數,這個時候向函數當中傳遞個數大於0的若干個整形數據,求全部這些數據的和。
    function add() {
        return Array.prototype.reduce.call(arguments, function(n1, n2) {
            return n1 + n2;
        });   
    }
相關文章
相關標籤/搜索