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 4 Current »

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__: {}} 是不合法的),可在此查看对比

Object 原型

空对象

往往,当我们谈及空对象时,可能指的通常是 {} —— 但它其实「没有那么空」,它存在着 Object 作为它的原型,因此实际存在着若干的属性

可以利用 console.log(Object.getPrototypeOf({})) 来查看一个空对象中所拥有的属性

image-20240628-080154.png

如果我们想创建一个「真正的」空对象,需要确保它的原型也为空,可以利用 Object.create(null) 来获得它

Object 原型

JS 中基本所有的非基本类型的对象的原型最终都是 Object(可点此实验,除了 null undefined Symbol 和特意自定义了原型的对象,包括自定义类的实例、各种基础类型在内的所有对象最初始的原型都是 Object)。

因为原型是一种链式结构,而几乎所有的对象的原型链最头上都是 Object,因此 Object 的方法几乎可以被所有对象所调用(在没有被同名属性屏蔽的情况下)

可以通过 console.log(Object.prototype) 获取到 Object 原型的内容,它包括

  • 与值相关的属性 TODO

  • 与 property 相关的属性

  • 与 prototype 相关的属性

    • (已经被弃用的)__proto__ 属性,用于获取或设置 prototype(上面说过了)

    • isPrototypeOf 属性:一个函数,用于判断调用当前函数的 this 是否存在于另一个对象的原型链中

      • 使用方法应该为 A.prototype.isPrototypeOf(b),即调用 isPrototypeOf 的对象应当为一个原型对象、而它的参数应该为存在原型的对象

      • 现在通常使用 instanceof,通常A.prototype.isPrototypeOf(b) 可以被 b instanceof A 替代

对象的 property

TODO

  • No labels