3

除了欺骗已经实现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

4 回答 4

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)

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

str.toString(str) 

或类似的规定。

那么重点是什么?

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

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

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

于 2013-03-10T04:29:23.767 回答
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正如您所看到的,使用然后构造函数接受上下文作为参数,而不是相反,编写函数要容易得多。

于 2013-03-10T13:15:39.733 回答
0

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

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

function mayCallback(param1, param2, context)

你可以写

function myCallback()

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

于 2013-03-10T09:20:23.403 回答
-1

为了解决我的主要目的——使用函数参数是否有性能优势?this——答案似乎是否定的:

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

尽管在对象中使用参数值而不是实例 ( ) 变量似乎有一点好处(但可能并不显着) 。this

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

关于其他答案所解决的目的: @Aadit 指出有一些简洁的用例,可维护性可能是个人偏好,但就像@ben336 所说,如果您正在使用对象(因此是 OOP),那么this可以更有用。

ECMAScript第5 版本机函数bind可能是两个世界之间有趣的桥梁,或者至少是一个需要探索的耗时切线。

上面引用的实例与参数值测试也可能是我观点的一个很好的例子——如果你正在构建一个静态的功能库,你可以obj.callback2通过将范围限定为不同的“劫持” this,或者直接调用obj.callback你的替代上下文。

于 2013-03-19T18:03:49.550 回答