JS中的原型能够类比于Java中的父类。javascript
在Java中实现继承有接口继承与实现继承两种方式,接口继承只继承方法签名,而实现继承则继承实际的方法。java
因为JS中的函数没有签名,在ECMAScript
没法实现接口继承,ECMAScript
只支持实现继承,而其实现继承的主要依靠原型与原型链来实现的。函数
咱们建立的每一个函数都有一个prototype(原型)
属性,这个属性是一个指针,指向一个对象(原型对象),而这个对象的用途是包含能够由特定类型的全部实例共享的属性和方法。以下面例子所示:ui
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
复制代码
在上面代码中,咱们将sayName
方法直接添加到Person
的prototype
属性中,构造函数为空,此时经过构造函数生成的person1
与person2
两个实例对象,都可共享Person.prototype
的属性与方法。this
不管何时,只要建立了一个新函数,就会根据一组特定的规则为该函数建立一个prototype
属性,这个属性指向函数的原型对象。在默认状况下,全部原型对象都会自动得到一个constructor(构造)
属性,这个属性是一个指向prototype
属性所在函数的指针。拿上面的代码示例来讲,Person.prototype.constructor
指向Person
,经过Person.prototype
可继续为原型对象添加方法与属性。spa
建立了自定义的构造函数后,其原型对象默认只会取得constructor
属性,至于其余方法,都是从Object
继承而来的。当调用构造函数建立一个新实例后,该实例的内部将包含一个指针(内部属性[[Prototype]]
),指向构造函数的原型对象。虽然在脚本中没有标准方式访问[[Prototype]]
,但每一个对象上支持一个属性__proto__
,可经过该属性访问原型对象。prototype
须要注意的是,
__proto__
这个链接存在于实例对象与原型对象之间,而不是存在于实例于构造函数之间。3d
之前面使用Person
构造函数建立实例对象的代码为例,下图展现了Person
与Person1
、Person2
及原型对象之间的关系。 指针
对于判断对象之间是否存在原型关系,有如下三种方式实现。code
__proto__
alert(person1.__proto__ == Person.prototype) //true
alert(person2.__proto__ == Person.prototype) //true
复制代码
因为
person1.__proto__
与person2.__proto__
都指向原型对象,而Person.prototype
也指向原型对象,因此返回值都为true
isPrototypeOf()
alert(Person.prototype.isPrototypeOf(person1)); //true
alert(Person.prototype.isPrototypeOf(person2)); //true
复制代码
A.isPrototypeOf(B)
,判断A
是不是B
的原型对象。对于person1.__proto__.isPrototypeOf(person1)
的返回值,也是为true,由于person1.__proto__
等于Person.prototype
。
getPrototypeOf()
alert(Object.getPrototypeOf(person1) == Person.prototype); //true
alert(Object.getPrototypeOf(person1).name); //"Nicholas"
复制代码
Object.getPrototypeOf(person1)
返回person1
的原型对象。
每当代码读取某个对象的属性时,都会执行一次搜索,目标是给定名字的属性。搜索首先从实例对象自己开始,若是对象自己存在该属性,则直接返回该属性的值,若是没有找到,则继续搜索该对象的原型对象,如有就返回属性值,若是都没找到,则会返回undefined
前面提到过,原型对象最初只包含
constructor
属性,而该属性也是共享的,所以能够经过实例对象访问
alert(person2.constructor) //function Person(){}
复制代码
调用person2.constructor
时返回function Person(){}
,证实了原型对象的constructor
属性确实指向构造函数。
虽然能够经过实例访问保存在原型中的值,但却不能经过实例对象重写原型中的值。若是咱们在实例中添加了一个属性,而该属性与实例原型中的一个属性同名,那咱们就在实例中建立该属性,该属性会屏蔽原型中的那个属性,以下所示。
person1.name = "Greg";
alert(person1.name); //"Greg" ——来自实例
alert(person2.name); //"Nicholas" ——来自原型对象
复制代码
当为对象添加一个属性时,这个属性就会屏蔽原型对象中保存的同名属性,虽然原型对象中的同名属性依旧存在;想要取消屏蔽,可使用delete
操做符彻底删除实例对象中的属性,而后才能访问原型中的属性,以下所示。
person1.name = "Greg";
alert(person1.name); //"Greg" ——来自实例
alert(person2.name); //"Nicholas" ——来自原型对象
delete person1.name;
alert(person1.name); //"Nicholas" ——来自原型对象
复制代码
hasOwnProperty
此外,使用hasOwnProperty()
方法能够检测一个属性是存在于实例中(该方法也是从Object
中继承而来),只有在给定属性存在于实例对象中时,才会返回true
。
alert(person1.hasOwnProperty("name")); //false
person1.name = "Greg";
alert(person1.name); //"Greg"——来自实例
alert(person1.hasOwnProperty("name")); //true
alert(person2.name); //"Nicholas"——来自原型
alert(person2.hasOwnProperty("name")); //false
delete person1.name;
alert(person1.name); //"Nicholas"——来自原型
alert(person1.hasOwnProperty("name")); //false
复制代码
in
操做符此外,使用in
操做符能够检测一个属性是存在于实例或其原型对象中,存在返回true
。
alert(person1.hasOwnProperty("name")); //false
alert("name" in person1); //true
复制代码
对于属性name
,它存在于person1
的原型对象中,但不存在于person1
实例中,因此调用hasOwnProperty
方法返回false
,调用in
操做符返回true
.
组合使用
hasOwnProperty
与in
操做符可正确判断属性是存在于实例中仍是原型对象中。
function Person(){
}
Person.prototype = {
constructor: Person,
friends : ["Shelby", "Court"],
};
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
复制代码
person1
对属性friends
的任何操做,都会立马反应到person2
上。