Reference methods from another class in traceur - ecmascript-6

I'm testing classes in ES6 using traceur but it's not working as I expected.
I am trying to use a method as a reference in another class but when it's called I get the reference of the caller class when read the value of this.
Here is my code:
class A {
constructor(anotherMethod){
this.anotherMethod = anotherMethod;
this.name = "A";
}
myMethod (){
console.log(this.name);
this.anotherMethod();
}
}
class B {
constructor(){
this.a = new A(this.myMethod);
this.name = "B";
}
myMethod(){
console.log(this.name);
}
}
var c = new B();
c.a.myMethod();
My expected log is:
A
B
But it is showing:
A
A

In class B, when the constructor function runs:
this.a = new A(this.myMethod);
You're actually setting the method myMethod of B to A. When A's constructor runs,
this.myMethod, is set to A's anotherMethod. Now if you try printing this.a in your B's constructor you will get name : A. Which is actually referencing the class A.
Now when you try executing the method, c.a.myMethod(), As A contains the reference to class A, it's invoking the myMethod of A. Inside this method, this will refer to the current execution context object which is A. That's the reason why you're seeing A in both the consoles.
In short, You're only assigning the function to A and not setting the context.
You can force fat arrow using below:
class B {
constructor(){
this.a = new A(this.myMethod);
this.name = "B";
}
myMethod = () => {
console.log(this);
}
}
Now you will get the desired output. But unfortunately traceur doesn't support it. Only babel supports fat arrow inside functions which is part of ES7 stage 0 Class Properties.
As suggested by Felix King: Binding the context using bind is more than enough currently
class B {
constructor(){
this.a = new A(this.myMethod.bind(this));
this.name = "B";
}
myMethod() {
console.log(this);
}
}

Related

Get class name in constructor

export class InvalidCredentialsError extends Error {
constructor(msg) {
super(msg);
this.message = msg;
this.name = 'InvalidCredentialsError';
}
}
As you can see above, I'm writing InvalidCredentialsError twice. Is there a way to somehow get the class name already in the constructor method and set it? Or does the object have to be instantiated?
In browsers with native ES6 class support, this.constructor.name will display the InvalidCredentialsError. If you transpile the code with Babel it will show Error.
Without Babel (use on Chrome or another browser that supports class):
class InvalidCredentialsError extends Error {
constructor(msg) {
super(msg);
console.log(this.constructor.name);
this.message = msg;
this.name = 'InvalidCredentialsError';
}
}
const instance = new InvalidCredentialsError('message');
With Babel:
class InvalidCredentialsError extends Error {
constructor(msg) {
super(msg);
console.log(this.constructor.name);
this.message = msg;
this.name = 'InvalidCredentialsError';
}
}
const instance = new InvalidCredentialsError('message');
this.myName="literallycopiedclassname"
Is not the same as
this.myName=this.constructor.name
Look at this:
class B extends Error {
constructor() {
super();
this.myName="B";
}
};
class D extends B {constructor() {super();}};
let o=new D(); console.log(o.myName);
And this:
class B extends Error {
constructor() {
super();
this.myName=this.constructor.name;
}
};
class D extends B {constructor() {super();}};
let o=new D(); console.log(o.myName);
As I know, you cannot access static class properties from within constructor of that class an easy way, except when explicitly referencing the class:
this.myName=B.name;
However, you can access static properties of the outermost class of the constructors super()-chain (the class of object being constructed) (and of its ancestors) using this.constructor. If it is, what you want, enjoy the this.constructor.name construction.
If you want to record the class name from within the constructor defined by that class, you need to explicitly reference the class, but then you need to write it's identifier once more as well. However, it doesn't need to be a string literal, as shown above. If the reason for your question is that you dislike using string literals where not necessary and/or you will avoid maintaining all the identifier-names and literals when renaming the class(es), you can try the following:
If you write
class B extends Error {};
Then B is an identifier refering to the class being defined, accessible in the class' body, AND that class' static property "name" (B.name) is set to it's literal representation (B.name="B"). Additionally, if written this way, it behaves like this:
B=class B extends Error {};
So you can reference the class with B from outside the class' body as well. But you can also write
C=class B extends Error {};
Than you can reference the class with C from outside, with B from inside and the "name" property becomes "B". It shows, that the "internal" name can differ from the name of variable holding the reference to the class. You can name it at will and there is no need to rename it:
const C=class ThisClass extends Error {
constructor() {
super();
this.myName=ThisClass.name;
}
};
let o=new C();
The drawback is, that your class will have the internal name ThisClass. It may or may not be wrong, all classes can have the same internal name without interferring, when you don't use it for some (e.g. debug) purposes. After all, the internal name can be reassigned (again, to whatever you want):
Object.defineProperty(C,"name",{value:"C", configurable:true});
However, you have to write it as a string literal again. But there is a workaround:
You can save the reference to the class whereever you want:
const cName="YourClass";
const classes={};
classes[cName]=class ThisClass extends Error {};
All put together:
const cName="YourClass";
const classes={};
Object.defineProperty (
classes[cName]=class ThisClass extends Error {
constructor() {
super();
this.myName=ThisClass.name;
}
},
"name",
{value:cName, configurable:true}
);
class D extends classes[cName] {
constructor() {super();}
};
let o=new (classes[cName])();
console.log("YourClass myName", o.myName);
console.log("YourClass constructor", o.constructor.name);
let od=new D();
console.log("D myName", od.myName);
console.log("D constructor", od.constructor.name);
Now there is only one place, where the class name is specified, as string literal. And you don't need to rewrite the name in new...

Indirect function references

Shouldn't this (or something like it) work? and/or How can I accomplish this some other way?
class A {
void init() {initializeSomething()}
}
main() {
var f = A.init;
A a = new A();
a.f();
}
I want, in general, to store a reference to an instance method
somewhere, then call it somewhere else (something I can do in most other
languages, including JavaScript). I thought functions were first class
in Dart... and aren't methods functions? Are methods not first class?
It works as long as the method is static (which is of no use in my case) but not for instance methods...
Functions and methdods are different. You can only call methods on an instance of a class that has this method.
In your example (var f = A.init) you reference an instance method like a static (class) method.
What works is:
make init static
class A {
static void init() => initializeSomething();
// or
// static void init() {
// initializeSomething();
// }
}
main() {
var f = A.init;
A a = new A();
a.f();
}
or use a reference to init() of an actual instance of A:
class A {
void init() => initializeSomething();
}
main() {
A a = new A();
var f = a.init;
a.f();
}

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";
}

Overriding function from another class

I am defining this function in one of my classes:
public function onUse():void {};
Then in another of my classes (let's call it "class2"), I create a object of this class, and then want to override this function with another one. After some Google-Fu, I have found this, and used it...
button.onUse {
variable = value;
}
...but it executes instantly, and not when onUse() is called - which seems to be an empty function, always.
I didn't find anything more than that - I tried a few things myself, like specifying a function inside class2 and using button.onUse = function();, but it always throws errors.
Can anyone tell me whether what I am trying to do is actually possible, and if it is, how can I do it?
You can only override functions when you are extending the class:
public class A {
public function foo():void {
doStuff();
}
}
public class B extends A {
override public function foo():void {
doOtherStuff();
}
}
var n:A = new A();
n.foo(); // => calls doStuff();
var o:B = new B();
o.foo(); // => calls doOtherStuff();
Hence, assigning a different function to a class method of an instance is not possible at runtime.
You can, however, let your original class contain a field of type Function, and then simply assign a different closure to it.
public class A {
public var foo:Function;
}
var n:A = new A();
n.foo = function ():void {
doStuff();
};
n.foo(); // => calls doStuff();
var o:A = new A();
o.foo = function ():void {
doOtherStuff();
}
o.foo(); // => calls doOtherStuff();
check the syntax of
button.onUse {
variable = value;
}
a function would be defined as
public function onUse():void {};
and overwritten with
override public function onUse():void {
}
in a different class
the way you're trying to do it, does not constitute overriding a function.
What I've done in similar circumstances is create a onClickFunction function in the class
public var onClickFunction:Function = null;
and then in the CLICK event listener function add
if(onClickFunction != null){
onClickFunction();
}
then you can assign your on-click functionality by doing something like this
button.onClickFunction = function():void{
variable = value;
// dostuff
}
this is not the best way of doing it, but probably the easiest way of implementing the functionality. And ideally you'd use inheritance the way the spacepirate suggested.

Catching the right event with the right listener?

Consider this scenario we have a class A which is singleton it has a function Sum(a,b) ** . Now we have two classes **Class B and Class C respectively. Both classes (B,C) call add function and listen for a event.
Class A
{
function add(a:int,b:int):void
{
c:int = a+b;
dispatchEvent(new myEvent(myEvent.addComplete,c));
}
}
Class B
{
function addSomething():void
{
var objectA:A = new A();
objectA.addEventListener(myEvent,onAddComplete);
var a:int = 10;
var b:int = 20;
objectA.add(a,b);
}
function onAddComplete(e:myEvent):void
{
trace(e.something);
}
}
Similarly there is a C and D class. Now when lets say both classes call A's add function (when A is singleton) how does one insure that the right answer is returned to the right listener.
Secondly what if the calls are changing something, is there a locking in singleton?
When you instantiate a Singleton, you don't use "new" , otherwise it wouldn't be a Singleton, you would keep creating instances of the A class. In order for your class to be a Singleton, only one instance can be created, this way , when you call A in other classes, you are calling the same instance of A.
Generally you use the getInstance method which either return an instance of the Singleton class if none exist or return the previously created instance of A.
Have a look at this for more information:
http://www.gskinner.com/blog/archives/2006/07/as3_singletons.html
It is generally accepted that Singleton should be avoided when possible. You should find a few posts here explaining why, here's an example:
What is so bad about singletons?
In your example, you could do this for instance:
class A
{
function add( params:Object , dispatcher:EventDispatcher )
{
c = params.a + params.b;
dispatcher.dispatchEvent( new MyEvent(c) );
}
}
class B
{
private var _dispatcher:EventDispatcher = new EventDispatcher();
function addSomething():void
{
var objectA:A = new A();
_dispatcher.addEventListener(MyEvent.ADD,onAddComplete);
var params:Object = {a:10 , b:20};
objectA.add(params , _dispatcher);
}
function onAddComplete(e:MyEvent):void
{
trace(e.something);
}
}