JavaScript和ActionScript中“this”之间的差异

时间:2017-03-09 22:12:03

标签: javascript flash actionscript

我的背景是JavaScript很重要。我对ES5和ES6都有非常深入的了解。在工作中,我最近被分配了一个涉及使用AS2的旧Flash应用程序的项目。我的理解是ActionScript与ES5非常相似,但是有类和可选的严格类型(类似于TypeScript和Flow),以及一些其他经典的OO功能。到目前为止它相当简单,但我无法理解this和引用在ActionScript中的工作方式。

这是我对JavaScript的理解。函数中的this可以引用:

  • 绑定变量,如果使用Function.bind()(以及Function.call()和Function.apply()),则无法在绑定函数中更改,例如:

function func() {
    return this.number;
}

var bound = func.bind({ number: 2 });
console.log(bound()); // 2
  • 一个对象,如果该函数作为该对象的方法被调用,例如:

function func() {
    return this.number;
}

var obj = { number: 2, func: func };
console.log(obj.func()); // 2
  • 类的实例,如果该类的原型上定义了该函数,例如:

function Class() {
    this.number = 2;
}
Class.prototype.func = function func() {
    return this.number;
}

console.log(new Class().func()); // 2
  • 全局对象,如果调用该函数时没有任何类型的绑定或对象或实例附加到它,例如:

var number = 2;

function func() {
    return this.number;
}

console.log(func()); // 2

在ActionScript中,事情似乎有点不同。首先,如果您在该类的方法中执行它,则可以访问没有this的类成员,类似于C#和Java等语言:

class MyClass {
    private var number:Number = 2;

    public function func():Number {
        return number;
    }
}

trace(new MyClass().func()); // 2

此外,ActionScript标准库似乎没有Function.bind()方法,但它确实有Function.apply()Function.call(),它们似乎与JavaScript变体一样:{{ 3}}。似乎也没有原型,这是有道理的,因为基于我的理解,类是更抽象的语法结构而不是函数(就像C#/ Java)。

所以我的问题是,排除缺少Function.bind()Function.prototype,ActionScript和JavaScript之间的规则是否相同?

此外,如果我这样做会发生什么:

class SomeClip extends MovieClip {
    private var childClip:MovieClip;
    private var number:Number = 2;

    public function SomeClip() {
        this.onLoad = function() {
            // SomeClip onLoad hander, `this` will be the SomeClip instance

            childClip._visible = true; // How is childClip resolved here?

            childClip.onRelease = function() {
                // childClip onRelease handler, `this` will be childClip

                trace(number); // How is number resolved here?
            };
        };
    }
}

基本上,如果您在事件处理程序中访问没有this的成员,或者其他一些不是该类方法的松散函数,会发生什么?我猜想在第一种情况下,它将解析为this.childClip并按预期工作,但在第二种情况下,解析将失败,因为onRelease处理程序的闭包不会包含引用到SomeClip实例。

2 个答案:

答案 0 :(得分:2)

我看到到目前为止写的评论更侧重于JS,所以我会尽力从ActionScript的角度回答。

在AS2 / AS3的世界中,定义为类的方法的函数将this值绑定到类。这是许多具有现代类的高级语言的典型,例如Java,Haxe等。因此,在ActionScript中,除了变量名称可能的情况之外,您很少会发现需要使用this关键字。被函数参数遮蔽:

public function Point(x:Number = 0, y:Number = 0)
{
    // A rare but necessary use-case of "this" in AS2/AS3
    this.x = x;
    this.y = y;
}

另一方面,如果您提供的函数是匿名的,就像您编写的示例一样,行为取决于您是否添加this

childClip.onRelease = function() {
    trace(number);
};

在这种情况下,ActionScript可以确定number是该类的成员,并且将打印2,就像您预期的那样。这是因为解释器在堆栈中查找下一个最接近的东西。换句话说,通过排除this使您感到不明确,因此它知道需要执行查找。

但是,如果你转到trace(this.number),你会发现你得到undefined(甚至可能是错误)。这是因为this不是类的成员变量,现在指向类似于JS的“全局对象”。为了避免与全局对象共舞,ActionScript开发人员通常会将所有侦听器定义为类实例方法:

class MyClass extends EventDispatcher
{
    private function MyClass()
    {
        addEventListener(Event.CHANGE, onChangeEvent);
    }
    private function onChangeEvent(e:Event) {
        trace(this); // refers to this class, and no need for bind() like JS
    }
}

组织良好的AS3代码几乎不会包含内联匿名函数,因为使用显式函数引用处理垃圾收集要容易得多。

最后要注意的一点是 - 您可以期望ActionScript中常规Objects方法的函数表现得像JavaScript,通过事件侦听器传递它们将导致this的上下文丢失,并且Flash不会进行魔术查找以找到您引用的变量:

var obj = {
    func: function () {
        trace(this); // weird global object
    }
};
addEventListener(Event.CHANGE, obj.func);

希望有所帮助!

答案 1 :(得分:2)

在AS2中,函数没有绑定并得到"这个"在调用时传递的引用(显然是通过Function.apply或对象引用):

function getIndex()
{
    trace(this.index);
}

var A = {index:1, getIndex:getIndex};
var B = {index:2, getIndex:getIndex};

A.getIndex(); // 1
B.getIndex(); // 2
B.getIndex.apply(A); // 1

调用某些对象的绑定方法"委托":http://help.adobe.com/en_US/AS2LCR/Flash_10.0/help.html?content=00001842.html#1001423简而言之,函数也是对象,你可以创建特殊的函数对象,它引用了调用方法和&# 34;这"要传递的对象:

function getIndex()
{
    trace(this.index);
}

function bind(method, target):Function
{
    var result:Function = function()
    {
        // arguments.callee is always a reference
        // to the current function object
        arguments.callee.method.apply(arguments.callee.target);
    }

    result.method = method;
    result.target = target;

    return result;
}

var A = {index:1};
var B = {index:2};

A.getIndex = bind(getIndex, A);
B.getIndex = bind(getIndex, B);

A.getIndex(); // 1
B.getIndex(); // 2
B.getIndex.apply(A); // 2

然后,如果你不使用"这个"引用,一旦你通过名称来处理某个变量,就会有几个上下文按顺序搜索这样一个变量:

  • 本地函数变量
  • 本地包装函数变量(这个真的太可怕了,因为没有人真正知道这些变量存在于哪里,这是一个强大的内存泄漏)
  • MovieClip,包含功能代码,局部变量
  • 全局变量

使用以下代码进行播放,评论一些" index"变量,你会看到它:

// Global variable.
_global.index = 6;

// MovieClip local variable.
var index = 5;

function wrap():Function
{
    // Wrapper function local variable.
    var index = 4;

    return function()
    {
        // Function local variable.
        var index = 3;
        trace(index);
    }
}

wrap()();