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
メソッドは2つの初期化パラメーターを取ることができます。
最初のパラメーターは必須であり、オブジェクトのプロトタイプを設定し、
nullまたはオブジェクトでなければなりません。
2番目のパラメーターはオプションであり、オブジェクトのプロパティを初期化します。
例を考えてみましょう:
object = Object.create {}, property: value: 1 object.property
この例では、最初のパラメーターを空のオブジェクトに設定します。これは、2番目のパラメーターで定義されたプロパティのプロトタイプオブジェクトになります。
神秘的な値のプロパティに悩まされないようにしましょう。少し後でこのトピックをさらに詳しく検討します。
では、最初のオプションと2番目のオプションの違いは何ですか?
次の例で、この質問に対する答えを明確にできると思います。
object = Object.create property: 1 object.property = 2 object.property
一体何をお願いしますか?
単純に、デフォルトの2番目の書き込み形式は、プロパティが読み取り専用であり、削除およびリストできないことを意味します!
例を見てみましょう:
object = Object.create {} property: value: 1 object.property = 2 object.property
ご覧のとおり、プロパティに新しい値を設定したり、列挙したり、削除したりすることはできませんでした。
そして、
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}"
この例では、オブジェクト
Aからプロパティの継承を実装し、
列挙可能な属性を追加して、オブジェクトBで定義されたプロパティを
for-ofループにリストできるようにしました。
for-ofステートメントに独自の演算子があると、ループの本体で
hasOwnPropertyメソッドが使用されず、オブジェクトプロトタイプチェーンでプロパティがチェックされ
ないことに注意してください。
この演算子のより詳細な説明は、以前の
記事にあります。
create()メソッドが内部からどのように機能
するかに興味があるなら、私が読んだものを統合するための割り当てとして、
仕様に従って実装をもたらします:
Object.create = (object, properties) ->
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ではユーザー記述子はありませんが、内部属性
DontEnum 、
ReadOnly 、および
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
第三に、そのようなメソッドは
列挙可能な属性の
真の値を持ちます:
Object::method = -> 1 object = {}; i for i of object
この
方法で
オブジェクトからメソッド
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
また、データ記述子とアクセス記述子を同時に使用することは許可されないことに注意してください。
Object.defineProperty {}, 'property' get: -> 1 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}"
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 = 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}"
アクセサーの再定義が機能しないことを思い出してください。
object = Object.defineProperty {}, 'property' get: -> 0 Object.defineProperty object, 'property' get: -> 1 object.property
明らかに、プロパティをオーバーライドするには、
構成可能な属性の値を変更する必要があります。
object = Object.defineProperty {}, 'property' get: -> 0 configurable: on Object.defineProperty object, 'property' get: -> 1 object.property
残念ながら、
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'
なぜなら 記述子プロパティは内部
ECMAScriptプロパティであり、
Object.getOwnPropertyDescriptor()は、そのような情報を取得できる唯一のメソッドです。
Object.getOwnPropertyDescriptor()は独自のプロパティでのみ機能することに注意して
ください :
Object.getOwnPropertyDescriptor {}, 'valueOf'
valueOfプロパティは継承され、プロトタイプチェーン内にあるため:
{}.hasOwnProperty 'valueOf'
既に述べたように、
hasOwnProperty()メソッドは、of演算子とは異なり、独自のプロパティのみをチェックします。
次のように、
valueOfプロパティに直接アクセスできます。
{}.constructor::valueOf
明確にするために、次の例を検討します。
object = {} object.constructor::valueOf = 1 object.valueOf
一見したところ、プロパティをオブジェクトに明示的に追加する場合とプロトタイプオブジェクトを使用して追加する場合に違いはなく、これらの記述形式は相互に置き換えることができます。 ただし、これは完全に真実ではありません。
object = {} object.valueOf = 1 object.constructor::valueOf = 2 object.toString
ご覧のとおり、プロパティがオブジェクトに明示的に設定されている場合(
独自のプロパティ )、その値はプロトタイプオブジェクトで指定されたプロパティと「オーバーラップ」します。 より正確には、プロトタイプオブジェクトのプロパティの値は消えず、単に解決せず、常に直接利用できます。
object = {} object.toString = 1 object.constructor::valueOf = 2 object.toString
つまり、オブジェクトのプロパティが明示的に設定されていない場合、プロトタイプチェーンで検索が続行されます。
object = {} object.constructor::constructor::constructor::property = 1 object.property
宿題として、次の例を検討することをお勧めします。
fn = (x) -> x * x fn 2
Object.keys(オブジェクト)
オブジェクトの列挙された独自のプロパティの名前を含む配列を返します。
典型的な例を考えてみましょう:
Object.keys a: 1 b: 2 .length
多くの場合、
Object.keysは
Arrayオブジェクトの他のメソッドと組み合わせて使用されます。
object = a: 1 b: 2 Object.keys(object).filter (i) -> i if object[i] > 1
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) ->
Object.getOwnPropertyNames(オブジェクト)
オブジェクトのプロパティの名前を含む配列を返します。
Object.keys(オブジェクト)とは異なり
、このメソッドは
列挙可能な属性の値を考慮しません。
object = a: 1 b: 2 Object.defineProperty object, '' value: 3, enumerable: off, Object.keys(object).length
仕様による
Object.getOwnPropertyNames()の実装:
Object.getOwnPropertyNames = (object) ->
Object.getPrototypeOf(オブジェクト)
指定されたオブジェクトの
[[Prototype]]プロパティへの参照を返します。
object = {} Object.getPrototypeOf(object).property = 1 object.property
メソッドの実装:
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
メソッド
Object.definePropertyとObject.definePropertiesを使用してロックされたオブジェクトを拡張しようとすると、
TypeError型の例外がスローされるという事実に特に注意を払いたいと思います。
object = {} Object.preventExtensions object Object.defineProperty object, 'property' value: 1
また、厳格モードで
はTypeErrorが常にスローされます。
do -> 'use strict' object = {} Object.preventExtensions object object.property = 1
この場合、
TypeErrorをキャッチしないとアプリケーションが動作しなくなるため、特に注意してください!
Object.isExtensible(オブジェクト)
オブジェクト拡張の可用性を決定します。 ブール値を返します。
object = {} Object.preventExtensions object Object.isExtensible object
Object.seal(オブジェクト)
オブジェクトのプロパティをシールします。
Object.seal = Object.preventExtensions + {[[Configurable]]: off}
例を考えてみましょう:
object = property: 1 Object.seal object delete object.property
Object.seal()は、すべてのオブジェクトプロパティの
構成可能な属性に
falseを
設定します。
Object.getOwnPropertyDescriptor(Object.seal property: 1, 'property').configurable
オブジェクト自体が封印されているのではなく、そのプロパティのみが封印されていることに注意してください。
object = {} Object.defineProperty, 'property' value: 1 Object.seal object delete object.property
ご覧のとおり、
delete [[Delete]]演算子はオブジェクトのプロパティのみを
削除するため、
オブジェクトの削除は機能しません。
これは、
JavaScriptコードの翻訳段階で、すべての変数の前にvarキーワードが付いているためです。
オブジェクト自体を削除するには、グローバルグローバルオブジェクトのプロパティにする必要があります。
global.object = {}
ただし、オブジェクトはグローバルスコープに該当するため、このようなオブジェクトの定義はお勧めできません。
do -> global.object = {} object
オブジェクトを削除する場合は、未定義の値を割り当ててください。
object = {} object = undefined object
ある時点で再びこの変数にアクセスする必要がある場合は、
null演算子を使用する必要があり
ます。null演算子は、変数にオブジェクトへの参照が含まれていたことを通知します。
1. CoffeeScriptにはvoid演算子はありませんが、代わりにundefinedを使用する必要があり、これはvoid 0に変換されます 。
2.定義済みプロパティの削除に関する明確なルールはありません。
たとえば、 Dateオブジェクトのnowプロパティを削除することは完全に許容できますが、同時にFunctionオブジェクトの呼び出しメソッドを削除することはできません。
delete Date.now
同時に、コンストラクタ自体を削除することもできます。 およびオブジェクト([構成可能]]:true) :
delete Object
2.削除演算子をより詳細に検討しなかったのは、 これは、この記事に含めるべき非常に大きなトピックです。 この質問にまだ興味がある場合は、記事kangax 'a Understanding deleteを読むことをお勧めします
Object.seal()がオブジェクトプロパティの削除を禁止していることに加えて、新しいプロパティの追加もブロックします。
object = {} Object.seal object object.property = 1 object.property
注:サーバー実装は
TypeErrorをスローします!
ただし、シーリングはオブジェクトのプロパティ値の変更には適用されません。
object = property: 1 Object.seal object object.property = 2 object.property
Object.preventExtensions()と
同様に 、strictモードでオブジェクトのプロパティを変更しようとしたり、
Object.defineProperty / Object.definePropertiesを使用すると
、TypeError例外が
スローされます!
仕様に従った
Object.seal()の実装:
Object.seal = (object) ->
Object.isSealed(オブジェクト)
オブジェクトがシールされているかどうかを判別します。 ブール値を返します。
object = {} Object.seal object Object.isSealed object
空のオブジェクトを非拡張可能にすると、シールされます。
object = {} Object.preventExtensions object Object.isSealed object
ただし、オブジェクトにプロパティを追加すると、シールが解除されます。
object = property: 1 Object.preventExtensions object Object.isSealed object
仕様に従った
Object.isSealed()の実装:
Object.isSealed = (object) ->
Object.freeze(オブジェクト)
オブジェクトをフリーズします。
Object.freeze = Object.preventExtensions + Object.seal + {[[Writable]]: off}
つまり、
Object.freeze()は、オブジェクトへの新しいプロパティの追加、既存のプロパティの変更および削除を防ぎます。
object = a: 1 object.a = 0
なぜなら 既に
Object.preventExtensions()と
Object.seal()を詳細に調べ
ましたが、繰り返すのは意味がありません。
注目したいのは、「フリーズ」の深さだけです。
object = property: internal: 1 Object.freeze object object.property = 0
ご覧のとおり、最初のレベルの子プロパティのみがブロックされています!
実際、
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
仕様に従った
Object.freeze()の実装:
Object.freeze = (object) ->
Object.isFrozen(オブジェクト)
オブジェクトが凍結されているかどうかを判断します。 object = property: 1 Object.isFrozen object
仕様によるObject.isFrozen()の実装: Object.isFrozen = (object) ->
Object.prototype.hasOwnProperty(プロパティ)
オブジェクトのプロパティが独自のものかどうかを判断します。ブール値を返します。 object = property: 1 object.hasOwnProperty 'property'
あなたは、あなたは演算子を使用する必要があり、その場合に限らず、自分の性質だけでなく、継承された、確認したい場合のプロトタイプチェーンを調べ、: object = property: 1 'property' of object
.hasOwnProperty()メソッドは、for-ofループと併用すると特に便利です。 Object::inherited = 0 object = property: 1 (i for i of object)
ご覧のとおり、hasOwnProperty()メソッドは、継承されたプロパティが列挙に含まれないことを保証できます。ただし、hasOwnPropertyという名前のプロパティが列挙オブジェクトに既に存在する可能性があります。 Object::inherited = -> object = own: 1 hasOwnProperty: -> @ for i of object i if object.hasOwnProperty i
もちろん、これは取得したい結果ではありません。それではどうしますか?この状況は非常に簡単に解決できます。必要なオブジェクトのコンテキストでObject.prototypeのhasOwnPropertyメソッドを呼び出すだけです。 Object::inherited = -> object = own: 1 hasOwnProperty: -> @ for i of object i if Object::hasOwnProperty.call object, i
.hasOwnPropertyメソッドを呼び出すこのメソッドが最も優先されます。1つ目は、名前の競合の可能性を解決し、2つ目は、メソッドへの直接参照により高速なフィルタリングを提供することです。前述のように、オブジェクトのプロパティがそれ自体の中に見つからない場合、検索はプロトタイプチェーンに沿ってさらに続行されます。これは、継承されたプロパティも暗黙的に列挙されることを意味します。そして以来
hasOwnProperty()メソッドは継承され、少なくとも以下のプロパティの中から検索されます: Object.getOwnPropertyNames Object.prototype [ 'toString', 'toLocaleString', 'hasOwnProperty', 'valueOf', 'constructor', 'propertyIsEnumerable', 'isPrototypeOf', ]
実装に応じて、このリストは拡張される場合があります。たとえば、V8、Presto、Geckoエンジンの場合、これらは次のプロパティになります。 '__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
Object.prototype.propertyIsEnumerable(オブジェクト)
指定されたプロパティが列挙可能かどうかを確認します。ブール値を返します。 object = property: 1 object.propertyIsEnumerable 'property'
結論:
仕様に従って、オブジェクトのすべてのメソッドとその内部実装を十分に検討しました。残念ながら、ECMAScript 5の多くのメソッドを、それらをサポートしていないブラウザーに実装することは非常に問題があります。部分的な実装が私の中に見つけることができるgithab「またはE ここにあなたがソースコードをブロードキャストしますしている場合はJavaScriptを、あなたも読んでください。この適合表を。