是否可以在不更改上下文的情况下调用函数.apply
Is it possible to call function.apply without changing the context?
在某些Javascript代码(特别是node.js(中,我需要在不更改上下文的情况下调用具有未知参数集的函数。 例如:
function fn() {
var args = Array.prototype.slice.call(arguments);
otherFn.apply(this, args);
}
上面的问题是,当我调用apply
时,我通过将this
作为第一个参数来更改上下文。 我想在不更改被调用函数的上下文的情况下args
传递给正在调用的函数。 我本质上想这样做:
function fn() {
var args = Array.prototype.slice.call(arguments);
otherFn.apply(<otherFn's original context>, args);
}
编辑:添加有关我的特定问题的更多详细信息。 我正在创建一个客户端类,其中包含一个套接字 (socket.io( 对象以及与连接相关的其他信息。 我通过客户端对象本身公开套接字的事件侦听器。
class Client
constructor: (socket) ->
@socket = socket
@avatar = socket.handshake.avatar
@listeners = {}
addListener: (name, handler) ->
@listeners[name] ||= {}
@listeners[name][handler.clientListenerId] = wrapper = =>
# append client object as the first argument before passing to handler
args = Array.prototype.slice.call(arguments)
args.unshift(this)
handler.apply(this, args) # <---- HANDLER'S CONTEXT IS CHANGING HERE :(
@socket.addListener(name, wrapper)
removeListener: (name, handler) ->
try
obj = @listeners[name]
@socket.removeListener(obj[handler.clientListenerId])
delete obj[handler.clientListenerId]
请注意,clientListenerId
是一个自定义唯一标识符属性,与此处找到的答案基本相同。
如果我理解正确的话:
changes context
| n | y |
accepts array n | func() | func.call() |
of arguments y | ???????? | func.apply() |
PHP 有一个函数,call_user_func_array
。不幸的是,JavaScript在这方面是缺乏的。看起来您使用 eval()
模拟此行为。
Function.prototype.invoke = function(args) {
var i, code = 'this(';
for (i=0; i<args.length; i++) {
if (i) { code += ',' }
code += 'args[' + i + ']';
}
eval(code + ');');
}
是的,我明白。没有人喜欢eval()
.它缓慢而危险。但是,在这种情况下,您可能不必担心跨站点脚本,至少,因为所有变量都包含在函数中。真的,JavaScript 没有本机功能太糟糕了,但我想我们eval
这种情况。
证明它有效:
function showArgs() {
for (x in arguments) {console.log(arguments[x]);}
}
showArgs.invoke(['foo',/bar/g]);
showArgs.invoke([window,[1,2,3]]);
火狐控制台输出:
--
[12:31:05.778] "foo"
[12:31:05.778] [object RegExp]
[12:31:05.778] [object Window]
[12:31:05.778] [object Array]
简单地说,只需将 this 分配给您想要的内容,即 otherFn:
function fn() {
var args = Array.prototype.slice.call(arguments);
otherFn.apply(otherFn, args);
}
this
' 是对函数上下文的引用。 这才是真正的重点。
如果您打算在类似这样的不同对象的上下文中调用它:
otherObj.otherFn(args)
然后只需将该对象替换为上下文:
otherObj.otherFn.apply(otherObj, args);
应该就是这样了。
函数绑定到对象,并且在任何地方都使用绑定函数,则可以使用 null 调用 apply,但仍会获得正确的上下文
var Person = function(name){
this.name = name;
}
Person.prototype.printName = function(){
console.log("Name: " + this.name);
}
var bob = new Person("Bob");
bob.printName.apply(null); //window.name
bob.printName.bind(bob).apply(null); //"Bob"
调用函数时 JavaScript 中可能发生的上下文更改的一种方法是,如果需要它们能够在this
不表示父对象的上下文中操作,则使用作为对象构造函数一部分的方法,方法是有效地创建一个本地私有变量来存储原始this
标识符。
我承认 - 就像JavaScript中大多数关于范围的讨论一样 - 这并不完全清楚,所以这里有一个例子来说明我是如何做到这一点的:
function CounterType()
{
var counter=1;
var self=this; // 'self' will now be visible to all
var incrementCount = function()
{
// it doesn't matter that 'this' has changed because 'self' now points to CounterType()
self.counter++;
};
}
function SecondaryType()
{
var myCounter = new CounterType();
console.log("First Counter : "+myCounter.counter); // 0
myCounter.incrementCount.apply(this);
console.log("Second Counter: "+myCounter.counter); // 1
}
这些天你可以使用 rest 参数:
function fn(...args) {
otherFn(...args);
}
唯一的缺点是,如果你想在fn
中使用一些特定的参数,你必须从args
中提取它:
function fn(...args) {
let importantParam = args[2]; //third param
// ...
otherFn(...args);
}
下面是一个可以尝试的示例(ES 下一个版本保持简短(:
// a one-line "sum any number of arguments" function
const sum = (...args) => args.reduce((sum, value) => sum + value);
// a "proxy" function to test:
var pass = (...args) => sum(...args);
console.log(pass(1, 2, 15));
我不会接受这个答案,因为我仍然希望有更合适的东西。 但这是我根据到目前为止对这个问题的反馈使用的方法。
对于任何将调用Client.prototype.addListener
或Client.prototype.removeListener
的类,我确实在其构造函数中添加了以下代码:
class ExampleClass
constructor: ->
# ...
for name, fn of this
this[name] = fn.bind(this) if typeof(fn) == 'function'
message: (recipient, body) ->
# ...
broadcast: (body) ->
# ...
在上面的示例中,message
和 broadcast
在实例化时将始终绑定到新的 ExampleClass
原型对象,从而允许我原始问题中的addListener
代码工作。
我相信你们中的一些人想知道为什么我不做以下事情:
example = new ExampleClass
client.addListener('message', example.bind(example))
# ...
client.removeListener('message', example.bind(example))
问题是每次调用.bind( )
时,它都是一个新对象。 因此,这意味着以下内容是正确的:
example.bind(example) != example.bind(example)
因此,removeListener
永远不会成功工作,因此我在实例化对象时绑定方法一次。
由于您似乎希望使用 Javascript 1.8.5 中定义的 bind
函数,并且能够检索您传递绑定函数的原始this
对象,因此我建议重新定义 Function.prototype.bind
函数:
Function.prototype.bind = function (oThis) {
if (typeof this !== "function") {
throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");
}
var aArgs = Array.prototype.slice.call(arguments, 1),
fToBind = this,
fNOP = function () {},
fBound = function () {
return fToBind.apply(this instanceof fNOP && oThis
? this
: oThis,
aArgs.concat(Array.prototype.slice.call(arguments)));
};
fNOP.prototype = this.prototype;
fBound.prototype = new fNOP();
/** here's the additional code **/
fBound.getContext = function() {
return oThis;
};
/**/
return fBound;
};
现在,您可以检索调用bind
函数的原始上下文:
function A() {
return this.foo+' '+this.bar;
}
var HelloWorld = A.bind({
foo: 'hello',
bar: 'world',
});
HelloWorld(); // returns "hello world";
HelloWorld.getContext(); // returns {foo:"hello", bar:"world"};
很久以后,我才想起这个问题。 现在回想起来,我认为我在这里真正想要完成的是类似于 React 库与其自动绑定的工作方式。
本质上,每个函数都是一个被调用的包装绑定函数:
function SomeClass() {
};
SomeClass.prototype.whoami = function () {
return this;
};
SomeClass.createInstance = function () {
var obj = new SomeClass();
for (var fn in obj) {
if (typeof obj[fn] == 'function') {
var original = obj[fn];
obj[fn] = function () {
return original.apply(obj, arguments);
};
}
}
return obj;
};
var instance = SomeClass.createInstance();
instance.whoami() == instance; // true
instance.whoami.apply(null) == instance; // true
只需将属性直接推送到函数的对象,并使用它自己的"上下文"调用它。
function otherFn() {
console.log(this.foo+' '+this.bar); // prints: "hello world" when called from rootFn()
}
otherFn.foo = 'hello';
otherFn.bar = 'world';
function rootFn() {
// by the way, unless you are removing or adding elements to 'arguments',
// just pass the arguments object directly instead of casting it to Array
otherFn.apply(otherFn, arguments);
}
- 是否可以在不更改上下文的情况下调用函数.apply
- Ajax:只在元素存在的情况下调用Ajax
- 可以“;超级“;可以在子类的方法内部使用,在不直接引用的情况下调用相应的超类方法
- typeaheadjs:如何在不使用缓存数据的情况下调用服务器
- 如何在没有 AJAX 的情况下调用上一页
- 是否可以在不加对象名称前缀的情况下调用对象的函数
- 谷歌应用程序脚本:如何在没有用户交互的情况下调用绑定到处理程序的函数
- Knockout:可以在没有参数的情况下调用可观察的扩展器
- Kinetic.js / Javascript:在不使用 eval() 的情况下调用变量作为属性
- 函数不't在添加括号的情况下调用它时运行(即使它不需要参数)
- 如何在不加载调用的页面的情况下调用 URL
- .js文件在不respond_to的情况下调用
- 在从服务器端 c# 使用 AJAX 进行部分渲染的情况下调用 javascript 函数
- 我可以在不使用 AJAX 的情况下调用 servlet URL 吗?
- 为什么我无法在以下情况下调用 javascript AJAX 函数
- 如何在没有 OnClick 事件的情况下调用 JavaScript 方法
- 在不使用.pipe()的情况下调用gulp-notify
- 如何在不加载的情况下调用函数
- Chrome JavaScript 开发者控制台:是否可以在没有换行符的情况下调用 console.log()
- 在打开 jQuery 的情况下调用多个函数.(“咔嚓”)