2016-08-20 21 views
-1

我正在學習函數式編程和node.js,當使用Function.prototype.apply.bind時,我遇到了這個奇怪的問題。函數參數不一定是對象嗎?

function Spy(target, method) { 
    var obj = {count: 0}; 
    var original = target[method] 
    target[method] = function(){//no specified arguments 
     obj.count++ 
     original.apply(this, arguments)//only arguments property passed 
    } 
    return obj; 
} 
module.exports = Spy 

此代碼正常工作,它成功偵測了target.method


//same code here 
    target[method] = function (args){//args specified 
     obj.count++ 
     original.apply(this, args)//and passed here 
    } 
//same code here 

此代碼,但是,其實不然。它給出了一個錯誤信息:TypeError: CreateListFromArrayLike called on non-object


然後最大的驚喜是,這種方法工作得很好。

//same code here 
    target[method] = function (args){ 
     obj.count++ 
     original.bind(this, args) 
    } 
//same code here 

那麼爲什麼我確切地得到這個錯誤呢?是否因爲函數參數不一定是對象?或者是因爲申請比綁定更嚴格?

+3

嗯,是的,['apply'](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/apply)和['bind'](https: //developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind)有很不同的描述。 'apply'將一個數組或一個['arguments'對象](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/arguments)作爲第二個參數 - 如果你不'它通過一個,它抱怨。 – Bergi

+0

非常不清楚。首先,作品/不工作代碼片段是相同的(除非我失去了一些東西)。其次,'bind'不會調用該函數,因此很難相信「有效」。 – Amit

+0

@非常抱歉,我要編輯,所以更明顯。 –

回答

1

在這個版本:

target[method] = function (args){//args specified 
     obj.count++ 
     original.apply(this, args)//and passed here 
    } 

在這裏你是不是把所有的論點,但只是一個人,名叫args。由於apply需要像對象這樣的數組,因爲它只是傳遞給原始目標的第一個參數,所以不能使用args

你可以把它改成:

target[method] = function (arg){ //only one argument specified 
     obj.count++ 
     original.apply(this,[arg]) //one argument passed here 
} 

現在它的工作原理,但你只能在一個參數的函數間諜。使用call會更好,因爲你只能有一個額外的參數:

target[method] = function (arg){ //only one argument specified 
     obj.count++ 
     original.call(this,arg) //one argument passed here 
} 

現在bind是一個完全不同的動物。它partial applies函數,從而返回函數。想象一下你需要發送一個不帶任何參數的回調函數,而是用一些參數來調用函數。你看到如下代碼:

var self = this; 
return function() { 
    self.method(a, b); 
} 

好吧。 bind爲您完成此:

return this.method.bind(this, a, b); 

當調用其中任一函數返回同樣的情況。調用方法method的參數爲ab。因此,在函數上調用bind會返回該函數的部分應用版本,並不會像callapply那樣調用它。

+0

綁定不會咖喱。如果有的話,它可以部分應用,但這不是它的主要目的。 – Bergi

+0

@Bergi你是對的部分應用程序是渴望語言的一個更好的術語,但我認爲你對'bind'的「主要」目的是錯誤的,因爲它正是它在'call'上下文中所做的。 – Sylwester

1

bind被稱爲與call相同的方式,即使他們做了非常不同的事情。

如果你真的想這樣使用bind。您可以使用傳播運營商(ES2015)的參數「數組」擴大到各個參數:

original.bind(null, ...args); 

將在original功能綁定數組值作爲單獨的參數。

+1

我不知道爲什麼你打擾使用切片時,你已經有傳播運算符。溼的猴子。 – Sylwester

+0

@Sylwester這是真的,我完全忘了你也可以在數組和類似數組的對象上使用spread運算符。使用前刪除轉換。 – Snivels