javascript中原型模式创建对象特点分析

在javascript中,我们创建的每个函数都有一个prototype(原型)属性。这个属性是一个指针,指向一个对象,而这个对象的用途是包含可以由特定类型的所有实例共享的属性和方法。使用原型对象的好处是可以让所有对象实例共享它所包含的属性和方法。

function Person(){}
Person.prototype.name = "Dawang";
Person.prototype.age = 22;
Person.prototype.job = "Software Engineer";
Person.prototype.sayName = function(){
    alert(this.name);
};

var person1 = new Person();
person1.sayName();//"Dawang"

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

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

在此,我们将sayName()方法和所有属性直接添加到Person的prototype属性中,构造函数变成了空函数。即使如此,也仍然可以通过构造函数来创建新对象,而且新对象还会具有相同的属性和方法。但是与构造函数不同的是,新对象的属性和方法是由所有实例共享的。
无论什么时候,只要我们创建了一个新函数,就会根据一组特定的规则为该函数创建一个prototype属性,这个属性指向函数的原型对象。在默认情况下,所有原型对象都会自动获得一个constructor(构造函数)属性,这个属性包含一个指向prototype属性所在函数的指针。以上面的代码来说,Person.prototype.constructor指向Person。通过这个构造函数,我们可以继续为原型对象添加其他属性和方法。
当我们创建一个属性,该属性与实例原型中的一个属性同名,该属性就会屏蔽原型中的那个属性。我们创建的这个属性,只会阻止我们访问原型中的那个属性,但不会修改那个属性。通过使用delete操作符可以完全删除我们创建的实力属性。通过使用hasOwnProperty()方法可以检测一个属性是存在于实例中,还是存在于原型中。这个方法是从Object继承来的。只在给定属性存在于对象实例中时,才会返回true。请看下面的代码示例:

function Person(){}
Person.prototype.name = "Dawang";
Person.prototype.age = 22;
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

person1.name = "Xiaosheng";
alert(person1.name);//"Xiaosheng"——来自实例
alert(person1.hasOwnProperty("name"));//true

alert(person2.name);//"Dawang"——来自原型
alert(person2.hasOwnProperty("name"));//false

delete person1.name;
alert(person1.name);//"Dawang"——来自原型
alert(person1.hasOwnProperty("name"));//false

读者可能注意到上面的例子中,每添加一个属性和防范就要敲一遍Person.prototype。为了减少不必要的输入,通常采用更简单的写法,用一个包含所有属性和方法的对象字面量来重写整个原型对象:

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

上述代码虽然简单了,但是constructor属性不再指向Person了,因为这种写法重写了默认的的prototype对象,因此constructor属性也就变成了新对象的constructor属性(指向Object构造函数),使用instanceof操作符能够返回正确的结果,但是constructor已经无法确定对象的类型了。如下所示:

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属性等于Object不等于Person,如果constructor的值很重要,可以将上述代码中注释那行去掉注释。
原型模式创建对象的缺点:
它省略了为构造函数传递初始化参数这一环节,结果所有实例在默认情况先都将取得相同的属性值。原型中所有属性是被很多实例共享的,这种共享对于函数来说非常合适,但是,对于包含引用类型值的属性来说,问题就比较突出。请看示例:

function Person(){}
Person.prototype = {
    constructor:Person,
    name: "Dawang",
    age: 22,
    job: "Software Engineer",
    friends: ["Hongtao","King"],
    sayName: function(){
        alert(this.name);
    }
};

var person1 = new Person();
var person2 = new Person();

person1.friends.push("Xiaowang");

alert(person1.friends);//"Hongtao,King,Xiaowang"
alert(person2.friends);//"Hongtao,King,Xiaowang"
alert(person1.friends === person2.friends);//true

从代码中可以看出,我们向person1.friends添加了一个Xiaowang,但是,person2共享了这个属性,如果我们的本意就是如此,没什么问题,但是通常实例是要有属于自己的属性的。正因为这个原因,原型模式很少有人单独使用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值