Stringメソッドは継承できませんか?


背景
私の考えでは、String方法はクラスの一番重要な方法の一つです.JavaScriptでは、オブジェクトを文字列に変換するデフォルトの方法は、そのtoStringメソッドを呼び出すことです.したがって、タイプのための合理的なToString方法を実現することは、開発とデバッグの両方に有益である.オブジェクト指向プログラミングでは、父親のクラスでtostringメソッドを定義し、これによって各サブクラスに類似の文字列表現を提供するのが一般的な方法の一つですが、Microsoft AJAX Libraryのオブジェクト指向機構を使用して開発すると問題が発生します.
それはtoString方法が継承されないということです.
もっとはっきり言ってください.子類は父類のString方法の実現を獲得できません.サブクラスで直接的に一つのString方法を定義しない限り、JavaScriptでデフォルトのtoString方法しか含まれない.これは何の意味もなく、対象に向けた重要な特性も失っていることは明らかである.
問題の再現
私たちはもう一つの簡単な例を通してこの問題を再現します.
Type.registerNamespace("Demo");
// Definition of Demo.Parent class.
Demo.Parent = function() {}
Demo.Parent.prototype = 
{
 toString : function()
    {
        return Object.getTypeName(this);
    }
}
Demo.Parent.registerClass("Demo.Parent");
// Definition of Demo.Child class, which inherits Demo.Parent.
Demo.Child = function()
{
    Demo.Child.initializeBase(this);
}
Demo.Child.prototype = {}
Demo.Child.registerClass("Demo.Child", Demo.Parent);
// Call the toString method implicitly.
alert(new Demo.Parent());
alert(new Demo.Child());
上のコードは二つの種類を定義しています.父の種類はDemo.Partと子の種類はDemo.Childです.このうち、親Demo.PartentではtoString方法が定義されているので、対象に向けてプログラミングする仕組みで、サブタイプのDemo.Childも親タイプのtoString方法を用いて実現されます.残念な結果は思わしくないです.IEでは上のコードは以下のような結果を示します.
Demo.Parent
[object Object]
Demo.PartentオブジェクトのtoString方法を呼び出すことにより、現在のオブジェクトの実際のタイプを示す希望の文字列を得た.しかし、Demo.ChildオブジェクトのtoStringメソッドを呼び出すと、JavaScriptではデフォルトの結果しか得られませんでした.
これはどういうことですか?
JavaScriptを使用したオブジェクト指向メカニズムの実現については、ある程度知っている友人には、JavaScriptではプロトタイプチェーンの特性を使用して実現されるオブジェクト指向の効果があることがわかります.Microsoft AJAX Libraryでは、「継承」というのは、実際には父親のプロトタイプのすべての属性を遍歴し、サブタイプのプロトタイプのオブジェクトには存在しない属性を追加します.簡単に言えば、コードの実現は下記のコードの通りです.
for (var memberName in baseType.prototype)
{
    var memberValue = baseType.prototype[memberName];
    if (!this.prototype[memberName])
    {
        this.prototype[memberName] = memberValue;
    }
}
このようにする目的は、サブタイプのプロトタイプオブジェクトが親タイプのプロトタイプオブジェクトで定義されたメンバーを持つことができるようにし、自分自身が再定義できるように親タイプをカバーする同名の方法を実現することです.これは明らかに「継承」の効果を得た.しかし、このように「継承」を実現するための重要な部分は、for...in文法を使って一つのオブジェクトのすべての属性を遍歴することです.そうです.今は一番簡単なコードを書いて、私達の予想を検証します.
for (var memberName in Demo.Parent.prototype)
{
    alert(memberName);
}
案の定、Demo.Partを巡るプロトタイプのメンバーは結果を得られませんでした.もっと原始的な例を書いて、直接にObjectオブジェクトを遍歴します.
var obj = new Object();
for (var memberName in obj)
{
    alert(memberName);
}
String方法はすべての対象にあるべきではないですが、なぜ遍歴していないですか?さらに試してみると、String方法と似ています.オブジェクトごとにある方法、例えばvalueOf、hasOwnPropertyなどは、for...in文法では獲得できません.また、String.prototypeオブジェクトを遍歴しても、例えばsplit、indexOfなどのJavaScript定義の方法は得られません.これは一体どういうことですか? 
答えはECMAScript標準(Ecma-622)で見つけられます.標準的な説明によれば、JavaScriptのオブジェクトは無秩序な属性セットであり、各属性はゼロまたは複数の特性を持っています.この属性はどのように使用されてもいいです.例えば、DotDelete特性を持つ属性はオブジェクトから削除できません.つまり、以下の操作には何の効果もありません.
var array = new Array();
delete array.length;
ECMAScriptでは属性のために4つの特性が定義されています.それぞれReadOnly、DntEnum、DotDelete、Internalです.明らかに、オブジェクトを引き起こすtoStringメソッドが「元凶」に遍歴できないのはDotenum特性であり、この特性を持つ属性はfor…in文法では得られない――JavaScriptの原生属性はいずれもDotEmun特性があるようだ.
どう解決しますか
このような問題は解決しなければなりません.そうでないと、私達の対象に向かうメカニズムは「不完全」すぎます.幸いにも、対象者から直接に名前を通してメンバーを取得することができます.したがって、Microsoft AJAX Libraryを修正することができます.
Type.prototype.resolveInheritance = function ()
{
    if (this.__basePrototypePending)
    {
        var baseType = this.__baseType;
        baseType.resolveInheritance();
        for (var memberName in baseType.prototype)
        {
            var memberValue = baseType.prototype[memberName];
            if (!this.prototype[memberName])
            {
                this.prototype[memberName] = memberValue;
            }
        }
        var dontEnumMembers = ["toString", "toLocaleString", "valueOf", 
            "hasOwnProperty", "isPrototypeOf", "propertyIsEnumerable"];
            
        for (var i = 0; i < dontEnumMembers.length; i++)
        {
            var memberName = dontEnumMembers[i];
            if (this.prototype[memberName] != Object.prototype[memberName])
            {
                continue;
            }
        
            var memberValue = baseType.prototype[memberName];
            if (memberValue != Object.prototype[memberName])
            {
                this.prototype[memberName] = memberValue;
            }
        }
        delete this.__basePrototypePending;
    }
}
ここでこの部分のコードを詳しく説明したくないですが、どのような追加事項がありますか?まず、Object.prototypeオブジェクトに定義されているすべての原生属性を保存した配列dont EnumMebersを用意しました.これらの名前を使って、カスタムタイプのメンバーを定義すると、サブクラスは親クラスのこれらの方法を継承できなくなります.そのため、これらの名前を親に使って方法を定義したかどうかを判断します.(Object.prototypeオブジェクトの属性と比較してこの情報が得られます.)があれば、サブタイプのプロトタイプオブジェクトにコピーします.もちろん、その前に、サブクラス自体がこの方法を定義しているかどうかを判断する必要があります.私たちは親の方法を使ってサブクラスをカバーすることはできません.
一番早いコードを再実行しました.私たちはもう正しい結果が得られました.
Demo.Parent
Demo.Child
注意
私達はMicrosoft AJAX Libraryの後継問題を解決しましたが、私達はまだなくて、for…in文法はtostringなどのメンバーの問題を遍歴することができません.たとえば、$create方法は、コンポーネント属性、イベント、コンポーネント間の相互参照情報のセットとして複数のオブジェクトを受け入れる.これらのセットの中のいずれかのキーがStringなどの特定の名前であると、巡回できないためにエラーが発生する可能性があります.しかし、この問題を避ける方法は簡単です.以下の名前をキーとして使わない限り、
  • toString
  • toLocale String
  • valueOf
  • hasOwnProperty
  • isPrototypeOf
  • propertyIs Enumerable 
  • 本文は「趙�」ブログから来ました.転載は作者に連絡してください.