JavaScript数组为何会被识别为字符串?Lodash isString为何要额外校验数组?
关于字符串类型判断的核心问题解析
为什么你现有的判断逻辑不会误判数组?
你目前写的判断逻辑为:
const date = '2021-02-05T16:17:26'; const isDateAString = date instanceof String || typeof date === 'string';
这套逻辑在标准运行环境下对数组是完全免疫的:
- 无论数组被追加了什么自定义属性、篡改了什么内置方法,
typeof []的返回值永远是'object',根本不可能命中typeof value === 'string'的原始值判断分支 - 数组的原型链天然指向
Array.prototype,哪怕存在跨iframe场景下构造函数不共享的问题,数组也不可能成为String构造函数的实例,instanceof String的判断结果永远为false
Lodash为什么要额外校验待判断值不是数组?
这是Lodash类型判断体系中的历史兜底逻辑,本质是为了弥补纯靠Object.prototype.toString判断类型的漏洞:
Lodash早期的类型判断没有用typeof做前置校验,是完全依靠Object.prototype.toString.call(value)的返回值(也就是所谓的「类型标签」)来识别具体类型的。这套逻辑的核心漏洞是:如果对象被人为修改了Symbol.toStringTag属性,让它的toString返回值和字符串类型的标准标识([object String])完全一致,纯靠标签判断就会出现误判。
会触发这类误判的常见场景包括:
- 代码中有人手动修改了数组原型的toString标签:比如执行了
Array.prototype[Symbol.toStringTag] = 'String',后续所有新建的数组实例,用toString方法判断时都会返回和字符串完全一致的结果 - 某些质量低劣的旧polyfill或第三方库,为了兼容旧环境错误地给数组对象绑定了和字符串匹配的toString标识
- 极端的对象篡改场景:比如手动把数组的原型链指向String.prototype,让它的原型链结构和字符串包装对象完全一致
Lodash加入「待判断值不是数组」的校验,就是为了在这类外部代码篡改运行环境的极端场景下,哪怕toString标签已经失真,也能把数组和真实的字符串类型区分开。
这个校验逻辑是为了适配遗留浏览器的怪异行为吗?
完全不是。
这个兜底设计和早期遗留浏览器的原生怪癖没有任何关联:
- 遗留浏览器(比如IE系列的旧版本)的原生实现中,数组的toString返回值就是标准的
[object Array],本身就不存在把数组识别成字符串的原生bug - 这个校验本质是通用类型工具的鲁棒性设计:Lodash的目标是成为可在任意第三方运行环境下可靠运行的工具库,需要保证哪怕运行环境的原生toString方法、原型链被其他代码改坏了,类型判断结果也尽可能准确,不会因为外部代码的错误篡改,就把数组当成字符串类型处理。
内容的提问来源于stack exchange,提问作者Liam Steele
相关产品推荐
相关产品推荐

