object-oriented

Recently in the review of javascript basic knowledge, hereby to record their own, one time to fix the object

  • Classes: encapsulation, polymorphism, inheritance
  • Constructor, instance, object literal
  • The namespace
  • Built-in object, host object, local object

First, there are no classes in JS, they are all based on prototypes. Both classes introduced in ES5/ES6 are syntactic sugar based on the archetypal inheritance model.

The constructor

It is a function itself, usually capitalized for the sake of specification, except that the function that generates the instance using new is a constructor, and the direct call is a normal function.

// ********** several steps of handwriting new ********** //
// create a new object.
// the prototype of the constructor is assigned to the proto of the new object.
// 3, the constructor this points to the new object and returns the result
// if the result is an object, return a new object

function myNew(fn, ... args) {
  let newobj = {};
  newobj.__proto__ = fn.prototype;
  let resObj = fn.apply(newobj, args);
  // Determine if the result is an object, otherwise return a new object
  return resObj instanceof Object ? resObj : newobj;
}
/ / test
function Parsen() {
  this.name = Longge "";
  this.age = "18";
}
Parsen.prototype.getName = function () {
  return this.name;
};
var parsen = myNew(Parsen);
Copy the code

Prototype and prototype chain

The prototype pattern

Prototype is used each time a function is declared in a constructor: the browser creates an object in memory that adds a constructor property, which the browser assigns to the constructor.

Javascript objects inherit methods and properties from prototypes, and Object.Prototype is at the top of the inheritance chain. The Javascript Prototype keyword can also be used to add new values and methods to constructors.

Prototype chain: when code reads an attribute, it first finds it in the instance and returns it. If it doesn’t find it, it continues to search through the instance’s prototype object (PROTO) until it does. If not, continue to look for the prototype object of the prototype object. All the way to Object, null is returned

Relationship between

  • Prototype: a function property: an object {}
  • Proto: is a property of the Object: Object {}
  • ** Each object instance has a __**proto__, which points to the constructor’s prototype
  • The above relationship can be tested using console.log
function Test() {
  this.a = 1;
};
var t = new Test();

// __proto__ holds the prototype of the object's constructor
t.__proto__ === Test.prototype // true

Test.prototype.__proto__ === Object.prototype // true: such a chain call forms a primitive chain

Object.prototype.__proto__ // The top level of the null primitive chain is null

/************* primitive chain *****************/

Test.prototype.b = 2; // Add an attribute b=2 to the constructor prototype
Object.prototype.c = 3
console.log(test)
// Draw the chain:
// test:
/ / {
// a: 1,
// __proto__: Test.prototype = {
// b: 2,
// __proto__: Oject.prototype = {
// c:3
/ / not __proto__
/ /}
/ /}
// }
// Protochain: find a chain of the prototype constructor along the __proto__ node, layer by layer until null

console.log(test.constructor) // Test(){}Test. constructor points to the constructor that instantiates the test object.constructorIs modifiable by assignment, /************* particularity *****************/Function,Object: function objectTest.__proto__= = =Function.prototype // true
Function.__proto__= = =Function.prototype // trueBecause the function constructs itselfObject.__proto__= = =Function.prototype // true/************* attribute lookup *****************/ //test => {a: 1.b:2}
test.hasOwnProperty('a') // true finds the stereotype property on the current object
test.hasOwnProperty('b') // true
test.hasOwnProperty('c') // false is inherited so no

'a' in test // true in: chain lookup
Copy the code

Class

/************ ES5 definition class ******************/
function User(name) {
  this.name = name;
}
// Add the function
User.prototype.showUser = function () {
  console.log(this.name);
};
/ / use
const user = new User("my.yang");
user.showUser();

/************ ES6 definition class ******************/
class Person {
  // Add attributes and methods to Person's prototype
  constructor(name) {
    this.name = name;
  }
  showName() {
    console.log(this.name); }}const person = new Person("My.Yang");
person.showName();

/ * * * * * * * * * * * * * * * * * * * class instances of * * * * * * * * * * * * * * * * * * * * * * * * * * * /

// All attributes of the instance are defined on the prototype, except for this
console.log(person.hasOwnProperty("name")); // true
console.log(person.hasOwnProperty("showName")); // false
console.log(person.__proto__.hasOwnProperty("showName")); // true

As with ES5, all instances of a class share a prototype object.
var a1 = new Person("long");
var a2 = new Person("mmmmm");
console.log(a1.__proto__ === a2.__proto__);

// It is not recommended to add private attributes and methods directly through __proto__, since shared instances are affected

/**************** Note: ********************/
Classes and modules use strict mode internally by default
// 2, there is no variable promotion ahead of access error ReferenceError

// Static method: static
// Add the static keyword to indicate that this method is not inherited by [instance], only through the class. Called directly, but extends can be inherited by subclasses

// Private methods and attributes
// Private methods: defined internally using _xxx, _func
Copy the code

Create a function named User that will be the result of the class declaration. 2. Store all methods in user. prototype such as showUser (as in ES5). 4. Class methods are not enumerable

inheritance

Class uses extends to implement inheritance, which is much cleaner and more convenient than ES5 does by modifying the stereotype chain.

class ColorPoint extends Point {
  constructor(x, y, color) {
    super(x, y); // Call parent constructor(x, y)
    this.color = color;
  }
  toString() {
    return this.color + "" + super.toString(); // Call the parent toString()}}Copy the code

Note: Subclasses must first call super() from constructor, otherwise new instances will report errors. ReferenceError addresses this problem by first adding superclass attributes and methods to this, and then modifying this with subclass constructors.

Use Object.getProtoTypeof () to determine whether a class inherits from another class object.getProtoTypeof (Xxxx) === XXX

Super can be used as either a function or an object as a function: super() represents the constructor of the parent class. => A.prototype.constructor.call(this)

As an object: super In normal methods, refers to the prototype object a.prototype of the parent class; Point to a parent class in a static method.

1. Prototype chain inheritance

Let the prototype object of the subclass directly point to the parent class instance. When the subclass instance cannot find the corresponding attribute and method, it will look for its prototype object (the parent class instance), so as to achieve the inheritance of the attributes and methods of the parent class.

/ / parent class
function Parent() {
  this.name = "I'm a demo.";
}
// Add a method to the parent class
Parent.prototype.getName = function () {
  return this.name;
};
/ / subclass
function Child() {}

Child.prototype = new Parent(); // Assign an instance of the parent class directly to the child class's prototype object
Child.prototype.constructor = Child; // Bind Constructor as per the rules of the prototype chain. This step does not affect inheritance, but is required when constructor is used

const child = new Child();
child.name; // I am a small sample
child.getName(); // I am a small sample
Copy the code

Disadvantages: All subclass instance prototypes refer to the parent class instance, so any modification of a parent class reference method or function by one subclass instance affects all subclasses. You cannot also pass parameters to the parent constructor.

Constructor inheritance

Execute the superclass constructor in the subclass constructor and bind the subclass this so that attributes in the superclass can be assigned to this of the subclass. This avoids sharing a stereotype instance between instances and allows you to pass arguments to the parent constructor. The downside is obvious: you don’t inherit properties and methods from your superclass prototype

function Parent(name) {
  this.name = [name];
}
Parent.prototype.getName = function () {
  return this.name;
};
function Child() {
  Parent.call(this.Parameters of "1"); // Execute the superclass constructor and bind the subclass's this so that attributes in the superclass can be assigned to the subclass's this
}

/ / test
const child1 = new Child();
const child2 = new Child();
child1.name[0] = "foo";
console.log(child1.name); // ['foo']
console.log(child2.name); // ['zhangsan']
child2.getName(); GetName () cannot be found. The constructor inherits properties and methods from the parent stereotype
Copy the code

3. Combinatorial inheritance

The prototype constructor calls the parent class call

function Parent(name) {
  this.name = [name];
}
Parent.prototype.getName = function () {
  return this.name;
};

function Child() {
  Parent.call(this."Parameters"); // Change the Parent this pointer with an argument
}
Child.prototype = new Parent(); // Assign the parent class instance to the subclass prototype
Child.prototype.constructor = Child;

var child = new Child();
var child2 = new Child();
child1.name = "yang";
console.log(child1.name); // yang
console.log(child2.name); / / parameters
child2.getName(); / / parameters
Copy the code

Disadvantages: The constructor (parent.call () and new Parent()) is executed twice each time an instance of the subclass is created, resulting in two copies of the same properties and methods in the prototype when an instance of the subclass is created, which is not elegant.

4. Parasitic Combination inheritance [Ultimate Solution]

To solve the problem of the composite inheritance constructor being executed twice, we changed the pointing to parent instance to pointing to the copied parent prototype, removing one constructor execution without affecting each other.

It's basically the original, but everything else is the same. Child.prototype =new Parent() => Child.prototype = Parent.prototype
Copy the code

But the problem is that the subclass prototype and the superclass prototype both point to the same object, and that still affects each other. Prototype = object.create (Parent. Prototype) all ES5 inheritance is present. Babel’s conversion of ES6 inheritance also uses parasitic combinative inheritance

function Parent(name) {
  this.name = [name];
}
Parent.prototype.getName = function () {
  return this.name;
};
function Child() {
  // Constructor inheritance
  Parent.call(this."zhangsan");
}
// Prototype chain inheritance
// Child.prototype = new Parent()
Child.prototype = Object.create(Parent.prototype); // Change 'point to parent instance' to 'point to parent prototype'
Child.prototype.constructor = Child;

/ / test
const child = new Child();
const parent = new Parent();
child.getName(); // ['zhangsan']
parent.getName(); // Error: getName() not found
Copy the code

Let’s review the implementation:

  1. At first, the most obvious idea is to inherit the attributes and methods of the parent class by pointing the stereotype of the subclass instance to the parent class instance. However, the drawback of stereotype chain inheritance is that modifying the reference type of the subclass instance inheritance affects all instance objects and cannot pass arguments to the constructor of the parent class.
  2. Therefore, we introduced constructor inheritance, which gets the attributes and methods of the parent class by calling the parent constructor in the subclass constructor and passing in the subclass this. However, constructor inheritance also has a defect. Constructor inheritance cannot inherit the attributes and methods on the parent class prototype chain.
  3. So we combined the advantages of both inheritance and proposed composite inheritance, but composite inheritance also introduces a new problem, it executes the superclass constructor twice every time it creates a subclass instance, I
  4. We solved this problem by changing the subclass stereotype to point to a shallow copy of the parent stereotype, the final implementation, parasitic combinatorial inheritance