uhyohyo.net

JavaScript初級者から中級者になろう

十六章第八回 オブジェクトリテラル

このページの最終更新日:

今回は比較的短いですが、オブジェクトリテラルについて解説します。オブジェクトリテラルはこういうやつですね。


{
  foo: 'bar',
  hoge: 3,
}

ES2015にはオブジェクトリテラルにも便利な記法が追加されましたので、それを解説します。

最後のカンマ

その前に、皆さんは気づいていたでしょうか。オブジェクトリテラルの最後のカンマについて気づいていたでしょうか。


{
  foo: 'bar',
  hoge: 3, // ←これ
}

です。オブジェクトリテラルの中身はプロパティ名: というのをカンマで区切って複数並べる形をしていましたが、一番最後にもこのようにカンマを付けることができます。

かなり昔のJavaScriptではできなかったので後方互換性を気にして最後のカンマを付けない人もいましたが、最近はそのような心配をする必要はほぼ無くなりましたので遠慮なく最後のカンマを付けましょう。

最後のカンマを付けられるのは非常に便利です。なぜなら、リテラルの一番最後に新しいプロパティを追加するときに、最後のカンマがないと「前のプロパティにカンマを追加する」「新しいプロパティを書く」という2ステップが必要になるからです。

プロパティ名

もうひとつ復習ですが、オブジェクトリテラルのプロパティ名の書き方は次の3種類があります。


var obj = {
  // 直書き
  foo: 'Hey!',
  // 文字列
  'foo bar': 123,
  // 数値
  12: 'hello',
};

直書きで書けるプロパティ名には限りがあります。例えばスペースを含むプロパティ名は無理です。一方、文字列を指定する記法では上の例のように任意のプロパティ名を使うことができます。

これはあまり知られていませんが、プロパティのところに数値リテラルを書くことができます。例えば上の例ではプロパティ名に12と書いてあり、これは"12"というプロパティ名に解釈されます。

数値リテラルを書いた場合はその数値を文字列に変換したものがプロパティ名になります。次のような例に注意してください。


var obj = {
  1e5: 'foo',
};

console.log(obj); // {100000: 'foo'}

Computed Property Name

では、まずcomputed property nameの話をします。これは、シンボルのところでも少しでてきましたが、プロパティ名を計算によって決められる記法です。具体的には、[]: のようにプロパティ名を[ ]で囲い、その中に式を書きます。式の結果がプロパティ名となります。


var str1 = 'foo', str2 = 'bar';
var obj = {
  [str1 + str2]: 12345,
}

console.log(obj); // {foobar: 12345}

今までの記法ではプロパティ名は完全に決め打ちでしたから、この方法により動的なプロパティ名をオブジェクトリテラル内で使えるのは画期的です。また、プロパティ名を文字列ではなくシンボルにしたい場合にこの記法は必須となります。

変数の省略

JavaScriptを書いていると、次のようなパターンが頻出します。


var foo = '何かの値';

var obj = {
  foo: foo,
};

この例では新しいオブジェクトobjを作る際に、そのオブジェクトのプロパティfooの値を変数fooの値で初期化しています。プログラム中ではfooと2回連続して書かれており、何か間抜けです。

このようなことが起こる典型的な場面は、何か複雑なオブジェクトを作る際にオブジェクトリテラルの中で全ての計算を行うと見難くなるので各プロパティをあらかじめ計算しておくという場合です。この場合、プロパティfooに入れるべき値を計算して変数fooに入れておいて、オブジェクトリテラルを書くときにはプロパティfooには変数fooの値を入れるようにするということです。

上の例は、ES2015では次のように書くことができます。


var foo = '何かの値';

var obj = {
  foo,
};

: の部分が消えました。このように、プロパティ名と、そのプロパティに入れるべき値が入った変数名が同じ場合は省略することができます。

こんなの何に使うんだと思うかもしれませんが、実際にプログラムを書いてみると意外と使います。

関数の省略記法

プロパティとして関数を入れたい場合の省略記法があります。次の2つのオブジェクトリテラルは基本的には同じ意味となります。


var obj1 = {
  func: function(foo, bar){
    return foo + bar;
  },
};

var obj2 = {
  func(foo, bar){
    return foo + bar;
  },
};

上が従来の記法であり、funcというプロパティにfunction(foo, bar){ return foo + bar; }という関数を入れています。下の記法はその省略形です。分かりやすくていいですね。

ただし、下の記法で作られた関数はコンストラクタとして使用できないという違いがあります。プロパティ(というかメソッド)がコンストラクタになるという場面はあまり無いと思いますが、もしそのようなことをしたい場合は注意しましょう。

また、ジェネレータ関数に対応する省略記法もあります。


var obj1 = {
  func: function*(){
    // ...
  },
};

var obj2 = {
  *func(){
    // ...
  },
};

なお、これらの省略記法に対しても上で紹介した3種類のプロパティ名の記法が全て使えます。よって、こんなことも可能です。


var obj = {
  'foo bar'(){
    return 0;
  },
  1e5(){
    return 1e5;
  },
  [Symbol.iterator](){
    throw new Error('Hi');
  },
};

やや余談になりますが、関数オブジェクトはnameというプロパティを持ちます。その名の通り関数の名前です。次のようにfunction宣言で作られた関数は宣言された名前を持ちます。


function foo(){
}

console.log(foo.name); // "foo"

関数式で作った関数は名前がない場合(俗にいう無名関数ですね)もありますが、実は直接変数に代入する場合は適切に名前をつけてもらえます。


var func1 = function(){};
var func2 = (function(){ return function(){}; })();
var func3 = function foobar(){};

console.log(func1.name); // "func1"
console.log(func2.name); // ""
console.log(func3.name); // "foobar"

プロパティについても基本的には同様です。ただし、nameプロパティ名は文字列ですので、シンボルをプロパティ名にした場合は名前無しになります。にもかかわらず、well-known symbolの場合は特殊な名前が付くこともあります。


var mysymbol = Symbol();

var obj = {
  foo: function(){
  },
  'foo bar'(){
    return 0;
  },
  1e5(){
    return 1e5;
  },
  [mysymbol](){
    return 300;
  },
  [Symbol.iterator](){
    throw new Error('Hi');
  },
};

console.log(obj.foo.name); // "foo"
console.log(obj['foo bar'].name); // "foo bar"
console.log(obj[1e5].name); // "100000"
console.log(obj[mysymbol].name); // ""
console.log(obj[Symbol.iterator].name); // "[Symbol.iterator]"

変数名・プロパティ名とエスケープ記法

はるか昔、文字列の回でエスケープシーケンスを紹介したのを覚えているでしょうか。特に、コードポイントを指定する記法では、\u1234のような形で文字を表すことができます。


console.log("foo\u28ffbar"); // "foobar"

恐ろしいことに、コードポイント記法はプロパティ名や変数名に直に書くことができます。


var foo\u{1d40d}bar = 'some value';
console.log(window['foo𝐍bar']); // 'some value'

var obj = {
  foo\u{1d439}: 123,
};
console.log(obj.foo𝐹); // 123

ただし、直に書く場合のコードポイントは識別子に使用可能な文字を表していなければいけません。なお、識別子 (identifier)はプログラム中に直に書いた変数名などを指す用語です。例えば、スペース(U+0020)は識別子に使用可能な文字ではないですから、次のようにしてもエラーになります。


var foo\u0020bar = 3;

この記法は見難いし使いどころがないような気がしますが、稀に使われるかもしれないので頭の片隅に置いておくのがよいでしょう。