Hatena::ブログ(Diary)

hogehoge @teramako RSSフィード

 

2014-08-17

Firefox 34 で Method Definition 構文が実装された

var o = {
  method: function method () {
    return "OK"
  },
};

と書いていたところを

var o = {
  method() {
    return "OK";
  },
};

こう書けるようになる。

2014-08-12

Firefox 34 でES6の Object.assign() が実装されたが…

皆さん待望の、mixin をするメソッドである。

第一引数のtargetに、第二引数以降のプロパティの値を代入していく。

var obj = {
  foo: "FOO",
};

var res = Object.assign(obj, { bar: "BAR" }, { piyo: "PIYO" });

obj.bar; // => "BAR";
obj.piyo; // => "PIYO"
res === obj; // => true

代入されるプロパティは:

  • ソースとなるオブジェクト自身のプロパティ(つまり、[[Prototype]]から継承したものは含まない)
  • 列挙可能であること(enumerable属性がtrueであること)

簡易的には以下の様な実装

Object.assign = function(target, ...sources) {
  for (let source of sources) {
    let keys = Object.getOwnPropertyNames(source);
    for (let key of keys) {
      let desc = Object.getOwnPropertyDescriptor(source, key);
      if (desc && desc.enumerable) {
        target[key] = source[key];
      }
    }
  }
  return target;
}

大まかな話はこんな感じ。

次は細かい話。

デスクリプタのコピーではなく、代入(=)がされる

上記簡易コードで、target[key] = source[key];と書いた。

本来、プロパティのコピーをするならObject.defineProperty(target, key, desc)と書くところだが、仕様も実装もそうなっていない。(個人的には気に食わない点)

var obj = Object.assign({}, {
  get foo() {
    return "FOO";
  },
});

というコードがあった場合、obj.fooはgetterではなく、ただプロパティとして代入される。

もっと酷い例を挙げると、

var obj = Object.assign({}, {
  get __proto__() {
    return null;
  },
});

これ、obj.__proto__ = nullしているのと同義になり、[[Prototype]&93;が書き換えられるというアレなことが起きる。

Symbolもあるんだよ

現状のFirefoxの実装( https://hg.mozilla.org/mozilla-central/diff/53769e48d35b/js/src/builtin/Object.js )では抜けているが、Symbol型のプロパティも代入の対象である。

上記の簡易実行コードは、下記のように書いたほうが正確である...(仕様が後で書き換わる可能性もあるが、現状では)

Object.assign = function(target, ...sources) {
  for (let source of sources) {
    let keys = [...Object.getOwnPropertyNames(source), ...Object.getOwnPropertySymbols(source)];
    for (let key of keys) {
      let desc = Object.getOwnPropertyDescriptor(source, key);
      if (desc && desc.enumerable) {
        target[key] = source[key];
      }
    }
  }
  return target;
}

現状のFx34のコードは仕様に沿ってないので、 https://bugzilla.mozilla.org/show_bug.cgi?id=937855#c32 にコメントだけは残しておいた。何の反応もなく進んでしまうようなら、正式にバグ登録したい。

追記

様子を見るまでもなく、バグ登録された。

2014-08-11

Firefox 34でES6構文の ComputedPropertyName が実装された

また、新しい構文。

オブジェクトリテラル中のプロパティ名部分に式を入れることができる様になる。

今まで

var prop = "foo";
var obj = {};
obj[prop] = "FOO Property";

今までは一旦オブジェクトを作ってから代入するしかなかったものが、

これから

var prop = "foo";
var obj = {
  [prop]: "FOO Property"
};

とできるようになる。

プロパティ名の部分に、[expr] と書く。

何が嬉しいの?

まぁ、普通に便利ですね。

と、これだけではアレなのでもう少し書くと、ECMAScript6thになってSymbolという型が登場し特殊なSymbol型のプロパティを定義できるわけだが、このComputedPropertyNameの構文がないとオブジェクトリテラルや将来的に実装されるであろうClass構文でSymbol型のプロパティを定義できるようにという図らいである。

より具体的には、Class構文でクラスを定義し、for-ofで回せるよう@@iteratorを定義したいとすると

class KeyValue {
  constructor() {
    // ...
  }
  set(key, value) {
    // ...
  }
}
KeyValue.prototype[Symbol.iterator] = function* () {
  for (var key of Object.keys(this).sort()) {
    yield [key, this[key]];
  }
};

などと書くとせっかくClass構文を使ってクラスっぽい書き方をしてラップしても内部のprototypeプロパティに直接アクセスとかしちゃってたら意味がなく馬鹿馬鹿しい。

どうせClass構文を使うなら、以下のように完全にクラスとして書けたほうが良い。

class KeyValue {
  constructor() {
    // ...
  }
  set(key, value) {
    // ...
  }
  *[Symbol.iterator]() {
    for (var key of Object.keys(this).sort()) {
      yield [key, this[key]];
    }
  }
}

ま、この辺りの考察は個人的なものだから実際はどうだか知らないけどね :P