什么是原型?
在【重学JS】JavaScript对象:具有高度动态性的属性集合中提到过:在不同的编程语言中,设计者利用各种语言特性来抽象描述对象。
JavaScript就是基于原型的编程语言,利用原型来描述对象。Java是基于类的方式来描述对象。
“基于类”的编程提倡使用一个关注分类和类之间的关系开发模型。在这类语言中,总是先有类,再从类去实例化一个对象。类和类之间又可能会形成继承、组合等关系。类又往往与语言的类型系统整合,形成一定编译时的能力。
与此相对,“基于原型”的编程看起来更为提倡程序员去关注一系列对象实例的行为,而后才去关心如何将这些对象,划分到最近的使用方式相似的原型对象,而不是将它们分成类。基于原型的面向对象系统通过“复制”的方式来创建新的对象。一些语言的实现中,还允许复制一个空对象。这实际上就是创建一个全新的对象。
原型系统的“复制操作”有两种实现思路:
- 一个是并不真的去复制一个原型对象,而是使得新对象持有一个原型的引用;
- 另一个是切实地复制对象,从此两个对象再无关联。
显然,JavaScript选择了前一种方式。
JavaScript的原型
原型系统可以说:
- 如果所有对象都有私有字段[[prototype]],就是对象的原型;
- 读一个属性,如果对象本身没有,则会继续访问对象的原型,直到原型为空或者找到为止。
从ES6开始,JavaScript提供了一系列内置函数,以便更为直接地访问操纵原型。三个方法分别为:
- Object.create根据指定地原型创建新对象,原型可以是null;
- Object.getPrototypeof获得一个对象的原型;
- Object.setPrototypeof设置一个对象的原型。
new的实现原理
new运算接受一个构造器和一组调用参数,实际上做了几件事:
- 以构造器的prototype属性为原型,创建新对象;
- 将this和调用参数传给构造器,执行;
- 如果构造器返回的是对象,则返回,否则返回第一步创建的对象。
function myNew(fn, ...args) {
let obj = Object.create(fn.prototype);
let res = fn.call(obj, ...args);
if (res && (typeof res === 'object' || typeof res === 'function')) {
return res;
}
return obj;
}
new 客观上提供了两种方式,一是在构造器中添加属性,二是在构造器的prototype属性上添加属性。
ES6的class
ES6中引入了 class 关键字,并且在标准中删除了所有[[class]]相关的额私有属性描述,类的概念正式从属性升级成语言的基础设施,从此,基于类的编程方式成为了JavaScript的官方编程范式。