Skip to end of metadata
Go to start of metadata

You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 2 Next »

JS 有个神奇的东西叫做 Prototype,这导致它与主流的面向对象的语言相差甚远,甚至有一个「基于对象的语言」专有名词给他。

认识原型

访问原型

根据 ECMAScript 规范,当提及原型时使用的标识符号为 someObject.[[Prototype]]

获取原型的方法是利用 Object.getPrototypeOfReflect.getPrototypeOf 函数来访问

在 ES5 中,传递给 Object.getPrototypeOf 的参数如果不是对象将抛出异常;在 ES2015 中将会返回对应的包装类型
使用 Reflect.getPrototypeOf 时则无论 ES5 还是 ES2015 中,只要参数不是对象都会抛出异常

大多情况下,原型也可以通过对象的 __proto__ 属性访问到(已弃用)

函数可能拥有 prototype 属性,这个属性不是这个函数本身的 Prototype,而是当利用这个函数作为构造函数的情况下所构造的对象的 Prototype

设置原型

原型是可以被动态修改的,利用 Object.setPrototypeOfReflect.setPrototypeOf 即可

根据 ECMAScript 规范,当提及设置原型时使用的标识符号为 someObject.[[SetPrototypeOf]]

Object.setPrototypeOfReflect.setPrototypeOf 的区别仅在于返回值与异常行为:

  • Object.setPrototypeOf 返回被设置原型的对象,无法设置原型时抛出异常

  • Reflect.setPrototypeOf 返回是否修改成功(true / false)

应当避免修改原型,修改原型的速度很慢,另外可能会导致各种微妙的问题

如果必须使用动态原型,应当尽量采用先创建一个无原型的对象(Object.create(null))再设置原型的方案

在如下的情况下,不可以设置原型:

  1. 被修改原型的对象是 nullundefined

  2. 新的原型不是 object 或是 null

  3. 被修改原型的对象是不可扩展的,或不可修改原型的对象(但如新的原型和原来的原型一致则不会抛出错误)

也可以通过 __proto__ 属性修改原型,但与访问这个属性相同,使用这个属性来设置原型是被弃用且不推荐的

初始化原型

可以利用 { __proto__: ... }Object.create(...) 初始化一个指定原型的对象

obj.__proto__ 不在规范中且已被弃用,而 { __proto__: ... } 是标准中的一部分,可以正常使用

类的本质是原型,本节不含初始化类对象的方案

特别注意:字面初始化时的 __proto__ 与对象的 __proto__ 属性不同,即 { __proto__: {} }{ ['__proto__']: {} }不一样的(前者是设置了对象的 prototype,后者是设置了一个普通的属性),可在此查看效果

普通的属性可以在初始化时定义多次(例如 {a: 1, a: 2}),但是 __proto__ 只能定义一次({__proto__:{}, __proto__: {}} 是不合法的),可在此查看对比

  • No labels