一、new操作符的缺点
上一节我们聊了 JavaScript构造函数(constructor),但是构造函数内部的属性和方法无法被共享。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21function Person(name){
this.name = name;
this.group = 'China'; // 我要将group作为实例对象的共有属性
this.getGroup = function() {
console.log('method: ', this.group);
}
}
// 生成两个实例对象
var p1 = new Person('p1');
var p2 = new Person('p2');
// 两个实例对象的group属性都是独立的,修改其中一个不会影响另外一个
p1.group = 'US';
p1.getGroup = function() {
console.log('change the method in p1: ', 'not US, not China');
}
console.log('change the property in p1: ', p1.group);
console.log(p1.getGroup());
console.log('property:', p2.group);
console.log(p2.getGroup());
在上面的代码中,p1和p2对象的group属性和getGroup方法都是独立的,修改其中一个不会影响另外一个,因此每一个实例对象都会拥有其构造函数内部的属性和方法的副本。这无法做到数据共享,也是占用内存极大的浪费资源。
二、prototype属性
为了解决构造函数内部的属性和方法无法被实例对象所共享的问题,我们可以把需要共享的属性和方法放在原型(prototype)对象上。原型对象上的属性和方法可以被所有实例对象所共享。
对于构造函数来说,prototype作为构造函数的属性,通常我们叫做原型;对于实例对象来说, prototype是实例对象的原型对象。所以prototype即是属性也是对象。
1 | function Person(name){ |
现在group属性和getGroup方法放在prototype对象里,是p1和p2实例对象共享的。只要修改了prototype对象中的属性和方法,就会同时影响p1和p2两个实例对象。
由于所有的实例对象共享同一个prototype对象,那么从外界看起来,prototype对象就好像是实例对象的原型,而实例对象则好像”继承”了prototype对象一样。这就是Javascript继承机制的设计思想。
需要注意的是,Object.prototype是所有原型链的终点, 所有对象都继承于Object。这点以后另开一篇来聊。
三、隐式原型
在 JavaScript构造函数(constructor)中 我们谈到new操作符到底干了啥? MDN中有这样的code: o.[[Prototype]] = Foo.prototype, 我们的理解是这样的code: obj.__proto__ = Person.prototype;
这里我们聊到了[[Prototype]]和__proto__两个属性,[[Prototype]]是实例对象拥有的属性,它是一个指针,指向构造函数的原型【注意首字母是大写】,他是“内部的”,这个特性是仅供Javascript引擎用的,因此在Javascript中不能直接访问它,同时为了表示特性是内部值,用两个方括号加以区分,像这样[[Prototype]]。而prototype是构造函数中的属性,是可见的。[[Prototype]]是对象的内部属性,仅供Javascript引擎用的,那么外部想访问它怎么办?
- Firefox,Safari,Chrome在每个对象上面都支持一个__proto__,可以当做[[Prototype]]
- 虽然无法访问[[Prototype]],但是可以通过isPrototype()方法确认对象之间是否存在某种关系,比如Person.prototype.isPrototypeOf(p1); 请 阅读
- ECMAScript5中新增了一个方法Object.getPrototypeOf(),在支持他的浏览器中返回[[Prototype]]的值,就像这样使用Object.getPrototypeOf(p1).name,这个方法在ie9+,Firefox3.5+ ,Opera12+,和Chrome中支持。请 阅读
函数有一个默认的prototype对象,这个prototype对象用来指定函数的继承关系,prototype对象默认有两个属性,一个是constructor,另一个就是__proto__属性,默认的prototype对象在被改变之前就像是这个函数用来表示自己的一个对象,其中__proto__属性和普通对象的__proto__属性一样用来对应继承关系,而constructor属性则代表了函数本身,
四、通过创建对象的两种常用方式来观察。
对象字面量和构造函数是创建对象的两种常用方式。
1)、 对象字面量(Object literal), var person = {}; 在console中打出来,我们会发现person对象下有个__proto__属性。
2)、 new 操作符调用构造函数; 在console中打出来,person对象下有个__proto__属性,同时,我们还发现Person函数下有个prototype和__proto__属性,说明Person也是个对象,符合JavaScript的万物对象论。1
2
3
4
5function Person() {
}
var person = new Person();
console.dir(person);
五、prototype和__proto__的区别
我们通过网络上流行的一张图来认识一下prototype和__proto__的区别
我们来验证一下
1 | var a = {}; |
六、对象的__proto__属性指向谁
1 | /*1、字面量方式*/ |