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,都曾报告过原型中毒缺陷。