我们如何覆盖存储在 Javascript 数组中的函数?

How do we overwrite a function stored in an array in 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 并不能解决所有问题 - 是的,您正在对同一函数对象进行新引用,但为了执行该函数,它仍然需要引用您正在定义的同一函数。 )

至于如何解决,我能想到的方法有好几种。可能最多 simple-minded - 但如果你只做一次就足够了 - 是制作一个临时副本:

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],而 this 在定义新函数时只引用了一次。它会在幕后简单地存储对旧函数(我标记为 func)的引用,就像第一种方法所做的那样。

这样做的一个好处是,如果您需要以相同的方式转换整个数组,那么您可以像 parsers = parsers.map(transformFunction).

一样简单地进行转换