【问题标题】:Found non-callable @@iterator when extending Array扩展数组时发现不可调用的@@iterator
【发布时间】:2021-09-04 04:39:33
【问题描述】:

当我尝试扩展 Array 类时:

class Collection extends Array {
    constructor(array){
        super(...array)
    }

    getTimes(){
        return this.map(value=> value.getTime())
    }

    min(){
        return new Date(Math.min(...this.getTimes()))
    }
}



const array = ['2000/02/01','2000/02/02']
const dates = array.map(value => new Date(value))

const test = new Collection(dates)

console.log(test[Symbol.iterator]); //Iterator exists
console.log(test.min()); ///Error: Found non-callable @@iterator

由于某种原因出现此错误,但是当我们这样做时:

class Collection extends Array {
    static from(array){ /// Using a Factory instead of a constructor
        return new Collection(...array)
    }
...
...
}

const test = Collection.from(dates)

console.log(test.min()); //returns the Date

一切正常。为什么构造函数与迭代器有问题??

【问题讨论】:

  • 您确定.from() 有效吗?我仍然面临.from() 方法的问题:jsfiddle.net/3p6y81dc
  • @NickParsons 我能够让 Array.from() 工作。你已经把事情的顺序调换了。试试这个:jsfiddle.net/9eqwg1nd
  • @ScottyJamison 啊,好的,谢谢! :)

标签: javascript arrays extends


【解决方案1】:

问题是 array.map() 返回一个 new 数组,并且会调用你的构造函数来这样做。

可以在此处看到此行为:

class CustomArray extends Array {
  constructor(...args) {
    super(...args)
    console.log('constructor called')
  }
}

const myArray = new CustomArray(1, 2, 3)
console.log('call .map()')
myArray.map(x => x)

因为您的自定义构造函数不采用与默认数组构造函数相同的参数,所以 .map() 函数在尝试使用它时会出错。下面的实现对你有用,因为它改变了构造函数来接受 .map() 期望的参数。

class Collection extends Array {
    constructor(...args){
        super(...args)
    }

    getTimes(){
        return this.map(value=> value.getTime())
    }

    min(){
        return new Date(Math.min(...this.getTimes()))
    }
}



const array = ['2000/02/01','2000/02/02']
const dates = array.map(value => new Date(value))

const test = new Collection(...dates)

console.log(test.min());

也许这不是你想要的,因为也许你想改变构造函数的行为。事实证明,Javascript 为数组提供了一个特殊的species symbol,专门用于帮助创建数组子类,就像你正在做的那样。你可以给这个物种符号一个不同的构造函数来使用,像.map()这样的函数将使用这个物种符号提供的任何东西。

class Collection extends Array {
    constructor(array){
        super(...array)
    }
    
    static [Symbol.species](...args) {
        return new Collection(args)
    }

    static get [Symbol.species]() {
      return function(...args) {
        return new Collection(args)
      }
    }

    getTimes(){
        return this.map(value=> value.getTime())
    }

    min(){
        return new Date(Math.min(...this.getTimes()))
    }
}



const array = ['2000/02/01','2000/02/02']
const dates = array.map(value => new Date(value))

const test = new Collection(dates)

console.log(test.min());

这使用了许多技巧,因此我建议尽可能避免使用此特定解决方案。但是,基本思想是应该将物种属性设置为一个类,然后通过 .map() 等函数对其进行更新。在上面的示例中,我将 species 属性设置为 getter(否则我无法使其正常工作 - 我认为它与默认值的设置方式有关)。这个 getter 返回了一个函数,在这种情况下它就像一个类(你不必使用 class 关键字来创建一个类)。当新建一个普通函数时,我只返回一个正确构造的 Collection 类的实例(即使这个匿名函数是正在构造的东西,它也可以返回完全不同的东西)。

【讨论】:

    猜你喜欢
    • 2019-11-08
    • 2022-12-07
    • 2021-12-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多