簡體   English   中英

Javascript函數調用:常規調用vs調用與綁定調用

[英]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並且它是可變的。

通過callapply函數的方法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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM