@blog.justoneplanet.info

日々勉強

JavaScript Constructor and Prototype

■コンストラクタ関数

以下のようにするとコンストラクタ関数を定義できる。

function Person(name){
    this.name = name;
    this.introduce = function(){
        alert("My name is " + this.name + ".");
    }
}
var john = new Person('John');
var mike = new Person('Mike');
john.introduce();//My name is John.
mike.introduce();//My name is Mike.

慣例でコンストラクタ関数(クラス)は大文字で定義する。

■プロトタイプ

上述のコードは全てのインスタンスが同じintroduce()メソッドを保持するようになる。もし全てのインスタンスが同じintroduce()メソッドを参照すればメモリの節約につながる。そこで以下のようにprototypeを用いる。

function Person(name){
    this.name = name;
}
Person.prototype.introduce = function(){
    alert("My name is " + this.name + ".");
}
var john = new Person('John');
var mike = new Person('Mike');
john.introduce();//My name is John.
mike.introduce();//My name is Mike.

この場合、以下のように書くとメソッドが増えたときにスッキリする。

function Person(name){
    this.name = name;
}
Person.prototype = {
    introduce : function(){
        alert("My name is " + this.name + ".");
    }
}
var john = new Person('John');
var mike = new Person('Mike');
john.introduce();//My name is John.
mike.introduce();//My name is Mike.

参考

以下のようにしてArrayクラスを継承させることもできる。

var undefined;
function Stack(){
    this.T = -1;
}
Stack.prototype = Array.prototype;

■プロパティの性質

以下のようにコンストラクタ関数で記述したintroduceメソッドは、直属のプロパティ(メソッド)になる。

function Person(name){
    this.name = name;
    this.introduce = function(){
        alert("My name is " + this.name + ".");
    }
}
var john = new Person('John');
alert(john.hasOwnProperty('name'));//true
alert(john.hasOwnProperty('introduce'));//true
alert("introduce" in john);//true

一方で以下のようにprototypeを使用した場合、introduceメソッドはPersonから継承されたプロパティ(メソッド)ということになる。

function Person(name){
    this.name = name;
}
Person.prototype = {
    introduce : function(){
        alert("My name is " + this.name + ".");
    }
}
var john = new Person('John');
alert(john.hasOwnProperty('name'));//true
alert(john.hasOwnProperty('introduce'));//false
alert("introduce" in john);//true

■prototypeにおける読み込みと書き込み

読み込み

プロパティを読み込む場合、インスタンスはプロトタイプのプロパティを参照するので(継承している)、prototypeが使われる。

function Person(name){
    this.name = name;
}
Person.prototype = {
    introduce : function(){
        alert("My name is " + this.name + ".");
    }
}
var john = new Person('John');
var mike = new Person('Mike');
john.introduce();//My name is John.
mike.introduce();//My name is Mike.

インスタンスは自身にプロパティが見つからない場合、コンストラクタ関数のprototypeプロパティを参照する。コンストラクタ関数で見つからない場合は、さらにprototypeをたどる。

書き込み

プロパティを書き込む場合、JavaScriptはprototypeを使用しない。

function Person(name){
    this.name = name;
}
Person.prototype = {
    introduce : function(){
        alert("My name is " + this.name + ".");
    }
}
var john = new Person('John');
var mike = new Person('Mike');
john.introduce = function(){
    alert("I'm " + this.name + ".");
}
john.introduce();//I'm John.
mike.introduce();//My name is Mike.

■ネイティブコンストラクタ関数の拡張

通常は行ってはならないが、古いブラウザに対して標準に準拠したメソッドを付加するには便利である。

array.forEach(callback[, thisObj]);

配列の各要素を引数にして指定された関数を呼び出す。

if(!Array.prototype.forEach){
    Array.prototype.forEach = function(func/*, thisObj*/){
        var length = this.length >>> 0;
        if(typeof func !== 'function'){
            throw new TypeError();
        }
        var thisObj = arguments[1];
        for(var i = 0; i < length; i++){
            if(i in this){
                func.call(thisObj, this[i], i, this);
            }
        }
    }
}
サンプル
[1, 2, 3, 4].forEach(
	function(element, index, array){
		alert("[" + index + "] is " + element);
	}
);
/*
[0] is 1
[1] is 2
[2] is 3
[3] is 4
*/

array.map(callback[, thisObj]);

配列の各要素(を引数として)に対して関数を実行し、返された値を配列にまとめて返す。

if(!Array.prototype.map){
    Array.prototype.map = function(func/*, thisObj*/){
        var length = this.length >>> 0;
        if(typeof func !== 'function'){
            throw new TypeError();
        }
        res = new Array(length);
        var thisObj = arguments[1];
        for(var i = 0; i < length; i++){
            if(i in this){
                res[i] = func.call(thisObj, this[i], i, this);
            }
        }
        return res;
    }
}
サンプル
alert([1, 16, 81].map(Math.sqrt));//1,4,9

function.apply()

if(!Function.prototype.apply){
    Function.prototype.apply= function(object, parameters){
        var f = this;
        var o = object || window;
        var args = parameters || [];
        o._$_apply_$_ = f;
        var stringArgs = [];
        for(var i = 0; i < args.length; i++){
            stringArgs[i] = "args[" + i + "]";
        }
        var arglist = stringArgs.join(",");
        var methodcall = "o._$_apply_$_(" + arglist + ");";
        var result = eval(methodcall);
        delete o._$_apply_$_;
        return result;
    }
}

各メソッドについて

array.forEach(callback([element[, index[, array]]])[, thisObj]);
callback…各要素を引数として実行される関数。
thisObj…callbackが実行するされるときにthisとして扱われるオブジェクト。
array.map(callback[, thisObj]);
callback…各要素を引数として実行される関数。
thisObj…callbackが実行するされるときにthisとして扱われるオブジェクト。
var result = fun.apply(thisArg, [argsArray]);
thisArg…カレントオブジェクト。省略された場合はグローバルオブジェクト。
argsArray…関数に渡される引数の配列。

参考

コメントはまだありません»

No comments yet.

RSS feed for comments on this post.TrackBack URL

Leave a comment