JavaScript高級プログラムの設計拡張――動態原型について

4711 ワード

しかし、著者のNichollas C.Zakasは、「動的原型」方式でオブジェクトを作成する際に、問題と解決策を深く追究していません.継承するだけで「動原型」のボトルネックを説明しました.つまり、サブクラスを継承するときは、ダイナミックプロトタイプでは実現できません.
原文は大体次の通りです.
継承メカニズムがダイナミック化されない理由は、プロトタイプオブジェクトの一意性です.インスタンスコード:
 
  
function A (i) {
this.a = i;
if (typeof A._init == 'undefined') {
A.prototype.func = function () {
return 0;
}
A._init = 1;
}
}
function subA (j) {
A.call(this, 1);
this.j = j;
if (typeof subA._init == 'undefined') {
subA.prototype = new A();
subA.prototype.func_sub = function () {
return ++j;
}
subA._init = 1;
}
}
var sub_a = new subA(1);
alert(sub_a.func_sub()); //error: sub_a.func_sub is not a function
Nichollasは、コードが実行される前にオブジェクトがインスタンスされ、プロトタイプに連絡していると説明していますが、現在のプロトタイプオブジェクトの置換は、それに影響を与えません.すなわち、現在の置換はアクセスできません.未来のオブジェクトのインスタンスだけがこのような変化を反映します.したがって、最初のインスタンスオブジェクトは正しくありません.しかし、二つ目以降のサブクラスの例は大丈夫です.
解決方法は構造関数の外に新しいプロトタイプのオブジェクトを与えることです.
 
  
function A (i) {
this.a = i;
if (typeof A._init == 'undefined') {
A.prototype.func = function () {
return 0;
}
A._init = 1;
}
}
function subA (j) {
A.call(this, 1);
this.j = j;
if (typeof subA._init == 'undefined') {
subA.prototype.func_sub = function () {
return ++j;
}
subA._init = 1;
}
}
subA.prototype = new A();
var sub_a = new subA(1);
alert(sub_a.func_sub()); //2
残念ですが、これは私たちがどうしてダイナミックプロトタイプを使ったのかという初心に反しています.
動体の原型を使うのは本来、構造関数を「統一江山」にするためのもので、視覚的には原型の方法はこのような構造の一部だと思われます.
以上は「JavaScript高級プログラム設計」における動的原型継承の小節の大体の内容です.
しかし、Nichollasは先の章で対象構造の「動原型」方式で、これと同じ問題を取り上げるのを忘れたようです.上記の最後の例を見ます.
 
  
var Obj = function (name) {
this.name = name;
this.flag = new Array('A', 'B');
if (typeof Obj._init == 'undefined') {
Obj.prototype = {
showName : function () {
alert(this.name);
}
};
Obj._init = true;
}
}
var obj1 = new Obj('aa');
var obj2 = new Obj('bb');
obj1.showName(); //error: is not a function
obj2.showName(); // bb;
はい、この問題は実はサブクラスの継承において発生した問題と同じです.プロトタイプは現在の置換では対象に何の影響も与えません.未来の例でしか見られません.Nichollasに従って動的原型継承を処理する方式で言うならば、構造関数の外でprototypeオブジェクトを新たに与えるしかないということです.これは「コンストラクション/プロトタイプミックス」の方式になっているのではないですか?いわゆる「動態原型」方式は存在しない…
実は私達は考えられます.どうして「構造関数/原型混合」という副作用がほとんどない構築対象方式の後に「動的原型」という一節が書かれていますか?作者の意図は構造関数を視覚的に統一したいだけですか?視覚的に統一するだけで、動態的な原型は不要です.
 
  
var Obj = function () {
function __initialize (name) {
this.name = name;
this.flag = new Array('A', 'B');
}
__initialize.prototype = {
showName : function () {
alert(this.name);
},
showFlag : function () {
alert(this.flag);
}
}
return __initialize;
}();
var obj1 = new Obj('aa');
var obj2 = new Obj('bb');
obj1.showName(); // aa
obj2.showName(); // bb
実は上の方式は視覚の統一と言えます.Objのコンストラクター内は_u u u u uを通してinitializeで属性を初期化します.initialize.prototypeプロトタイプ初期化方法ちょっとだけ「カンニング」という感じです.initializeはObjの初期化を代理しました.
以下はtangoobiからの「構造類」のパッケージですが、考え方は上とほぼ一致しています.唯一の違いは彼が属性を原型の方式で作成したことです.同時に初期化属性と方法を構造関数パラメータのオブジェクトに投げました.便利なカスタマイズ:
 
  
/* == form tangoboy == */
window['$Class'] = {
// /
create: function(config) {
var obj = function(){},config = config||{};
//
obj = obj.prototype.constructor = config["__"]||obj;
delete config["__"];
obj.prototype = config;
return obj;
}
}
/* -- eg -- */
var man = $Class.create({
__ : function (name) {
this.name = name;
},
sex : 'male',
showName : function () {
alert(this.name);
}
});
var me = new man('ru');
me.showName(); //ru
視覚の統一を強要すれば、動原型を使わなくてもいいです.つまり上の考えを見てみると、私たちが最もよく使う「類構造」の方式に遡ります.
 
  
var Class = {
create : function () {
return function () {
this.initialize.apply(this, arguments);
}
}
}
上のコードを信じています.みんなはよく知らないかもしれません.詳しく調べていくと、上のコードと一致しています.initialize関数で初期化の代理を作って、視覚の統一を完成しました.