Open felix-cao opened 6 years ago
在 《JavaScript 构造器》中我们聊过,通过 new 操作符调用构造器来创建一个对象,但是 new 操作符有个很严重的缺点:构造器内部的属性和方法无法被共享,本文就是来聊一聊原型对象上的属性和方法被实例对象所共享的问题。
new
为了解决构造器内部的属性和方法无法被实例对象所共享的问题,我们把需要共享的属性和方法放在构造器的原型(prototype)属性上。JavaScript 每个函数都有一个 prototype (原型)属性,这个属性是一个指针,指向一个对象。
prototype
JavaScript
我们认真分析上面的话,可以看出:
我们经常说“对象的原型”,就 JavaScript 的真正实现来说,其实并不能说对象有原型,而只能说对象的构造器有原型。 👍
prototype 属性是所有通过 new 操作符调用构造器创建实例对象的原型对象。原型对象上的属性和方法可以被所有实例对象所共享。
对于构造器来说,prototype 作为构造器的属性,指向的对象我们通常叫做原型;对于实例对象来说, prototype 指向的对象是实例对象的原型对象。
function Person(name){ this.name = name; } Person.prototype.group = 'China'; Person.prototype.getGroup = function() { return this.group + ' - method'; } // 生成两个实例对象 var p1 = new Person('p1'); var p2 = new Person('p2'); console.log('property in p1: ', p1.group, p1.getGroup()); console.log('property in p2: ', p2.group, p2.getGroup()); // 修改原型对象上的属性和方法 Person.prototype.group = 'US'; Person.prototype.getGroup = function() { return this.group + ' - method changed'; } console.log('property in p1: ', p1.group, p1.getGroup()); console.log('property in p2: ', p2.group, p2.getGroup());
现在 group 属性和 getGroup 方法放在 prototype 对象里,是 p1 和 p2 实例对象共享的。只要修改了 prototype 对象中的属性和方法,就会同时影响 p1 和 p2 两个实例对象。
group
getGroup
p1
p2
由于所有的实例对象共享同一个 prototype 指向的对象,那么从外界看起来,prototype 对象就好像是实例对象的原型,而实例对象则好像"继承"了 prototype 指向的对象一样。这就是 Javascript 继承机制的设计思想。
Javascript
需要注意的是,Object.prototype 是所有原型链的终点, 所有对象都继承于 Object 。这点以后另开一篇来聊。
Object.prototype
Object
__proto__
每个对象都会记住自己的原型 👍
在 《JavaScript 构造器》 中第四部分我们谈了 new 操作符到底干了啥? MDN 中有这样的代码: o.[[Prototype]] = Foo.prototype, 我们的理解是这样: obj.__proto__ = Person.prototype;
MDN
o.[[Prototype]] = Foo.prototype
obj.__proto__ = Person.prototype
这里我们聊到了 [[Prototype]](注意首字母是大写) 和 __proto__ 两个属性,[[Prototype]] 是实例对象拥有的属性,它是一个指针,指向构造器的原型对象,他是“内部的”,这个特性是仅供 Javascript 引擎用的,因此在 Javascript 中不能直接访问它,同时为了表示特性是内部值,用两个方括号加以区分,像这样 [[Prototype]]。而 prototype 是构造函数中的属性,是可见的。[[Prototype]] 是对象的内部属性,仅供 Javascript 引擎用的,那么外部想访问它怎么办?
[[Prototype]]
Firefox
Safari
Chrome
isPrototype()
Person.prototype.isPrototypeOf(p1)
ECMAScript5
Object.getPrototypeOf()
Object.getPrototypeOf(p1).name
ie9+
Firefox3.5+
Opera12+
函数有一个默认的 prototype 对象,这个 prototype 对象用来指定函数的继承关系,prototype 对象默认有两个属性,一个是 constructor,另一个就是 __proto__ 属性,默认的 prototype 对象在被改变之前就像是这个函数用来表示自己的一个对象,其中 __proto__ 属性和普通对象的 __proto__ 属性一样用来对应继承关系,而 constructor 属性则代表了函数本身,
constructor
我们通过网络上流行的一张图来认识一下 prototype 和 __proto__ 的区别
我们来验证一下
var a = {}; console.log(a.prototype); // undefined console.log(a.__proto__); // Object {} var b = function(){} console.log(b.prototype); // b {} console.log(b.__proto__); // ƒ () { [native code] }
/*1、字面量方式*/ var a = {}; console.log(a.__proto__); // Object {} console.log(a.__proto__ === a.constructor.prototype); // true /*2、构造器方式*/ var A = function(){}; var a = new A(); console.log(a.__proto__); // A {} console.log(a.__proto__ === a.constructor.prototype); // true /*3、Object.create()方式*/ var a1 = {a:1} var a2 = Object.create(a1); console.log(a2.__proto__); // Object {a: 1} console.log(a.__proto__ === a.constructor.prototype); // false(此处即为图1中的例外情况)
在 《JavaScript 构造器》中我们聊过,通过
new
操作符调用构造器来创建一个对象,但是new
操作符有个很严重的缺点:构造器内部的属性和方法无法被共享,本文就是来聊一聊原型对象上的属性和方法被实例对象所共享的问题。一、原型和原型对象
为了解决构造器内部的属性和方法无法被实例对象所共享的问题,我们把需要共享的属性和方法放在构造器的原型(
prototype
)属性上。JavaScript
每个函数都有一个prototype
(原型)属性,这个属性是一个指针,指向一个对象。我们认真分析上面的话,可以看出:
prototype
。 👍我们经常说“对象的原型”,就 JavaScript 的真正实现来说,其实并不能说对象有原型,而只能说对象的构造器有原型。 👍
prototype
属性是所有通过new
操作符调用构造器创建实例对象的原型对象。原型对象上的属性和方法可以被所有实例对象所共享。对于构造器来说,
prototype
作为构造器的属性,指向的对象我们通常叫做原型;对于实例对象来说,prototype
指向的对象是实例对象的原型对象。现在
group
属性和getGroup
方法放在prototype
对象里,是p1
和p2
实例对象共享的。只要修改了prototype
对象中的属性和方法,就会同时影响p1
和p2
两个实例对象。由于所有的实例对象共享同一个
prototype
指向的对象,那么从外界看起来,prototype
对象就好像是实例对象的原型,而实例对象则好像"继承"了prototype
指向的对象一样。这就是Javascript
继承机制的设计思想。需要注意的是,
Object.prototype
是所有原型链的终点, 所有对象都继承于Object
。这点以后另开一篇来聊。二、对象的
__proto__
属性每个对象都会记住自己的原型 👍
在 《JavaScript 构造器》 中第四部分我们谈了
new
操作符到底干了啥?MDN
中有这样的代码:o.[[Prototype]] = Foo.prototype
, 我们的理解是这样: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
属性则代表了函数本身,三、prototype 和
__proto__
的区别我们通过网络上流行的一张图来认识一下
prototype
和__proto__
的区别我们来验证一下
四、对象的
__proto__
属性指向谁Reference