Vue双方向バインド原理(二)アクセサ属性defineProperty()とパブリッシュ/サブスクリプションモード


アクセサのプロパティの説明
参考資料:『JavaScriptプレミアムプログラミング』(第3版)第6章
jsのオブジェクトには、データ属性とアクセサ属性の2つの属性があります.
1.データ属性
データ・プロパティには、データ値の場所が含まれます.この位置では値の読み取りと書き込みができます.データ・プロパティは、最も一般的なオブジェクト・プロパティです.データ・プロパティには、4つの他の動作を記述するプロパティがあります.
  • Configurable:deleteで属性を削除して属性を再定義できますか.デフォルトはtrue
  • Enumerable:for-inで遍歴できるかどうか、すなわち列挙できるかどうか.デフォルトはtrue
  • Writable:属性の値を変更できるかどうか.デフォルトはtrue
  • Value:この属性を含むデータ値は、属性を読み書きするときに実はここで読み書きします.デフォルトはundefined
  • プロパティの上記4つのデフォルトプロパティを変更するには、ECMAScriptのObjectを使用する必要があります.defineProperty()メソッド.このメソッドには、3つのパラメータが含まれます.プロパティが縮小されたオブジェクト、プロパティ名、記述子オブジェクトです.ディスクリプタオブジェクトのプロパティは、上記の4つのプロパティにある必要があります.例:
    var person = {};
    Object.defineProperty(person,"name",{
        writable: false,
        value: "Nicholas"
    });
    
    alert(person.name);   // "Nicholas"
    person.name = "Tom";
    alert(person.name);  // "Nicholas"

    前の例では、書き込み不可能なnameプロパティを作成し、値を割り当てました.だから修正できません.
    注意、コンフィグケーブル属性をfalseに設定すると、trueに戻すことはできません.このとき、特性を変更しようとすると、エラーが発生します.
    2.アクセサのプロパティ
    アクセサ属性にはデータ値は含まれません.getter関数とsetter関数のペアが含まれています(必須ではありません).アクセサ属性の値を読み書きすると、対応するgetterとsetter関数が呼び出されますが、私たちのvueはgetterとsetter関数に必要な操作を追加します.
    アクセサのプロパティには、次の4つの機能があります.
  • Configurable:deleteで属性を削除して属性を再定義できますか.デフォルトtrue
  • Enumerable:for-inで遍歴できるかどうか、すなわち列挙できるかどうか.デフォルトtrue
  • get:プロパティを読み込むときに呼び出される関数、デフォルトundefined
  • set:属性を書き込むときに呼び出される関数、デフォルトundefined
  • var book = {
        _year: 2004,
        edition: 1
    };
    Object.defineProperty(book,"year",{
        get: function(){
            return this._year;
        },
        set: function(newValue){
            if(newValue>2004){
                this.year= newValue;
                this.edition += new Value-2004;
            }
        }
    });
    
    book.year = 2005;
    alert(book.edition); //2

    前の例ではbookオブジェクトを作成し、2つのデフォルト属性を定義しました.yearとedition.yearの前の下線は、オブジェクトメソッドでのみアクセスできるプロパティを示します.アクセサプロパティyearにはgetter関数とsetter関数が含まれます.yearプロパティを変更すると、_yearとeditionが変わりました.これが、アクセサのプロパティの一般的な方法です.つまり、プロパティ値を設定すると、他のプロパティが変化します.
    もちろん、必ずしもgetterとsetterを同時に作成する必要はありません.getterだけでは書けません.setterだけでは読めません.
    パブリッシュ/サブスクリプションモードの説明
    パブリケーション/サブスクリプションモードは設計モードの1つであり、多くの場所で彼と観察者モードを混同しているが、実際にはいくつかの違いがある.デザインモデルについては、実は今の私の理解も非常に限られていますが、このモデルの原理を明らかにしたいだけであれば、実は簡単です.
    簡単に言えば、パブリケーション/サブスクリプションモードは彼の字面の意味です.彼は1対多の関係を定義し、複数のサブスクリプションが同時にパブリケーションに注目させ、パブリケーションの状態が変化すると、すべてのサブスクリプションのオブジェクトに通知します.例えば、B駅で番組を追いかけて、購読を注文した後、その番組の購読者になって、アニメーションが更新されると、ニュースを発表して、すべての購読者に知らせて、それからあなたは新しい1話に行くことができます.
    Vueでの役割
    Vueはインスタンスのdataプロパティを巡回し、各dataをアクセサに設定し、そのプロパティのgetter関数でwatcherに設定し、setterで他のwatcherに変更メッセージを発行します.このように、パブリケーション/サブスクリプションモードに合わせて、そのうちの1つの値を変更すると、メッセージがパブリッシュされ、すべてのwatcherが自分を更新します.これらのwatcherはdomにバインドされた表示情報、例えばv-text="year"や{{year}}などのノードです.ブラウズdomを変更し、ブラウザでリアルタイムに変更する効果を達成します.コードは以下の通りです.
       //       data     ,     Vue        
        function observe(obj,vm){
            Object.keys(obj).forEach(function(key){
                defineReactive(vm,key,obj[key]);
            });
        }
        //        ,   getter setter   ,        。    。
        function defineReactive(obj,key,val){
            //        (  /  )  ,            ,              ,                        ,               。
            //         ,           
            var dep = new Dep();
            // data          Vue        ,    data   
            //      Vue.data   ,       get set  。     v-model input  ,    input  ,      Vue.data   ,       set  
            Object.defineProperty(obj,key,{
                get: function(){
                    //Dep.target    watcher,     watcher     Dep
                    if(Dep.target){
                        dep.addSub(Dep.target);
                    }
                    return val;
                },
                set: function(newVal){
                    if(newVal === val){
                        return
                    }
                    val = newVal;
                    //console.log(val);
                    //        watchers    
                    dep.notify();
                }
            });
        }
    
        //    Dep    
        function Dep(){
            this.subs = [];
        }
        //Dep     ,              
        Dep.prototype = {
            addSub: function(sub){
                this.subs.push(sub);
            },
            notify: function(){
                this.subs.forEach(function(sub){
                    sub.update();
                });
            }
        }

    Object.definePropertyはES 5のみでサポートされており、shimできない特性です.これは、VueがIE 8以降のブラウザをサポートしていない理由です.
    documentFragment、defineProperty、パブリケーション/サブスクリプションモードを組み合わせると、双方向バインドを簡単に実現できます.もちろん、これは最も基本的な考え方ですが、実際にはvueは複雑に処理されます.