ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

instanceof 运算符与 Symbol.toStringTag:Modern JavaScript Tutorial 类检查机制完全解析

instanceof 运算符与 Symbol.toStringTag:Modern JavaScript Tutorial 类检查机制完全解析 文档/教程前端【免费下载链接】en.javascript.infoModern JavaScript Tutorial项目地址https://gitcode.com/gh_mirrors/en/en.javascript.info点击查看免费下载instanceof是 JavaScript 中判断对象是否属于某个类并兼顾继承关系的核心运算符而Object.prototype.toString配合Symbol.toStringTag则提供了一种增强版 typeof的字符串化类型识别方案。本文以 en.javascript.info 教程的 Class checking: instanceof 章节为主体结合仓库内的配套练习与源码级示例完整讲解这两种类型检查机制的语法、底层算法、自定义扩展方式以及实战中的取舍读完后你将能熟练运用它们构建多态函数、识别内建对象类型并理解构造函数本身不参与 instanceof 判定这一关键陷阱。instanceof 运算符语法、用途与基本示例instanceof运算符用于检查一个对象是否属于某个特定的类并且会考虑继承关系。这种检查在很多场景下都是必需的例如在构建多态函数时——即根据参数类型的不同采取不同处理逻辑的函数。其语法非常简洁obj instanceof Class如果obj属于Class本身或者属于继承自Class的某个类则返回true。与 class 一起使用class Rabbit {} let rabbit new Rabbit(); // 它是 Rabbit 类的对象吗 alert( rabbit instanceof Rabbit ); // true与构造函数一起使用instanceof并不局限于class语法对传统的构造函数同样生效// 使用构造函数而不是 class function Rabbit() {} alert( new Rabbit() instanceof Rabbit ); // true与内建类一起使用它同样适用于Array、Object等内建类let arr [1, 2, 3]; alert( arr instanceof Array ); // true alert( arr instanceof Object ); // true请注意arr同时也属于Object类——这是因为Array在原型上继承自Objectinstanceof沿原型链查找时会在第二层命中。instanceof 的判定算法从 Symbol.hasInstance 到原型链通常情况下instanceof通过检查原型链来完成判定。但我们可以通过静态方法Symbol.hasInstance为其设置自定义逻辑。obj instanceof Class的完整算法大致如下第一步优先调用静态方法 Symbol.hasInstance如果类上存在静态方法Symbol.hasInstance则直接调用ClassSymbol.hasInstance它应返回true或false判定到此结束。这正是我们自定义instanceof行为的入口。例如下面的代码假设任何拥有canEat属性的对象都是动物// 设定 instanceof 检查规则 // 任何拥有 canEat 属性的对象都视为 Animal class Animal { static Symbol.hasInstance { if (obj.canEat) return true; } } let obj { canEat: true }; alert(obj instanceof Animal); // true会调用 AnimalSymbol.hasInstance注意这里obj只是一个普通对象字面量与Animal类没有任何原型关系但自定义的Symbol.hasInstance仍然让它通过了判定——这正是该机制可用于实现结构化类型匹配的原因。在仓库的 Symbol 章节中Symbol.hasInstance与Symbol.iterator、Symbol.toPrimitive等一起被列为 JavaScript 内部使用的系统符号system symbols其行为由规范中的 Well-known symbols 表定义。第二步大多数类走标准逻辑——原型链比对大多数类并没有Symbol.hasInstance此时使用标准逻辑检查Class.prototype是否等于obj原型链中的某一个原型。换句话说就是逐级比对obj.__proto__ Class.prototype? obj.__proto__.__proto__ Class.prototype? obj.__proto__.__proto__.__proto__ Class.prototype? ... // 若任何一级比对为 true则返回 true // 若到达链的末尾仍无匹配则返回 false在上面的例子中rabbit.__proto__ Rabbit.prototype所以第一步就能给出答案。在继承场景下匹配会发生在第二级class Animal {} class Rabbit extends Animal {} let rabbit new Rabbit(); alert(rabbit instanceof Animal); // true // rabbit.__proto__ Animal.prototype 不匹配 // rabbit.__proto__.__proto__ Animal.prototype 匹配下图直观展示了rabbit instanceof Animal时rabbit的原型链如何与Animal.prototype逐级比对从图中可以看到rabbit的[[Prototype]]指向Rabbit.prototype再往上指向Animal.prototype最终指向null。判定过程就是沿着这条链把每一级原型与Animal.prototype做比较。关键洞察构造函数不参与判定prototype 才定义类型instanceof还存在一个等价写法objA.isPrototypeOf(objB)方法若objA位于objB的原型链中的某个位置则返回true。因此obj instanceof Class可以改写为Class.prototype.isPrototypeOf(obj)有意思的是Class构造函数本身完全不参与检查只有原型链和Class.prototype才是判定依据。这会导致一个有趣的后果当对象的prototype属性在对象创建之后被修改时instanceof的结果会随之改变function Rabbit() {} let rabbit new Rabbit(); // 修改了 prototype Rabbit.prototype {}; // ……不再是兔子了 alert( rabbit instanceof Rabbit ); // false原因在于rabbit的[[Prototype]]仍然指向旧的Rabbit.prototype对象而新的Rabbit.prototype {}是另一个全新对象两者不相等原型链比对自然失败。仓库配套练习Strange instanceof这一特性在仓库的配套练习 1-strange-instanceof 中被进一步强化。题目问下面的代码中a明明不是由B()创建的为什么instanceof返回truefunction A() {} function B() {} A.prototype B.prototype {}; let a new A(); alert( a instanceof B ); // true官方给出的解答正是本节结论的印证instanceof关心的不是函数本身而是它的prototype是否与对象的原型链相匹配。这里a.__proto__ B.prototype所以返回true。按照instanceof的逻辑prototype 实际上定义了类型而不是构造函数。Bonus把 Object.prototype.toString 当作增强版 typeof我们知道普通对象被转换为字符串时会得到[object Object]let obj {}; alert(obj); // [object Object] alert(obj.toString()); // 同样输出 [object Object]这是它们对toString的实现。但toString还有一个隐藏的强大特性它可以被提取出来在任意其他值的上下文中执行并作为增强版typeof使用甚至可以作为instanceof的替代方案。根据规范内建的toString可以从对象中提取并在任何其他值的上下文中执行其返回结果取决于该值数字[object Number]布尔值[object Boolean]null[object Null]undefined[object Undefined]数组[object Array]……等等可自定义。演示如下// 把 toString 方法复制到变量中以便调用 let objectToString Object.prototype.toString; // 这个值的类型是什么 let arr []; alert( objectToString.call(arr) ); // [object Array]这里使用了call方法详见仓库的 call-apply-decorators 章节以this arr的上下文执行objectToString函数。toString算法内部会检查this并返回对应结果。更多示例let s Object.prototype.toString; alert( s.call(123) ); // [object Number] alert( s.call(null) ); // [object Null] alert( s.call(alert) ); // [object Function]Symbol.toStringTag自定义类型标签Object.prototype.toString的行为可以通过一个特殊属性Symbol.toStringTag来自定义。例如let user { [Symbol.toStringTag]: User }; alert( {}.toString.call(user) ); // [object User]大多数环境特有的对象都带有这个属性。下面是浏览器中的一些具体示例// 环境特有对象和类的 toStringTag alert( window[Symbol.toStringTag]); // Window alert( XMLHttpRequest.prototype[Symbol.toStringTag] ); // XMLHttpRequest alert( {}.toString.call(window) ); // [object Window] alert( {}.toString.call(new XMLHttpRequest()) ); // [object XMLHttpRequest]可以看到结果恰好就是Symbol.toStringTag如果存在的话包上[object ...]的外壳。最终我们得到的是一个打了类固醇的 typeof它不仅适用于原始数据类型也适用于内建对象甚至可以被自定义。当我们需要把类型作为字符串获取而不仅仅是做布尔检查时可以用{}.toString.call替代instanceof来处理内建对象。类型检查方法总结与选型仓库文档对目前学过的类型检查方法做了系统总结方法适用对象返回值typeof原始类型字符串{}.toString原始类型、内建对象、带Symbol.toStringTag的对象字符串instanceof对象true/false从中可以看出{}.toString在技术上是一种更高级的typeof——它既能覆盖原始类型又能区分内建对象还支持通过Symbol.toStringTag自定义。instanceof的真正用武之地在于类层次结构当我们需要在考虑继承关系的前提下判断对象属于哪个类时它是最合适的选择。仓库实战印证instanceof 在错误处理中的应用instanceof在仓库的其他章节中被大量实战使用最典型的是 自定义错误章节。该章节明确指出如果自定义错误类继承自Error就可以用obj instanceof Error来识别错误对象。其核心模式是if (err instanceof ValidationError) { // 处理 ValidationError } else if (err instanceof SyntaxError) { // 处理 SyntaxError }文档还特别强调了选择instanceof而非其他判断方式的原因因为将来ValidationError可能被扩展出PropertyRequiredError等子类型而instanceof检查对新的继承类依然有效具有面向未来的健壮性。这恰好呼应了本文第一节instanceof 会考虑继承关系的论断也印证了文档中当我们处理类层次结构并希望在考虑继承的前提下检查类归属时instanceof 真正大放异彩的总结。小结obj instanceof Class判断对象是否属于某个类或其子类判定时只比较Class.prototype与对象原型链构造函数本身不参与。可通过静态方法Symbol.hasInstance完全自定义判定逻辑。Class.prototype.isPrototypeOf(obj)是instanceof的等价表述。Object.prototype.toString.call(value)是覆盖原始类型、内建对象且可自定义的增强版 typeof。需要布尔判断且关注继承关系时选instanceof需要拿到类型字符串时选{}.toStringSymbol.toStringTag。赞分享文档/教程前端【免费下载链接】en.javascript.infoModern JavaScript Tutorial项目地址https://gitcode.com/gh_mirrors/en/en.javascript.info点击查看免费下载相关推荐JavaScript 基础运算符与数学运算全解析Modern JavaScript TutorialJavaScript 基础运算符与数学运算全解析Modern JavaScript Tutorial 本篇指南围绕 Modern JavaScript Tu文档/教程前端Modern JavaScript Tutorial 精读JavaScript 的 8 种数据类型与 typeof 运算符全解析Modern JavaScript Tutorial 精读JavaScript 的 8 种数据类型与 typeof 运算符全解析 JavaScript 中的每文档/教程前端JavaScript 逻辑运算符完全指南||、 与 ! 的求值机制与短路行为Modern JavaScript Tutorial 实战解析JavaScript 逻辑运算符完全指南 || 、 与 ! 的求值机制与短路行为Modern JavaScript Tutorial 实战解析 本篇文档/教程前端上一篇Superpowers给AI编程助手的完整技能库工作流下一篇BookmarkHub开发者指南基于WXT和React的浏览器插件开发创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表