都知道 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 来进行判断,更加准确和安全。

相关推荐
PgSheep33 分钟前
深入理解 JVM:StackOverFlow、OOM 与 GC overhead limit exceeded 的本质剖析及 Stack 与 Heap 的差异
jvm·面试
肥肥呀呀呀44 分钟前
在Flutter上如何实现按钮的拖拽效果
前端·javascript·flutter
Zero1017131 小时前
【React的useMemo钩子详解】
前端·react.js·前端框架
养军博客1 小时前
spring boot3.0自定义校验注解:文章状态校验示例
java·前端·spring boot
uperficialyu1 小时前
2025年01月10日浙江鑫越系统科技前端面试
前端·科技·面试
付朝鲜2 小时前
用自写的jQuery库+Ajax实现了省市联动
java·前端·javascript·ajax·jquery
coderYYY2 小时前
多个el-form-item两列布局排齐且el-select/el-input组件宽度撑满
前端·javascript·vue.js·elementui·前端框架
荔枝吖2 小时前
项目中会出现的css样式
前端·css·html
Dontla2 小时前
何时需要import css文件?怎么知道需要导入哪些css文件?为什么webpack不提示CSS导入?(导入css导入规则、css导入规范)
前端·css·webpack
小堃学编程2 小时前
前端学习(2)—— CSS详解与使用
前端·css·学习