我们如何用Javascript重写存储在数组中的函数



我正在用javascript编写一个语言解析器,并采用了构造嵌套函数的函数方法来提高可伸缩性。

我的代码目前看起来像下面的

const parseDate = (query) => {
return query.match(/stuff/)
}
const parseAuthor = (query) => {
return query.match(/stuff/)
} 
const parseTags = (query) => {
return query.match(/stuff/)
}
//...
const transform = (match) => {
const target = match?.[0]?.trim()
// some more post processing that modifies "target"
return target;
}
const parsers = [parseAuthor, parseTags, parseReviewer, parseSearch, parseAfter].map(parser => {
return (query) => transform(parser(query))
})

我正在尝试将数组中的函数parseDate与另一个函数交叉,该函数将获取parseDate的输出并将其转换为ISO格式。

parsers[4] = (query) => {
return new Date(parsers[4](query)).toISOString();
};

这导致了RangeError: Maximum call stack size exceeded,我认为这是因为javascript正在构建一个递归函数,该函数接受旧parseDate函数的输出,并通过新函数再次运行它,该输出到同一个函数。。。等等

那不是我想要的。我只想用一个新的函数来替换parsers[4]函数。

我试着复制这个功能,但没有成功,得到了同样的错误

parsers[4] = (query) => {
return new Date(parsers[4].bind({})(query)).toISOString();
};

我们到底该怎么做?

由于您正在向列表中添加一个函数(在分配时不会自动计算(,因此在执行时,对parsers内第4个索引处对象的引用将指向解析器的当前状态,这使其成为自引用(导致无限递归循环,导致堆栈大小爆炸(。

如果您有对parseDate的引用,您可以简单地使用它本身,或者在使用它之前将parsers[4]中的当前对象存储在一个临时变量中:

var temp = parsers[4]
parsers[4] = (query) => {
return new Date(temp(query)).toISOString();
};

您试图提供的任何一种形式的定义的问题都是您定义了一个无条件调用自身的函数parsers[4]。试着通读一遍,假装你是Javascript引擎——为了计算parsers[4](query),你需要首先无限地计算parsers[4](query)等等。(.bind的使用并不能完全解决这一问题——是的,你正在对同一函数对象进行新的引用,但为了执行该函数,它仍然需要引用你定义的同一函数。(

至于如何解决,我能想到很多方法。也许最简单的方法是制作一个临时副本:

const oldParser = parsers[4];
parsers[4] = (query) => {
return new Date(oldParser(query)).toISOString();
};

你也可以把它写成";装饰师";或功能转换:

const transformFunction = (func) => (query) => {
return new Date(func(query)).toISOString();
};
parsers[4] = transformFunction(parsers[4]);

请注意,上面的内容不会导致无限递归,甚至根本不会导致任何递归,尽管它看起来可能与原始内容相似。这是因为在执行时,原始函数引用了parsers[4],而在定义新函数时,它只引用了一次。它将简单地在封面下存储对旧函数(我将其标记为func(的引用,就像第一种方法一样。

这样做的一个优点是,如果需要以相同的方式转换整个数组,那么可以像parsers = parsers.map(transformFunction)一样简单地进行转换。

最新更新