是否可以在不更改上下文的情况下调用function.apply?

Mat*_*ins 33 javascript function node.js

在一些Javascript代码(具体是node.js)中,我需要使用一组未知的参数调用函数而不更改上下文.例如:

function fn() {
    var args = Array.prototype.slice.call(arguments);
    otherFn.apply(this, args);
}
Run Code Online (Sandbox Code Playgroud)

上面的问题是,当我调用时apply,我通过传递this第一个参数来改变上下文.我想传递args给被调用的函数而不改变被调用函数的上下文.我基本上想要这样做:

function fn() {
    var args = Array.prototype.slice.call(arguments);
    otherFn.apply(<otherFn's original context>, args);
}
Run Code Online (Sandbox Code Playgroud)

编辑:添加有关我特定问题的更多详细信息.我正在创建一个Client类,其中包含一个socket(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]
Run Code Online (Sandbox Code Playgroud)

请注意,这clientListenerId是一个自定义唯一标识符属性,与此处的答案基本相同.

Sco*_*yet 7

' this' 对函数上下文的引用.这才是真正的重点.

如果你的意思是在这样的不同对象的上下文中调用它:

otherObj.otherFn(args)
Run Code Online (Sandbox Code Playgroud)

然后简单地将该对象替换为上下文:

otherObj.otherFn.apply(otherObj, args);
Run Code Online (Sandbox Code Playgroud)

那应该是它.

  • 问题是,在我访问`otherFn`时,我不知道它属于哪个对象.如果我只有对函数的引用,有没有办法确定它的当前绑定? (4认同)
  • 不.如果你有原始功能,你无法从中知道它来自哪里.它本来可以绑定任何东西,或者根本没有任何东西. (2认同)

Bri*_*hon 6

如果我理解正确的话:

                          changes context
                   |    n     |      y       |
accepts array    n |  func()  | func.call()  |
of arguments     y | ???????? | func.apply() |
Run Code Online (Sandbox Code Playgroud)

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 + ');');
}
Run Code Online (Sandbox Code Playgroud)

是的我知道.没有人喜欢eval().这是缓慢而危险的.但是,在这种情况下,您可能不必担心跨站点脚本,至少,因为所有变量都包含在函数中.实际上,JavaScript没有本机功能太糟糕了,但我想这就是我们拥有的这种情况eval.

证明它有效:

function showArgs() {
    for (x in arguments) {console.log(arguments[x]);}
}

showArgs.invoke(['foo',/bar/g]);
showArgs.invoke([window,[1,2,3]]);
Run Code Online (Sandbox Code Playgroud)

Firefox控制台输出:

--
[12:31:05.778] "foo"
[12:31:05.778] [object RegExp]
[12:31:05.778] [object Window]
[12:31:05.778] [object Array]
Run Code Online (Sandbox Code Playgroud)

  • 这是实际回答问题的唯一回应.其他人都认为OP要么_access_到上下文(他们没有),或者它没有上下文,或者他们只是不知道`Function.apply`(他们显然这样做) ).感谢你是唯一一个理解这个问题而且说不的人,没有像PHP的`call_user_func_array`那样的东西. (2认同)

Dav*_*ave 6

简单地说,只需将其指定为您想要的,即otherFn:

function fn() {
    var args = Array.prototype.slice.call(arguments);
    otherFn.apply(otherFn, args);
}
Run Code Online (Sandbox Code Playgroud)


小智 5

如果将函数绑定到对象,并且在任何地方都使用绑定函数,则可以使用 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"
Run Code Online (Sandbox Code Playgroud)