抱歉,您的浏览器无法访问本站

本页面需要浏览器支持(启用)JavaScript


了解详情 >

1. 原型

  每个函数都会创建一个prototype属性,这个属性是一个对象,包含应该由特定引用类型的实例共享的属性和方法。实际上这个对象就是通过调用构造函数创建的对象的原型。这么讲太绕了。简而言之,就是构造函数的prototype是一个对象,其他函数的prototype是一个指向其对应的构造函数的prototype对象的指针。同时,所有prototype对象都有一个constructor属性指向与之相关的构造函数。

  我的理解是这样:

  在自定义构造函数时,原型对象默认只会获得constructor属性,其他属性都继承自Object。脚本中没有访问实力的prototype的标准方式,但是chrome、firefox、safari都会在每个对象上暴露_proto_属性,通过这个属性可以访问对象的原型。实际上实例与构造函数原型有直接的关系,但是实例和构造函数是没有直接的关系的。

  用代码来理解:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
function Person(){}

console.log(Person.prototype);
/*
{
constructor: function Person(),
_proto_: Obkect
}
*/

console.log(Person.prototype.constructor === Person) //true

//正常的原型链都会终止于Object的原型对象
//Object的原型是null
console.log(Person.prototype._proto_ === Object.prototype)//true
console.log(Person.prototype._proto_.constructor === Object)//true
console.log(Person.prototype._proto_._proto_ === Object)//true


let person1 = new Person(), person2 = new Person();

//构造函数、原型对象和实例是三个完全不同的对象
console.log(person1 !== Person)//true
console.log(person1 !== Person.prototype)//true
console.log(Person.prototype !== Person)//true

/*
* 实例通过_proto_链接到原型对象
* _proto_其实就是实例的隐藏特性prototype
*/
console.log(person1._proto_ === Person.prototype)//true
console.log(person1._proto_.constructor === Person)//true

//同一个构造函数的两个实例共享同一个原型对象
console.log(person1._proto_ === person2._proto_)//true

//instanceof检查实例的原型链中是否包含指定构造函数的原型
console.log(person1 instanceof Person)//true
console.log(person1 instanceof Object)//true
console.log(Person.prototype instanceof Object)//true

  在通过对象访问属性时,会按照这个属性的名称开始搜索,搜索开始于对象实例本身。如果在这个实例上发现了给定的名称,则返回给定名称对应的值,如果没有找到这个属性,则会沿着指针进入原型对象,在原型对象中进行搜索。如果在原型对象中找到属性,则返回对应的值。这就是多个对象实例之间共享属性和方法的原理。

  虽然可以通过实例读取原型对象上的值,但是不能通过实例修改原型对象上的值。如果在实例上添加了一个与原型对象中同名的属性,那就会在实例上创建这个属性。这个属性会遮住原型对象上的属性。同时会遮蔽(shadow)对原型对象上同名属性的访问。把原型对象上的这个属性设置为null并不能恢复其与原型的联系,只有使用delete操作符才可以删除实例上的这个属性,从而让解析过程能够继续搜索原型对象。

  代码示例如下:

1
2
person1.name="Luna";
delete person1.name;

  通过hasOwnProperty()方法可以确定某个属性是在实例上还是在原型对象上。

1
2
3
4
person1.name="Luna";
console.log(person1.hasOwnProperty("name"))//true
delete person1.name;
console.log(person1.hasOwnProperty("name"))//false

  in可以判断一个对象是否有这个属性,无论这个属性是在实例上还是在原型上。

1
console.log("name" in person1)//true

  原型最主要的问题在于其共享特性,一般来说,不同的实例应该有属于自己的属性副本。

2. 原型链

  原型链被定义为ECMAScript的主要继承方式。其基本思想就是通过原型继承多个引用类型的属性和方法。当访问一个对象的属性时,如果这个对象内部不存在这个属性,那么它就会去它的原型对象里找这个属性,这个原型对象又会有自己的原型,于是就这样一直找下去,也就是原型链的概念。

  继承意味着复制操作,然而 JavaScript 默认并不会复制对象的属性,相反,JavaScript 只是在两个对象之间创建一个关联,这样,一个对象就可以通过委托访问另一个对象的属性和函数,所以与其叫继承,委托的说法反而更准确些。

评论