Javascript:使用函数上下文与作为参数传递的好处是什么?
Javascript: What is the benefit of using function context vs passing as parameter
除了欺骗已经实现this
的现有函数之外,为什么你要写一个javascript函数,这样你就需要改变它的上下文(通过.call
或.apply
),而不是显式地传递"context"作为另一个参数?是否有性能上的好处?
的例子:
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);
}
在许多情况下,您可能希望使用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
编写函数,然后构造接受上下文作为参数的函数,要比使用其他方法容易得多。
据我所知,
this
的使用并没有什么不同另一个参数,它的存在方式更复杂修改。
我认为回答你的问题最简单的方法是想象一下,如果基础Javascript语言的创建者遵循了你的约定。
没有this
的世界
没有this
的世界是一个可怕的嘈杂的地方,有很多过度的重复:
var arr = [1,2,3,4];
arr.reverse(arr); //4321
有更多的机会产生误导或冗长的语法
var str = "stringtobesplit";
"abiglongstringnotbeingsplit".split(str,":");
String.prototype.split(str,":");
它并没有完全摆脱apply至少:
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
消失,apply至少也会有用处。把api写在异常情况下从来都不是一个好主意。如果您正在创建面向对象的代码,您应该使用this
作为一种简单的方法来引用作为调用上下文的对象。如果你写得很好,那么call和apply应该在特殊情况下很少使用。
TL;DR - this
被设计为Javascript的一部分是有原因的,当你在对象上创建方法时使用它,以获得更清晰易懂的语法。
当你做面向对象编程时,你的函数将依赖于上下文,把它作为参数提供是没有意义的,因为这会破坏面向对象编程的目的。
为回调提供隐式上下文也是有意义的。如果只需要上下文,则不必记住参数的正确顺序。在这种情况下,您根本不需要使用参数。所以不用
function mayCallback(param1, param2, context)
你可以直接写
function myCallback()
,如果不需要param1和param2,则使用this
。
为了解决我的主要目的——使用this
是否比函数参数有性能优势?,答案似乎是没有:
虽然在对象中使用参数值而不是实例(this
)变量似乎有一点好处(但是可能不是很明显)。
(请自己测试,如果有不同请评论)
关于其他答案的目的:@Aadit指出了一些整洁的用例,可维护性是有争议的个人偏好,但就像@ben336所说的,如果你正在使用对象(因此是OOP),那么this
可能更有用。
ECMAScript第5版的本机函数bind
可能是两个世界之间的一个有趣的桥梁,或者至少是一个耗时的探索。
上面引用的实例与参数值测试也可能是我的观点的一个很好的例子——如果你正在构建一个静态功能库,你可以"劫持"obj.callback2
通过作用域到不同的this
,或者只是直接调用obj.callback
在你的备用上下文中。
- 用javascript记录传递给回调的参数的约定是什么
- 几何位置的参数格式是什么
- 将一个函数及其参数传递给另一个函数以执行它的语法是什么
- 在本地将单个变量传递给具有多个参数的函数的最佳方法是什么
- 什么是将对象属性作为函数参数传递的良好策略/模式
- 在 Array.prototype 中传递“for each”循环的参数的语法是什么?
- Handlebars:向registerHelper函数传递多个参数的最佳方式是什么
- 传递给回调函数的“context”参数是什么
- 在Backbone.js中,通过在选项中传递模型来初始化视图和将模型对象作为参数传递有什么区别
- addEventListener传递给我的函数的参数是什么
- 在js的as3回调中传递参数的最佳方式是什么?
- 在使用选择器时传递函数参数的好方法是什么?
- Javascript:使用函数上下文与作为参数传递的好处是什么?
- 在javascript中,如果foo是一个函数,那么foo()和foo之间的区别是什么?当它们作为参数传递给高阶函数时,
- 通过Javascript传递带参数的变量:什么是undefined
- 在javascript中,整数、字符串和数组作为函数参数传递时有什么区别
- Javascript addEventListener不管函数的名称在第二个参数中是什么
- 什么'这是在JavaScript中将元素作为第一个参数传递给事件处理程序的最简单方法
- 作为第二个参数传递给链接函数的函数的目的是什么?
- 在JavaScript中将元素作为第一个参数传递给事件处理程序的最简单方法是什么?