对象的键会怎样转换?属性顺序和 delete 有什么规则?

先理解原理和示例,再练习完整口述与追问。

普通对象的键只有字符串和 Symbol。数字、对象作键时会先转成字符串,所以 1 和 '1' 是同一个键,两个不同对象经常都变成 [object Object]。现代引擎枚举自有属性时,先按升序给出整数索引,再按插入顺序给出其他字符串,最后是 Symbol。delete 删除自有属性,删数组元素会留下空位,不改变 length。

obj[key] = value 时,除 Symbol 外的键都走 ToString。obj[1] = 'a'; obj['1'] = 'b' 只留下一个键。obj[{}] 和 obj[{ id: 1 }] 都使用 '[object Object]',后写的覆盖先写的。需要用对象作键,应该用 Map。Map 的键用 SameValueZero,对象按引用区分,+0 和 -0 算同一个键。

自己的属性顺序是语言现在规定的:像数组下标的字符串(规范里的整数索引)按数值升序,其余字符串按创建顺序,Symbol 按创建顺序。Object.keys、Object.entries 和 JSON.stringify 都遵循字符串键的这部分顺序,但 JSON.stringify 不包含 Symbol。这个顺序只针对普通自有属性,不要依赖 for...in 的继承属性顺序做协议。

delete obj.key 删除自有、可配置的属性,删不存在的属性返回 true。数组 delete arr[1] 删除这个下标,length 不变,中间变成空位;真正缩短数组是给 length 赋值,或用 splice。空位和值为 undefined 的元素在 map、for...in 里行为不同,见宝典 80。

js
const bag = {}
bag[1] = 'a'
bag['1'] = 'b'
bag[{}] = 'c'
bag[{ id: 1 }] = 'd'
console.log(bag) // { '1': 'b', '[object Object]': 'd' }

const order = { 2: 'a', b: 'b', 1: 'c' }
console.log(Object.keys(order)) // ['1', '2', 'b']

const list = ['x', 'y', 'z']
delete list[1]
console.log(1 in list, list.length) // false 3

整数键会被插到其他字符串前面,后写入的 1 仍出现在早写入的 'b' 前面,不能把对象当严格的插入序列表。delete 成功不代表数组变短。频繁 delete 对象属性还会让 V8 的隐藏类变慢,热路径上用赋值 undefined 或 Map。

对象键只有字符串和 Symbol。用数字或对象作下标会先转成字符串,1 和 '1' 是同一个键,两个普通对象通常都变成 [object Object],对象当键要用 Map。枚举时整数索引按数值升序,其他字符串和 Symbol 按创建顺序。delete 删的是可配置的自有属性;删数组的某一项只会留下空位,length 不变,要去掉元素得用 splice 或修改 length。