为什么 String([]) 是空字符串,而 String([1,2]) 是 '1,2'?
数组转字符串时会走 Array.prototype.toString,通常再调用无参数的 join(),用逗号连接元素。空数组、空槽、null 和 undefined 元素会产生容易误读的结果;嵌套数组又会递归转换,[] + [] 的结果还受加号的原始值转换规则影响。
JavaScript · 值与类型面试题第 1 页,显示第 1–50 题,共找到 50 道完整解析,可继续按分类、标签与关键词缩小范围。
按稳定语义路径排序
数组转字符串时会走 Array.prototype.toString,通常再调用无参数的 join(),用逗号连接元素。空数组、空槽、null 和 undefined 元素会产生容易误读的结果;嵌套数组又会递归转换,[] + [] 的结果还受加号的原始值转换规则影响。
typeof 对数组和普通对象都返回 object,对 null 也一样,因此无法单独用它判断数组。Array.isArray 能跨窗口识别真正的数组,instanceof 受原型链和 realm 影响,Object.prototype.toString 则可能被 Symbol.toStringTag 改写。
BigInt 能精确表示超过 Number 安全整数范围的整数,但两种数值类型不能直接混合做算术,Math 方法也不接受 BigInt。除法会向零截断,JSON.stringify 默认无法直接序列化;接口传输和类型转换要明确选择格式与精度边界。
10n 字面量和 BigInt() 都能得到大整数,但 BigInt() 不接受非整数 Number,非法字符串也会抛错。已经超出安全整数范围的数字在传入 BigInt() 前可能丢失精度;从接口或 JSON 接收大整数时应尽量保留原始字符串。
JavaScript 的假值只有固定几类:false、零、NaN、空字符串、null、undefined,浏览器中的 document.all 还有历史例外。空数组和字符串 "0" 都是真值;条件判断与 == false 使用不同转换步骤,结果不能靠肉眼类比。
new Boolean(false) 得到的是对象,在 if 中仍然是真值;Boolean(false) 才返回布尔原始值。包装对象与原始值在 typeof、宽松相等和条件判断中表现不同,拿包装对象保存开关状态容易造成反直觉的分支结果。
JavaScript 字符串按 UTF-16 码元存储,部分表情符号占两个码元,因而 "😀".length 为 2。charAt 和 charCodeAt 按码元读取,codePointAt 可读完整码点;截取用户看到的字符时还要考虑组合表情等字素簇。
[] == ![] 为真,是因为 ![] 先得到 false,随后宽松相等又把两边转换为数字零;[] == [] 为假,则是两个不同对象按引用比较。类似表达式必须按转换顺序逐步推导,不能把“数组等于 false”当作通用结论。
用 == 比较字符串和数字时,JS 会把字符串转成数字再比较。空字符串变 0、带字母的变 NaN。搞清 ToNumber 的转换规则和 parseInt 的区别,就能避开最常见的隐式转换陷阱。
null == undefined 为 true 是 JS 规范里的特殊规则,它们只和彼此宽松相等,不会匹配 0 或 false。搞清这条规则就能理解 value == null 这个常见写法的原理和边界。
1e3 就是 1 乘以 10 的 3 次方,等于 1000。指数表示法在词法层面解析,不是运行时乘法。搞清它和 parseInt、Number 的关系,以及什么时候引擎会自动用科学计数法输出。
正数除以零得到 Infinity,负数除以零得到 -Infinity。Infinity 是合法的 number 类型值,参与算术有自己的规则。搞清哪些运算会产生它、怎么检测它、以及 JSON 序列化时的坑。
typeof 返回字符串,适合判断原始类型但对 null 返回 object。instanceof 检查原型链,适合判断对象是哪个构造函数的实例但对原始值无效。搞清两者的适用范围和常见陷阱。
Number 基于 IEEE 754 双精度浮点,安全整数范围是正负 9007199254740991。超过后相邻整数会被舍入成同一个值。搞清为什么是 2 的 53 次方减 1,以及什么时候该用 BigInt。
JS 的逻辑或和逻辑与返回的不是 true 或 false,而是两个操作数中的某一个原值。搞清短路求值的规则和返回值的类型,就能理解默认值写法和条件执行的原理。
NaN 与自身用 === 比较也为 false,这是 IEEE 754 的相等规则。Number.isNaN 不做类型转换,全局 isNaN 会先转换输入;数组的 indexOf 找不到 NaN,includes 却能找到,因为两种方法使用的相等算法不同。
IEEE 754 保留了正零和负零的区别。JS 里 -0 === 0 为 true,但 Object.is 能区分,1 除以 -0 得到 -Infinity。搞清负零怎么产生、怎么检测、以及 JSON 序列化会丢失符号。
?? 只在左侧为 null 或 undefined 时启用默认值,|| 遇到零、false 或空字符串也会回退,因此处理合法业务值时结果不同。?? 与 &&、|| 直接混用有语法限制,??= 只在空值时赋值,可选链常与它一起处理缺失字段。
JavaScript 的 Number 采用 IEEE 754 双精度格式,包含符号位、偏移指数和有效数字尾数。指数影响可表示范围,尾数限制精度;隐含的最高有效位解释了安全整数边界,非规格化数则关系到最小正数和下溢。
全局 isFinite 会先做数值转换,因此字符串 "42"、null 与空字符串都可能得到 true;Number.isFinite 只接受真正的有限 number。表单输入应先明确解析规则,再检查是否有限;BigInt 和无穷大也不能混为一谈。
parseInt 会从字符串开头读取可识别的整数部分,碰到不合法字符就停止,一个数字都读不到才是 NaN。radix 指定进制,0x 前缀有特殊处理;把它直接传给 map 或用于极小浮点数时,隐式参数和科学计数法会制造意外结果。
Number() 和一元加号要求输入整体符合数字转换规则,parseInt 与 parseFloat 则能在前缀合法时提前停止。空串、带单位文本、进制前缀和科学计数法因此会得到不同结果;处理用户输入时应先决定是否允许尾部杂质。
toFixed 返回字符串,并按 Number 实际保存的二进制值舍入;十进制的 1.005 不能精确存储,保留两位可能得到 1.00。数值足够大时还会改用指数记法,金额计算应避免把展示格式化当成精确运算。
Number 的 toString(radix) 可把数值表示为 2 到 36 进制字符串,负数输出负号而非补码。小数转换可能无法有限结束,补零又是字符串格式问题;超过 Number 安全整数范围的输入应先保留精度,再考虑用 BigInt 转换。
0.1 和 0.2 不能用有限二进制小数精确表示,Number 存储时发生舍入,相加后会看到 0.30000000000000004。用 Number.EPSILON 比较只适合相应数量级的误差,金额运算通常需整数分或十进制定点方案。
Number.MIN_VALUE 是最小正的非零浮点数,约为 5e-324,和最小安全整数毫无关系。更小的正数会下溢;正负零在 === 下相等、在 Object.is 下不同,除法等运算还能保留或暴露负零符号。
数字分隔符允许在字面量的数字之间插入下划线,十进制、其他进制、BigInt 和指数部分都能用,但不能放在开头、结尾或紧挨小数点。它只是源码书写语法,Number("1_000") 不会按分隔符解析,反而得到 NaN。
typeof 对数组、日期等对象只能给出粗略的 object 结果,Object.prototype.toString.call 可得到 [object Array] 标签。借用基类方法可避开实例自己的 toString;Symbol.toStringTag 却能改写标签,判断数组应使用 Array.isArray。
对象转原始值时,字符串上下文通常先尝试 toString,数字和默认上下文通常先尝试 valueOf;Symbol.toPrimitive 存在时会优先接管。返回对象而非原始值可能触发回退或异常,Date 的默认转换又与一般对象不同。
a = b === c 会先比较 b 与 c,再把布尔结果赋给 a,因为相等比较的优先级高于赋值。连续赋值还涉及结合方向;在 if 条件中少写一个等号、取反位置写错或混入位运算符,都可能让表达式合法却语义错误。
一元加号和 Number() 对常见字符串、布尔值、null、undefined 与对象使用相同的数值转换规则,但对 BigInt 不同:Number(10n) 可显式转换,+10n 会抛 TypeError。它们与 parseInt 的前缀解析也不同,处理输入要避免静默截断。
读取字符串等原始值的方法时,引擎会临时提供对应包装对象,从原型上找到方法,用完不保留这层包装。给原始值添加自定义属性不会形成持久状态;new String 等显式包装对象又有不同的真值与相等行为,null 和 undefined 更不能这样访问。
Object.is 与 === 的差别集中在 NaN 和正负零;includes、Set、Map 使用的 SameValueZero 则把 NaN 视为相同,也不区分正负零。React 的状态比较采用 Object.is,遇到负零或 NaN 更新时,不能直接拿 === 的结果推断。
加号会先把操作数转为原始值,只要其中一个是字符串就拼接,否则再做数值相加。因此 1 + 2 + "3" 与 "1" + 2 + 3 的结果不同;对象、数组及 null 参与时还要看转换顺序,减号却不会执行字符串拼接。
JavaScript 字符串用反斜杠表示换行、引号和反斜杠本身;十六进制与 Unicode 转义还能写出指定码点。四位 Unicode 转义放不下部分表情符号,需用花括号码点形式;正则表达式和 Windows 路径经过多层字符串解析时尤其容易多写或漏写反斜杠。
字符串下标可以读取 UTF-16 码元,却不能像数组下标那样改写字符;严格模式下给只读索引赋值还会报错。charAt、at 与方括号对越界和负索引的处理不同,表情符号被下标切开时还可能只取到代理项的一半。
字符串 length 统计 UTF-16 码元,常用汉字通常占一个码元,许多表情符号占两个,家庭表情等组合序列还会占更多。展开运算符按码点遍历,Intl.Segmenter 才能更贴近用户看到的字素簇;昵称截断要选对单位。
预组合的 é 与 e 加组合重音符视觉相同,底层码点不同,直接用 === 比较会失败。normalize 可转换成 NFC、NFD 等形式以便搜索和去重;兼容正规化还可能改变字符含义,用户名校验与文件名处理要谨慎选用。
slice、substring、substr 在普通正数范围下看起来相似,负数和起止颠倒时却明显不同:slice 可从末尾计数,substring 会把负数当零并交换边界,substr 的第二个参数是长度。新代码优先选语义清楚的 slice,避免依赖遗留方法。
String 包装对象的 valueOf 与 toString 都返回内部原始字符串,但对象转换时先调用谁取决于上下文。模板插值偏向字符串转换,加号通常采用默认转换;重写其中一个方法后,相同对象在两种表达式中可能显示不同结果。
Symbol 属性键不会与同名字符串键冲突,也不会出现在 Object.keys、for...in 或 JSON.stringify 的普通对象属性结果中。Reflect.ownKeys 能读到它,展开运算符与 Object.assign 还会复制可枚举 Symbol 键,所以隐藏不等于私有。
每次调用 Symbol() 都创建新的唯一原始值,描述文字相同也不会相等;Symbol.for 则通过全局注册表复用键。Symbol 不能用 new 创建,隐式转成字符串也会失败;作为对象属性键时它能避免普通字符串命名冲突。
模板插值会按字符串上下文转换对象,加号拼接通常先走默认原始值转换;自定义 valueOf 和 toString 后,两种写法可能得到不同文本。Symbol 的隐式转换还有特别限制,嵌套模板与标签模板也不等同于普通字符串拼接。
JSON.stringify 遇到 undefined、函数或 Symbol 时,在对象属性里会省略,在数组位置会变成 null,作为顶层值则返回 undefined。NaN、Date、BigInt 与循环引用又有不同处理结果;replacer 能控制部分字段,但不能直接解决所有不可序列化值。
对象参与运算时,Symbol.toPrimitive 若存在会优先接收 number、string 或 default 提示,决定对象如何变成原始值。没有该方法时才按顺序尝试 valueOf 和 toString;Date 在默认提示下偏向字符串,因此日期对象与数字相加可能得到拼接结果。
字符串是不可变的原始值,截取、替换等方法返回新字符串;数组是对象,push、sort、splice 等方法会直接改动自身。两者在传参和 React 状态更新中容易被混淆,数组若需保留原值可用 toSorted、toSpliced 等非修改方法。
typeof 对数组、Date、Map、正则、包装对象和 null 都返回 object,无法单凭结果细分对象种类。判断空值要直接与 null 比较,判断数组用 Array.isArray;未声明变量与暂时性死区中的变量对 typeof 也并非同样安全。
undefined 常代表尚未赋值或缺少参数,null 通常是代码主动放入的空值。两者在 typeof、宽松相等、数字转换、默认参数和 JSON 序列化中有不同结果;接口字段需要表达“缺失”还是“明确清空”时,这个区别尤其重要。
Number 包装对象的 valueOf 返回内部保存的数字;自定义对象参与加减法或比较时,valueOf 也可能成为取得原始值的第一步。若 Symbol.toPrimitive 存在,它会优先接管转换;返回的仍是对象时还可能继续尝试 toString 或抛错。
void 会先求值右侧表达式,再把整个表达式的结果固定为 undefined。void 0 曾被用来取得可靠的 undefined,如今也常用来显式丢弃回调返回值或标记不等待的 Promise;它不会吞掉 Promise 的未处理拒绝。