• Object Creation


      Although using the object constructor or an object literal are convenient ways to create single objects, there is an obvious downside: creating multiple objects with the same interface requires a lot of code duplication. To solve this problem, developers began using a variation of the factory pattern.

    The Factory Pattern

      With no way to define classes in ECMAScript, developers created functions to encapsulate the creation of objects with specific interfaces, such as in this example:

     1 function createPerson(name, age, job){
     2     var o = new Object();
     3     o.name = name;
     4     o.age = age;
     5     o.job = job;
     6     o.sayName = function(){
     7         alert(this.name);
     8     };
     9     return o; 
    10 }
    11 
    12 var person1 = new Person("Nicholas", 29, "Software Engineer");
    13 
    14 var person2 = new Person("Greg", 27, "Doctor");
    View Code

      Though this solved the problem of creating multiple similar objects, the factory pattern didn't address the issue of object identification(what type of object an object is). 

    The Constructor Pattern

      constructors in ECMAScript are used to create specific types of objects. For instance, the previous example can be rewritten using the constructor pattern as following:

     1 function Person(name, age, job){
     2     this.name = name;
     3     this.age = age;
     4     this.job = job;
     5     this.sayName = function(){
     6         alert(this.name);
     7     };
     8 }
     9 
    10 var person1 = new Person("Nicholas", 29, "Software Engineer");
    11 
    12 var person2 = new Person("Greg", 27, "Doctor");
    View Code

      By convention, constructor functions always begin with an uppercase letter, whereas nonconstructor functions begin with a lowercase letter.

      To create a new instance of Person, use the new operator. Calling a constructor in this manner essentially causes the following four steps to be taken:

    1. Create a new object.
    2. Assign the this value of the constructor to the new object(so this points to the new object).
    3. Execute the code inside the constructor (adds properties to the new object).
    4. Return the new object.

    Constructors as Functions

      Any function that is called with the new operator acts as a constructor, whereas any function called without it acts just as you would expect a normal function call to act. For instance, the Person() function from the previous example may be called in any of the following ways:

     1 // use as a constructor
     2 var person = new Person("Nicholas", 29, "Software Engineer");
     3 person.sayName();              // "Nicholas"
     4 
     5 // call as a function
     6 Person("Greg", 27, "Doctor");           // adds to window
     7 window.sayName();             // "Greg"
     8 
     9 // call in the scope of another object
    10 var o = new Object();
    11 Person.call(o, "Kristen", 25, "Nurse");
    12 o.sayName();                // "Kristen"
    View Code

    Probles with Constructors

      The major downside to constructors is that methods are created once for each instance. So, in the previous example, both person1 and person2 have a method called sayName(), but those methods are not the same instance of Function. Remember, functions are objects in ECMAScript, so every time a function is defined, it's actually an object being instantiated. Logically, the constructor actually looks like this:

    1 function Person(name, age, job){
    2     this.name = name;
    3     this.age = age;
    4     this.job = job;
    5     this.sayName = new Function("alert(this.name)");             // logical equivalent
    6 }
    View Code

      functions of the same name on different instances are not equivalent, as the following code proves:

    1 alert(person1.sayName === person2.sayName);              // false
    View Code

       It's possible to work around this limitation by moving the function definition outside of the constructor, as follow:

     1 function Person(name, age, job){
     2     this.name = name;
     3     this.age = age;
     4     this.job = job;
     5     this.sayName = sayName;
     6 }
     7 
     8 function sayName(){
     9     alert(this.name);
    10 }
    View Code

      This solves the problem of having duplicate functions that do the same thing but also creates some clutter in the global scope by introducing a function that can realistically be used only in relation to an object. 

    The Prototype Pattern

      The benefit of using the prototype is that all of its properties and methods are shared among object instances.

     1 function Person(){
     2 }
     3 
     4 Person.prototype.name = "Nicholas";
     5 Person.prototype.age = 29;
     6 Person.prototype.job = "Software Engineer";
     7 Person.prototype.sayName = function(){
     8   alert(this.name);
     9 };
    10 
    11 var person1 = new Person();
    12 person1.sayName();               // "Nicholas"
    13 
    14 var person2 = new Person();
    15 person2.sayName();               // "Nicholas"
    16 
    17 alert(person1.sayName === person2.sayName);           // true
    View Code

    How Prototypes work

      Whenever a function is created, its prototype property is also created according to a specific set of rules. By default, all prototypes automatically get a property called constructor that points back to the function on which it is a property.

      Each time the constructor is called to create a new instance, that instance has a internal pointer to the constructor's prototype. In ECMA-262 fifth edition, this is called [[Prototype]]. There is no standard way to access [[Prototype]] form script, but Firefox, Safari and Chrome all support a property on every object called __proto__; in other implementations, this property is completely hidden from script.

      Even though [[Prototype]] is not accessible in all implementations, the isPrototypeOf() method can be used to determine if this relationship exists between objects;

    1 alert(Person.prototype.isPrototypeOf(person1));          // true
    View Code

      ECMAScript 5 adds a new method called Object.getPrototypeOf(), which returned the value of [[Prototype]] in all supporting implementations. For example:

    1 alert(Object.getPrototypeOf(person1) == Person.prototype);              // true
    2 alert(Object.getPrototypeOf(person1).name);          // "Nicholas"
    View Code

      Although it's possible to read values on the prototype from object instances, it is not possible to overwrite them. If you add a property to an instance that has the same name as a property on the prototype, you create the property on the instance, which then masks the property on the prototype. 

      The delete operator completely removes the instance property and allows the prototype property to be access again as follows:

     1 function Person(){}
     2 
     3 Person.prototype.name = "Nicholas";
     4 Person.prototype.age = 29;
     5 Person.prototype.job = "Software Engineer";
     6 Person.prototype.sayName = function(){
     7     alert(this.name);
     8 };
     9 
    10 var person1 = new Person();
    11 var person2 = new Person();
    12 
    13 person1.name = "Greg";
    14 
    15 alert(person1.name);                  // "Greg"   - from instance
    16 alert(person2.name);                  // "Nicholas"  - from prototype
    17 
    18 delete person1.name;
    19 alert(person1.name);                  // "Nicholas"   - from the prototype
    View Code

      The hasOwnProperty() method determines if a property exists on the instance or on the prototype.

    Prototypes and the in Operator

      There are two ways to use the in operator: on its own or as a for-in loop;

      When used on its own, the in operator returns true when a property of the given name is accessible by the object, which is to say that the property may exist on the instance or on the prototype. 

      When using a for-in loop, all properties that are accessible by the object and can be enumerated will be returned, which includes properties both on the instance and on the property.

      To retrieve a list of all enumerable instance properties on an object, you can use the ECMAScript 5 Object.keys() method, which accepts an Object as its argument and returns an array of strings containing the names of all enumerable properties. For example:

     1 function Person(){}
     2 
     3 Person.prototype.name = "Nicholas";
     4 Person.prototype.age = 29;
     5 Person.prototype.job = "Software Engineer";
     6 Person.prototype.sayName = function(){
     7     alert(this.name);
     8 };
     9 
    10 var keys = Object.keys(Person.prototype);
    11 alert(keys);                   // "name, age, job, sayName"
    12 
    13 var p1 = new Person();
    14 p1.name = "Rob";
    15 p1.age = 31;
    16 var p1keys = Object.keys(p1);
    17 alert(p1keys);               // "name, age"
    View Code

      If you'd like a list of all instance properties, whether enumerable or not, you can use Object.getOwnPropertyNames() in the same way:

    1 var keys = Object.getOwnPropertyNames(Person.prototype);
    2 alert(keys);          // "constructor, name, age, job, sayName"
    View Code

    Alternate Prototype Syntax

      To limit this redundancy and to better visually encapsulate functionality on the prototype, it has become more common to simply overwrite the prototype with an object literal that contains all of the properties and methods, as in this example:

     1 function Person(){}
     2 
     3 Person.prototype = {
     4     constructor: Person,
     5     name: "Nicholas",
     6     age: 29,
     7     job: "Software Engineer",
     8     sayName: function(){
     9         alert(this.name);
    10     }
    11 };
    View Code

       Keep in mind that restoring the constructor in this manner creates a property with [[Enumerable]] set to true. Native constructor properties are not enumerable by default, so if you're using an ECMAScript 5-compliant JavaScript engine, you may wish to use Object.defineProperty() instead;

     1 function Person(){}
     2 
     3 Person.prototype = {
     4     name:  "Nicholas",
     5     age:  29,
     6     job:  "Software Engineer",
     7     sayName:  function(){
     8         alert(this.name);
     9     }
    10 };
    11 
    12 // ECMAScript 5 only - restore the constructor
    13 Object.defineProperty(Person.prototype, "constructor", {
    14     enumerable: false,
    15     value: Person
    16 });
    View Code

    Problems with Prototypes

      The prototype pattern isn't without its faults. For one, it negates the ability to pass initialization arguments into the constructor, meaning that all instances get the same property values by default. The main problem comes with their shared nature.

      All properties on the prototype are shared among instances, which is ideal for function. Properties that contain primitive values also tend to work well, as shown in the previous example, where it's possible to hide the prototype property by assigning a property of the same name to the instance. The real problem occurs when a property contains a reference value. Consider the following example:

     1 function Person(){}
     2 
     3 Person.prototype = {
     4     constructor:  Person,
     5     name:  "Nicholas",
     6     age:  29,
     7     friends:  ["Shelby", "Court"],
     8     sayName: function(){
     9         alert(this.name);
    10     }
    11 };
    12 
    13 var person1 = new Person();
    14 var person2 = new Person();
    15 
    16 person1.friends.push("Van");
    17 
    18 alert(person1.friends);             // "Shelby, Court, Van"
    19 alert(person2.friends);             // "Shelby, Court, Van"
    20 alert(person1.friends === person2.friends);                // true
    View Code
     1 function Person(){}
     2 
     3 Person.prototype = {
     4     constructor:  Person,
     5     name:  "Nicholas",
     6     age:  29,
     7     friends:  ["Shelby", "Court"],
     8     sayName: function(){
     9         alert(this.name);
    10     }
    11 };
    12 
    13 var person1 = new Person();
    14 var person2 = new Person();
    15 
    16 person1.friends = ["linxd"];
    17 
    18 alert(person1.friends);             // "linxd"
    19 alert(person2.friends);             // "Shelby, Court"
    20 alert(person1.friends === person2.friends);                // false
    View Code

      通过这两段代码的对比,我们可以知道,最开始的person1是不包含friends属性的。当执行person1.friends.push("Van")语句时,JavaScript引擎实际上先搜索friends属性,并且在Person.prototype中找到了该属性。需要注意的是,所有属性值是reference value(包括Array, Function, Object), 他们存储的都只是一个指向对象的指针,而不是对象的一个副本。因此,person1.friends和person2.friends实际上都查询到Person.prototype的同一个属性值。而通过person1.friends.push("Van")改变值后,在person2.friends中也能体现出来。

      而在第二段代码中,Person1声明了自己的实例属性friends,这个实例属性覆盖了继承的Person.prototype.friends属性。同时,需要明白的是,Person1.friends的属性值也是一个指向Array对象的指针,并不是Array对象的副本。

    Combination Constructor / Prototype Pattern

      The most common way of defining custom types is to combine the constructor and prototype patterns. The constructor pattern defines instance properties, whereas the prototype pattern defines methods and shared properties. This pattern allows arguments to be passed into the constructor as well, effectively combining the best parts of each pattern. The previous example can now be rewritten as follows:

     1 function Person(name, age, job){
     2     this.name = name;
     3     this.age = age;
     4     this.job = job;
     5     this.friends = ["Shelby", "Court"];
     6 }
     7 
     8 Person.prototype = {
     9     constructor:  Person,
    10     sayName:  function(){
    11         alert(this.name);
    12     }
    13 };
    14 
    15 var person1 = new Person("Nicholas", 29, "Software Engineer");
    16 var person2 = new Person("Greg", 27, "Doctor");
    17 
    18 person1.friends.push("Van");
    19 
    20 alert(person1.friends);                         // "Shelby, Court, Van"
    21 alert(person2.friends);                         // "Shelby, Court"
    22 alert(person1.friends === person2.friends);                     // false
    23 alert(person1.sayName === person2.sayName);              // true
    View Code
  • 相关阅读:
    ARCDesktop 学习笔记(一) 添加地图图层
    ARCGIS 开发常用技巧参考
    关于引用动软代码 找不到dll命名空间问题
    银行转账手续费
    Silverlight 笔记
    地图API
    (转)使用ImageBrush替换PictureMarkerSymbol以加强graphic显示性能
    WPF 网摘
    ArcCatalog添加GISserverIP问题
    ARCGIS 10 中文版 3D Analyst Tools等未授权问题
  • 原文地址:https://www.cnblogs.com/linxd/p/4488826.html
Copyright © 2020-2023  润新知