Exploring JavaScript: Powerful prototype and prototype chain

Source: Internet
Author: User
Tags hasownproperty function calculator
JavaScript does not contain traditional class inheritance models, but uses prototypal prototype models.

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));

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;};

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));

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));

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 two object Methods BaseCalculator to BaseCalculator. prototype. add = function (x, y) {return x + y;}; BaseCalculator. prototype. subtract = function (x, y) {return x-y ;};

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;    }};

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 ();

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, which is the alert (calc. decimalDigits );

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;

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);
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.

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 Bar of Foo. prototype = new Foo (); Bar. prototype. foo = 'Hello world'; // corrected Bar. prototype. constructor is the Bar itself. 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. */};

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.

Attribute 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.

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
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) ;}} 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: 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.

Additional reading

The topic list of this article is as follows:

  1. How should we understand the working principle of the JavaScript engine?
  2. JavaScript exploration: the importance of writing maintainable code
  3. JavaScript exploration: exercise caution when using global variables
  4. JavaScript exploration: var pre-parsing and side effects
  5. JavaScript exploration: for Loop (for Loops)
  6. JavaScript exploration: for-in loop (for-in Loops)
  7. Exploring JavaScript: Prototypes is too powerful
  8. JavaScript: eval () is the devil"
  9. JavaScript exploration: Using parseInt () for Numerical Conversion
  10. Exploring JavaScript: Basic coding specifications
  11. JavaScript exploration: function declaration and function expression
  12. JavaScript exploration: Name function expressions
  13. JavaScript: function name in the debugger
  14. JavaScript: JScript Bug
  15. JavaScript exploration: Memory Management of JScript
  16. Exploring JavaScript: SpiderMonkey's quirks
  17. JavaScript exploration: an alternative solution to naming function expressions
  18. JavaScript exploration: Object
  19. JavaScript exploration: Prototype chain
  20. JavaScript exploration: Constructor
  21. JavaScript probing: executable context Stack
  22. Execution context 1: Variable object and activity object
  23. Execution context 2: Scope chain Scope Chains
  24. Execution context 3: Closure Closures
  25. Execution context 4: This pointer
  26. Exploring JavaScript: Powerful prototype and prototype chain
  27. JavaScript Functions 1: function declaration
  28. JavaScript function 2: function expressions
  29. JavaScript function 3: function expressions in a group
  30. JavaScript function 4: function Constructor
  31. JavaScript variable object 1: VO Declaration
  32. JavaScript variable object 2: VO in different execution contexts
  33. JavaScript variable object 3: two stages of execution Context
  34. JavaScript variable object IV: Variables
  35. Property of the JavaScript variable object __parent _
  36. JavaScript scope chain 1: Scope chain Definition
  37. JavaScript scope chain 2: function Lifecycle
  38. JavaScript scope chain 3: Scope chain features
  39. JavaScript closure 1: Introduction to closures
  40. JavaScript closure 2: Implementation of closure
  41. JavaScript closure 3: Closure usage

This article is available at http://www.nowamagic.net/librarys/veda/detail/1648.

Related Article

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.