一、原型是什么?
男孩子应该不少人都玩过经典单机游戏《虐杀原型》,说实话,也是这款游戏让我对单机游戏情有独钟,游戏的剧情设定,人物刻画以及游戏本身的精彩,哈哈,意犹未尽。
转入正题,游戏的大致剧情,主角Alex(好像是这个名字)是一名退伍军人,后来被军方某神秘生化实验室抓去,做了一些特殊的二次改造,后来拥有超凡能力,后来家人被杀。Alex便开始追杀凶手,凶手正是Alex的“原型”,一个 big boss,是这个家伙给了Alex身上的许多改造和特性,所以Alex才拥有如此巨大的能力。
JavaScript的原型也是一样的,比如我们在创建一个对象,如函数(函数也是对象)时,js就会在内部为该对象创建一个原型属性,原型属性指向函数的原型,即原型对象。这个原型对象与这个函数(下文统称源对象)的关系可以理解为Alex之与Big Boss。
原型对象在默认情况下,会自动获得一个constructor属性,这个属性里边藏有一个指向自定义函数的指针,比如
function Person() {};
Person.prototype = {
name:"zhangsan",
age:21,
job:"nurse",
say:function () {
alert(this.name);
}
}
Person.prototype.constructor === Person 这个构造函数。如果将该构造函数实例化之后,如:var zhangsan = new Person();
这个zhangsan实例内部也会包含一个指向构造函数的原型对象的指针–[[ Prototype]] ,样子很奇怪,因为js脚本无法访问,chrome和firefox ,Safari 里的控制台里经常看见一个proto 就是指的是实例对象和原型对象之间的链接–[[Prototype]]
为了理解prototype 、constructor、实例、原型、构造函数之间的关系,自己画的示意图如下:
二、原型与in操作符
使用原型模式创建对象的时候,往往原型上有很多的属性。而在实例化对象之后,实例对象也可以添加属性。众多的属性究竟哪些属性是原型的,哪些是属于实例的。因此in操作符主要用来判断属性的宿主和迭代属性。
1. in操作符:不管是属性是属于原型还是实例,只要能访问到,就返回true,否则返回false;
如:
function Person () {};
Person.prototype = {
constructor:Person;
name:'zhangsan',
age:21,
job:'teacher'
}
var zhangsan = new Person();
alert('name' in zhangsan) //true
zhangsan.name = 'vin',
alert('name' in zhangsan) //同样返回true,即使是实例添加的属性
in 操作符与hasOwnProperty() 函数搭配可判断属性是否是原型属性:
function hasProtoPro(obj,prop) {
return !obj.hasOwnProperty(prop) && prop in obj;
}
2 for ( var prop in obj) 循环对象的可访问的,而且是可枚举的属性。同样,不管是原型属性还是实例属性。
赠送两个函数:
Object.keys() 获取对象所有可枚举的属性名,返回字符串数组;
Object.getOwnPropertyNames() 获取对象所有实例属性,不论是否可枚举;
三、原型的动态性
理解:一句话,对原型对象的属性和方法的所有修改,都可以立刻反映在实例对象上。
言外之意,如果重写原型对象,实例和新原型对象没有任何“血缘关系”,好比,儿子和继父,虽然伦理上也是父亲,但实质上已经“重写”了父亲,然而,儿子和亲父的血缘关系依旧,反映在原型对象的重写问题上,则是实例对象和旧原型对象依然彼此连接,引用的依然是最初的原型。
如:
function Person() {};
var lisi = new Person();
//重写原型对象
Person.prototype = {
name:"zhangsan",
age:21,
job:"nurse",
say:function () {
alert(this.name);
}
}
lisi.say() //fatal error
四、原型模式创建对象的问题
一句话,正是其把所有属性和方法都放在原型对象上,这样换来的“共享精神”出现了引用类型的属性出现引用悖论。
代码奉上:
function Person() {};
Person.prototype = {
name:"zhangsan",
age:21,
wife:['w1','w2','w3']
}
var zhangsan = new Person();
zhangsan.wife.push('w4');
alert(zhangsan.wife) //['w1','w2','w3','w4']
var lisi = new Person();
alert(lisi.wife) //['w1','w2','w3','w4']
zhangsan 纳入的小妾w4 ,同时也变成了lisi的小妾。乱套了。