Details about prototype and prototype chain in JavaScript -- Item15
Those who have used JavaScript must be familiar with prototype. However, this makes it difficult for beginners to understand that a function has a prototype attribute, you can add functions for it for instance access. The rest is unclear. Recently, I read some JavaScript advanced program designs and finally unveiled the secrets.
Each function has a prototype attribute pointing to an object reference. This object is called a prototype object. The prototype object contains methods and attributes shared by function instances, that is to say, when a function is used as a constructor call (called using the new operator), the newly created object inherits attributes and methods from the prototype object.Unlike the traditional object-oriented language, the Inheritance Mechanism of Javascript is based on the prototype, rather than the Class.
1. Private variables and functions
A few related things are mentioned before prototype to better understand prototype design intent. Before learning about the JavaScript prototype chain, it is necessary to first understand the scope chain of JavaScript. The function scope of JavaScript. If the variables and functions defined in the function do not provide external interfaces, they cannot be accessed externally, that is, they become private variables and private functions.
Function Obj () {var a = 0; // Private variable var fn = function () {// Private function }}
In this way, variables a and fn cannot be accessed outside the function object Obj, and they become private and can only be used inside Obj. Even the function Obj instance still cannot access these variables and functions.
var o=new Obj();console.log(o.a); //undefinedconsole.log(o.fn); //undefined
2. Static variables and functions
After defining a function, the added attributes and functions can still be accessed through the object, but cannot be accessed by its instance. Such variables and functions are called static variables and static functions, respectively, students who have used Java and C # Have a good understanding of static meanings.
Function Obj () {} Obj. a = 0; // static variable Obj. fn = function () {// static function} console. log (Obj. a); // 0console. log (typeof Obj. fn); // functionvar o = new Obj (); console. log (o. a); // undefinedconsole. log (typeof o. fn); // undefined
3. instance variables and functions
In object-oriented programming, apart from some library functions, we still want to define some attributes and methods at the same time when the object is defined, which can be accessed after instantiation, and JavaScript can do the same.
Function Obj () {this. a = []; // instance variable this. fn = function () {// instance method} console. log (typeof Obj. a); // undefinedconsole. log (typeof Obj. fn); // undefinedvar o = new Obj (); console. log (typeof o. a); // objectconsole. log (typeof o. fn); // function
This can achieve the above purpose, however
Function Obj () {this. a = []; // instance variable this. fn = function () {// instance method} var o1 = new Obj (); o1.a. push (1); o1.fn = {}; console. log (o1.a); // [1] console. log (typeof o1.fn); // objectvar o2 = new Obj (); console. log (o2.a); // [] console. log (typeof o2.fn); // function
The running result of the above Code is exactly as expected, but it also shows a problem. In o1, a and fn are modified, but not in o2. Because arrays and functions are all objects, is a reference type, which means that the attributes and methods in o1 are not a reference, but a copy of the attributes and methods defined by the Obj object, although they are the same as those in o2.
This is no problem for attributes, but it is a big problem for methods, because methods are doing the same function, but there are two copies, if a function object has thousands of methods and instance methods, each of its instances must copy thousands of methods. This is obviously not scientific. Can this be swollen,Prototype emerged. Let's first look at the meaning of the object:
4. Common objects and function objects
In JavaScript, everything is an object! But there are also differences between objects. It can be divided into common objects and Function objects. Objects and functions are built-in Function objects of JS. The following is an example
function f1(){}; var f2 = function(){}; var f3 = new Function('str','console.log(str)'); var o3 = new f1(); var o1 = {}; var o2 =new Object(); console.log(typeof Object); //function console.log(typeof Function); //function console.log(typeof o1); //object console.log(typeof o2); //object console.log(typeof o3); //object console.log(typeof f1); //function console.log(typeof f2); //function console.log(typeof f3); //function
In the preceding example, o1 o2 o3 is a common object, and f1 f2 f3 is a function object. How to differentiate, in fact, is very simple,All objects created through new Function () are Function objects, while others are common objects.F1 and f2 are all created using the new Function () method. Function objects are also created through New Function.
5. prototype
In JavaScript, every time an object (function) is defined, the object contains somePredefined attributes. One Property of the function object is prototype. Note: common objects do not have prototype, but have_ Proto _Attribute.
The prototype object is actually a Common Object (except Function. prototype, which is a Function object, but it is very special. It does not have the prototype attribute (previously mentioned that Function objects all have the prototype attribute )). See the following example:
Function f1 () {}; console. log (f1.prototype) // f1 {} console. log (typeof f1.prototype) // Object console. log (typeof Function. prototype) // Function, this special console. log (typeof Object. prototype) // Object console. log (typeof Function. prototype. prototype) // undefined
From the output of console. log (f1.prototype) // f1 {}, we can see that f1.prototype is an instance object of f1 (this is the prototype object of f1 ). When f1 is created, it creates an instance object and assigns prototype to it. The basic process is as follows:
var temp = new f1(); f1. prototype = temp;
Therefore, why is Function. prototype A Function object solved? All objects generated by new Function () are Function objects, so temp1 is a Function object.
var temp1 = new Function (); Function.prototype = temp1;
What is the prototype used?It is mainly used for inheritance.. For example:
Var person = function (name) {this. name = name}; person. prototype. getName = function () {return this. name; // here this points to the prototype object person ==> person. name} var xpg = new person ('xiaopingguo'); xpg. getName (); // xiaopingguo
From this example, we can see that by setting the property of a function object for person. prototype, a common object from the person instance (in this example: xpg) inherits this property. The following prototype chain is required for the implementation of inheritance.
In depth: whenever a new function is created, A prototype attribute (also an object) is created for the function based on a specific set of rules. By default, the prototype attribute (object) obtains a constructor (constructor) by default) property. This property is a pointer to the function where the prototype property is located. It's a bit of a loop, writing code ,!
function Person(){}
It can be seen that the Person object will automatically obtain the prototyp attribute, and prototype is also an object, it will automatically obtain a constructor attribute, which is directed to the Person object.
When a constructor is called to create an instance, the instance contains an internal pointer (the pointer name of many browsers is_ Proto _) Points to the prototype of the constructor. This connection exists between the instance and the prototype of the constructor, rather than between the instance and the constructor.
function Person(name){ this.name=name;}Person.prototype.printName=function(){ alert(this.name);}var person1=new Person('Byron');var person2=new Person('Frank');
The Person instance person1 contains the name attribute, and_ Proto _Property, which points to the prototype of Person and can access the printName method defined in prototype, which is like this:
Write a program test to see if the prototype attributes and methods can be shared.
function Person(name){ this.name=name;}Person.prototype.share=[];Person.prototype.printName=function(){ alert(this.name);}var person1=new Person('Byron');var person2=new Person('Frank');person1.share.push(1);person2.share.push(2);console.log(person2.share); //[1,2]
Sure enough! In fact, when the Code reads an attribute of an object, it will perform a search. The target is an attribute with a given name. The search starts from the object instance, if this attribute is found in the instance, it is returned. If no prototype is found, prototype is queried. If no prototype is found, the prototype object of prototype is recursive until it is found, if the object still does not exist after recursion, an error is returned. Similarly, if a prototype attribute or function with the same name is defined in an instance, the prototype attribute or function is overwritten. --This is the prototype chain of Javascript.
Function Person (name) {this. name = name;} Person. prototype. share = []; var person = new Person ('byron '); person. share = 0; console. log (person. share); // 0; instead of [] in prototype
6. prototype chain
When JS creates an object (whether a common object or a function object), it is called_ Proto _Is used to point to the prototype of the created function object. The above example
console.log(xpg.__ proto __ === person.prototype) //true
Similarly, the person. prototype object also has_ Proto _Property, pointing to the prototype of the function Object
console.log(person.prototype.__ proto __=== Object.prototype) //true
Continue. The Object. prototype Object also has_ Proto _Attribute, but it is special. It is null.
console.log(Object.prototype.__ proto __) //null
This has_ Proto _Until the Object. prototype._ Proto _A null chain is called a prototype chain. For example:
Search for properties in the prototype chain:
When you look for an Object's properties, JavaScript will traverse the prototype chain up until the property of the given name is found until the search reaches the top of the prototype chain-that is, the Object. prototype-but the specified attribute is still not found, undefined is returned. Let's take an example:
Function foo () {this. add = function (x, y) {return x + y;} foo. prototype. add = function (x, y) {return x + y + 10;} Object. prototype. subtract = function (x, y) {return x-y;} var f = new foo (); alert (f. add (1, 2); // The result is 3 instead of 13 alert (f. subtract (1, 2); // The result is-1.
Through code running, we found that subtract is the result of installing what we call upward lookup, but the add method is a little different, which is what I want to emphasize, that is, when searching for properties, we first look for their own properties. If no prototype is found, and no prototype is found, we go up and insert it to the prototype of the Object. So at a certain level, when you use the for in statement to traverse attributes, efficiency is also a problem.
Another thing to note is that we can assign values of any type to the prototype, but cannot assign values of the atomic type. For example, the following code is invalid:
Function Foo () {} Foo. prototype = 1; // invalid
7. Differences between constructors, instances, and prototype objects
An instance is created by a constructor. When an instance is created, it has the constructor attribute (pointing to the constructor) andProtoProperty (pointing to a prototype object ),
The constructor has a prototype attribute, which is a pointer to its prototype object.
The prototype object also has a pointer (constructor attribute) pointing to the constructor: Person. prototype. constructor = Person;
The instance can access the properties and methods defined on the prototype object.
Here, person1 and person2 are instances, and prototype is their prototype object.
Let's look at another example:
<Script type = text/javascript> function Animal (name) // accumulate constructor {this. name = name; // set object attributes} Animal. prototype. behavior = function () // Add the behavior method {alert (this is a + this. name);} var Dog = new Animal (dog); // create the Dog object var Cat = new Animal (cat); // create the Cat object Dog. behavior (); // call the behavior method Cat directly through the Dog object. behavior (); // output this is a cat alert (Dog. behavior = Cat. behavior); // output true; </script>
8. Use of Prototype
Prototype usage 1:
Before using the prototype, we need to make minor changes to the Code:
var Calculator = function (decimalDigits, tax) { this.decimalDigits = decimalDigits; this.tax = tax; };
Then, the prototype of the Calculator object is set by assigning the object literal to the prototype attribute of the Calculator object.
Calculator.prototype = { add: function (x, y) { return x + y; }, subtract: function (x, y) { return x - y; } }; //alert((new Calculator()).add(1, 3));
We can call the add method to calculate the result after the new Calculator object is created.
Prototype usage 2:
The second method is to assign values using the expression immediately executed by function when assigning values to prototype, that is, the following format:
Calculator.prototype = function () { } ();
Its benefits are already known in the previous Item, that is, it can encapsulate private functions and expose simple use names in the form of return to achieve the effect of public/private, the modified code is as follows:
Calculator.prototype = function () { add = function (x, y) { return x + y; }, subtract = function (x, y) { return x - y; } return { add: add, subtract: subtract }} ();//alert((new Calculator()).add(11, 3));
In the same way, we can call the add method after the new Calculator object to calculate the result.
Step-by-Step declaration:
When using the prototype, a restriction is that the prototype object is set at one time. Let's talk about how to set each attribute of the prototype separately.
Var BaseCalculator = function () {// declare a decimal point for each instance. this. decimalDigits = 2 ;};
// Use the prototype to extend to BaseCalculator
BaseCalculator.prototype.add = function (x, y) { return x + y;};BaseCalculator.prototype.subtract = function (x, y) { return x - y;};
Declares a BaseCalculator object. In the constructor, The decimalDigits attribute of a decimal point is initialized, and two functions are set through the prototype attribute, which are add (x, y) and subtract (x, y), of course, you can also use either of the two methods mentioned above. Our main purpose is to see how to set the BaseCalculator object to the real Calculator prototype.
var BaseCalculator = function() { this.decimalDigits = 2;};BaseCalculator.prototype = { add: function(x, y) { return x + y; }, subtract: function(x, y) { return x - y; }};
Rewrite prototype:
When using third-party JS class libraries, sometimes the prototype method they define cannot meet our needs, but it is inseparable from this class library, therefore, we need to rewrite one or more attributes or functions in their prototype. We can override the previous add function by continuing to declare the same add code, the Code is as follows:
// Overwrite the add () function Calculator of the previous Calculator. prototype. add = function (x, y) {return x + y + this. tax ;}; var calc = new Calculator (); alert (calc. add (1, 1 ));
In this way, the calculated result is a tax value more than the original one, but note that the code to be rewritten must be placed at the end to overwrite the previous code.
9. hasOwnProperty Function
HasOwnProperty is Object. prototype is a good method. It can determine whether an object contains custom attributes rather than attributes on the prototype chain, hasOwnProperty is the only function in JavaScript that processes attributes but does not search for prototype links.
// Modify the Object. prototypeObject. prototype. bar = 1; var foo = {goo: undefined}; foo. bar; // 1 'bar' in foo; // truefoo. hasOwnProperty ('bar'); // falsefoo. hasOwnProperty ('goo'); // true
Only hasOwnProperty can provide correct and expected results, which is useful when traversing object attributes. No other method can be used to exclude attributes on the prototype chain, rather than defining attributes on the object itself.
However, JavaScript does not protect hasOwnProperty from being illegally occupied. Therefore, if an object happens to have this property, you need to use the external hasOwnProperty function to obtain the correct result.
Var foo = {hasOwnProperty: function () {return false ;}, bar: 'Here be dragons'}; foo. hasOwnProperty ('bar'); // always returns false // use the hasOwnProperty of the {} object and set it up and down to foo {}. hasOwnProperty. call (foo, 'bar'); // true
HasOwnProperty is the only available method to check whether a property exists on an object. At the same time, we recommend that you always use the hasOwnProperty method when traversing objects using the for in loop method, which will avoid interference caused by the extension of the prototype object. Let's take a look at the example below:
// Modify the Object. prototypeObject. prototype. bar = 1; var foo = {moo: 2}; for (var I in foo) {console. log (I); // two output attributes: bar and moo}
We cannot change the behavior of the for in statement. to filter the results, we can only use the hasOwnProperty method. The Code is as follows:
// The foo variable is for (var I in foo) {if (foo. hasOwnProperty (I) {console. log (I); // moo} in the preceding example }}
The code of this version is the only correct method. Because hasOwnProperty is used, only moo is output this time. If hasOwnProperty is not used, this Code may cause errors when the native Object prototype (such as Object. prototype) is extended.
Conclusion: hasOwnProperty is recommended. Do not make any assumptions about the code running environment, or whether the native object has been extended.
10. Expansion
_ Ptoto _Attribute
_ Ptoto _Attribute (not supported by IE) is a pointer to the prototype object of the instance. Its function is to point to the constructor, a prototype property of the constructor, you can access the attributes and methods in the prototype.
Object instances in Javascript are essentially composed of a series of attributes. Among these attributes, there is an internal invisible special attribute --_ Proto _The value of this attribute points to the prototype of the object instance. An object instance has only one unique prototype.
Function Box () {// uppercase, representing the constructor Box. prototype. name = trigkit4; // prototype attribute Box. prototype. age = 21; Box. prototype. run = function () // Prototype Method {return this. name + this. age + 'studying';} var box1 = new Box (); var box2 = new Box (); alert (box1.constructor); // you can obtain the constructor itself, // The role is to be located by the prototype pointer and then get the constructor itself
_ Proto _Difference between properties and prototype Properties
Prototype is a unique attribute of a prototype object.
_ Proto _It is an implicit property of a Common Object. When it is new, it will point to the object indicated by prototype;
_ Ptoto _It is actually a property of an object, and prototype is a property of the constructor._ Ptoto _It can only be used in a learning or debugging environment.
Execution Process in prototype mode
1. first look for the attributes or methods in the constructor instance. If so, return immediately.
2. If the constructor instance does not exist, find it in its prototype object. If so, return immediately.
Prototype object
Function Box () {// uppercase, representing the constructor Box. prototype. name = trigkit4; // prototype attribute Box. prototype. age = 21; Box. prototype. run = function () // Prototype Method {return this. name + this. age + 'studying';} var box1 = new Box (); alert (box1.name); // trigkit4, the value in the prototype is box1.name = Lee; alert (box1.name ); // Lee, in principle var box2 = new Box (); alert (box2.name); // trigkit4, prototype value, not modified by box1
Constructor
Function Box () {this. name = Bill;} Box. prototype. name = trigkit4; // prototype attribute Box. prototype. age = 21; Box. prototype. run = function () // Prototype Method {return this. name + this. age + 'studying';} var box1 = new Box (); alert (box1.name); // Bill, in the prototype, the value box1.name = Lee; alert (box1.name ); // Lee, on-going Principle
To sum up, sort out the following:
Function Person () {}; Person. prototype. name = trigkit4; Person. prototype. say = function () {alert (? Hi);} var p1 = new Person (); // prototype is the prototype of p1 and p2. var p2 = new Person (); // p2 is the instantiated object, there is a _ proto _ attribute inside it, pointing to the Person prototypeconsole. log (p1.prototype); // undefined, which is an object and cannot access the console. log (Person. prototype); // Personconsole. log (Person. prototype. constructor); // The prototype object also has a pointer (constructor attribute) pointing to the constructor console. log (p1. _ proto _); // This property is a pointer to the prototype object p1.say (); // The instance can access the properties and methods defined on the prototype object.
Constructor = constructor (Template) prototype object. isPrototypeof (Instance Object) determines whether the prototype of the instance object is the current object.