JavaScriptのオブジェクトについてまとめました。
オブジェクトとは
オブジェクトとは、プロパティとメソッドが集まったもの。プロパティはオブジェクトのデータや属性といったものにあたり、メソッドはオブジェクトが行う処理のこと。
オブジェクトの作成方法の種類
JavaScriptでオブジェクトを作成する方法はオブジェクトリテラル、コンストラクタ関数、クラスを使う方法の3種類ある。
オブジェクトリテラル
オブジェクトリテラルで最も手軽にオブジェクトを作成できる方法で、{}
を使って書く。コンストラクタ関数やクラスと異なり、引数に値を渡すといったことはできないので固定の値を持ったオブジェクトになる。
const person = { // プロパティ age: 10, // メソッド hello: function() { console.log('hello') } }
{}
によって作られたオブジェクトはJavaScriptのビルトインオブジェクトであるObject
のインスタンス。
ビルトインオブジェクトとは、JavaScriptにもともと定義されているオブジェクト。
Object
は全てのオブジェクトの元となるオブジェクト。
Object
にnew
演算子を使ってインスタンスを生成することもできるが、{}
の方が簡潔に書けるため使う必要はない。
const obj = new Object() console.log(obj) // {}
プロパティへのアクセス方法
プロパティにアクセスするにはドット記法とブラケット記法の2種類ある。
// ドット記法 console.log(person.age) // ブラケット記法 console.log(person['age'])
存在しないプロパティにアクセスするとundefined
が返される。
console.log(person.name)
コンストラクタ関数
コンストラクタ関数は複数の似たオブジェクトを作成するための関数で、関数名は大文字から始める。
コンストラクタ関数を定義しただけではオブジェクトは作成されず、new
演算子を使って実際にオブジェクトを作成する。
仮引数を作っておき、new
演算子でインスタンス化するときに引数に値を渡すことで、構造は同じだが値が異なるオブジェクトを作成できる。
function Person(name) { // プロパティ this.name = name } // インスタンスを作成 const takashi = new Person('takashi') const hanako = new Person('hanako') console.log(takashi) // Person {name: 'takashi'} console.log(hanako) // Person {name: 'hanako'}
インスタンスメソッド・プロトタイプメソッド
コンストラクタ関数に直接記述した関数はインスタンスメソッドになる。
function Obj() { // インスタンスメソッド this.fn = function() { console.log('fn') } }
インスタンスメソッドはインスタンスごとに作られるメソッド。そのためインスタンスの数だけメソッドが定義される。
インスタンス固有の動作やインスタンスの状態と処理が結びつく処理を書きたいときはインスタンスメソッドを使う。
一方、インスタンス間で共通の処理を実装したいときはプロトタイプメソッドを定義する。
プロトタイプメソッドはコンストラクタ関数のprototype
オブジェクトに記述する。
インスタンスメソッドはインスタンス化したオブジェクトそれぞれで定義されるが、プロトタイプメソッドはprototype
オブジェクトに定義されたメソッドへの参照を保持するので、1つだけ定義するだけで済む。
1つだけで済むということはそれだけメモリの消費が少なくなる。
イメージとしてはプロトタイプは親のオブジェクトであり、親のオブジェクトにメソッドを定義することで、子にあたるインスタンスで共通して親のメソッドを使えるということ。
function Person(age, name) { this.age = age this.name = name } // プロトタイプメソッド Person.prototype.fn = function() { console.log('fn') }
コンストラクタ関数はただの関数
コンストラクタ関数はnew
演算子を使えばオブジェクトを生成できるというだけで、実際はただの関数。逆に言えば普通の関数もnew
演算子を使えばコンストラクタ関数としてオブジェクトを作成できる。
// 関数宣言 function fn() {} // 関数をnew演算子でインスタンス化 const obj = new fn() console.log(obj) // fn {} // コンストラクタ関数 function Fn() { console.log(this) } // コンストラクタ関数を関数として実行 Fn() // Window
コンストラクタ関数はオブジェクトを作るためのものだが、関数としてつかうこともできてしまうため、追加されたオブジェクトを作るための構文であるクラスを使ったほうが良い。
余談
コンストラクタ関数と似た用語にFunction
コンストラクタがあり、Functionコンストラクタ
と似た用語にFunctionオブジェクト
がある。
Functionオブジェクト
Function
オブジェクトは組み込みのオブジェクトで、関数そのもの。関数宣言や関数式などで作られる関数は全てオブジェクト。
Functionコンストラクタ
Function
コンストラクタはFunction
オブジェクトを作るための組み込みオブジェクト。
const obj = new Function('console.log(this)') obj()
ただし非推奨なので関数式などを使って関数を作るのが普通。
クラス
class
構文はES6から追加されたもので、コンストラクタ関数を書きやすくしたもの。
コンストラクタ同様、1文字目を大文字にする。
他の言語のクラスとは異なり、内部的にはプロトタイプで動いている。
class Person { constructor(age, name) { this.age = age this.name = name } hello() { console.log('hello') } // このメソッドの書き方はNG // hello: function() {} } const takashi = new Person('10', 'takashi')
コンストラクタ関数同様、new
演算子を使ってインスタンス化する。
constructor
はクラスが必ず持つ、インスタンス化したときに実行されるメソッド。
インスタンスメソッド・プロトタイプメソッド
constructor
内に定義したメソッドがインスタンスメソッドになり、class
直下に定義したメソッドがプロトタイプメソッドになる。
同じ名前のインスタンスメソッドとプロトタイプメソッドを定義しても上書きされずエラーにならない。インスタンスメソッドはプロトタイプメソッドより優先して実行される。
class Obj { constructor() { // インスタンスメソッド this.fn = function() { console.log('instance') } } // プロトタイプメソッド fn() { console.log('prototype') } } const obj = new Obj() // インスタンスメソッドがプロトタイプメソッドより優先的に呼ばれる obj.fn() // instance
継承
継承する時はextends
を使う。継承することで、親のプロパティやメソッドを子で使用することができる。
// 親のクラスを定義 class Animal { constructor(name) { this.name = name } speak() { console.log(`${this.name}の鳴き声`) } } // 子のクラスを定義 class Dog extends Animal {} // インスタンス化 const sibaken = new Dog('柴犬') // 親のメソッドを使える sibaken.speak() // 柴犬の鳴き声
親のメソッドを上書きする
子クラスで親のメソッドを上書きしたい時は子クラスで同じ名前のメソッドを定義する。
class Animal { constructor(name) { this.name = name } speak() { console.log(`${this.name}の鳴き声`) } } // 子のクラスを定義 class Dog extends Animal { // 親のメソッドを上書き speak() { console.log(`${this.name}が吠えた`); } } // インスタンス化 const sibaken = new Dog('柴犬') // 親のメソッドを使える sibaken.speak() // 柴犬が吠えた
親クラスのconstructorを子クラスでも実行する
親クラスのconstructor
を子クラスでも使いたい時はsuper
という特殊な変数を使う。
子クラスのconstructor
内でsuper
を使うことで子クラスをインスタンス化した時に親クラスのconstructor
を呼び出せる。
class Parent { constructor(name) { this.name = name console.log('親のconstructor'); } } // 子のクラスを定義 class Child extends Parent { constructor(name) { super(name) console.log('子のconstructor') console.log(this.name) // 子供 } } // インスタンス化 const child = new Child('子供')
子クラスのconstructor
でsuper
を記述しないとUncaught ReferenceError: Must call super constructor in derived class before accessing 'this' or returning from derived constructor
が表示される。
また子クラスのconstructor
とsuper
それぞれの引数に親クラスのconstructor
と同じ引数を指定しないまま子クラスでその引数を使おうとするとundefined
になる。
親クラスのメソッドを子クラスでも使う
super
を使うことで親クラスのメソッドを子クラスでも使うことができる。
class Animal { constructor(name) { this.name = name } speak() { console.log(`${this.name}の鳴き声`) } } // 子のクラスを定義 class Dog extends Animal { // 親のメソッドを上書き speak() { // 親のメソッドをそのまま使う super.speak() console.log(`${this.name}が吠えた`); } } // インスタンス化 const sibaken = new Dog('柴犬') // 親のメソッドを使える sibaken.speak()
クラスは関数として呼び出せない
コンストラクタ関数と異なり、クラスは関数として呼び出すことはできない。
class Obj {} Obj() // Uncaught TypeError: Class constructor Obj cannot be invoked without 'new'