夏も終わりに近づいてきて、少しずつ蚊などの虫も減ってきていますが、まだまだ世の中に生まれるバグは減りそうにありません。あらっきぃです。
何があったの?
ChromeやNode.jsに用いられているV8の現在の実装は、Object.freeze
を適用した状態の配列に対して、Arrayの破壊的なメソッド(pop
, push
, splice
等)を呼び出してもTypeError
例外が発生しません。FirefoxとかIE10だと例外なんだけど…。
どういうことなのかというと、
'use strict'; //あってもなくても特に関係ない
var
arr = Object.freeze([1, 2, 3]);
console.log(arr.pop()); //=> Chromeだと3が返る。FirefoxだとTypeError
arr.push(4); //=> FirefoxだとTypeError
console.log(arr); //=> [1, 2, 3] (配列自身は変更されない)
と、コメントに書いたような事態になります。
これって仕様的(ES5)にまずいんじゃない? って気がしたのでStack Overflowで訊いてみたら、案の定仕様に反しているとのことでした。
ぶっちゃけ、V8の/src/array.js
のpop
などの定義に、
//417行目あたり
function ArrayPop() {
'use strict';
if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
throw MakeTypeError("called_on_null_or_undefined",
["Array.prototype.pop"]);
}
...
と'use strict'
を書くだけでpop
とかで例外を吐くようになるんですけど、そう簡単にはいかないめんどくさい事情があるみたいです、V8の開発陣には。
大人しく仕様準拠すればいいものを…。
何が問題なの?
これで何が問題なのかというと――って言うまでもないかと思いますが、Object.freeze
を使うときはそのオブジェクトに対して破壊的な操作をされたくないときになります。なので当然、pop
やpush
されたくないわけです。でもって、それが禁止されているなら、できることなら実行したときに教えてくれるとありがたいです。というか、意図してObject.freeze
を使うぐらいですからそれを期待します。
ここからは例え話ですが、上のような意図をもってObject.freeze
を使ったライブラリを作ったとします。そのことをライブラリの作成者はうっかりリファレンスに記述し忘れて、利用者はそのことを知りません。となると、利用者はそれが普通に配列であると信じてpop
やpush
を行うかもしれませんが、Chromeさんは困ったことにその配列を変更しようとしても例外を吐かず、しかも配列が変更されていないというかなり不可解な事態に陥ってしまいます。これって知らないとすぐには気付けないバグになりますよね。
締め
こういうことって知らないと割と悩む原因になるので、僕みたいに時間を無駄にする人が後生に現れ無いようにと書いてみました。あとStack Overflowで訊いたら「英語を読みやすく直してやったよ」と言って質問の内容を書き改められたのが衝撃的でした。
それではっ。