我们如何覆盖存储在 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)
.
一样简单地进行转换
我正在 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)
.