The black magic of eval loses the sphere
disclaimer I do not support the use of eval, and I do not use it when writing code.
I am playing out the logic in a legacy project and was running into some very strange behavior with eval and would like to know under the hood an explanation of why this behavior exists.
var baz = function(cb) {
cb('asdf');
}
function foo(qux, callback) {
setTimeout(function() {
// eval(callback('asdf')); // works
// eval(baz(callback)); // works
// eval(qux + "(" + callback + ")"); // resolve is undefined
});
}
function bar() {
return new Promise((resolve, reject) => {
try {
foo('baz', function(response){
resolve(response);
});
} catch (e) {
reject(e);
}
});
}
function init() {
bar()
.then(response => {
console.log(response)
})
.catch(e => console.log(e));
}
init();
I wrote three examples of different ways to use eval, commented out the function foo
The third example doesn't work. This is the example I'm working on since our legacy code works. I know there are other ways to solve this problem. My solution was to just rewrite the function and not use eval at all.
My question is very precise, why does eval lose scope when passing a function name as a string, but it doesn't lose scope when passing an actual reference to the same function?
Here is a jsfiddle: https://jsfiddle.net/tkcjay4x/6/
source to share
I have to admit, the title scares me a little. If I understand this "black magic", does it make me an evil sorcerer?
When you execute eval(baz(callback))
, baz
calls callback
, and eval
executes the return value baz
, which is undefined
. eval(undefined)
equal eval('undefined')
, which is equal undefined
. So, in this case, using eval
essentially no-op - just doing it baz(callback)
will give the same result.
When you execute eval(qux + "(" + callback + ")")
, it qux + "(" + callback + ")"
is evaluated up to 'baz(function(response){ resolve(response); })'
. When you pass this value to eval, the code tries to access a named function resolve
that is not in the current scope. This is why it doesn't work. If you call a function directly, it can access variables that were present in the scope in which it was defined, but if you convert the function to a string and pass it in eval
, it can only access variables from the current scope.
source to share