如何将变量添加到 pre-defined 函数的范围?
How do you add variables to the scope of a pre-defined function?
标题有点说明了一切。在 JavaScript 中,您如何将变量注入到您无法控制的函数(例如回调)的 inner-scope 中?这通常在各种 JavaScript 框架中完成,其中 Jest 为您提供 describe
、it
、beforeAll
等,所有这些都在 body 回调中Jest 没有明确的知识。 Cucumber 使用共享的 World
实例更干净地做到了这一点,并且还提供了为该 object 提供自定义构造函数的控件,但如果我想添加 logger
object到使用我的库的任何东西的执行范围,是将它添加到 global
的唯一答案,还是有办法将值限制在被调用函数及其回调的范围内?
编辑:这是我认为笨拙的解决方案,但它在技术上实现了目标。如果这是公认的解决方案,我会继续使用它,但我认为必须有更多 sophisticated/clean 的方法来做到这一点。
function a(opts, callback) {
Object.assign(global, opts);
const assignedKeys = Object.keys(opts).filter(k => Object.prototype.hasOwnProperty.call(global, k));
for(const k in opts) {
console.log('%s: %s', k, opts[k]); // val: 5
callback(); // Can I see it? true
}
console.log('Before clean-up', assignedKeys.map(k => `${k}: ${global[k]}`).join(', '));
// Before clean-up val: 5
for(const k in opts) {
delete global[k];
}
console.log('After clean-up', assignedKeys.map(k => `${k}: ${global[k]}`).join(', '));
// After clean-up val: undefined
}
function b() {
console.log('Can I see it?', !!val);
// Can I see it? true
}
a({ val: 5 }, b);
可能不是最优雅的,但您可以 toString()
一个函数并利用 Function
构造函数注入变量。
这是您示例的 dumbed-down 版本:
function inject(cb, hw = "Hello, world!") {
const injection = `const hw = "${hw}";`;
const fn = new Function(`${injection} return (${cb.toString()})();`);
const result = fn();
if(result)
console.log(result);
// cb(); <-- error: hw is undefined
}
inject(() => {
console.log(hw);
});
inject(function() {
console.log(hw);
}, "Hello, Whosebug!");
inject(function test() {
console.log(hw);
return 5;
});
是的,使用全局变量是这方面的标准做法。没有办法改变你不控制的函数的范围,不可能向它“注入”新的局部变量。您唯一可以做的就是将变量放在共享范围内(并假设该函数不会隐藏它们),这通常是全局范围。
像 React 或 Jest 这样的框架可以避免这种做法,因为它们是 编写所有代码的全局框架,因此开发人员知道此功能并且不会得到被“魔法”变量搞糊涂了——而且只有很少的 well-known 个变量。不过,仍然需要告知工具有关这些全局变量的信息。这是一个 trade-off,但有时它会导致更简洁的代码是值得的。
请注意,与您的 a
函数不同,此类全局变量不是动态的而是静态声明的,它们不关心冲突和清理。如果您使用这样的框架,您将知道 up-front 有哪些全局变量,并且不会编写干扰它们的代码。
如果你真的想动态地将任意值“注入”到被调用的函数中,有一个标准技术:使用参数!您调用的函数需要声明参数才能使用它们,但这是正确的干净解决方案,并且会真正将变量限制在您想要的范围内。
标题有点说明了一切。在 JavaScript 中,您如何将变量注入到您无法控制的函数(例如回调)的 inner-scope 中?这通常在各种 JavaScript 框架中完成,其中 Jest 为您提供 describe
、it
、beforeAll
等,所有这些都在 body 回调中Jest 没有明确的知识。 Cucumber 使用共享的 World
实例更干净地做到了这一点,并且还提供了为该 object 提供自定义构造函数的控件,但如果我想添加 logger
object到使用我的库的任何东西的执行范围,是将它添加到 global
的唯一答案,还是有办法将值限制在被调用函数及其回调的范围内?
编辑:这是我认为笨拙的解决方案,但它在技术上实现了目标。如果这是公认的解决方案,我会继续使用它,但我认为必须有更多 sophisticated/clean 的方法来做到这一点。
function a(opts, callback) {
Object.assign(global, opts);
const assignedKeys = Object.keys(opts).filter(k => Object.prototype.hasOwnProperty.call(global, k));
for(const k in opts) {
console.log('%s: %s', k, opts[k]); // val: 5
callback(); // Can I see it? true
}
console.log('Before clean-up', assignedKeys.map(k => `${k}: ${global[k]}`).join(', '));
// Before clean-up val: 5
for(const k in opts) {
delete global[k];
}
console.log('After clean-up', assignedKeys.map(k => `${k}: ${global[k]}`).join(', '));
// After clean-up val: undefined
}
function b() {
console.log('Can I see it?', !!val);
// Can I see it? true
}
a({ val: 5 }, b);
可能不是最优雅的,但您可以 toString()
一个函数并利用 Function
构造函数注入变量。
这是您示例的 dumbed-down 版本:
function inject(cb, hw = "Hello, world!") {
const injection = `const hw = "${hw}";`;
const fn = new Function(`${injection} return (${cb.toString()})();`);
const result = fn();
if(result)
console.log(result);
// cb(); <-- error: hw is undefined
}
inject(() => {
console.log(hw);
});
inject(function() {
console.log(hw);
}, "Hello, Whosebug!");
inject(function test() {
console.log(hw);
return 5;
});
是的,使用全局变量是这方面的标准做法。没有办法改变你不控制的函数的范围,不可能向它“注入”新的局部变量。您唯一可以做的就是将变量放在共享范围内(并假设该函数不会隐藏它们),这通常是全局范围。
像 React 或 Jest 这样的框架可以避免这种做法,因为它们是 编写所有代码的全局框架,因此开发人员知道此功能并且不会得到被“魔法”变量搞糊涂了——而且只有很少的 well-known 个变量。不过,仍然需要告知工具有关这些全局变量的信息。这是一个 trade-off,但有时它会导致更简洁的代码是值得的。
请注意,与您的 a
函数不同,此类全局变量不是动态的而是静态声明的,它们不关心冲突和清理。如果您使用这样的框架,您将知道 up-front 有哪些全局变量,并且不会编写干扰它们的代码。
如果你真的想动态地将任意值“注入”到被调用的函数中,有一个标准技术:使用参数!您调用的函数需要声明参数才能使用它们,但这是正确的干净解决方案,并且会真正将变量限制在您想要的范围内。