2015年11月11日水曜日

JavaScriptで他のファイルをincludeする

昨日に続きいろいろな言語のめんどくさい部分をどうにかするシリーズ。

JavaScripの中から別ファイルのJavaScriptを読み込みたいときがよくあります。
複数人で作業するときや、スマホ対応するときに読み込むJavaScriptファイルを動的に変えたり。
いつもHTMLの中に読み込むJavaScriptファイルを固定的に書くのはとてもめんどくさい。
JavaScripファイルを分割して読み込みたい要求はたくさんあります。

しかしWebで調べてもみたのですが、JavaScriptを動的にincludeする方法はChromeで動かなかったり、どれもいまいち。

ということで、JavaScriptファイルを動的に読み込むinclude関数を作ってみました。


読み込む側(main.js)
--------------------------------------

include=function(src){
    var xhr=null;
    if (window.XMLHttpRequest)xhr=new XMLHttpRequest();
    else if(window.ActiveXObject)try {
        xhr=new ActiveXObject("Msxml2.XMLHTTP");
    }catch(e){xhr=new ActiveXObject("Microsoft.XMLHTTP");}
    xhr.open("GET",src,false);xhr.send("");eval(xhr.responseText);
}

include("test.js");
println("Hello!");

--------------------------------------





読み込まれる側(test.js)
--------------------------------------
println=function(src){
alert(src);
}
--------------------------------------

上記場合の注意点として、関数や変数はvarをつけないで「hoghoge=」として宣言しないといけないです。include()関数は内部でeval()関数を使用しているのですが、Chromeとかではeval()関数内で宣言したvar変数や関数がローカル変数として扱われてしまうからです。varをつけないで宣言するとグローバル変数として扱われ、元のJavaScriptファイルから呼び出せます。

でもこれだとincludeされる関数を作るのがめんどくさい・・・。
しかも外部のライブラリ使えないジャン。

じゃあどうするのか。
ここからが本日最大のポイント。
「eval」のところを「(0,eval)」にすればよさそうな気がする。

自分で書いていて思うんですが、
はぁ僕何言っての?
いっている意味まったくもってわからない。

 (function(){ return eval;}(0))('hoge');


上記の式が等価なのですが、詳しくは以下のサイトに書いてあります。
http://qiita.com/SFPGMR/items/7a09507eead5b5db8d17

簡単にまとめると、JavaScriptのeval関数の仕様では、関数を直接呼び出すと関数内で宣言された変数のスコープが呼び出し元の関数となるのですが、関数を変数経由で間接的に呼び出すと変数のスコープがグローバルになるのです。
そして、eval関数を簡単に間接的に呼び出す方法が、(0,eval)('hoge')なのです。

ということでeval部分を修正してみました。


読み込む側(main.js)
--------------------------------------
function include(src){
    var xhr=null;
    if (window.XMLHttpRequest)xhr=new XMLHttpRequest();
    else if(window.ActiveXObject)try {
        xhr=new ActiveXObject("Msxml2.XMLHTTP");
    }catch(e){xhr=new ActiveXObject("Microsoft.XMLHTTP");}
    xhr.open("GET",src,false);xhr.send("");(0,eval)(xhr.responseText);
}

include("test.js");
println("Hello2!");
--------------------------------------



読み込まれる側(test.js)
--------------------------------------
function println(src){
alert(src);
}
--------------------------------------


やったーinclude関数ができた。
ちゃんとincludeが終わるまで関数をぬけないので、includeが終わった直後からincludeした関数使えるし、includeしたファイルの中からさらにincudeできるし、完璧。

セキュリティーの関係上、他のサーバのファイルはincludeできないのはAJAXと同じです。

eval関数のこの仕様はEcmaScript5から適用されて、それ以前の仕様では変数はローカルスコープになるようです。
したがって、読み込まれる方の関数の形式は後者の方法はInternetExploler8では動かないらしいので、InternetExploler8に対応したい場合は前者の方法で、InternetExploler9以降やスマホのみでよい場合はこの方法が使えます。
読み込む方のinclude関数は後者の方法にしておけばどれでも大丈夫です。
InternetExploler8のシェア2%くらいしかないので、ほとんど無視できますが。

JavaScriptほんとうに奥が深いです。

0 件のコメント:

コメントを投稿