続・先取り! Google Chrome Extensions

第8回Google Chrome拡張とHTML5 #2

こんにちは、太田です。今回はGoogle Chrome拡張に使えるHTML5関連技術の2回目をお送りします。

前回はcanvas、ドラッグ・アンド・ドロップを取り上げましたが、今回はHTML5周辺の技術として、ECMAScript 5やCSS3の先行実装を取り上げます。ECMAScript 5は昨年末(2009年12月)にリリースされたばかりですが、WebKit・Chromiumでは早速(実際にはかなり先行して)その実装が進められています。またCSS3についても、多くのモジュールがWorking Draft(草案)の段階ですが、WebKitでは先行実装がされており、Chrome拡張ではその機能を存分に試すことができます。前回も書きましたが、Chrome拡張はそういった最新技術を試すのに格好のプラットフォームです。

Chrome拡張で使えるECMAScript 5

Google ChromeのJavaScriptエンジンはGoogle製のV8ですが、V8はWebKit(Safari)のJavaScriptCoreとの互換性を意識して実装が進められています。そのため、多くのECMAScript APIはJavaScriptCoreで実装されたらV8にも実装されるという流れを踏んでいます。JavaScriptCoreでのECMAScript 5の実装状況はQt Labs Blogs ≫ ECMAScript 5 and WebKit/JavaScriptCoreで、V8についてはECMAScript 5 and Chromium/V8 - 0xFFでまとめられています。

Native JSON

まず、なんといってもECMAScript 5ではJSONが正式にサポートされた点が大きなニュースです。JSONをサポートするプログラミング言語が増えてきた昨今、ECMAScript発であるJSONを今ごろになってECMAScriptが正式にサポートというのは少々驚かれるかもしれません(それだけECMAScriptの改訂が止まっていた、と言えます⁠⁠。

JavaScriptにおけるJSONのメソッドは、JSON.stringifyとJSON.parseの2つだけです。

JSONのメソッド
var data = {
  a:1,
  b:[1,2],
  c:new Date(),
  f:function(a){return a;}
};
var json_text = JSON.stringify(data);
console.log(json_text);
//{"a":1,"b":[1,2],"c":"2010-01-18T14:01:36Z"}
var json_text2 = JSON.stringify(data,null,2);
console.log(json_text2);
/*
{
  "a": 1,
  "b": [
    1,
    2
  ],
  "c": "2010-01-18T14:01:36Z"
}
*/

JSON.stringifyは第一引数に渡したオブジェクトをJSON文字列に変換するメソッドです。第3引数に数値を渡すとその数値だけインデントさせることができます。第3引数には文字列を持たすこともできるので、\tを渡せばタブインデントさせることも可能です。

JSONメソッドの第2引数
function replacer(key, value) {
  if (this[key] instanceof Date) {
    return this[key].getTime();
  } else if (this[key] instanceof Function) {
    return this[key].toString();
  } else {
    return value;
  }
}
var json_text3 = JSON.stringify(data, replacer,'\t');
console.log(json_text3);
/*
{
	"a": 1,
	"b": [
		1,
		2
	],
	"c": 1263823296890,
	"f": "function (a){return a;}"
}
*/
var data2 = JSON.parse(json_text);
console.log(data2);

JSON.stringifyの第2引数には関数を渡すことで(主にイレギュラーなデータの扱いについて)出力をカスタマイズすることができます。上記のreplacerの例のように、日付をミリ秒で出力したり、通常は無視されてしまう関数を文字列として出力するといったことが可能です(なお、余談ですがFirefox3.5ではstringifyの第2引数についてバグがあるため、意図通りの結果を得られません cf:Bug 512447⁠。

また、オブジェクトのtoJSONメソッドを書き換えることで、特定のオブジェクトについて出力をコントロールすることも可能です(ただし、この機能のためprototype.jsを読み込んだ環境でJSON.stringfyを使用すると意図しない結果になってしまう現象が起こるので注意が必要です⁠⁠。

JSON.parseはJSON文字列をJavaScriptのオブジェクトに変換します。第2引数はやはり出力をカスタマイズすることができるので、上記replacerの逆の処理を行うことで柔軟にオブジェクトをやり取りすることができるようになります。

なお、このJSONですが、JavaScriptの感覚で書こうとすると必ずと言っていいほど正しくないJSONを書いてしまいます。JavaScriptでのオブジェクトの表記法は曖昧さを許容するゆるい仕様ですが、データ記述言語としてのJSONは曖昧さを許さないためです。特に間違いやすいのは、文字列を囲むときにダブルクォートを使用しなければいけない(シングルクォートは使えない)点、キーもダブルクォートで文字列として表記しないといけない点の2つです。JSONは手で書くのではなく、ライブラリやJSON.stringifyで出力させるのがよいでしょう。

Array拡張

配列メソッドとしてindexOf, lastIndexOf, every, some, forEach, map, filter, reduce, reduceRightが追加されています。indexOf, lastIndexOfは配列を検索し、最初に一致した添字を返すメソッドです。それ以外のメソッドはforEachに代表されるように、配列の各要素を順番に処理するメソッドで、それぞれ結果の扱い方が少しずつ異なります。

Array.forEach
var list = [1,2,3];
list.forEach(function(item, i, array){
console.log(item);
});

このArray拡張はFirefoxでは1.5からサポートされていますし、Google Chromeもリリース当初からサポートしているメソッドで、prototype.jsなどのライブラリで同種のメソッドが古くから実装されてるので、すでに定番となっていると言えます。

Object.keys

Object.keysはオブジェクトのキーを配列で取得するメソッドです。先述のArray拡張と組み合わせて、オブジェクトを柔軟に処理できるようになる便利なメソッドです。

Object.keys
var data = {
  a:1,
  b:[1,2],
  c:new Date(),
  f:function(a){return a;}
};
console.log(Object.keys(data));
//["a", "b", "c", "f"]

そのオブジェクト自身が持つプロパティのみを列挙し、prototypeは辿りません。

Object.keys(prototypeは無視される)
function Point(x, y){
  this.x = x;
  this.y = y;
}
Point.prototype = {
  clone:function(){
    return new Point(this.x, this.y);
  }
};
var p1 = new Point(1, 2);
console.log(Object.keys(p1));
// ["y", "x"]

Object.keysを従来のJavaScriptで実装すると、下記のようになります。

Object.keysのJavaScript実装
if (!Object.keys){
Object.keys = function(object){
  if (!(object instanceof Object)) {
    throw new TypeError('Object.keys called on non-object')
  }
  var keys = [];
  for (var key in object){
    if(object.hasOwnProperty(key)){
      keys.push(key);
    }
  }
  return keys;
}
}
console.log(Object.keys(data));
//["a", "b", "c", "f"]

なお、Object.keysでは、第1引数にObjectでないものを渡すとTypeErrorになると定義されているので、instanceof Objectでチェックするようにしました。typeofが"object"であるかという点でチェックする方法もありますが、前者ではnullでTypeErrorに、後者ではTypeErrorにならないという違いがあります。

Object.create

※2010年1月にリリースされたGoogle Chrome 4.0.249.78(安定版)はObject.createが実装される前のバージョンとなったため、上記バージョンではObject.createは使用できません。ご了承ください。

Object.createはオブジェクトを定義するためのメソッドです。prototype継承を簡略化し、従来のJavaScriptではできなかった、列挙できないプロパティや変更不可能なプロパティを作ることができます。

Object.createによるpointの定義
var Point = {
  clone:function(){
    return new Point(this.x, this.y);
  }
};
var p1 = Object.create(Point,{
  x:{
    value: 1,
    writable: true,
    enumerable: true,
    configurable: true
  },
  y:{
    value:2,
    writable: true,
    enumerable: true,
    configurable: true
  }
});
console.log(p1);
// ["y", "x"]

Object.createは第1引数でprototypeとなるオブジェクトを渡し、第2引数は省略可能で、そのオブジェクト自身のプロパティを定義します。valueは値、writableは変更可能かどうか、enumerableはfor inなどで列挙されるかどうか、configurableはdelete演算子でプロパティを消せるかどうかとwritable, enumerable, configurableを変更することができるかどうかを決めます(ただし、現在のChrome(V8 2.0.5.5)ではconfigurableプロパティは実質サポートされていません⁠⁠。

Object.createの第2引数で定義するプロパティは、writable、enumerableを省略するとデフォルトでfalseにセットされるため、上記のように冗長な記述が必要になってしまいます。第2引数は書き換えを禁止したい、列挙させたくないプロパティを定義する際に使用するとよいでしょう。

CSS3

Google Chromeは最新のWebKitを採用していますので、CSS3の先行実装を試すことができます。そのなかでも、Chrome拡張ですぐに使えるテクニックをいくつか紹介します。

border-radius

border-radiusはいわゆる角丸を実現します。WebKitでは-webkit-border-radiusのようにプリフィックスが付きます。将来的にはこのプリフィックスは不要になる点に注意が必要です。実際、Chrome 4はプリフィックスなしの記述に対応していますので、Chrome拡張ではプリフィックスなしの記述を使用しても問題ありません。

border-radius
#box{
border:3px solid #0099cc;
border-radius:15px;
}

gradient

gradientは背景などでグラデーションを実現します。

gradient
#box{
background: -webkit-gradient(linear, left top, left bottom,
             from(#99cccc), to(#3399cc));
width:100px;
height:100px;
}
図1 border-radiusとgradient(左が画像、右がCSSを指定した要素)
図1 border-radiusとgradient 

CSSの応用

下記のようなCSSでボタンにスタイルを当てると、画像を使わずにスタイリッシュなインターフェースを作成できます

ボタンのグラデーションと角丸
input[type="button"],
input[type="submit"],
button{
-webkit-appearance: button;
color:#ffffff;
background: -webkit-gradient(linear, left top, left bottom,
             from(#777777), to(#333333));
border: 1px solid #999999;
border-radius: 3px;
height: 1.7em;
padding: 0 0.6em;
vertical-align: middle;
}
input[type="button"]:hover,
input[type="submit"]:hover,
button:hover{
background: -webkit-gradient(linear, left top, left bottom,
             from(#999999), to(#666666));
}
input[type="button"][disabled],
input[type="submit"][disabled],
button[disabled]{
background:#999999;
}
input[type="button"][disabled]:hover,
input[type="submit"][disabled]:hover,
button[disabled]:hover{
background:#999999;
}
図2 スタイルを適用したボタン(上が画像、下がCSSを指定した要素)
図2 スタイルを適用したボタン

まとめ

今回はChrome拡張で使えるHTML5周辺技術としてECMAScript 5とCSS3を取り上げました。次回はさらにHTML5周辺技術から、CSS transformsや、Web StorageやWeb SQL databaseなどのAPIを取り上げる予定です。

おすすめ記事

記事・ニュース一覧