背景
ブラウザで動くjavascriptエディター jsnoteを作っていた. Functionコンストラクタでユーザが入力したコードを実行できるようにした. ES6のモジュールで作った変数をそのエディタでよんでも実行できなくて困った.
問題
ブラウザでES6のmoduleでimportした変数をFunctionコントラクタの中で呼べない.
実行環境: Chrome ver63
import * as myapp from "./myapp/index.mjs"
myapp.sample() // OK
let code = "myapp.sample()"
new Function(code)() // ERROR
consoleでmyappが存在しないと言われて実行できない
原因
MDNによると同じjavascriptのファイル内に書いた変数はスコープが違うからFunctionの中で呼び出せない.
Function コンストラクタによる関数の生成は、生成コンテキストにクロージャを作りません。つまり常にグローバルスコープで作成します。これを実行すると、 Function コンストラクタの呼び出し元のスコープは入らず、自身のローカル変数とグローバル変数だけにアクセスできます。これは関数式のコードに eval を使うのとは異なっています。
たとえ、varを使ってグローバル変数に入れても効かない.
import * as myapp from "./myapp/index.mjs"
myapp.sample() // OK
var MYAPP = myappp
let code = "myapp.sample()"
new Function(code)() // ERROR
対処法
windowのプロパティに入れてグローバル変数にすると使える
import * as myapp from "./myapp/index.mjs"
myapp.sample() // OK
window.myapp = myapp
let code = "myapp.sample()"
new Function(code)() // OK