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

时间:2013-03-10 04:26:41

标签: javascript function optimization functional-programming

除了欺骗已经实现this的现有函数之外,为什么要编写一个javascript函数,以便您需要更改其上下文(通过.call.apply)而不是明确地将“上下文”作为另一个参数传递?是否有性能优势?

示例:

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

4 个答案:

答案 0 :(得分:1)

  

据我所知,使用this并没有什么不同   另一个参数,它只是有一个更复杂的存在方式   修改。

我认为回答您问题的最简单方法是想象一下基本Javascript语言的创建者是否遵循了您的惯例。

没有this

的世界

没有this的世界是一个可怕的嘈杂的地方,有很多过多的重复:

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

更多误导或冗长语法的机会

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

至少它完全没有应用:

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

实际上,在创建仅全局函数或在原型或对象上创建函数之间可以做出选择,这些函数对它所接收的参数类型进行了假设,但没有强制执行这些假设。例如,想象一下我们幻想世界中的toString方法。

您可以创建一个全局toString方法,该方法将接收每种类型的对象,并尝试使它们全部工作,或者您可以在当前工作的每种类型的原型上具有一个函数,而不执行它将在该类型上调用。有人可以致电

Array.prototype.toString(str)

我们需要优雅地处理它(因为它的值得这样做,应用似乎恢复到Object.prototype.toString并返回[Object String])。因此我们需要确定在这些情况下调用的正确原型方法,这意味着我的猜测是约定会调用

str.toString(str) 

或类似的东西。

那么重点是什么?

this用于处理原型链上javascript方法的常见情况。它为我们提供了一个简写,允许对象在不重复调用它或必须确切知道它的原型是什么的情况下对自己进行操作。没有它,我们要么必须在对象上没有函数,要么每次都必须显式调用函数,引入额外的语法和潜在的错误。

callapply是例外情况,即使this消失,至少也会有用。将异常写入异常案例绝不是一个好主意。如果您正在创建面向对象的代码,则应使用this作为一种简单的方法来引用作为调用上下文的对象。如果你写得好,那么应该很少和特殊情况下使用call和apply。

TL; DR - this由于某种原因被设计为Javascript的一部分,当您在对象上创建方法时使用它以获得更清晰易懂的语法。

答案 1 :(得分:1)

在许多情况下,您可能希望使用this而不是传递额外参数。例如,考虑以下函数:

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

此函数允许我们按如下方式推迟函数调用:

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

您可以在此处查看演示:http://jsfiddle.net/AjwQu/

不是将函数绑定到this,而是将其作为参数传递:

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

我们现在就这样使用它:

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

结果相同:http://jsfiddle.net/63dBF/

但是要获得参数,我们必须使用[].slice.call(arguments, 1)。在第一个例子中,我们可以简单地使用arguments,因为函数不是参数列表的一部分。

一切都有它的优点和缺点。你只需要知道什么时候使用。希望这有点帮助。

加分:将使用this的函数转换为接受额外参数的函数非常容易,反之亦然。首先让我们定义一些实用函数:

var functProto = Function.prototype;

var bind = functProto.bind;

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

bindable函数允许您创建现有函数的可绑定版本,该函数在调用时返回绑定到给定参数的新函数。

callable函数允许您创建现有函数的可调用版本,该函数在调用时使用给定的参数和this指针调用现有函数。

appliable函数允许您创建现有函数的可应用版本,该函数在调用时将给定参数和this指针应用于现有函数。

然后给出第一个例子中的函数,我们可以在第二个例子中创建函数,如下所示:

var async = callable(functProto.async);

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

类似地,我们可以将第二个示例中的函数转换为第一个示例中的函数,如下所示:

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

请参阅此处的演示:http://jsfiddle.net/rJQyS/

正如您所看到的,使用this编写函数要容易得多,然后构造函数接受上下文作为参数,而不是相反。

答案 2 :(得分:0)

当你进行面向对象编程时,你的函数将取决于上下文,并且将它作为参数提供是没有意义的,因为这会破坏面向对象编程的目的。

为回调提供隐式上下文也是有意义的。如果只需要上下文,则不必记住参数的正确顺序。在这种情况下,您根本不必使用参数。而不是

function mayCallback(param1, param2, context)

你可以写

function myCallback()

并使用this,如果您不需要param1和param2。

答案 3 :(得分:-1)

为了解决我的主要目的 - 使用this对函数参数有什么性能优势? - 答案似乎是:< / p>

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

虽然there seems to be a slight benefit(但可能并不重要)围绕对象使用参数值而不是实例(this)变量。

(请自行测试并评论是否有所不同)

关于其他答案所解决的目的:有一些neat use cases as pointed out by @Aadit,可维护性是个人偏好,但like @ben336 said如果您正在使用对象(以及OOP)那么this可能更有用。

ECMAScript 5th-edition native function bind可能是两个世界之间的一个有趣的桥梁,或者至少是一个吸引时间的探索。

上面提到的instance vs parameter values test也可能是我的观点的一个很好的例子 - 如果你正在建立一个静态的功能库,你可以劫持&#34; obj.callback2通过范围限定到其他this,或直接在备用上下文中调用obj.callback