Why is ES6 class syntactic sugar?

Why is ES6 class syntactic sugar?

0. Preface

Let's read this article with questions:

  • Why is ES6 class syntactic sugar?
  • Is class syntactic sugar for prototypes?
  • So how do you use prototypes to implement the syntactic sugar of class?

1. OOP based on Prototype

Let's first look at an example of prototype:

function Person (name, sex) {
	this.name = name
	this.sex = sex
}

function Man (name) {
	this.name = name
}

Man.prototype = new Person('', 'male')

let Jy = new Man('Jy')

console.log(Jy.name, Jy.sex) //Jy, male
 

This is a very simple example of our use of prototypes, Person has a name and gender, Man is a Person whose gender is male, and Jy is a Man. Let's remember this example first, and we will rewrite this example using class below.

Tips: new, this, etc. are added by Brendan Eich to make it more like Java's OOP. Interested readers can consult relevant information on their own.

2. OOP of ES6 Class

class Person {
	constructor (name, sex) {
		this.name = name
		this.sex = sex
	}
}

class Man extends Person {
	constructor (name) {
		super('', 'male')
		this.name = name
	}
}

let Jy = new Man('Jy')

console.log(Jy.name, Jy.sex) //Jy, 'male'
 

We rewrite this example, using the words class, constructor, extends, and super, and then let's talk about specifically what is done with them in the ES6 specification.

3. Class OOP (ES6 specification) implemented by Prototype

Before ES6, JS objects are actually a collection of attributes, and attributes are a set of key-value pairs (key, value), the key can be String or Symbol, and the value includes data attribute feature values and accessor feature values.

You said that ordinary properties are okay, don't there still be methods under objects? How did it become a collection of attributes?

In fact, the definition of method that appears in the ES5 specification is "function that is the value of a property", which is a function property of an object, and cannot be called a method. It was not until the emergence of ES6 that Method Definitions were included in the specification.

ES3 something about OOP we can think of: prototype, new, this, constructor , instanceof, not even the standard __proto__attributes.

Fortunately, in ES5 we have added many methods to complete it and make it complete:

  • Object.defineProperty
  • Object.freeze
  • Object.create
  • Object.getPrototypeOf
  • Object.setPrototypeOf
  • isPrototypeOf
  • ......

Let's look at a piece of code:

let obj = {
	name: 'Jy',
	speak () { //Note: it's not speak: function () {}
		console.log(this.name, super.name)
	}
}

obj.speak() //Jy, undefined

Object.setPrototypeOf(obj,  { name: 'super' })

obj.speak() //Jy, super

let speak = obj.speak
speak() //undefined, super
 

obj.speak is defined as a Method in ES6, it has the attribute [[homeObject]], homeObject points to the object where the method is called (obj in the code), which is the Internal Slots bound in the object, that is, you If you can't modify it, it's the equivalent of writing dead.

So what is the use of homeObject? It is closely related to super. When the keyword super is parsed, the prototype of homeObject will be found.

Simply put, it can be summarized as the following two formulas:

  • let homeObj = Method[[HomeObject]] = obj
  • super = Object.getPrototypeOf(homeObj)

Note: homeObject is statically bound in internal slots, while super is dynamically searched.

After talking about super, let s talk about extends and constructor

class A extends B { }

class A extends B {
	constructor (...args) {
		super(args)
	}
}

class C extends null { }
 

Extends mainly does the following two things:

  • Object.setPrototypeOf(A, B)
  • Object.setPrototypeOf(A.prototype, B.prototype)

If the parent class is null, execute Object.setPrototypeOf(C.prototype, null)

The difference between the first and second parts of the above code is whether there is a display declaration of the constructor, so are these two pieces of code equivalent? The answer is equivalent.

This is how it is defined in the specification:

The third part of the code inherits null, it will not report a grammatical error, but we cannot create a new C. The reason is that the null constructor is called when new, and null does not have a constructor.

Seeing this, ES6's class oop and specification declarations all use prototypes to operate, so can we say that class is syntactic sugar for prototypes?

4. Babel compiled class

In our actual projects, we mostly use babel to compile ES6 and 7 code, so in this section we will analyze the following babel compiled code, which will omit some related codes for error reporting and type detection to better present the prototype to achieve The subject of OOP.

Before compilation:

class A extends B {}

console.log(new A)
 

After compilation:

"use strict";

function _getPrototypeOf(o) {
  _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) {
    return o.__proto__ || Object.getPrototypeOf(o);
  };
  return _getPrototypeOf(o);
}

function _inherits(subClass, superClass) {
  if (typeof superClass !== "function" && superClass !== null) {
    throw new TypeError("Super expression must either be null or a function");
  }
  subClass.prototype = Object.create(superClass && superClass.prototype, {
    constructor: {
      value: subClass,
      writable: true,
      configurable: true
    }
  });
  if (superClass) _setPrototypeOf(subClass, superClass);
}

function _setPrototypeOf(o, p) {
  _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) {
    o.__proto__ = p;
    return o;
  };
  return _setPrototypeOf(o, p);
}

var A =
  /*#__PURE__*/
  function (_B) {
    _inherits(A, _B);

    function A() {

      return _getPrototypeOf(A).apply(this, arguments);
    }

    return A;
  }(B);

console.log(new A());
 

Let's focus on the _inherits method, which is the same as the two things we said extends above:

  • Object.setPrototypeOf(subClass, superClass)
  • Object.setPrototypeOf(subClass.prototype, superClass.prototype)

It's just that it uses the Object.create method. The difference between these two methods can be checked on MDN.

Looking at the inside of function A, in fact, the constructor function of B is executed to achieve the effect of super(arguments). This is consistent with the specification: if there is no explicit declaration, the constructor will be automatically added to the constructor.

5. Summary

At this point, we finally understand why class is syntactic sugar for prototypes and how to use prototypes to implement class syntactic sugar.

But remember that the purpose of our prototype is not to simulate class oop, prototype based oop should be understood by prototype instead of class.

ES6's class oop is not complete, such as abstract class, interface, private, etc. are not yet available, but some features are already in the proposal, you can embrace it, or TypeScript is a good choice, if you use TS in your project , You are welcome to share your feelings in the comment area.

6. Reference link