prototype
概念:给其他对象提供共享属性
的对象。也就是说prototype
自己也是一个对象,只是被赋予了某个职能。
就如实现发布/订阅模式时,我们将subscriber称为订阅者,publisher称为发布者。并不是他们与其他对象有什么本质区别,只是一种约定。
同理,当某个对象为其他对象
承担了共享属性
的职能时,它就成了该对象的prototype
,失去该职能也就不再是该对象的prototype
。
也就是我们在说prototype
时,实际上表示的是两个对象的某种关系,比如A对象为B对象提供属性访问权限。所有对象都可以作为另一个对象的prototype
。
一个对象如何为另外一个对象提供属性访问?
在JavaScript规范中,明确描述了所有对象都有一个隐式的引用,它被称为这个对象的prototype
原型。
如上如所示,我们只申明了一个对象,但是在控制台可以发现它有__proto__
属性,这意味着申明的a
隐式的引用了另外一个对象的属性,置于__proto__
属性中。
__proto__
历史问题
ECMAScript 规范描述 prototype 是一个隐式引用,但之前的一些浏览器,已经私自实现了__proto__
这个属性,使得可以通过a.__proto__
这个显式的属性访问,访问到被定义为隐式属性的 prototype
由于部分浏览器提前开了__proto__
的口子,使得可以通过a.__proto__
直接访问到原型,通过a.__proto__ = antherObj
直接设置原型。所以ECMAScript 2015
规范,将__proto__
属性纳入规范的一部分。
- 可以通过
Object.getPrototypeOf(obj)
间接访问指定对象的prototype
- 可以通过
Object.setPrototypeOf(obj, anotherObj)
间接设置指定对象的prototype
虽然在对象的prototype
上有__proto__
属性,实际上它是一个accessor property(访问器属性)
,在get
方法里面调用getPrototypeOf
,在set
方法里面调用setPrototypeOf
扩展:访问器属性是什么?
对于JavaScript来说,属性并非只是简单的名称和值,JavaScript用一组特征attribute
来描述属性property
- 数据属性
value:就是属性的值。
writable:决定属性能否被赋值。
enumerable:决定for in能否枚举该属性。
configurable:决定该属性能否被删除或者改变特征值。
- 访问器(getter/setter)属性
个人理解:访问器属性,实际就是对对象的某个属性,进行取值,设置值,以及其他一些情况的操作。
getter:函数或undefined,在取属性值时被调用。
setter:函数或undefined,在设置属性值时被调用。
enumerable:决定for in能否枚举该属性。
configurable:决定该属性能否被删除或者改变特征值。
prototype chain 原型链
概念:因为prototype
本身也是对象,所以它也有自己的隐式引用,有自己的prototype
对象。如此就构成了对象的原型==>原型==>原型的链条,直到某个对象的隐式引用为null,整个链条终止。
原型继承方式
- 显示继承
- 隐式继承
显示继承
前文有提到,具体的方式如下图
实际显示继承有两种方式
Object.setPropertyOf
Object.create
两者的差别主要
Object.setPropertyOf
给我两个对象,我把其中一个设置为另一个的原型Object.create
,给我一个对象,它将作为我创建的新对象的原型。- 所以当我们已经拥有两个对象时,要构建原型关联,可以通过
Object.setPrototypeOf
来处理。 - 当我们只有一个对象,想以它为原型,创建新对象,则通过
Object.create
来处理。
隐式继承
const user = {
name:'Tom',
age:12
}
这种创建方式叫做对象字面量
,这种创建方式,实际有两层隐式行为
- 隐式的通过
new Object()
创建对象 - 隐式的进行原型继承