主催のid:cimadai さんお疲れさまでした。
Secrets of the Javascript Ninja
- 作者: John Resig
- 出版社/メーカー: Manning Pubns Co
- 発売日: 2012/11/28
- メディア: ペーパーバック
- クリック: 339回
- この商品を含むブログ (15件) を見る
今回は[Chapter 9. Code Evaluation]を読みました。
evalといえばJavaScript: The Good PartsでBad partsにあげられてたりしますが、使いこなすと面白いことができるということが分かりましたよ。
コード評価基礎編
JavaScriptでコード評価を行うには2種類の方法があります。
eval
evalはトップレベル関数であり、引数に指定された文字列を評価します。
評価する際に今のスコープで実行されます。
var assert= function(expr,msg){ console.log(expr?msg:""); }; assert(eval("var t=5;")===undefined, '返り値なし'); (function(){ eval("var t = 6;"); assert( t === 6, '今のスコープで実行'); })(); assert( t === 5, '外のスコープの値は変更されない' );
new Function
function name(){} で関数定義できますが、new Functionでも関数の定義が可能です。
var add = new Function("a", "b", "return a + b;"); assert( add(3, 4) === 7, "Functionが定義され、実行もできる" );
evalとnew Functionの違いはスコープの違いです。下の例を見てみましょう。
var a = 1; (function(num){ var a = 10; // function literal var functionLiteral = function(b){return a + b;}; console.log("functionLiteral: " + functionLiteral(num)); // => 110 // eval eval("var functionEval = function(b){return a + b;}"); console.log("eval: " + functionEval(num)); // => 110 // Function object var functionObject = new Function("b","return a + b;"); console.log("new Function: " + functionObject(num)); // => 101 <- window.aが使われる } )(100);
globalでevalを実行
globalでevalを実行したいときがあります。
script 要素を生成し、head 要素へ動的に追加し、削除します。
この実装はjQueryのjQuery.globalEval(code) - jQuery API 1.4.4 日本語リファレンス - StackTraceでも使われてます。
<script> function globalEval( data ) { data = data.replace(/^\s*|\s*$/g, ""); if ( data ) { var head = document.getElementsByTagName("head")[0] || document.documentElement, script = document.createElement("script"); script.type = "text/javascript"; script.text = data; head.appendChild( script ); head.removeChild( script ); } } window.onload = function(){ (function(){ globalEval("var test = 5;"); })(); assert( test === 5, "The code is evaluated globally." ); }; </script>
ちなみにid:hagino3000さんに教えていただいたのですが、Chromeのdeveloper consoleにはバグがあって
evalで実行すると、globalスコープで実行されるそうです。
Issue 51496 - chromium - Eval does not have access to scoped variables in WebKit console - An open-source browser project to help move the web forward. - Google Project Hosting
例
JSON
JSON文字列を評価するときに一番使われていたのがevalを使う方法です。
いまでも一番速いのがevalを使う方法です。
var json = '{"key":"value"}'; var object = eval("(" + json + ")"); assert( object.key === "value","JSONが評価されました");
でも、他のサーバーからくるJSON文字列をそのままevalするのは危険です。
JSONの仕様作成者Douglas Crockfordのjson2.jsを使いましょう。
JavaScript Ninja内でjson2.jsが内部でどのようにコードチェックをしているかが載っています。
詳しくは @kzys さんが英文ブログで書かれてますので一読するといいと思います。
json2.js - Kato Kazuyoshi
script要素に新しいtypeを追加する
scriptタグのtype要素が"text/javascript"以外の場合実行されないことを利用して機能を追加することができます。
ページがロードされたら実行するスクリプトタグのタイプを作成する例は以下です。
<script> window.onload = function(){ var scripts = document.getElementsByTagName("script"); for ( var i = 0; i < scripts.length; i++ ) { if ( scripts[i].type == "onload" ) { // You may want to use a globalEval implementation here eval( scripts[i].innerHTML ); } } }; </script> <script type="onload"> ok(true, "I will only execute after the page has loaded."); </script>
この技法はファイルの非同期読込で使われていたりします。
ControlJSはtypeを"text/cjs"とすることで非同期読み込みされるそうです。
この辺りの話は id:os0xさんがBPSTUDY#41でされていました。
http://ss-o.net/event/js20110128/
メタ言語 processing.js
Processing.jsはJohn ResigによるProcessing Visualization LanguageのJavaScriptでの実行環境です。
John Resig - Processing.js
Processing.jsはProcessingのコードを実行時にJavaScriptに変換し、evalして実行しています。
ちなみに、jsfiddle,jsdo.itでも実行可能ですよ。
私が作った例を紹介しときます。
jsdo.itはtwitterに"jsdo.itでもprocessing.jsの最新バージョン使いたい"とつぶやいたところ @kyo_ago さんがみてくれて最新バージョンに
アップデートしていただけました。 @kyo_ago さんありがとうございました。
その他
scrlipt.aculo.us内でプログラムを動的に書き換える際に利用されています。
第5回 effects.js(前編)速度的なボトルネックを解消するための土台の最適化:script.aculo.usを読み解く|gihyo.jp … 技術評論社
まとめ
evalなどのコード評価はライブラリ内で使うといいことがおおいですね。
ただしリモートサーバーからきた文字列をそのまま実行するのは危険なのでこういう場合には使わないのが
幸せになれそうです。
懇親会
新宿X'ianにて行いました。ここの料理は辛くておいしいです(^^)
以上です、押忍。