JavaScript对象的原型(prototype)简介
原文:Intro to Object Prototypes in JavaScript
JavaScript的继承是基于原型(prototype-based)的,即使使用ES6中类的extends关键词时也是如此。本文介绍了原型(prototype)的一些知识。
JavaScript中用{}创建一个空对象,它其实自带了一些内置属性,例如toString()函数。
1 | const obj = {}; |
Mozilla的文档把这个函数记录为Object.prototype.toString(),因为obj是Object类的一个实例。
在调用toString属性时,JavaScript先查看obj是否有toString属性,如果没有,JavaScript沿着继承链向上找到Object.prototype,继续查看Object.prototype是否有toString属性。
1 | const obj = {}; |
可以把Object.prototype看作是一个模板对象,所有对象都从它这里继承一些方法和属性。
向prototype添加属性
prototype与JavaScript的其他对象没有本质区别。这意味着可以向Object.prototype添加新属性,这样所有对象都可以访问到这些属性。
1 | // 为所有对象添加 `getAnswer()` 函数 |
虽然可以向Object.prototype添加方法,但这样做是不对的,这种操作会导致未来版本JavaScript的兼容性问题。例如,一个流行的库添加了一个Array.prototype.flatten(),与一个新的JavaScript内置函数冲突,导致了著名的“Smoosh门”崩溃事件。
创建自己的prototype原型
在ES6之前的类,只是一个用new调用的普通的传统函数。
1 | function MyClass() {} |
MyClass函数有一个prototype属性,是可以修改的。
1 | function MyClass() {} |
甚至可以整体覆盖MyClass函数的prototype。
1 | function MyClass() {} |
从其他类继承
prototype对象并不一定是一个普通的对象,它可以是任何其他类的实例。要创建一个MyChildClass类,继承MyClass,可以将MyChildClass的prototype设置为MyClass的一个实例。
1 | function MyClass() {} |
MyChildClass继承于MyClass,MyClass继承于Object。因为MyChildClass.prototype是MyClass的实例,而MyClass.prototype是Object的实例。这就是JavaScript开发者常说的原型链。
获取对象的原型
给定一个对象,可以用.constructor.prototype获取它的prototype。
1 | function MyClass() {} |
这是因为对象的constructor属性指向了Object.prototype.constructor。另外还有一个和constructor.prototype相似的,非标准的__proto__属性。
constructor和__proto__属性是潜在的原型中毒攻击向量。一些流行的JavaScript库,包括lodash和Mongoose,都曾报告过原型中毒缺陷。