Article Introduction: Each function we create has a prototype (prototype) attribute, which is a pointer to an object that is intended to contain properties and methods that can be shared by all instances of a particular type. If understood by literal means, then prototype is the prototype object of that object instance created by calling the constructor. Using a prototype object |
Each function we create has a prototype (prototype) attribute, which is a pointer to an object that is intended to contain properties and methods that can be shared by all instances of a particular type. If understood by literal means, then prototype is the prototype object of that object instance created by calling the constructor. The advantage of using a prototype object is that all object instances can share the properties and methods it contains. In other words, instead of defining the object instance's information in the constructor, you can add the information directly to the prototype object, as shown in the following example:
function person () {}
Person.prototype.name = "Nicholas";
Person.prototype.age = "a";
Person.prototype.job = "Software Engineer";
Person.prototype.sayName = function () {
alert (this.name);
};
var person1 = new Person ();
Person1.sayname (); "Nicholas"
var person2 = new Person ();
Person2.sayname (); "Nicholas";
Alert (Person1.sayname = = Person2.sayname); True
Here, we add the Sayname () method and all the attributes directly to the person's prototype property, and the constructor becomes an empty function. Even so, you can still create a new object by calling the constructor, and the new object will also have the same properties and methods. But unlike the constructor pattern, these properties and sides of the new object
The method is shared by all instances. In other words, Person1 and Person2 are accessing both the same set of attributes and the same sayname () function. In order to understand the working principle of the prototype model, we must understand the nature of the ECMAScript central object.
Understanding the Prototype object
Whenever you create a new function, you create a prototype attribute for the function based on a specific set of rules, which points to the prototype object of the function. By default, all prototype objects will get a constructor (constructor) property that contains a pointer to the function where the prototype property is located. Take the previous example, Person.prototype.constructor point to person. With this constructor, we can also continue to add other properties and methods to the prototype object.
After the custom constructor is created, its prototype object will only get the constructor property, and for other methods, it is inherited from object. When the constructor is called to create a new instance, the internal of the instance will contain a pointer (internal property) that points to the stereotype object of the constructor. In many implementations, the name of the internal attribute is _proto_ and can be accessed via scripting (in Firefox, Safari, Chrome, and Flash's ActionScript, which can be accessed via scripting _proto_), and in other implementations, This property is completely invisible to the script. However, the really important point to be clear is that the connection exists between the instance and the stereotype object of the constructor, not the instance between the constructor.
Although internal _proto_ properties cannot be accessed in some implementations, the isPrototypeOf () method can be used in all implementations to determine whether the relationship exists between objects. Essentially, if an object's _proto_ points to an object (Person.prototype) that calls the isPrototypeOf () method, the method returns True, as follows:
Alert (Person.prototype.isPrototypeOf (Person1)); true;
Alert (Person.prototype.isPrototypeOf (Person2)); True
Here, we test the Person1 and Person2 with the isPrototypeOf () method of the prototype object. All of them return true because they have a pointer to the person.prototype inside them.
Whenever the code reads a property of an object, it performs a search, with the target being a property with the given name. The search starts first from the object instance itself. If a property with the given name is found in the instance, the value of the property is returned, and if not found, the search continues to the prototype object to which the pointer points, looking for the property with the given name in the prototype object. If this property is found in the prototype object, the value of the property is returned. In other words, when we call Person1.sayname (), we will perform two searches successively. First, the parser asks, "Does the instance Person1 have sayname properties?" "No," replied the secretary. "Then, it continues to search, and then asks:" Person1 's prototype has sayname attribute? "Yes," said the secretary. So, it reads the function that is stored in the prototype object. When we call Person2.sayname (), we will reproduce the same search process and get the same result. This is precisely the rationale for the properties and methods that multiple objects share with the prototype.
As mentioned earlier, the prototype initial value contains the constructor attribute, which is also shared, so it can be accessed through an object instance.
Although you can access values that are stored in the prototype through an object instance, you cannot override the values in the prototype through an object instance. If we add a property to the instance that has the same name as a property in the instance prototype, we create the property in the instance that will mask that property in the prototype. Look at the following example:
function person () {}
Person.prototype.name = "Nicholas";
Person.prototype.ae =;
Person.prototype.job = "Software Engineer";
Person.prototype.sayName = function () {
alert (this.name);
}
var person1 = new Person ();
var person2 = new Person ();
Person1.name = "Greg";
alert (person1.name); "Greg"--from instance
alert (person2.name);//"Nicholas"--from prototype
In this example, the name of Person1 is masked by a new value. However, both the access Person1.name and the access Person2.name can properly return the value, namely "Greg" (from the object instance) and "Nicholas" (from the prototype). When you access Person1.name in alert (), you need to read its value, so you will search for a property named name on this instance. This property does exist, so it returns its value without having to search the prototype again. When Person2.name is accessed in the same way, the property is not found on the instance, so the search for the prototype continues, resulting in the name attribute being found there.
When a property is added to an object instance, this property masks the attributes of the same name that are saved by the prototype object rollup; in other words, adding this property only organizes the attributes that we access in the prototype, but does not modify that attribute. Even if this property value is set to NULL, this property will only be set in the instance. The connection to the prototype is not returned. No, using the delete operator allows you to completely remove the instance property, allowing us to regain access to the properties in the prototype, as follows:
function person () {}
Person.prototype.name = "Nicholas";
Person.prototype.age =;
Person.prototype.job = "Software Engineer";
Person.prototype.sayName = function () {
alert (this.name);
}
var person1 = new Person ();
var person2 = new Person ();
Person1.name = "Greg";
alert (person1.name); "Greg"-from instance
alert (person2.name);//"Nicholas"--from the prototype
delete person1.name;
alert (person1.name); "Nicholas"--from prototypes
In this modified example, we removed the person1.name using the delete operator, which previously saved the "Greg" value masking the stereotype attribute with the same name. After it is deleted, the connection to the Name property in the prototype is restored. Therefore, the next time you call Person1.name, the value of the Name property in the prototype is returned.
Use the hasOwnProperty () method to detect whether a property exists in the instance or in the prototype. This method (remember that it is inherited from object) returns true only if the given property exists in the object instance. Look at the following example:
function person () {}
Person.prototype.name = "Nicholas";
Person.prototype.jog = "Software Engineer";
Person.prototype.sayName = function () {
alert (this.name);
};
var person1 = new Person ();
var person2 = new Person ();
Alert (Person1.hasownproperty ("name")); False
Person1.name = "Greg";
alert (person1.name); "Greg"-from instance
alert (Person1.hasownproperty ("name")),//true
alert (person2.name);//"Nicholas"-from prototype
Alert (Person2.hasownproperty ("name"));//false
delete person1.name;
alert (person1.name); "Nicholas"-From prototype
alert (Person1.hasownproperty ("name"));//flase
When you access an instance property by using the hasOwnProperty () method, it is clear when you access the prototype attribute. When Person1.hasownproperty ("name") is invoked, it returns true only if Person1 overrides the Name property, because only then name is an instance property, not a prototype property.
prototypes and in Operators
There are two ways to use the In operator: separate use and use in the for-in loop. When used alone, the in operator returns True when the given property is accessible through an object, regardless of whether the property exists in the instance or in the prototype. Take a look at the following example:
function person () {}
Person.prototype.name = "Nicholas";
Person.prototype.age = "a";
Person.prototype.job = "Software Engineer";
Person.prototype.sayName = function () {
alert (this.name);
};
var person1 = new Person ();
var person2 = new Person ();
Alert (Person1.hasownproperty ("name")); False
alert ("name" in Person1);//true
person1.name = "Greg";
alert (person1.name); "Greg"--from instance
alert (Person1.hasownproperty ("name"));//true;
Alert ("name" in Person1); True
alert (person2.name);//"Nicholas"-From prototype
alert (Person2.hasownproperty ("name");//false
Alert ("name" in Person1); True
Delete person1.name;
alert (person1.name); "Nicholas"-From prototype
alert (Person1.hasownproperty ("name")),//false
alert ("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 the "name" in Person1 always returns true regardless of whether the property exists in the instance or 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 follows:
function Hasprototypeproperty (object, name) {return
!object.hasownproperty (name) && (name in object);
}
Because the in operator returns True,hasownproperty () only if it has access to the property through the object, it returns true only if the attribute exists in the instance, so as long as the in operator returns True and hasOwnProperty () returns FALSE, You can determine that the attribute is a property in the prototype. Here's a look at the function defined above Hasprototypeproperty () usage:
function person () {
}
Person.prototype.name = "Nicholas";
Person.prototype.age = "a";
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.
When using the for-in loop, all of the enumerable (enumerated) attributes that are accessible through the object are returned, including the properties that exist in the instance, as well as the properties that exist in the prototype. The instance property that masks an enumerable property in the prototype (that is, a property that has the [[[Dontenum]] tag set) is also returned in the for-in loop because all developer-defined attributes are enumerable-except IE.
There is a bug in IE's JScript implementation where instance properties that block an enumerable attribute 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");
When the above code is run, a warning box should appear indicating that the ToString () method was found. Object o here defines a method named ToString () that masks the ToString () method in the prototype (not enumerable). In IE, because its implementation considers the ToString () method of the prototype to be marked with a [[dontenum]] tag, it should skip the property, and we will not see the warning box. The bug affects all properties and methods that are not enumerable by default, including: hasOwnProperty (), propertyisenumerable (), tolocalestring (), toString (), and valueof (). Some browsers also have a [[dontenum]] tag for the constructor and prototype properties, but this is not a common practice for all browsers.
Simpler prototype Syntax
The reader probably noticed that in the previous example each addition of a property and method would have to be knocked over Person.prototype. In order 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 in the following example:
function person () {
}
person.prototype = {
name: "Nicholas",
age:29,
job: "Software Engineer",
sayname:function () {
alert (this.name);
}
};
In the code above, we set the person.prototype to be equal to a new object created in the literal form of an object. 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, and the object automatically gets the constructor attribute. The syntax we use here essentially 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, it is not possible to determine the object's type by constructor, as follows:
function person () {
}
person.prototype = {
name: "Nicholas",
age:29,
job: "Software Engineer" ,
sayname:function () {
alert (this.name);
}
};
var person = new person ();
Alert (person instanceof Object); True
alert (person instanceof person);//true
alert (person.constructor = = person);//false
alert ( Person.constructor = = Object); True
Here, test object and person with the instanceof operator still returns true, but the constructor property equals object instead of 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);
}
};
var person = new person ();
The above code deliberately includes a constructor property and sets its value to person, ensuring that the appropriate values are accessible through this property.
Alert (person instanceof Object); True
alert (person instanceof person);//true
alert (person.constructor = = person);//true
alert ( Person.constructor = = Object); False
The dynamic nature of the prototype
Since the process of looking up a value in a prototype is a search, any changes we make to the prototype object can be immediately reflected from the instance-even if Xi ' an has created the instance and modified the prototype. Take a look at the following example:
var person = new person ();
Person.prototype.sayHi = function () {
alert ("HI");
};
Person.sayhi (); "Hi" (No problem!) )
The above code first creates an instance of person and saves it in person. The next statement then adds a method Sayhi () to the Person.prototype. Even though the person instance was created before the new method was added, it can still access the new method. The reason can be attributed to the loose connection between the instance and the prototype. When we call Person.sayhi (), we first search the instance for a property named Sayhi and continue searching for the prototype if not found. Because the connection between the instance and the prototype is just a pointer, not a copy, you can find the new Sayhi property in the prototype and return the function that was saved there.
Although you can add properties and methods to the stereotype at any time, and the modifications are immediately reflected in all object power, the situation is different if you rewrite the entire prototype object. We know that when you call a constructor, the aftertaste instance adds a _proto_ pointer to the original prototype, and modifying the prototype to another object is tantamount to cutting off the relationship between the constructor and the original prototype. Keep in mind that the pointer in the instance is near the prototype, not the constructor. Look at the following example:
function person () {}
var person = new person ();
Person.prototype = {
Constructor:person,
name: "Nicholas",
age:29,
job: "Software engineer",
Sayname:function () {
alert (this.name);
}
};
Person.sayname (); Error
In this example, we first create an instance of person and then rewrite its prototype object. Then an error occurred while calling Person.sayname () because the stereotype that person points to does not contain attributes that are renamed by name.
the prototype of the native object
The importance of the center pattern is not only embodied in the creation of custom types, but even all native reference types are created in this pattern. All native reference types (Object, Array, String, and so on) define the method on the prototype of its constructor. For example, the sort () method can be found in array.prototype, and the substring () method can be found in String.prototype, as follows:
Alert (typeof Array.prototype.sort); "Function"
alert (typeof String.prototype.substring);//"function"
By the principle of native objects, you can not only get references to all the default methods, but also define new methods. You can modify the prototype of a native object as you would a prototype of a custom object, so Karki add the method at any time. The following code adds a method named StartsWith () to the base wrapper type string: Yield
String.prototype.startsWith = function (text) {return
this.indexof (text) = 0;
};
var msg = "Hello world!";
Alert (Msg.startswith ("Hello")); True
The StartsWith () method defined here returns True when the incoming text is at the beginning of the yield string. Now that the method is added to the string.pprototype, all strings in the current environment can call it. Because MSG is a string. And the background will call the string base Royalist function to create the strings, so you can invoke the StartsWith () method via Msg.
Although this can be done, we do not recommend modifying the prototype of the native object in the production program. If you add this method to the prototype of a native object because a method is missing from an implementation, you may cause a naming conflict when you run code in another implementation that supports the method. Also, doing so might accidentally rewrite the native method.
problem with a prototype object
The prototype model is also not without drawbacks. First, it omits the process of passing initialization parameters for constructors, resulting in the same property values being obtained by default for all instances. While this may be inconvenient in some way, it is not the biggest problem with prototypes. The biggest problem with archetypal patterns is that they are shared by nature.
All the attributes in the prototype are shared by many instances, and this sharing is appropriate for the function. For those that contain basic values, the past, after all, can hide the corresponding properties in the prototype by adding a property with the same name on the instance. However, the problem is more pronounced for properties that contain reference types. Look at the following example:
function person () {}
Person.prototype = {
Constructor:person,
name: "Nicholas",
age:2,
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); Ture
Here, the Person.prototype object has a property named friends that contains an array of strings. Then, two instances of person were created. It then modifies the Person1.friends reference array and adds a string to the array. Because the firends array exists and person.prototype rather than Person1, the changes just mentioned will be reflected through person2.friends. If our intention is to share an array in all instances like this, then I have nothing to say about the result. However, the example is generally to have their own all the attributes. And that's why we rarely see someone using the prototype model alone.