
所有原型对象都会自动获得一个
constructor (构造函数)属性,这个属性包含一个指向 prototype
属性所在函数的指针。就拿前面的例子来说, Person.prototype.constructor 指向
Person
。而通过这个构造函数,我们还可继续为原型对象添加其他属性和方法。

展示了 Person 构造函数、Person 的原型属性以及 Person 现有的两个实例之间的关系。
在此,
Person.prototype
指向了原型对象,而
Person.prototype.constructor
又指回了
Person
。
原型对象中除了包含
constructor
属性之外,还包括后来添加的其他属性。
原型与 in 操作符
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" ——来自实例
alert(person1.hasOwnProperty("name")); //true
alert("name" in person1); //true
alert(person2.name); //"Nicholas" ——来自原型
alert(person2.hasOwnProperty("name")); //false
alert("name" in person2); //true
delete person1.name;
alert(person1.name); //"Nicholas" ——来自原型
alert(person1.hasOwnProperty("name")); //false
alert("name" in person1); //true
因此,调用
"name" in person1
始终都返回
true
,无论该属性存在于实例中还是存在于原型中。 同时使用 hasOwnProperty()
方法和
in
操作符,就可以确定该属性到底是存在于对象中,还是存在于原型中,
function hasPrototypeProperty(object, name){
return !object.hasOwnProperty(name) && (name in object);
}
要取得对象上所有可枚举的实例属性,可以使用
ECMAScript 5
的
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"
var p1 = new Person();
p1.name = "Rob";
p1.age = 31;
var p1keys = Object.keys(p1);
alert(p1keys); //"name,age"
更简单的原型语法
function Person(){
}
Person.prototype = {
name : "Nicholas",
age : 29,
job: "Software Engineer",
sayName : function () {
alert(this.name);
}
};
var friend = new Person();
alert(friend instanceof Object); //true
alert(friend instanceof Person); //true
alert(friend.constructor == Person); //false
alert(friend.constructor == Object); //true
用
instanceof
操作符测试
Object
和
Person仍然返回 true
,但
constructor
属性则等于 Object
而不等于
Person
了。如果
constructor
的值真的很重要,可以像下面这样特意将它设置回适当的值。
function Person(){
}
Person.prototype = {
constructor : Person,
name : "Nicholas",
age : 29,
job: "Software Engineer",
sayName : function () {
alert(this.name);
}
};
原型的动态性
var friend = new Person();
Person.prototype.sayHi = function(){
alert("hi");
};
friend.sayHi(); //"hi"(没有问题!)
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
调用构造函数时会为实例添加一个指向最初原型的[[Prototype]]指针,而把原型修改为另外一个对象就等于切断了构造函数与最初原型之间的联系。

原生对象的原型
所有原生引用类型(
Object
、
Array
、
String
,等等)都在其构造函数的原型上定义了方法。
在
Array.prototype
中可以找到
sort()
方法,而在
String.prototype
中可以找到substring()方法,如下所示。
alert(typeof Array.prototype.sort); //"function"
alert(typeof String.prototype.substring); //"function"
通过原生对象的原型,不仅可以取得所有默认方法的引用,而且也可以定义新方法。
下面的代码就给基本包装类型String 添加了一个名为
startsWith()
的方法。
String.prototype.startsWith = function (text) {
return this.indexOf(text) == 0;
};
var msg = "Hello world!";
alert(msg.startsWith("Hello")); //true
原型对象的问题
原型模式的最大问题是由其共享的本性所导致的。
person2.friends(与 person1.friends 指向同一个数组)反映出来, 此时发现person2.friends也被添加了“Van”值。这样会被污染风险
function Person(){
}
Person.prototype = {
constructor: Person,
name : "Nicholas",
age : 29,
job : "Software Engineer",
friends : ["Shelby", "Court"],
sayName : function () {
alert(this.name);
}
};
var person1 = new Person();
var person2 = new Person();
person1.friends.push("Van");
alert(person1.friends); //"Shelby,Court,Van"
alert(person2.friends); //"Shelby,Court,Van"
alert(person1.friends === person2.friends); //true