[英]Javascript Function Calls: Regular call vs Call vs Bind Call
我的問題很簡單:我將函數傳遞給其他函數以便稍后調用(示例回調函數),問題是何時,為什么以及最佳實踐是什么。
示例:我有xxx()函數,我必須傳遞它,正如我在window.onload事件中向您展示的那樣。
什么是最佳做法,為什么? 有任何性能方面或為什么我應該選擇使用call或bind來調用此函數
function xxx(text)
{
var div = document.createElement("div");
div.innerHTML = text + " - this: " + this.toString();
document.body.appendChild(div)
}
function callFunction(func)
{
func("callFunction");
}
function callUsingCall(func)
{
func.call(this, ["callUsingCall"]);
}
function callUsingBind(func)
{
func.call(this, ["callUsingCall"]);
}
window.onload = function(){
callFunction(xxx);
callUsingCall(xxx);
callUsingBind(xxx.bind(document));
}
謝謝,
塞巴斯蒂安P.
我不認為有任何“最好”的做法。
如果您正在呼叫的功能關心this
是什么,您可以使用call
。
如果要確保只能使用指定的this
值調用該函數,請使用bind
。
[兩者都有一些開銷,即至少一個函數調用/范圍的深度]
否則你只需要調用該函數。
簡單:)
在this
對象是該函數的上下文。 這就像你為你制造一台機器, this
物體就像機器一樣,就像你的房子一樣。 您可以隨意移動它。
我們有4種方法來設置this
對象。
調用不是方法的函數:
fn(someArguments)
這樣, this
對象設置為null或可能是窗口對象。
將函數作為方法調用:
someObject.fn(someArguments)
在這種情況下, this
對象將指向someObject
並且它是可變的。
通過call
或apply
函數的方法call
。
fn.call(anotherObject, someArguments)
someObject.call(anotherObject, someArguments)
someObject.apply(anotherObject, [someArguments])
在這種情況下, this
對象將指向someObject
。 在調用它時,你強迫它有另一個上下文。
綁定一個函數
var fn2 = fn.bind(anotherObject, someArguments)
這將創建一個綁定到另一個函數this
對象,我們把它( anotherObject
)。 無論你怎么稱呼它, this
對象都是一樣的。
現在你可以做一些棘手的事情了解這一點。 我們為什么在這里(我認為它首先來自C ++)的原因是對象的方法需要訪問它們的父對象。 在this
對象提供了訪問。
var coolObject = {
points : ['People are amazing'],
addPoint : function (p) { this.points.push(p) }
}
因此,如果您執行以下操作,則無效:
var addPoint = coolObject.addPoint;
addPoint('This will result in an error');
將拋出該錯誤,因為此對象不再是我們的coolObject
,並且沒有points屬性。 所以有時這樣,你可以這樣:
var addPoint = coolObject.addPoint;
addPoint.call({points : []}, 'This is pointless');
這是毫無意義的,但功能將起作用,即使this
對象不是它應該是的。
var anotherCoolObject = {
points : ['Im a thief!'],
addPoint : coolObject.addPoint
}
anotherCoolObject.addPoint('THIS IS CALL STEALING');
如果你這樣調用它,函數仍然有效,因為this
對象將指向另一個具有points
屬性的String對象。
我見過的最流行的用例是切片參數對象:
function returnHalf() {
return [].slice.call(arguments, 0, arguments.length / 2);
}
returnHalf('Half', 'is', 'not', 'awesome');
// >> [Half', 'is']
所以你看,arguments對象不是一個instanceof數組。 如果我們做arguments.slice(...)
那么你將被編譯器殺死。 但是這里我們在arguments對象上使用數組的方法,因為它的數組就像。
有時您不希望更改函數上下文,或者您想要添加自己的參數,您使用bind。
例如,當您使用jquery為事件添加偵聽器時,當jquery調用您的函數時,此對象將是該元素。 但有時你想做一些棘手的事情並改變它:
var myElement = {
init : function () {
$(this.element).click(this.listener.bind(this));
},
view : "<li>${Name}</li>",
name : 'ed',
element : $('#myelement'),
listener : function () {
this.element.append($.tmpl( this.view, this ));
}
}
myElement.init();
所以在這里,您將它綁定到myElement,這樣您就可以訪問對象屬性來呈現視圖。 另一個例子如下:
for (var i = 0; i < 10; i++) {
setTimeout(function () {console.log(i)}, 10)
}
// All of them will be 10.
for (var i = 0; i < 10; i++) {
setTimeout((function () {console.log(this.i)}).bind({ i : i }, 10)
}
如果你在一個循環中放入一個異步函數調用,那么在調用回調時,循環結束,並且計數器已經到了結尾,你可以使用bind將當前計數器干凈地綁定到你的回調。
另一個很好用的例子,我經常使用帶有參數的函數傳遞給async
模塊,而不創建閉包。
async.parallel({
writeFile : function (cb) {
fs.writeFile('lolz.txt', someData, cb);
},
writeFile2 : function (cb) {
fs.writeFile('lolz2.txt', someData, cb);
}
}, function (err){
console.log('finished')
});
async.parallel({
writeFile : fs.writeFile.bind(fs, 'lolz.txt', someData),
writeFile2 : fs.writeFile.bind(fs, 'lol2z.txt', someData),
}, function (err){
console.log('finished')
});
這兩個實現是相同的。
看看這些:
http://jsperf.com/bind-vs-call2
http://jsperf.com/js-bind-vs-closure/2
http://jsperf.com/call-vs-closure-to-pass-scope/10
bind
其他類型的調用相比, bind
具有很大的性能開銷,但請確保您不會因為使用預先成熟的優化而犧牲性能和可維護性。
您也可以查看這篇文章。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.