・関数,クロージャ,オブジェクトなどの細かい実装差分
手元ではnode.js v0.10.32で出力させて確認。
/* sample1 ただの関数 */ console.log("---sample1---"); function init1() { var num = 0; function disp() { num++; console.log(num); } disp(); } init1(); // 1 init1(); // 1 init1(); // 1 /* sample2 closureができる */ console.log("---sample2---"); function init2() { var num = 0; function disp() { num++; console.log(num); } return disp; } var i = init2(); var j = init2(); var k = init2(); i(); // 1 i(); // 2 i(); // 3 j(); // 1 環境が変数ごとに保存されている、はず j(); // 2 k(); // 1 /* sample3 引数つきclosure */ console.log("---sample3---"); function init3(numnum) { var num = 0; function disp() { num += numnum; console.log(num); } return disp; } var iii = init3(1); var jjj = init3(10); var kkk = init3(100); iii(3); // 1 iii(1); // 2 iii(2); // 3 jjj(3); // 10 jjj(0); // 20 kkk(); // 100 /* sample4 引数の再代入あり */ console.log("---sample4---"); function init4(numnum) { var num = 0; function disp(numnum) { num += numnum; console.log(num); } return disp; } var iiii = init4(1); var jjjj = init4(10); var kkkk = init4(100); iiii(3); // 3 iiii(1); // 4 iiii(2); // 6 jjjj(3); // 3 jjjj(); // NaN kkkk(1); // 1 /* sample5 関数式+クロージャ */ console.log("---sample5---"); var init5var = function (numnum) { //関数名の有無は問われない var num = 1000; function disp() { num += numnum; console.log(num); } return disp; }; var s = init5var(1); var t = init5var(10); s(); // 1001 s(); // 1002 t(); // 1010 t(); // 1020 /* sample6 class+instance */ console.log("---sample6---"); var init6var = function (numnum) { var num = 1000; //外部から参照できない(private) this.nnn = 33; //外部から参照できる(property) this.disp = function () { //this.disp = method num += numnum; console.log(num); }; }; var ss = new init6var(1); //newあり = return an instance of Object var tt = new init6var(100); //インスタンスとして独立 var uu = init6var(100); //newなし = 何も入らない console.log(ss); // { nnn: 33, disp: [Function] } ss.disp(); // 1001 ss.disp(); // 1002 console.log(ss.num); // undefined console.log(ss.nnn); // 33 tt.disp(); // 1100 tt.disp(); // 1200 console.log(uu); //undefined /* sample6.5 class+newなしreturnあり */ console.log("---sample6.5---"); var init66var = function (numnum) { var num = 1000; //外部から参照できない(private) this.nnn = 33; //外部から参照できる(property) this.disp = function () { //this.disp = method num += numnum; console.log(num); }; return num; }; var ss5 = init66var(1); //newなし, returnあり var tt5 = new init66var(100); var uu5 = init66var(); console.log(ss5); // 1000 returnがあるのでnumだけ返る console.log(tt5); // { nnn: 33, disp: [Function] } tt5.disp(); // 1100 ss5は値だけなのでdispを呼べない /* sample7 クラス+返り値なし無名関数 */ console.log("---sample7---"); var init7var = function (numnum) { var num = 1000; this.disp = (function () { //this.disp = property num += numnum; console.log(num); // returnがないので一度実行してdispにはundefinedが入る })(); }; var sss = new init7var(1); // 1001 disp内は宣言時に実行される var ttt = init7var(33); // 1033 propertyは全部評価される console.log(sss); // { disp: undefined } console.log(init7var); // [function] 関数を含んだオブジェクトを返す console.log(sss.disp); // undefined console.log(ttt); // undefined returnがないので /* sample7.5 クラス+返り値あり無名関数 */ console.log("---sample7.5---"); var init77var = function (numnum) { var num = 1000; this.disp = (function () { //this.disp = property num += numnum; return num; })(); return this.disp; //このreturn文はnewなし宣言のときに返る }; var sss5 = new init77var(100); //disp実行なし console.log(sss5); // { disp: 1100 } console.log(sss5.disp); // 1100 console.log(sss5.disp); // 1100 値は増えない /* sample8 ただの連想配列 */ console.log("---sample8---"); var init8 = { num: 1000, disp: function () { this.num += 1; console.log(this.num); } }; var ssss = init8; var tttt = init8; console.log(ssss); // { num: 1000, disp: [function] } ssss.disp(); // 1001 ssss.disp(); // 1002 tttt.disp(); // 1003 値渡しではなく参照渡し /* sample9 連想配列を作る関数 */ console.log("---sample9---"); function init9func () { var init9 = { num: 1000, disp: function () { this.num += 1; console.log(this.num); } }; return init9; } var a = init9func(); var b = init9func(); console.log(a); // { num: 1000, disp: [function] } a.disp(); // 1001 a.disp(); // 1002 b.disp(); // 1001
どこか間違ってるかも……。でもなんとなく動作がわかった。
次はクロージャを使うメリットの話。
Closures - JavaScript | MDNのPractical closuresの項がなぜそうなるのかよく分からない、onclick = makeSizer(n)にしてmakeSizer関数内も直接fontSize = size + 'px'うんぬん書いてはいけないのだろうか。
イベントとして呼ばれる前に評価が終わっちゃうからみたいなんだけど、そのあたりがまだもやもやしてる。でも眠いので今日はここまで。
追記 2015-03-09 クラス内でのプロパティやメソッドの呼び出し
n = 10000; //varを付けないとグローバルになる?? var cr = function () { // private var i = 10; // public this.n = 1000; // private function add3 () { i += 3; this.n += 3; //globalのnを指してる } // public this.add100 = function () { i += 100; this.n += 100; //class内のnの指してる } // public this.add3and100 = function () { add3(); this.add100(); } // public this.retI = function () { return i; } } var ss = new cr(); ss.add100(); ss.add3and100(); console.log(ss.retI()); // 213 console.log(ss.n); // 1200 //3はss.nに足されない console.log(n); // 10003
node.jsとブラウザ動作でglobalの仕様が違うかもしれないけどこんな感じっぽいです。クラス内のprivateなプロパティやメソッドは、「privateなものとして扱われる」というよりも「メンバではない、クラスに関わらないものとして扱われる」。「ただクラス内に書いてある」のであって、「クラス内定義のスコープの中に書いてある」くらいだと思う。はっきりと仕様がわからないからなんとも言えないけど……。こういうのもっとしっかり仕様書読んで確認しておくべきである。そのうちね、そのうち。