2011年12月10日土曜日

Chromeのconsole.logの謎



この記事を読んで、Chromeのconsole.logの動きが謎すぎる気がして確認してみました。


CoffeeScriptで、newするときのプロパティ変数の初期化ってどーなってんの? - uzullaがブログ


検証


ソースコード


var a = [];

a.push('A');
console.log(a);

a.push('A');
console.log(a);


これをChromeのDeveloper toolのコンソールで実行してみました。


期待する出力


["A"]
["A", "A"]


実際の出力


["A", "A"]
["A", "A"]


調べてみた


console.log lazy等でググるとStackoverflow等のサイトがでてきた。


Is Chrome's JavaScript console lazy about evaluating arrays? - Stack Overflow


javascript - Bizarre console.log behaviour in Chrome Developer Tools - Stack Overflow


Bug 35801 – console.log object maps are read when you open the treeview on the console, not when they’re output in the console


なるほど。Webkit特有なのかもしれません。


console.logにオブジェクトを渡すと、console.logはそのオブジェクトの内容を出力します。


console.logが出力する準備をしている間にそのオブジェクトに変更を加えると変更後のオブジェクトの内容が出力されると。


シンプルな解決方法として




  • JSON.stringify

  • toString

  • slice (配列の場合)


等のメソッドや関数を使って別のオブジェクトを作成してから渡す方法が挙げられてますね。


ちなみに



Coffeescriptのクラスのプロパティ変数って、初期化が必須なの?(;´Д`)
コンストラクタで宣言しなおさないと、暗黙のウチに共有されちゃうの?
そういうもんなの??

JavaScriptのprototype継承ですね。



class Chain
queue: []
test: -> @queue.push "A"




var Chain;
Chain = (function() {
function Chain() {}
Chain.prototype.queue = [];
Chain.prototype.test = function() {
return this.queue.push("A");
};
return Chain;
})();


JavaScriptになるとこうなっています。


Chainクラスはprototypeというオブジェクトを持っています。(以下prototypeオブジェクト)


CoffeeScriptのクラスのメソッド等はこのprototypeオブジェクトのプロパティに定義されます。


Chainクラスのインスタンスはprototypeオブジェクトへの参照を持っています。


Chainクラスのインスタンスからqueueプロパティにアクセスするとき




  1. まずインスタンス自身がqueueプロパティを持っていないか調べる

  2. 持っていなければクラスのprototypeオブジェクトにqueueがないか調べる。

  3. クラスのprototypeオブジェクトにもqueueプロパティがなければ更に継承元のクラスを辿って行く


という感じになってます。


インスタンス自身がプロパティを持っているかどうか調べるためにはhasOwnPropertyを使います。



ソースコード



class Chain
queue: []
test: -> @queue.push "A"

c = new Chain
console.log "before c.queue = []"
console.log c.hasOwnProperty "queue"

c.queue = []

console.log "after c.queue = []"
console.log c.hasOwnProperty "queue"


実行結果


before c.queue = []
false
after c.queue = []
true


queueプロパティに配列を代入するまで、Chainクラスのインスタンスは自分自身のqueueプロパティを持ちません。


そのため、全てのインスタンスがChainクラスのprototypeオブジェクトのqueueプロパティを参照しています。


なのでtestメソッドの中でthis.queue.push("A")を呼び出すと、Chainクラスのprototypeオブジェクトのqueueプロパティが変更されるわけです。


こう書くとなおるわけ



ソースコード



class Chain
constructor: -> @queue = []

console.log "c = new Chain"
c = new Chain
console.log "c.hasOwnProperty 'queue': #{c.hasOwnProperty 'queue'}"


JavaScriptになると



var Chain, c;
Chain = (function() {
function Chain() {
this.queue = [];
}
return Chain;
})();

console.log("c = new Chain");
c = new Chain;
console.log("c.hasOwnProperty 'queue': " + (c.hasOwnProperty('queue')));


実行結果


c = new Chain
c.hasOwnProperty 'queue': true


コンストラクタの中で以下のコードを呼び出すことで、インスタンス自身のプロパティとしてqueueを定義しています。



this.queue = [];


インスタンス自身のプロパティなので、ほかのインスタンスのqueueを変更しても、関係ない。


そんな感じ。


まとめ




  • Webkitのconsole.logはオブジェクトが表示されるまでにディレイがあって、その途中でオブジェクトの中身を変更すると、変更後の内容が出力される

  • CoffeeScriptのクラスの仕組みについて知るためにはJavaScriptのプロトタイプチェーン等に関する知識が必要。


JavaScriptについて勉強するにはパーフェクトJavaScriptがお勧めです。



パーフェクトJavaScript (PERFECT SERIES 4)

パーフェクトJavaScript (PERFECT SERIES 4)










0 件のコメント:

コメントを投稿