咱们建立的每一个函数都有一个 prototype (原型)属性,这个属性是一个指针,指向一个原型对象,而这个原型对象中拥有的属性和方法能够被因此实例共享。函数
function Person(){ } Person.prototype.name = "Nicholas"; Person.prototype.age = 29; 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
1、理解原型对象测试
不管何时,只要建立了一个新函数,就会根据一组特定的规则为该函数建立一个 prototype属性,这个属性指向函数的原型对象。this
在默认状况下,全部原型对象都会自动得到一个 constructor(构造函数)属性,这个属性包含一个指向 prototype 属性所在函数的指针。spa
当调用构造函数建立一个新实例后,该实例的内部将包含一个指针(内部属性),指向构造函数的原型对象。ECMA-262 第 5 版中管这个指针叫 [[Prototype]] 。prototype
虽然在脚本中没有标准的方式访问 [[Prototype]] ,但 Firefox、Safari 和 Chrome 在每一个对象上都支持一个属性__proto__ ;而在其余实现中,这个属性对脚本则是彻底不可见的。指针
不过,要明确的真正重要的一点就是,这个链接存在于实例与构造函数的原型对象之间,而不是存在于实例与构造函数之间。code
之前面使用 Person 构造函数和 Person.prototype 建立实例的代码为例,图 6-1 展现了各个对象之间的关系。对象
在此, Person.prototype 指向了原型对象,而 Person.prototype.constructor 又指回了 Person 。blog
person1 和 person2 都包含一个内部属性,该属性仅仅指向了 Person.prototype ;换句话说,它们与构造函数没有直接的关系。继承
能够调用 person1.sayName() 。这是经过查找对象属性的过程来实现的。(会先在实例上搜索,若是搜索不到就会继续搜索原型。)
用isPrototypeOf()方法判断实例与原型对象之间的关系
alert(Person.prototype.isPrototypeOf(person1)); //true alert(Person.prototype.isPrototypeOf(person2)); //true
用Object.getPrototypeOf() 方法返回实例的原型对象
alert(Object.getPrototypeOf(person1) == Person.prototype); //true
使用 hasOwnProperty() 方法能够检测一个属性是存在于实例中,仍是存在于原型中。
alert(person1.hasOwnProperty("name")); //false 来着原型
person1.name = "Greg";
alert(person1.name); //"Greg"——来自实例
alert(person1.hasOwnProperty("name")); //true
2、更简单的原型语法
前面例子中每添加一个属性和方法就要敲一遍 Person.prototype 。为减小没必要要的输入,也为了从视觉上更好地封装原型的功能,更常见的作法是用一个包含全部属性和方法的对象字面量来重写整个原型对象。
function Person(){ } Person.prototype = { name : "Nicholas", age : 29, job: "Software Engineer", sayName : function () { alert(this.name); } };
在上面的代码中,咱们将 Person.prototype 设置为等于一个以对象字面量形式建立的新对象。最终结果相同,但有一个例外: constructor 属性再也不指向 Person 了。
前面曾经介绍过,每建立一个函数,就会同时建立它的 prototype 对象,这个对象也会自动得到 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
在此,用 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); } };
3、原生对象的原型
全部原生引用类型( Object 、 Array 、 String ,等等)都在其构造函数的原型上定义了方法。
例如,在 Array.prototype 中能够找到 sort() 方法,而在 String.prototype 中能够找到substring() 方法。尽管能够这样作,但不推荐修改原生对象的原型。
4、原型对象的问题
原型模式的最大问题是由其共享的本性所致使的。 修改其中的一个,另外一个也会受影响。
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
5、原型链
其基本思想是利用原型让一个引用类型继承另外一个引用类型的属性和方法。而后层层递进,就构成了实例与原型的链条,这就是所谓原型链的基本概念。
function SuperType(){ this.property = true; } SuperType.prototype.getSuperValue = function(){ return this.property; }; function SubType(){ this.subproperty = false; } //继承了 SuperType SubType.prototype = new SuperType(); SubType.prototype.getSubValue = function (){ return this.subproperty; }; var instance = new SubType(); alert(instance.getSuperValue()); //true
一张图说明:
property 则位于 SubType.prototype 中。这是由于 property 是一个实例属性,而 getSuperValue() 则是一个原型方法。既然 SubType.prototype 如今是 SuperType的实例,那么 property 固然就位于该实例中了。