In object-oriented programming, a class is a template for an object that defines properties and methods shared by all instances. Prior to ES6, there was no syntax in JavaScript to define a class like Java’s class, and only emulation could give JavaScript the form of a class. Traditional object-oriented programming has three major features: encapsulation, inheritance, and polymorphism. In the following sections, we will look at how ES5 and ES6 emulate these features. The first step is to understand the most common means of defining classes and inheritance in ES5.

function Person(name, age) {
    this.name = name;
    this.age = age;
    this.friends = ["Andy", "Anne"];
}
Person.prototype.sayName = function () {
    alert(this.name);
}
var person1 = new Person("Nake", 28);
var person2 = new Person("Miachel", 28);
person1.friends.push("TOM");
alert(person1.friends) // ["Andy", "Anne", "Tom"]
alert(person2.friends) // ["Andy", "Anne"]
alert(person1.sayName === person2.sayName)    // true
Copy the code

In the code above, we use the most common composite constructor and stereotype pattern to simulate classes, with each new instance having its own properties and shared stereotype methods

function obejct(o) { function F() {}; F.prototype = o; return new F(); } function inheritPrototype(subType, superType)) { var prototype = object(superType.prototype); prototype.constructor = subType; subType.prototype = prototype; } function SuperType(name) { this.name = name; this.colors = ["red"]; } SuperType.prototype.sayName = function () { return this.name; } function SubType(name, age) { SuperType.call(this, name); this.age = age; } // inheritPrototype(SubType, SuperType);Copy the code

Then we use parasitic inheritance to copy the cloned prototype to the SubType prototype, using inheritPrototype(SubType, SuperType) code. Let’s take a look at the characteristics of traditional object-oriented and how to simulate them in ES5 and ES6

encapsulation

Private property

In traditional object-oriented, the existence of encapsulation hides implementation details and provides public access. One of the best uses of encapsulation is to privatize attributes and provide corresponding GET and set methods to access them, such as in Java

class Person { private int age; public void setAge(int a) { age = a; } public int getAge() { return age; }}Copy the code

In the beginning, the most commonly used means of object creation and inheritance in ES5, although encapsulation is carried out to a certain extent, attributes are not privatized. Generally, attributes are privatized by _, such as _age, but they can still be accessed if access is forced. In the ES6 proposal, a # is preceded by the genus name to indicate that the property is private, as follows

class SuperClass { #age = 20; setAge(age) { this.#age = age; } getAge() { console.log(this.#age); }}Copy the code

An error will be thrown if we access from outside

So what does the ES6 actually do? Let’s compile to ES5 via Babel and let’s see if we can pass thisLinks to onlineaccess

var _age = /*#__PURE__*/new WeakMap(); var SuperClass = /*#__PURE__*/function () { function SuperClass() { _classCallCheck(this, SuperClass); _age.set(this, { writable: true, value: 20 }); } _createClass(SuperClass, [{ key: "setAge", value: function setAge(age) { _classPrivateFieldSet(this, _age, age); } }, { key: "getAge", value: function getAge() { console.log(_classPrivateFieldGet(this, _age)); } }]); return SuperClass; } ();Copy the code

We’ll see that the compiled code does several things

  1. Define an immediate execution function
  2. _classCallCheck ensures that this function must be called with new
  3. Define a weakmap (for browser does not support weakmap, can undertake polyfill gist.github.com/Gozala/1269… _age to hold the definition in #age
  4. Save setAge and getAge in SuperClass. Prototype

This is very similar to the way we defined classes in ES5 at the beginning, with extra validation of calls and special handling of private attributes

Static attributes

So, what’s special about static properties? Let’s take a look at the definition in ES6

class SuperClass { #age = 20; static age = 24; setAge(age) { this.#age = age; } getAge() { console.log(this.#age); }}Copy the code

Compiled to ES5 via Babel, it can be accessed via this online link

var SuperClass = /*#__PURE__*/function () { function SuperClass() { _classCallCheck(this, SuperClass); _age.set(this, { writable: true, value: 20 }); } _createClass(SuperClass, [{ key: "setAge", value: function setAge(age) { _classPrivateFieldSet(this, _age, age); } }, { key: "getAge", value: function getAge() { console.log(_classPrivateFieldGet(this, _age)); } }]); return SuperClass; } (); _defineProperty(SuperClass, "age", 24);Copy the code

We’ll notice that static attribute handling is placed directly on the class, not on this instance. _defineProperty(SuperClass, “age”, 24); Now you know the difference between classes defined in ES6 and those defined in ES5, and what you can do to ensure that the two are translated

inheritance

In traditional object orientation, inheritance improves code reusability and relationships between classes, providing the premise for a third feature polymorphism. In Java, for example

class Person { private int age; public void setAge(int a) { age = a; } public int getAge() { return age; } } class Student extends Person { public String grade; public Student(age, grade) { super(age); this.grade = grade; }}Copy the code

We started with parasitic inheritance, which doesn’t cover private and static properties, so let’s take a look at the ES6 inheritance keyword extends

class SuperClass { #age = 20; static age = 24; setAge(age) { this.#age = age; } getAge() { console.log(this.#age); } } class SubClass extends SuperClass { constructor(props) { super(props); this.sub = 'sub'; }}Copy the code

Compiled to ES5 via Babel, it can be accessed via this online link

var SuperClass = /*#__PURE__*/function () { function SuperClass() { _classCallCheck(this, SuperClass); _age.set(this, { writable: true, value: 20 }); } _createClass(SuperClass, [{ key: "setAge", value: function setAge(age) { _classPrivateFieldSet(this, _age, age); } }, { key: "getAge", value: function getAge() { console.log(_classPrivateFieldGet(this, _age)); } }]); return SuperClass; } (); _defineProperty(SuperClass, "age", 24); var SubClass = /*#__PURE__*/function (_SuperClass) { _inherits(SubClass, _SuperClass); var _super = _createSuper(SubClass); function SubClass(props) { var _this; _classCallCheck(this, SubClass); _this = _super.call(this, props); _this.sub = 'sub'; return _this; } return SubClass; }(SuperClass);Copy the code

_inherits(SubClass, _SuperClass); Implements our inheritance

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); }Copy the code

A closer look at the code shows that we’re doing two things

  1. Subclass.prototype = object.create (XXX); Point the proTO of a subclass’s prototype to the parent class’s prototype, ensuring that all methods are inherited
  2. Through setPrototypeOf (ttf_subclass, superClass); Point the proto of a subclass to the parent class, ensuring that all static members are inherited

polymorphism

Polymorphism is reflected in the program, the parent class reference or interface reference points to the object of the child class, from a certain extent can be considered as method overwriting.

The resources

  • Introduction to es6
  • JavaScript defines the best way to write a class