CoffeeScript:オブジェクトメソッド

CoffeeScriptオレンジキューブのメソッド

ECMAScriptの第5版では、オブジェクトを操作するための多くの新しいメソッドが登場しましたが、それらの詳細な説明とロシア語(多くの場合英語)の内部実装はそれほど単純ではありません。 このため、この記事では、 ECMAScript仕様の第3版と第5版に従って、 Objectオブジェクトのすべてのメソッドを詳細に調べて説明します。

内容

1. Object.create(プロトコル[、プロパティ])
2. Object.defineProperty(オブジェクト、プロパティ、記述子)
3. Object.defineProperties(オブジェクト、プロパティ)
4. Object.getOwnPropertyDescriptor(オブジェクト、プロパティ)
5. Object.keys(オブジェクト)
6. Object.getOwnPropertyNames(オブジェクト)
7.データ記述子
8.アクセサー記述子
9. Object.getPrototypeOf(オブジェクト)
10. Object.preventExtensions(オブジェクト)
11. Object.isExtensible(オブジェクト)
12. Object.seal(オブジェクト)
13. Object.isSealed(オブジェクト)
14. Object.freeze(オブジェクト)
15. Object.deepFreeze(オブジェクト)(非標準)
16. Object.prototype.hasOwnProperty(プロパティ)
17. Object.prototype.isPrototypeOf(オブジェクト)
18. Object.prototype.propertyIsEnumerable(オブジェクト)
19.結論



Object.create(proto [、properties])


Object.create()は、指定されたプロトタイプオブジェクトとプロパティで新しいオブジェクトを作成します。

object = Object.create property: 1 object.property # 1 

メソッドは2つの初期化パラメーターを取ることができます。
最初のパラメーターは必須であり、オブジェクトのプロトタイプを設定し、 nullまたはオブジェクトでなければなりません。
2番目のパラメーターはオプションであり、オブジェクトのプロパティを初期化します。

例を考えてみましょう:

 object = Object.create {}, property: value: 1 object.property # 1 

この例では、最初のパラメーターを空のオブジェクトに設定します。これは、2番目のパラメーターで定義されたプロパティのプロトタイプオブジェクトになります。

神秘的な値のプロパティに悩まされないようにしましょう。少し後でこのトピックをさらに詳しく検討します。

では、最初のオプションと2番目のオプションの違いは何ですか?
次の例で、この質問に対する答えを明確にできると思います。

 object = Object.create property: 1 object.property = 2 object.property # 2 object = Object.create {}, property: value: 1 object.property = 2 object.property # 1 

一体何をお願いしますか?
単純に、デフォルトの2番目の書き込み形式は、プロパティが読み取り専用であり、削除およびリストできないことを意味します!

例を見てみましょう:

 object = Object.create {} property: value: 1 object.property = 2 object.property # 1 object.method = -> object.property do object.method # 1 key for key of object # method delete object.property # false 

ご覧のとおり、プロパティに新しい値を設定したり、列挙したり、削除したりすることはできませんでした。

そして、 Object.create()を使用してプロトタイプの継承がどのように実装されるかに注目したいと思います。

 A = a: 1 b: 2 B = Object.create A, c: value: 3 enumerable: on for own key, value of B console.log " # {key}: # {value}" # c:3, a:1, b:2 

この例では、オブジェクトAからプロパティの継承を実装し、 列挙可能な属性を追加して、オブジェクトBで定義されたプロパティをfor-ofループにリストできるようにしました。

for-ofステートメントに独自の演算子があると、ループの本体でhasOwnPropertyメソッドが使用されず、オブジェクトプロトタイプチェーンでプロパティがチェックされないことに注意してください。
この演算子のより詳細な説明は、以前の記事にあります。

create()メソッドが内部からどのように機能するかに興味があるなら、私が読んだものを統合するための割り当てとして、 仕様に従って実装をもたらします:

 Object.create = (object, properties) -> # 1. If Type(O) is not Object or Null throw a TypeError exception. if typeof object is not 'object' throw new TypeError "Object.create: # {object.toString()} is not an Object or Null" # 2. Let obj be the result of creating a new object as if by the expression new Object() # where Object is the standard built-in constructor with that name __new__ = new Object # 3. Set the [[Prototype]] internal property of obj to O. if '__proto__' of init __new__.__proto__ = object; else __new__.constructor:: = object # 4. If the argument Properties is present and not undefined, add own properties # to obj as if by calling the standard built-in function Object.defineProperties # with arguments obj and Properties. if typeof props is not 'undefined' Object.defineProperties object, props # 5. Return obj. object 


Object.defineProperty(オブジェクト、プロパティ、記述子)


Object.defineProperty()メソッドを使用すると、オブジェクトの新しいプロパティを定義したり、既存のプロパティの属性を変更したりできます。 その結果、新しいオブジェクトが返されます。

object-プロパティを定義するオブジェクト
property-定義または変更されるプロパティの名前
記述子 -記述子

例を考えてみましょう:

 object = {} Object.defineProperty object, 'property' value: 1 

この場合、おそらくvalueプロパティの使用に関してすでに質問があります。 ここで、この問題をさらに詳しく説明しようとします。

新しいメソッドでは、 ECMAScript 5 、特にユーザー記述子にも新しい用語が登場しました。

記述子は、独自のプロパティの値とアクセスレベルを設定し、その説明を保存できる単純なオブジェクトです。

つまり、記述子を使用すると、プロパティの属性を制御できます。
ECMAScript 5が登場する前は、オブジェクト属性を操作するためのはるかに控えめなツールObject.prototype.propertyIsEnumerable()およびObject.prototype.hasOwnProperty()にアクセスできました。

正式には、記述子はデータ記述子データ記述子 )、 アクセス記述子アクセサ記述子 )、および一般記述子汎用記述子 )の3つのタイプに分けられます
ただし、最後のタイプの記述子は考慮しません。 それらの説明は、実際よりも本質的に理論的です。

データ記述子


記述子が空の場合、または記述子にvalueまたはwritableの 2つの属性のいずれかがある場合、このプロパティが作成されます。
内部メソッド[[IsDataDescriptor]]は、属性の存在を確認します。

 IsDataDescriptor (Descriptor): if Descriptor is undefined return off if !Descriptor.[[Value]] and !Descriptor.[[Writable]] return off return on 

必須属性に加えて、データ記述子にはオプションの属性configurableおよびenumerableがあります。

 { configurable: false, enumerable: false } 

それでは、すべての属性を個別に見てみましょう。

value-オブジェクトのプロパティの値を設定します。
writable-プロパティを変更できるかどうかを決定します。
構成可能 -プロパティを削除する機能を定義します。
enumerable-プロパティ列挙のアクセシビリティを決定します

実装内では、これらの属性の名前は[[Value]][[Writable]][[Enumerable]][[Configurable]]になります。

デフォルトでは、すべてのデータ記述子属性はfalseであり、 value属性を除き、その値はundefinedに設定されます。

注:正式には、 ECMAScript 3ではユーザー記述子はありませんが、内部属性DontEnumReadOnly 、およびDontDeleteがあります。

そのため、オブジェクトプロパティに対して同様の属性を設定できます。

 Object.defineProperty {}, 'property' value: 1 writable: on enumerable: on configurable: on 

ここで、 Objectプロトタイプを拡張する必要があると想像してみましょう。

 Object::method = -> @ 

そのため、 Objectプロトタイプに新しいメソッドを追加し、さらに使用することができます。
実際には、このソリューションには多くの欠点があります。

まず、同じ名前のメソッドが将来の標準に登場する可能性があり、私たちのメソッドがそれをオーバーライドする可能性があります。 あるいは、メソッドに複雑な名前を付けて、静かに眠ることができます。

例:

 Object::my_super_method = -> @ 

その名前のメソッドが仕様に該当することはまずありません。

次に、 Objectプロトタイプに追加するものはすべて他のオブジェクトに分類されます。

 Object::method = -> 1 list = [] do list.method # 1 

第三に、そのようなメソッドは列挙可能な属性の真の値を持ちます:

 Object::method = -> 1 object = {}; i for i of object # method 

この方法オブジェクトからメソッドmethodの列挙を防ぐことができます

 i for own i of object 

このソリューションは機能しますが、 Objectプロトタイプにメソッドを追加する段階でも改善できます。

 Object.defineProperty Object::, 'method' value: -> 1 enumerable: false i for i of object # 

ご覧のように、 メソッドは for-ofステートメントにリストされていません。

基本型のプロトタイプを拡張することは常に良い習慣とは限りませんが、そのような必要性が時々発生します。 したがって、特に他の人があなたのコードで作業する場合は、自分の熊手を踏まないように、すべての合理的な措置を講じるようにしてください。

アクセサー記述子


アクセス記述子は、 getまたはset属性を含むものです。 この場合、これらの属性が一緒に存在してもかまいません。 ただし、 書き込み可能な属性の存在は許可されて いません

 IsDataDescriptor (Descriptor): if Descriptor is undefined return off if !Descriptor.[[Get]] and !Descriptor.[[Set]] return off return on 

次に、内部メソッド[[DefineOwnProperty]]が呼び出されます
実装内では、記述子属性の名前は[[Get]]および[[Set]]です。

構成可能および列挙可能な属性は、データ記述子でも使用できます。

 property = 0 object = Object.defineProperty {}, 'property' get: -> property set: (value) -> property = value configurable: on enumerable: on object.property = 1 # set object.property # 1, get 

また、データ記述子とアクセス記述子を同時に使用することは許可されないことに注意してください。

 Object.defineProperty {}, 'property' get: -> 1 value: 1 # TypeError: property descriptors must not specify a value or be writable when a getter or setter has been specified 'value: 1" 

Object.create()と同様に、継承も同じ原則に従います。

 A = a: 1 b: 2 B = Object.defineProperty A, 'c' value: 3 enumerable: on for own key, value of B console.log " # {key}: # {value}" # a:1, b:2, c:3 


Object.defineProperties(オブジェクト、プロパティ)


オブジェクトの新しいプロパティを定義したり、既存のプロパティの属性を変更したりできます。 その結果、新しいオブジェクトが返されます。 つまり、 Object.defineProperties()は、多くのプロパティのみを使用してObject.defineProperty()と同じことを行います。

 object = Object.defineProperties {}, a: value: 1 enumerable: on b: value: 2 enumerable: on for own key, value of object console.log " # {key}: # {value}" # a:1, b:2 

定義されたプロパティの名前が継承されたプロパティと一致する場合、継承されたプロパティが再定義されます。

 A = a: 1 b: 2 object = Object.defineProperties {} a: value: 3 enumerable: on b: value: 4 enumerable: on for own key, value of object console.log " # {key}: # {value}" # a:3, b:4 

アクセサーの再定義が機能しないことを思い出してください。

 object = Object.defineProperty {}, 'property' get: -> 0 Object.defineProperty object, 'property' get: -> 1 object.property # 0 

明らかに、プロパティをオーバーライドするには、 構成可能な属性の値を変更する必要があります。

 object = Object.defineProperty {}, 'property' get: -> 0 configurable: on Object.defineProperty object, 'property' get: -> 1 object.property # 1 

残念ながら、 IEはこのメソッドをバージョン9以下でサポートしていません。 ただし、すべてがそれほど悪いわけではありません。 8番目のバージョンには、 Object.defineProperties()を実装できるObject.defineProperty()メソッドがあります。

 Object.defineProperties = (object, properties) -> type = (object) -> Object::toString.call object is '[object Object]' if !type object and !type properties throw new TypeError 'Object.defineProperties(Object object, properties Object)' if !Object.defineProperty return object; for own key, value of properties Object.defineProperty object, key, value object 


Object.getOwnPropertyDescriptor(オブジェクト、プロパティ)


このメソッドを使用すると、記述子のプロパティにアクセスできます。

 object = {} Object.defineProperty object, 'property' value: 1 writable: off enumerable: off configurable: on Object.getOwnPropertyDescriptor object, 'property' ### { value: 1, writable: true, enumerable: true, configurable: true } ### 

なぜなら 記述子プロパティは内部ECMAScriptプロパティであり、 Object.getOwnPropertyDescriptor()は、そのような情報を取得できる唯一のメソッドです。 Object.getOwnPropertyDescriptor()は独自のプロパティでのみ機能することに注意してください

 Object.getOwnPropertyDescriptor {}, 'valueOf' # undefined 

valueOfプロパティは継承され、プロトタイプチェーン内にあるため:

 {}.hasOwnProperty 'valueOf' # false 'valueOf' of {} # true 

既に述べたように、 hasOwnProperty()メソッドは、of演算子とは異なり、独自のプロパティのみをチェックします。

次のように、 valueOfプロパティに直接アクセスできます。

 {}.constructor::valueOf # function 

明確にするために、次の例を検討します。

 object = {} object.constructor::valueOf = 1 object.valueOf # 1 

一見したところ、プロパティをオブジェクトに明示的に追加する場合とプロトタイプオブジェクトを使用して追加する場合に違いはなく、これらの記述形式は相互に置き換えることができます。 ただし、これは完全に真実ではありません。

 object = {} object.valueOf = 1 object.constructor::valueOf = 2 object.toString # 1 

ご覧のとおり、プロパティがオブジェクトに明示的に設定されている場合( 独自のプロパティ )、その値はプロトタイプオブジェクトで指定されたプロパティと「オーバーラップ」します。 より正確には、プロトタイプオブジェクトのプロパティの値は消えず、単に解決せず、常に直接利用できます。

 object = {} object.toString = 1 object.constructor::valueOf = 2 object.toString # 1 object.constructor::valueOf # 2 'valueOf' of object # true object.hasOwnProperty 'valueOf' # false 

つまり、オブジェクトのプロパティが明示的に設定されていない場合、プロトタイプチェーンで検索が続行されます。

 object = {} object.constructor::constructor::constructor::property = 1 object.property # 1 

宿題として、次の例を検討することをお勧めします。

 fn = (x) -> x * x fn 2 # 4 fn::constructor 4 # 8 


Object.keys(オブジェクト)


オブジェクトの列挙された独自のプロパティの名前を含む配列を返します。

典型的な例を考えてみましょう:

 Object.keys a: 1 b: 2 .length # 2 

多くの場合、 Object.keysArrayオブジェクトの他のメソッドと組み合わせて使用​​されます。

 object = a: 1 b: 2 Object.keys(object).filter (i) -> i if object[i] > 1 # 2 

Object.keys()メソッドの実装は非常に簡単です:

 Object.keys = (object) -> i for own i of object 

ただし、入力引数の検証を常に使用することを強くお勧めします。
少し修正した後、 Object.keys()の典型的な実装は次のようになります。

 Object.keys = (object) -> if Object::toString.call(object) is not '[object Object]' throw new TypeError "Object.keys: # {object.toString()} is not an Object" i for own i of object 

仕様に従ったObject.keys()の実装:

 Object.keys = (object) -> # 1. If the Type(O) is not Object, throw a TypeError exception if Object::toString.call(object) is not '[object Object]' throw new TypeError "Object.keys: # {object.toString()} is not an Object" # 2. Let count be the number of own enumerable properties of O count = Object.getOwnPropertyNames(object).length # 3. Let array be the result of creating a new Object as if by the expression new Array(n) # where Array is the standard built-in constructor with that name array = new Array count # 4. Let index be 0 index = 0; # 5. For each own enumerable property of O whose name String is P for own property of object if !object.propertyIsEnumerable property continue # a. Call the [[DefineOwnProperty]] internal method of array with arguments # ToString(index), the PropertyDescriptor # {[[Value]]: P, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: true}, and false # b. Increment index by 1 Object.defineProperty array, index++, value: property writable: on enumerable: on configurable: on # 6. Return array array 


Object.getOwnPropertyNames(オブジェクト)


オブジェクトのプロパティの名前を含む配列を返します。
Object.keys(オブジェクト)とは異なりこのメソッドは列挙可能な属性の値を考慮しません。

 object = a: 1 b: 2 Object.defineProperty object, '' value: 3, enumerable: off, Object.keys(object).length # 2 Object.getOwnPropertyNames(object).length # 3 

仕様によるObject.getOwnPropertyNames()の実装:

 Object.getOwnPropertyNames = (object) -> # 1. If the Type(O) is not Object, throw a TypeError exception if Object::toString.call(object) is not '[object Object]' throw new TypeError "Object.getOwnPropertyNames: # {object.toString()} is not an Object" # 2. Let array be the result of creating a new Object as if by the expression new Array(n) # where Array is the standard built-in constructor with that name array = new Array # 3. Let index be 0 index = 0; # 4. For each named own property P of O for own name of object # a. Let name be the String value that is the name of P. # b. Call the [[DefineOwnProperty]] internal method of array with arguments # ToString(n), the PropertyDescriptor {[[Value]]: name, [[Writable]]: true, # [[Enumerable]]: true, [[Configurable]]: true}, and false. # c. Increment n by 1. Object.defineProperty array, index++, value: name writable: on enumerable: on configurable: on # console.log array # 5. Return array array 


Object.getPrototypeOf(オブジェクト)


指定されたオブジェクトの[[Prototype]]プロパティへの参照を返します。

 object = {} Object.getPrototypeOf(object).property = 1 object.property # 1 

メソッドの実装:

 Object.getPrototypeOf = (object) -> if Object::toString.call(object) is not '[object Object]' throw new TypeError "Object.getPrototypeOf: # {object.toString()} is not an Object" object.__proto__ or object.constructor:: 


Object.preventExtensions(オブジェクト)


オブジェクトの拡張子をブロックします。

 object = a: 1 Object.preventExtensions object object.b = 1 'b' of object # false Object.getOwnPropertyDescriptor object, 'a' # { configurable: true, enumerable: true, value: 1, writable: true } 

メソッドObject.definePropertyとObject.definePropertiesを使用してロックされたオブジェクトを拡張しようとすると、 TypeError型の例外がスローされるという事実に特に注意を払いたいと思います。

 object = {} Object.preventExtensions object Object.defineProperty object, 'property' value: 1 # TypeError: Object.defineProperties(object, 'property', ...) is not extensible 

また、厳格モードではTypeErrorが常にスローされます。

 do -> 'use strict' object = {} Object.preventExtensions object object.property = 1 # "TypeError: object.property is not extensible 

この場合、 TypeErrorをキャッチしないとアプリケーションが動作しなくなるため、特に注意してください!


Object.isExtensible(オブジェクト)


オブジェクト拡張の可用性を決定します。 ブール値を返します。

 object = {} Object.preventExtensions object Object.isExtensible object # false 


Object.seal(オブジェクト)


オブジェクトのプロパティをシールします。

 Object.seal = Object.preventExtensions + {[[Configurable]]: off} 

例を考えてみましょう:

 object = property: 1 Object.seal object delete object.property # false Object.getOwnPropertyDescriptor object, 'property' # { configurable: false, enumerable: true, value: 1, writable: true } 

Object.seal()は、すべてのオブジェクトプロパティの構成可能な属性にfalse設定します。

 Object.getOwnPropertyDescriptor(Object.seal property: 1, 'property').configurable # false 

オブジェクト自体が封印されているのではなく、そのプロパティのみが封印されていることに注意してください。

 object = {} Object.defineProperty, 'property' value: 1 Object.seal object delete object.property # false delete object # false object.property # 1 object # Object 

ご覧のとおり、 delete [[Delete]]演算子はオブジェクトのプロパティのみを削除するため、 オブジェクトの削除は機能しません。
これは、 JavaScriptコードの翻訳段階で、すべての変数の前にvarキーワードが付いているためです。

オブジェクト自体を削除するには、グローバルグローバルオブジェクトのプロパティにする必要があります。

 global.object = {} #  @object,     Object.seal object delete object object # ReferenceError: object is not defined 'object' 

ただし、オブジェクトはグローバルスコープに該当するため、このようなオブジェクトの定義はお勧めできません。

 do -> global.object = {} object # Object 

オブジェクトを削除する場合は、未定義の値を割り当ててください。

 object = {} object = undefined object # undefined 

ある時点で再びこの変数にアクセスする必要がある場合は、 null演算子を使用する必要があります。null演算子は、変数にオブジェクトへの参照が含まれていたことを通知します。

1. CoffeeScriptにvoid演算子はありませんが、代わりにundefinedを使用する必要があり、これはvoid 0に変換されます
2.定義済みプロパティの削除に関する明確なルールはありません。
たとえば、 Dateオブジェクトのnowプロパティを削除することは完全に許容できますが、同時にFunctionオブジェクトの呼び出しメソッドを削除することはできません。

  delete Date.now # true Date.now # undefined delete Function.call Function.call # [Function: call] 

同時に、コンストラクタ自体を削除することもできます。 およびオブジェクト([構成可能]]:true)

  delete Object # true typeof Object # undefined Object # ReferenceError: Object is not defined 

2.削除演算子をより詳細に検討しなかったのは、 これは、この記事に含めるべき非常に大きなトピックです。 この質問にまだ興味がある場合は、記事kangax 'a Understanding deleteを読むことをお勧めします

Object.seal()がオブジェクトプロパティの削除を禁止していることに加えて、新しいプロパティの追加もブロックします。

 object = {} Object.seal object object.property = 1 object.property # undefined 

注:サーバー実装はTypeErrorをスローします!

ただし、シーリングはオブジェクトのプロパティ値の変更には適用されません。

 object = property: 1 Object.seal object object.property = 2 object.property # 1 

Object.preventExtensions()同様に 、strictモードでオブジェクトのプロパティを変更しようとしたり、 Object.defineProperty / Object.definePropertiesを使用すると、TypeError例外がスローされます!

仕様に従ったObject.seal()の実装:

 Object.seal = (object) -> # 1. If Type(O) is not Object throw a TypeError exception if Object::toString.call(object) is not '[object Object]' throw new TypeError "Object.seal: # {object} is not callable!" # 2. For each named own property name P of O, Object.getOwnPropertyNames(object).forEach (property) -> # a. Let desc be the result of calling the [[GetOwnProperty]] internal method of O with P __desc__ = Object.getOwnPropertyDescriptor object, property # b. If desc.[[Configurable]] is true, set desc.[[Configurable]] to false. if __desc__.configurable is on __desc__.configurable = off # c. Call the [[DefineOwnProperty]] internal method of O with P, desc, and true as arguments Object.defineProperty object, property, __desc__ # 3. Set the [[Extensible]] internal property of O to false # 4. Return O. Object.preventExtensions object 


Object.isSealed(オブジェクト)


オブジェクトがシールされているかどうかを判別します。 ブール値を返します。

 object = {} Object.seal object Object.isSealed object # true 

空のオブジェクトを非拡張可能にすると、シールされます。

 object = {} Object.preventExtensions object Object.isSealed object # true 

ただし、オブジェクトにプロパティを追加すると、シールが解除されます。

 object = property: 1 Object.preventExtensions object Object.isSealed object # false 

仕様に従ったObject.isSealed()の実装:

 Object.isSealed = (object) -> # 1. If Type(O) is not Object throw a TypeError exception. if Object::toString.call(object) is not '[object Object]' throw new TypeError "Object.isSealed: # {object} is not callable!" # 2. For each named own property name P of O then Object.getOwnPropertyNames(object).forEach (property) -> # a. Let desc be the result of calling the [[GetOwnProperty]] internal method of O with P. __desc__ = Object.getOwnPropertyDescriptor object, property # b. If desc.[[Configurable]] is true, then return false. if __desc__.configurable is on return off # 3. If the [[Extensible]] internal property of O is false, then return true. # 4. Otherwise, return false. if !Object.isExtensible(object) then on else off 


Object.freeze(オブジェクト)


オブジェクトをフリーズします。

 Object.freeze = Object.preventExtensions + Object.seal + {[[Writable]]: off} 

つまり、 Object.freeze()は、オブジェクトへの新しいプロパティの追加、既存のプロパティの変更および削除を防ぎます。

 object = a: 1 object.a = 0 # false    object.b = 0 # false     delete object.a # false    Object.getOwnPropertyDescriptor object, 'a' # { configurable: false, enumerable: true, value: 1, writable: false} 

なぜなら 既にObject.preventExtensions()Object.seal()を詳細に調べましたが、繰り返すのは意味がありません。
注目したいのは、「フリーズ」の深さだけです。

 object = property: internal: 1 Object.freeze object object.property = 0 # false object.property.internal = 0 # true Object.getOwnPropertyDescriptor(object.property, 'internal').writable # true 

ご覧のとおり、最初のレベルの子プロパティのみがブロックされています!
実際、 ECMASctipt 5では、ディープフリーズメソッドないことを心配する必要はありません。 Object.deepFreeze()を自分で実装してみましょう。

 Object.deepFreeze = (object) -> isObject = (value) -> Object::toString.call(value) is '[object Object]' if !isObject object throw new TypeError "Object.deepFreeze: # {object} is not callable!" for own key, value of object if isObject(value) and !Object.isFrozen value Object.deepFreeze(value) Object.freeze object object = property: internal: 1 Object.deepFreeze object Object.getOwnPropertyDescriptor(object.property, 'internal').writable # false 

仕様に従ったObject.freeze()の実装:

 Object.freeze = (object) -> # 1. If Type(O) is not Object throw a TypeError exception if Object::toString.call(object) is not '[object Object]' throw new TypeError "Object.freeze: # {object} is not callable!" # 2. For each named own property name P of O, Object.getOwnPropertyNames(object).forEach (property) -> # a. Let desc be the result of calling the [[GetOwnProperty]] internal method of O with P __desc__ = Object.getOwnPropertyDescriptor object, property # b. If IsDataDescriptor(desc) is true, then # If desc.[[Writable]] is true, set desc.[[Writable]] to false if __desc__.value and __desc__.writable is on __desc__.writable = off # c. If desc.[[Configurable]] is true, set desc.[[Configurable]] to false if __desc__.configurable is on __desc__.configurable = off # d. Call the [[DefineOwnProperty]] internal method of O with P, desc, and true as arguments Object.defineProperty object, property, __desc__ # 3. Set the [[Extensible]] internal property of O to false # 4. Return O. Object.preventExtensions object 


Object.isFrozen(オブジェクト)


オブジェクトが凍結されているかどうかを判断します。

 object = property: 1 Object.isFrozen object # false Object.freeze object Object.isFrozen object # true 

仕様によるObject.isFrozen()の実装

 Object.isFrozen = (object) -> # 1. If Type(O) is not Object throw a TypeError exception. if Object::toString.call(object) is not '[object Object]' throw new TypeError "Object.isFrozen: # {object} is not callable!" # 2. For each named own property name P of O then Object.getOwnPropertyNames(object).forEach (property) -> # a. Let desc be the result of calling the [[GetOwnProperty]] internal method of O with P. __desc__ = Object.getOwnPropertyDescriptor object, property # b. If IsDataDescriptor(desc) is true then # i. If desc.[[Writable]] is true, return false. if __desc__.value and __desc__.writable is on return off # c. If desc.[[Configurable]] is true, then return false. if __desc__.configurable is on return off # 3. If the [[Extensible]] internal property of O is false, then return true. # 4. Otherwise, return false. if !Object.isExtensible(object) then on else off 


Object.prototype.hasOwnProperty(プロパティ)


オブジェクトのプロパティが独自のものかどうかを判断します。ブール値を返します。

 object = property: 1 object.hasOwnProperty 'property' # true object.hasOwnProperty 'toString' # false 

あなたは、あなたは演算子を使用する必要があり、その場合に限らず、自分の性質だけでなく、継承された、確認したい場合プロトタイプチェーンを調べ、:

 object = property: 1 'property' of object # true 'toString' of object # true 

.hasOwnProperty()メソッドはfor-ofループと併用すると特に便利です

 Object::inherited = 0 object = property: 1 (i for i of object) # [inherited, property] for i of object i if object.hasOwnProperty i # property 

ご覧のとおり、hasOwnProperty()メソッドは、継承されたプロパティが列挙に含まれないことを保証できます。
ただし、hasOwnPropertyという名前のプロパティが列挙オブジェクトに既に存在する可能性があります

 Object::inherited = -> object = own: 1 hasOwnProperty: -> @ for i of object i if object.hasOwnProperty i # inherited, own, hasOwnProperty 

もちろん、これは取得したい結果ではありません。それではどうしますか?

この状況は非常に簡単に解決できます。必要なオブジェクトのコンテキストでObject.prototypehasOwnPropertyメソッド呼び出すだけです。

 Object::inherited = -> object = own: 1 hasOwnProperty: -> @ for i of object i if Object::hasOwnProperty.call object, i # own, hasOwnProperty 

.hasOwnPropertyメソッドを呼び出すこのメソッドが最も優先されます。1つ目は、名前の競合の可能性を解決し、2つ目は、メソッドへの直接参照により高速なフィルタリングを提供することです。

前述のように、オブジェクトのプロパティがそれ自体の中に見つからない場合、検索はプロトタイプチェーンに沿ってさらに続行されます。
これは、継承されたプロパティも暗黙的に列挙されることを意味します。そして以来hasOwnProperty()メソッドは継承され、少なくとも以下のプロパティの中から検索されます:

 Object.getOwnPropertyNames Object.prototype [ 'toString', 'toLocaleString', 'hasOwnProperty', 'valueOf', 'constructor', 'propertyIsEnumerable', 'isPrototypeOf', ] 

実装に応じて、このリストは拡張される場合があります。たとえば、V8PrestoGeckoエンジンの場合、これらは次のプロパティになります。

  '__lookupGetter__', '__defineGetter__', '__defineSetter__', '__lookupSetter__' 

ではGeckoのさらに他のプロパティがします見るunwatch

したがって、目的のプロパティを検索するリソースを無駄にしないために、常に明示的にその場所を示し、ループ本体の外部のオブジェクトへの変数と参照の定義を行ってください。

 object = {} own = Object::hasOwnProperty for i of object i if own.call object, i 

hasOwnProperty()メソッドfor-ofステートメントと組み合わせて使用​​されることが多いためCoffeeScriptにownという特別な演算子があります。

 alert i for own i of object 

ブロードキャスト結果:

 var i, __hasProp = {}.hasOwnProperty; for (i in object) { if (!__hasProp.call(object, i)) continue; alert(i); } 

独自のステートメントおよびfor-of命令の詳細については、CoffeeScript:ループの詳細ガイドを参照してください

オブジェクトのプロパティの名前を取得するためのObject.keys()およびObject.getOwnPropertyNames()メソッドがあります。これはfor-own-ofステートメントよりもこのタスクに適しています

Object.prototype.isPrototypeOf(オブジェクト)


指定されたオブジェクトがプロトタイプチェーンにあるかどうかを確認します。ブール値を返します。

 object = {} Object::isPrototypeOf object # true Object.isPrototypeOf object # false Function::isPrototypeOf Object # true Function::isPrototypeOf (new ->).constructor # true fn = -> instance = new fn fn::.isPrototypeOf instance # true 


Object.prototype.propertyIsEnumerable(オブジェクト)


指定されたプロパティが列挙可能かどうかを確認します。ブール値を返します。

 object = property: 1 object.propertyIsEnumerable 'property' # true object.propertyIsEnumerable 'toString' # false object.propertyIsEnumerable 'prototype' # false object.propertyIsEnumerable 'constructor' # false list = [''] list.propertyIsEnumerable 0 # true list.propertyIsEnumerable 'length' # false 


結論:


仕様に従ってオブジェクトのすべてのメソッドとその内部実装を十分に検討しました残念ながら、ECMAScript 5の多くのメソッドを、それらをサポートしていないブラウザーに実装することは非常に問題があります。部分的な実装が私の中に見つけることができるgithab「またはE ここに

あなたがソースコードをブロードキャストしますしている場合はJavaScriptを、あなたも読んでください。この適合表を。

Source: https://habr.com/ru/post/J146440/


All Articles