我正在编写一些ActionScript3代码,试图将方法应用于在运行时确定的对象。 Function.apply和Function.call的AS3文档都指出这些函数的第一个参数是在执行函数时将用作'this'值的对象。
但是,我发现在所有情况下,当正在执行的函数是一个方法时,不使用apply / call的第一个参数,'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
对上述代码进行微小修改以创建引用“this”的内联匿名函数,表明当应用/调用的函数不是绑定方法时,会发生正确的行为。
当我尝试在方法上使用时,我是否使用了apply / call错误?但是,AS3文档专门为此案例提供了代码:
myObject.myMethod.call(myOtherObject, 1, 2, 3);
如果这确实被打破了,除了将目标方法变成函数之外还有其他解决方法(在我看来这会非常难看)吗?
答案 0 :(得分:17)
它不是一个“错误”,但call
和apply
的文档非常具有误导性,并且在解释最新情况时并没有做得很好。所以这里是对正在发生的事情的解释。
Methods
与ActionScript中的Functions
不同。 Methods
被定义为类定义的一部分,方法始终绑定到该实例。请参阅this link的第二个方法。引用那里:
方法是属于类定义的函数。创建类的实例后,方法将绑定到该实例。与在类外声明的函数不同,方法不能与它所附加的实例分开使用。
因此,当您创建new
MyObj
实例时,其所有方法都绑定到该实例。这就是为什么当您尝试使用call
或apply
时,您没有看到this
被覆盖。有关详细信息,请参阅Bound Methods部分。
有关 traits对象的解释,请参阅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!
请注意boundWork
将始终在其所属的实例的上下文中执行,无论您使用this
或call
传递给apply
的内容。在ActionScript中,这种行为正是将类方法绑定到其实例的原因。因此,无论它们在何处使用,它们仍然指向它们来自的实例(这使得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);
};
}
请注意sayName
和pSayName
之间的声明差异。 sayName
将始终绑定到为其创建的实例。 pSayName
是一个可用于MyObj
实例的函数,但不绑定到它的特定实例。
call
和apply
的文档在技术上是正确的,只要您谈论的是原型functions
而不是类methods
,我不认为它提到了。
答案 1 :(得分:1)
我也测试了它,并尝试传递参数,并且在所有情况下,传递的thisArg似乎根本不被使用(显然对我来说似乎是一个错误)。
我不得不使用类似的东西,但还有一个额外的限制,即需要访问该方法而不创建对象的实例(这在其他语言中是可能的,但在AS3&gt;。&lt;中则不可能)。所以我最终创建了静态函数并改为通过我自己的“thisArg”。
因此,改为使用静态函数是一种可能的解决方法:
static public function SayName(thisArg : MyObj) : void
{
trace(thisArg._name);
}
不是最好的,因为你最终可能会加倍像这样的代码&gt;。&lt;
或者,如果方法是公共的,您可以保存函数的名称而不是函数,并通过执行以下操作来访问它的方法:
var funcName : String = "sayName";
objB[funcName].apply(null, []);
objB[funcName].call(null);
但是,这取决于您的方法的范围是有限的(公共方法可以在任何地方使用这样的方法,与您的类在同一个包中的内部方法和仅在类内部的私有方法)。因此,它比使用方法的实际Function实例更具限制性,可以在任何地方使用。
这似乎是一个非常讨厌的错误O.o我希望其他人有更好的解决方案。
答案 2 :(得分: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
真正做的是重新绑定{{1关键字。如果是这种情况,那就充其量只是一种神秘,但值得一试。
答案 3 :(得分:0)
.call()工作正常,“ this”与调用站点提供的一样。好答案。但是请注意,访问修饰符(公共的,受保护的)受到尊重,因此,如果您调用不同的类,则将无法(自然地)访问受保护的成员。