How to use this before super? - ecmascript-6

I know that this is not allowed before super but I need to do it. I was wondering if there was a legal way to do this in es6?
My code:
class DOMElement {
constructor(aNodeName) {
this.name = aNodeName;
this.create();
}
create() {
let domref = document.createElement(this.name);
document.body.appendChild(domref);
return domref;
}
}
class Button extends DOMElement {
constructor(aLabel) {
this.label = aLabel;
super('button');
}
create() {
let domref = super.create();
domref.textContent = this.label;
}
}
If I don't set this.label before calling super('button') in Button.prototype.create then domref.textContent is set to undefined.

There's no 'legal' way.
It can be be a getter in cases where static property value is supposed to be used by parent class on construction:
get label() {
return 'label';
}
In this case this means that class design is just wrong. There's nothing in parent class that would impose such restriction. Actually, label is only used by child class. It should be:
constructor(aLabel) {
super('button');
this._domref.textContent = alabel;
}
create() {
// if parent class is user-defined, it should be done there
// instead of returning a value, because it doesn't make sense
// to leave an important part of the object without a reference
this._domref = super.create();
}

Related

How can I write a closure function in getter in ES6 ?

In ES5, I can write it like this:
MyClass.prototype.value = (function() {
var privateVariable = 0;
return function() {
return ++privateVariable;
};
})();
But in ES6, how can I do it ?
class MyClass {
get value() {
// return ??
}
}
A direct counterpart, which is not idiomatic to ES6 classes:
class MyClass {}
MyClass.prototype.value = (() => {
let privateVariable = 0;
return function() {
return ++privateVariable;
};
})();
There are no practical reasons to make privateVariable completely unavailable from the outer scope, especially since it doesn't play well with ES6 classes. This cannot be considered a proper encapsulation because privateVariable isn't available for reflection; it cannot act as protected member as well.
There can be private member that stores a value:
class MyClass {
constructor() {
this._value = 0;
}
value() {
return ++this._value;
}
}
It could be a symbol but this doesn't make class design more secure, just introduces additional complications when a class is inherited in another module:
// should be carried around everywhere to make the class extendable
export const VALUE = Symbol('value');
class MyClass {
constructor() {
this[VALUE] = 0;
}
value() {
return ++this[VALUE];
}
}
Note that get value() {} is not same thing as value() {}.
One of TypeScript (which is a superset of ECMAScript) benefits is the encapsulation that is enforced at compilation time.
The equivalent to the ES5
function MyClass() {}
var privateVariable = 0;
MyClass.prototype = {
get value() {
return ++privateVariable;
}
};
in ES6 class syntax would be
let privateVariable = 0;
class MyClass {
get value() {
return ++privateVariable;
}
}
Whether you put all that in an IEFE or module or whatnot for local scope doesn't matter, it's the same.

How to Override Constants in ActionScript 3

I have the two following classes:
public class Parent{
static internal const _name:String = "Parent";
public function get name():String{
return _name;
}
}
public class Child{
static internal const _name:String = "Child";
}
If I create an instance of class Child and call its name() getter, since it will call the name() method it inherits from Parent, it returns "Parent". I could, of course, override the name() method:
public class Child{
static internal const _name:String = "Child";
override public function get name():String{
return _name;
}
}
That returns "Child". However, it seems silly to have to copy the exact same code of the method from the parent. Is there any simpler way to do this?
I would take a different approach by making the "name" property a requirement for the parent's constructor:
public class Parent
{
static internal var _name : String;
public function Parent(name : String = "Parent") {
_name = name;
}
public function get name() : String {
return _name;
}
}
Child Class:
public class Child extends Parent
{
public function Child() {
super("Child");
}
}
Firstly, you cannot override static methods or properties - they are not inherited, so no override for them.
Secondly, if you declared a constant to be of a complex type, it is not really a constant. I.e. if it is an object, then you can change its keys / values, if it is an array, you can add / remove members and so on.
But the desire to make this functionality more generic is understandable. So, what I'd do:
Have some property outside both parent and child, let say in class X, or package Y. Let it be package Y. So, you'd create a dictionary in package Y, let it be Y.names and in your name getter you'd do:
import Y.names;
. . .
public function get name() {
return names[(this as Object).constructor];
}
your names variable would be:
package Y {
public var names:Dictionary = generateNames();
internal function generateNames():Dictionary {
var result:Dictionary = new Dictionary();
result[ChildClass] = "child";
result[ParentClass] = "parent";
. . .
return result;
}
}
This way it would be sufficient to only implement name getter in super-class, and all inheriting classes will be able to use super-class code as is, no need to change anything. However, this means that some (maybe important) information pertaining to this class will be stored elsewhere (may be difficult to find, this is not the common way people program in AS3).
your implementation of get name should look like this, then the getter is one and each of the new classes needs to have it's own public static var _name defined:
//in the base class
public function get name():String
{
var _sName:String;
if ((this as Object).constructor._name)
{
_sName = (this as Object).constructor._name;
}
else
{
try
{
var o:Object = getSuperClass(this);
while (o)
{
if (o._name)
{
_sName = o._name;
break;
}
o = getSuperClass(o);
}
}
catch (e:*)
{}
}
return _sName;
}
//as found here: http://www.actionscriptdeveloper.co.uk/getting-the-class-of-an-object-in-as3/
public static function getSuperClass(o: Object): Object
{
var n: String = getQualifiedSuperclassName(o);
if (n == null)
return(null);
return getDefinitionByName(n);
}
the static members can be accessed only via class reference which we can get from constructor object, "this" will point to the current class in the inheritance chain so you can call this in parent class and it will point to a Child in a Child class.
[EDIT]
I've modified it so it tests for existance of the public static property _name if not found on "this" instance then in a loop the parent class is checked until one is found - like inheritance:)
I'm using this feature to create clone method: constructor as helper in clone method implementation
best regards
Why don't you store such a constant within a corresponding function instead of declaring an inaccessible constant?
class Parent {
...
public function get name():String { return 'Parent'; }
}
class Child extends Parent {
...
override public function get name():String { return 'Child'; }
}
By the way, if your Parent class is a descendant of DisplayObject, you should be careful with name property, as it's needed sometimes by operating code, e.g. getChildByName().
I have found something that seems to work. Any feedback is greatly appreciated:
public class Parent{
prototype._name = "Parent";
public function get name():String{
return this["_name"];
}
}
public class Child{
prototype._name = "Child";
}

How to pass a reference to class into another class

The question is a bit silly. I am trying to implement a skill updating system. So to explain.
There is a class
class AppInfo
{
public static var power:int = 10;
public static var speed:int = 20;
}
and class SmartButton which should take a reference to one of the static variables e.g. power in a constructor and increment it on the given value.
e.g.
class SmartButton
{
public function onClick(skillReference:int = <AppInfo.power>, incrementVAlue:int = 10)
{
skillReference += incrementVAlue
}
}
I want this code to update the value of the power in AppInfo class. But this doesn't happen... I assume because the skill was passed as value not as reference...
Can you suggest a way of solving the task?
Thanks
Your assumption is correct, ints are passed by value rather than reference. One direct approach would be to encapsulate power into a reference type (a class) rather than a value type:
class Skill {
public var value:int;
public function Skill(val:int) {
this.value = val;
}
}
class AppInfo
{
public static var power:Skill = new Skill(10);
public static var speed:Skill = new Skill(20);
}
Then passing power should pass it as a reference to the instance. Though you would have to change your implemenation a bit to use skillReference.value instead.
Aside from that, I think there are a couple of ways to abstract what you want out. One way would be use an interface and leverage some dependency injection.
interface ISkills
{
function get power():int;
function set power(val:int):void;
}
class AppInfo implements ISkills
{
private static _power:int = 0;
public function get power():int { return _power; }
public function set power(val:int):void { _power = val; }
}
class SmartButton
{
public function onClick(skills:int = ISkills, skill:String = "power", incrementVAlue:int = 10)
{
skills[skill] += incrementVAlue
}
}
The idea here that you want to decouple your usage from your implementation. In this case SmartButton doesn't need to know how Skills work just how to operate on them. It loses its reference to the static class AppInfo in favor of an injectable instance. There are some advantages to this approach, it makes it easier to test and easier to swap implementations later if you decide that a static class isn't the best implementation idea without having to update a bunch of classes/code. Also, rather than injecting ISkills into the method, you could inject it into the constructor of SmartButton, and keep a private reference to the skill container.
Another approach would be to use a functional approach.
class SmartButton
{
public var defaultWorker:Function = function(val:int):void {
AppInfo.power += val;
}
public function onClick(worker:Function = undefined, incrementValue:int = 10):void
{
if(worker == undefined) worker = defaultWorker;
worker.call(this, incrementValue);
}
}
Again, in this case, rather than tightly coupling your implementation to use the AppInfo class directly, you inject a "worker" for it do the work for you (if the worker is undefined then use the default worker. You can then swap out which property gets changed by changing the closure that gets passed in. For instance if you wanted to change speed instead then you would call:
var smartButton:SmartButton;
smartButton.onClick(function(val:int):void { AppInfo.speed += val});
Not quite as succinct as it could be, but it gets the job done.
The obligatory "elegantly sophisticated" approach using the command pattern:
Interface Command {
function execute():void;
}
Class UpdatePower implements Command {
private var appInfo:AppInfo;
private var delta:int;
public function UpdatePower(appInfo:AppInfo, delta:int) {
this.appInfo = appInfo;
this.delta = delta;
}
public function execute():void {
appInfo.delta += delta;
}
}
Class SmartButton {
var command:Command;
public function SmartButton(command:Command) {
this.command = command;
}
public function onClick(event:Event):void {
command.execute();
}
}
I would probably implement this in a slightly different way.
Maybe something like;
class Properties {
private var _properties:Dictionary = new Dictionary();
public function setValue(key:String, value:int) {
_properties[key] = value;
}
public function getValue(key:String):int {
if( !_properties[key] ) return 0;
else return _properties[key];
}
public function modifyValue(key:String, value:int) {
setValue(key, getValue(key) + value);
}
}
class SmartButton
{
public function onClick(target:Properties, key:String, incrementValue:int = 10) {
target.modifyValue(key, incrementValue);
}
}
Or something along those lines.

Overriding a public variable with set

I have a base class like this
class Base {
public var space:Number;
}
which gets extended by
class Desc extends Base {
override public function set space( space:Number ):void {
//code
}
}
This doesn't compile. Say, you don't have control of the base class, what ways is there implement the same thing?
The obvious is create a function setSpace(), but this object is being embedded in an already existing system that use the public space.
Thanks in advance.
Your base class should be defined that way:
class Base {
// Don't use public variables. Make them private and define setters and getters
private var space_:Number;
public function get space():Number {
return space_;
}
public function set space(v:Number):void {
if (space_ === v) return;
space_ = v;
}
}
And then it can be overriden that way:
class Desc extends Base {
override public function set space( space:Number ):void {
//code
}
}
Edit:
I missed the part where you say you don't have control over the base class. In that case, hope that space is defined as a getter/setter (it should be if the class is implemented properly). If not, you'll indeed have to use a function such as getSpace and setSpace.
From Adobe:
Class member variables that are declared using the var keyword cannot
be overridden in a subclass.
Using a setter is not an option in your case. If you need to preserve the interface, use composition instead of inheritance:
class Desc {
private var _base:Base;
public function set space(space:Number):void {
//code
this._base.space = space;
}
}

How to override the transform.matrix setter

I have a class which extends the Sprite object in as3. I need to be able to override the transform.matrix setter in this class but haven't been successful in doing so.
I've tried many things, along with creating my own separate class which extends the Transform class and then overrides its set matrix function, and set my transform = new CustomTransform(). Sadly this didn't work.
In code this is what i tried:
public class MyClass extends Sprite
{
public function MyClass()
{
super(); transform = new MyTransform(this);
}
}
class MyTransform extends Transform
{
public function MyTransform(dp:DisplayObject)
{
super();
}
override public function set matrix(value:Matrix)
{
super.matrix = value;
customcode();
}
}
All help is greatly appreciated!
This seems to work:
public class MyClass extends Sprite
{
public function MyClass()
{
super();
transform = new MyTransform(this,super.transform);
// i'm drawing a rect just to see the results of scaling
graphics.beginFill(0xff0000);
graphics.drawRect(0,0,100,100);
graphics.endFill();
}
override public function get transform():Transform {
var tmp:Transform;
if(super.transform is MyTransform) {
tmp = super.transform;
} else {
tmp = new MyTransform(this,super.transform);
}
return tmp;
}
override public function set transform(value:Transform):void {
var tmp:Transform;
if(value is MyTransform) {
tmp = value;
} else {
tmp = new MyTransform(this,value);
}
super.transform = tmp;
}
}
public class MyTransform extends Transform
{
public function MyTransform(dp:DisplayObject,transf:Transform = null)
{
super(dp);
if(transf) {
for(var prop:String in transf) {
this[prop] = transf[prop];
}
}
}
override public function set matrix(value:Matrix):void
{
super.matrix = value;
// customcode();
}
}
Use:
var sp:MyClass = new MyClass();
var mat:Matrix = sp.transform.matrix;
mat.scale(3,3);
trace(sp.transform);
sp.transform.matrix = mat;
addChild(sp);
The problem is that, even if you create and assign your tranform to be of type MyTransform, the getter returns a regular Transform object. There's something weird about how transform objects work in Flash (this is also true for SoundTransform, for instance). There's some kind of cache mechanism implemented in a rather lame way that forces you to reassign the instance if you want to commit your changes.
I mean this pattern:
var t:Transform = mc.transform;
// do something with t
mc.transform = t;
So I think this is related to why your code doesn't work as expected.
To get around this, I'm checking both in the setter and the getter if the trasnform object passed is of type MyTransform. If it is, I use it as is. If it's not, I create a MyTransform object and copy all of the properties from the original Transform. It'd be nice if the Transform class had a clone method, but it doesn't, so I implemented this simple copy mechanism. Not sure if this doesn't mess up with some internal state in Transform (could be the case). I haven't tested it apart from applying a scale, once. You might want to do it, as there could be other side effects I'm not considering. Also, this is probably not the most performant. But I can't think of another way to have your matrix setter called.
Edit
Using a static/global dispatcher is not a good idea except you really need it to be global. Implementing IEventDispatcher, since you can't directly extend EventDispatcher, is what you want.
The code needed for that is a bit verbose, but it's a no-brainer anyway. All you need is having an internal instance of event dispatcher and implement the methods of the interface. In said methods, you forward the parameteres to the actual dispatcher.
public class MyTransform extends Transform implements IEventDispatcher
{
private var _dispatcher:EventDispatcher;
public function MyTransform(dp:DisplayObject,transf:Transform = null)
{
super(dp);
_dispatcher = new EventDispatcher(this);
if(transf) {
for(var prop:String in transf) {
this[prop] = transf[prop];
}
}
}
override public function set matrix(value:Matrix):void
{
super.matrix = value;
// customcode();
}
public function dispatchEvent(event:Event):Boolean {
return _dispatcher.dispatchEvent(event);
}
public function addEventListener(type:String,listener:Function,useCapture:Boolean = false, priority:int = 0, useWeakReference:Boolean = false):void {
_dispatcher.addEventListener(type,listener,useCapture,priority,useWeakReference);
}
public function removeEventListener(type:String,listener:Function,useCapture:Boolean = false):void {
_dispatcher.removeEventListener(type,listener,useCapture);
}
public function hasEventListener(type:String):Boolean {
return _dispatcher.hasEventListener(type);
}
public function willTrigger(type:String):Boolean {
return _dispatcher.willTrigger(type);
}
}