Deep understanding of JavaScript series (5): Powerful prototype and prototype chain

Source: Internet
Author: User
Tags hasownproperty

Preface
JavaScript does not contain the traditional class inheritance model, but uses the prototypal prototype model.
Although this is often mentioned as a disadvantage of JavaScript, prototype-based inheritance models are more powerful than traditional class inheritance models. It is easy to implement the traditional class inheritance model, but it is much more difficult to implement prototype inheritance in JavaScript.
Since JavaScript is the only language widely used based on prototype inheritance, it takes some time to understand the differences between the two inheritance modes. Today, let's take a look at prototype and prototype chain.
Prototype
When I first learned JavaScript 10 years ago, I generally used the following method to write code:
Var decimalDigits = 2,
Tax = 5;

Function add (x, y ){
Return x + y;
}

Function subtract (x, y ){
Return x-y;
}

// Alert (add (1, 3 ));
Copy code
After learning the prototype, we can use the following methods to beautify the code.
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;
};
Copy code
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 ));
Copy code
In this way, we can call the add method to calculate the result after the new Calculator object.
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 have been known in the previous post, that is, it can encapsulate private functions and expose simple names to achieve public/private effects through return, 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 ));
Copy code
In the same way, we can call the add method after the new Calculator object to calculate the result.
A little more
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 place for each instance
This. decimalDigits = 2;
};

// Use the prototype to extend two object methods for BaseCalculator
BaseCalculator. prototype. add = function (x, y ){
Return x + y;
};

BaseCalculator. prototype. subtract = function (x, y ){
Return x-y;
};
Copy code
First, a BaseCalculator object is declared. 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;
}
};
Copy code
After the above Code is created, let's start:
Var Calculator = function (){
// Declare a tax number for each instance
This. tax = 5;
};

Calculator. prototype = new BaseCalculator ();
Copy code
We can see that the prototype of Calculator is directed to an instance of BaseCalculator, so that Calculator can integrate its add (x, y) and subtract (x, y) functions, another point is that, since its prototype is an instance of BaseCalculator, no matter how many Calculator object instances you create, their prototype points to the same instance.
Var calc = new Calculator ();
Alert (calc. add (1, 1 ));
// The decimalDigits attribute declared in BaseCalculator can be accessed in Calculator.
Alert (calc. decimalDigits );
Copy code
After running the code above, we can see that the Calculator prototype is directed to the BaseCalculator instance, so we can access its decimalDigits attribute value, what if I don't want Calculator to access the attribute value declared in the BaseCalculator constructor? To do this:
Var Calculator = function (){
This. tax = 5;
};

Calculator. prototype = BaseCalculator. prototype;
Copy code
By assigning the BaseCalculator prototype to the Calculator prototype, you will not be able to access the decimalDigits value on the Calculator instance. If you access the following code, the error will be improved.
Var calc = new Calculator ();
Alert (calc. add (1, 1 ));
Alert (calc. decimalDigits );
Copy code
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 of the previous Calculator.
Calculator. prototype. add = function (x, y ){
Return x + y + this. tax;
};

Var calc = new Calculator ();
Alert (calc. add (1, 1 ));
Copy code
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.
Prototype chain
Before we chain the prototype, let's start with the code:
Function Foo (){
This. value = 42;
}
Foo. prototype = {
Method: function (){}
};

Function Bar (){}

// Set the prototype attribute of Bar to the Instance Object of Foo
Bar. prototype = new Foo ();
Bar. prototype. foo = 'Hello world ';

// Modify Bar. prototype. constructor to Bar itself
Bar. prototype. constructor = Bar;

Var test = new Bar () // create a new instance of Bar

// Prototype chain
Test [Bar instance]
Bar. prototype [Foo instance]
{Foo: 'Hello world '}
Foo. prototype
{Method :...};
Object. prototype
{ToString:.../* etc .*/};
Copy code
In the above example, the test object inherits from Bar. prototype And Foo. prototype. Therefore, it can access the Foo prototype method. At the same time, it can also access the Foo instance attribute value defined on the prototype. Note that new Bar () does not create a new Foo instance, but uses the instance on its prototype again. Therefore, all Bar instances share the same value attribute.
Property search:
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
Copy code
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
Copy code
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 Object. prototype
Object. prototype. bar = 1;
Var foo = {goo: undefined };

Foo. bar; // 1
'Bar' in foo; // true

Foo. hasOwnProperty ('bar'); // false
Foo. hasOwnProperty ('goo'); // true
Copy code
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 to foo
{}. HasOwnProperty. call (foo, 'bar'); // true
Copy code
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 Object. prototype
Object. prototype. bar = 1;

Var foo = {moo: 2 };
For (var I in foo ){
Console. log (I); // two attributes are output: bar and moo.
}
Copy code
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 in the previous example.
For (var I in foo ){
If (foo. hasOwnProperty (I )){
Console. log (I );
}
}
Copy code
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: We recommend that you use hasOwnProperty. Do not make any assumptions about the code running environment, or whether the native object has been extended.

Summary
The prototype greatly enriches our development code. However, you must pay attention to the precautions mentioned above during normal usage.

From Uncle Tom's blog

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.