Although development today uses ES6 classes and TypeScript interface definitions, decorators, etc., it’s important to understand object creation and inheritance prior to ES6 (ES6 classes encapsulate the syntax sugar of constructor + stereotype inheritance).

Creating complex objects

The factory pattern

Used to abstract the process of creating a particular object

function createPerson(name, age, job) {
    let o = new Object();
    o.name = name;
    o.age = age;
    o.job = job;
    o.sayName = function () {
        console.log(this.name);
    };
    return o;
}

let person1 = createPerson('echo', 12, 'engineer');
let person2 = createPerson('Jon', 1, 'Doctor');
Copy the code

Problems with the factory model

While this pattern solves the problem of creating multiple similar objects, it does not solve the problem of object identification (what type the newly created object is)

Instead, use constructors to identify instances

Constructor pattern

Constructors in ECMAScript are functions that create (specific type) objects.

Constructors & functions

Constructors are functions, and the only difference is how they are called. Any function called with the new operator is a constructor, otherwise it is a normal function

By management, constructor names are capitalized (borrowed from object-oriented programming languages).

Custom constructors

Native constructors, such as Object and Array, can be used directly by the runtime in the execution environment

You can also customize constructors that define properties and methods for your own object types in the form of functions, ensuring that instances are identified as specific types

function Person(name, age, job) { this.name = name; this.age = age; this.job = job; this.sayName = function() { console.log(this.name); } } let person1 = new Person('echo', 12, 'engineer'); let person2 = new Person('Jon', 1, 'Doctor'); person1.sayName(); // 'echo' console.log(person1.constructor == Person); // true // constructor is originally used to identify the type of the object, but it is generally considered a more reliable way to determine the type of the object when using the instanceof operator // since the constructor point may be changed console.log(person1) instanceof Person); // true // every Object is an instanceof Object, since all custom objects inherit from Object console.log(person1 instanceof Object); // trueCopy the code

Let person = new person; let person = new person;

The code inside Person() differs from that inside createPerson() as follows:

  • Create objects without showing them
  • Properties and methods are directly assigned tothis
  • There is noreturn
  • PersonCapitalized the first letter of the

To create instances using constructors, use the new operator. Calling the constructor in this way does the following:

Create a new object in memory

2. The [[Prototype]] property inside this new object is assigned to the constructor’s Prototype property.

3. This inside the constructor is assigned to the new object.

4. Execute the code inside the constructor

5. If the constructor returns a non-empty object, it returns that object. Otherwise, the newly created object is returned

Calling a function without explicitly setting itthisValue (that is, no method is called as an object, or is not usedcall()/appy()Call),thisAlways point toGlobalObject (in the browserwindowObject)

Person('g', 20, 'Doctor'); // Add to the window object window.sayname (); // 'g' let o = new Person(); Person.call(0, 'k', '10', 'Nurse'); o.sayName(); // 'k'Copy the code

Constructor problem

The main problem is that the methods defined are created on each instance, so functions with the same name on different instances are not equal but do the same thing.

Resolve (move function definition outside constructor) :

function Person(name, age, job) {
    this.name = name;
    this.age = age;
    this.job = job;
    this.sayName = sayName;
}

function sayName() {
    console.log(this.name);
}
Copy the code

The above approach solves the problem of repeating functions with the same logic, but it also affects the global scope

So here’s the prototype

The prototype pattern

Each function creates a Prototype property, which is an object containing properties and methods that should be shared by instances of a particular reference type

The advantage of using a stereotype object is that properties and methods defined on it can be shared by the object instance

Values that are assigned directly to object instances in constructors can be assigned directly to their prototypes

function Person() {}

Person.prototype.name = 'echo';
Person.prototype.sayName = function() {
    console.log(this.name);
}

const person1 = new Person();
const person2 = new Person();

console.log(person1.sayName == person2.sayName); // true
Copy the code

The above code adds the properties and methods directly to the Person prototype property. The same properties and methods are accessed from instance to instance.

Understand the prototype & prototype chain

Problems with prototype patterns

First, it weakens the ability to pass initialization parameters to constructors, resulting in all instances taking the same property values by default

The main problem stems from its shared nature — properties that contain reference values, and changing this reference value property on one instance causes corresponding properties on other instances to change as well. But in general, different instances should have their own copies of properties. This is why prototyping patterns are usually not used in isolation in real development

function Person() {}

Person.prototype = {
    constructor:Person,
    name: 'echo',
    age: 12,
    friends: ['Jon', 'May'],
    sayName() {
        console.log(this.name);
    }
}

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

person1.friends.push('Van');

console.log(person1.friends); // ['Jon', 'May', 'Van']
console.log(person2.friends); // ['Jon', 'May', 'Van']
Copy the code