我一直在尝试实现一个方法,该方法将一个数组和一个索引作为输入,将给定数组的最后一个元素复制到给定索引处的条目中,然后截断最后一个元素(即,设置数组缩短一)。
以下是我建议执行的功能:
function swapWithLastAndRemove(array, index) {
array[index] = array.pop();
}
然后我测试了此函数,以确保该函数可以在任何给定的索引上工作:
for (let index = 0; index < 5; index++) {
var array = [0, 1, 2, 3, 4];
swapWithLastAndRemove(array, index);
console.log(array);
}
我发现它对除最后一个索引外的所有索引均正常工作
[ 4, 1, 2, 3 ]
[ 0, 4, 2, 3 ]
[ 0, 1, 4, 3 ]
[ 0, 1, 2, 4 ]
[ 0, 1, 2, 3, 4 ]
换句话说,对于最后一个元素,它将其从数组中弹出,然后将其重写到数组中。
让我印象深刻的是这一行:
array[index] = array.pop();
在最后一个元素上执行时,相当于以下行:
array[array.length - 1] = array.pop();
不可能array
的内容等于[ 0, 1, 2, 3, 4 ]
。
我的看法是,这里有两个选择:
array.pop()
之前执行语句array.length - 1
array.pop()
之后执行语句array.length - 1
在第一种情况下,数组的内容将发生如下变化:
[ 0, 1, 2, 3, 4 ] // initial state
[ 0, 1, 2, 3 ] // after popping 4
[ 0, 1, 2, 4 ] // after assignment
在第二种情况下,它应该引发某种内存访问冲突,因为当数组的长度为时,将尝试写入数组的第五个条目(array[4]
)。只有4个条目。
我知道NodeJS可能会应用某种内存管理方案,该方案将以某种方式允许它完成而不会出现“数组索引超出范围”的异常,但是我仍然不明白为什么它会让该操作“摆脱”它”,而且,结果为何如此。
第array[array.length - 1] = array.pop()
行是否可能是未定义的行为?
在Javascript中是否还存在未定义行为之类的东西?
非常感谢您一直阅读:)
答案 0 :(得分:2)
最后一个索引的结果为[0, 1, 2, 3, <empty>, 4]
,而不是[ 0, 1, 2, 3, 4 ]
:
function swapWithLastAndRemove(array, index) {
array[index] = array.pop();
}
var array = [0, 1, 2, 3, 4];
swapWithLastAndRemove(array, 5);
console.log(array);
执行顺序在这里实际上不必担心,因为index
是常量-参数index
从未重新分配,因此传递给swapWithLastAndRemove
的数字将始终是index
,将其分配给弹出项。考虑到这样做会带来不必要的混乱,更容易记住传递的index
永远不会被重新分配。
这是怎么回事:
(1)弹出数组的最后一项(4
),导致数组变成[0, 1, 2, 3]
(2)传入的index
为5,因此array[5]
设置为4
。该操作等效于:
const arr = [0, 1, 2, 3];
arr[5] = 4;
console.log(arr);
这将导致生成稀疏数组-索引4处不再有任何项目。这不是不确定的行为,只是奇怪,因为稀疏数组很奇怪。
甚至在Javascript中是否存在未定义的行为?
是的,请参见here。
答案 1 :(得分:2)
第二个选项:语句
array.pop()
在表达式之后执行array.length - 1
被评估
这确实是发生了什么。 JS评估始终是从左到右。它计算array.length - 1
到数组的4
的索引中,然后从数组中弹出最后一个元素,然后将该元素分配给索引4。
在第二种情况下,它应该引发某种内存访问冲突,因为当数组的长度为时,将尝试写入数组的第五个条目(
array[4]
)。只有4个条目。
否,JS中没有内存访问冲突。它只是再次创建一个新属性,并相应地更改数组的长度。这与代码中发生的事情一样
const array = [0, 1, 2, 3, 4];
const element = array.pop();
console.log(JSON.stringify(array)); // [0,1,2,3]
array[4] = element;
console.log(JSON.stringify(array)); // [0,1,2,3,4]
类似地,填充数组使用相同的功能:
const array = [];
for (let i=0; i<5; i++)
array[i] = i; // no memory access violation
console.log(array.length); // 5