javascriptの最近のブログ記事
最近話題の「はてなスター」を入れてみました。
ついさっき設置完了しました。
この発想はひじょーに面白いですね。
昔ははてなは技術者嗜好なアプリケーションを作っていたイメージで、ちょっと一般人には敷居が高いイメージでしたけど、Rimoといい、このはてなスターといい、一般ユーザーフレンドリーな感じがします。
設置も、Javascriptをblogに加えるだけとお手軽なのも良い感じ。
条件はブログのエントリタイトルに<h3>で囲っている事と、タイトルにパーマリンク(記事単体への固定のリンク)があることです。多くのblogがタイトルは<h3>で、パーマリンクがついていると思うのですが、もしついていなかった場合は、パーマリンクって言葉が一般ユーザーにわかるのでしょうか??ちょっと気になります。
とはいえ、その辺りは段々Hackされてくるんでしょうけどね。
ちなみにid:naoyaさんのblogによると、id:jkondoさんの発案みたいです。とても「らしい」サービスな感じがしています。
昔書いたものですが、再利用します。オブジェクト思考だから。
前に書いたOOJS続きです。
Javascriptでの継承とオーバーライド
前回Javascriptでのオブジェクト指向プログラミングを見てきました。
今回は継承とオーバーライドについてお話します。Javascriptでは継承はcall()とapply()を使います。
call()メソッドを使った継承の例
下のコードを見てみてください。
<html>
<body>
<script type="text/javascript">
function Person(_name, _email) {
this.name = _name;
this.email = _email;
this.show = function () {
document.writeln(" 名前 : " + this.name + "</br>");
document.writeln(" Eメール : " + this.email + "</br><hr>");
}
}
function Manager(_name, _email, _roll) {
Person.call(this, _name, _email);
this.roll = _roll
this.show = function () {
document.writeln(" 名前 : " + this.name + "</br>");
document.writeln(" Eメール : " + this.email + "</br>");
document.writeln(" 役職 : " + this.roll + "</br><hr>");
}
}
var taro = new Person("太郎", "taro@example.com");
taro.show();
var hanako = new Manager("花子", "hanako@example.com", "部長");
hanako.show();
</script>
</body>
</html>
Person関数はnameとemailというプロパティを持っています。また、プロパティを表示するshow()というメソッドを持っています。
ここで、役職を持っているAdminという関数が必要になったとします。
役職を識別するrollというプロパティが新たに必要です。勿論Adminにもnameとemailはあるでしょう。
こういった場合、Personを継承してAdminという関数(クラス)を作るのが良いです。
function Manager(_name, _email, _roll) {
Person.call(this, _name, _email);
this.roll = _roll
this.show = function () {
document.writeln(" 名前 : " + this.name + "</br>");
document.writeln(" Eメール : " + this.email + "</br>");
document.writeln(" 役職 : " + this.roll + "</br><hr>");
}
}
ポイントは、Person関数をcall()メソッドで読んでいる所です。
call()メソッドはFunctionオブジェクトが持つメソッドで、関数に引数を渡しています。また、showメソッドを上書きしています。
継承元の関数を、継承した関数で上書きすることをオーバーライドと言います。
これでオブジェクト指向プログラミングのポリモフィズムは実現できそうです。
ただし、オーバライドを含む継承の際にはcall()を呼ぶ場所に注意してください。試しに下記コードを実行してみてください。
call()を呼ぶ位置を一番下にしてみました。
function Manager(_name, _email, _roll) {
this.roll = _roll
this.show = function () {
document.writeln(" 名前 : " + this.name + "</br>");
document.writeln(" Eメール : " + this.email + "</br>");
document.writeln(" 役職 : " + this.roll + "</br><hr>");
}
Person.call(this, _name, _email);
}
Person関数のなかでもthis.showプロパティにファンクションを代入しているため、showがオーバーライドされてしまっていますね?
ほとんどの場合、は先頭で呼んでおけば問題は無いと思います。
apply()メソッドを使った継承の例
ほとんどcall()の場合と変わりません。
<html>
<body>
<script type="text/javascript">
function Person(_name, _email) {
this.name = _name;
this.email = _email;
this.show = function () {
document.writeln(" 名前 : " + this.name + "</br>");
document.writeln(" Eメール : " + this.email + "</br><hr>");
}
}
function Manager() {
Person.apply(this, arguments);
this.roll = arguments[2];
this.show = function () {
document.writeln(" 名前 : " + this.name + "</br>");
document.writeln(" Eメール : " + this.email + "</br>");
document.writeln(" 役職 : " + this.roll + "</br><hr>");
}
}
var taro = new Person("太郎", "taro@example.com");
taro.show();
var hanako = new Manager("花子", "hanako@example.com", "部長");
hanako.show();
</script>
</body>
</html>
apply()メソッドは可変長引数に対応しています。
但し、IEではサポートしていないので、注意が必要です。
Javascriptでのオーバーロード
Javascriptではオーバーロードは可能でしょうか?
オーバーロードとは、同じ名前のメソッドですが引数の数を変えることで、別のメソッドとして扱う事です。
下のコードはJavaでのオーバーロードの例です。
class hoge() {
// 省略
void example(String strVal) {
// 処理1
}
void example(String strVal, int intVal) {
// 処理2
}
}
上記の様な同名のメソッドが合った場合、example("文字列")と呼び出せば、処理1が実行されます。example("文字列", 1)と呼び出された場合、処理2が実行されます。感の良い皆様はおわかりだと思いますが、Javascriptにおいてはオーバーロードは実装出来ません。
Javascriptのメソッドはあくまでプロパティと同じ扱いなので、上記の用に書いた場合、2個目のファンクションで1つめのファンクションが上書きされてしまいます。
Javaにおいてはメソッドを識別するのに、メソッド名と引数の定義を使っています。コレをメソッド・ディスクリプタと言います。
Javascriptではプロパティ名としか認識していませんし、可変長引数が有るため、引数の定義でメソッドを識別出来ないためですね。誠に残念ではありますが、此処は泣いて馬謖を斬るしか有りません。
これでオブジェクト指向プログラミングの最低限の道具はそろいました。
最近、昔書いた物をあれこれまとめ中です。
折角編集したのでここに、さらします。
オブジェクト指向を説明するのに使ったもの。
JavaScriptとJavaを説明に使っています。Object Oriented JavaScriptって奴です。
途中変なテンションになっているのはご容赦下さい。
オブジェクト指向についてのおさらい
まず、オブジェクト指向についておさらい。
オブジェクト指向言語Javaで書いてサンプルを見てみましょう。
class Dog {
String name;
Dog(String name) {
this.name= name;
}
String bark() {
return name + "[わん]";
}
}
public class BarkCall {
public static void main(String[] args) {
Dog pochi = new Dog("ポチ");
Dog taro = new Dog("タロー");
System.out.println(pochi.bark());
System.out.println(taro.bark());
}
}
実行結果はこうなります。
[oojs azazel]$ java BarkCall ポチ[わん] タロー[わん]
シンプルなオブジェクト指向の一例です。
Dogクラスのインスタンスである”pochi”と"taro"に泣けとメッセジーを送ると、自分の名前と共に、[わん]と泣きます。
コレはあたかも、現実世界の犬に”お手!”とメッセージを送っているようなモノです。
次にオブジェクト指向の神髄の一つ、ポリモフィズム(多態性)について見てみましょう。
abstract class Animal {
abstract String cry();
}
class Dog extends Animal{
String cry() {
return "わん!";
}
}
class Baby extends Animal{
String cry() {
return "おぎゃー!";
}
}
class Cat extends Animal{
String cry() {
return "にゃー!";
}
}
public class CryCall {
public static void main(String[] args) {
Animal dog = new Dog();
Animal baby = new Baby();
Animal cat = new Cat();
System.out.println(dog.cry());
System.out.println(baby.cry());
System.out.println(cat.cry());
}
}
ポリモフィズムのメリットは2点あります。
一点目はAnimalクラスに実装の無い抽象メソッド、cry()を定義している点。
こうすることで、Animalを継承したクラスには、cry()メソッドを実装する事を強制出来ます。
つまり、必ず必要なインターフェースを統一出来るのです。
二点目は継承してクラスの実体(インスタンス)は、スーパークラスの型に代入出来る点です。
こうすることで、インスタンスを利用する側は、今、「泣け!」とメッセージを送った相手が、Dogであるのか、Babyであるのか、Catであるのか意識する必要がないのです。
これは、あたかも「現実のモノに対してメッセージを送る」というように、現実社会のあり方に沿った自然な書き方なんです。
オブジェクト指向万歳!
・・・・
なんて説明をよく聞きますけど、理解できますか?
私は出来ませんでした。そもそも「動物」って動物を現実社会で見たことは有りません。
私の考えるオブジェクト指向のメリットは非常に単純です。
「コードを書く量を減らす->修正が楽になる=開発が楽になる。」
それだけです。
オブジェクト指向の神髄とメリットを実感するなら、デザインパターンを学ぶのが良いと思いますが、それはまた次回に。
OOJS(Object Oriented JavaScript)
とにかく、JavaScriptでオブジェクト指向を実装してみましょう。
単純なクラスの例
基本的に変わらないのですが、ちょっとだけ癖が有ります。
今度は単純にDogクラスを実装してみます。
<html>
<body>
<script type="text/javascript">
function Dog(_name) {
this.name = _name;
}
var dog = new Dog("ポチ");
document.writeln(dog.name);
</script>
</body>
</html>
此処まではほとんどかわりませんよね?
ではbarkメソッドを追加してみましょう。
<html>
<body>
<script type="text/javascript">
function Dog(_name) {
this.name = _name;
this.bark = bark
}
function bark() {
document.writeln(dog.name + " : わん!")
}
var dog = new Dog("ポチ");
dog.bark();
</script>
</body>
</html>
Javaの場合と違うのは、クラス、メソッドという概念ではなく、bark()というファンクションを、Dogのthis.dogというプロパティに代入している事です。
また、JavaScriptの無名関数という方法で以下の用に書くことも出来ます。
<html>
<body>
<script type="text/javascript">
function Dog(_name) {
this.name = _name;
this.bark = function () {
document.writeln(dog.name + " : わん!");
}
}
var dog = new Dog("ポチ");
dog.bark();
</script>
</body>
</html>
また、JavaScriptならではの特徴も有ります。
「メンバ(メソッド)の後付」です。下記コードを見てください。
<html>
<body>
<script type="text/javascript">
var obj = new Object();
obj.bark = function () {
document.writeln(" わん!");
}
obj.bark();
</script>
</body>
</html>
いかがでしょう?
クラス、メソッドという概念ではなく、あくまで、プロパティに関数を代入しているだけというのがおわかり頂けましたか?
では、もう一つ実験です。
今度はオブジェクトを2つ作ってみましょう。
きっと上手く行くはずです。
<html>
<body>
<script type="text/javascript">
var obj1 = new Object();
obj1.bark = function () {
document.writeln(" わん!");
}
var obj2 = new Object();
obj2.bark(); // ここでエラーになる
</script>
</body>
</html>
エラー!?
なんて事でしょう。
もう絶望です。JavaScriptではオブジェクト指向を実装出来ないのでしょうか?
prototypeという解法
いいえ、そんな事は有りません。 上記のコードでは、インスタンスであるobj1のプロパティに対してのみbarkメソッドを追加していました。 クラスであるObjectはこの時点では、変化が及んでいないのでObjectをインスタンス化したobj2には、barkプロパティが存在しないためエラーとなってしまいました。 ではどうすれば良いのでしょうか? JavaScriptにはprototypeという特殊なプロパティが有ります。
<html>
<body>
<script type="text/javascript">
Object.prototype.bark = function () {
document.writeln(" わん!");
}
var obj1 = new Object();
var obj2 = new Object();
obj1.bark();
obj2.bark();
</script>
</body>
</html>
いかがでしょう?
問題なく表示されましたね。
JavaScriptの決まりごとで、prototypeプロパティには、prototypeオブジェクトという特殊なオブジェクトが代入されます。
prototypeプロパティは、その関数をインスタンス化した場合のインスタンス参照(prototypeオブジェクト)が代入される事になっています。
つまり、prototypeプロパティが宣言された関数から生成されたインスタンス全てに影響が及びます。
現実的な事をいえば、Object関数は全てのオブジェクトの基底関数(クラス)ですので、下手にプロパティを追加すると、全てのインスタンスに影響が及んでしまいます。
あまり不用意に使うべきでは無いでしょう。
またスゴイUIライブラリ発見してしまった。
UIZEというUI用のjavascriptライブラリ。作者はChris van Rensburg。
Prototype.jsやscript.aculo.usの様な巨大ライブラリ群で45ファイルからなっている。
画像をwipe処理で切り替えられるUize.Wipeクラスや、スライダーインターフェース、カラーミキサーなどのインターフェースを提供するUize.Widgetクラス群。果には、テーブルのソート機能まで提供している。
特にwipeクラスがカッコ良すぎ!
是非サンプルを見てもらいたい。
最近のjsライブラリにしては珍しくPrototype.js非依存なようだ。
ちなみに、GPL(General Public License)















