Object.defineProperty できなくなるケース
ES-Discuss のメーリングリストで知った。
Object.prototype.get = function(){}; var o = {}; Object.defineProperty(o, "hoge", { value: "OK" }); // TypeError: property descriptors must not specify a value or be writable when a getter or setter has been specified
Object.prototype
に get
, set
あたりのプロパティを追加してしまうと、Object.defineProperty()
時に例外が発生していしまう件、というやつ。
何故か
Object.defineProperty
の第3引数にはDescriptorという特定のプロパティを持つオブジェクトを指定することになる。このDescriptorは以下の2種類がある。
- DataDescriptor
- 普通のプロパティ
- AccessorDescriptor
- Getter や Setter
そして、互いに存在してはいけないプロパティがある。DataDescriptorにはget
,set
を持っていてはいけないし、AccessorDescriptorはvalue
,writable
を持ってはいけない、という具合だ。
defineProperty時にはこの検証が行われるわけだが、問題なのはこのプロパティを持っているかの検証に、[[HasProperty]]を使用する点だ。[[HasProperty]]は、[[Prototype]]内も見る内部関数なので、Object.prototype.*
内も見てしまう事だ。これが[[GetOwnProperty]]による検証だったら問題はなかっただろう…。
防ぐには
Object.prototype への追加を禁止する。
Object.freeze(Object.prototype);
Object.create(null) からデフォルトのdescriptorを作る
function hasOwn(obj, key) { return Object.prototype.hasOwnProperty.call(obj, key); } function defineProperty(obj, key, desc) { var d = Object.create(null); d.configurable = hasOwn(desc,"configurable") ? desc.configurable : false; d.enumerable = hasOwn(desc, "enumrable") ? desc.enumerable : false; if (hasOwn(desc, "value")) { d.writable = hasOwn(desc, "writable") ? desc.writable : false d.value = desc.value; } else { d.get = hasOwn(desc, "get") ? desc.get : undefined; d.set = hasOwn(desc, "set") ? desc.set : undefined; } return Object.defineProperty(obj, key, d); }
くらいしか思いつかん。
同様の事は、Object.create
の第2引数指定時、Object.defineProperties()
にも起こりえる。
皆さん、注意しましょう。
# これ、JavaScript sucks 入りじゃね?