El título lo dice todo. En JavaScript, ¿cómo inyectaría una variable en el ámbito interno de una función que no controla (una devolución de llamada, por ejemplo)? Esto se hace comúnmente en varios marcos de JavaScript, donde Jest le proporciona describe , it , beforeAll , etc., todo dentro del cuerpo de las devoluciones de llamada de las que Jest no tiene conocimiento explícito. Cucumber hace esto de manera más limpia, usando la instancia World compartida, y también proporciona controles para proporcionar un constructor personalizado para ese objeto, pero si quisiera agregar un objeto logger al ámbito de ejecución de cualquier cosa que use mi biblioteca, es la única respuesta para agregar a global , o ¿hay alguna manera de mantener los valores dentro del alcance de la función llamada y sus devoluciones de llamada?
Editar: esto es lo que veo como una solución torpe, pero técnicamente logra el objetivo. Si esta es la solución aceptada, continuaré con ella, pero pensé que tenía que haber una forma más sofisticada/limpia de hacerlo.
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);Probablemente no sea el más elegante, pero podría toString() en una función e inyectar la variable, utilizando el constructor de Function .
Aquí hay una versión simplificada de su ejemplo:
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, StackOverflow!"); inject(function test() { console.log(hw); return 5; });Sí, usar variables globales es la práctica estándar para esto. No hay forma de alterar el alcance de una función que no controla, es imposible "inyectar" nuevas variables locales en ella. Lo único que puede hacer es colocar las variables en un ámbito compartido (y asumir que la función no las sombrea), que normalmente es el ámbito global.
Los marcos como React o Jest se salen con la suya porque son el marco global para el que se escribe todo el código, por lo que los desarrolladores conocen esta función y no se confundirán con las variables "mágicas", y son muy pocas, bueno. variables conocidas. Sin embargo, aún se debe informar a las herramientas sobre estos globales. Es una compensación, pero a veces vale la pena cuando conduce a un código mucho más breve.
Tenga en cuenta que, a diferencia de su función a , tales variables globales no son dinámicas sino declaradas estáticamente, y no se preocupan por las colisiones y la limpieza. Si usa un marco de este tipo, sabrá por adelantado qué globales hay y no escribirá código que interfiera con ellos.
Si realmente desea "inyectar" dinámicamente valores arbitrarios en una función llamada, existe una técnica estándar: ¡ use argumentos ! La función a la que llama necesita declarar parámetros para usarlos, pero esta es la solución limpia adecuada y realmente mantendrá las variables dentro del alcance que desee.