2007年11月27日火曜日

JavaScript と継承 3

Doublas Crockford さんがClassical Inheritance in JavaScript で JavaScript の継承についての記事を残していました.

Simple helper

まずは オブジェクトのプロトタイプに関数をバインドする簡単な関数です.これは以下に続く継承のヘルパとなっています.
// 新しい関数をオブジェクトのプロトタイプにバインドするヘルパ関数
Function.prototype.method = function(name, func){
this.prototype[name] = func;
return this;
}

継承

先ほどのヘルパ関数を使って継承関数を書きます.
Function.method('inherits', function(parent) {
// どのくらいの深さにいるか
var depth = 0;

// スーパークラスのメソッドを継承
var ptoro = this.prototype = new parent();

// 継承でオーバーライトされたスーパークラスのメソッドを呼出せるようにする
this.method('uber', function uber(name) {
var func;
var ret;
var v = parent.prototype;

// 既に uber 関数の中にいるとき
if (depth) {
for(var i = depth; i > 0; i++) {
v = v.constructer.prototype;
}

func = v[name];
}
else { // 最初の uber call
func = proto[name];
if (func == this[name]) {
func = v[name];
}
}

depth += 1;

ret = func.apply(this, Array.prototype.slice.apply(arguments, [1]));

depth -= 1;

return ret;

});
return this;
});

継承のポイント

上記の継承関数は Single parent の継承に使うことができます。コードのほとんどの部分は this.uber('methodName') を呼出すための部分で、これにより、オーバーライトした親メソッドを呼出すことができます。
オーバーライトしたメソッドのスーパークラスのメソッドを呼出しは、JavaScript の継承ではサポートされていないため、これで補えます。

使用例

上記の継承関数は以下のような形で使えます。
function Person(name) {
this.name = name;
}

Person.method('getName', function(){
return this.name
});

function User(name, password){
this.name = name;
this.password = password;
}

User.inherits(Person);

User.method('getPassword', function(){
return this.password;
});

// Overwrite したスーパークラスのメソッドを呼出し
User.method('getName', function(){
return 'My Name is:' this.uber('getName');
})

2007年11月26日月曜日

JavaScript の オーバーロード

jQuery のコア開発者として知られている、John Resig さんのブログに JavaScript Method Overloading という大変面白い記事がありました。

JavaScript のオーバーロード

ソースコードを見てみます。簡潔で素晴らしいコード。
// addMethod - By John Resig (MIT Licensed)
function addMethod(object, name, fn){
// 指定されたオブジェクトには,
// 既に同じ名前のメソッドが定義されている
// かもしれないので,一時変数に保存しておく
var old = object[ name ];

// オブジェクトのプロパティに関数定義を上書き.
object[ name ] = function(){
// 呼び出しされたメソッドの引数の数と関数に定義されている
// 引数の数が一致したとき呼び出されたメソッドを適用する.
if ( fn.length == arguments.length )
return fn.apply( this, arguments );

// 呼び出されたメソッドと,定義時の引数の数が
//一致しなかった場合には,一時保存しておいた
// 同名のプロパティをチェック.
// 一時保存されたプロパティが関数オブジェクトだった場合には
// プロパティに引数を適用
else if ( typeof old == 'function' )
return old.apply( this, arguments );
};
}
上記のコードを見ると分かるように,あるメソッドが呼ばれた場合,引数の数とメソッドが期待する引数の数をチェックします.引数の数が一致した段階で呼びだされたメソッドを実行しますが,一致しない場合には,引数の数が一致するまで addMethod でオーバーロードされたメソッドを辿ることになります.

オーバーロードされたメソッド,及び一番最初に定義されたメソッドを全て辿っても引数の数が一致するメソッドが見つからなかった場合には,処理が何も行われません.

この関数を以下のように使います。
// ユーザの検索処理を引数の数によって分岐
function Users(){

// 引数なしのとき
addMethod(this, "find", function(){
// Find all users...
});

// 引数がひとつのとき
// find メソッドをオーバーロード
addMethod(this, "find", function(name){
// Find a user by name
});

// 引数がふたつの場合
// find メソッドをオーバーロード
addMethod(this, "find", function(first, last){
// Find a user by first and last name
});
}

// ユーザのコンストラクタ呼出し
var users = new Users();

// 引数なし
users.find(); // Finds all

//引数 1つ
users.find("John"); // Finds users by name

// 引数 2つ
users.find("John", "Resig"); // Finds users by first and last name

// 引数 3つのときは定義していないので何もしない
users.find("John", "E", "Resig"); // Does nothing

fn.length??

このオーバーロードメソッドの肝は、
if ( fn.length == arguments.length )
の部分です。この部分では,引数の数が一致するまでメソッドを辿り、一致した段階でメソッドに引数を適用しています。
This isn't very well known, but all functions have a length property on them. This property equates to the number of arguments that the function is expecting. Thus, if you define a function that accepts a single argument, it'll have a length of 1, like so:

(function(foo){}).length == 1JavaScript Method Overloading
とあるように、関数は length プロパティを持っており、関数自体が期待する引数の数が保持されています。

このコードの注意点として,
  • 引数の数以外,例えば引数のデータ型や引数の名前などではオーバーロードできない
  • 関数呼び出しにオーバヘッドがあるから高パフォーマンスが求められる状況では適宜判断する
といったことがありますが,とても勉強になりました.
こういう素晴らしいコードを早く書けるようになりたいです.

2007年11月25日日曜日

Rake ことはじめ

Rails アプリケーションを作っていると、
rake db:migrate 
というコマンドをよく叩きます。しかし、Rake についてよく理解していないので調査してみました。

Rake とは?

Rake は Ruby 版 makeです。

Rake でできること

  • メンバーのリストを取得して、Email を送る
  • 毎日のバッチ処理
  • 古いキャッシュを消去し生成
  • データベースやサブバージョンのバックアップ
  • データ操作のスクリプトを走らせる

タスクを登録する

ゆで卵を食べるために、以下の 3 つのタスクを考えてみます。
  • 卵を買う
  • 水を沸騰させる
  • ゆで卵をゆでる
これらのタスクを Rake ファイルを使って呼出してみます。
task :purchaseEggs do
puts 'Purchase eggs.'
end

task :boilWater do
puts 'Boil the water.'
end

task :makeBoiledEggs do
puts 'Make boiled eggs. They are delicious!'
end
このスクリプトを記述したファイルを Rakefile として保存して、これらのタスクをコマンドラインから呼出してみましょう。
# rake purchaseEgg
Purchase eggs.

# rake boilWater
Boil the water.

# rake makeBoiledEggs
Make boiled eggs.

タスクの依存関係の設定

卵を茹でるタスクには、次のような依存関係があります。
  • 水を沸騰させる前に卵を購入する
  • 卵を茹でる前に水を沸騰させておく
というわけで、卵を茹でるタスクたちに依存関係を設定します。
task :purchaseEggs do
puts 'Purchase eggs.'
end

task :boilWater => :purchaseEggs do
puts 'Boil the water.'
end

task :makeBoiledEggs => :boilWater do
puts 'Make boiled eggs. They are delicious!'
end
このように、依存関係は、
タスク名=> '依存するタスク名' 
で指定します。それではコマンドラインからタスクを呼出してみます。
# rake purchaseEgg
Purchase eggs.

# rake boilWater
Purchase eggs.
Boil the water.

# rake makeBoiledEggs
Purchase eggs.
Boil the water.
Make boiled eggs. They are delicious!
依存関係を設定した今回の場合、卵を茹でるタスクの前に、卵を買うタスク、水を沸騰させるタスクを呼ぶことができました。

タスクの説明文

desc を使ってタスクの説明文をつけます。自分以外の人がタスクを発行する場合には説明文があった方が助かりますね。
desc 'This task will purchase eggs.'
task :purchaseEggs do
puts 'Purchase eggs.'
end

desc 'This task will boil the water.'
task :boilWater => :purchaseEggs do
puts 'Boil the water.'
end

desc 'This task will make my boiled eggs.'
task :makeBoiledEggs => :boilWater do
puts 'Make boiled eggs. They are delicious!'
end
desc の記述は 'rake -T' または 'rake --tasks' コマンドで確認することができます。
# rake --tasks
rake purchaseEggs # This task will Purchase eggs.
rake boilWater # This task will boil the water.
rake makeBoiledEggs # This task will make my boiled eggs.

Rake の名前空間を設定

タスクを沢山登録した後は名前空間を使ってカテゴライズしておくのが吉です。というわけで茹で卵のタスクにも名前空間を設定します。
namespace :boiledeggs do
desc 'This task will purchase eggs.'
task :purchaseEggs do
puts 'Purchase eggs.'
end

desc 'This task will boil the water.'
task :boilWater => :purchaseEggs do
puts 'Boil the water.'
end

desc 'This task will make my boiled eggs.'
task :makeBoiledEggs => :boilWater do
puts 'Make boiled eggs. They are delicious!'
end
end
先程と同じように rake--tasks コマンドを発行してみます。
# rake --tasks
rake boiledeggs:purchaseEggs # This task will Purchase eggs.
rake boiledeggs:boilWater # This task will boil the water.
rake boiledeggs:makeBoiledEggs # This task will make my boiled eggs.
このタスクを走らせるには、
# rake boiledeggs:makeBoiledEggs
となります。

Rails で Task

Ruby のタスクを見てきましたが、Rails アプリケーションでタスクを登録して実行するやり方も見ておきます。Rails APP のルートディレクトリで
# rake --tasks
とコマンドを発行させると既に沢山のタスクが登録されているのが分かります。自分でオリジナルのタスクを登録するには、#{RAILS_ROOT}/lib/tasks ディレクトリに 'foo.rake' と保存しておきます。
上記の例では、boiledeggs という名前空間をつけた茹で卵を作るタスク群を foo.rake いうファイル名で保存しておけば、以下のようにタスクを実行できます。
# rake boiledeggs:makeBoiledEggs
Purchase eggs.
Boil the water.
Make boiled eggs. They are delicious!

Rails 内のモデルを使うタスクを書く

Rails APP 内のモデルを使ってタスクを呼出すことができます。以下の例を見てください。
namespace :boiledeggs do
desc 'This task will purchase eggs.'
task :purchaseEggs do
puts 'Purchase eggs'
end

desc 'This task will boil the water.'
task :boilWater => :purchaseEggs do
puts 'Boil the water.'
end

desc 'This task will make my boiled eggs.'
task :makeBoiledEggs => [:boilWater,:environment] do
User.find(:all).each{|u|
puts "Emailg #{u.email}"
puts 'Make boiled eggs. They are delicious!'
# ここにメール送信の処理を書く
}
end
end
茹で卵を茹でたら、ユーザ一人一人にメールを送信しています。
上記のように、モデルにアクセスするには、'=> :environment' 宣言が必要です。
production の DB を使いたい場合は、
rake RAILS_ENV=production boiledeggs:makeBoiledEggs
と指定して実行します。
バッチ実行処理などの場合は以下のようにクーロン登録しておくと便利ですね。
0 0 * * * cd /path/to/railsapp; /usr/local/bin/rake RAILS_ENV=production boiledeggs:makeBoiledEggs

参考: 本記事では以下の記事にお世話になりました m(_ _)m。
File: rakefile.rdoc
活動日誌(2005-02-14)(上記の日本語訳)
Rails Envy: Ruby on Rails Rake Tutorial.
僕にもできた! Rake

2007年11月24日土曜日

ダック・タイピング

Duck Typing (ダック・タイピング) という言葉が気になったので調べてみました。

動的言語のデメリット

動的言語と静的言語を比較するとき、動的言語のデメリットとそれに対する解答は以下になります。
  • コンパイル時にエラーが見つけられない。⇒ エラーはユニットテストで見つければ良い
  • 最適化の情報が少ない。⇒ 昨今では実行効率よりもプログラムの柔軟性や生産性の方が重要
  • 型情報がないので、コード読解のヒントが少ない。⇒ ドキュメントによる支援で解決する。
特に、静的なプログラム言語と動的なプログラム言語で同様の処理をするとき、多くの場合静的なプログラム言語が実行速度で勝つことについて、この理由として言及されるのは、以下の点です。
  • 動的なプログラミング言語には大抵型チェックが必要。
  • 静的型なプログラミング言語はソース・コードを直接実行できる形式に変換するコンパイル型処理系が多い
  • 動的型のプログラミング言語はインタプリタ型処理系が多い。
一方で、動的プログラミング言語のメリットとしては、
  • ソースコードが簡潔になりやすい。⇒ 処理の本質部分に集中してプログラムを書けるので、生産性向上
  • 柔軟性 ⇒ 型宣言がないので、開発時に想定していなかったデータを取り扱うことが容易
とういことがあります。この柔軟性を実現するのが ダック・タイピング です。

ダック・タイピングとは??

動的プログラミング言語の柔軟性を表現するのがダック・タイピングです。
Wikipedia をチェックしてみるとこんな風に書いてあります。
In Computer Programming, duck typing is a style of dynamic typing in which an object's current set of methods and properties determines the valid semantics, rather than its inheritance from a particular class. The name of the concept refers to the duck test, attributed to James Whitcomb Riley, which may be phrased as follows:
If it walks like a duck and quacks like a duck, I would call it a duck.Wikipedia--Duck Typing
この文章にもあるように、ダック・タイピングは、

If it walks like a duck and quacks like a duck, I would call it a duck.
アヒルのように歩いたり鳴いたりするものはアヒルであろう。


という表現に由来します。あるオブジェクトがどのクラスに所属するオブジェクトなのかを考慮せず、どのようなメソッドを持つか、に注目してプログラムするのがダック・タイピングです。

ダック・タイピングの原則

ダック・タイピングにおける原則は次のようなものです。
基本的な原則はたった1つ,最低限これだけを覚えておけば大丈夫です。
明示的な型チェックを避けるまつもと直伝 プログラミングのオキテ 第4回(3)
つまり型チェックをするのではなく、メソッドを持っているかでチェックする、ということになります。例えば、
if not obj.kind_of?(String)
raise TypeError, 'not a String'
end
とすると型チェックをするので静的な言語と変わらなくなる。
if not obj.kind_of?('to_str')
raise TypeError, 'not a String'
end
とすればメソッドを持っているかの判定になる。ということです。

※self.kind_of?(mod) メソッドは、self が、
  • クラス mod とそのサブクラス
  • モジュール mod をインクルードしたクラスとそのサブクラス
のいずれかのインスタンスであるとき真を返すメソッドです。

2007年11月23日金曜日

JavaScript と高階関数

JavaScript における高階プログラミング という記事を読みました。
高階プログラミングとは高階関数を使ったプログラミングのことです。

高階関数とは?

高階関数について、以下のような記述があります。
プログラミング言語において、関数を引数にしたり、あるいは関数を戻り値とするような関数の事である。引数や戻り値の関数もまた高階関数となり得る。これは主に関数型言語やその背景理論であるラムダ計算において多用される。数学でも同様の概念はあり、汎関数と呼ばれる。Wikipedia -- 高階関数
関数を引数にしたり、関数を戻り値とする手法って、Ruby や JavaScript では当たり前に使います。
例えば、クロージャを使うって関数を返り値として使う例を見てみます。
function multiple(a){
return function(num){
return a * num;
}
}

// 5 を掛ける関数を生成
var multi5 = multiple(5);

alert(multi5(4));
///// 20

alert(multi5(5));
///// 25
また、次の例のように、関数を引数に取るパターンとして比較関数の例をあげることができます。
// 座標を保持するオブジェクト
function Point(x, y){
this.x = x;
this.y = y;
}

var a = new Point(2, 8);
var b = new Point(5, 6);
var c = new Point(3, 3);

// y 座標が大きい順に並べ替える比較関数を使ってオブジェクトを並べ替え
console.log([a, b, c].sort(
function (x, y) {
return y.y - x.y;
}
);
///// [Object x=2 y=8, Object x=5 y=6, Object x=3 y=3]
今回は高階関数の以下の特徴をそれぞれ見ていきたいと思います。
  • 関数を引数として使う
  • 関数を返り値として使う

関数を引数として使う

html を生成するプログラムを考えます。まず高階関数を使わないで書く場合、
var arr = ['text1', 'text2', 'text3'];
var html='';

for (var i = 0, l = arr.length; i < arr.length; i++) {
var item = arr[i];
html+= '<p>' + item + '</p>';
}

document.getElementById('comment-form').innerHTML = html;
///// <p>text1</p><p>text2</p><p>text3</p>
関数を引数にとる高階関数の考え方を使って同様の動作をするプログラムを書くと次のようになります。
 // 引数の関数を Array の要素それぞれに適用し、
// 結果を文字列として連結する関数
Array.prototype.reduce = function(templateFunction) {
var str = '';
for (var i = 0, l = this.length;i < l; i++) str += templateFunction(this[i]);
return str;
}

// 配列の要素にそれぞれ適用
function prettyTemplate(item) {
return '<p>' + item + '</p>';
}

document.getElementById('comment-form').innerHTML = (['text1', 'text2', 'text3'].reduce(prettyTemplate));
///// <p>text1</p><p>text2</p><p>text3</p>

関数を返り値として使う

関数を返り値として使う高階関数の考え方を使って上記の例を書き直してみると以下のようになります。
function wrapP(){
return function(item){
return '<p>' + item + '</p>';
}
}

['text1', 'text2', 'text3'].reduce(wrapP());
///// <p>text1</p><p>text2</p><p>text3</p>
この例の wrapP 関数をクロージャを使ってもう少し抽象化すると、次のように任意のタグで囲むことができるようになります。
function wrap(tag) {
var s_tag='<'+tag+'>';
var e_tag='</'+tag.replace(/s.*/,'')+'>';

return function(x) {
return s_tag + x + e_tag;
}
}

['text1', 'text2', 'text3'].reduce(wrap('p class="test"'));
///// <p class="test">text1</p><p class="test">text2</p><p class="test">text3</p>

2007年11月22日木曜日

プライベートメンバをサブクラス間で共有してしまう問題

以下のコードのようにローカル変数をオブジェクトに閉じこめる場合、期待した結果と違う結果となることがあります。
この例を見ると少し驚きます。
function Shape() {
var area = 50;
this.setArea = function(a) {area = a;};
this.getArea = function() {return area;};
}

function Square() {
}

Square.prototype = new Shape();

var shape1 = new Square();
var shape2 = new Square();

shape1.setArea(100);

alert(shape1.setArea());
/////100

alert(shape2.setArea());
/////100
/////??...
このように、あるクラスを継承したクラスが 2つの中にローカル変数を閉じ込める場合、ローカル変数がサブクラス間で共有されてしまいます。。

解決法

スーパークラスのコンストラクタをサブクラスのコンストラクタから呼ぶことです。
これにより各々のサブクラスはプライベート変数のローカルコピーを作成するので、サブクラス間でデータが共有されることが確実になくなります。
function Shape() {
var area = 50;
this.setArea = function(a) {area = a;};
this.getArea = function() {return area;};
}

function SquareB() {
Shape.call(this);
}

var shape1B = new SquareB();
var shape2B = new SquareB();

shape1B.setArea(100);

alert(shape1.setArea());
/////100

alert(shape2.setArea());
/////50
とするか、
function Shape() {
var area = 50;
this.setArea = function(a) {area = a;};
this.getArea = function() {return area;};
}

function SquareA() {
this.Shape = Shape;
this.Shape();
}

var shape1A = new SquareA();
var shape2A = new SquareA();

shape1A.setArea(100);

alert(shape1.setArea());
/////100

alert(shape2.setArea());
/////50
とすると成功しますね。

参考:Object-Oriented Programming with JavaScript, Part II: Methods

2007年11月21日水曜日

JavaScript と 継承 2

OOP と JavaScript における継承について、JavaScript と継承 の続きです。

__proto__ プロパティ

Netscape Navigator 4.x 及び Netscape 6 では オブジェクトの __proto__ プロパティにより、オブジェクトのプロトタイプを確認できます。
例を以下に示します。

※ __proto__ プロパティは Internet Explorer ではサポートされていません。
function Shape() {
this.borderWidth = 5;
}

function Square() {
this.edge = 12;
}

// Squeare のプロトタイプとして Shape を呼出す
Square.prototype = new Shape;

// Square のインスタンス生成
myPicture = new Square;

// myPicture のプロトタイププロパティを確認
alert(myPicture.__proto__);

// スーパークラスから継承したプロパティが呼出される
alert(myPicture.borderWidth);
///// 5
この例において、ローカル変数で定義されていないプロパティ boderWidth が呼出されると、__proto__ プロパティがプロパティを探すオブジェクト ( この場合 Shape) を指示します。

また、以下の例のように、オブジェクトの、__proto__ プロパティを次々とたどることができます。
function Language(){};

function English(){};
English.prototype = new Language;

function Alphabet(){};
Alphabet.prototype = new English;

newCharacter = new Alphabet();

alert(newCharacter.__proto__ == Alphabet.prototype);
///// true

alert(newCharacter.__proto__.__proto__ == English.prototype);
///// true

alert(newCharacter.__proto__.__proto__.__proto__ == Language.prototype);
//true

instanceOf メソッドの実装

__proto__ プロパティが利用できる NS では、以下のように、オブジェクトが特定のコンストラクタから生成されたインスタンスかを調べる instanceOf メソッドを定義できます。
// オブジェクトがコンストラクタのインスタンスかどうかを調べる関数
// object, constructorName: コンストラクタ名
function instanceOf(object, constructorName) {
while (object != null) {
if (object == constructorName.prototype) return true;
object = object.__proto__;
}
return false;
}
これを前出の例に適用してみます。newCharacter は Alphabet, English, Language の全てのインスタンスですから、この関数を適用すると以下のようになります。
alert(instanceOf(newCharacter, Alphabet));
///// true

alert(instanceOf(newCharacter, English));
///// true

alert(instanceOf(newCharacter, Language));
///// true

for...in

オブジェクトのプロパティ名は、
for (プロパティ in オブジェクト名)
で参照できます。コンストラクタ employee により生成された newPerson オブジェクトのプロパティ名は以下のように得られます。
function employee() {
this.dept = "HR";
this.manager = "John Johnson";
}

function printProp() {
var newPerson = new employee();
// ken オブジェクトのプロパティを順に表示
for (property in newPerson) {
alert(property);
}
}

printProp();
///// dept
///// manager
以下のように Employee のスーパークラス Person を定義し、Employee のプロトタイプとした場合に、オブジェクトのスーパークラスのプロパティもたどるのか試してみます。
function Person(){
this.animal = 'human';
}

function Employee() {
this.dept = 'HR';
this.manager = 'John Johnson';
}
Employee.prototype = new Person;

function printProp3() {
var Ken = new Employee();
for(prop in Ken) alert(prop);
}

printProp3();
///// dept
///// manager
///// animal
これより、for...in では、オブジェクトのスーパークラスのプロパティもたどることが分かりました。

あるオブジェクトに指定したプロパティが存在するかどうかをチェックするには、hasOwnProperty, や propertyIsEnumerable メソッドを使います。
上記の例に続く以下の例を見てみます。

Ken = new Employee();

// dept プロパティを持つかチェック
alert(Ken.hasOwnProperty('dept'));
///// true

// name プロパティを持つかチェック
alert(Ken.hasOwnProperty('name'));
///// false

Ken.children = new Array('taro', 'sara');

// 0 番目の要素があるか
alert(Ken.children.propertyIsEnumerable(0));
///// true

// children は 3 番目の要素があるか
alert(Ken.children.propertyIsEnumerable(3));
///// false
参考: Object-Oriented Programming with JavaScript, Part I: Inheritance

2007年11月20日火曜日

JavaScript の継承

JavaScript と オブジェクト指向プログラミング (Object-Oriented Programming: OOP) に暫く焦点を当てていきます。

OOP 主要 3 原則

  • カプセル化
  • 継承
  • ポリモーフィズム

継承

OO デザインの特徴として、再利用があります。再利用の王道は継承です。
  • スーパークラスはサブクラスによって生成される
  • サブクラスはスーパークラスの全てのメソッドとプロパティを継承する
  • サブクラスでは継承したスーパークラスのメソッドやプロパティをオーバーライドできる
JavaScript では継承が公式にサポートされていませんが、継承を実装することはできます。
継承の方法を 2 通見てみます。

その 1: 関数による継承

サブクラスのオブジェクト定義の中で、スーパークラスのコンストラクタを呼ぶ方法です。
まず以下のようにスーパークラスとサブクラスを定義し、継承を確認します。
// スーパークラス
function superClass() {
this.bye = superBye;
this.hello = superHello;
}

function superHello() {
return "Hello from superClass";
}

function superBye() {
return "Bye from superClass";
}

// サブクラス
function subClass() {
// スーパークラスのコンストラクタを呼出し
this.inheritFrom = superClass;
this.inheritFrom();
this.bye = subBye;
}

function subBye() {
return "Bye from subClass";
}

// 継承を確認
var newClass = new subClass();

alert(newClass.bye());
///// "Bye from subClass"

alert(newClass.hello());
///// "Hello from superClass"
1 つ目の bye() メソッドはスーパークラスをオーバーライドした結果、subBye() が呼び出されています。
2 つ目の hello() メソッドは親クラスのメソッドを継承し superHello が呼出されています。

その 2: プロトタイプを使った継承

スーパークラスのオブジェクトを定義し、サブクラスのプロトタイプとする方法です。例 1 をプロトタイプを使った継承の方法で書き直すと以下のようになります。
 // スーパークラスのコンストラクタ
function superClass() {
this.bye = superBye;
this.hello = superHello;
}

function superHello() {
return "Hello from superClass";
}

function superBye() {
return "Bye from superClass";
}

function subClass() {
this.bye = subBye;
}

// サブクラスのプロトタイプとして
// スーパークラスのコンストラクタを呼出し
subClass.prototype = new superClass;

function subBye() {
return "Bye from subClass";
}

// 継承を確認する
var newClass = new subClass();

// サブクラスのプロパティが呼出される
alert(newClass.bye());
///// "Bye from subClass";

// サブクラスのプロトタイプ
// (= スーパークラスのコンストラクタ) から
// メソッドを呼出し
alert(newClass.hello());
///// "Hello from superClass";
コンストラクタが実行された後にスーパークラスのメソッドやプロパティを追加しても、サブクラスは新しいメソッドやプロパティを使うことができます。
この例として、以下のように、スーパークラスのプロトタイププロパティを加えてみます。
(上記の例の続きです。)
// スーパークラスのプロトタイプに新しいプロパティを追加
superClass.prototype.goodNight = superGoodNight();

function superGoodNight(){
return "Good Night from superClass.";
};

alert(newClass.goodNight);
///// "Good Night from superClass."
参考: Object-Oriented Programming with JavaScript, Part I: Inheritance

(続く)

2007年11月19日月曜日

クロージャ 3

JavaScript でクロージャを考えます。
クロージャクロージャと Privileged メソッド の続きです。
この前、簡単にクロージャの考え方を示したら、クロージャは面白そうだけれども、結局どんなことができるの? と聞かれてしまいました。
まだまだ例が不足しているので、僕が前からチェックしようと思っていてなかなかチェックできていなかった、Javascript Closures
というサイトがあるのでいま一度チェックしてみました。

ここにはクロージャを使った 3 つの例が示されています。

例 1: 関数への参照を使った setTimeout

これは前に示しました。
// 無名内部関数への参照を返す
function callLater(elem, paramB, paramC){
return (function(){
elem.style[paramB] = paramC;
});
}

//
// 実行時のコンテクストによって生成された内部関数への参照を返す
// 関数を呼出す。
//
var functRef = callLater(document.getElementById('clHead')
, 'display', 'none');

//
// 0.5 秒後にエレメントを消す
//
setTimeout('functRef()', 500);

例 2: オブジェクトのインスタンスメソッドに関数を関連させる

2 つめに挙げられている例は以下のように、自分でイベント名を定義し直すパターンです。この例では定義し直しているメソッドは 1 つだけだけれども、複数定義する場合で、オブジェクトのインスタンスを沢山生成するときには便利そうです。
function associateObjWithEvent(obj, methodName){
return (function(e){
e = e||window.event;
return obj[methodName](e, this);
});
}

function DhtmlObject(elemId){
var el = document.getElementById(elemId);
// 要素が見つかったとき
if(el){
el.onclick = associateObjWithEvent(this, 'doOnClick');
}
}

DhtmlObject.prototype.doOnClick = function(event, element){
alert('要素をクリックしました。');
};

var a = new DhtmlObject('clObjI');

例3: カプセル化

最後は分かりやすいです。image 要素を持った div 要素の様々なプロパティをクロージャを使って指定する例です。様々に指定して要素が作れますね。
var getImgInPositionedDivHtml = (function(){
var buffAr = [
'<div id="',
'', //index 1, DIV ID attribute
'" style="position:absolute;top:',
'', //index 3, DIV top position
'px;left:',
'', //index 5, DIV left position
'px;width:',
'', //index 7, DIV width
'px;height:',
'', //index 9, DIV height
'px;overflow:hidden;\"><img src=\"',
'', //index 11, IMG URL
'\" width=\"',
'', //index 13, IMG width
'\" height=\"',
'', //index 15, IMG height
'\" alt=\"',
'', //index 17, IMG alt text
'\"><\/div>'
];

return (function(url, id, width, height, top, left, altText){
buffAr[1] = id;
buffAr[3] = top;
buffAr[5] = left;
buffAr[13] = (buffAr[7] = width);
buffAr[15] = (buffAr[9] = height);
buffAr[11] = url;
buffAr[17] = altText;
return buffAr.join('');
});
})();

document.getElementById('clHead').innerHTML =
getImgInPositionedDivHtml(
'http://www.cirius.co.jp/',
'new-elem',
'100',
'100',
'100',
'100',
'新しい要素'
);

そのほか


この他には クロージャと Privileged メソッド で挙げた Privileged メソッドが良い例ですね。

追記: JavaScript とクロージャ も面白いです。

2007年11月18日日曜日

クロージャと Privileged メソッド

昨日の クロージャの続きです。
OOP な JavaScript では Public メソッド, Private メソッド, Privileged メソッド を定義できますが、クロージャを用いることにより Privileged メソッド が実現できます。
これらのメソッドについて順に見ていくことにします。

Public メソッド

まず Public メソッド を見てみます。オブジェクトのプロパティは、全てが Public で、これらはどんな関数からもアクセスすることができます。
// 神様のコンストラクタ
function God (name, feature) {
this.name = name;
this.feature = feature;
}

// 神様プロトタイプを上書き
God.prototype.getName = function(){
return this.name;
}
God.prototype.getFeature = function(){
return this.feature;
}

// 神様をインスタンス化して大黒天を生成
var god = new God('Daikokuten', 'Syokuji');

// Public メソッド呼出し
alert(god.getName());
///// Daikokuten
オブジェクトにプロパティを追加するには、上記のように、
  • コンストラクタで this キーワードにより追加する
  • プロトタイププロパティで定義する
の 2 通りあります。

プロパティ及び Public メソッドはいつでも追加可能です。
例えば god オブジェクトに年齢プロパティと年齢を取得する Public メソッドを追加しようと思ったら、次のように実現できます。
// 年齢プロパティを追加
god.age = 2000;

// 年齢取得用 Public メソッドを追加
god.getAge = function (){alert(this.age)};

god.getAge();
/////2000

他の神様オブジェクトにも年齢プロパティと年齢を取得する Public メソッドを追加したい場合には、次のように God の prototype プロパティを上書きする方法を取ります。
// God の prototype プロパティを上書き
God.prototype.age = '';
God.prototype.getAge = function(){alert(this.age)};

// age プロパティに代入
god.age = 2000;

// Puclic メソッド呼出し
god.getAge();
/////2000

Private メソッド

次に Private メソッド を見てみます。
// 神様のコンストラクタ
function God (name, feature) {
/**
* 名前を表示する。
* var _getName = function(){....} と同じ
* @access private
*/
function _getName(){
alert(name);
}

// 渡された引数を保存
var name = name;
var feature = feature;

var self = this;

// 名前を表示する関数を実行
_getName();
}

// Public メソッドも定義する
function _getName(){ alert('uso')};

var god = new God('Daikokuten', 'Syokuji');
///// Daikokuten

// Private メソッドにはアクセスできない
_getName();
///// uso
上の例の場合、外部から関数内の var で定義された変数にアクセスすることができず、期待する値と異なる値が返ってくることが分かります。
また、name, age プロパティは定義されておらず、外部からアクセスできないことが分かります。

Privileged メソッド

作成時にしか名前を教えてもらえないのは不便なので、上記を改良して、3 回までなら神様の名前を教えてもらえる関数を作ってみます。
この場合、name をプロパティとして定義せず、限定的に公開するメソッド (Privileged メソッド) を使って以下のように実現することができます。
// 神様のコンストラクタ
function God (name, feature) {
/**
* 制限回数以内かを返却する
* @access private
*/
function _getPrev() {
if (allowed > 0) {
allowed--;
return true;
}
return false;
}

// 渡された引数を保存
var name = name;
var feature = feature;

// 名前を教えることを許可する回数
var allowed = 3;
var self = this;

// Privileged メソッド
this.getName = function (){
return getPrev()? name: 'もう教えません。';
};
}

var god = new God('Daikokuten', 'Syokuji');

alert(god.getName());
///// Daikokuten

alert(god.getName());
///// Daikokuten;

alert(god.getName());
///// Daikokuten;

// 3 回目移行は教えてもらえない
alert(god.getName());
///// もう教えません。
上記のように Privileged メソッドは、クロージャを使い実現されます。

まとめ

以上からPrivileged メソッドの特徴は次のようになります。
  • オブジェクト生成時に作成される
  • プライベート変数とプライベートメソッドにアクセスできる
  • パブリックメソッドなので、削除したり置き換えたりすることができる
  • 改竄したり、隠蔽しているものを無理に明け渡させることはできない

2007年11月17日土曜日

クロージャ

JavaScript のクロージャを勉強してみる。
クロージャとは以下のようなことだ。
内部関数が、外部関数が終了した後でも、常に外部関数で宣言された変数とパラメータにアクセスできること。
http://d.hatena.ne.jp/brazil/20051028/1130468761
クロージャを使わないときとクロージャを使うときを比較して考えてみる。
まずクロージャを使わないパターン。
// 引数同士を加算する関数
function adder(a, num) {
return a + num;
}

var a = adder;
var b = 3;

a(b, 2);
///// 5

a(b, 5);
///// 8
被加算数は同じなのに、毎回被加算数を書く必要があるのが少し冗長に感じる。
クロージャを使って、もう少し綺麗に書ける。
// 足し算をする関数を返す関数
function adder(num) {
return function(add){
return num + add;
};
}

var a = adder(3);

a(2);
///// 5

a(5);
////// 8
次に、以下のように、ページ訪問者にメッセージを表示する例を考えてみる。
まずはクロージャを使わずに普通に実装する。
var msg = 'welcome!';

// ロード後にメッセージを表示
window.onload = function(){
document.getElementById('msg').innerHtml = msg;
}
これだとグローバル変数がもったいない。
クロージャを使って以下のようにグローバル変数を節約できる。
// ロード後にメッセージを表示
(function(){
var msg = 'welcome!';

window.onload = function(){
document.getElementById('msg').innerHTML = msg;
})();
次はクロージャを使ってメッセージを遅延させて表示する関数を考える。
// メッセージを遅延させて表示する
function delayedMsg(msg, time) {
setTimeout(function(){
alert(msg);
}, time);
}

delayedMsg('Welcome!', 2000);
///// Welcome!
クロージャを使わない場合は、以下のようにコードが汚なくなる。
var msg = 'Welcome!';

setTimeout("alert('" + msg + "')", 2000);
///// Welcome!

2007年11月16日金曜日

DOM のロード直後に実行する関数の指定方法

Javascript で DOM を操作する際にはページ内の DOM 要素がロードされている必要がある。
このとき問題になるのは、
  • DOM がロードされたタイミングをどのように検知するか。
  • DOM がロードされたタイミングで実行する関数の指定方法。
ということになる。

まず、次のような html を考える。
<html>
<head>
<title></title>
<script>
// ページヘッダの読み込み時刻を記録
var start = new Date();
</script>
</head>
<body>

<!--ロードに10秒かかる画像を指定-->
<img src="http://example.com/sleep10" />
</body>
</html>

ここで、 image 要素には 10 秒後に結果を返すスクリプトを指定している。
window.onload を使い、ページのヘッダが読み込まれてから、関数が実行されるまでの実行時間を計測し、Firebug コンソールに書き込む。
window.onload = function(){
// 関数が実行され始める時刻を記録
stop = new Date();
// firebug のコンソールにログ出力
console.log('window.onload' + (stop - start));
}

/////window.onload 100005

window.onload で指定した関数はページ内の画像ファイルが全てロードされてから実行されるので、結果は、以下のようになる。
画像のロード時間 (10秒) + 関数の実行時間

しかし、DOM を操作するために、画像が完全にロードされている必要はない。
そこで、以下のように、body の最後の子要素に、実行したい関数を書き、DOM がロードされたら自動的に関数が実行されるようにする。
この方法だと、DOM がロードされているが、画像のロードを待たずにすむので、先程より速くなる。
<html>
<head>
<title></title>
<script>
var start = new Date();
</script>
</head>
<body>

<!--ロードに10秒かかる画像を指定-->
<img src="http://example.com/sleep10" />

<!--この位置では全ての DOM 要素は読み込まれている-->
<script>
stop = new Date();
console.log('script-end ' + (stop - start));
</script>
</body>
</html>

/////script-end 5
ただし、この方法は html 内に余計なDOM 要素を加えるので一般的に推奨されていない。
jQuery では、以下のように function 部分に任意の関数を指定すると、
$(document).ready(function(){});
DOM 要素がロードされてから、実行される関数を指定可能である。
ロード後に実行したい関数を指定できる数に制限はなく、DOM ロード後に実行したい関数が複数ある場合は、
// DOM ロード後に実行したい 1 つ目の関数
$(document).ready(function1(););

// DOM ロード後に実行したい 2 つ目の関数
$(document).ready(function2(););
....
という書き方ができる。
これと同様に、jQuery を使わない場合でも、以下のように DOM ロード直後に実行する関数を指定できると嬉しい。
domReady(function(){
stop = new Date();
console.log('using domReady ' + (stop - start));
});

////using domReady 5
この原理は、以下の 2 つの関数で達成される。
function domReady(f){
// DOM がロードされているときは関数をそのまま実行
if(domReady.done) return f();

// ロード後に実行する関数が複数ある場合は配列に挿入
if(domReady.timer){
domReady.ready.push(f);
}
else {
// DOM がロードされる方がisDomReady で
// 検知される場合より早かった場合の対策。
window.onload = function(){isDomReady();};

// DOM ロード直後に実行する関数の配列の初期化
domReady.ready = [f];

// DOM がロードされているかを定期的にチェック
domReady.timer = setInterval(isDomReady, 13);
}
}

/**
* DOM がロードされているかをチェックする。
*/
function isDomReady(){
if (domReady.done) return false;

// ページ内の DOM 要素がロードされているかをチェックしている。
if (document && document.getElementsByTagName &&
document.getElementById && document.body) {

clearInterval(domReady.timer);
domReady.timer = null;

// 予約された関数を順次実行
for(var i = 0, l = domReady.ready.length; i < l; i++) {
domReady.ready[i]();
}

domReady.ready = null;
domReady.done = true;
}
}

jQuery などのライブラリを使わない場合で、DOM ロード直後に実行する関数を指定したい場合に使えます。

2007年11月5日月曜日

OpenSocial を紐解く


日本では mixi 、海の向こうでは Facebook と、Social Networking はまさにピークを迎えているようにも思える。
一方で開発者視点で見ると発展の余地があり、Google を中心とした OpenSocial という API 群が公開された。

OpenSocial とは何か


OpenSocial について、Google code で公開されている OpenSocial のドキュメント を見ていく。API 群の見出しは以下である。
The web is better when it's social.

これを踏まえて、ドキュメントを読んでいく。

The web is more interesting when you can build apps that easily interact with your friends and colleagues. But with the trend towards more social applications also comes a growing list of site-specific APIs that developers must learn.

Social な WebAPP により、Web の世界はますます面白くなる。
一方で、この流れにより、開発者は API の仕様を Web APP ごとに覚え、APP を作成しなければならない。

そこで、Social Networking な Web APP に公開される API の仕様を OpenSocial として標準化していくことで、開発者の負担を減少させることができる、ということだ。

OpenSocial provides a common set of APIs for social applications across multiple websites. With standard JavaScript and HTML, developers can create apps that access a social network's friends and update feeds.

この文章からOpenSocial は以下のようなものであることが分かる。
  • ソーシャルネットワークそのものではない。
  • Javascript と html により、開発者にソーシャルネットワークにおける中心的な機能、及び中心的な情報へのアクセスを提供する汎用的な API 群 (Common set of APIs) である。
OpenSocial の特徴として前出のページで列挙されている特徴は以下の 2 つである。
  • Many sites, one API
  • Server Optional
    • OpenSocial は Google Gadget の技術を使用しており、開発されたサードパーティ制の APP を Google を始め、好きなサーバにホスティングさせることができる。

OpenSocial が提供する API


OpenSocial API Documentation によると、OpenSocial の API群を使うにあたり、
  • Client-side で javascript を用いる方法
  • Server-side で Rest スタイルの dataAPIs を用いる方法
の 2種類の方法がある。
Client-side: Javascript による API
以下の情報にアクセスできる。
  1. プロフィール情報 (user data)
  2. 友達情報 (social graph)
  3. 活動 (things that happen, News Feed type stuff)
OpenSocial のトップページで利用されている上図は、SNS 空間から、上記の情報を API により抽出しているイメージだと思われる。
Server-side: Atom Pub (GData API) を用いたリソースの操作
Server Side では以下の 3 つの API が定義されている。
  1. The People and Friends data API

    • クライアント APP は Atom Pub または GData APIs を使い、プロフィールや友人関係の閲覧・編集ができる。

  2. The Activities data API

    • クライアント APP は Atom Pub または GData APIs を使い、"action" を閲覧、発行できる.
    • ここでいう "action" とは例えばユーザがいつコンテンツをアップロードしたか、とか作成したか、といった情報のことである。

  3. The Persistence data API

    • クライアント APP はクライアント APP 特有の key, value の組み合わせを閲覧、発行できる

※本記事は以下を参考にしています。
OpenSocial - Google Code
Google Announces the OpenSocial API
たけまる / OpenSocial Protocol

上記以外の関連リンクは以下にまとめてみました。
Haida's clips OpenSocial