Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Javascript `eval()` scope in ES6

I am dynamically creating a function on a Foo1 instance named test. I am creating this function using eval. I would expect that this function would have access to the Foo2 class, however I am getting a ReferenceError: Foo2 is not defined.

I have opened up an issue with Babel about this and can be be found here

If you would like to run the example code yourself, download it from here and follow the instructions in the README.MD to reproduce.

To run:

npm install
npm run start
naviage to localhost:8080

Here is the directory structure for my Minimal, Complete, and Verifiable example in my environment:

root
  - src
    - Foo1.js
    - Foo2.js
  - .babelrc
  - app.js
  - package.json

Foo1.js

import Foo2 from './Foo2.js';

export default class Foo1 {
    constructor() {
        // Isolate the impact of eval within makeFunction
        let makeFunction = text => {
            return eval("(function() { " + text + "})");
        };
        this.test = makeFunction('let foo2 = new Foo2(); foo2.test();');
    }
}

Foo2.js

export default class Foo2 {

    test() {
        console.log('i\'m working!');
    }

}

.babelrc

{
  "presets": ["es2015"]
}

app.js

import express from 'express';
import http from 'http';
import Foo1 from './src/Foo1.js';

const app = express();
const server = http.createServer(app);

app.get('/', (req, res) => {
    let test = new Foo1();
    test.test();

    res.end('bye');
});

server.listen(8080);

package.json

{
    "name": "test",
    "scripts": {
        "start": "./node_modules/babel-cli/bin/babel-node.js ./app.js"
    },
    "dependencies": {
        "http": "*",
        "express": "*",
        "babel-cli": "^6.7.7",
        "babel-core": "^6.7.7",
        "babel-polyfill": "^6.3.14",
        "babel-preset-es2015": "^6.6.0"
    }
}

Now, If I change the Foo2.js class to the previous version of javascript everything works like a charm:

function Foo2() { }

Foo2.prototype.test = function() {
    console.log('i\'m working!');
};

module.exports = Foo2;
like image 659
frankgreco Avatar asked Jul 29 '16 15:07

frankgreco


People also ask

What does eval () do in JavaScript?

The eval() function evaluates JavaScript code represented as a string and returns its completion value. The source is parsed as a script.

Why should you avoid JavaScript eval function when possible?

Malicious code : invoking eval can crash a computer. For example: if you use eval server-side and a mischievous user decides to use an infinite loop as their username. Terribly slow : the JavaScript language is designed to use the full gamut of JavaScript types (numbers, functions, objects, etc)… Not just strings!

What is $$ eval?

$$eval() method. This method runs Array. from(document. querySelectorAll(selector)) within the page and passes the result as the first argument to the pageFunction .

What is a safe alternative to using eval () JavaScript?

An alternative to eval is Function() . Just like eval() , Function() takes some expression as a string for execution, except, rather than outputting the result directly, it returns an anonymous function to you that you can call. `Function() is a faster and more secure alternative to eval().


1 Answers

It looks like your code is being encapsulated in a module. Top-level declarations in modules are not globals, but as you've discovered, functions created with new Function do not close over the context in which they're created; they're created as though they were at global scope.

As you've indicated, new Function isn't ideal as it provides for evaluation of arbitrary code, but if you control and can trust the code you're evaluating, that's not necessarily a problem. The presence of new Function also pretty much blows up the JavaScript engine's ability to optimize the code where it appears (since it cannot know what's in the function text), so best to keep these fairly isolated if you can.

Since new Function already has both of those problems, we can go ahead and use eval, which shares them: eval works in the current scope, rather than global scope.

eval example:

// Scoping function to fake the effect of module scope
(function() {
    let foo = "bar";

    let algorithm = "console.log(foo);";
    let fn = makeFunction(algorithm);
    fn();

    // Isolate the impact of eval within makeFunction
    function makeFunction(text) {
        return eval("(function() { " + text + "})");
    }
})();

Let me reiterate the issues with using eval, just to be really clear:

  • It's important that you only eval code you can trust

  • Using eval in an execution context basically makes it impossible for the JavaScript engine to optimize the code in that context, so keep it isolated to small functions if you can to contain the issue

like image 189
T.J. Crowder Avatar answered Oct 04 '22 02:10

T.J. Crowder