ECMAScript 5 規定の undefined は書き換え不可能
概要
次のようなコードをよく見ます。
undefined = 1; // 1 (function () { var undefined; // 同名のローカル変数を定義 alert(undefined); // undefined })();
ところが、ECMAScript 5.1 規定の undefined は書き換え不可能([[Writable]]: false)です。
15.1.1.3 undefined # Ⓣ Ⓡ
http://es5.github.com/#x15.1.1.3
The value of undefined is undefined (see 8.1). This property has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: false }.
従って、ローカル変数を定義する必要がなくなります。
undefined = 1; // 1 alert(undefined); // undefined (OK = Firefox 5 / NG = Google Chrome 12, Opera 11.50, IE8)
ローカル変数 undefined を定義する手法の問題点
ローカル変数 undefined
は [[Writable]]: true ですので、ローカル変数定義後に値を書き換えることを許してしまいます。
undefined = 1; // 1 alert(undefined); // undefined (function () { var undefined; // 同名のローカル変数を定義 undefined = 1; alert(undefined); // 1 })();
書き換え不可能なグローバル変数を定義する
ECMAScript 5.1 規定の Object.defineProperty
を実装しているブラウザなら書き換え不可能なプロパティ undefined
を定義できます。
/** * グローバルコード */ if (typeof Object.defineProperty === 'function') { Object.defineProperty(this, 'undefined', {value: void 0, writable: false, enumerable: false}); // [[Writable]]: false, [[Enumerable]]: false } else { undefined = void 0; } undefined = 1; // 1 alert(undefined); // undefined
参考リンク
Tweetログ
備忘録です。
Constellation utatane
@think49 Object.defineProperty(this, "undefined", { writable: false }); の間違いではないでしょうかー?
Constellation utatane
configurable:falseのときにwritableをtrueからfalseにのみ書き換え可能なのはsemantics的な理由もあるけどもっと実用的な理由としてはObject.sealした後にfreezeしても問題ないようにというのが. とECMA262厨発言をしつつ.
think49 think49
@Constellation うむー。何か勘違いしてるかもしれないですが、MDC http://goo.gl/xXHKr によると writable: false は規定値なので140文字の都合上省略してしまいました。仕様は読んでなかったので間違ってるかも…。
think49 think49
@Constellation 実際は {value: void 0, writable: false, enumerable: false} http://goo.gl/1yhy2 ですが、期待通りに動作しませんでした。
think49 think49
@Constellation で、configurable: true が既存のプロパティ定義に必要なのかなと当たりをつけて試しましたが、動作せず。http://goo.gl/o9QEb 「さて、どうしたものか?」って状況ですー。
Constellation utatane
think49 think49
defineProperty の Attributes の解説は http://es5.github.com/#x8.10.5 か。HasProperty が false を返す場合は何もしないわけだから false または undefined が規定値になる、と。
Constellation utatane
@think49 属性値のみ書き換える場合はGenericDescriptorを指定するといいです. この場合writableの値だけ変更したいのであれば, defineProperty(this, "undefined", { writable : false })ですね.
think49 think49
@Constellation ああ、わかりましたです。期待通りに動かないのは "undefind" の typo が原因ですね…。OTL お騒がせしました。
http://jsfiddle.net/3DS2v/5/
http://jsfiddle.net/3DS2v/5/
think49 think49
@Constellation GenericDescriptor はまだよくわかっていないのですが、例示されたコードから推測するに未指定のプロパティは既存の値をそのまま使う仕様になってる、という理解で合ってるでしょうか?
Constellation utatane
@think49 正確にはGenericDescriptorではないのですが><(writableがあるので), {writable: false}の場合, configurable, enumerableはabsentになります.
Constellation utatane
@think49 ものすごーく大雑把に言うと, absentは対象がすでに定義済みproperty descriptorの時は影響を与えないようにする値になりますー. つまり対象descriptorの値そのままということですねー.
think49 think49
@Constellation なるほど。ES5 も併せて読んでおぼろげながら理解できました。ありがとうございます。
think49 think49
@think49 あれ…、となると MDN http://goo.gl/xXHKr の writable の項の "Defaults to false." は間違いな気がする。
think49 think49
やっぱり間違いなので先の3件ツイートは削除…。よく読まなきゃ。
think49 think49
@think49 "Table 7 — Default Attribute Values" にデフォルト値がのってた。/
8.6.1 Property Attributes - Annotated ES5
http://goo.gl/l3xVD #ES5
8.6.1 Property Attributes - Annotated ES5
http://goo.gl/l3xVD #ES5
think49 think49