Function.apply不使用thisArg参数

时间:2011-08-30 05:13:35

标签: flash flex actionscript-3 functional-programming

我正在编写一些ActionScript3代码,试图将方法应用于在运行时确定的对象。 Function.applyFunction.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);

如果这确实被打破了,除了将目标方法变成函数之外还有其他解决方法(在我看来这会非常难看)吗?

4 个答案:

答案 0 :(得分:17)

它不是一个“错误”,但callapply的文档非常具有误导性,并且在解释最新情况时并没有做得很好。所以这里是对正在发生的事情的解释。

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

  

方法是属于类定义的函数。创建类的实例后,方法将绑定到该实例。与在类外声明的函数不同,方法不能与它所附加的实例分开使用。

因此,当您创建new MyObj实例时,其所有方法都绑定到该实例。这就是为什么当您尝试使用callapply时,您没有看到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将始终在其所属的实例的上下文中执行,无论您使用thiscall传递给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);
    };
}

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

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

答案 1 :(得分:1)

哇,这是非常令人惊讶的O.o

我也测试了它,并尝试传递参数,并且在所有情况下,传递的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”与调用站点提供的一样。好答案。但是请注意,访问修饰符(公共的,受保护的)受到尊重,因此,如果您调用不同的类,则将无法(自然地)访问受保护的成员。