Windows Internet Explorer 9

IE9は、IE8までのIEで採用しているJavaScriptゲッターメソッド__defineGetter__およびセッターメソッド__defineSetter__をサポートせずに、ECMAScript 5仕様で定められているゲッターメソッドおよびセッターメソッドの表記のみをサポートすることになった。ブラウザごとに若干動作が異なる従来の記述をサポートすることは、将来の互換性に禍根を残すというのがその理由だ。

たとえばIE8では次のようにコーディングしていたものは

myObject.__defineGetter__("p", function() {/* getter function body */});
myObject.__defineSetter__("p", function(v) {/* setter function body */});

IE9では次のようにコーディングすることになる。

Object.defineProperty(myObject,"p",
   {get: function() {/* getter function body */}}
);
Object.defineProperty(myObject,"p",
   {set: function(v) {/* setter function body */}}
);

しかし短期的にみると、これはWebデベロッパにIE9とIE8の双方に対応させるための追加のコーディングを強いることを意味している。どのように対応させればよいかがTransitioning Existing Code to the ES5 Getter/Setter APIs - IEBlogで紹介されている。紹介されているアプローチは2つある。従来向けのコーディングに対してIE9対応を追加する方法と、IE9向けにコーディングしたものに対して従来のコーディング対応を追加する方法だ。

1. 従来向けのコーディングに対してIE9対応を追加する方法

//emulate legacy getter/setter API using ES5 APIs
try {
   if (!Object.prototype.__defineGetter__ &&
        Object.defineProperty({},"x",{get: function(){return true}}).x) {
      Object.defineProperty(Object.prototype, "__defineGetter__",
         {enumerable: false, configurable: true,
          value: function(name,func)
             {Object.defineProperty(this,name,
                 {get:func,enumerable: true,configurable: true});
      }});
      Object.defineProperty(Object.prototype, "__defineSetter__",
         {enumerable: false, configurable: true,
          value: function(name,func)
             {Object.defineProperty(this,name,
                 {set:func,enumerable: true,configurable: true});
      }});
   }
} catch(defPropException) {/*Do nothing if an exception occurs*/};

この方法は、IE8以前向けに開発されたJavaScriptをほとんど変更することなくIE9に対応させる方法といえる。IE9で従来のコードが動作するには、要するにサポートされていない__defineGetter__および__defineSetter__を定義して用意すればいい。これを実現するために、次のステップでブラウザの提供している機能を判定し、最終的にObject.defineProperty getの処理を__defineGetter__および__defineSetter__で呼び出せるようにしている。

  1. __defineGetter__が定義されているかどうか。定義されていれば処理を抜ける。
  2. Object.definePropertyがあるかどうか。なければ例外を発生し処理を抜ける。
  3. Object.definePropertyでゲッターを定義できるか。できなければ例外が発生するか、評価結果がfalseになって処理を抜ける。
  4. Object.defineProperty getの処理が__defineGetter__で呼ばれるように設定。

Object.definePropertyがあるかどうかとゲッターを定義できるかどうかは、実際にゲッターを定義しようとすることで実施している。ない場合には例外が発生するため全体がtry catchで囲まれている。2.と3.のように処理を分けて考えているのは、IE8がdefinePropertyは持っているもののDOMオブジェクトに対しては使用できないため、それを検出するためとされている。

2. IE9向けのコーディングに対して従来のコーディング対応を追加する方法

//emulate ES5 getter/setter API using legacy APIs
if (Object.prototype.__defineGetter__&&!Object.defineProperty) {
   Object.defineProperty=function(obj,prop,desc) {
      if ("get" in desc) obj.__defineGetter__(prop,desc.get);
      if ("set" in desc) obj.__defineSetter__(prop,desc.set);
   }
}

この方法は、IE9向けに新しく開発したり、従来のコードをIE9向けに書き直したあとで、IE8以前のブラウザにも対応させるための方法となる。コーディングとしてはこちらの方が簡単だ。__defineGetter__が定義されており、かつ、Object.definePropertyが存在しない場合に、Object.definePropertyの実態として__defineGetter__および__defineSetter__を使った関数を定義するという内容になっている。なお、掲載した4つのサンプルはすべてTransitioning Existing Code to the ES5 Getter/Setter APIsに掲載されているサンプルを抜粋したもの。

Microsoftは単一のファイルで複数のブラウザに対応させる方法として機能検出ベースでのアプローチを推奨している。ブラウザを検出してコードを切り替える方法はよくないとし、機能を検出してそれに対して処理を実施するアプローチの方が優れていると主張している。Transitioning Existing Code to the ES5 Getter/Setter APIsで紹介されているコーディングはこの機能検出ベースでのアプローチを踏襲しており、実際にどういったコーディングを実施すればよいのかの参考になる。