简体   繁体   English

Javascript:使用函数上下文与传递作为参数有什么好处

[英]Javascript: What is the benefit of using function context vs passing as parameter

Other than tricking existing functions that already implement this as something, why would you want to write a javascript function so that you need to alter its context (via .call or .apply ) rather than explicitly passing the "context" as another parameter? 除了欺骗已经实现现有功能this是什么东西,为什么你要这样,你需要改变它的上下文(通过写一个JavaScript函数.call.apply ),而不是明确地传递了“语境”作为另一个参数? Is there a performance benefit? 是否有性能优势?

Example: 例:

function tryIncrement(inc, context) {
    context = context || this; // just so we can reuse same fn for the example

    if( typeof context.val!= typeof 1|| typeof inc != typeof 1 ) return false;
    context.val += inc;
    return true;
}

var a = {name: 'A', val: 5}, b = {name: 'B', val: 20};

// reassign internal context
for(var i = 0, n = [1,3,"not a num",5]; i < n.length; i++) {
    if( tryIncrement.call(a, n[i]) ) console.log('incremented', i, n[i], a);
    else console.log('failed to increment', i, n[i], a);
}

// provide explicit context;
// could just as easily declared function so context was first param
// so it looked the same as previous implementation
for(var i = 0, n = [1,3,"not a num",5]; i < n.length; i++) {
    if( tryIncrement(n[i], b) ) console.log('incremented', i, n[i], b);
    else console.log('failed to increment', i, n[i], b);
}

As far as I can tell the use of this isn't really any different than another parameter, it just has a more complicated way of being modified. 至于我可以告诉使用this是不是真的比其他任何参数不同,它只是有一个被修改的更加复杂的方式。

I think the easiest way to answer your question is to imagine if the creator of the base Javascript language had followed your conventions. 我认为回答你问题的最简单方法是想象一下基础Javascript语言的创建者是否遵循了你的约定。

A world without this 没有this的世界

A world without this is a scary noisy place with lots of excessive duplication: 没有this的世界是一个可怕的嘈杂的地方,有很多过多的重复:

var arr = [1,2,3,4];
arr.reverse(arr); //4321

More opportunities for misleading or verbose syntax 更多误导或冗长语法的机会

var str = "stringtobesplit";
"abiglongstringnotbeingsplit".split(str,":");
String.prototype.split(str,":");

And its not at all rid of apply at least: 而且至少它完全没有应用:

Math.max.apply(arr);  //didn't add the initial `this` since it doesn't exist

Effectively there would be a choice between creating only global functions, or creating functions on prototypes or objects that made assumptions about the types of the arguments it was receiving but didn't enforce those assumptions. 实际上,可以选择仅创建全局函数,或者在原型或对象上创建函数,这些函数对它所接收的参数类型进行了假设,但没有强制执行这些假设。 For instance imagine the toString method in our fantasy world. 例如,想象一下我们幻想世界中的toString方法。

You could either create a global toString method which would take in an object of every type ever, and try to make them all work, or you could have a function on the prototypes of each type as it works currently, with no enforcement that it would be called on that type. 您可以创建一个全局toString方法,该方法将接收每种类型的对象,并尝试使它们全部工作,或者您可以在每种类型的原型上使用当前正常工作的函数,而不执行任何操作被称为那种类型。 Someone could call 有人可以打电话

Array.prototype.toString(str)

And we would need to handle it gracefully (for what its worth doing this with apply seems to revert to the Object.prototype.toString and returns [Object String] ). 我们需要优雅地处理它(因为它的值得做的事情似乎恢复到Object.prototype.toString并返回[Object String] )。 So we would need to identify the correct prototype method to call in those cases, which means my guess is that the convention would be to call 因此我们需要确定在这些情况下调用的正确原型方法,这意味着我的猜测是约定是调用

str.toString(str) 

or something along those lines. 或类似的规定。

So whats the point? 那么重点是什么?

this is built to handle the common case for javascript methods on the prototype chain. this是为了处理原型链上的javascript方法的常见情况而构建的。 It gives us a shorthand to allow an object to act on itself without duplicating the call to it or having to know exactly what its prototype is. 它为我们提供了一个简写,允许对象在不重复调用它或必须确切知道它的原型是什么的情况下对自己进行操作。 Without it, we would either have to have no functions on objects, or would have to explicitly call the function on itself every time, introducing extra syntax and potential errors. 没有它,我们要么必须在对象上没有函数,要么每次都必须显式调用函数,引入额外的语法和潜在的错误。

call and apply are the exception cases, and apply at least would have uses even if this went away. callapply是例外情况,并且至少应用即使this消失也会有用。 Its never a good idea to write your apis to the exception cases. 将异常写入异常案例绝不是一个好主意。 If you're creating object oriented code, you should use this as an easy way to refer to the object that is the context for the call. 如果您正在创建面向对象的代码,那么您应该使用this作为一种简单的方法来引用作为调用上下文的对象。 If you write this well, then call and apply should be used rarely and in special situations. 如果你写得好,那么应该很少和特殊情况下使用call和apply。

TL;DR - this was designed as part of Javascript for a reason, use it when you're creating methods on objects for more clear and understandable syntax. TL; DR - this被设计成一个理由的Javascript的一部分,用它当你创建的对象的更加清晰和易于理解的语法的方法。

There are many cases where you may wish to use this instead of passing an extra parameter. 在许多情况下,您可能希望使用this而不是传递额外的参数。 Consider the following function for example: 例如,考虑以下函数:

Function.prototype.async = function () {
    setTimeout.bind(null, this, 0).apply(null, arguments);
};

This function allows us to defer a function call as follows: 此函数允许我们按如下方式推迟函数调用:

alert.async("This will display later.");
alert("This will display first.");

You can see the demo here: http://jsfiddle.net/AjwQu/ 你可以在这里看到演示: http//jsfiddle.net/AjwQu/

Instead of binding the function to this we could have passed it as a parameter instead: 我们可以将它作为参数传递而不是将函数绑定this

function async(funct) {
    setTimeout.bind(null, funct, 0).apply(null, [].slice.call(arguments, 1));
}

We would use it like this now: 我们现在就这样使用它:

async(alert, "This will display later.");
alert("This will display first.");

The result is the same: http://jsfiddle.net/63dBF/ 结果是一样的: http//jsfiddle.net/63dBF/

However to get the arguments we have to use [].slice.call(arguments, 1) instead. 但是要获取参数,我们必须使用[].slice.call(arguments, 1) In the first example we could simply use arguments as the function was not a part of the argument list. 在第一个例子中,我们可以简单地使用arguments因为函数不是参数列表的一部分。

Everything has it's advantages and disadvantages. 一切都有它的优点和缺点。 You just need to know what to use when. 你只需要知道什么时候使用。 Hope this helps a bit. 希望这个对你有帮助。

Bonus: It's really easy to convert a function that uses this into a function that accepts an extra parameter and vice versa. 额外:将使用this函数的函数转换为接受额外参数的函数非常容易,反之亦然。 First let's define a few utility functions: 首先让我们定义一些实用函数:

var functProto = Function.prototype;

var bind = functProto.bind;

var bindable = bind.bind(bind);
var callable = bindable(functProto.call);
var appliable = bindable(functProto.apply);

The bindable function allows you to create a bindable version of an existing function which when called returns a new function bound to the given arguments. bindable函数允许您创建现有函数的可绑定版本,该函数在调用时返回绑定到给定参数的新函数。

The callable function allows you to create a callable version of an existing function which when called calls the existing function with the given arguments and this pointer. callable函数允许您创建现有函数的可调用版本,该函数在调用时使用给定参数和this指针调用现有函数。

The appliable function allows you to create an appliable version of an existing function which when called applies the given arguments and this pointer to the existing function. appliable功能允许您创建一个现有的函数调用时应用给定参数的appliable版本, this指针指向现有的功能。

Then given the function in the first example we can create the function in the second example as follows: 然后给出第一个例子中的函数,我们可以在第二个例子中创建函数,如下所示:

var async = callable(functProto.async);

See the demo here: http://jsfiddle.net/3dSBS/ 请参阅此处的演示: http//jsfiddle.net/3dSBS/

Similarly we can convert the function in the second example into the function in the first example as follows: 类似地,我们可以将第二个示例中的函数转换为第一个示例中的函数,如下所示:

Function.prototype.async = function () {
    return async.apply(null, [this].concat([].slice.call(arguments)));
};

See the demo here: http://jsfiddle.net/rJQyS/ 请参阅此处的演示: http//jsfiddle.net/rJQyS/

As you can see it's much easier to write a function using this and then construct the function accepting the context as a parameter from it than the other way around. 正如你可以看到它更容易使用编写一个函数this ,然后构造函数接受上下文从它比周围的其他方法的参数。

When you do object oriented programming your functions WILL depend on the context and it does not make sense do provide it as a parameter, as this would deafeat the purpose of object oriented programming. 当你进行面向对象的编程时,你的函数将取决于上下文,并且将它作为参数提供是没有意义的,因为这会破坏面向对象编程的目的。

It also makes sense to provide an implicit context for callbacks. 为回调提供隐式上下文也是有意义的。 You do not have to remember the correct order of the parameters if you only need the context. 如果只需要上下文,则不必记住参数的正确顺序。 You would not have to use parameters at all in that case. 在这种情况下,您根本不必使用参数。 So instead of 而不是

function mayCallback(param1, param2, context)

you could just write 你可以写

function myCallback()

and use this , if you do not need param1 and param2. 如果你不需要param1和param2,请使用this

To address my main purpose -- is there a performance benefit using this over a function parameter? 为了解决我的主要目的 - 在函数参数上使用this有一个性能优势吗? -- the answer seems to be no : - 答案似乎是否定的

http://jsperf.com/function-context-vs-parameter http://jsperf.com/function-context-vs-parameter

Although there seems to be a slight benefit (may not be significant, however) around using parameter values instead of instance ( this ) variables within objects. 虽然在对象中使用参数值而不是实例( this )变量似乎有一点点好处 (但可能并不重要)。

(Please test for yourself and comment if it's different) (请自己测试并评论是否有所不同)

Regarding the purpose being addressed by the other answers: there are some neat use cases as pointed out by @Aadit , maintainability is debatably a personal preference, but like @ben336 said if you're working with Objects (and thus OOP) then this can be more useful. 关于其他答案所解决的目的: @Aadit指出有一些整洁的用例 ,可维护性是个人偏好,但是像@ ben336所说,如果你正在使用Objects(以及OOP)那么this可以更有用。

The ECMAScript 5th-edition native function bind may be an interesting bridge between the two worlds, or at least a time-sucking tangent to explore. ECMAScript第5版本机函数bind可能是两个世界之间的一个有趣的桥梁,或至少是一个时间吸引切线探索。

The instance vs parameter values test referenced above may also be a good example of my point -- if you're building a static library of functionality, you can "hijack" obj.callback2 by scoping to a different this , or just call obj.callback directly on your alternate context. 实例VS参数值测试上面提到的也可能是我的观点的一个很好的例子-如果你正在构建的功能的静态库,你可以“劫持” obj.callback2的范围界定为不同的this ,或者只是调用obj.callback直接在备用上下文中obj.callback

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

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