オブジェクトとプロパティ

《 初回公開:2022/11/17 , 最終更新:未 》

いまさらながらあらためてオブジェクトとプロパティについてさぐってみる。

【 目次 】

Objectクラスのインスタンスを生成

Objectクラスのインスタンスを生成するには

let obj1 = new Object();

これは、以下のコードと等価である。

let obj2 = {};

この波括弧で囲む表記法をオブジェクトリテラル(表記)という。
またMDNのページではオブジェクト初期化子という言葉で表現されている。

オブジェクト初期化子
オブジェクトリテラル表記法

ObjectクラスのインスタンスもオブジェクトリテラルもObject型のオブジェクト

console.log(obj1.constructor.name); // Object
console.log(obj2.constructor.name); // Object

javascriptのオブジェクトと連想配列とドット記法とブラケット記法

オブジェクトリテラルで記述すると、オブジェクトの生成時にプロパティを同時に設定する事ができる。
また、オブジェクトのプロパティにアクセスするにはドット記法(表記) ブラケット記法(表記)とがあって

オブジェクトリテラルで記述

let obj = { a: "aaa", b: "bbb", c: "ccc" };

これは以下のドット表記によるコードと等価。

let obj = new Object(); // または let obj = {};
obj.a="aaa";
obj.b="bbb";
obj.c="ccc";

また上記のコードの2行目以下をブラケット表記で記述すると。

obj["a"] = "aaa";
obj["b"] = "bbb";
obj["c"] = "ccc";

オブジェクトリテラルとブラケット表記の組み合わせは他の言語の連想配列のコードと同じ(もしくは似ている)。
このため、JavaScriptのオブジェクトはクラスのインスタンスであると同時に、連想配列としても機能もはたしている。

オブジェクトリテラルとドット記法とブラケット記法にはいろいろと規則があって

プロパティ名の指定には引用符で囲む必要があるのと無いものがある

オブジェクトリテラル表記

変数の識別子として利用できるプロパティ名は引用符で囲んでも囲まなくてもどっちでも良い。
数字だけのプロパティ名も引用符で囲んでも囲まなくてもどっちでも良い。
でも、変数の識別子として利用できないプロパティ名は引用符で囲む必要があって。

let obj = {
    // 変数の識別子として利用できるプロパティ名は引用符で囲んでも囲まなくてもどっちでも良い
    a: "aaa",
    "b": "bbb",
    // 数字だけのプロパティ名も引用符で囲んでも囲まなくてもどっちでも良い
    99: "##",
    "999": "###",
    // 変数の識別子として利用できないプロパティ名は引用符で囲む必要があって
    //99t: "%%",    // エラー
    "999t": "%%%"
};

ドット記法

ドット記法でプロパティにアクセスする時はプロパティ名を引用符で囲むとエラーになる。 従って、変数の識別子として利用できるプロパティ名でないとドット記法ではアクセスできない事になる。

console.log(obj.a); // aaa
// 引用符で囲むとエラーになる。
console.log(obj."a"); // エラー
// 数字だけのプロパティ名は変数の識別子として利用できないのでドット記法ではアクセスできない
console.log(obj.99); // エラー
// 変数の識別子として利用できないプロパティ名はドット記法ではアクセスできない
console.log(obj.999t);  // エラー

ブラケット表記

ブラケット表記の場合はプロパティ名が文字列の場合は引用符で囲む必要があって 変数の識別子として利用できないプロパティ名はドット記法ではアクセスできないが、ブラケット表記ではアクセス可能。

console.log(obj["999t"]); // %%%

数字だけのプロパティ名は変数の識別子として利用できないのでドット記法ではアクセスできないが、ブラケット表記ではアクセスできて、数字だけのプロパティ名は 引用符で囲んでも囲まなくてもどっちでも良い。

console.log(obj[99]);   // ##
console.log(obj["99"]); // ##

console.log(obj[999]);  // ###
console.log(obj["999"]); // ###

プロパティへのアクセス

オブジェクトに割り当てられていないプロパティは undefined で、null ではない。
割り当てられていないプロパティにアクセスしてもエラーは発生しない。
文法上のエラーは別だが。

プロパティ名はtoString() メソッドが呼び出されて文字列に変換される。 MDNのページより引用すると

オブジェクトプロパティの名前には、あらゆる有効な JavaScript 文字列 (空文字列を含む) か、文字列に変換できるものが使用できます。しかしながら、JavaScript 識別子として有効ではないプロパティ名 (例えば空白やダッシュを含んでいたり、数字で始まったりするプロパティ名) には、ブラケット (角括弧) 表記法でのみアクセスできます。この表記法はプロパティ名を動的に決める場合 (プロパティ名が実行時に決まる場合) に便利です。例を示します。

// カンマで区切られた 4 つの変数が作成され、
// 1 つに割り当てられます。
var myObj = new Object(),
    str = 'myString',
    rand = Math.random(),
    obj = new Object();

myObj.type              = 'ドット表記';
myObj['date created']   = '空白入りの文字列';
myObj[str]              = '文字列の値';
myObj[rand]             = '乱数';
myObj[obj]              = 'オブジェクト';
myObj['']               = '空文字列も可能';

console.log(myObj);

なお、JavaScript のオブジェクトのプロパティ名 (キー) は文字列かシンボルしか扱えないので、各括弧表記の中のキーはすべて、シンボルを除いて文字列に変換されます (将来、クラスフィールドの提案が進行すると、プライベート名も追加される予定ですが、[] の形は使用できません)。例えば、上記のコードで obj キーが myObj に追加されるとき、JavaScript は obj.toString() メソッドを呼び出し、結果の文字列を新しいキーとして使用します。

変数によるプロパティの指定

ブラケット表記ではプロパティ名に変数を指定できる。

let b = "bbb";
let obj = {
    a: b
}
console.log(obj.a);     // bbb
// ブラケット表記に変数を指定
let x = "a"
console.log(obj[x]);    // bbb

これは当たり前。
だけれども、変数名だけを指定すると変数名がプロパティ名に変数の値がプロパティの値に。

let a = "aaa";
let b = "bbb";
let obj = {
    a,
    b,
}
console.log(obj.a);     // aaa
console.log(obj.b);     // bbb

Computed property names - 計算プロパティ名もしくは計算されたプロパティ名

「オブジェクトリテラル表記」中に[]を使って演算結果をプロパティとする式を記述できる。

// Computed property names (ES2015)
let i = 0
let a = {
  ['foo' + ++i]: i,
  ['foo' + ++i]: i,
  ['foo' + ++i]: i
}

console.log(a.foo1) // 1
console.log(a.foo2) // 2
console.log(a.foo3) // 3

const items = ["A","B","C"];
const obj = {
[items]: "Hello"
}
console.log(obj); // A,B,C: "Hello"
console.log(obj["A,B,C"]) // "Hello"

let param = 'size'
let config = {
  [param]: 12,
  ['mobile' + param.charAt(0).toUpperCase() + param.slice(1)]: 4
}

console.log(config) // {size: 12, mobileSize: 4}

プロパティの追加

オブジェクトはObjectクラスのインスタンスだけではなく、組み込み型も含めすべてのクラスやクラスのインスタンスを意味していて、すべてのオブジェクトに任意のプロパティを追加できるはずだが、例外があって文字列や数値等のプリミティブ型の値にはイミュータブル であってプロパティを追加する事はできないようだ。

Arrayクラスのインスタンスにプロパティを追加。

let array = [];
array.a = "aaa";
console.log(array.a);   // aaa

Stringクラスのインスタンスにプロパティを追加

let str = new String("文字列オブジェクト");
str.a = "aaa";
console.log(str.a); // aaa

文字列リテラルではプロパティを追加する事はできない

let str2 = "文字列オブジェクト";
str2.a = "aaa";
console.log(str2.a);    // undefined

数値リテラルもプロパティを追加する事はできない

let n = 123;
n.a = "aaa";
console.log(n.a);   // undefined

プリミティブ型

リテラル

プロパティを削除

プロパティの削除はdelete 演算子。

メソッドの指定

メソッドはプロパティの値として関数を指定しいたものという見方もできる。
プロパティの値に関数類も指定できてクラスと同様にゲッタやセッターやジェネレーターメソッドの追加が可能。
オブジェクトのプロパティに関数を指定すると、そのインスタンスだけで使えるメソッドになる。

メソッドの定義には簡略構文が利用できて、 この簡略構文はクラス内でも使う事ができる。

const obj = {
  foo: function() {
    // ...
  }
}

これを簡略構文を使うと以下のように記述できる。

const obj = {
  foo() {
    // ...
  }
}

メソッドの簡略構文は計算されたプロパティ名にも対応していて

const obj = {
    ['foo' + 2]() { return 2; }
}
console.log(obj.foo2());  // 2

let name = 'foo';
let i = 2;
console.log(obj[name + i]());  // 2

// A global function
function foo3() {
    return 3;
}
console.log(globalThis[name + ++i]());  // 3

オブジェクトリテラル表記でゲッタやセッターやジェネレーターメソッドの指定例を示すと

let o = {
    _seed: 3,
    // ゲッター
    get seed() {
        return this._seed;
    },
    // セッター
    set seed(seed) {
        this._seed = seed;
    },
    // メソッド
    multiSeed(multi) {
        return this._seed * multi;
    },
    // ジェネレーターメソッド
    *g() {
        let index = 0;
        while (true) {
            yield index += this._seed;
        }
    }
}

// ゲッタ-, セッターの呼び出し
o.seed = 5;
console.log(o.seed); // 5

// メソッドの呼び出し
console.log(o.multiSeed(4)); // 20

// ジェネレーターメソッドの呼び出し
const it = o.g()
for (let i = 0; i < 5; i++) {
    console.log(it.next().value);   // 5, 10, 15, 20, 25
}

Objectクラスのインスタンスに後からメソッドを追加すると

let o = new Object();
o._seed = 3;
Object.defineProperty(o, 'seed',
    {
        get: function () { return this._seed; },
        set: function (seed) { this._seed = seed; }
    }
);
o.multiSeed = function (multi) {
    return this._seed * multi;
};
o.g = function* () {
    let index = 0;
    while (true) {
        yield index += this._seed;
    }
};

// ゲッタ-, セッターの呼び出し
o.seed = 5;
console.log(o.seed); // 5

// メソッドの呼び出し
console.log(o.multiSeed(4)); // 20

// ジェネレーターメソッドの呼び出し
const it = o.g()
for (let i = 0; i < 5; i++) {
    console.log(it.next().value);   // 5, 10, 15, 20, 25
}

Object.definePropertyメソッドについては次ページのプロパティの詳細な指定 - 愚鈍人


参考までに

ページのトップへ戻る