为何JavaScript空数组调用pop时会重复将length属性设为0?
为什么数组length属性已经为0时,调用pop方法仍要再次将其设置为0
ECMAScript 2023规范中pop方法定义的前三个执行步骤如下:
1. Let O be ? ToObject(this value). 2. Let len be ? LengthOfArrayLike(O). 3. If len = 0, then a. Perform ? Set(O, "length", +0𝔽, true). b. Return undefined.
这一步看似冗余,实际是为了覆盖所有类数组调用场景的语义一致性,根本不是多余设计,核心原因有两点:
pop是通用类数组方法,不限制调用者必须是原生数组
这个方法从设计之初就支持被任意类数组对象调用,只要求对象存在可读取的length属性即可。和原生数组的length由引擎底层特殊管控不同,普通类数组的length可以是任意形式:可能是带getter/setter的访问器属性,可能是继承自原型链的属性,甚至可能是被代理拦截的属性。哪怕读取length得到的值是0,也不代表对length的写入操作无意义——比如带setter的类数组,哪怕length读出来恒为0,这一步写入也会触发setter逻辑,保证方法行为符合预期。
简单测试示例:const fakeArray = { get length() { return 0 }, set length(val) { console.log('触发length写入,值为', val) } } // 调用pop时,哪怕读取length返回0,依然会触发上面定义的setter Array.prototype.pop.call(fakeArray)- 强制修正length为规范要求的合法值+0
JS的数字类型存在+0和-0的区分,二者做相等比较时返回true,但语义上存在差异。规范中LengthOfArrayLike操作读取长度时,会把读取到的-0通过ToLength规则转换为+0返回,也就是说如果类数组的length实际存储的是-0,第二步拿到的len依然是0,会进入这个分支。这一步执行Set操作把length设为+0,就是为了把不符合规范的-0覆盖掉,保证所有类数组经过pop操作后,length一定是合法的非负整数+0。
除此之外,如果类数组本身没有自有的length属性,是从原型链上继承到值为0的length,这一步写入也会给对象本身添加值为+0的自有length属性,符合数组方法操作后length为对象自有属性的一致语义。
内容的提问来源于stack exchange,提问作者Matt Kleinsmith
相关产品推荐
相关产品推荐

