我的背景是JavaScript很重要。我对ES5和ES6都有非常深入的了解。在工作中,我最近被分配了一个涉及使用AS2的旧Flash应用程序的项目。我的理解是ActionScript与ES5非常相似,但是有类和可选的严格类型(类似于TypeScript和Flow),以及一些其他经典的OO功能。到目前为止它相当简单,但我无法理解this
和引用在ActionScript中的工作方式。
这是我对JavaScript的理解。函数中的this
可以引用:
的
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
实例。
答案 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
然后,如果你不使用"这个"引用,一旦你通过名称来处理某个变量,就会有几个上下文按顺序搜索这样一个变量:
使用以下代码进行播放,评论一些" 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()();