Understand JavaScript objects

To understand JavaScript inheritance, we first need to look at JS objects, mainly how they are created.

How objects are created

The factory pattern

  1. What is?

Factory pattern is a widely used design pattern in software field. It is a process of creating specific objects abstractly. 2. What problem was solved?

  • Solve the problem of creating multiple similar objects.
  1. Specific cases:
// 1. The process of creating a specific object (person)
function createPerson(name,sex,age) {
    let person = {}
    person.name = name
    person.sex = sex
    person.age = age 
    person.sayName = function() {
        console.log(this.name)
    }
    return person
}

// 2. Problem solved: create multiple similar objects
let person1 = createPerson('Ming'.'male'.'18')
let person2 = createPerson('flower'.'woman'.'21')

// 3. There are problems, return all Object type
console.log(person1.constructor)  ƒ Object() {[native code]}
console.log(person2.constructor)  ƒ Object() {[native code]}
Copy the code
  1. The problem?
  • It does not address the problem of object identification, that is, newly created objects of schema type. The object identifier here refers to the instanceconstructor, it always returnsObjectType, not clear type.
  • Each time an object is created, an object method is performedsayNameCreation.

Constructor pattern

  1. What is?

Constructors are also procedures for creating specific objects, like Objecr and Array, which are native constructors for creating objects and arrays. 2. Problem solved?

  • Solve the problem of creating multiple similar objects
  • Resolve object identification issues in factory mode
  1. Specific cases:
// 1. The process of creating a specific object (person)
function Person(name,sex,age) {
    this.name = name
    this.sex = sex
    this.age = age
    this.sayName = function() {
        console.log(this.name)
    }
}

// 2. Problem solved: Create multiple similar objects and specify the object identity
let person1 = new Person('Ming'.'male'.'18')  // Think about the process of new
let person2 = new Person('flower'.'woman'.'21')

console.log(person1.constructor)  // It is better to use instanceof to determine the object identity
console.log(person2.constructor)

// 2. Problem solved: Specify the object identifier -- Person
ƒ Person(name,sex,age) {
    this.name = name
    this.sex = sex
    this.age = age
    this.sayName = function() {
        console.log(this.name)
    }
}

Copy the code
  1. The problem?
  • Each time an object is created, an object method is performedsayNameCreation. Because they all do the same thing, there’s no need to create them multiple times
  • Temporary solution: willsayNameThe creation of is placed externally, providing a reference internally. The problem with this scheme is creating global variables, not good!!
let sayName = function() {
    console.log(this.name)
}
function Person(name,sex,age) {
    this.name = name
    this.sex = sex
    this.age = age
    this.sayName = sayName
}
Copy the code
  1. Difference from factory mode?

There is no difference. The inner implementation of the constructor is the same as the factory pattern, except that the inner implementation uses this to bind object instances and new to identify objects.

The prototype pattern

  1. What is?

Each function is created with a Prototype property, which is an object that can contain the shared properties and methods of a particular instance 2. Problem solved?

  • You only need to create properties and methods once
  1. Specific cases:
function Person() {}
// Only need to create once
Person.prototype.name = 'flower'
Person.prototype.sayName = function() {
    console.log(this.name)
} 

let person1 = new Person()
let person2 = new Person()

console.log(person1.sayName === person2.sayName) // true
Copy the code
  1. The problem: Stereotype attributes can easily be changed by instances, affecting other instances
function Person() {}
// Only need to create once
Person.prototype.friends = [1.2.3]

let person1 = new Person()
let person2 = new Person()
console.log(person1.friends) / / [1, 2, 3]
console.log(person2.friends) / / [1, 2, 3]

// Data is changed, affecting other instances
person1.friends.push(4)

console.log(person1.friends) / / [1, 2, 3, 4]
console.log(person2.friends) / / [1, 2, 3, 4]

Copy the code

inheritance

Now that we’ve looked at several ways to create objects, let’s look at inheritance in JavaScript.

Many object-oriented languages support two types of inheritance: interface inheritance and implementation inheritance. The former inherits only method signatures, while the latter inherits actual methods. Interface inheritance is not possible in JavaScript, because functions have no signature and can only use implementation inheritance. Inheritance is implemented in JavaScript primarily through prototype chains

Prototype chain

Take a look at the relationship between constructors, stereotypes, and instances: Each constructor has a Prototype property (which is an object), and the prototype object has a default constructor property that points to the constructor; There is a pointer inside the instance that points directly to the constructor, which is accessed directly from the constructor property of the instance, and the prototype object from the constructor’s Prototype.

Stereotype objects can define properties and methods shared by instances

If the constructor’s prototype object is an instance of another constructor, this means that the constructor’s prototype may also be an instance of another constructor. This creates a chain of references to the constructor’s prototype, called the prototype chain. The hierarchy of the prototype chain is Object.prototype.proto: null

  1. Specific cases:
/ / parent class
function Parent(name) {
    this.name = name
}
Parent.prototype.sayName = function() {
    console.log(this.name)
}

/ / subclass
function Son() {}
// Override the prototype of the subclass
Son.prototype = new Parent('flower')

let son = new Son()
son.sayName() / / flower
console.log(son.constructor) // Parent
Copy the code
  1. Problem solved

How to inherit properties and methods of other reference types. 3. Existing problems:

  • We usually define properties in constructors and methods in stereotypes. When inheritance is implemented using stereotype chains, it means that the original instance properties may become stereotype properties, that is, reference properties. This is also a problem with stereotype patterns.
  • Cannot pass arguments to a parent class when a subclass is instantiated
/ / parent class
function Parent() {
    this.friends = [1.2.3]}/ / subclass
function Son() {}
// Override the prototype of the subclass
Son.prototype = new Parent()

let son1 = new Son() // Cannot pass a parameter to the parent class
let son2 = new Son()
console.log(son1.friends) / / [1, 2, 3]
console.log(son2.friends) / / [1, 2, 3]

son1.friends.push(4) 
console.log(son1.friends) / / [1, 2, 3, 4]
console.log(son2.friends) / / [1, 2, 3, 4]

Copy the code

Embezzle the constructor schema

  1. Specific case

Basic idea: call the parent class once in the subclass constructor

/ / parent class
function Parent(name) {
    this.name = name
    this.friends = [1.2.3]}/ / subclass
function Son(name) {
    Parent.call(this,name)  // Can pass arguments to the parent class
}
let son1 = new Son('Ming') // Cannot pass a parameter to the parent class
let son2 = new Son('flower')
console.log(son1.friends,son1.name) Xiao Ming / / [1, 2, 3]
console.log(son2.friends,son2.name) / / [1, 2, 3] flower

son1.friends.push(4) 
console.log(son1.friends) / / [1, 2, 3, 4]
console.log(son2.friends) / / [1, 2, 3]
Copy the code
  1. Problem solved
  • Subclasses can pass parameters to the parent constructor
  • Ensure that subclasses maintain a separate copy of their parent class’s properties (Friends)
  1. Existing problems:
  • Methods must be defined in the constructor of the parent class
  • Subclasses do not have access to properties and methods on the parent class stereotype

Combination of inheritance

Combining the advantages of the prototype chain pattern and the stolen constructor pattern, we use the prototype chain pattern to inherit the attributes and methods on the parent class prototype, and use the stolen constructor pattern to inherit the instance attributes.

  1. Specific case
/ / parent class
function Parent(name) {
    this.name = name
}
Parent.prototype.sayName = function() {
    console.log(this.name)
}

/ / subclass
function Son(name) {
    Parent.call(this,name)  // Inherit the instance attributes and save a copy
}
Son.prototype = new Parent()  // Inherits stereotype attributes and methods

let son = new Son('flower')
son.sayName() / / flower

Copy the code
  1. Problem solved
  • The stereotype chain pattern inherits properties and methods from the superclass stereotype
  • The embezzled constructor pattern inherits instance attributes, keeping their respective copies
  1. Existing problems

The superclass constructor is called twice

/ / parent class
function Parent(name) {
    this.name = name
}
Parent.prototype.sayName = function() {
    console.log(this.name)
}

/ / subclass
function Son(name) {
    Parent.call(this,name)  // First call
}
Son.prototype = new Parent()  // Second call

let son = new Son('flower')
son.sayName() / / flower
Copy the code

Prototype inheritance

There is no custom type. Information is shared between objects through prototypes

Implementation scheme:

function object(obj) {
    function f() {}
    f.prototype = o;
    return new f()
}
Copy the code
  1. The specific implementation

This pattern applies when an object already exists and you want to create an object based on that object.

function object(obj) {
    function f() {}
    f.prototype = o;
    return new f()
}
// There are already objects
let student = {
    name: 'Ming'
}
// Enhance objects
let goodStudent = object(student) // Inherit the student's name
goodStudent.score = 90 // Enhance score attributes (external enhancement - vs parasitic inheritance)
Copy the code

This mode is called Object.create(obj,des) in ES5

Parasitic inheritance

Idea: Prototype inheritance + factory pattern

  1. The specific implementation
// Prototype inheritance
function object(obj) {
    function f() {}
    f.prototype = obj;
    return new f()
}

// Factory mode
function createAnother(o) {
    let temp = object(o)
    temp.sayName = function() {  // Enhance it internally
        console.log(this.name)
    }
    return temp
}

let o = {
    name: 'Ming'
}
let newO = createAnother(o)
console.log(newO.name) / / xiao Ming

Copy the code

Parasitic combinatorial inheritance

Instead of defining a subclass stereotype by calling the superclass constructor, get a copy of the superclass stereotype, enhance that copy by parasitic inheritance, and then assign that copy to the subclass stereotype.

  1. The specific implementation
function object(obj) {
    function f() {}
    f.prototype = obj;
    return new f()
}
function init(Person,Son) {
    let prototype = object(Person.prototype) // Get the parent prototype
    prototype.constructor = Son  // Subclass the constructor
    Son.prototype = prototype   // Change the subclass prototype
}
Copy the code
  1. Specific case
function object(obj) {
    function f() {}
    f.prototype = obj;
    return new f()
}
function init(Person,Son) {
    let prototype = object(Person.prototype) // Get the parent prototype
    prototype.constructor = Son  // Change the subclass constructor
    Son.prototype = prototype   // Change the subclass prototype
}
/ / parent class
function Parent(name) {
    this.name = name 
}
Parent.prototype.sayName = function() {  // Prototype chain inheritance
    console.log(this.name)
}

/ / subclass
function Son(name,age) {  // embezzle the constructor
    Parent.call(this,name)
    this.age = age
}
// Parasitic combination inheritance
init(Parent,Son)

let son = new Son('Ming'.18)
console.log(son.name)
son.sayName() / / xiao Ming
Copy the code
  1. Problem solved
  • Fixed the problem of calling the superclass constructor twice in composite inheritance
  • The prototype chain remains the same

conclusion

Personal summary inheritance is mainly realized through two ideas:

  1. Reference inheritance through prototype chain is mainly realized as follows: prototype chain inheritance, embeded constructor inheritance and combinatorial inheritance
  2. Inheritance is implemented by enhancing an existing object, which may be a constructorprototypeOr it can be a single object. The main implementations are: prototype inheritance (Object.create), parasitic inheritance (internal enhancement Object), parasitic combined inheritance (theft constructor, parasitic inheritance)

If the above is wrong, please criticize and correct!