Create Objects - Prototype Patterns and Constructors

create object

Factory Mode

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("Nicholas", 29, "Software Engineer");
var person2 = createPerson("Greg", 27, "Doctor");

person1.sayName();   //"Nicholas"
person2.sayName();   //"Greg"
  • Although factory mode solves the problem of creating multiple similar objects, it does not solve the problem of object recognition (that is, how to know the type of object)

Constructor Pattern

function Person(name, age, job){
    this.name = name;
    this.age = age;
    this.job = job;
    this.sayName = function(){
        alert(this.name);
    };    
}

var person1 = new Person("Nicholas", 29, "Software Engineer");
var person2 = new Person("Greg", 27, "Doctor");

person1.sayName();   //"Nicholas"
person2.sayName();   //"Greg"

alert(person1 instanceof Object);  //true
alert(person1 instanceof Person);  //true
alert(person2 instanceof Object);  //true
alert(person2 instanceof Person);  //true

alert(person1.sayName == person2.sayName);  //false 
  • Constructors should always start with a capital letter, and the new operator must be used to create new instances.

  • Each instance has a constructor property that points to the constructor of the instance.

    alert(person1.constructor == Person);  //true
    alert(person2.constructor == Person);  //true
  • The only difference between constructors and other functions is that they are called differently. Any function can be used as a constructor if it is called with the new operator

  • Problem with constructors: Each method is recreated on each instance, and methods with the same name on different instances are not equal to each other

Prototype mode

  • Each function has a prototype property, which is a pointer to an object whose purpose is to contain properties and methods that can be shared by all instances of a particular type.Prototype is the prototype object of the instance of that object created by calling the constructor.

    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();
    person1.sayName();   //"Nicholas"
    
    var person2 = new Person();
    person2.sayName();   //"Nicholas"
    
    alert(person1.sayName == person2.sayName);  //true
  • These properties and methods of the new object are shared by all instances

Understanding prototype objects

  • Whenever a new function is created, a prototype property is created for the function based on a specific set of rules that point to the prototype object of the function.

  • By default, all prototype objects automatically get a constructor property, which is a pointer to the function in which the prototype property resides.For the example above, Person.prototype.constructor points to Person.

  • Once a custom constructor is created, its prototype object only gets the constructor property. For the other methods, it is all inherited from Object. When a constructor is called to create a new instance, the inside of the instance will contain a pointer (internal property) pointing to the prototype object of the constructor.This property is called [[prototype]], or u proto_u.Note: This connection exists between the instance and the prototype object of the constructor and is not directly related to the constructor.

  • [[prototype]] is not accessible in all implementations, but can the isPrototypeOf() method be used to determine whether this relationship exists between objects?

    alert(Person.prototype.isPrototypeOf(person1));  //true
    alert(Person.prototype.isPrototypeOf(person2));  //true
  • Object.getPrototypeOf() takes a prototype of an object

    //only works if Object.getPrototypeOf() is available
    if (Object.getPrototypeOf){
        alert(Object.getPrototypeOf(person1) == Person.prototype);  //true
        alert(Object.getPrototypeOf(person1).name);  //"Nicholas"
    }
  • The prototype initially contained only the constructor property, which is also shared and therefore accessible through an object instance

  • Although values stored in the prototype can be accessed through object instances, values in the prototype cannot be overridden through object instances.

    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();
    
    person1.name = "Greg";
    alert(person1.name);   //"Greg" from instance
    alert(person2.name);   //"Nicholas" from prototype
  • The delete operator allows us to completely delete instance properties, allowing us to re-access properties in the prototype

    delete person1.name;
    alert(person1.name);   //"Nicholas" - from the prototype
  • The hasOwnProperty() method detects whether an attribute exists in an instance or in a prototype and returns true only if a given attribute is in an object instance.

Prototype and in operator

  • The in operator is used in two ways, one in a for-in loop and the other in isolation.When used alone, the in operator returns true when a given property is accessible through an object, whether in an instance or in a prototype.

    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"));  //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 person2);  //true
    
    delete person1.name;
    alert(person1.name);   //"Nicholas" - from the prototype
    alert(person1.hasOwnProperty("name"));  //false
    alert("name" in person1);  //true
  • In a for-in loop, all enumerable properties that can be accessed through the object are returned.

  • Object.keys() takes an object as a parameter and returns an array of strings containing all enumerable properties.

    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"
  • Object.getOwnPropertyNames() gets all instance properties, whether or not enumerable

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

Simpler prototype syntax

function Person(){
}

Person.prototype = {
    name : "Nicholas",
    age : 29,
    job: "Software Engineer",
    sayName : function () {
        alert(this.name);
    }
};
  • If Person.prototype is set to equal a new object created as an object literal, the constructor will no longer point to Person.Because this sample essentially completely overrides the default prototype object, the constructor property becomes the constructor property (pointing to Object) constructor for the new object, no longer pointing to the Person function.

    var friend = new Person();
    
    alert(friend instanceof Object);  //true
    alert(friend instanceof Person);  //true
    alert(friend.constructor == Person);  //false
    alert(friend.constructor == Object);  //true
  • You can reset the constructor back to the appropriate value.

    function Person(){
    }
    Person.prototype = {
    constructor: Person,
    name : "Nicholas",
    age : 29,
    job: "Software Engineer",
    sayName : function () {
        alert(this.name);
    }
    };
  • Resetting the constructor property in this way will cause its [[Enumerable]] property to be set to true.By default, the native constructor attribute is not enumerable.

Dynamics of prototypes

  • Since the process of finding value in a prototype is a search, any modifications we make to the prototype object are immediately reflected in the instance -- even if we create the instance first and then modify the prototype.

  • The connection between an instance and a prototype is a pointer, not a copy.

  • When the constructor is called, a [[prototype]] pointer to the original prototype is added to the instance.If you override the entire prototype object, you break the connection between the constructor and the original prototype.Pointer in instance only points to prototype, not constructor!

    function Person(){
    }
    
    var friend = new Person();
    
    Person.prototype = {
        constructor: Person,
        name : "Nicholas",
        age : 29,
        job : "Software Engineer",
        sayName : function () {
            alert(this.name);
        }
    };
    
    friend.sayName();   //error
  • The reason for the above error is that rewriting the prototype object disconnects the existing prototype from the existing object instance.The instance still references the original prototype.

Combining constructors with prototypes

  • The most common pattern for creating custom types is to combine constructors with prototypes, which define instance properties, and prototypes which define methods and shared properties.In this way, each instance will have its own copy of the instance properties, but it will also share a reference to the method, minimizing memory savings.

    function Person(name, age, job){
        this.name = name;
        this.age = age;
        this.job = job;
        this.friends = ["Shelby", "Court"];
    }
    
    Person.prototype = {
        constructor: Person,
        sayName : function () {
            alert(this.name);
        }
    };
    
    var person1 = new Person("Nicholas", 29, "Software Engineer");
    var person2 = new Person("Greg", 27, "Doctor");
    
    person1.friends.push("Van");
    
    alert(person1.friends);    //"Shelby,Court,Van"
    alert(person2.friends);    //"Shelby,Court"
    alert(person1.friends === person2.friends);  //false
    alert(person1.sayName === person2.sayName);  //true

Keywords: Javascript Attribute

Added by spighita on Wed, 05 Jun 2019 19:06:43 +0300