GitBookjs-primerオブジェクトオブジェクトはプロパティの集合です。プロパティとは名前(キー)と値(バリュー)が対になったものです。プロパティのキーには文字列またはSymbolが利用でき、値には任意のデータを指定できます。1つのオブジェクトは複数のプロパティを持てるため、1つのオブジェクトで多種多様な値を表現できます。今までも登場してきた、配列や関数などもオブジェクトの一種です。JavaScriptには、あらゆるオブジェクトの元となるObjectというビルトインオブジェクトがあります。ビルトインオブジェクトは実行環境に組み込まれたオブジェクトのことです。ObjectというビルトインオブジェクトはECMAScriptの仕様で定義されているため、あらゆるJavaScriptの実行環境で利用できます。この章では、オブジェクトの作成や扱い方、Objectというビルトインオブジェクトについて見ていきます。オブジェクトを作成するオブジェクトを作成するには、オブジェクトリテラル({})を利用します。{};オブジェクトリテラルでは、初期値としてプロパティを持つオブジェクトを作成できます。プロパティは、オブジェクトリテラル({})の中にキーと値を:(コロン)で区切って記述します。"value"};オブジェクトリテラルのプロパティ名(キー)はクォート("や')を省略できます。そのため、次のように書いても同じです。"value"};ただし、ハイフンを含むプロパティ名(キー)はクォート("や')を省略できません。OK};オブジェクトリテラルでは複数のプロパティ(キーと値の組み合わせ)を持つオブジェクトも作成できます。複数のプロパティを定義するには、それぞれのプロパティを,(カンマ)で区切ります。"blue"};プロパティの値に変数名を指定すれば、そのキーは指定した変数を参照します。}のように省略して書けます。次のコードは、プロパティ名nameに変数nameを値にしたプロパティを設定しています。}この省略記法は、モジュールや分割代入においても共通した表現です。そのため、{}の中でプロパティ名が単独で書かれている場合は、この省略記法を利用していることに注意してください。{}はObjectのインスタンスオブジェクトObjectはJavaScriptのビルトインオブジェクトです。オブジェクトリテラル({})は、このビルトインオブジェクトであるObjectを元にして新しいオブジェクトを作成するための構文です。オブジェクトリテラル以外の方法として、new演算子を使うことで、Objectから新しいオブジェクトを作成できます。Object()でオブジェクトを作成していますが、これは空のオブジェクトリテラルと同じ意味です。Object()を使う利点はありません。Object()でオブジェクトを作成することは、「Objectのインスタンスオブジェクトを作成する」と言います。Objectやインスタンスオブジェクトなどややこしい言葉の使い分けが必要となってしまいます。Objectどちらの方法であっても、単に「オブジェクトを作成する」と呼びます。オブジェクトリテラルは、Objectから新しいインスタンスオブジェクトを作成していることを意識しておくとよいでしょう。プロパティへのアクセスオブジェクトのプロパティにアクセスする方法として、ドット記法(.)を使う方法とブラケット記法([])があります。それぞれの記法で、オブジェクトの右辺へプロパティ名を指定すると、その名前を持ったプロパティの値を参照できます。"value"ドット記法(.)では、プロパティ名が変数名と同じく識別子の命名規則を満たす必要があります(詳細は「変数と宣言」の章を参照)。NG一方、ブラケット記法では、[と]の間に任意の式を書けます。そのため、識別子の命名規則とは関係なく、任意の文字列をプロパティ名として指定できます。プロパティ名は文字列へと暗黙的に変換されることに注意してください。"my-value"また、ブラケット記法ではプロパティ名に変数も利用できます。次のコードでは、プロパティ名にmyLangという変数をブラケット記法で指定しています。"日本語"ドット記法ではプロパティ名に変数は利用できないため、プロパティ名に変数を指定した場合はブラケット記法を利用します。基本的には簡潔なドット記法(.)を使い、ドット記法で書けない場合はブラケット記法([])を使うとよいでしょう。同じオブジェクトのプロパティを何度もアクセスする場合に、何度もオブジェクト.プロパティ名と書くと冗長となりやすいです。そのため、短い名前で利用できるように、そのプロパティを変数として定義し直すことがあります。次のコードでは、変数jaとenを定義し、その初期値としてlanguagesオブジェクトのプロパティを代入しています。assignment)が利用できます。オブジェクトの分割代入では、左辺にオブジェクトリテラルのような構文で変数名を定義します。右辺のオブジェクトから対応するプロパティ名が、左辺で定義した変数に代入されます。次のコードでは、先ほどのコードと同じようにlanguagesオブジェクトからjaとenプロパティを取り出して変数として定義しています。代入演算子のオペランドとして左辺と右辺それぞれにjaとenと書いていたのが、分割代入では一箇所に書くことができます。"英語"プロパティの追加オブジェクトは、一度作成した後もその値自体を変更できるというミュータブル(mutable)の特性を持ちます。そのため、作成したオブジェクトに対して、後からプロパティを追加できます。プロパティの追加方法は単純で、作成したいプロパティ名へ値を代入するだけです。そのとき、オブジェクトに指定したプロパティが存在しないなら、自動的にプロパティが作成されます。プロパティの追加はドット記法、ブラケット記法どちらでも可能です。"value"先ほども紹介したように、ドット記法は変数の識別子として利用可能なプロパティ名しか利用できません。ブラケット記法はobject[式]の式の評価結果を文字列にしたものをプロパティ名として利用できます。そのため、次のものをプロパティ名として扱う場合にはブラケット記法を利用します。key"ブラケット記法を用いたプロパティ定義は、オブジェクトリテラルの中でも利用できます。namesと呼ばれます。namesはES2015から導入された記法ですが、式の評価結果をプロパティ名に使う点はブラケット記法と同じです。namesを使いkey変数の評価結果である"key-string"をプロパティ名にしています。"value"JavaScriptのオブジェクトは、作成後にプロパティが変更可能というmutableの特性を持つことを紹介しました。そのため、関数が受け取ったオブジェクトに対して、勝手にプロパティを追加できてしまいます。次のコードは、changeProperty関数が引数として受け取ったオブジェクトにプロパティを追加している悪い例です。"value"このように、プロパティを初期化時以外に追加してしまうと、そのオブジェクトがどのようなプロパティを持っているかがわかりにくくなります。そのため、できる限り作成後に新しいプロパティは追加しないほうがよいでしょう。オブジェクトの作成時のオブジェクトリテラルの中でプロパティを定義することを推奨します。プロパティの削除オブジェクトのプロパティを削除するにはdelete演算子を利用します。削除したいプロパティをdelete演算子の右辺に指定して、プロパティを削除できます。先ほどのコード例で、constで宣言したオブジェクトのプロパティがエラーなく変更できていることがわかります。次のコードを実行してみると、値であるオブジェクトのプロパティが変更できていることがわかります。"Hi!"JavaScriptのconstは値を固定するのではなく、変数への再代入を防ぐためのものです。そのため、次のようなobj変数への再代入は防げますが、変数に代入された値であるオブジェクトの変更は防げません(「変数と宣言のconstについて」を参照)。SyntaxError作成したオブジェクトのプロパティの変更を防止するにはObject.freezeメソッドを利用する必要があります。Object.freezeはオブジェクトを凍結します。凍結されたオブジェクトでプロパティの追加や変更を行うと例外が発生するようになります。modeと合わせて使います。modeではない場合は、凍結されたオブジェクトのプロパティを変更しても例外が発生せずに単純に無視されます。read-onlyプロパティの存在を確認するJavaScriptでは、存在しないプロパティに対してアクセスした場合に例外ではなくundefinedを返します。次のコードは、objには存在しないnotFoundプロパティにアクセスしているため、undefinedという値が返ってきます。undefinedこのように、JavaScriptでは存在しないプロパティへアクセスした場合に例外が発生しません。プロパティ名を間違えた場合に単にundefinedという値を返すため、間違いに気づきにくいという問題があります。次のようにプロパティ名を間違えていた場合にも、例外が発生しません。プロパティ名をネストしてアクセスした場合に、初めて例外が発生します。例外が発生した文以降は実行されませんundefinedやnullはオブジェクトではないため、存在しないプロパティへアクセスすると例外が発生してしまいます。あるオブジェクトがあるプロパティを持っているかを確認する方法として、次の3つがあります。存在しないプロパティへアクセスした場合にundefinedを返すため、実際にプロパティアクセスすることでも判定できそうです。次のコードでは、keyプロパティの値がundefinedではないという条件式で、プロパティが存在するかを判定しています。console.log("`key`プロパティの値は`undefined`ではない");}しかし、この方法はプロパティの値がundefinedであった場合に、プロパティそのものが存在するかを区別できないという問題があります。次のコードでは、keyプロパティの値がundefinedであるため、プロパティが存在しているにもかかわらずif文の中は実行されません。この行は実行されません}このような問題があるため、プロパティが存在するかを判定するにはin演算子かhasOwnPropertyメソッドを利用します。in演算子を使うin演算子は、指定したオブジェクト上に指定したプロパティがあるかを判定できます。false次のコードではobjにkeyプロパティが存在するかを判定しています。in演算子は、プロパティの値は関係なく、プロパティが存在した場合にtrueを返します。hasOwnPropertyメソッドオブジェクトのhasOwnPropertyメソッドは、オブジェクト自身が指定したプロパティを持っているかを判定できます。このhasOwnPropertyメソッドの引数には、存在を判定したいプロパティ名を渡します。false次のコードではobjにkeyプロパティが存在するかを判定しています。hasOwnPropertyメソッドも、プロパティの値は関係なく、オブジェクトが指定したプロパティを持っている場合にtrueを返します。console.log("`object`は`key`プロパティを持っている");}in演算子とhasOwnPropertyメソッドは同じ結果を返していますが、厳密には動作が異なるケースもあります。この動作の違いを知るにはまずプロトタイプオブジェクトという特殊なオブジェクトについて理解する必要があります。次の章の「プロトタイプオブジェクト」で詳しく解説するため、次の章でin演算子とhasOwnPropertyメソッドの違いを見ていきます。toStringメソッドオブジェクトのtoStringメソッドは、オブジェクト自身を文字列化するメソッドです。Stringコンストラクタ関数を使うことでも文字列化できます。この2つにはどのような違いがあるのでしょうか?(Stringコンストラクタ関数については「暗黙的な型変換」を参照)実はStringコンストラクタ関数は、引数に渡されたオブジェクトのtoStringメソッドを呼び出しています。そのため、Stringコンストラクタ関数とtoStringメソッドの結果はどちらも同じになります。Object]"このことは、オブジェクトにtoStringメソッドを再定義してみるとわかります。独自のtoStringメソッドを定義したオブジェクトをStringコンストラクタ関数で文字列化してみます。すると、再定義したtoStringメソッドの返り値が、Stringコンストラクタ関数の返り値になることがわかります。オブジェクトのプロパティ名は文字列化されるオブジェクトのプロパティへアクセスする際に、指定したプロパティ名は暗黙的に文字列に変換されます。ブラケット記法では、オブジェクトをプロパティ名に指定することもできますが、これは意図したようには動作しません。Object]"という文字列になるためです。次のコードでは、keyObject1とkeyObject2をブラケット記法でプロパティ名に指定しています。Object]"という同じプロパティ名となります。そのため、プロパティは意図せず上書きされてしまいます。}唯一の例外として、Symbolだけは文字列化されずにオブジェクトのプロパティ名として扱えます。"2"基本的にはオブジェクトのプロパティ名は文字列として扱われることを覚えておくとよいでしょう。Mapというビルトインオブジェクトはオブジェクトをキーとして扱えます(詳細は「Map/Set」の章で解説します)。オブジェクトの静的メソッド最後にビルトインオブジェクトであるObjectの静的メソッドについて見ていきましょう。静的メソッド(スタティックメソッド)とは、インスタンスの元となるオブジェクトから呼び出せるメソッドのことです。これまでのtoStringメソッドなどは、Objectのインスタンスオブジェクトから呼び出すメソッドでした。これに対して、静的メソッドはObjectそのものから呼び出せるメソッドです。ここでは、オブジェクトの処理でよく利用されるいくつかの静的メソッドを紹介します。最初に紹介したように、オブジェクトはプロパティの集合です。そのオブジェクトのプロパティを列挙する方法として、次の3つの静的メソッドがあります。オブジェクトのプロパティ名と値の配列の配列を返すそれぞれ、オブジェクトのキー、値、キーと値の組み合わせを配列にして返します。3]]これらの列挙する静的メソッドと配列のforEachメソッドなどを組み合わせれば、プロパティに対して反復処理ができます。次のコードでは、Object.keysメソッドで取得したプロパティ名の一覧をコンソールへ出力しています。"three"オブジェクトのマージと複製Object.assignメソッド[ES2015]は、あるオブジェクトを別のオブジェクトに代入(assign)できます。このメソッドを使うことで、オブジェクトの複製やオブジェクト同士のマージができます。Object.assignメソッドは、targetオブジェクトに対して、1つ以上のsourcesオブジェクトを指定します。sourcesオブジェクト自身が持つ列挙可能なプロパティを第一引数のtargetオブジェクトに対してコピーします。Object.assignメソッドの返り値は、targetオブジェクトになります。...sources);オブジェクトのマージ具体的なオブジェクトのマージの例を見ていきます。次のコードでは、新しく作った空のオブジェクトをtargetにしています。この空のオブジェクト(target)にobjectAとobjectBをマージしたものが、Object.assignメソッドの返り値となります。}第一引数には空のオブジェクトではなく、既存のオブジェクトも指定できます。引数に既存のオブジェクトを指定した場合は、そのオブジェクトのプロパティが変更されます。次のコードでは、第一引数に指定されたobjectAに対してプロパティが追加されています。true空のオブジェクトをtargetにすることで、既存のオブジェクトには影響を与えずマージしたオブジェクトを作ることができます。そのため、Object.assignメソッドの第一引数には、空のオブジェクトリテラルを指定するのが典型的な利用方法です。このとき、プロパティ名が重複した場合は、後ろのオブジェクトのプロパティにより上書きされます。JavaScriptでは、基本的に処理は先頭から後ろへと順番に行います。そのため、空のオブジェクトへobjectAを代入してから、その結果にobjectBを代入するという形になります。}オブジェクトのspread構文でのマージES2018では、オブジェクトのマージを行うオブジェクトの...(spread構文)が追加されました。ES2015で配列の要素を展開する...(spread構文)はサポートされていましたが、オブジェクトに対してもES2018でサポートされました。オブジェクトのspread構文は、オブジェクトリテラルの中に指定したオブジェクトのプロパティを展開できます。オブジェクトのspread構文は、Object.assignとは異なり必ず新しいオブジェクトを作成します。spread構文はオブジェクトリテラルの中でのみ記述でき、オブジェクトリテラルは新しいオブジェクトを作成するためです。次のコードではobjectAとobjectBをマージした新しいオブジェクトを返します。}プロパティ名が被った場合の優先順位は、後ろにあるオブジェクトが優先されます。そのため同じプロパティ名を持つオブジェクトをマージした場合には、後ろにあるオブジェクトによってプロパティが上書きされます。}オブジェクトの複製JavaScriptには、オブジェクトを複製する関数は用意されていません。新しく空のオブジェクトを作成し、そこへ既存のオブジェクトのプロパティをコピーすれば、それはオブジェクトの複製をしていると言えます。次のように、Object.assignメソッドを使うことでオブジェクトを複製できます。copy)する点です。copyとは、sourcesオブジェクトの直下にあるプロパティだけをコピーするということです。そのプロパティの値がオブジェクトである場合に、ネストした先のオブジェクトまでも複製するわけではありません。copy)と呼びます。copyすることで実現できます。次のコードでは、deepCloneをshallowCloneを使うことで実現しています。falseこのように、JavaScriptのビルトインメソッドは浅い(shallow)実装のみを提供し、深い(deep)実装は提供していないことが多いです。言語としては最低限の機能を提供し、より複雑な機能はユーザー側で実装するという形式を取るためです。JavaScriptは言語仕様で定義されている機能が最低限であるため、それを補うようにユーザーが作成した小さな機能を持つライブラリが数多く公開されています。それらのライブラリはnpmと呼ばれるJavaScriptのパッケージ管理ツールで公開され、JavaScriptのエコシステムを築いています。Node.jsでCLIアプリケーション」の章で紹介します。まとめこの章では、オブジェクトについて学びました。Objectというビルトインオブジェクトがある{}(オブジェクトリテラル)でのオブジェクトの作成や更新方法プロパティの存在確認するin演算子とhasOwnPropertyメソッドオブジェクトのインスタンスメソッドと静的メソッドJavaScriptのObjectは他のオブジェクトのベースとなるオブジェクトです。次の「プロトタイプオブジェクト」の章では、Objectがどのようにベースとして動作しているのかを見ていきます。