简体   繁体   English

Javascript函数调用:常规调用vs调用与绑定调用

[英]Javascript Function Calls: Regular call vs Call vs Bind Call

My question is simple: I'm passing a function to some other function to be call later (sample callback function), the question is when, why and what is the best practice to do it. 我的问题很简单:我将函数传递给其他函数以便稍后调用(示例回调函数),问题是何时,为什么以及最佳实践是什么。

Sample: I have the xxx() function, and I have to pass it, as I show you below in the window.onload event. 示例:我有xxx()函数,我必须传递它,正如我在window.onload事件中向您展示的那样。

What is the best practice and why? 什么是最佳做法,为什么? There is any performance aspect or why should I choose to use call or bind to call this function 有任何性能方面或为什么我应该选择使用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));
}

Thank you, 谢谢,

Sebastian P. 塞巴斯蒂安P.

I don't think there's any "best" practise. 我不认为有任何“最好”的做法。

You use call if the function you're calling cares what this is. 如果您正在呼叫的功能关心this是什么,您可以使用call

You use bind if you want to ensure that the function can only be called with the specified value of this . 如果要确保只能使用指定的this值调用该函数,请使用bind

[There's some overhead to both, ie at least one depth of function calls / scope] [两者都有一些开销,即至少一个函数调用/范围的深度]

Otherwise you just call the function. 否则你只需要调用该函数。

Simples :) 简单:)

The this object is the context of the function. this对象是该函数的上下文。 It's like you make a machine that something for you, and the this object would be the place that the machine works in, like your house. 这就像你为你制造一台机器, this物体就像机器一样,就像你的房子一样。 You can move it as you like. 您可以随意移动它。

We have 4 ways setting this objects. 我们有4种方法来设置this对象。

Calling the function that is not a method: 调用不是方法的函数:

fn(someArguments)

This way the this object is set to null or probably the window object. 这样, this对象设置为null或可能是窗口对象。

Calling the function as a method: 将函数作为方法调用:

someObject.fn(someArguments)

In this case the this object will point to someObject and it's mutable. 在这种情况下, this对象将指向someObject并且它是可变的。

Calling with call or apply methods of the function. 通过callapply函数的方法call

fn.call(anotherObject, someArguments)
someObject.call(anotherObject, someArguments)
someObject.apply(anotherObject, [someArguments])

In this case the this object will point to someObject here. 在这种情况下, this对象将指向someObject You are forcing it to have another context, when calling it. 在调用它时,你强迫它有另一个上下文。

Binding a the function 绑定一个函数

var fn2 = fn.bind(anotherObject, someArguments)

This will create another function that is binded to that this object we gave it( anotherObject ). 这将创建一个绑定到另一个函数this对象,我们把它( anotherObject )。 No matter how you call it, the this object is going to be the same. 无论你怎么称呼它, this对象都是一样的。

Use Cases 用例

Now you can do some tricky stuff knowing this. 现在你可以做一些棘手的事情了解这一点。 The reason that why we have it here(I think it came first from C++) is that methods of an object need to access to their parent. 我们为什么在这里(我认为它首先来自C ++)的原因是对象的方法需要访问它们的父对象。 The this object provides the access. this对象提供了访问。

var coolObject = {
  points : ['People are amazing'],
  addPoint : function (p) { this.points.push(p) }
}

So if you do the following it won't work: 因此,如果您执行以下操作,则无效:

var addPoint = coolObject.addPoint;
addPoint('This will result in an error');

The error will be thrown because the this object is not our coolObject anymore and doesn't have the points property. 将抛出该错误,因为此对象不再是我们的coolObject ,并且没有points属性。 So at times like this, you can something like this: 所以有时这样,你可以这样:

var addPoint = coolObject.addPoint;
addPoint.call({points : []}, 'This is pointless');

This is pointless, but the function will work, even the this object is not what its supposed to be. 这是毫无意义的,但功能将起作用,即使this对象不是它应该是的。

var anotherCoolObject = {
  points : ['Im a thief!'],
  addPoint : coolObject.addPoint
}
anotherCoolObject.addPoint('THIS IS CALL STEALING');

Still the function will work if you call it like that, since the this object will point to anotherCoolObject which has the points property. 如果你这样调用它,函数仍然有效,因为this对象将指向另一个具有points属性的String对象。

The most popular use case I've seen is slicing the arguments object: 我见过的最流行的用例是切片参数对象:

function returnHalf() {
  return [].slice.call(arguments, 0, arguments.length / 2);
}

returnHalf('Half', 'is', 'not', 'awesome');
// >> [Half', 'is']

So you see, arguments object is not an instanceof array. 所以你看,arguments对象不是一个instanceof数组。 If we do arguments.slice(...) then you're gonna be killed by the compiler. 如果我们做arguments.slice(...)那么你将被编译器杀死。 But here we use the array's method on arguments object, since it's array like. 但是这里我们在arguments对象上使用数组的方法,因为它的数组就像。

Sometimes you don't want your function context to be changed or you wanna add your own arguments, you use bind. 有时您不希望更改函数上下文,或者您想要添加自己的参数,您使用bind。

For example when you add a listener for an event with jquery, when jquery calls your function, the this object will be the element. 例如,当您使用jquery为事件添加侦听器时,当jquery调用您的函数时,此对象将是该元素。 But sometimes you wanna do tricky stuff and change it: 但有时你想做一些棘手的事情并改变它:

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();

So here, you bind it to the myElement, so you can have access to the object properties to render the view. 所以在这里,您将它绑定到myElement,这样您就可以访问对象属性来呈现视图。 Another examples would be the following: 另一个例子如下:

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)
}

If you have put an asynchronous function call in a loop, by the time the callback is called, the loop is finished, and the counter have reached the end, you can use bind to cleanly bind the current counter to your callback. 如果你在一个循环中放入一个异步函数调用,那么在调用回调时,循环结束,并且计数器已经到了结尾,你可以使用bind将当前计数器干净地绑定到你的回调。

Another good use case of it, that I use a lot is when passing my functions with arguments to async module, without creating closures. 另一个很好用的例子,我经常使用带有参数的函数传递给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')
});

These two implementations are identical. 这两个实现是相同的。

Performance 性能

Just check these out: 看看这些:

http://jsperf.com/bind-vs-call2 http://jsperf.com/bind-vs-call2

http://jsperf.com/js-bind-vs-closure/2 http://jsperf.com/js-bind-vs-closure/2

http://jsperf.com/call-vs-closure-to-pass-scope/10 http://jsperf.com/call-vs-closure-to-pass-scope/10

bind has a big performance overhead comparing to other types of calling, but make sure you don't sacrifice performance with maintainability with pre-mature optimizations. bind其他类型的调用相比, bind具有很大的性能开销,但请确保您不会因为使用预先成熟的优化而牺牲性能和可维护性。

Also you can have a look at this article. 您也可以查看这篇文章。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM