ES6 inheritance: uses `super` to access the properties of the parent class - javascript

Javascript's super keyword, when I run the code on Chrome, Babel, TypeScript, I got different results.
My question is which result is correct? And what part of specification defines such behavior?
The following code:
class Point {
getX() {
console.log(this.x); // C
}
}
class ColorPoint extends Point {
constructor() {
super();
this.x = 2;
super.x = 3;
console.log(this.x) // A
console.log(super.x) // B
}
m() {
this.getX()
}
}
const cp = new ColorPoint();
cp.m();
The results:
Chrome 58.0.3029.110 64bit (V8 5.8.283.38)
Babel Repl 6.24.2
TypeScript 2.3
links:
gist
babel

Short answer:
Chrome is correct. And this is caused by the unbalance between get and set.
OrdinarySet is reciever sensitive, but OrdinaryGet is not.
So super.x = 3 has the same effect of this.x = 3, because the receiver here is this. Evaluatingsuper.x, which never reach this, will always get undefined because A.prototype does not have such field.
More details:
The super.x is a SuperReference. And assignment to SuperReference will call PutValue(V, W), which in turn call super object's internal slot [[Set]] and finally OrdinarySet.
In plain JavaScript, the statement super.x = 3 is basically equivalent to:
OrdinarySet(proto, 'x', 3, this).
Where proto is the super object, internally the [[HomeObject]] of constructor ColorPoint. proto is equivalent to Object.create(Point.prototype), as the ClassDefinitionEvaluation specifies, and it is passed to constructor as [[HomeObject]].
Now let's see how OrdinarySet works. In step 4c and 4d, the spec requires set operation is done on receiver this, not the proto object.
Let existingDescriptor be ? Receiver.[GetOwnProperty].
If existingDescriptor is not undefined, then
If IsAccessorDescriptor(existingDescriptor) is true, return false.
If existingDescriptor.[[Writable]] is false, return false.
Let valueDesc be the PropertyDescriptor{[[Value]]: V}.
Return ? Receiver.[[DefineOwnProperty]](P, valueDesc).
These statements says OrdinarySet(proto, 3, this) means this.x = 3.
On the other hand, OrdinaryGet ignores Receiver . super.x is
OrdinaryGet(proto, 'x', this).
OrdinaryGet does not have Receiver in its clauses at all! So super.x is equivalent to Object.create(Point.prototype).x, which is undefined of course.
As a rule of thumb, if there is discrepancy between transpilers and browsers, browsers, especially Chrome, are usually more loyal to the ECMAScript specification. Transpilers usually trade some edge case correctness for runtime efficiency.

Diffrence I saw :-
super helps to take all the this part of above class in it
and we can change that this accordingly`
we can change this.x of class point by super.x OR this.x in JAVASCRIPT
But we can can't make change in tyescript with super.x (it will behave
the new obect of class colorPoint)..this change can
only be happen by this.x
we can't make even call 2nd time to super() in JAVASCRIPT it will give an
err and we can call the super() in type script
it will make re-Initilize the Point constructor class
Typescript thing I have notice in TypeScript Playground by Runing it
"use strict";
class Point {
getX() {
console.log("POINT " + this.x);
}
constructor() {
this.x = 10;
}
}
class ColorPoint extends Point {
constructor() {
super();
this.y = 2;
console.log("THIS x" + this.x); // 10
console.log("THIS y" + this.y); // 2
console.log("SUPER !" + super.x); // undef
super.x = 3;
console.log("THIS.x after a " + this.x); // 10
this.x = 20;
console.log("THIS.x after b " + this.x); // 20
super();
console.log("SUPER x" + super.x); //
console.log("THIS x" + this.x); //
console.log(this); // give this of colorPoint
console.log(super()); // give this of colorPoint
}
}
const cp = new ColorPoint();
cp.getX();
Javascript thing i have notice in console by command node super.js
"use strict";
class Point {
getX() {
console.log("POINT " + this.x);
}
constructor() {
this.x = 10;
}
}
class ColorPoint extends Point {
constructor() {
super();
this.y = 2;
console.log("THIS x" + this.x); //
console.log("THIS y" + this.y); //
// console.log("SUPER" + super.x); //
super.x = 3;
console.log("THIS.x after a " + this.x); //
this.x = 20;
console.log("THIS.x after b " + this.x); //
//gives err
// super();
// console.log("SUPER x" + super.x); //
// console.log("THIS x" + this.x); //
}
// m() {
// this.getX();
// }
}
const cp = new ColorPoint();
cp.getX();

A rule of programming Garbage in -> Garbage out. The following code :
class Point {
x: number;
getX() {
console.log(this.x);
}
}
class ColorPoint extends Point {
constructor() {
super();
this.x = 2;
super.x = 3; // ERROR
}
}
You get a nice TypeScript error Only public and protected methods can be accessed. Note the word methods. super should not be used for properties. Invalid code is not something you should run.

Related

Performance benchmarking of function wrapper over class vs only constructor call invocation and HaveSameMap

I did some performance measure with CODE A and CODE B and would like to know why exactly does V8 outputs so.
Code A
const makePoint = () => {
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
}
return new Point(1, 2);
}
const a = makePoint();
const b = makePoint();
console.log(%HaveSameMap(a, b)); // false
First Question, why in the world HaveSameMap returns false. I believe that both a and b are having same shape and going through same process. So why would they be different?
Code B
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
}
var a = new Point();
var b = new Point();
Second question - Comparing performance time of both these have substantial diff. I just want to understand the underlying of type systems of V8. Why it would behave so. Calling new Point() vs returning it inside makePoint() function. What's going on here ?
Update - TEST METHODOLOGY
I am testing the same via external package
and my test code looks like
const { performance } = require('perf_hooks');
performance.mark('start');
while (iterations--) {
makePoint();
}
performance.mark('end');
performance.measure('My Special Benchmark', 'start', 'end');
You're creating a new Point class on every call to makePoint. This might become a bit more obvious when you directly return the class:
const makePoint = (x, y) => {
class Point {...};
return Point;
}
A class is create every time the class literal is evaluated. You can somewhat see this happening when adding a console.log right before the class definition. In your case that is evaluated every time.
Similarly, creating a new class each time is more costly that simply re-using the same class. V8 has to create a lot of internal data structures to support fast instance creation. Using a class a single time defeats this very common patter which is the slowdown you see.
In your first snippet, you were not only creating the instance in makePoint but also the class Point itself. Every time you called makePoint(), you made a new class:
console.log(makePoint().constructor === makePoint().constructor) // false
I think you are looking for
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
}
const makePoint = () => new Point(1, 2);
const a = makePoint();
const b = makePoint();
console.log(%HaveSameMap(a, b)); // true
console.log(a.constructor == b.constructor, a.constructor == Point); // true, true

(Typescript) Setter is not called on a sub-property of a class instance

this is a difficult question that I know is a problem for lots of programs (I will elaborate on this at the end). I want to create a custom setter in typescript but the datatype of the property being set is not just a number, string, bool it is actually a class object. This works fine - but if a property of the class instance is modified then the setter is not called. Here is an example of such a situation:
//This class contains two properties
class Point
{
public x : number;
public y : number;
constructor(x : number, y : number) { this.x = x; this.y = 0; }
}
//How here is another class that contains a Point
//But it is private and a getter/setter is used
class PointStore
{
private _foo : Point;
public get foo() : Point { return this._foo; }
//Here is the problem, the setter is only called when the whole of foo is changed
public set foo(n : Point) { this._foo = n; console.log("Foo has been set!"); }
constructor() { this._foo = new Point(0, 0); }
}
//Use case
let bar : PointStore = new PointStore();
bar.foo = new Point(10, 10); //Logs "Foo has been set!"
bar.foo.x = 20; //Doesn't log anything
The problem is pretty clear from the example but I just want to say the following:
Is there anyway around this at all? Because I have seen from APIs such as Unity3D they have opted to make their 'Point' class have only private members and so data can only be set through the constructor e.g:
//the 'Unity' solution
transform.position = new Vector2(10, 10); //Okay
transform.position.x = 20; //Error
But this is not at all a perfect solution to the problem, as it makes programming with the 'Point' class much more difficult from then on.
If anyone has a trick to solve this, it would be greatly appreciated.
The setter will be used only when you assign a value to the property. One way you can circumvent this is by using Object.assign, like so:
bar.foo = new Point(10, 10);
bar.foo = Object.assign(bar.foo, {x: 20})
You can also go deeper:
bar.foo = Object.assign(bar.foo, {x: {z: 20} })
You can use a Proxy for that:
class PointStore {
private _foo: Point;
constructor() {
this.createProxy(new Point(0, 0));
}
public get foo(): Point { return this._foo; }
public set foo(point: Point) {
this.createProxy(point);
console.log("Foo has been set!");
}
private createProxy(point: Point) {
this._foo = new Proxy(point, {
set: function (target: Point, property: string, value: any) {
target[property] = value;
console.log("Foo has been set (using proxy)!");
return true;
}
});
}
}
(code in playground)
The great thing about typescript is that, 2 types are compatible if they have the same shape. So, based on what you described, I think this seems to fit the bill and this will not break your code because PointStore here is compatible with Point class
class PointStore
{
private x : number;
private y : number;
constructor(x: number, y: number) {
this.x = x;
this.y = y;
};
public get x() { return this.point.x; };
public set x(x: number ) {
// your custom logic here
this.point.x = x;
};
// setter and getter for other y omitted
}

Declare a class property outside of a class method

See how x and y are declared in constructor:
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
toString() {
return '(' + this.x + ', ' + this.y + ')';
}
}
is there an way to declare properties outside of functions for instance:
class Point {
// Declare static class property here
// a: 22
constructor(x, y) {
this.x = x;
this.y = y;
}
toString() {
return '(' + this.x + ', ' + this.y + ')';
}
}
So I want to assign a to 22 but I am unsure if i can do it outside the constructor but still inside the class..
Initializing properties directly on a class in ES6 is not possible, only methods can currently be declared in this way. Same rules stand in ES7 as well.
However, it is a proposed feature that might come after ES7 (currently in stage 3). Here is the official proposal.
Additionally, the syntax the proposal is suggesting is slightly different (= instead of :):
class Point {
// Declare class property
a = 22
// Declare class static property
static b = 33
}
If you are using Babel, you can use the stage 3 settings to enable this feature.
Here's a Babel REPL example
The other way to do this in ES6, other than in the constructor, is to do it after the class definition:
class Point {
// ...
}
// Declare class property
Point.prototype.a = 22;
// Declare class static property
Point.b = 33;
Here's a good SO Thread diving into this topic some more
Note:
As Bergi mentioned in the comments, the suggested syntax:
class Point {
// Declare class property
a = 22
}
is just syntactic sugar to provide a shortcut for this code:
class Point {
constructor() {
this.a = 22;
}
}
Where both of those statements assign a property to an instance.
However, this isn't exactly the same as assigning to the prototype:
class Point {
constructor() {
this.a = 22; // this becomes a property directly on the instance
}
}
Point.prototype.b = 33; // this becomes a property on the prototype
Both would still be available via an instance:
var point = new Point();
p.a // 22
p.b // 33
But getting b would require going up the prototype chain while a is available directly on the object.
#nem035 is right that it is in proposal stage.
However, #nem035's sugggetion is one way to achieve it as class instance member.
// Declare static class property here
Seems you are looking to declare a static member. If yes,
JavaScript way is
class Point {
// ...
}
Point.a = '22';
The way you are actually expecting can be done in TypeScript
class Point {
static a = 22;
}
The compiled output will be same as above example
Point.a = '22';

Dynamically change prototype for instance in JavaScript

Is it possible to dynamically append to the prototype of an object's instance? For example, I have two dell objects, one's a laptop and one's a desktop. I want to be able to create two instances of dell and then extend the prototype to either laptop or desktop so I can use the appropriate getters / setters.
JSFiddle link:
http://jsfiddle.net/QqXgV/1/
var dell = function(){}
dell.prototype = new computer();
dell.prototype.constructor = dell;
var hp = function(){}
hp.prototype = new computer();
hp.prototype.constructor = hp;
var computer = function(){}
computer.prototype.setType = function(type){
//Here is where I would extend.
this.prototype extends window[type].prototype;
}
var laptop = function(){}
laptop.prototype.getKeyboard = function(){
return "motherboard";
}
var desktop = function(){}
desktop.prototype.getKeyboard = function(){
return "usb";
}
var dellDesktop = new dell();
dellDesktop.setType("desktop");
var dellLaptop = new dell();
dellLaptop.setType("laptop");
//This is the end goal.
dellDesktop.getKeyboard(); //Returns usb
dellLaptop.getKeyboard(); //Returns motherboard
//And then be able to do the same thing with hp.
In a case where a Dell can be either a Laptop or a Desktop one might create a DellLaptop and a DellDesktop constructor. But Laptops come in ultrabooks and netbooks so one has to create DellLaptopUltrabook and DellLaptopNetBook. So in a case where something can be either this or that and other objects can be this or that (like HP can also be either Laptop or Desktop) maybe the following pattern can help out:
var Computer = function(){};
//proxy getKeyboard based on what type it is
Computer.prototype.getKeyboard = function(){
return this.type.getKeyboard.call(this);
};
var Dell = function(args){
//do some checks on args here
//re use Computer constructor
Computer.call(this.args);
//re use laptop or Desktop constructor code
args.type.call(this,args);
//re use Laptop or Desktop protype members
this.type=args.type.prototype;
};
Dell.prototype = Object.create(Computer.prototype);
Dell.prototype.constructor = Dell;
var Laptop = function(){};
Laptop.prototype.getKeyboard = function(){
return "motherboard";
};
//ultrabook and netbook can inherit from Laptop
// so you could pass UltraBook or Netbook as type
var Desktop = function(){};
Desktop.prototype.getKeyboard = function(){
return "usb";
};
var dellDesktop = new Dell({type:Desktop});
var dellLaptop = new Dell({type:Laptop});
//This is the end goal.
console.log(dellDesktop.getKeyboard()); //Returns usb
console.log(dellLaptop.getKeyboard()); //Returns motherboard
While the answer by HMR is the better practice, if you absolutely need to change a running instance's prototype from one object to another, use the Object.setPrototypeOf function:
let X = class {
constructor() { this.v = 10; }
test() { console.log(this.v); }
morph(T) { Object.setPrototypeOf(this, T.prototype); }
};
let Y = class extends X {
constructor() { super(); }
test() { super.test(); console.log(10 * this.v); }
};
let Z = class { test() { console.log(34 * this.v); }};
let x = new X();
console.log(`plain test`);
x.test();
x.morph(Y);
console.log(`rewriten test 1`);
console.log(`x instanceof X?:`, x instanceof X);
console.log(`x instanceof Y?:`, x instanceof Y);
console.log(`x instanceof Z?:`, x instanceof Z);
console.log(`test result:`);
x.test();
x.morph(Z);
console.log(`rewriten test 2`);
console.log(`x instanceof X?:`, x instanceof X);
console.log(`x instanceof Y?:`, x instanceof Y);
console.log(`x instanceof Z?:`, x instanceof Z);
console.log(`test result:`);
x.test();
And note that this can even be done while in the constructor, which you should be even less tempted to do. But you can.
let X = class {
constructor() {
this.v = 10;
Object.setPrototypeOf(this, Y.prototype);
}
test() { console.log(this.v); }
};
let Y = class extends X {
test() { console.log(10 * this.v); }
};
let x = new X();
console.log(`x is an instance of Y:`, x instanceof Y);
And to answer the "when would you ever need this?" You might need this if you're working with, say, a polymorphic object such as a variable dimensional geometry, in which you want an intuitive constructor new Geometry(inputs) that rebinds its prototype based on the inputs, with the same API but wildly different implementations. Could you do that by telling users that they need their own code to figure out which specific constructor they need to use? Sure. But that'd be a very unpleasant experience for the user, for whom the implementation details are irrelevant, and it's that original constructor name that matters.

Calculated/transient fields in JavaScript

I am trying to achieve something like the following using OO JavaScript:
class Sample
{
public int x {get; set;}
public int y {get; set;}
public int z
{
get {return x+y;}
}
}
I could not understand on how to implement property 'z' in above class.
You have to use a function. As of ECMAScript 5th edition (ES5), that function can be a "getter" for a property that you access in the normal non-function way; prior to that you have to use an explicit function call.
Here's the ES5 way, using defineProperty: Live copy | source
function Sample()
{
// Optionally set this.x and this.y here
// Define the z property
Object.defineProperty(this, "z", {
get: function() {
return this.x + this.y;
}
});
}
Usage:
var s = new Sample();
s.x = 3;
s.y = 4;
console.log(s.z); // "7"
With ES3 (e.g., earlier versions):
function Sample()
{
// Optionally set this.x and this.y here
}
Sample.prototype.getZ = function() {
return this.x + this.y;
};
Usage:
var s = new Sample();
s.x = 3;
s.y = 4;
console.log(s.getZ()); // "7"
Note that you have to actually make the function call getZ(), whereas ES5 makes it possible to make it a property access (just z).
Note that JavaScript doesn't (yet) have a class feature (although it's a reserved word and one is coming). You can do classes of objects via constructor functions and prototypes, as JavaScript is a prototypical language. (Well, it's a bit of a hybrid.) If you start getting into hierarchies, there starts to be some important, repetitive plumbing. See this other answer here on Stack Overflow for more about that.

Categories

Resources