都知道 instanceof 能够判断类型,但别忘了这个细节,以后跟面试官又可以多吹一点内容!

扯皮

前段时间看红宝书引用类型中的数组篇章时,其中有一个关于判断数组的小节提到了 instanceof 的一个弊端,这里赶紧写一篇水文做个总结,以后和面试官深情交流时就可以再多讲一些,拿下的机会更高了!

正文

instanceof

关于 instanceof 就是用来判断一些引用类型对象是否由某个构造函数所构造出来,但准确来讲是检测该对象的原型链上是否存在该构造函数的 prototype

根据这一特性我们完全就能够手写出来一个 instanceof,只需沿着该对象的原型链去判断即可:

javascript 复制代码
function myInstanceof(o, constructor) {
  if (typeof o !== "object" || o === null) return false;
  const prototype = constructor.prototype;
  let proto = o.__proto__;
  while (proto) {
    if (proto === prototype) return true;
    proto = proto.__proto__;
  }
  return false;
}

const arr = [];
console.log(arr instanceof Array); // true
console.log(arr instanceof Object); // true
console.log(arr instanceof RegExp); // false

// my
console.log(myInstanceof(arr, Array)); // true
console.log(myInstanceof(arr, Object)); // true
console.log(myInstanceof(arr, RegExp)); // false

两个弊端

篡改原型链

关于这一点我相信是大部分人都知道的,包括我个人在面试的时候也都会提到这一点来说明 instanceof 本身的局限性。

由于 instanceof 是沿着原型链去判断,因此当我们尝试修改原有原型链的指向就会造成判断错误

javascript 复制代码
function Person() {}
function Monster() {}

const p = new Person();

console.log(p instanceof Person); // true
Person.prototype = Monster.prototype;
console.log(p instanceof Person); // false
console.log(p.__proto__ === Person.prototype); // false

但我们知道针对于一些内置的构造函数:Object、Array、Date 等它们的 prototype 是不允许进行修改的,因此可以大胆的进行使用 instanceof 进行判断,我们直接来看这些构造函数针对于 prototype 属性的属性描述符:

javascript 复制代码
console.log(Object.getOwnPropertyDescriptor(Object, "prototype"));
console.log(Object.getOwnPropertyDescriptor(Array, "prototype"));
console.log(Object.getOwnPropertyDescriptor(Date, "prototype"));
console.log(Object.getOwnPropertyDescriptor(RegExp, "prototype"));
console.log(Object.getOwnPropertyDescriptor(Map, "prototype"));
console.log(Object.getOwnPropertyDescriptor(Set, "prototype"));

结果理所当然,清一色的 false:

所以这种操作就别想辣:

javascript 复制代码
const cache = Array.prototype;
const emptyObj = Object.create(null);
Array.prototype = emptyObj; // no use ❗
console.log(Array.prototype === emptyObj, Array.prototype === cache); // false  true
console.log([] instanceof Array); // true

看似这里 instanceof 好像没其他问题可以放心用了,直到我看到了红宝书里面的内容,我们继续往下研究。

同一个全局执行上下文

在红宝书中关于 instanceof 又提到了另外一个点指明它也并不是万能的:

针对于 instanceof 这里只提到了这一段话,并且提到了一个关键词:全局执行上下文,我们自然而然的可以想象的到是网页的 window 对象,所以根据这段话来推理就可能出现这样的情景,如下图所示:

一个页面有多个全局对象,如果是来自不同对象的变量进行判断就不再符合预期了:

关于这一点我查阅了 MDN 文档,针对 instanceof 的这个问题也做了解释(怪自己学习的时候没仔细看,全看八股去了😭)instanceof - JavaScript | MDN (mozilla.org)

OK,眼见为实,我们自己来模拟一下一下 iframe 的场景,开启 live Server 在一个 html 中通过 iframe 引入另外一个页面:

html 复制代码
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <h1>page1</h1>
    <!-- 引入 iframe -->
    <iframe src="./two.html"></iframe>
    <script>
      const arr = [];
      const window2 = window.frames[0]; // 拿到 iframe window 对象
      console.log(arr instanceof window2.Array);
    </script>
  </body>
</html>

总结

instanceof 可以用作引用类型的判断,但是它自身也有局限性,比如内部是根据原型链的顺序来判断,如果人为修改原型链会导致判断错误。即使 JS 的内置构造函数 Array、Object 等的 prototype 属性是不可修改的,但如果遇到多个执行上下文的场景(iframe)并牵扯到页面之间的数据通信,也可能会造成 instanceof 判断错误。

综上所述判断类型要斟酌当前的场景再合理的使用 instanceof,当然也可以使用 Array.isArray 或者 Object.prototype.toString.call 来进行判断,更加准确和安全。

相关推荐
东风破_4 小时前
danci 项目(三):从 JSON 数据到 AI Coding,真实项目里的数据清洗、Prompt 和工程规范
前端·后端·node.js
怕浪猫4 小时前
FDE 最大的浪费不是写出有 bug 的代码,而是漂亮地解决了一个错误的问题
面试·架构·github
东风破_4 小时前
danci 项目(一):从需求到架构,一个单词学习系统为什么会这样设计
前端·后端·node.js
蒲公英eric4 小时前
从客户端到服务端:DVWA DOM 型 XSS 模块完整漏洞分析教程
前端·web安全·ai·xss·dvwa·ai安全
单线程_014 小时前
从案例分析 Vue3 Tokenizer+Parser 源码三
前端·javascript·vue.js
顶点多余5 小时前
那些在算法中适合巩固的知识点---1
java·前端·算法
Setsuna_F_Seiei5 小时前
前端转型 Agent 开发 03 之 Agent Tools - 给 Agent 装上手脚
前端·agent·ai编程
jay神5 小时前
【计算机毕业设计】基于SpringBoot的程序教学辅助系统
java·前端·vue.js·spring boot·后端·毕业设计·课程设计
黄敬峰7 小时前
Next.js 全栈实战:数据清洗、ORM 设计与 AI Prompt 工程最佳实践
面试·github
kyriewen8 小时前
我装了30多个Skill,给AI安排了8个岗位
前端·javascript·ai编程