search
HomeWeb Front-endJS TutorialDetailed explanation of prototypal inheritance in JavaScript_javascript tips

JavaScript is an object-oriented language. There is a very classic saying in JavaScript, everything is an object. Since it is object-oriented, it has three major characteristics of object-oriented: encapsulation, inheritance, and polymorphism. What we are talking about here is JavaScript inheritance, and we will talk about the other two later.

JavaScript inheritance is different from C inheritance. C inheritance is based on classes, while JavaScript inheritance is based on prototypes.

Now comes the problem.

What is a prototype? For the prototype, we can refer to the class in C, which also saves the properties and methods of the object. For example, let’s write a simple object

Copy code The code is as follows:

function Animal(name) {
This.name = name;
}
Animal.prototype.setName = function(name) {
This.name = name;
}
var animal = new Animal("wangwang");

We can see that this is an object Animal, which has an attribute name and a method setName. It should be noted that once the prototype is modified, such as adding a method, all instances of the object will share this method. For example

Copy code The code is as follows:

function Animal(name) {
This.name = name;
}
var animal = new Animal("wangwang");

At this time, animal only has the name attribute. If we add a sentence,

Copy code The code is as follows:

Animal.prototype.setName = function(name) {
This.name = name;
}

At this time, animal will also have a setName method.

Inherit this copy - starting from the empty object. We know that among the basic types of JS, there is one called object, and its most basic instance is the empty object, that is, the instance generated by directly calling new Object(), or It is declared using the literal { }. An empty object is a "clean object" with only predefined properties and methods, and all other objects inherit from the empty object, so all objects have these predefined properties and methods. A prototype is actually an object instance. The meaning of prototype is: if the constructor has a prototype object A, then the instances created by the constructor must be copied from A. Since the instance is copied from object A, the instance must inherit all of A's properties, methods, and other properties. So, how is replication achieved? Method 1: Construction copying Each time an instance is constructed, an instance is copied from the prototype. The new instance and the prototype occupy the same memory space. Although this makes obj1 and obj2 "completely consistent" with their prototypes, it is also very uneconomical - the consumption of memory space will increase rapidly. As shown in the picture:


Method 2: Copy-on-write This strategy comes from a consistent trick to the system: copy-on-write. A typical example of this kind of deception is the dynamic link library (DDL) in the operating system, whose memory area is always copied on write. As shown in the picture:


We only need to indicate in the system that obj1 and obj2 are equal to their prototypes, so that when reading, we only need to follow the instructions to read the prototypes. When we need to write the properties of an object (such as obj2), we copy a prototype image and point future operations to this image. As shown in the picture:


The advantage of this method is that we don’t need a lot of memory overhead when creating instances and reading attributes. We only use some code to allocate memory when writing for the first time, which brings some code and memory overhead. . But there is no such overhead anymore, because the efficiency of accessing the image is the same as accessing the prototype. However, for systems that frequently perform write operations, this method is no more economical than the previous method. Method 3: Read traversal This method changes the replication granularity from prototype to member. The characteristic of this method is that only when writing a member of an instance, the member information is copied to the instance image. When writing an object attribute, for example (obj2.value=10), an attribute value named value will be generated and placed in the member list of the obj2 object. Look at the picture:

It can be found that obj2 is still a reference pointing to the prototype, and no object instance of the same size as the prototype is created during the operation. In this way, write operations do not result in large memory allocations, so memory usage becomes economical. The difference is that obj2 (and all object instances) need to maintain a member list. This member list follows two rules: It is guaranteed to be accessed first when reading. If no attribute is specified in the object, an attempt is made to traverse the entire prototype chain of the object until the prototype is empty or the attribute is found. The prototype chain will be discussed later. Obviously, among the three methods, read traversal has the best performance. Therefore, JavaScript's prototypal inheritance is read traversal. constructor People who are familiar with C will definitely be confused after reading the code of the top object. It’s easier to understand without the class keyword. After all, there is the function keyword, which is just a different keyword. But what about constructors? In fact, JavaScript also has a similar constructor, but it is called a constructor. When using the new operator, the constructor has actually been called and this is bound to an object. For example, we use the following code

Copy code The code is as follows:

var animal = Animal("wangwang");

animal will be undefined. Some people will say that no return value is of course undefined. Then if you change the object definition of Animal:

Copy code The code is as follows:

function Animal(name) {
This.name = name;
Return this;
}

Guess what animal is now?
At this time, the animal becomes a window. The difference is that the window is extended so that the window has a name attribute. This is because this defaults to window, which is the top-level variable, if it is not specified. Only by calling the new keyword can the constructor be called correctly. So, how to prevent users from missing the new keyword? We can make some small changes:

Copy code The code is as follows:

function Animal(name) {
If(!(this instanceof Animal)) {
          return new Animal(name);
}
This.name = name;
}

This way you’ll be foolproof. The constructor is also used to indicate which object the instance belongs to. We can use instanceof to judge, but instanceof returns true for both ancestor objects and real objects during inheritance, so it is not suitable. When constructor is called with new, it points to the current object by default.

Copy code The code is as follows:

console.log(Animal.prototype.constructor === Animal); // true

We can think differently: prototype has no value at all when the function is initialized. The implementation may be the following logic

//Set __proto__ as a built-in member of the function, and get_prototyoe() is its method

Copy code The code is as follows:

var __proto__ = null;
function get_prototype() {
If(!__proto__) {
​​​​ __proto__ = new Object();
         __proto__.constructor = this;
}
Return __proto__;
}

The advantage of this is that it avoids creating an object instance every time a function is declared, saving overhead. The constructor can be modified, which will be discussed later. Prototype-based inheritance I believe everyone knows almost what inheritance is, so I won’t show off the lower limit of IQ.

There are several types of JS inheritance, here are two types

1. Method 1 This method is the most commonly used and has better security. Let’s first define two objects

Copy code The code is as follows:

function Animal(name) {
This.name = name;
}
function Dog(age) {
This.age = age;
}
var dog = new Dog(2);

To construct inheritance is very simple, point the prototype of the child object to the instance of the parent object (note that it is an instance, not an object)

Copy code The code is as follows:

Dog.prototype = new Animal("wangwang");

At this time, dog will have two attributes, name and age. And if you use the instanceof operator

on dog

Copy code The code is as follows:

console.log(dog instanceof Animal); // true
console.log(dog instanceof Dog); // false

In this way, inheritance is achieved, but there is a small problem

Copy code The code is as follows:

console.log(Dog.prototype.constructor === Animal); // true
console.log(Dog.prototype.constructor === Dog); // false

You can see that the object pointed to by the constructor has changed, which does not meet our purpose. We cannot judge who our new instance belongs to. Therefore, we can add a sentence:

Copy code The code is as follows:

Dog.prototype.constructor = Dog;

Let’s take a look again:

Copy code The code is as follows:

console.log(dog instanceof Animal); // false
console.log(dog instanceof Dog); // true

done. This method is part of the maintenance of the prototype chain and will be explained in detail below. 2. Method 2 This method has its advantages and disadvantages, but the disadvantages outweigh the advantages. Let’s look at the code first

Copy code The code is as follows:

function Animal(name) {<br>
This.name = name;<br>
}<br>
Animal.prototype.setName = function(name) {<br>
This.name = name;<br>
}<br>
function Dog(age) {<br>
This.age = age;<br>
}<br>
Dog.prototype = Animal.prototype;<br>

This achieves prototype copying.

The advantage of this method is that it does not need to instantiate objects (compared to method 1), which saves resources. The disadvantages are also obvious. In addition to the same problem as above, that is, the constructor points to the parent object, it can only copy the properties and methods declared by the prototype of the parent object. In other words, in the above code, the name attribute of the Animal object cannot be copied, but the setName method can be copied. The most fatal thing is that any modification to the prototype of the child object will affect the prototype of the parent object, that is, the instances declared by both objects will be affected. Therefore, this method is not recommended.

Prototype Chain

Everyone who has written inheritance knows that inheritance can be inherited at multiple levels. In JS, this constitutes the prototype chain. The prototype chain has been mentioned many times above, so what is the prototype chain? An instance should at least have a proto attribute pointing to the prototype, which is the basis of the object system in JavaScript. However, this property is invisible. We call it the "internal prototype chain" to distinguish it from the "constructor prototype chain" composed of the constructor's prototype (which is what we usually call the "prototype chain"). Let’s first construct a simple inheritance relationship according to the above code:

Copy code The code is as follows:

function Animal(name) {
This.name = name;
}
function Dog(age) {
This.age = age;
}
var animal = new Animal("wangwang");
Dog.prototype = animal;
var dog = new Dog(2);

As a reminder, as mentioned before, all objects inherit empty objects. Therefore, we constructed a prototype chain:


We can see that the prototype of the child object points to the instance of the parent object, forming the constructor prototype chain. The internal proto object of the child instance also points to the instance of the parent object, forming an internal prototype chain. When we need to find a certain attribute, the code is similar to

Copy code The code is as follows:

function getAttrFromObj(attr, obj) {
If(typeof(obj) === "object") {
        var proto = obj;
​​​​while(proto) {
If(proto.hasOwnProperty(attr)) {
                     return proto[attr];
            }
              proto = proto.__proto__;
}
}
Return undefined;
}

In this example, if we look for the name attribute in dog, it will be searched in the member list in dog. Of course, it will not be found because the member list of dog now only has age. Then it will continue to search along the prototype chain, that is, the instance pointed to by .proto, that is, in animal, it will find the name attribute and return it. If it is looking for a property that does not exist, and cannot find it in animal, it will continue to search along .proto and find an empty object. If it cannot find it, it will continue to search along .proto, and the empty object. proto points to null, looking for exit.

Maintenance of the prototype chain We raised a question when we just talked about prototypal inheritance. When using method 1 to construct inheritance, the constructor of the child object instance points to the parent object. The advantage of this is that we can access the prototype chain through the constructor attribute, but the disadvantages are also obvious. An object, the instance it generates should point to itself, that is,

Copy code The code is as follows:

(new obj()).prototype.constructor === obj;

Then, when we override the prototype property, the constructor of the instance generated by the sub-object does not point to itself! This goes against the original intention of the constructor. We mentioned a solution above:

Copy code The code is as follows:

Dog.prototype = new Animal("wangwang");
Dog.prototype.constructor = Dog;

Looks like there is no problem. But in fact, this brings a new problem, because we will find that we can't backtrack the prototype chain, because we can't find the parent object, and the .proto property of the internal prototype chain is inaccessible. Therefore, SpiderMonkey provides an improved solution: adding an attribute named __proto__ to any created object, which always points to the prototype used by the constructor. In this way, any modification to the constructor will not affect the value of __proto__, which makes it easier to maintain the constructor.

However, there are two more problems:

__proto__ is overridable, which means there are still risks when using it

__proto__ is a special processing of spiderMonkey and cannot be used in other engines (such as JScript).

We also have another way, which is to keep the prototype's constructor properties and initialize the instance's constructor properties within the subclass constructor function.

The code is as follows: Rewrite the sub-object

Copy code The code is as follows:

function Dog(age) {
This.constructor = arguments.callee;
This.age = age;
}
Dog.prototype = new Animal("wangwang");

In this way, the constructors of all child object instances correctly point to the object, and the prototype's constructor points to the parent object. Although this method is relatively inefficient because the constructor attribute must be rewritten every time an instance is constructed, there is no doubt that this method can effectively solve the previous contradiction. ES5 takes this situation into consideration and completely solves this problem: you can use Object.getPrototypeOf() at any time to get the real prototype of an object without having to access the constructor or maintain an external prototype chain. Therefore, to find object properties as mentioned in the previous section, we can rewrite it as follows:

Copy code The code is as follows:

function getAttrFromObj(attr, obj) {
If(typeof(obj) === "object") {
do {
          var proto = Object.getPrototypeOf(dog);
If(proto[attr]) {
                     return proto[attr];
            }
}
​​​​while(proto);
}
Return undefined;
}

Of course, this method can only be used in browsers that support ES5. For backward compatibility, we still need to consider the previous method. A more appropriate method is to integrate and encapsulate these two methods. I believe readers are very good at this, so I won’t show off here.

Statement
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn
es6数组怎么去掉重复并且重新排序es6数组怎么去掉重复并且重新排序May 05, 2022 pm 07:08 PM

去掉重复并排序的方法:1、使用“Array.from(new Set(arr))”或者“[…new Set(arr)]”语句,去掉数组中的重复元素,返回去重后的新数组;2、利用sort()对去重数组进行排序,语法“去重数组.sort()”。

JavaScript的Symbol类型、隐藏属性及全局注册表详解JavaScript的Symbol类型、隐藏属性及全局注册表详解Jun 02, 2022 am 11:50 AM

本篇文章给大家带来了关于JavaScript的相关知识,其中主要介绍了关于Symbol类型、隐藏属性及全局注册表的相关问题,包括了Symbol类型的描述、Symbol不会隐式转字符串等问题,下面一起来看一下,希望对大家有帮助。

原来利用纯CSS也能实现文字轮播与图片轮播!原来利用纯CSS也能实现文字轮播与图片轮播!Jun 10, 2022 pm 01:00 PM

怎么制作文字轮播与图片轮播?大家第一想到的是不是利用js,其实利用纯CSS也能实现文字轮播与图片轮播,下面来看看实现方法,希望对大家有所帮助!

JavaScript对象的构造函数和new操作符(实例详解)JavaScript对象的构造函数和new操作符(实例详解)May 10, 2022 pm 06:16 PM

本篇文章给大家带来了关于JavaScript的相关知识,其中主要介绍了关于对象的构造函数和new操作符,构造函数是所有对象的成员方法中,最早被调用的那个,下面一起来看一下吧,希望对大家有帮助。

JavaScript面向对象详细解析之属性描述符JavaScript面向对象详细解析之属性描述符May 27, 2022 pm 05:29 PM

本篇文章给大家带来了关于JavaScript的相关知识,其中主要介绍了关于面向对象的相关问题,包括了属性描述符、数据描述符、存取描述符等等内容,下面一起来看一下,希望对大家有帮助。

javascript怎么移除元素点击事件javascript怎么移除元素点击事件Apr 11, 2022 pm 04:51 PM

方法:1、利用“点击元素对象.unbind("click");”方法,该方法可以移除被选元素的事件处理程序;2、利用“点击元素对象.off("click");”方法,该方法可以移除通过on()方法添加的事件处理程序。

整理总结JavaScript常见的BOM操作整理总结JavaScript常见的BOM操作Jun 01, 2022 am 11:43 AM

本篇文章给大家带来了关于JavaScript的相关知识,其中主要介绍了关于BOM操作的相关问题,包括了window对象的常见事件、JavaScript执行机制等等相关内容,下面一起来看一下,希望对大家有帮助。

foreach是es6里的吗foreach是es6里的吗May 05, 2022 pm 05:59 PM

foreach不是es6的方法。foreach是es3中一个遍历数组的方法,可以调用数组的每个元素,并将元素传给回调函数进行处理,语法“array.forEach(function(当前元素,索引,数组){...})”;该方法不处理空数组。

See all articles

Hot AI Tools

Undresser.AI Undress

Undresser.AI Undress

AI-powered app for creating realistic nude photos

AI Clothes Remover

AI Clothes Remover

Online AI tool for removing clothes from photos.

Undress AI Tool

Undress AI Tool

Undress images for free

Clothoff.io

Clothoff.io

AI clothes remover

AI Hentai Generator

AI Hentai Generator

Generate AI Hentai for free.

Hot Article

R.E.P.O. Energy Crystals Explained and What They Do (Yellow Crystal)
2 weeks agoBy尊渡假赌尊渡假赌尊渡假赌
Repo: How To Revive Teammates
4 weeks agoBy尊渡假赌尊渡假赌尊渡假赌
Hello Kitty Island Adventure: How To Get Giant Seeds
4 weeks agoBy尊渡假赌尊渡假赌尊渡假赌

Hot Tools

WebStorm Mac version

WebStorm Mac version

Useful JavaScript development tools

MinGW - Minimalist GNU for Windows

MinGW - Minimalist GNU for Windows

This project is in the process of being migrated to osdn.net/projects/mingw, you can continue to follow us there. MinGW: A native Windows port of the GNU Compiler Collection (GCC), freely distributable import libraries and header files for building native Windows applications; includes extensions to the MSVC runtime to support C99 functionality. All MinGW software can run on 64-bit Windows platforms.

Dreamweaver CS6

Dreamweaver CS6

Visual web development tools

Atom editor mac version download

Atom editor mac version download

The most popular open source editor

SublimeText3 English version

SublimeText3 English version

Recommended: Win version, supports code prompts!