The ECMA-262 defines an object as: "a set of properties that can contain basic values, objects, or functions ." Strictly speaking, this is equivalent to indicating that the object is a group of values with no specific order. Each property or method of an object has a name, and each name is mapped to a value. Because of this, we can think of ECMAScript objects as a hash: A group of name pair values, where values can be data or functions. The ECMA-262 defines an object as: "a set of properties that can contain basic values, objects, or functions ." Strictly speaking, this is equivalent to indicating that the object is a group of values with no specific order. Each property or method of an object has a name, and each name is mapped to a value. Because of this, we can think of ECMAScript objects as a hash: A group of name pair values, where values can be data or functions.
The simplest way to create a custom Object is to create an Object instance and then add attributes and methods to it, as shown below:
var person = new Object();person.name = "liubei";person.age = 29;person.job = "shayemuyou";person.sayName = function(){ alert(this.name);}
In the preceding example, an object named person is created and three attributes and a method are added to the object. The sayName () method is used to display the name attribute. this. name will be parsed as person. name, which is often used by early developers to create objects. Later, the object literal method became the preferred mode for object creation, the preceding example uses the syntax of object literal as follows:
var person = { name:"liubei", age:29, job:"shayemuyou", sayName:function(){ alert(this.name); }}
In this example, the person object is the same as the previous object and has the same attributes and methods.
Although Object constructors or Object literal methods can be used to create a single Object, these methods have an obvious drawback: using the same interface to create many objects, A large amount of repeated code is generated. To solve this problem, people began to use a variant of the factory model.
1. Factory Model
The factory model is a well-known design pattern in the software engineering field, which abstracts the process of creating a specific object. Considering that classes cannot be created in ECMAScript, developers have invented a function that encapsulates the details of creating objects with specific interfaces, as shown below:
function createPerson(name, age, job){ var o = new Object(); o.name = name; o.age = age; o.job = job; o.sayName = function(){ alert(this.name); } return o;}var person1 = createPerson("wei",25,"software");var person2 = createPerson("bu",25,"software");
The createPerson () function can construct a Person object containing all necessary information based on accepted parameters. You can call this function multiple times. Each time, an object containing three attributes and one method is returned. Although the factory mode solves the problem of creating multiple similar objects, it does not solve the problem of object recognition, that is, how to know which object type this is.
2. constructor Mode
Native constructors such as Array and Object will automatically appear in the execution environment during runtime. In addition, we can create custom constructors to define attributes and methods of custom types. For example, we can use the constructor to rewrite the previous example:
function Person(name, age, job){ this.name = name; this.age = age; this.job = job; this.sayName = function(){ alert(this.name); }}var person1 = new Person("wei",25,"software");var person2 = new Person("bu",25,"software");
In this example, the Person () function replaces the createPerson () function. We note that the difference between Person () and createPerson () is:
In addition, you should note that the function name Person uses the uppercase letter P. By convention, constructor should always start with an upper-case letter, rather than a lower-case letter. This practice draws on other OO languages to distinguish them from other functions in ECMAScript. Because the constructor itself is a function, it can only create objects.
To create a Person instance, you must use the new operator. The preceding steps take the following four steps:
1. Create a new object
2. Assign the scope of the constructor to the new object (so this points to the new object)
3. Execute the code in the constructor
4. Return the new object
At the end of the previous example, person1 and person2 respectively save a different instance of Person. Both objects have a constructor attribute that points to Person. As follows:
console.log(person1.constructor == Person); //trueconsole.log(person2.constructor == Person); //trueconsole.log(person1.constructor == Person); //trueconsole.log(person2.constructor == Person); //true
The constructor attribute of an object is initially used to identify the object type. However, the instanceof operator is more reliable when it comes to object types. The objects created in this example are instances of Object objects and instance of Person objects, which can be verified by the instanceof operator.
console.log(person1 instanceof Object); //trueconsole.log(person1 instanceof Person); //trueconsole.log(person2 instanceof Object); //trueconsole.log(person2 instanceof Person); //true
Creating a custom constructor means that his instance can be identified as a specific type in the future. This is where the constructor mode is better than the factory mode. In this example, person1 and person2 are the same Object instances because all objects are inherited from objects.
The main problem with constructor is that every method must be re-created on the instance, resulting in a waste of memory. In the previous example, person1 and person2 both have a method named sayName (), but the two methods are not instances of the same Function. Do not forget that functions in ECMAScript are also objects. Therefore, every function defined is an object instantiated. Logically, the constructor can be defined as follows:
Function Person (name, age, job) {this. name = name; this. age = age; this. job = job; this. sayName = new Function ("alert (this. name); ") // logically equivalent to the declarative function}
From this perspective, it is easier to understand that each Person instance contains the essence of a different Function instance. Otherwise, different scopes and identifiers will be resolved, but the mechanism for creating a new Function instance is still the same. Therefore, functions of the same name on different instances are not equal. The following code can prove this.
alert(person1.sayName == person2.sayName); //false
However, it is unnecessary to create two Function instances that complete the same task. Besides, this object does not need to bind the Function to a specific object before executing the code. Therefore, we can solve this problem by moving the function definition to the external part of the constructor as follows.
function Person(name, age, job){ this.name = name; this.age = age; this.job = job; this.sayName = sayName;}function sayName(){ alert(this.name);
This solution solves multiple functions to solve the same problem, but a new problem arises. In the global scope, it is actually called only by a certain object, which makes the global object a bit named. What is even more unacceptable is that if the object needs to define many methods, many global functions should be defined. Therefore, the custom reference type is not encapsulated at all. Fortunately, these problems can be solved using the prototype mode.
3. Prototype
Each function we create has a prototype attribute, which is a pointer pointing to an object, the purpose of this object is to include attributes and methods that can be shared by all instances of a specific type. An instance that uses a prototype object is used to allow all instances to share its attributes and methods. In other words, you do not need to define the instance information of the object in the constructor, but can directly add the information to the prototype object, as shown below:
function Person(){}Person.prototype.name = "wei";Person.prototype.age = 27;Person.prototype.job = "Software";Person.prototype.sayName = function(){ alert(this.name);}var person1 = new Person();person1.sayName(); //"wei"var person2 = new Person();person2.sayName(); //"wei"alert(person1.sayName == person2.sayName);
Here, we add the sayName () method and all attributes directly to the prototype attribute of Person, and the constructor becomes an empty function. Even so, we can still create an object through the constructor, and the new object will have the same attributes and methods. However, unlike constructors, these attributes and methods of new objects are shared by all instances. In other words, both person1 and person2 access the same group of attributes and the same sayName () function. To understand the working principle of the prototype, you must first understand the nature of the original object in ECMAScript.
The nature of the prototype object will be analyzed in detail in the next chapter due to its long length. We have discussed the benefits of the prototype. Next, let's take a look at the disadvantages of the prototype. The prototype mode skips the process of passing parameters for the constructor. As a result, all instances have the same attribute value by default. This may cause inconvenience to some extent. This is not the biggest problem in the prototype mode, because if we want to add attributes for an object created in the prototype mode, this property will block the attributes of the same name saved by the prototype object. In other words, the added attribute will prevent us from accessing the attributes in the prototype, but will not change the attributes in the prototype.
The biggest problem with the prototype model is the nature of its sharing. All attributes in the prototype are shared by many instances. This type of sharing is very suitable for functions. It also applies to attributes that contain basic values. However, the attribute values of the reference type are more prominent, here is an example:
Function Person () {} Person. prototype = {constructor: Person, name: "wei", age: 29, friends: ["Qianlong", "Kangxi"], sayName: function () {alert (this. name) ;}} var person1 = new Person (); var person2 = new Person (); person1.friends. push ("zhengzheng"); console. log (person1.friends); // ["Qianlong", "Kangxi", "Zheng"] console. log (person2.friends); // ["Qianlong", "Kangxi", "Zheng"] console. log (person1.friends === person2.friends); // true
In the above example, the Person. prototype object has an attribute named friends, which contains a string array. Create two Person instances, modify the array referenced by person1.friends, and add a string to the array because the array exists in Person. prototype rather than person1, so person2.friends will also be modified. However, generally, each object must have its own attributes. Therefore, we seldom see that someone uses the prototype mode to create an object.
4. Use the constructor mode and prototype mode in combination.
The most common way to create a custom type is to combine the constructor mode and the prototype mode. The constructor mode defines instance attributes, and the prototype mode defines methods and shared attributes. As a result, each instance has its own copy of the Instance attribute, but it also shares the reference to the method, saving the memory to the maximum extent. In addition, this mixed mode also supports passing parameters to the constructor. The following code overwrites the previous example:
Function Person (name, age) {this. name = name; this. age = age; this. friends = ["Qianlong", "Kangxi"];} Person. prototype = {constructor: Person, sayName: function () {alert (this. name) ;}} var person1 = new Person ("wei", 29); var person2 = new Person ("bu", 25); person1.friends. push ("zhengzheng"); console. log (person1.friends); // ["Qianlong", "Kangxi", "Zheng"] console. log (person2.friends); // ["Qianlong", "Kangxi"] console. log (person1.friends === person2.friends); // falseconsole. log (person1.sayName === person2.sayName); // true
In this example, the instance attributes are defined in the constructor and the method sayName () shared by all instances are defined in the prototype. Therefore, modifying person1.friends does not change person2.friends because they reference different arrays.
This constructor and prototype are the most widely used methods in ECMAScript to create custom types. It can be said that this is a default form used to define the reference.
5. Dynamic Prototype
Developers with experience in other OO languages may be very confused when they see independent constructors and prototypes. The dynamic prototype is a solution to solve this problem. It encapsulates all the information in the constructor, by initializing the prototype in the constructor (only when necessary), the advantage of using the constructor and prototype at the same time is maintained. In other words, you can check whether a method that should exist is valid to determine whether to initialize the prototype. Let's look at an example:
Function Person (name, age) {this. name = name; this. age = age; this. friends = ["Qianlong", "Kangxi"]; // note the if statement if (typeof this. sayName! = "Function") {Person. prototype. sayName = function () {alert (this. name) ;}}var person1 = new Person ("wei", 29); person1.friends. push ("Zheng"); person1.sayName ();
Note that the if statement in the constructor code is added to the prototype only when the sayName () method does not exist. This code is executed only when the constructor is called for the first time. After that, the prototype has been initialized and does not need to be modified. Remember that the changes made here are immediately reflected in all instances. Therefore, this method is indeed perfect. The if statement checks any methods and attributes that should exist after initialization-you don't have to use a lot of if Statements to check each attribute and method, just check one of them. For an object created in this mode, you can also use the instanceof operator to determine its type.
Note: when using the dynamic prototype mode, you cannot use the object literal to overwrite the prototype. If the prototype is rewritten when an instance has been created, the connection between the existing instance and the new original type is cut off.
6. Parasitic constructor Mode
Generally, the parasitic constructor mode can be used when none of the preceding modes are suitable. The basic idea of this mode is to create a function. The function only encapsulates the code for creating an object and then returns the newly created object, this function is similar to a typical constructor. Let's look at an example:
function Person(name, age, job){ var o = new Object(); o.name = name; o.age = age; o.job = job; o.sayName = function(){ alert(this.name); } return o;}var person = new Person("wei",29,"banzhuan");person.sayName(); //"wei"
In this example, the Person function creates an object, initializes the object with the corresponding attributes and methods, and returns the object. In addition to calling the constructor using the new operator, this mode is not much different from the factory mode. If no value is returned, the constructor returns the instance of the new object by default. By adding a return statement at the end of the constructor, You can override the value returned when calling the constructor.
In this mode, you can create constructors for objects in special cases. Suppose we want to create a special array with additional methods. Because you cannot directly modify the Array constructor, you can use this mode:
Function SpecialArray () {// create an Array var values = new Array (); // Add the value values. push. apply (values, arguments); // Add the values method. toPipedString = function () {return this. join ("|") ;}// return array return values;} var colors = new SpecialArray ("red", "blue", "green"); console. log (colors. toPipedString (); // red | blue | green
In this example, we create a constructor named SpecialArray. In this function, an array is created first, and the value of the array is initialized by the push () method. Then, the toPipedString () method is added to the array instance to return array values separated by vertical bars. Finally, the array is returned as a function. Then, we call the SpecialArray constructor, pass in the initialization value, and call the toPipedString () method.
For the parasitic constructor mode, we need to declare that: first, the returned object has nothing to do with the constructor or constructor prototype; that is, the objects returned by the constructor are no different from those created outside the constructor. Therefore, the instanceof operator cannot be used to determine the object type. Because of this problem, we recommend that you do not use this mode when other modes are available.
7. Secure constructor Mode
Douglas clarford invented the concept of secure objects in JavaScript. The so-called secure object means that there is no public attribute and its method does not reference this object. Secure objects are best suited to some security environments (this and new are not allowed in these environments), or to prevent data from being changed by other applications. The safe constructor follows a similar pattern to the parasitic constructor, but there are two differences: first, the instance method of the newly created object does not reference this; second, the constructor is not called using the new operator. According to the requirements of the secure constructor, you can rewrite the previous Person constructor as follows:
Function Person (name, age, job) {// create a new Object var o = new Object () to be returned (); // Private variables and functions can be defined here. // Add method o. sayName = function () {alert (this. name) ;}; // return object return o ;}
Note: In the objects created in this mode, there is no way to access the value of name except the sayName () method. You can use a secure Person constructor as follows:
var person =Person("weiqi",22,"banzhuan");person.sayName(); //weiqi
In this way, the variable person stores a secure object, and there is no other way to access other data members except the sayName () method. Even if other Code adds methods or data members to the object, it is impossible to access the original data passed into the constructor. The security provided by the SAFE constructor mode makes it ideal for use in some secure execution environments, such as the environments provided by ADsafe (www.adsafe.org.
Note: similar to the parasitic constructor mode, the objects created in the safe constructor mode have no relationship with the constructor. Therefore, the instanceof operator has no significance for this object.
The above is the content of the 7 modes for creating objects in JavaScript. For more information, see PHP Chinese website (www.php1.cn )!