Overview
All objects in JavaScript have their own inheritance chain. That is, each object inherits another object, which is called a "prototype" object. Except for null, it does not have its own prototype object.
The importance of a prototype object is that if object A is a prototype of object B, object B can obtain all the properties and methods of object A. The Object.getPrototypof method is used to get the prototype object of the current object.
var p = Object.getPrototypeOf(obj);
In the above code, object p is the prototype object of object obj.
The Object.create method is used to generate a new object and inherit the specified object.
var obj = Object.create(p);
In the above code, the prototype of the newly generated obj object is object p.
The non-standard __proto__ attribute (two underscores in front and behind) can rewrite the prototype object of a certain object. However, you should try to use this property as little as possible, but use Object.getPrototypeof() and Object.setPrototypeOf() to read and write prototype objects.
var obj = {};var p = {};obj.__proto__ = p;Object.getPrototypeOf(obj) === p // trueThe above code sets the p object as the prototype of the obj object through the __proto__ attribute.
Here is a practical example.
var a = {x: 1};var b = {__proto__: a};bx // 1In the above code, object b sets its prototype object as a object through the __proto__ attribute, so object b can get all the properties and methods of object a. The b object itself does not have an x attribute, but the JavaScript engine finds its prototype object a through the __proto__ attribute, and then reads the x attribute of a.
The new command creates a new instance object through a constructor. It is essentially binding the prototype of the instance object to the prototype property of the constructor, and then executing the constructor on the instance object.
var o = new Foo();// is equivalent to var o = new Object();o.__proto__ = Foo.prototype;Foo.call(o);
The prototype object's own __proto__ attribute can also point to other objects, thereby forming a "prototype chain" level by level.
var a = { x: 1 };var b = { __proto__: a };var c = { __proto__: b };cx // 1It should be noted that looking for a certain attribute in the prototype chain has an impact on performance. The higher the level of the prototype object you are looking for, the greater the impact on performance. If you look for a non-existent property, it will traverse the entire prototype chain.
This action pointing
No matter where this is defined, when used, it always points to the current object, not the prototype object.
var o = { a: 2, m: function(b) { return this.a + 1; }};var p = Object.create(o);pa = 12;pm() // 13In the above code, the m method of the p object comes from its prototype object o. At this time, this object inside the m method does not point to o, but to p.
Inheritance of constructors
This section introduces how to make one constructor inherit another constructor.
Assume there is a Shape constructor.
function Shape() { this.x = 0; this.y = 0;}Shape.prototype.move = function (x, y) { this.x += x; this.y += y; console.info('Shape moved.');};The Rectangle constructor inherits Shape. function Rectangle() { Shape.call(this); // Call the parent class constructor}// Another way to write function Rectangle() { this.base = Shape; this.base();}// Subclass inherits the parent class method Rectangle.prototype = Object.create(Shape.prototype);Rect.prototype.constructor = Rectangle;var rect = new Rectangle();rect instanceof Rectangle // truerect instanceof Shape // truerect.move(1, 1) // 'Shape moved.'The above code shows that the inheritance of the constructor is divided into two parts, one is that the subclass calls the constructor method of the parent class, and the other is that the prototype of the subclass points to the prototype of the parent class.
In the above code, the subclass inherits the parent class as a whole. Sometimes, only the inheritance of a single method is required, and the following writing method can be used.
ClassB.prototype.print = function() { ClassA.prototype.print.call(this); // some code}In the above code, the print method of subclass B first calls the print method of parent class A, and then deploys its own code. This is equivalent to inheriting the print method of parent class A.
__proto__ attribute
The __proto__ attribute points to the prototype object of the current object, that is, the prototype attribute of the constructor.
var obj = new Object();obj.__proto__ === Object.prototype// trueobj.__proto__ === obj.constructor.prototype// true
The above code first creates a new object obj, its __proto__ attribute, pointing to the prototype attribute of the constructor (Object or obj.constructor). Therefore, after comparing the two, return true.
Therefore, there are three ways to obtain the prototype object of the instance object obj.
Among the above three methods, the first two are not very reliable. The latest ES6 standard stipulates that only the browser needs to deploy the __proto__ attribute, and other environments may not be deployed. However, obj.constructor.prototype may fail when manually changing the prototype object.
var P = function () {};var p = new P();var C = function () {};C.prototype = p;var c = new C();c.constructor.prototype === p // falseIn the above code, the prototype object of the C constructor is changed to p, and the result is that c.constructor.prototype is distorted. Therefore, when changing the prototype object, the constructor attribute should be set at the same time.
C.prototype = p;C.prototype.constructor = C;c.constructor.prototype === p // true
Therefore, it is recommended to use the third Object.getPrototypeOf method to obtain the prototype object. The usage of this method is as follows.
var o = new Object();Object.getPrototypeOf(o) === Object.prototype// true
You can use the Object.getPrototypeOf method to check whether the browser supports the __proto__ attribute, which is not supported by old browsers.
Object.getPrototypeOf({ __proto__: null }) === nullThe above code sets the __proto__ attribute of an object to null, and then uses the Object.getPrototypeOf method to obtain the prototype of this object to determine whether it is equal to null. If the current environment supports the __proto__ attribute, the comparison result of the two should be true.
With the __proto__ attribute, it is easy to set up the prototype of the instance object. Suppose there are three objects: machine, vehicle and car, where machine is the prototype of vehicle and vehicle is the prototype of car, which can be set with just two lines of code.
vehicle.__proto__ = machine;car.__proto__ = vehicle;
The following is an example. The properties defined on the prototype object are read respectively through the __proto__ attribute and the constructor.prototype attribute.
Array.prototype.p = 'abc';var a = new Array();a.__proto__.p // abca.constructor.prototype.p // abc
Obviously, __proto__ looks a little more concise.
When an instance object is generated through a constructor, the __proto__ attribute of the instance object automatically points to the prototype object of the constructor.
var f = function (){};var a = {};f.prototype = a;var o = new f();o.__proto__ === a// trueInheritance of attributes
There are two types of attributes. One is the native attribute of the object itself, and the other is the inherited attribute inherited from the prototype.
Native properties of an object
All properties of the object itself can be obtained using the Object.getOwnPropertyNames method.
Object.getOwnPropertyNames(Date)// ["parse", "arguments", "UTC", "caller", "name", "prototype", "now", "length"]
Among the properties of the object itself, some are enumerable (enumerable), while others are not enumerable. Only obtain those properties that can be enumerated, use the Object.keys method.
Object.keys(Date) // []hasOwnProperty()
The hasOwnProperty method returns a Boolean value to determine whether a certain property is defined on the object itself or on the prototype chain.
Date.hasOwnProperty('length')// trueDate.hasOwnProperty('toString')// falseThe hasOwnProperty method is the only method in JavaScript that does not traverse the prototype chain when processing object properties.
Inheritance properties of an object
Objects created with the Object.create method will inherit the properties of all prototype objects.
var proto = { p1: 123 };var o = Object.create(proto);o.p1 // 123o.hasOwnProperty("p1") // falseGet all attributes
To determine whether an object has a certain property (whether it is its own or inherited), use the in operator.
"length" in Date // true"toString" in Date // true
Get all enumerable properties of an object (whether it is own or inherited), and you can use a for-in loop.
var o1 = {p1: 123};var o2 = Object.create(o1,{ p2: { value: "abc", enumerable: true }});for (p in o2) {console.info(p);}// p2// p1In order to obtain the object's own properties in the for...in loop, you can use the hasOwnProperty method to judge.
for ( var name in object ) { if ( object.hasOwnProperty(name) ) { /* loop code */ }}To obtain all the properties of the object (whether it is own or inherited, and whether it is enumerable), you can use the following function.
function inheritedPropertyNames(obj) { var props = {}; while(obj) { Object.getOwnPropertyNames(obj).forEach(function(p) { props[p] = true; }); obj = Object.getPrototypeOf(obj); } return Object.getOwnPropertyNames(props);}The usage is as follows:
inheritedPropertyNames(Date)// ["caller", "constructor", "toString", "UTC", "call", "parse", "prototype", "__defineSetter__", "__lookupSetter__", "length", "arguments", "bind", "__lookupGetter__", "isPrototypeOf", "toLocaleString", "propertyIsEnumerable", "valueOf", "apply", "__defineGetter__", "name", "now", "hasOwnProperty"]
Copy of the object
If you want to copy an object, you need to do the following two things.
Make sure that the copied object has the same prototype object as the original object.
Make sure that the copied object has the same properties as the original object.
The following is the function of copying the object written based on the above two points.
function copyObject(orig) { var copy = Object.create(Object.getPrototypeOf(orig)); copyOwnPropertiesFrom(copy, orig); return copy;}function copyOwnPropertiesFrom(target, source) { Object .getOwnPropertyNames(source) .forEach(function(propKey) { var desc = Object.getOwnPropertyDescriptor(source, propKey); Object.defineProperty(target, propKey, desc); }); return target;}Multiple inheritance
JavaScript does not provide multiple inheritance functions, that is, it does not allow one object to inherit multiple objects at the same time. However, this function can be achieved through workarounds.
function M1(prop) { this.hello = prop;}function M2(prop) { this.world = prop;}function S(p1, p2) { this.base1 = M1; this.base1(p1); this.base2 = M2; this.base2(p2);}S.prototype = new M1();var s = new S(111, 222);s.hello // 111s.world // 222In the above code, subclass S inherits both parent classes M1 and M2. Of course, from the perspective of the inheritance chain, S has only one parent class M1, but since on the instance of S, the constructors of M1 and M2 are executed at the same time, it inherits the methods of these two classes at the same time.