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

相关推荐
吃杠碰小鸡22 分钟前
commitlint校验git提交信息
前端
虾球xz1 小时前
游戏引擎学习第20天
前端·学习·游戏引擎
我爱李星璇1 小时前
HTML常用表格与标签
前端·html
疯狂的沙粒1 小时前
如何在Vue项目中应用TypeScript?应该注意那些点?
前端·vue.js·typescript
小镇程序员1 小时前
vue2 src_Todolist全局总线事件版本
前端·javascript·vue.js
野槐1 小时前
前端图像处理(一)
前端
程序猿阿伟1 小时前
《智能指针频繁创建销毁:程序性能的“隐形杀手”》
java·开发语言·前端
疯狂的沙粒1 小时前
对 TypeScript 中函数如何更好的理解及使用?与 JavaScript 函数有哪些区别?
前端·javascript·typescript
瑞雨溪2 小时前
AJAX的基本使用
前端·javascript·ajax
力透键背2 小时前
display: none和visibility: hidden的区别
开发语言·前端·javascript