This article gives you the content is about a comprehensive analysis of the principle of JavaScript inheritance, there is a certain reference value, the need for friends can refer to, I hope to help you.
Inherited
We know that JS is OO programming, naturally without OO programming features, after learning the prototype, we strike, to talk about OO programming one of the three characteristics-inheritance.
The word "inheritance" should be better understood, we are familiar with, inherit property, inherit the family business and so on, their premise is to have an heir, then you are the successor, so as to inherit. Yes, the inheritance in JS, as you can understand, is a pair.
Inheritance is the copying of an object's properties to an object that needs to be inherited
The OO language supports two ways of inheriting: interface inheritance and implementation inheritance, where interface inheritance inherits only the method signature , and implementation inheritance inherits the actual method . Because the function in ECMAScript does not have the signature, therefore cannot implement the interface inheritance, only supports the implementation inheritance, but inherits the main way, is realizes through the prototype chain , must understand the prototype chain, first must know what is the prototype, does not understand the small partner, What do you think of this JavaScript prototype? A detailed explanation of JavaScript prototypes
In fact, inheritance is plainly
① it must have a parent above it.
② and it acquires all instances and methods of this parent
Here to popularize a small concept, the above mentioned no signature , the first time to see this word is not very understand, search for a bit, think this statement is still more acceptable.
No signature
We know that JS is a weakly typed language, its parameters can be represented by 0 or more values of the array, we can be named for the JS function parameters, this practice is only for convenience, but not necessarily, that is, we are not named parameters and pass-through parameters are not necessarily linked, can be named parameters, but not pass ( At this time the default is undefined), you can also do not name parameters, but the argument , this way of writing in JS is legal, on the contrary, the strong type of language, the requirements are very strict, define a few parameters must be passed several parameters. named parameters this block must require that the function signature be created beforehand, and future calls must be consistent with that signature. (that is, define a few parameters to pass a few down) * *, and JS does not have these rules, the parser will not verify the named parameters, so that JS no signature.
As an example,
function Jsnosignature () { console.log ("first params" + arguments[0] + "," + "second params" + arguments[1]);} Jsnosignature ("Hello", "World");
This is an obvious example. The named argument is empty, but we can still pass the argument and call the method. So-called parameter type, parameter number, parameter position, access parameters, JS all do not care, all of its values are put into the arguments, need to return the value of the words directly return, do not declare, this is called JS no signature.
Prototype chain
What is a prototype chain? It is also very well understood that it is called the prototype chain that strings all the prototypes together. Of course, this explanation is just for the convenience of understanding, the prototype chain is the main way to implement inheritance, the basic idea is to use the prototype to let one reference type inherit another reference type properties and methods . We know that each constructor has a prototype object, and the prototype object contains a pointer to the constructor, and the instance contains an internal pointer to the prototype. At this point, if we let the prototype object be equal to another instance of the type, then the prototype object will contain a pointer to another prototype, corresponding to another prototype also contains a pointer to another constructor, this cycle of connection, which constitutes the example and prototype chain, this is the prototype chains.
In a word, it is instance → prototype → example → prototype → example ... Connecting down is the prototype chain.
I think inheritance is a manifestation of the prototype chain .
After we know the prototype chain, to know how he is going to use it, the ECMA provides a basic pattern of prototype chains as follows
Basic model of prototype chain
Create a parent class function Fathertype () { this.fathername = ' name the headache ';} FatherType.prototype.getFatherValue = function () { return this.fathername;} function Childtype () { this.childname = ' George ';} Inheriting the fathertype and assigning an instance to a function prototype, we say that the prototype inherits another instance of the function//the prototype of the subclass points to the instance of the parent class Childtype.prototype = new Fathertype (); ChildType.prototype.getChildValue = function () { return this.childname;} Let instance = new Childtype (); Console.log (Instance.getfathervalue ()); Name the most headache
Three search steps after calling Instance.getfathervalue ()
① Search Instance
② Search Childtype.prototype
③ Search Fathertype.prototype, at this point in this step to find the method, in the case of a property or method is not found, the search process will always be a ring-to-line forward to the end of the prototype chain to stop.
The prototype chain at this time is Instance→childtype.prototype→fathertype.prototype
After executing instance.getfathervalue (), getfathervalue inside this is Childtype, at this time Childtype will be based on the prototype chain to find Fathername properties, eventually found in Fathertype.
At this point instance.constructor is pointing to Fathertype
The default prototype
all reference types inherit object by default, and this inheritance is implemented through the prototype chain, so the default prototype for all functions is an instance of object, so the default prototype will contain an internal pointer to object. Prototype, which means that all custom types inherit the root cause of the default methods such as ToString (), ValueOf ().
The array type also inherits the object type.
Therefore, we can summarize that at the very top of the prototype chain is the object type, and all functions inherit the attributes from the object by default.
Validation of prototypes and instance relationships
isPrototypeOf method
What is the JavaScript prototype? In a detailed explanation of JavaScript prototypes, we have mentioned that the isPrototypeOf method can be used to determine if the pointer to this instance is pointing to the prototype, which we have learned from the prototype chain, which is supplemented by the order of the prototype chain, The isPrototypeOf method can be used to determine whether this instance belongs to this prototype.
Still use the above example//note, here is the prototype, Object.prototype,fathertype.prototype,childtype.prototypeconsole.log ( Object.prototype.isPrototypeOf (instance)); Trueconsole.log (FatherType.prototype.isPrototypeOf (instance)); Trueconsole.log (ChildType.prototype.isPrototypeOf (instance)); True
Another method is described below, with the instanceof operator, and the relationship between the prototype and the instance can be determined.
instanceof operator
The instanceof operator is used to test whether a constructor in the prototype chain has this instance
function Fathertype () { this.fathername = ' name the headache ';} FatherType.prototype.getFatherValue = function () { return this.fathername;} function Childtype () { this.childname = ' George ';} Inherited fathertypechildtype.prototype = new Fathertype ();//Create instance let instance = new Childtype ();//Add a new method to the Childtype prototype, To be placed after inheriting fathertype, this is because new Fathertype () overwrites all of the newly added methods on the Childtype prototype ChildType.prototype.getChildValue = function () { return this.childname;} At this point getfathervalue is rewritten ChildType.prototype.getFatherValue = function () { return True}console.log ( Instance.getfathervalue ()); True
② You cannot use object literals to create a prototype method when inheriting through a prototype chain, because it overrides the prototype chain. This part of the example and explanation in JavaScript prototype is what? A detailed explanation of the JavaScript prototype has been expressed. The same truth, just replace the prototype with a prototype chain.
Bug in prototype chain
Although the prototype chain is powerful, it can be used to implement inheritance, but it is also a bug, its biggest bug comes from the prototype containing the reference type value. This means that the prototype attribute defined above the prototype chain is shared by all instances.
It also has a bug that when you create an instance of a subtype, you cannot pass parameters to the constructor of the parent type (super-type). Or there's no way to pass parameters to a super-type constructor without affecting all object instances.
Based on these two reasons, it is rare to use the prototype chain alone in the practice process.
Borrowing constructors
Its design idea is to call the parent class (superclass) constructor inside the subtype constructor.
Because a function is simply an object that executes code in a particular environment, the apply () and call () methods can also execute constructors on (future) newly created objects.
function Fathertype () { this.name = ' George ';} function Childtype () { //change the point of this by the call method, At this point, this in fathertype refers to Childtype, which is equivalent to defining its own property in the constructor. Fathertype.call (this);} Let Instance1 = new Childtype (); Instance1.name = ' Name the most headache '; Console.log (instance1.name); ' Name the headache ' let Instance2 = new Childtype (); Console.log (Instance2.name); George
This method solves the problem of prototype attribute sharing well, moreover, since it is a function, it can also pass the corresponding parameter, so it can also be implemented to pass parameters to the Super type constructor in the subtype constructor function.
function Fathertype (name) { this.name = name}function Childtype () { Fathertype.call (this, "George"); This.age = 18}let Instance = new Childtype (); Console.log (instance.name); Georgeconsole.log (instance.age); 18
Problems with borrowing constructors
The use of constructors, which are defined in constructors, means that the reuse of functions is not possible, and that the methods defined in the prototype of the parent class (super-type) are not visible to the child type, and only the constructor pattern is used for all types.
Combining inheritance
Combinatorial inheritance is also called pseudo-classical inheritance, and its design idea is to combine the prototype chain and the technique of borrowing the constructor into a piece, and to play a kind of inheritance pattern of the two, the idea behind it is to use the prototype chain to inherit the prototype properties and methods, and to implement the inheritance of the instance attributes by borrowing the constructor function. This enables the reuse of functions by defining methods on the prototype, and ensures that each instance has its own properties.
function Fathertype (name) { this.name = name this.colors = [' Red ', ' blue ', ' Green ']} FatherType.prototype.sayName = function () { console.log (this.name)}//borrows the constructor to implement inheritance of the instance function Childtype (name, age { //Use the call method to inherit the attribute Fathertype.call (this, name) in Fathertype; This.age = age}//inherits the prototype properties and methods using the prototype chain Childtype.prototype = new Fathertype (); Assigning an instance of Fathertype to the Childtype prototype ChildType.prototype.constructor = Childtype; Let Childtype's prototype point to the Childtype function ChildType.prototype.sayAge = functions () { console.log (this.age)}let Instance1 = new Childtype (' Name the most headache ', Instance1.colors.push (' black '); Console.log (instance1.colors) ; ' Red, blue, green, Black ' instance1.sayname (); Instance1.sayage (); var instance2 = new Childtype (' name the most headache ', 18); Console.log (instance2.colors); ' Red, blue, Green ' instance2.sayname (); ' Name the most headache ' instance2.sayage (); 18
The method of combining inheritance avoids the defects of prototype chain and borrowing constructor, and is the common inheritance way in JS.
Prototype chain inheritance
Prototype chain inheritance does not use a strict constructor, its idea is to create new objects based on existing objects
This object function returns an instance in which object () performs a shallow copy of the objects passed in. function Object (o) { function F () {} //Create a temporary constructor F.prototype = O; Return the passed-in object as the prototype of the constructor to the new F (); Returns a new instance of this temporary constructor}let demo = { name: ' George ', like : [' apple ', ' dog ']}let demo1 = Object (demo);d emo1.name = ' naming '; c8/>//Basic type Demo1.like.push (' cat '); Reference types share a memory address let Demo2 = Object (demo);d emo2.name = ' headache '; Basic type Demo2.like.push (' chicken ')//reference type share a memory address Console.log (demo.name)//Georgeconsole.log (Demo.like)//["Apple", " Dog "," cat "," chicken "]
The prerequisite for a prototype chain inheritance is that one object can be used as the basis for another object. After a new object is generated by the object () function, the new object is modified as required. Since the new object (Demo1, Demo2) is the prototype of the incoming object (demo), when it comes to reference types, they share a memory address, and the reference type is shared by all instances, which is actually equivalent to creating two copies of the demo object.
Object.create () method
The new Object.create () method in
ECMA5 normalizes the stereotype inheritance. The method receives two parameters
① base Object, the actual function of this parameter is to define the template object in the properties, like the demo in the example above, only one parameter case, Object.create () the same as in the example of the object
② This is an optional parameter, An object that defines an extra property for the underlying object, which is written in the same format as the second parameter of the Object.defineproperties () method, and each property is defined by its own descriptor, and any property specified in this manner overrides the same name property on the prototype object.
Only one parameter var demoobj = { name: ' George ', like : [' apple ', ' dog ', ' cat ']}let demo1obj = object.create (demoobj);d emo 1obj.name = ' name ';d emo1Obj.like.push (' banana '); let demo2obj = Object.create (demoobj);d emo2obj.name = ' headache '; Demo2Obj.like.push (' walk '); Console.log (demoobj.like)//["Apple", "dog", "cat", "banana", "walk"]//two parameters var demoobj = { C2/>name: ' George ', like : [' apple ', ' dog ', ' cat ']}let demo1obj = Object.create (demoobj, { name: { value: ') Name ' }, like:{ value: [' Monkey ' }, new_val: { value: ' New_val '} }); Console.log ( Demoobj.name)//Georgeconsole.log (Demo1obj.name)//Name Console.log (demo1obj.like)//["Monkey"]console.log ( Demo1obj.new_val)//New_valconsole.log (Object.getownpropertydescriptor (demo1obj, ' new_val ')//{value: "New_val", Writable:false, Enumerable:false, Configurable:false}
If you want only one object to hold the type with another object, the stereotype inheritance is perfectly capable, but it is important to note that properties that reference the value of the type always share the corresponding value.
Parasitic inheritance
Parasitic inheritance is a kind of thought that is closely related to prototype inheritance, and its design idea is similar to the parasitic constructor and the factory pattern, that is, to create a function that encapsulates the inheritance process, which in some way enhances the object, and finally returns an object.
The object returned by this function has all the properties and methods of original, as well as its own SayHello method function Createanother (original) {let clone = Object.create ( original); Clone.sayhello = function () { console.log (' HELLO World ') } return clone; Let person = { name: ' George ', foods: [' apple ', ' banana ']}let Anotherperson = createanother (person); Anotherperson.sayhello (); HELLO World
Using parasitic inheritance to add a function to an object can reduce efficiency because it cannot be reused, which is similar to the constructor pattern.
Parasitic combined inheritance
The so-called parasitic combined inheritance, that is, by borrowing the constructor to inherit the property, through the hybrid form of the prototype chain to inherit the method. The idea behind it: instead of calling a super-type constructor to specify a prototype of a subtype, all we need is a copy of the super-type prototype. It is plain to use parasitic inheritance to inherit a super-type prototype, and then assign the result to the prototype of the subtype.
function Inheritprototype (Childtype, fathertype) {let fatherobj = Object.create (fathertype.prototype); Create object fatherobj.constructor = Childtype; The default constructor attribute Childtype.prototype = Fatherobj, which is lost in remedying the rewrite prototype; Specify Object}
The above example is the simplest form of parasitic combined inheritance, which takes two parameters: a subtype constructor and a supertype constructor, within a function, ① creates a copy of the parent type prototype, ② adds constructor attributes to the replica created. This will compensate for the default constructor property lost due to rewriting the prototype. ③ assigns the newly created object (that is, the copy) to the prototype of the subtype.
function Fathertype (name) { this.name = name; This.foods = [' Apple ', ' banana '];} FatherType.prototype.sayName = function () { console.log (this.name)}function Childtype (name, age) { Fathertype.call (this, name); This.age = age;} Inheritprototype (Childtype, Fathertype); ChildType.prototype.sayAge = function () { console.log (this.age)}
Summarize
The main way of JS inheritance is through the prototype chain implementation
Examples-prototypes-examples-prototypes ... The infinite link goes down the prototype chain.
The default prototype for all reference types is Object
Both the instanceof operator and the isPrototypeOf method can be used to determine the relationship between an instance and a prototype, the difference being that the former is a prototype, the latter using a constructor
The code to add a method to the prototype must be placed after the inheritance, because, at the time of inheritance, the successor will overwrite all the methods on the successor's prototype.
The Object.create () method is used to create a new object whose properties are placed on the prototype of the object
There are 6 ways of inheritance, namely, prototype chain, borrowing constructors, combinatorial inheritance, prototype inheritance, parasitic inheritance and parasitic combined inheritance.