The peak season of Gold, Silver and Four is about to begin. During this period of time, some friends go to the interview, and they communicate with each other and find that the highest frequency of questions is: talk about your understanding of prototype and prototype chain?

The invisible blade is the most deadly. — Lord of shadow flow

A seemingly the most basic interview questions, but this send points, the answer is not good may become send a proposition. Let’s discuss ~

Prototype-based languages?

JavaScript is often described as a prototype-based language — each instance object has a private property (called proto) that points to its prototype constructor. The prototype object also has a prototype object of its own (PROTO), cascading up until an object’s prototype object is null. Null, by definition, has no prototype and serves as the last link in the prototype chain.

Almost all objects in JavaScript are instances of Object at the top of the prototype chain.

In traditional object orientation, a “class” is first defined, and then all properties and methods defined in the class are copied into the instance when an object instance is created. This is not the case in JavaScript. Instead, you create a link between the object instance and its constructor (which is the **proto property, derived from the constructor’s Prototype ** property), and then find those properties and methods in the constructor by going up the prototype chain.

Note: Understand the prototype of the object (can be passedObject.getPrototypeOf(obj)Or has been abandoned__proto__Property acquisition) and the constructorprototypeThe distinction between attributes is important. The former is a property that exists on every instance, and the latter is a property of the constructor. In other words,Object.getPrototypeOf(new Foobar())andFoobar.prototypePointing to the same object.

Object.getprototypeof (obj): The method returns the prototype of the specified Object, or null if there are no inherited properties.

__proto__: The __proto__ attribute points to the prototype of its constructor. (This feature has been removed from Web standards, although some browsers still support it.)


The above description is abstract; Let’s start with an example.

In javascript, each function has a special property called prototype (** Prototype **), as shown below. Note that the code below is a separate piece (it is safe in the absence of other code on the page). For the best learning experience, open a console (Ctrl+Shift+I in Chrome and Firefox), switch to the “Console” TAB, copy and paste the JavaScript code below, and press Enter to run it

Function coolFn(){} console.log(coolfn.prototype); // No matter how you declare functions, functions always have a default value of the prototype property var coolFn = function(){}; console.log( coolFn.prototype );Copy the code

As you can see above, the coolFn function has a default stereotype property, which is rendered on the console. After running this code, an object like this should appear on the console.

ƒ {constructor: ƒ coolFn(), __proto__: {constructor: ƒ Object(), hasOwnProperty: ƒ hasOwnProperty(), isPrototypeOf: ƒ isPrototypeOf(), propertyIsEnumerable: ƒ propertyIsEnumerable(), toLocaleString: ƒ toLocaleString(), toString: ƒ toString(), valueOf: ƒ the valueOf ()}}Copy the code

Now we can add some properties to the coolFn prototype, as shown below.

Function coolFn(){} coolFn. Prototype. foo = "bar"; console.log( coolFn.prototype );Copy the code

Results:

{foo: "bar", constructor: ƒ coolFn(), __proto__: {constructor: ƒ Object(), hasOwnProperty: ƒ hasOwnProperty(), isPrototypeOf: ƒ isPrototypeOf(), propertyIsEnumerable: ƒ propertyIsEnumerable(), toLocaleString: ƒ toLocaleString(), toString: ƒ toString(), valueOf: ƒ the valueOf ()}}Copy the code

We can then use the new operator to create an instance of coolFn based on our current prototype. The correct way to use the new operator is to prefix the function name with a new prefix when a function is called normally. With this method, the function is called with a new, and it returns an instantiation of the function. You can then add some attributes to the object:

Function coolFn(){} coolFn. Prototype. foo = "bar"; // Add property to prototype var coolInstancing = new coolFn(); coolInstancing.prop = "some value"; // Add a property console.log(coolInstancing) on the object;Copy the code

Results:

{prop: "some value", __proto__: {foo: "bar", constructor: ƒ coolFn(), __proto__: ƒ {constructor: ƒ Object(), hasOwnProperty: ƒ hasOwnProperty(), isPrototypeOf: ƒ isPrototypeOf(), ƒ IsEnumerable: ƒ propertyIsEnumerable(), toLocaleString: ƒ toLocaleString(), toString: ƒ toString(), valueOf: ƒ valueOf()}}}Copy the code

  

As you can see above the __proto__ property of coolInstancing is ** coolfn. prototype.** But what good does that do?

Well, when you visit a coolInstancing property, the browser first looks to see if coolInstancing has that property. If coolInstancing doesn’t have this property, then the browser will look for it in coolInstancing’s __proto__ (that is, coolfn.prototype). If coolInstancing __proto__ has this property, then this property on coolInstancing __proto__ will be used. Otherwise, if coolInstancing’s __proto__ doesn’t have this property, the browser will look up coolInstancing’s __proto__ to see if it does.


By default, the __proto__ of the prototype attribute for all functions is window.object.prototype. So coolInstancing __proto__ (aka coolfn. prototype’s __proto__ (aka object.prototype)) will be looked up to see if it has this property. If you don’t find the property in it, then look at coolInstancing’s __proto__ __proto__. There’s a problem, however: coolInstancing’s __proto__ __proto__ doesn’t exist. Finally, all __proto__ on the prototype chain is found, and the attribute is not found on all __proto__ declared by the browser, and it is concluded that the attribute is undefined.

Function coolFn(){} coolFn. Prototype. foo = "bar"; Var coolInstancing = new coolFn(); coolInstancing.prop = "some value"; The console. The log (" coolInstancing. Prop: "+ coolInstancing. Prop); The console. The log (" coolInstancing. Foo: "+ coolInstancing. Foo); The console. The log (" coolFn. Prop: "+ coolFn. Prop); The console. The log (" coolFn. Foo: "+ coolFn. Foo); The console. The log (" coolFn. Prototype. Prop: "+ coolFn. Prototype. Prop); console.log("coolFn.prototype.foo: " + coolFn.prototype.foo);Copy the code

Results:

CoolInstancing. Prop: some value coolInstancing. Foo: bar coolFn. CoolFn. Prototype. prop: undefined coolFn. Prototype. foo: barCopy the code

Understanding prototype objects

Let’s go back to the example of the Person() constructor. Write the following code examples to the browser console in turn.

In this example we will first define a constructor function:

Function Person(first, last, age, gender, interests) {// attribute and method definition};Copy the code

  

Then create an object instance on the console:

var person1 = new Person('Bob', 'Smith', 32, 'male', ['music', 'skiing']);

Copy the code

  

Enter “person1.” in the JavaScript console, and you’ll see that the browser will auto-complete the object based on the available member names:

In this list, you can see the members — name, age, gender, interests, Bio, greeting — defined in the prototype object of Person1, the Person() constructor. There are also other members — watch, valueOf, and so on — that are defined on top of the prototype Object, Object, of the Person() constructor. The diagram below shows how the prototype chain works.

So what happens when the “actually defined on Object” method of person1 is called? Such as:

person1.valueOf()

Copy the code

This method simply returns the value of the called object. In this example, the following process occurs:

  • The browser first checks if the Person1 object has a valueOf() method available.

  • If not, the browser checks whether the prototype object of the Person1 object (that is, the object pointed to by the Prototype attribute of the Person constructor) has a valueof() method available.

  • If not, the browser checks to see if the prototype Object of the Object pointed to by the prototype attribute of the Person() constructor has a valueOf() method available. There’s this method, and that method gets called.

Note: It is important to reiterate that methods and properties in the stereotype chain are not copied to other objects — they need to be accessed through the “stereotype chain” described earlier.

Note: There is no official method for accessing an object’s prototype object directly — the “link” in the prototype chain is defined in an internal property, represented in the JavaScript language standard as **[[prototype]]** (see ECMAScript). However, most modern browsers do provide an attribute called **__proto__** (with two underscores on each side), which contains the prototype of the object. You can try typing **person1.__proto__** and **person1.__proto__.__proto__** to see what the prototype chain looks like in the code!

The prototype property

So, where are those inherited properties and methods defined? If you look at the Object reference page, you’ll see a number of properties and methods listed on the left — much more than the number of inherited members we see in the Person1 Object. Some properties or methods are inherited and others are not — why?

The reason for this is that inherited properties and methods are defined on top of the Prototype property (you can call it a sub-namespace) — those whose names start with Object.prototype. Properties that start with **Object**. The value of the Prototype attribute is an object where properties and methods that we want to inherit from objects downstream of the prototype chain are stored.

So object.prototype.watch (), object.prototype.valueof (), etc., apply to any Object type that inherits from Object(), including new Object instances created using constructors.

Object.is(), object.keys (), and other members that are not in the Prototype Object are not inherited by “Object instances” or “Object types inherited from Object()”. These methods/properties can only be used by the Object() constructor itself.

Note: This seems strange — constructors are functions themselves, so how can you define a method in a constructor function? Function is also an object type **. **

You can check existing Prototype properties. Going back to the previous example, type in the JavaScript console:

Person.prototype 2. Not much output, after all we didn't define any members for the custom constructor prototype. By default, the constructor's 'prototype' property is initially blank. Now try:Copy the code
 Object.prototype

Copy the code

You’ll see a number of methods defined on the Object prototype property; As shown earlier, any Object that inherits from Object can use these methods.

JavaScript is full of examples of inheritance through prototype chains. For example, you can try to find methods and properties from the prototypes of String, Date, Number, and Array global objects. They both define some methods on the prototype, so when you create a string:

var myString = 'This is my string.';

Copy the code

  

MyString immediately had some useful methods, such as split(), indexOf(), replace(), and so on.

Important: The Prototype attribute is probably one of the most confusing names in JavaScript. You might think that the this keyword refers to the prototype object of the current object, but it doesn’t (remember? The prototype object is an internal object and should be accessed using __proto__. The Prototype property contains (points to) an object in which you define the members to be inherited.

The create () method

The object.create () method creates a new Object instance.

For example, type in the JavaScript console from the previous example:

``` var person2 = Object.create(person1); ` ` `Copy the code

What create() actually does is create a new object from the specified prototype object. Here the Person2 object is created from the person1 prototype object. On the console enter:

  ```
  person2.__proto__
  ```
Copy the code

The result is the return object person1.

The constructor property

Each instance object inherits from the stereotype a constructor property that points to the constructor used to construct this instance object.

  1. For example, continue to try the following command in the console:

    person1.constructor
    person2.constructor
    
    Copy the code

      

    Both return the Person() constructor because it contains the original definitions of these instances.

    As a neat tip, you can create another object instance with this constructor by adding a pair of parentheses (containing the required parameters) to the end of the constructor property. After all, the constructor is a function, so it can be called with parentheses; You can use this function as a constructor simply by adding the new keyword in front of it.

    In the console, enter:

      var person3 = new person1.constructor('Karen', 
                    'Stephenson', 26,
                    'female', 
                    ['playing drums', 'mountain climbing']);
    Copy the code

Now try to access the properties of the newly created object, for example:

    person3.name.first
    person3.age
    person3.bio()
Copy the code

Working normally. You don’t normally create new instances this way; But if you happen to have no reference to the original constructor for some reason, this approach can be useful.

In addition, the constructor attribute has other uses. For example, to get the name of the constructor for an instance of an object, you can use:

instanceName.constructor.name

Copy the code

Specifically, like this:

person1.constructor.name

Copy the code

How to Modify the prototype

How can we modify the constructor from the following exampleprototypeProperties.

Added a new method to the constructor’s Prototype:

Function Person(first, last, age, gender, interests) {// attribute and method definition}; Var person1 = new Person('Tammi', 'Smith', 32, 'neutral', ['music', 'skiing', 'kickboxing']); Person. The prototype. Farewell = function () {alert (enclosing name. The first + 'has left the building. Bye for now! '); }Copy the code

  

butfarewell()Methods are still availableperson1Object instances – The functionality available for older object instances is automatically updated. This proves the prototype chain model described earlier. In this inheritance model, methods of upstream objects are not copied to downstream object instances. These methods are not defined by the downstream object itself, but the browser finds them from the upstream object by going up the prototype chain. This inheritance model provides a powerful and extensible system of functionality.

You’ll rarely see attributes defined in the Prototype attribute because it’s not flexible enough. For example, you can add a property:

Person.prototype.fullName = 'Bob Smith';

Copy the code

  

But that’s not flexible enough, because people might not call it that. It is much better to form a fullName with name.first and name.last:

Person.prototype.fullName = this.name.first + ' ' + this.name.last;

Copy the code

  

However, this is ineffective because in this case this refers to the global scope, not the function scope. Accessing this property will only get undefined. But this statement is valid in a method previously defined on Prototype, where the statement is in function scope and can be successfully converted to object instance scope. You might define constant properties on Prototype (those you never need to change), but in general it’s better to define properties in a constructor.

Note: The question of what scope/object the ** **this** keyword refers to is beyond the scope of this article. In fact, it’s a bit complicated, so don’t worry if you don’t understand it now.

In fact, an extremely common object definition pattern is to define properties in the constructor (function body) and methods on the **prototype** ** attribute. ** Thus, constructors contain only property definitions, while methods are packed into separate code blocks, making the code more readable. Such as:

Function Test(a,b,c,d) {// attribute definition}; Test.prototype.x = function () {... } // define the second method: test.prototype. y = function () {... } // etc...Copy the code

The resources

  • Inheritance and prototype chain -MDN

  • Previously read a foreign friend based on JS prototype understanding, due to the long link lost

At the end

The above is about JS prototype, prototype chain understanding, please point out the shortcomings, exchange together, I hope to help you.

The New Year is coming, the students should all start to go home, I wish you a pleasant journey, the New Year suddenly rich, single ~

I’ve been on the top of mountains and I’ve been down on valleys, and I’ve learned a lot from both.