visa丶

导航

创建对象----原型模式和构造函数

创建对象

工厂模式

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"

  • 工厂模式虽然解决了创建多个相似对象的问题,但是没有解决对象识别的问题(即如何知道对象的类型)

构造函数模式

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

  • 构造函数始终都应该以一个大写字母开头,要创建新的实例,必须使用new操作符。

  • 每一个实例都有一个constructor(构造函数)属性,该属性指向实例的构造函数。

    alert(person1.constructor == Person); //true
    alert(person2.constructor == Person); //true

  • 构造函数与其他函数唯一的不同就是调用方式不同,任何函数只要用new操作符来调用,那它就可以作为构造函数

  • 构造函数的问题:每个方法在每个实例上都要重新创建一遍,不同实例上的同名方法其实是互相不相等的


原型模式

  • 每个函数都有一个prototype(原型)属性,这个属性是一个指针,指向一个对象,这个对象的作用是包含可以由特定类型的所有实例共享的属性和方法。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();
    person1.sayName(); //"Nicholas"

    var person2 = new Person();
    person2.sayName(); //"Nicholas"

    alert(person1.sayName == person2.sayName); //true

  • 新对象的这些属性和方法是所有实例共享的

理解原型对象

  • 无论什么时候,只要创建了一个新函数,就会根据一组特定的规则为该函数创建一个prototype属性,这个属性指向函数的原型对象。

  • 默认情况下,所有原型对象会自动获得一个constructor(构造函数)属性,这个属性是一个指向prototype属性所在函数的指针。就上面的例子而言,Person.prototype.constructor指向Person。

  • 创建了自定义构造函数之后,其原型对象只会获得constructor属性,至于其他方法,则全是从Object继承来的,当调用构造函数创建一个新实例之后,该实例的内部将会包含一个指针(内部属性),指向构造函数的原型对象。这个属性叫做[[prototype]],也就是__proto__。注意:这个连接存在于实例和构造函数的原型对象之间,和构造函数没有直接关系。

  • 虽然所有实现中都无法访问到[[prototype]],但是可以通过isPrototypeOf()方法来确定对象之间是否是否存在这种关系

    alert(Person.prototype.isPrototypeOf(person1)); //true
    alert(Person.prototype.isPrototypeOf(person2)); //true

  • Object.getPrototypeOf() 取得一个对象的原型

    //only works if Object.getPrototypeOf() is available
    if (Object.getPrototypeOf){
    alert(Object.getPrototypeOf(person1) == Person.prototype); //true
    alert(Object.getPrototypeOf(person1).name); //"Nicholas"
    }

  • 原型最初只包含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();
    var person2 = new Person();

    person1.name = "Greg";
    alert(person1.name); //"Greg" from instance
    alert(person2.name); //"Nicholas" from prototype

  • 通过delete操作符可以完全删除实例属性,从而让我们能重新访问原型中的属性

    delete person1.name;
    alert(person1.name); //"Nicholas" - from the prototype

  • hasOwnProperty() 方法可以检测一个属性是存在于实例中,还是原型中,只有给定属性在对象实例中,才会返回true。

原型与in操作符

  • in操作符有两种使用方式,一种是在for-in循环中,一种是单独使用。单独使用时,in操作符会在通过对象能够访问访问给定属性时返回true,无论该属性在实例中还是原型中。

    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

  • 在for-in循环时,返回的是所有能够通过对象访问的,可枚举的属性。

  • Object.keys() 接受一个对象作为参数,返回一个包含所有可枚举属性的字符串数组。

    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() 获得所有实例属性,无论是否可枚举

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

更简单的原型语法

function Person(){
}

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

  • 如果将Person.prototype设置为等于一个以对象字面量形式创建的新对象,那么constructor将不再指向Person。因为这样本质上完全重写了默认的prototype对象,因此constructor属性也就变成了新对象的constructor属性(指向Object)构造函数,不再指向Person函数。

    var friend = new Person();

    alert(friend instanceof Object); //true
    alert(friend instanceof Person); //true
    alert(friend.constructor == Person); //false
    alert(friend.constructor == Object); //true

  • 可以把constructor重新设置回适当的值。

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

  • 以这种方式重设constructor属性会导致它的[[Enumerable]]特性被设置为true。默认情况下,原生的constructor属性是不可枚举的。

原型的动态性

  • 由于在原型中查找值得过程是一次搜索,因此我们对原型对象做的任何修改都能立即从实例上反映出来————即使是先创建实例后修改原型。

  • 实例和原型之间的联系是一个指针,而不是一个副本。

  • 调用构造函数时,会为实例添加一个指向最初原型的[[prototype]]指针。如果重写整个原型对象,会切断构造函数与最初原型之间的联系。实例中的指针仅指向原型,并非构造函数!

    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

  • 上面报错的原因是重写原型对象切断了现有原型和已经存在的对象实例的联系。实例引用的仍是原来的原型。


组合使用构造函数和原型

  • 创建自定义类型的最常见模式就是组合使用构造函数和原型,构造函数用于定义实例属性,原型用于定义方法和共享的属性。这样,每个实例都会有自己的一份实例属性的副本,但同时也共享着对方法的引用,最大限度地节省了内存。

    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

posted on 2017-08-11 15:27  visa丶  阅读(290)  评论(0)    收藏  举报