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

相关推荐
RadiumAg41 分钟前
记一道有趣的面试题
前端·javascript
yangzhi_emo1 小时前
ES6笔记2
开发语言·前端·javascript
yanlele1 小时前
我用爬虫抓取了 25 年 5 月掘金热门面试文章
前端·javascript·面试
中微子2 小时前
React状态管理最佳实践
前端
烛阴2 小时前
void 0 的奥秘:解锁 JavaScript 中 undefined 的正确打开方式
前端·javascript
小兵张健2 小时前
武汉拿下 23k offer 经历
java·面试·ai编程
中微子2 小时前
JavaScript 事件与 React 合成事件完全指南:从入门到精通
前端
Hexene...3 小时前
【前端Vue】如何实现echarts图表根据父元素宽度自适应大小
前端·vue.js·echarts
爱莉希雅&&&3 小时前
技术面试题,HR面试题
开发语言·学习·面试
天天扭码3 小时前
《很全面的前端面试题》——HTML篇
前端·面试·html