Introduction to JavaScript Design Patterns (iii) prototype pattern extension knowledge

Source: Internet
Author: User
Tags hasownproperty

Prototypes and in Operators

There are two ways to use the In operator: use separately and in the for-in loop. When used alone, the in operator returns True when the given property is accessible through the object, whether the attribute exists in an instance or in a prototype. Take a look at the following example.

function person () {}person.prototype.name= "Nicholas"; Person.prototype.age= 29; person.prototype.job= "Software Engineer"; Person.prototype.sayname= function () {  alert (this.name);}; var person1 = new Person (), var person2 = new Person (); Alert (Person1.hasownproperty ("name"));//falsealert ("name" in Person1); True person1.name= "Greg"; alert (person1.name);//"Greg"-from Instance Alert (Person1.hasownproperty ("name"));//truealert ( "Name" in Person1); True alert (person2.name);//"Nicholas"-from the prototype alert (Person2.hasownproperty ("name"));//falsealert ("Name" in Person2 ); True Delete Person1.name;alert (person1.name);//"Nicholas"-from the prototype alert (Person1.hasownproperty ("name"));// Falsealert ("name" in Person1); True


Throughout the execution of the above code, the Name property is either accessed directly on the object or accessed through a prototype. Therefore, calling "name" in Person1 always returns true regardless of whether the attribute exists in the instance or exists in the prototype. Using both the hasOwnProperty () method and the In operator, you can determine whether the property exists in the object or in the prototype, as shown below.

function Hasprototypeproperty (object, name) {         return!object.hasownproperty (name) && (name in object);}

Because the in operator returns True,hasownproperty () only if the property is accessible through the object, it returns true only if the property exists in the instance, so as long as the in operator returns True and hasOwnProperty () returns FALSE, You can determine that the property is a property in the prototype. Let's take a look at the usage of the function hasprototypeproperty () defined above.

function person () {}person.prototype.name= "Nicholas"; Person.prototype.age= 29; person.prototype.job= "Software Engineer"; Person.prototype.sayname= function () {        alert (this.name);}; var person = new person (); Alert (Hasprototypeproperty ( Person, "name")); True person.name= "Greg"; alert (Hasprototypeproperty (person, "name")); False


Here, the Name property first exists in the prototype, so Hasprototypeproperty () returns True. When the Name property is overridden in an instance, the property is present in the instance, so Hasprototypeproperty () returns false. Even though the name attribute is still in the prototype, the name attribute in the prototype is not available because the attribute is also present in the instance.

When you use the for-in loop, you return all the enumerable (enumerated) properties that can be accessed through the object, including the properties that exist in the instance and the properties that exist in the prototype. Instance properties that block non-enumerable properties in the prototype (properties that will be marked as false for [[Enumerable]]) are also returned in the for-in loop because, as a rule, all developer-defined properties are enumerable-except in IE8 and earlier versions.

There is a bug in the implementation of earlier versions of IE that the instance properties that mask non-enumerable properties do not appear in the for-in loop. For example:

var o = {        tostring:function () {               return "My Object";        }}; For (Var prop in O) {        if (prop = = "ToString") {                 alert ("Found tostring");//not shown in IE        }}


To get all the enumerable instance properties on an object, you can use the Object.keys () method of ECMAScript 5. This method takes an object as a parameter and returns an array of strings containing all the enumerable properties. For example:

function person () {}person.prototype.name= "Nicholas"; Person.prototype.age= 29; person.prototype.job= "Software Engineer"; Person.prototype.sayname= function () {        alert (this.name);}; var keys = Object.keys (Person.prototype); alert (keys) ;//"Name,age,job,sayname" var p1 = new Person ();p 1.name= "Rob";p 1.age= 31;var P1keys = Object.keys (p1); alert (p1keys);//" Name,age "


Here, the variable keys will hold an array in which the string "name", "Age", "job", and "Sayname" are in the array. This order is also the order in which they appear in the for-in loop. If it is called through an instance of person, the array returned by Object.keys () contains only two instance properties of "name" and "age". If you want to get all the instance properties, regardless of whether it is enumerable, you can use the Object.getownpropertynames () method.

var keys = object.getownpropertynames (Person.prototype); alert (keys);//"Constructor,name,age,job,sayname"



Note The constructor attribute is included in the results. Both the Object.keys () and Object.getownpropertynames () methods can be used to replace the for-in loop. Browsers that support both methods are ie9+, Firefox 4+, Safari 5+, Opera 12+, and Chrome.

Simpler prototype syntax

In the previous example, every property and method you add will be knocked over Person.prototype. To reduce unnecessary input and to visually better encapsulate the functionality of the prototype, it is more common to rewrite the entire prototype object with an object literal that contains all the properties and methods, as shown in the following example.

function person () {}person.prototype= {        name: "Nicholas",        age:29,        job: "Software Engineer",        sayname: function () {                 alert (this.name);        }};


In the above code, we set the person.prototype to equal to a new object created as an object literal. The end result is the same, with one exception: The constructor property no longer points to person. As described earlier, each time you create a function, it creates its prototype object, which also automatically gets the constructor property. The syntax we use here essentially completely overrides the default prototype object, so the constructor property becomes the constructor property of the new object (pointing to the object constructor) and no longer points to the person function. At this point, although the instanceof operator can return the correct result, the type of the object cannot be determined by constructor, as shown below.

var friend = new person (); alert (friendinstanceof Object); Truealert (friendinstanceof person); Truealert (friend.constructor== person); Falsealert (friend.constructor== Object); True


Here, the instanceof operator is used to test that object and person still return true, but the constructor property is equal to object and not equal to person. If the value of constructor is really important, you can deliberately set it back to the appropriate value as follows.

function person () {} person.prototype= {        Constructor:person,        name: "Nicholas",        age:29,        job: " Software Engineer ",        sayname:function () {                 alert (this.name);}};


The code above intentionally contains a constructor property and sets its value to person, ensuring that the appropriate value is accessible through the property. Note that resetting the constructor property in this way causes its [[Enumerable]] attribute to be set to true. By default, the native constructor property is not enumerable, so if you use a JavaScript engine that is compatible with ECMAScript 5, you can try Object.defineproperty ().


function person () {}person.prototype= {        name: "Nicholas",        age:29,        job: "Software Engineer",        sayname: function () {                 alert (this.name);        }};


Reset constructors, only for ECMAScript 5 compatible browsers

Object.defineproperty (Person.prototype, "constructor", {        enumerable:false,        Value:person});


The dynamic nature of prototypes

Because the process of looking up values in a prototype is a search, any modifications we make to the prototype object are immediately reflected from the instance-even if the prototype was modified before the instance was created. Take a look at the example below.

<pre name= "Code" class= "javascript" >var friend = new person (); person.prototype.sayhi= function () {        alert ("HI");}; Friend.sayhi ();//"HI" (No problem!) )


The above code first creates an instance of the person and saves it in a friend. Then, the next statement is in person. A method Sayhi () is added to the prototype. Even if the friend instance was created before the new method was added, it can still access the new method. The reason can be attributed to the loosely connected relationship between the instance and the prototype. When we call Friend.sayhi (), we first search for an attribute named Sayhi in the instance, and will continue to search for the prototype if it is not found. because the connection between the instance and the prototype is simply a pointer, not a copy, so you can find the new Sayhi property in the prototype and return the function that is stored there.

Although you can add properties and methods to your prototype at any time, and the modifications are immediately reflected in all object instances, the situation is different if you rewrite the entire prototype object. We know when the constructor is called, a [[Prototype]] pointer is added to the instance that points to the original prototype , and modifying the prototype to another object is tantamount to cutting off the connection between the constructor and the original prototype. Keep in mind that the pointer in the instance points only to the prototype, not to the constructor. Look at the following example.

function person () {} var friend = new person (); person.prototype= {        Constructor:person,        name: "Nicholas",        age:29,        job: "Software Engineer",        Sayname:function () {                 alert (this.name);        


In this example, we first create an instance of the person and then rewrite the prototype object. Then an error occurred when calling Friend.sayname () because a friend pointed to a prototype that does not contain a property named by that name. Figure 6-3 shows the inside of the process.


As you can see from figure 6-3, rewriting the prototype object cuts off the connection between the existing prototype and any previously existing object instances, and they still refer to the original prototype.

Prototypes of native objects

The importance of prototype patterns is not only in the creation of custom types, but even in all native reference types, which are created using this pattern. All native reference types (Object, Array, String, and so on) define methods on the prototype of their constructors. For example, the sort () method can be found in array.prototype, and the substring () method can be found in String.prototype, as shown below.

alert (TypeofArray.prototype.sort); "Function" alert (typeofString.prototype.substring); "Function"


By prototyping a native object, you can not only get references to all the default methods, but you can also define new methods. You can modify the prototype of a native object just as you would a prototype of a custom object, so you can add a method at any time. The following code adds a method named StartsWith () to the basic wrapper type string.

string.prototype.startswith= function (text) {        Returnthis.indexof (text) = = 0;}; varmsg = "Hello world!"; Alert (Msg.startswith ("Hello"));//true


The newly defined StartsWith () method here returns True when the incoming text is at the beginning of a string. Now that the method is added to String.prototype, all strings in the current environment can be called. Because MSG is a string, and the background calls the string Basic wrapper function to create this, the StartsWith () method can be called via MSG.

Although this can be done, we do not recommend modifying native object prototypes in a product-ready program. If a method is missing from an implementation, this method is added to the prototype of the native object, and then when another

naming conflicts can result when running code in the implementation of this method. Also, doing so may inadvertently override the native method.


Problems with prototype objects

Prototyping mode is also not without drawbacks. First, it omits the process of passing initialization parameters to the constructor , resulting in all instances getting the same property value by default. While this will somehow bring some inconvenience, it's not the biggest problem with prototypes.

The biggest problem with prototype patterns is the nature of their sharing . All of the properties in the prototype are shared by many instances, and the share is appropriate for the function. It makes sense for those attributes that contain basic values, after all (as shown in the previous example), by adding a property of the same name to the instance, you can hide the corresponding attribute in the prototype. However, for attributes that contain reference type values, the problem is more prominent. Take a look at the following example.

function person () {}person.prototype= {        Constructor:person,        name: "Nicholas",        age:29,        job: " Software Engineer ",        friends: [" Shelby "," Court "],        sayname:function () {                 alert (this.name);        }}; var person1 = new Person (), var person2 = new Person (); Person1.friends.push ("Van"); alert (person1.friends);//"Shelby,court,van" alert (person2.friends);//"Shelby,court,van" alert (person1.friends=== Person2.friends); True


Here, the Person.prototype object has a property named friends that contains an array of strings. Then, two instances of person are created. Next, an array of person1.friends references is modified, and a string is added to the array. since the Friends array exists in Person.prototype rather than person1 , So the changes you just mentioned will also be reflected by person2.friends(and person1.friends pointing to the same array). . If our original intention is to share an array in all instances like this, then there is nothing to say about this result. However, the example is generally to have all of their own properties. And that's why we rarely see people using prototype patterns alone.






Introduction to JavaScript Design Patterns (iii) prototype pattern extension knowledge

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.