またもや仕様のご認識をしていて、トラブってしまいました。
javascriptのnew演算子で対応するライブラリを作っていた時に、想定した動きをしないな〜とバグを探していた時に、どうやらインスタンス化したローカル変数が、別のnew宣言をした値で上書きされているという事を見つけ、どうやらコンストラクタの使い方が間違っているということを理解することができました。
以前まではnew宣言を使わない階層型の関数による書き方をしていたのですが、クロージャー思考が魅力で使いやすかったためにnew宣言方式に自作したライブラリを書き換えていたのですが、そうした時に有る時突然気がついたという次第です。
new宣言しているのに上書き??
そもそも、javascriptのクロージャーはnew宣言を使ったやり方でthisを引っ張り回すやり方が色々なサイトで記述されているのですが、以下のようなケースで不具合を発生させてしまいました。
# 正常パターン
var func = function(val){
if(!val){return}
this.value.val1 = val;
};
func.prototype.value = {
"val1":"--",
"val2":"@@"
};
var test0 = new func();
console.log(test0.value.val1);
var test1 = new func("aa");
console.log(test1.value.val1);
var test2 = new func("bb");
console.log(test2.value.val1);
> --
> aa
> bb
# 失敗パターン
var func = function(val){
if(!val){return}
this.value.val1 = val;
};
func.prototype.value = {
"val1":"--",
"val2":"@@"
};
var test0 = new func();
var test1 = new func("aa");
var test2 = new func("bb");
console.log(test0.value.val1 +"/"+ test1.value.val1 +"/"+ test2.value.val1);
> bb/bb/bb
成功パターンも失敗パターンもどちらも同じソースなんですが、実行タイミング(結果表示)が違うところがポイントです。
成功パターンの、コンストラクタをインスタンス化した直後であれば、正常に値を取得できるのですが、
失敗パターンの、全てのコンストラクタをインスタンス化した最後にすべての値を参照してみると、最後の値で上書きされているという状況です。
なので、成功パターンも成功ではなくて実は失敗しているんですけどね。
解決方法
このバグの原因は2つあり、"func.prototype.value = ~~"の箇所を定義してその中の値はグローバルコンストラクタとして書き換えられてしまうようです。
少し分かりにくいのでわかりやすく言うと、"func.prototype.value"を変数として書き換えるのであれば、問題ないのですが、その内容データはクロージャー変数ではなく、既存の定義データとして存在するために上書きという処理になってしまうようです。
この状態を解決するには・・・
var func = function(val){
if(!val){return}
this.value = {};
this.value.val1 = val;
};
func.prototype.value = {
"val1":"--",
"val2":"@@"
};
var test0 = new func();
var test1 = new func("aa");
var test2 = new func("bb");
console.log(test0.value.val1 +"/"+ test1.value.val1 +"/"+ test2.value.val1);
> --/aa/bb
ローカル変数の代入の際に、プロトタイプの親に当たる変数をクリア(その場で宣言)することで解決しました。
this.value = {};
もっとわかりやすく考えると、ローカル変数は、事前に記載しておかずに、宣言の際に代入することでトラブルを無くせそうです。
こうした基本概念を知らずにライブラリを作っていたことを考えると怖くて仕方が有りません。
たまたま、変数の上書きに引っかかる使い方が無かっただけで、バグが含まれている状態でしたね・・・おーこわ!
これまで作ったライブラリを見直さないと気が済まなくなってきました。
関連リンク
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Operators/new
https://www.sejuku.net/blog/25026
0 件のコメント:
コメントを投稿