JS中的原型与原型链

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方法直接添加到Personprototype属性中,构造函数为空,此时经过构造函数生成的person1person2两个实例对象,都可共享Person.prototype的属性与方法。this

理解原型对象

不管何时,只要建立了一个新函数,就会根据一组特定的规则为该函数建立一个prototype属性,这个属性指向函数的原型对象。在默认状况下,全部原型对象都会自动得到一个constructor(构造)属性,这个属性是一个指向prototype属性所在函数的指针。拿上面的代码示例来讲,Person.prototype.constructor指向Person,经过Person.prototype可继续为原型对象添加方法与属性。spa

建立了自定义的构造函数后,其原型对象默认只会取得constructor属性,至于其余方法,都是从Object继承而来的。当调用构造函数建立一个新实例后,该实例的内部将包含一个指针(内部属性[[Prototype]]),指向构造函数的原型对象。虽然在脚本中没有标准方式访问[[Prototype]],但每一个对象上支持一个属性__proto__,可经过该属性访问原型对象。prototype

须要注意的是,__proto__这个链接存在于实例对象与原型对象之间,而不是存在于实例于构造函数之间。3d

之前面使用Person构造函数建立实例对象的代码为例,下图展现了PersonPerson1Person2及原型对象之间的关系。 指针

image

原型关系判断

对于判断对象之间是否存在原型关系,有如下三种方式实现。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.

组合使用hasOwnPropertyin操做符可正确判断属性是存在于实例中仍是原型对象中。

原型对象的问题

  1. 省略了为构造函数传递初始化参数这一环节,致使全部实例默认状况下都将取得相同的属性值。
  2. 原型中的属性被不少实例共享,这对于函数来讲很是合适,对于那些包含基本值得属性来讲也能够,但对于引用类型来讲,问题就比较突出了。
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上。

相关文章
相关标签/搜索