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.setPropertyOfObject.create
两者的差别主要
Object.setPropertyOf给我两个对象,我把其中一个设置为另一个的原型Object.create,给我一个对象,它将作为我创建的新对象的原型。- 所以当我们已经拥有两个对象时,要构建原型关联,可以通过
Object.setPrototypeOf来处理。 - 当我们只有一个对象,想以它为原型,创建新对象,则通过
Object.create来处理。
隐式继承
const user = {
name:'Tom',
age:12
}
这种创建方式叫做对象字面量,这种创建方式,实际有两层隐式行为
- 隐式的通过
new Object()创建对象 - 隐式的进行原型继承