コンテンツにスキップ

オブジェクト

はじめに

本サイトにつきまして、【IT技術の知見】はじめに - 俺の技術ノート をご認識のほど宜しくお願いいたします。


01. 標準ビルトインオブジェクト

標準ビルトインオブジェクトとは

JavaScript の実行環境にあらかじめ組み込まれたオブジェクト。


Object

オブジェクトを作成するオブジェクト。

他の標準ビルトインオブジェクトすべての継承元になっているため、標準ビルトインオブジェクトは、Object が持つ振る舞いと状態を使用できる。

// new演算子を使用してインスタンスを作成する。
const obj = new Object();


02. オブジェクトの作成、初期化

リテラル表記法

変数の宣言と代入を同時に定義する方法である。

オブジェクトをリテラル表記で作成する。

ローワーキャメルケースを使用する。

▼ 非省略形

// リテラル表記
const foo = {
  // 慣習的にアンダースコアでprivateを表す。
  _property: 0,

  alertValue: (value) => {
    alert(value);
  },

  setValue(value) {
    this._property = value;
  },

  getValue() {
    return this._property;
  },
};

▼ 省略形

リテラル表記で、methodA(): fucntion{} とするところを、methodA() {} と記述できる。

// リテラル表記
const foo = {

 // ...

  alertValue(value) {
      alert(value);
  },

  // ...
}


コンストラクタ関数の使用

▼ Object コンストラクタ関数

ローワーキャメルケースを使用する。

プロパティを作成するためには、値を格納する必要がある。

関数宣言あるいは関数式で記述する。

パスカルケース (大文字から始める記法) を使用する。

補足として、オブジェクトのプロパティ値として作成された関数を、関数と呼ぶ。

const foo = new Object({
  // 慣習的にアンダースコアでprivateを表す。
  _property: 0,

  setValue(value) {
    this._property = value;
  },

  getValue() {
    return this._property;
  },
});

▼ Function コンストラクタ関数

const Foo = new Function();

ただし、公式からこのような記法は、非推奨とされている。

以下の関数宣言、関数式、アロー関数による関数式省略の記法が推奨である。

特にアロー関数では、this が宣言されたオブジェクト自身を指すため、保守性が高くおすすめである。

// 関数宣言
function Foo() {
  // 慣習的にアンダースコアでprivateを表す。
  this._property = 0;

  // プロパティ値として宣言した関数を、関数という。
  this.setValue = (value) => {
    this._property = value;
  };

  this.getValue = () => {
    return this._property;
  };
}

// コール
const foo = new Foo();
// 関数式
const Foo = function (value) {
  // 慣習的にアンダースコアでprivateを表す。
  this._property = 0;

  this.setValue = (value) => {
    this._property = value;
  };

  this.getValue = () => {
    return this._property;
  };
};
// アロー関数では、オブジェクトを返す関数として定義する
const Foo = (value = 0) => ({
  // 慣習的にアンダースコアでprivateを表す。
  _property: value,

  setValue(value) {
    this._property = value;
  },

  getValue() {
    return this._property;
  },
});

リテラル表記と Object コンストラクタ関数による作成とは異なり、コンストラクタ関数によって宣言されたオブジェクトは、暗示的に prototype プロパティを持つ。

// リテラル表記による作成
const object1 = {};

// Objectコンストラクタ関数による作成
const object2 = new Object({});

// ユーザー宣言Functionコンストラクタ関数による作成
const Object3 = () => {};

// 出力結果
console.log(
  object1.prototype, // undefined
  object2.prototype, // undefined
  object3.prototype, // Object3 {}
);


糖衣構文の class の使用

▼ JavaScript のクラスとは

ES6 から、糖衣構文の class によって、オブジェクトを宣言できるようになった。

クラス宣言あるいはクラス式で記述する。

オブジェクトの作成時、constructor() 関数でオブジェクトを初期化する。

パスカルケース (大文字から始める記法) を使用する。

▼ クラス宣言記法

// named exportによる出力
export class Foo {

  private property;

  // classでしか使用できない。
  // Setterの代わりにコンストラクタでイミュータブルを実現。
  // 状態の宣言と格納が同時に行われる。
  constructor(value) {
    this.property = value;
  }

  getValue() {
    return this.property;
  }
}
// ファイルの読み出し
import {Foo} from "./foo.js";

// 作成、初期化
const foo = new Foo(1);

// 関数のコール
foo.getValue();

▼ クラス式記法

// named exportによる出力
export const Foo = class {

  private _property;

  // classでしか使用できない。
  // Setterの代わりにコンストラクタでイミュータブルを実現。
  // 状態の宣言と格納が同時に行われる。
  constructor(value) {
    this._property = value;
  }

  getValue() {
    return this._property;
  }
};
// ファイルの読み出し
import {Foo} from "./foo.js";

// 作成、初期化
const foo = new Foo(1);

// 関数のコール
foo.getValue();


03. オブジェクトの操作

プロトタイプチェーンによる継承

▼ プロトタイプチェーンとは

オブジェクトが暗示的に持つ prototype プロパティに、別のオブジェクトのメンバを追加することによって、そのオブジェクトのプロトタイプを継承できる。

オブジェクトからプロパティや関数をコールしたとき、そのオブジェクトにこれらが存在しなければ、継承元まで辿る仕組みを『プロトタイプチェーン』という。

クラスベースのオブジェクト指向で使用されるクラスチェーンについては、別ノートを参照せよ。

▼ new Obejct 関数を使用した継承

// 大元となるオブジェクトは個別ファイルで管理しておくのがベター。
// コンストラクタ関数の関数式による宣言。
const Foo = (value) => {
  // 慣習的にアンダースコアでprivateを表す。
  this._property = 0;

  this.setValue = (value) => {
    this._property = value;
  };

  this.getValue = () => {
    return this._property;
  };
};

別クラスで、以下のように継承する。

// 継承元のオブジェクトのファイルを読み込むことも忘れずに。
// prototypeプロパティの継承先のオブジェクトを宣言。
const SubFoo = (subValue) => {
  // 慣習的にアンダースコアでprivateを表す。
  this.subProperty = subValue;

  this.setSubValue = (subValue) => {
    this.subProperty = subValue;
  };

  this.getSubValue = () => {
    return this.subProperty;
  };
};

// new Foo()を使用した継承。
SubFoo.prototype = new Foo();

// SubFooクラスにはgetValue()は無い。
// 継承元まで辿り、Examlpeクラスから関数がコールされる (プロトタイプチェーン) 。
const result = SubFoo.getValue();
console.log(result);

▼ Object.create() 関数を使用した継承とメンバ追加

// 継承元のオブジェクトのファイルを読み込むことも忘れずに。
// prototypeプロパティの継承先のオブジェクトを宣言。
const SubFoo = () => {
  // 慣習的にアンダースコアでprivateを表す。
  this._property = 0;

  this.setSubValue = (subValue) => {
    this.subProperty = subValue;
  };

  this.getSubValue = () => {
    return this.subProperty;
  };
};

// Object.create()を使用した継承。
SubFoo.prototype = Object.create(Foo.prototype);

// SubFooクラスにはgetValue()は無い。
// 継承元まで辿り、Examlpeクラスから関数がコールされる (プロトタイプチェーン) 。
const result = SubFoo.getValue();
console.log(result);

また、Object.create() 関数を使用する場合、継承のみでなく、メンバを新しく追加もできる。

// Object.create()による継承。
SubFoo.prototype = Object.create(Foo.prototype, {
  // 状態を定義
  subProperty: "テスト",

  // 関数を定義
  printSubValue: () => {
    return "これは" + this.subProperty + "です。";
  },
});

// SubFooクラスにはprintSubValue()が追加された。
const result = SubFoo.printSubValue();
console.log(result);


04. this の参照先

関数としてコールする場合

関数内の this は、foo オブジェクトを指す。

const foo = {
  // 慣習的にアンダースコアでprivateを表す。
  _property: 0,

  setValue(value) {
    this._property = value;
  },

  getValue() {
    return this._property;
  },
};
// 関数内のthisは、fooオブジェクトを指す。
foo.setValue(1);
foo.getValue(); // 1


コンストラクタ関数としてコールする場合

▼ 関数宣言と関数式によるコンストラクタ関数内の this の場合

コンストラクタ関数内の this は、自身がコールされたオブジェクトを指す。

// 一番外側はWindowオブジェクト
param = "global param";

// 関数宣言
function printParam() {
  console.log(this.param);
}

// オブジェクト1
const object1 = {
  param: "object1 param",
  func: printParam,
};

// オブジェクト2
const object2 = {
  param: "object2 param",
  func: printParam,
};
// コンストラクタ関数内のthisの場合
// コンストラクタ関数内のthisは、自身がコールされたオブジェクトを指す。
// ここでは、object1とobject2

object1.printParam; // object1 param
object2.printParam; // object2 param

▼ アロー関数によるコンストラクタ関数内の this の場合

アロー関数内の this の参照先には、十分な注意が必要である。

今まで、JavaScript では、this の参照先が文脈によって変わることに批判が集まっていた。

そこで、参照先が文脈によって変わらない機能が追加された。

this は、自身が宣言されたオブジェクトを指す。

// 一番外側はWindowオブジェクト
param = "global param";

// アロー関数による省略記法
const printParam = () => {
  console.log(this.param);
};

// オブジェクト1
const object1 = {
  param: "object1 param",
  func: printParam,
};

// オブジェクト2
const object2 = {
  param: "object2 param",
  func: printParam,
};
// アロー関数内のthisの場合
// thisは、自身が宣言されたオブジェクトを指す。
// ここでは、一番外側のWindowオブジェクトであり、object1とobject2ではない。
// 参照先は文脈によって変わらない。

object1.printParam; // global param
object2.printParam; // global param

また、アロー関数がコールバック関数として引数に渡されている場合も、this は呼び出し元ではなく、アロー関数を宣言したスコープの this を指す。