手写 instanceof

发布于 2023-05-07 01:14:13 字数 3738 浏览 75 评论 0

开始之前,先了解一些基本背景...

一、前言

我们知道,在 ECMAScript 标准中,当前数据类型分为两类(共 8 种):

  • 原始类型(Primitives): 包含 Undefined、Null、Boolean、String、Number、Symbol、BigInt 共 7 种基本数据类型。
  • 引用类型(Objects):除原始类型之外,其余均属于引用类型,归为一大类,比如 ObjectArrayMap 等内置方法及其实例对象。

其中,原始值都是不可改变的,且不含任何属性或方法。平时看到类似的 'string'.length 写法,本质上是发生了隐式类型转换,先将 'string' 转换为 Object('string'),然后调用 String 实例对象的 length 属性罢了。

下面,我们将「引用类型」划分为两类:

函数对象(function object):一个具有 [[Call]] 内部方法(详见)的对象,简单来说,就是可以通过 () 调用的对象,比如内置的 ObjectFunctionArray 等方法。
普通对象(ordinary object):除函数对象,其余引用值均可称为普通对象。

注意,这里提到的对象泛指引用类型,而不是单指平常所写的 {...} 对象。请记住:所有 Function 的实例都是函数对象,而其他的都是普通对象

前面划分对象,就是为了方便分清楚 prototype(原型对象)和 __proto__ (原型)的区别:

对象类型prototype__proto__
普通对象
函数对象

换句话说:所有对象都有 __proto__ 属性,而只有函数对象才具有 prototype 属性。

不作过多介绍,如果对这俩兄弟不太了解的,可看文章数据类型详解

二、instanceof

MDN 可知,其语法非常简单:

object instanceof constructor

用于检测 constructor.prototype 是否存在于参数 object 的原型链上。

要自实现 instanceof,就要了解这些特性:

  • object 必须是引用值,否则将会返回 false
  • constructor 必须是函数对象,否则会抛出 TypeError。

需要注意的是:不同上下文(比如网页中多个 <iframe>)之间拥有不同的全局对象,可理解为不同的引用地址,因此会出现如下情况:

const iframe = document.createElement('iframe')
document.body.appendChild(iframe)

const xArray = window.frames[window.frames.length - 1].Array
const xarr = new xArray()
const arr = new Array()

console.log(xarr instanceof Array) // false
console.log(xarr.constructor === Array) // false

console.log(arr instanceof Array) // true
console.log(arr.constructor === Array) // true

因此,使用 instanceof 来判断是否为数组是不准确的,可看文章

三、实现

获取原型对象的方法:

// 构造函数访问 prototype 属性
constructor.prototype

// 实例对象访问 __proto__ 属性
instance.__proto__
// __proto__ 非 ECMAScript 标准,只是被所有浏览器支持罢了
// 可使用标准中的 Object.getPrototypeOf() 方法替换

实现如下:

function myInstanceof(inst, ctor) {
  // 是否为函数对象
  const isCallable = val => typeof val === 'function'

  // 是否为引用值
  const isObject = val => typeof val === 'function' || (val !== null && typeof val === 'object')

  // ctor 必须是引用值
  if (!isObject(ctor)) throw new TypeError(`Right-hand side of 'instanceof' is not an object`)

  // ctor 必须是函数对象
  if (!isCallable(ctor)) throw new TypeError(`Right-hand side of 'instanceof' is not callable`)

  // inst 为原始值,则返回 false
  if (!isObject(inst)) return false

  do {
    const proto = inst.__proto__ // 可换成标准方法 const proto = Object.getPrototypeOf(inst)
    // 原型链顶端(proto 为 null)或者 inst 通过 Object.create(null) 构造(proto 为 undefined)
    if (proto == null) return false
    if (proto === ctor.prototype) return true
    inst = proto // 往上一级查找
  } while (true)
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据

关于作者

0 文章
0 评论
23 人气
更多

推荐作者

eins

文章 0 评论 0

世界等同你

文章 0 评论 0

毒初莱肆砂笔

文章 0 评论 0

初雪

文章 0 评论 0

miao

文章 0 评论 0

qq_zQQHIW

文章 0 评论 0

    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文