Unlike class-based programming languages, such as C + + and Java,javascript, inheritance is based on prototypes. At the same time, because JavaScript is a very flexible language, its implementation of inheritance is also very many ways.
The first basic concept is about constructors and prototype chains, where the parent object's constructor is called parent, and the child object's constructor is called the parent and child objects, respectively.
The object has a hidden property [[prototype]] (note that it is not prototype), which is __proto__ in Chrome and inaccessible in some environments, and it points to the prototype of the object. When you access the properties or methods of any one object, you first search for all of the properties of the object, and if you cannot find it, you will search for the properties on its prototype object, depending on [[prototype]] along the prototype chain, until you find it, or return undefined.
1. Prototype Chain inheritance:
The prototype chain is the default way to implement inheritance in JavaScript, and the easiest way to make a child object inherit the parent object is to point the prototype attribute of the child object constructor to an instance of the parent object:
Copy Code code as follows:
function Parent () {}
function child () {}
Child.prototype = new Parent ()
At this point, the child's prototype attribute is overridden to point to a new object, but the constructor attribute of the new object does not correctly point to the CHILD,JS engine and does not automatically complete the work for us. This requires us to manually point the constructor attribute of the child's prototype object back to the child:
Copy Code code as follows:
Child.prototype.constructor = Child
These are the default inheritance mechanisms in JavaScript that migrate properties and methods that need to be reused into a prototype object, while the parts that are not reusable are set to the object's own properties, but this inheritance requires a new instance as a prototype object, which is less efficient.
2. Prototype inheritance (non-prototype chain):
To avoid the problem of creating a prototype object instance repeatedly by the previous method, you can point the prototype of the child object constructor directly to the prototype of the parent object constructor, so that Properties and methods in all Parent.prototype can also be reused without the need to recreate the prototype object instance:
Copy Code code as follows:
Child.prototype = Parent.prototype
Child.prototype.constructor = Child
But we know that in JavaScript, objects exist as reference types, This approach actually points the saved pointer to the same object in Child.prototype and Parent.prototype, so when we want to extend some of the attributes in the child object prototype so that it inherits later, the prototype of the parent object will also be overwritten because the prototype object instance here is always the only one, which is also The disadvantage of this way of inheriting.
3. Temporary constructor Inheritance:
To solve the problem above, a temporary constructor can be used to play the role of a middle tier, and all child object prototypes are performed on an instance of the temporary constructor and do not affect the parent object's prototype:
Copy Code code as follows:
var F = function () {}
F.prototype = Parent.prototype
Child.prototype = new F ()
Child.prototype.constructor = Child
Also, in order to be able to access properties in the parent prototype in a child object, a property, such as Uber, that points to the parent object's prototype can be added to the child object constructor, so that the parent prototype object can be accessed directly from the child object via Child.constructor.uber.
We can encapsulate the above work into a function that can be easily implemented in the future by calling this function:
Copy Code code as follows:
function extend (child, Parent) {
var F = function () {}
F.prototype = Parent.prototype
Child.prototype = new F ()
Child.prototype.constructor = Child
Child.uber = Parent.prototype
}
You can then call this:
Copy Code code as follows:
4. Property copy:
This kind of inheritance basically does not change the relationship of the prototype chain, but simply copies the attributes in the parent prototype object to the child object prototype, of course, the replication here only applies to the basic data type, and the object type only supports reference passing.
Copy Code code as follows:
function Extend2 (Child, Parent) {
var p = Parent.prototype
var C = Child.prototype
for (var i in P) {
C[i] = P[i]
}
C.uber = P
}
In this way, some of the prototype properties are rebuilt and the objects are less efficient when they are built, but they can reduce the search of the prototype chain. But I personally think the advantages of this approach are not obvious.
5. Inheritance between objects:
In addition to the inheritance method between constructors, it is possible to throw the constructor directly into the inheritance between objects. That is, a direct copy of the object's properties, including shallow copies and deep copies.
Shallow copy:
Accepts the object to inherit, creates a new empty object, copies the properties of the object to the new object, and returns the new object:
Copy Code code as follows:
function Extendcopy (p) {
var c = {}
for (var i in P) {
C[i] = P[i]
}
C.uber = P
Return C
}
After the copy is complete, the properties that need to be overwritten in the new object can be rewritten manually.
Deep copy:
The problem with shallow copy is also obvious, it can't copy object type attribute and can only pass reference, to solve this problem, we need to use deep copy. The focus of the deep copy is the recursive invocation of the copy, which detects the properties of the object type, creates the corresponding object or array, and copies the underlying type values.
Copy Code code as follows:
function deepcopy (p, c) {
c = C | | {}
for (var i in P) {
if (P.hasownproperty (i)) {
if (typeof p[i] = = ' object ') {
&nbs p; C[i] = Array.isArray (p[i))? []: {}
deepcopy (p[ I], c[i]
} else {
& nbsp; C[i] = p[i]
}
}
}
return c
}
A ES5 Array.isarray () method is used to determine whether the parameter is an array, and an environment that does not implement this method needs to manually encapsulate a shim.
Copy Code code as follows:
Array.isarray = function (p) {
return P instanceof Array
}
However, it is not possible to use the instanceof operator to determine the array variables from different frames, but this is relatively rare.
6. Prototype Inheritance:
With the parent object, create a new object with a parent object as a prototype by using the constructor:
Copy Code code as follows:
function Object (o) {
var n
function F () {}
F.prototype = O
n = new F ()
N.uber = O
return n
}
Here, the Object.create () method in ES5 is the implementation of a prototype that directly sets the parent object as a child object.
7. prototype inheritance and attribute copy blending:
The prototype inheritance method constructs child objects as prototypes for the incoming parent object, and it can also add additional objects to the attributes that need to be copied in addition to the properties provided by the parent object:
Copy Code code as follows:
function Ojbectplus (o, stuff) {
var n
function F () {}
F.prototype = O
n = new F ()
N.uber = O
for (var i in stuff) {
N[i] = Stuff[i]
}
return n
}
8. Multiple Inheritance:
This approach does not involve the operation of the prototype chain, passing in multiple objects that need to be copied, and then making a full copy of the attributes:
Copy Code code as follows:
function multi () {
var n = {}, stuff, I = 0,
Len = Arguments.length
for (i = 0; i < len; i++) {
Stuff = Arguments[i]
for (var key in stuff) {
N[i] = Stuff[i]
}
}
return n
}
Copies sequentially, according to the order in which the objects are passed in, that is, if the object passed in later contains the same attributes as the previous object, the latter overrides the former.
9. Constructor borrowing:
The call () and apply () methods in JavaScript are very handy, and the ability to change the execution context of a method can also work in an inherited implementation. Constructor borrowing refers to the use of the parent object's constructor in the child object constructor to operate on this:
Copy Code code as follows:
function Parent () {}
Parent.prototype.name = ' Parent '
function Child () {
Parent.apply (this, arguments)
}
var child = new Child ()
Console.log (Child.name)
The great advantage of this approach is that in the constructor of the child object, the object's own property is completely rebuilt, and a variable of the reference type generates a new value instead of a reference, so any action on the object does not affect the parent object.
The disadvantage of this approach is that the new operator is not used during the construction of the child object, so the child object does not inherit any attributes on the parent prototype object, and in the code above, the Name property of the children will be undefined.
To resolve this problem, you can manually set the child Object Builder prototype to an instance of the parent object again:
Copy Code code as follows:
Child.prototype = new Parent ()
But this brings another problem, that is, the constructor of the parent object is called two times, once in the parent object Builder lease, and in the inheritance prototype process.
To solve this problem, we need to remove the call of the parent object constructor, the constructor borrowed can not be omitted, then only remove the last call, the other way to implement the prototype is iterative replication:
Copy Code code as follows:
Use the previously implemented Extend2 () method.