“ array [array.length-1] = array.pop()”会产生未定义的行为吗?

时间:2019-11-27 10:49:57

标签: javascript arrays node.js

我一直在尝试实现一个方法,该方法将一个数组和一个索引作为输入,将给定数组的最后一个元素复制到给定索引处的条目中,然后截断最后一个元素(即,设置数组缩短一)。

以下是我建议执行的功能:

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 ]

我的看法是,这里有两个选择:

  1. 在计算表达式array.pop()之前执行语句array.length - 1
  2. 在计算表达式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中是否还存在未定义行为之类的东西?

非常感谢您一直阅读:)

2 个答案:

答案 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