nayucolony

勉強したこととか

変数をキーとしてオブジェクトのプロパティを参照する際はブラケット表記法を使う。

結論

  • JavaScriptにおいて、変数をキーとしてオブジェクトのプロパティを参照する際はブラケット表記法を使う。
  • ブラケット表記法には、ドット表記法にはない「式の評価」処理があるため、変数を解決できる。

経緯

for文の中にfor文を配置して二次元配列的にループしてさせようとしていた。

<div id="demo">
  <tr v-for="entry in gridData">
    <td v-for="key in gridColumns">{{ entry.key }}</td>
  </tr>
</div>
const demo = new Vue({
  el: '#demo',
  data: {
    gridColumns: ['name', 'power'],
    gridData: [
      { name: 'Chuck Norris', power: Infinity },
      { name: 'Bruce Lee', power: 9000 },
      { name: 'Jackie Chan', power: 7000 },
      { name: 'Jet Li', power: 8000 }
    ],
  },
});

するとこうなった。

f:id:nayucolony:20170530191723p:plain

ちゃんとループはしているのだが、データがバインドできていないという状態だ。

解決方法

オブジェクトのデータ参照方法をドット表記法でなくブラケット表記法に変更した。

それぞれの表記法については下記の通り。 メンバー演算子 - JavaScript | MDN

<div id="demo">
  <tr v-for="entry in gridData">
    <td v-for="key in gridColumns">{{ entry[key] }}</td>
  </tr>
</div>

f:id:nayucolony:20170530192017p:plain

チャックノリスもにっこり。

なぜなのか

ブラケット表記法とドット表記法は、単純に「ふた通りの表記法があります」という話ではない。 たとえば、今回のケースではkeyが変数であるということが大きなポイント。ドット表記法では、プロパティ名に変数をもちいることができない

ここに書いてあった。 ECMAScript 2015 Language Specification – ECMA-262 6th Edition

12.3.2.1 Runtime Semantics: Evaluation

MemberExpression : MemberExpression [ Expression ]
1.Let baseReference be the result of evaluating MemberExpression.
2.Let baseValue be GetValue(baseReference).
3.ReturnIfAbrupt(baseValue).
4.Let propertyNameReference be the result of evaluating Expression.
5.Let propertyNameValue be GetValue(propertyNameReference).
6.ReturnIfAbrupt(propertyNameValue).
7.Let bv be RequireObjectCoercible(baseValue).
8.ReturnIfAbrupt(bv).
9.Let propertyKey be ToPropertyKey(propertyNameValue).
10.ReturnIfAbrupt(propertyKey).
11.If the code matched by the syntactic production that is being evaluated is strict mode code, let strict be true, else let strict be false.
12.Return a value of type Reference whose base value is bv and whose referenced name is propertyKey, and whose strict reference flag is strict.

MemberExpression : MemberExpression . IdentifierName
1.Let baseReference be the result of evaluating MemberExpression.
2.Let baseValue be GetValue(baseReference).
3.ReturnIfAbrupt(baseValue).
4.Let bv be RequireObjectCoercible(baseValue).
5.ReturnIfAbrupt(bv).
6.Let propertyNameString be StringValue of IdentifierName
7.If the code matched by the syntactic production that is being evaluated is strict mode code, let strict be true, else let strict be false.
8.Return a value of type Reference whose base value is bv and whose referenced name is propertyNameString, and whose strict reference flag is strict.

どうみてもプロセスの数が違う。ブラケット表記法の方がプロセスが多い。 私の解釈が間違っていなければ、「式の評価」というプロセスがあるかないかという差分がある。 (そもそも、MemberExpression [ Expression ]MemberExpression . IdentifierNameという違いがある。前者は式、後者は識別子と明確に別物)

ブラケット表記法では、変数を解決するプロセスをしっかりもっているが、ドット表記法にはそれがない。

ということで、これはべつにバグではなくかっちり定義されている仕様。二つの表記法は好みで使い分けていいものだと思っていたのでいい勉強になった。

関連

今回の現象は以下のサンプルをいじっていたときに発生したものでした。

グリッドコンポーネント - Vue.js