2011-08-30 50 views
9

我正在写一些Actionscript3代码,试图将方法应用于在运行时确定的对象。 Function.applyFunction.call的AS3文档都指出这些函数的第一个参数是在执行函数时将用作“this”值的对象。Function.apply不使用thisArg参数

但是,我发现在所有情况下,当被执行的函数是一个方法时,不会使用应用/调用的第一个参数,而'this'总是指该方法所绑定的原始对象。下面是一些示例代码和其输出:

package 
{ 
    import flash.display.Sprite;  
    public class FunctionApplyTest extends Sprite 
    { 
     public function FunctionApplyTest() 
     { 
      var objA:MyObj = new MyObj("A"); 
      var objB:MyObj = new MyObj("B"); 

      objA.sayName(); 
      objB.sayName(); 

      objA.sayName.apply(objB, []); 
      objA.sayName.call(objB); 
     } 
    } 
} 

internal class MyObj 
{ 
    private var _name:String; 
    public function MyObj(name:String) 
    { 
     _name = name; 
    } 
    public function sayName():void 
    { 
     trace(_name); 
    } 
} 

输出:

A 
B 
A 
A 

A小调修改上面的代码来创建一种在线匿名功能,其是指“这个”表明正确当正在应用/调用的函数不是绑定方法时,会发生行为。

我是否在使用方法时使用apply/call不正确?在AS3文件明确规定的代码针对这种情况,但是:

myObject.myMethod.call(myOtherObject, 1, 2, 3); 

如果这确实是坏了,有没有变通除了使目标的方法进入功能(这将是相当难看,在我看来)?

+0

如果你需要这种编码,我将不得不说你正在做的事情非常错误。你究竟想通过这样做来完成什么? –

+0

对我进行了测试。看起来像一个错误。尝试在JIRA中搜索:http://bugs.adobe.com/jira/browse如果您没有找到它,请将其提交 – divillysausages

+0

它不是语言错误,但是文档写得不好 –

回答

17

它不是一个“错误”,但callapply的文档非常具有误导性,并且在解释发生了什么事情方面做得并不好。所以这里是对正在发生的事情的解释。

Methods与ActionScript中的Functions不同。 Methods被定义为类定义的一部分,并且方法总是绑定到该实例。请参阅方法第二个this link。从那里引用:

方法是作为类定义一部分的函数。一旦创建了类的实例,方法就会绑定到该实例。与在类外部声明的函数不同,除了它所连接的实例外,方法不能使用。

因此,当您制作newMyObj的实例时,其所有方法都绑定到该实例。这就是为什么当你尝试使用callapply时,你没有看到this被覆盖。详情请参阅Bound Methods一节。

请参阅:this document有关特性对象的解释,其中actionscript用于解决方法并用于幕后的性能原因可能是责任。这或类方法是以下的ECMAScript模式只是语法糖:

var TestClass = function(data) { 
    var self = this; 
    this.data = data; 
    this.boundWork = function() { 
     return self.constructor.prototype.unboundWork.apply(self, arguments); 
    }; 
}; 

TestClass.prototype.unboundWork = function() { 
    return this.data; 
}; 

然后:

var a = new TestClass("a"); 
var b = new TestClass("b"); 

alert(a.boundWork()); // a 
alert(b.boundWork()); // b 

alert(a.unboundWork()); // a 
alert(b.unboundWork()); // b 

alert(a.boundWork.call(b)); // a 
alert(a.boundWork.call(undefined)); // a 

alert(a.unboundWork.call(b)); // b 

或更有趣:

var method = a.unboundWork; 
method() // undefined. ACK! 

Vs的:

method = a.boundWork; 
method() // a. TADA MAGIC! 

请注意0123无论您通过thiscall还是apply传递,将始终在其所属实例的上下文中执行。其中,在ActionScript中,这种行为正是类方法绑定到其实例的原因。所以无论他们在哪里使用,他们仍然指向他们来自的实例(这使得动作事件模型更加“理智”)。一旦你理解了这一点,那么解决方法应该变得明显。

对于想要做某些魔术的地方,避免使用基于ActionScript 3的硬绑定方法来支持原型函数。

例如,考虑以下ActionScript代码:

package 
{ 
    import flash.display.Sprite;  
    public class FunctionApplyTest extends Sprite 
    { 
     public function FunctionApplyTest() 
     { 
      var objA:MyObj = new MyObj("A"); 
      var objB:MyObj = new MyObj("B"); 

      objA.sayName(); 
      objB.sayName(); 

      objA.sayName.apply(objB, []); // a 
      objA.sayName.call(objB); // a 

      objA.pSayName.call(objB) // b <--- 
     } 
    } 
} 

internal dynamic class MyObj 
{ 
    private var _name:String; 
    public function MyObj(name:String) 
    { 
     _name = name; 
    } 
    public function sayName():void 
    { 
     trace(_name); 
    } 

    prototype.pSayName = function():void { 
     trace(this._name); 
    }; 
} 

通知sayNamepSayName之间的声明差。 sayName将始终绑定到为其创建的实例。 pSayName是一个函数,可用于MyObj的实例,但不绑定到它的特定实例。

callapply文档是技术上是正确的,只要你所谈论的原型functions,而不是类methods,我不认为它提到的。

+0

非常感谢您解释这一点,这非常有启发性!我认为方法声明可能不是您的第一个代码示例的语法糖,因为如果这是真的,那么对于每个未绑定的方法,ClassType.prototype都会具有属性。对我来说,这似乎是两全其美,与Python的机制有些类似,objA.sayName()是MyObj.sayName(objA)的语法糖。 –

1

哇,这是非常令人惊讶的吴

测试它在我的身边也并试图在参数传递,以及在所有情况下,通过thisArg似乎并没有在所有使用(明确好像给我一个错误)。

我不得不使用类似的东西,但有额外的约束需要访问该方法而不创建对象的实例(这可能在其他语言但不在AS3> <)。所以我最终创建了静态函数,并通过我自己的“thisArg”代替。

因此使得静态函数,而不是一个可能的解决方法:

static public function SayName(thisArg : MyObj) : void 
{ 
    trace(thisArg._name); 
} 

不是最伟大的,因为你很可能会落得像这样加倍码>。 <

另外,如果方法是公开的,可以改为保存函数的函数的名称和访问它的方法,通过做这样的事情:

var funcName : String = "sayName"; 
objB[funcName].apply(null, []); 
objB[funcName].call(null); 

然而,这是有限取决于范围的方法(公共方法可以像这样从任何地方使用,与你的类在同一个包中的内部方法和从类内部的私有方法)。所以它比使用方法的实际Function实例更具有限制性,它可以在任何地方使用。

这似乎是一个非常讨厌的错误O.o我希望别人有更好的解决方案。

1

您是否明确尝试过实际使用this引用,即:

internal class MyObj 
{ 
    private var _name:String; 
    public function MyObj(name:String) 
    { 
     _name = name; 
    } 
    public function sayName():void 
    { 
     trace(this._name); 
    } 
} 

这可能只是省略this关键字时,原始实例是用来查找的字段/变量,而什么thisArg确实是对this关键字重新绑定。如果是这种情况,它最好是神秘的,但它可能值得一试。

+0

+1'thisArg' does not re-绑定'这个'。我发现'Array'文档提供了一些[类似示例](http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/Array.html#filter%28%29)行为。 – NoobsArePeople2

+0

是的,我也尝试明确使用'this'关键字,不幸的是发现了相同的行为。上面链接的Array.filter的文档明确指出,如果回调函数是一个方法,'thisArg'参数必须为null,所以过滤器的实现者似乎也遇到了这个问题。 –