IT戦記

プログラミング、起業などについて書いているプログラマーのブログです😚

Prototype.js を使った JavaScript OOP 講座 #01

社内の精鋭エンジニアを中心に定期的に勉強会をすることになった。んで、 JavaScript の講義は僕がやることになった。
資料を社内だけでとどめておくのはもったいないので、ここに公開していきます。社内の人も社外の人も読んでください。

講義の内容は基本的にソース嫁。ソースレビュー形式。


※ターゲットは JavaScript は書いたことない、オブジェクト指向言語プログラマ。

Section 00 Prototype.js の前に JavaScript のオブジェクトの概要・・・

オブジェクトを作ってみる。

var object = {};

オブジェクトにメソッドとかプロパティを追加してみる。

var object = {
    field: 'IT戦士',
    method: function() {
        alert('hello ' + this.field);
    }
};
object.method();

後で関数を付けたりできる。関数内の this は実行時に決まる。

var object = {
    field: 'IT戦士'
};
function func() {
    alert('hello ' + this.field);
}
object.method = func;
object.method();

呼び出すときに直接 this を与えることも出来る。

function func() {
    alert('hello ' + this.field);
}
var object = {
                field: 'IT戦士'
};
func.apply(object);
//  func.call(object);

同じ形式のオブジェクトを大量生産したい場合がある。以下のようにする。
関数の prototype プロパティに大量生産したいオブジェクトを入れる。new を付けて関数を呼び出してオブジェクトのコピーを大量生産する。

function factory() {}
factory.prototype = {
    field: 'IT戦士',
    method: function() {
        alert('hello ' + this.field);
    }
};
var object00 = new factory();
var object01 = new factory();
var object02 = new factory();

new を付けて呼び出すとコピーされたオブジェクトが this となって関数が呼ばれる。すなわち、初期化を行うことが出来る。

function factory() {
    this.field = 'IT戦士';
}
factory.prototype = {
    method: function() {
        alert('hello ' + this.field);
    }
};
var object00 = new factory();
var object01 = new factory();
var object02 = new factory();

ちなみに下の二つの関数宣言はほとんど同じ意味。(ちょっと違うけど)

function factory() {}
var factory = function() {};

これを踏まえて、さっそく Secsion 01 でクラスを作ります。

Section 01 Class.create

Source 01 通常のクラス
var Human = function(name, hobby){
    this.name = name || '';
    this.hobby = hobby || '';
};
Human.prototype = {
    name: '',
    hobby: '',
    someThing1: 'hogehoge',
    someThing2 : 'hugahuga',
    someMethod1 : function() {
        alert(this.someThing1);
    },
    someMethod2 : function() {
        alert(this.someThing2);
    },
    toString: function() {
        var array = [];
        array.push('名前:', this.name || 'なし', ', 趣味:', this.hobby || 'なし');
        return array.join('');
    }
};
var amano =new Human('天野','勉強');
amano.someMethod1();
amano.someMethod2();
alert(amano);

上の例を実行

Source 02 コンストラクタは中に書きたい
var Human = function(name, hobby){
    this.intialize(name, hobby);
};
Human.prototype = {
    name: '',
    hobby: '',
    someThing1: 'hogehoge',
    someThing2 : 'hugahuga',
    intialize: function(name, hobby){
        this.name = name || '';
        this.hobby = hobby || '';
    },
    someMethod1 : function() {
        alert(this.someThing1);
    },
    someMethod2 : function() {
        alert(this.someThing2);
    },
    toString: function() {
        var array = [];
        array.push('名前:', this.name || 'なし', ', 趣味:', this.hobby || 'なし');
        return array.join('');
    }
};
var amano =new Human('天野','勉強');
amano.someMethod1();
amano.someMethod2();
alert(amano);

上の例を実行

Source 03 Class.create を使うと・・・
var Human = Class.create();
Human.prototype = {
    name: '',
    hobby: '',
    someThing1: 'hogehoge',
    someThing2 : 'hugahuga',
    initialize  : function(name, hobby) {
        this.name = name || '';
        this.hobby = hobby || '';
    },
    someMethod1 : function() {
        alert(this.someThing1);
    },
    someMethod2 : function() {
        alert(this.someThing2);
    },
    toString: function() {
        var array = [];
        array.push('名前:', this.name || 'なし', ', 趣味:', this.hobby || 'なし');
        return array.join('');
    }
};
var amano =new Human('天野','勉強');
amano.someMethod1();
amano.someMethod2();
alert(amano);

上の例を実行


Section 02 Object.extend

Source 01 通常の継承
var Human = Class.create();
Human.prototype = {
    name: '',
    hobby: '',
    someThing1: 'hogehoge',
    someThing2 : 'hugahuga',
    initialize  : function(name, hobby) {
        this.name = name || '';
        this.hobby = hobby || '';
    },
    someMethod1 : function() {
        alert(this.someThing1);
    },
    someMethod2 : function() {
        alert(this.someThing2);
    },
    toString: function() {
        var array = [];
        array.push('名前:', this.name || 'なし', ', 趣味:', this.hobby || 'なし');
        return array.join('');
    }
};

var Programmer = Class.create();
Programmer.prototype = new Human();
Programmer.prototype.programmingLanguage = '';
Programmer.prototype.initialize = function(name, hobby, programmingLanguage) {
    Human.prototype.initialize.apply(this, [name, hobby]);
    this.programmingLanguage = programmingLanguage;
};
Programmer.prototype.toString = function() {
    var array = [];
    array.push(Human.prototype.toString.apply(this), ', プログラミング言語:', this.programmingLanguage || 'なし');
    return array.join('');
}

var amano =new Programmer('天野','勉強', 'JavaScript');
amano.someMethod1();
amano.someMethod2();
alert(amano);

//  Human の定義を変更
Human.prototype.toString = function() {
    var array = [];
    array.push('名前:', this.name || 'なし');  //  趣味の表示をやめる
    return array.join('');
    return '';
};

alert(amano);

上の例を実行

Source 02 Object.extend を使うと・・・
var Human = Class.create();
Human.prototype = {
    name: '',
    hobby: '',
    someThing1: 'hogehoge',
    someThing2 : 'hugahuga',
    initialize  : function(name, hobby) {
        this.name = name || '';
        this.hobby = hobby || '';
    },
    someMethod1 : function() {
        alert(this.someThing1);
    },
    someMethod2 : function() {
        alert(this.someThing2);
    },
    toString: function() {
        var array = [];
        array.push('名前:', this.name || 'なし', ', 趣味:', this.hobby || 'なし');
        return array.join('');
    }
};

var Programmer = Class.create();
Programmer.prototype = Object.extend(new Human(), {
    programmingLanguage: '',
    initialize: function(name, hobby, programmingLanguage) {
        Human.prototype.initialize.apply(this, [name, hobby]);
        this.programmingLanguage = programmingLanguage;
    },
    toString: function() {
        var array = [];
        array.push(Human.prototype.toString.apply(this), ', プログラミング言語:', this.programmingLanguage || 'なし');
        return array.join('');
    }
});

var amano =new Programmer('天野','勉強', 'JavaScript');
amano.someMethod1();
amano.someMethod2();
alert(amano);

//  Human の定義を変更
Human.prototype.toString = function() {
    var array = [];
    array.push('名前:', this.hobby || 'なし'); //  趣味の表示をやめる
    return array.join('');
    return '';
};

alert(amano);

上の例を実行

Source 03 Object.extend は継承のためにある訳ではない
var Human = Class.create();
Human.prototype = {
    name: '',
    hobby: '',
    someThing1: 'hogehoge',
    someThing2 : 'hugahuga',
    initialize  : function(name, hobby, options) {
        options = Object.extend({
            sex: 'なし',
            age: 'なし',
            location: 'なし'
        }, options || {});
        Object.extend(this, options);
        this.name = name || '';
        this.hobby = hobby || '';
    },
    someMethod1 : function() {
        alert(this.someThing1);
    },
    someMethod2 : function() {
        alert(this.someThing2);
    },
    toString: function() {
        var array = [];
        array.push('名前:', this.name || 'なし', ', 趣味:', this.hobby || 'なし');
        array.push(', 性別:', this.sex, ', 年齢:', this.age, ', 地域:', this.location);
        return array.join('');
    }
};

var Programmer = Class.create();
Programmer.prototype = Object.extend(new Human(), {
    programmingLanguage: '',
    initialize: function(name, hobby, programmingLanguage, options) {
        Human.prototype.initialize.apply(this, [name, hobby, options]);
        this.programmingLanguage = programmingLanguage;
    },
    toString: function() {
        var array = [];
        array.push(Human.prototype.toString.apply(this), ', プログラミング言語:', this.programmingLanguage || 'なし');
        return array.join('');
    }
});

//  天野誕生
var amano =new Programmer('天野','勉強', 'JavaScript', { sex: 'おとこ' });
amano.someMethod1();
amano.someMethod2();
alert(amano);

上の例を実行


Section 03 Array.prototype.each

Source 01 配列の要素走査
//  Section 02 および Section 03 で作った Human と Programmer を使用する。
var humans = [
    new Programmer('きたの','レゴ', 'Java'),
    new Programmer('ZIGOROゥ','Perl', 'Perl', { location: 'ゑびす' }),
    new Human('天野','勉強')
];

for(var i = 0, length = humans.length; i < length; i ++) {
    alert(humans[i]);
}

上の例を実行

Source 02 Array.prototype.each を使った配列走査
//  Section 02 および Section 03 で作った Human と Programmer を使用する。
[
    new Programmer('きたの','レゴ', 'Java'),
    new Programmer('ZIGOROゥ','Perl', 'Perl', { location: 'ゑびす' }),
    new Human('天野','勉強')

].each(alert);

上の例を実行


Section 04 Function.prototype.bind

Source 01 通常のインスタンスメソッドを使ったコールバック
//  Section 02 および Section 03 で作った Human と Programmer を使用する。
var Room = Class.create();
Room.prototype = {
    initialize: function() {
        this.members = [];
    },
    addHuman: function(human) {
        this.members.push(human);
    },
    toString: function() {
        return this.members.length + '人';
    }
};

var room = new Room();
[
    new Programmer('きたの','レゴ', 'Java'),
    new Programmer('ZIGOROゥ','Perl', 'Perl', { location: 'ゑびす' }),
    new Human('天野','勉強')

].each(function(human) { room.addHuman(human) });
alert(room);

上の例を実行

Source 02 Function.prototype.bind を使ったインスタンスメソッドを使ったコールバック
//  Section 02 および Section 03 で作った Human と Programmer を使用する。
var Room = Class.create();
Room.prototype = {
    initialize: function() {
        this.members = [];
    },
    addHuman: function(human) {
        this.members.push(human);
    },
    toString: function() {
        return this.members.length + '人';
    }
};

var room = new Room();
[
    new Programmer('きたの','レゴ', 'Java'),
    new Programmer('ZIGOROゥ','Perl', 'Perl', { location: 'ゑびす' }),
    new Human('天野','勉強')

].each(room.addHuman.bind(room));
alert(room);

上の例を実行