function Person(name, age,job) {
    this.name = name;
    this.age = age;
    this.job = job;
    this.sayName = function() {
        console.log(this.name); }}var person1 = new Person("Xiao Ming".29."Engineer")
var person2 = new Person("Little red".20."Test Engineer")
Copy the code

The new operator, called the operator, goes through four procedures.

  1. Create a new object
  2. Assigns the constructor’s scope to the new object (thus this refers to the object)
  3. Points to the code in the constructor
  4. A new object is returned

Person1 and person2 each hold an instance of Person. Both objects have a constructor property that points to Person, as shown below:

console.log(person1.constructor === Person) // true
console.log(person2.constructor === Person) // true
Copy the code

The constructor property of an object is originally used to identify the object type. However, instanceof is more reliable when it comes to detecting object types. The Object we create in this example is both an Object instance and a Person instance. Because all objects inherit from Object.

Constructors defined in this way are defined in the Global object (or window object in browsing). Here is a test done on the browser console.

1. Treat constructors as functions

The only difference between constructors and other functions is how they are called. Any function called with the new operator can be used as a constructor. If it is not called with the new operator, it can be used as a normal function. For example, a defined Person can be called in the following way.

// use it as a constructor
var person1 = new Person("Xiao Ming".29."Engineer");
person1.sayName(); // "xiaoming"

// Used as a normal function
Person("Little red".20."Test Engineer");
window.sayName(); // "/"

// use in the scope of another object
var o = new Object(a); Person.call(0."Little red".20."Test Engineer");
o.sayName(); // "/"

Copy the code

Instead of calling Person() with the new operator, properties and methods are added to the window object. This is because when a function is called in a Global scope, this always points to Global(the browser is the Window object). Therefore, after calling the function, the sayName() method can be called from the window object.

2. Constructors

The main problem with constructors is that each method is created on each instance. In the current example, person1 and person2 both have a method named sayName(), but neither method is an instance of the same Function. Functions in ECMAScript are objects, so each function defined is an instantiation of an object. From a logical point of view, the constructor could also be defined as follows:

function Person(name, age,job) {
    this.name = name;
    this.age = age;
    this.job = job;
    this.sayName = new Function("console.log(this.name)") // Is logically equivalent to declaring a function
}
Copy the code

Therefore, functions of the same name on different instances are not equal. However, it really isn’t necessary to create two instances of Function that do the same thing. Plus, with this, you don’t need to bind functions to specific objects before executing code. Therefore, you can solve this problem by escaping the function definition outside the 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

We move the sayName function outside of the constructor, making it a global function. Person1 and person2 share the same function in the global scope. This really solves the problem of two functions doing the same thing. However, functions in a global scope can really only be called by an object, which makes the global scope a bit of a misnomer. Even worse, if we need to define a lot of methods, then there are many global functions, and our custom reference types have no encapsulation at all.

The prototype pattern

function Person() {
    Person.prototype.name = "Xiao Ming";
    Person.prototype.age = 29;
    Person.prototype.job = "Engineer";
    Person.prototype.sayName = function() {
        console.log(this.name)
    }
}

var person1 = new Person();
person1.sayName(); / / xiao Ming

var person2 = new Person();
person2.sayName(); / / xiao Ming

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

1. Understand the prototype object

When you create a new function, you create a prototype property for the function that points to the function’s prototype object. By default, all prototype objects automatically get a constructor property that contains a pointer to the function where the Prototype property is located. Such as a Person. The prototype. The constructor to the Person. With this constructor, we can continue to add additional properties and methods to the prototype object.

After a custom constructor is created, its prototype object acquires only the constructor attribute by default. As for the other methods, they are inherited from Object. Call the constructor to create a new instance that contains a [[Prototype]]. The real implementation is to support an attribute __proto__ on each object. This connection exists between the prototype objects instantiated in the constructor, but not between the constructors. Here is the little Red Book.

Although our instance does not contain attributes and methods, we can call person1.sayName(). This is done by looking up the properties of the object.

Although none of the implementations have access to [[Prototype]], you can use the isPrototypeOf() method to determine whether this relationship exists between objects.

console.log(Person.prototype.isPrototypeOf(person1)); // true
console.log(Person.prototype.isPrototypeOf(person2)); // true

console.log(Object.getPrototypeOf(person1) === Person.prototype) //true
console.log(Object.getPrototypeOf(person1).name) / / xiao Ming

Copy the code

When we read a property, a search is performed. The search starts with the object instance itself. Returns if an attribute with the given name is found in the instance. If not, search continues for the prototype object to which the pointer points.

Although it is possible to access the value stored in the stereotype through the object instance, it is not possible to override the value in the stereotype through the object instance. If a property name exists in the stereotype instance, we create the property in that instance, which will mask the value in the stereotype.

2. Stereotypes and the IN operator

There are two ways to use the IN operator: alone and in for-in loops. When used alone, the IN operator returns true based on whether the object can access the property, whether it is in the object or in the stereotype. HasOwnPrototype returns true only if the property exists in the object. HasPrototypeProperty () returns true only if the property is in the prototype

3. Simpler prototyping

 function Person {
 }
 
 Person.prototype = {
     name: "Xiao Ming".age: 29.job: "Engineer".sayName: function() {
         console.log(this.name); }}Copy the code

Note that the constructor property here no longer points to Person. We’re essentially rewriting the Prototype Object here, so the constructor property becomes the constructor property of the new Object (pointing to the Object constructor) instead of executing the Person constructor. You can deliberately set Constructor to a specific value.

 function Person {
 }
 
 Person.prototype = {
     constructor: Person,
     name: "Xiao Ming".age: 29.job: "Engineer".sayName: function() {
         console.log(this.name); }}Copy the code

Note: The constructor property we set this way causes its [[Enumerable]] property to be set to true. By default, the native constructor property is not Enumerable. To set not enumerable:

 function Person {
 }
 
 Person.prototype = {
     name: "Xiao Ming".age: 29.job: "Engineer".sayName: function() {
         console.log(this.name); }}// Reset the constructor to use only ecmascript5-compatible browsers
 Object.defineProperty(Person.pertotype, "constructor", {
     enumerablea: false.value: Person
 })
 
Copy the code

Prototype chain