在 javascript 的 Array 子类中使用 .splice 方法?

Using .splice method in subclass of Array in javascript?

我正在尝试创建 javascript 数组的子 class。我想用数组类型的参数启动 subclass 并添加一个方法来从数组中删除一个元素 (subclass).

我的代码如下所示:

class CustomArray extends Array {
        
  constructor(array) {
    console.log('Initiating array:', array)
    super(...array);
  }

  remove(element) {
    let index = this.indexOf(element);
    if (index > -1) {
      return this.splice(index, 1);
    } 
    return [];
  }

}

var a = ['a', 'b', 'c', 'd', 'e'];

var list = new CustomArray(a)
console.log('list:', list);
console.log('remove:', list.remove('c'));
console.log('list:', list);

问题是,当我调用 .splice() 时,它会从数组中删除元素,还会删除 returns 数组中已删除的元素,实际上它 returns 是我的子 class CustomArray,应该以数组类型的参数启动,但是.splice()以整数类型的参数启动它。

这是我认为调用 .splice():

时发生的情况的示例

假设我们有 CustomArray class 的实例 list 以参数 ['a','b','c','d','e'] 启动,然后我们调用方法 list.remove('c')。 (就像在代码片段中一样)。
CustomArray class 的方法 remove 检查 c 的索引在数组 ['a','b','c','d','e'] 中,即 2 然后调用 method this.splice(2,1)这应该删除数组 ['a','b','c','d','e'] 中索引 2 处的 1 个元素。方法 splice 从数组中删除元素 c,但也 return 类似 new CustomArray(1) 的东西,因为从数组中删除了一个元素,因此它尝试创建长度为 1 的数组,但是失败,因为 class CustomArray 是期望数组。

我想阻止 splice 方法启动 CustomArray class 的新实例,而不是 return 普通数组(Array 对象的实例)。

Jsbin link.

I want to prevent splice method from initiating a new instance of CustomArray class and instead return normal array (an instance of Array object).

然后您需要创建一个不同名称的不同方法。 splice 的语义是 clearly and precisely defined;他们形成了 Array 类型的合同。让您的 CustomArray 违反该合同将意味着它不再是 Array,而是 array-like,不应扩展 [=13] =].

既然你的方法被称为remove,那很好;如果你想要remove到returnArray,而不是CustomArray,你只需要自己实现逻辑:

remove(element) {
  let index = this.indexOf(element);
  if (index > -1) {
    const newLength = this.length - 1;
    while (index < newLength) {
        this[index] = this[index + 1];
        ++index;
    }
    this.length = newLength;
    return [element];
  } 
  return [];
}

或者,当然,使 CustomArray 的构造函数在被各种 Array.prototype 方法调用时正确工作。 (除了用 console.log 记录您不期望的内容外,您在问题中遇到的那个工作得很好。)

可能有 splice return 一个标准数组——所以不用它调用你的构造函数。它是通过更改自定义 class 的 @@species property 来确定将使用哪个构造函数。但请注意,这不仅会影响 splice,还会影响 所有 其他会创建新实例的方法,包括 mapfilterslice, ...

您可以通过覆盖相应的静态 getter:

来更改 @@species 属性

class CustomArray extends Array {
  static get [Symbol.species]() { return Array; } // <-----------

  constructor(array) {
    console.log('Initiating array:', array)
    super(...array);
  }

  remove(element) {
    let index = this.indexOf(element);
    if (index > -1) {
      return this.splice(index, 1); // Now calls Array constructor, not CustomArray
    } 
    return [];
  }

}

var a = ['a', 'b', 'c', 'd', 'e'];

var list = new CustomArray(a)
console.log('list:', list);
console.log('remove:', list.remove('c'));
console.log('list:', list);

// Some examples of effects on other methods
console.log(list.map(x => x) instanceof CustomArray); // false
console.log(list.filter(x => 1) instanceof CustomArray); // false
console.log(list.slice() instanceof CustomArray); // false
console.log(list.concat() instanceof CustomArray); // false
// Other methods, that do not return a new instance, are not affected:
console.log(list.reverse() instanceof CustomArray); // true
console.log(list.sort() instanceof CustomArray); // true