2016-07-29 38 views
5

Dynamicznie tworzę funkcję w instancji Foo1 o nazwie . Tworzę tę funkcję za pomocą eval. Spodziewam się, że ta funkcja będzie miała dostęp do klasy Foo2, jednak otrzymuję numer ReferenceError: Foo2 is not defined.JavaScript `eval()` scope w ES6

I otworzyły się problem z Babel na ten temat i można znaleźć here

Jeśli chciałbyś uruchomić przykładowy kod samemu, pobrać go z here i postępuj zgodnie z instrukcjami zawartymi w README.MD do reprodukcji.

Aby uruchomić:

 
npm install 
npm run start 
naviage to localhost:8080 

Oto struktura katalogów na moim Minimal, kompletne i weryfikowalne przykład w moim otoczeniu:

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" 
    } 
} 

Teraz, jeśli mogę zmienić klasę Foo2.js do poprzedniej wersji wszystko działa jak javascript urok:

function Foo2() { } 

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

module.exports = Foo2; 
+0

Alright ja "Edytuję teraz pytanie: – frankgreco

+2

Doskonały wysiłek na MCVE, btw. –

Odpowiedz

4

Wygląda na to, że kod jest enkapsulowany w module. Deklaracje najwyższego poziomu w modułach to globale o wartościach , a nie, ale jak odkryłeś, funkcje utworzone za pomocą new Function nie zamykają się w kontekście, w którym zostały utworzone; są tworzone tak, jakby miały globalny zasięg.

Jak już wskazano, new Function nie jest idealna, ponieważ przewiduje ocenę niepożądanego kodu, ale jeśli kontrola i można zaufać kod jesteś oceniający, że nie musi to stanowić problemu. Obecność numeru new Function znacznie zwiększa zdolność silnika JavaScript do optymalizacji kodu w miejscu, w którym się pojawia (ponieważ nie wie, co to jest w tekście funkcji), więc najlepiej, jeśli to możliwe, utrzymać je w miarę izolowane.

Po new Function już oba te problemy, możemy śmiało użyć eval, który dzieli je: eval działa w bieżącym zakresie, a nie globalny zasięg.

eval przykład:

// Scoping function so we know for sure we're not creating globals 
 
(function() { 
 
    var foo = "bar"; 
 
    
 
    var algorithm = "console.log(foo);"; 
 
    var f = makeFunction(algorithm); 
 
    f(); 
 
    
 
    // Isolate the impact of eval within makeFunction 
 
    function makeFunction(text) { 
 
    return eval("(function() { " + text + "})"); 
 
    } 
 
})();

Pozwól mi powtórzyć problemów z użyciem eval, żeby być naprawdę jasne

  • To importantt że tylko kod eval można zaufanie

  • Korzystanie eval w kontekście wykonania w zasadzie uniemożliwia silnik JavaScript, żeby zoptymalizować kod w tym kontekście, więc miej to pojedyncze małych funkcji, jeśli można zawierać WYDANIE

+0

Tak, ja używam tutaj klas ES6 i importuję inne klasy ES6 przez moduły. W jaki sposób dałbym wtedy moim funkcjom utworzone za pomocą "nowej funkcji" taki sam zakres, jak gdybym faktycznie wpisał tę funkcję? – frankgreco

+0

@Frank: Brzydka odpowiedź to 'eval'. :-) –

+0

Ponieważ chciałbyś eval na "Make this a function", a nie tylko "Zrób to", myślę, że dla każdej funkcji zawiniesz ją w '" function() {} "' – Katana314