简体   繁体   中英

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

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:

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 . Evaluating super.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.

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.

Diffrence I saw :-

super helps to take all the this part of above class in it and we can change that this accordingly`

  1. 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

  2. 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();

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM