Vue setter/getter 是何原理?

1 、 defineProperty 重定義對象javascript

JS原生es5版本提供對象從新定義的接口 defineProperty java

defineProperty 能夠修改對象的訪問器屬性,對象屬性值發生變化先後能夠觸發回調函數。react

對象的訪問器屬性包括 2 種類型:數據描述符、 存取描述符數組

 1.1 數據描述符
value:對象key的值,默認是 空字符串 ''
writeable:是否可寫,默認 true
configurable:true是否可配置,默認 true
enumerable:true是否可枚舉, 默認 true異步

Object.getOwnPropertyDescriptors(obj);
{
    k:{
      configurable: true,
      enumerable: true,
      value: 90,
      writable: true
    }
}

 

 1.2 存取描述符async

set:function(){}屬性訪問器 進行寫操做時調用該方法
get:function(){}屬性訪問器 進行讀操做時調用該方法
屬性描述符: configurable 、enumerable
configurable 、enumerable、 set 、 get函數

對象中新增key的value發生變化時會通過set和get方法ui

var obj = {};
var
temp = ''; Object.defineProperty(obj, 'name', { configurable: true, enumerable: true, get: function () { return temp; }, set: function (newValue) { temp = newValue } }); // 須要維護一個可訪問的變量 temp

或寫在 obj對象內,以下:this

 var obj = {
       tempValue: 'duyi',
       get name () {
            return this.tempValue;
       },
       set name (newValue) {
            this.tempValue = newValue;
       }
 };

 obj.name = 10;
 console.log( obj.name ); // 10

 小結一次:到這裏來基本上知道 getter setter是能夠實現的,基於這個簡單理論做出一個複雜邏輯。es5

 

2 、Observer 源碼

  /**
   * Observer類方法將對象修改成可被觀察。
   * 一旦應用了這個類方法, 對象的每個key會被轉換成 getter/setter
   * 用於收集依賴項和觸發更新。
   */
  var Observer = function Observer (value) {
    this.value = value;          // 保存被觀察的值到方法的實例屬性
    this.dep = new Dep();        // 創建一個Dep實例
    this.vmCount = 0;            // vmCount 記錄vm結構的個數
    def(value, '__ob__', this);  // value 對象 增長 ‘__ob__’ key,並賦值 this
    if (Array.isArray(value)) {  // value 對象若是是數組
      if (hasProto) {            // 表示在[]上可使用 __proto__ 
protoAugment(value, arrayMethods); // 將arrayMethods 添加到value的__proto__。arrayMethods 好像是重寫了數組的一部分原生方法,後面再看 } else { copyAugment(value, arrayMethods, arrayKeys); // 說不定他不支持 __proto__ ,調用def方法加強對象。 } this.observeArray(value); // 而後將這個加強後的數組,每一項都執行observe } else { this.walk(value); // walk 在Observer的原型上,對象轉換爲 getter setter。 } }; /** * 遍歷全部屬性將它們轉換爲 getter/setter,僅當值類型爲對象時調用。 */ Observer.prototype.walk = function walk (obj) { var keys = Object.keys(obj); for (var i = 0; i < keys.length; i++) { defineReactive$$1(obj, keys[i]); // defineReactive$$1 () 方法,這個方法纔是實現 getter / setter 的原方法!!! } }; /** * 觀察數組項列表 */ Observer.prototype.observeArray = function observeArray (items) { for (var i = 0, l = items.length; i < l; i++) { observe(items[i]); // observe 方法 } }; /** * 截獲原型鏈使用 __proto__ 的方式來 * 加強一個目標的對象或數組,簡稱原型加強。 */ function protoAugment (target, src) { /* eslint-disable no-proto */ target.__proto__ = src; /* eslint-enable no-proto */ } /** * 增長對象原型properties */ function copyAugment (target, src, keys) { for (var i = 0, l = keys.length; i < l; i++) { var key = keys[i]; def(target, key, src[key]); } } /**
* 在Observer方法的原型屬性 observerArray 上有用到!
* 大概意思是建立可觀察實例,返回可觀察實例或已有的可觀察對象。
*/ function observe (value, asRootData) {

// 若是不是object時,或是 VNode 的實例不進行 observe
if (!isObject(value) || value instanceof VNode) { return } var ob; if (hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) { ob = value.__ob__; } else if ( shouldObserve && !isServerRendering() && (Array.isArray(value) || isPlainObject(value)) && Object.isExtensible(value) && !value._isVue ) { ob = new Observer(value); } if (asRootData && ob) { ob.vmCount++; } return ob } /** * 這裏是實現getter setter的關鍵代碼
* 這裏是實現getter setter的關鍵代碼
* 這裏是實現getter setter的關鍵代碼
*
* 在對象上定義一個 有反應的原型
* 傳參:obj對象,key關鍵字,val值,customSetter在set的時候會被執行(第4個參數),shallow 默認爲undefined,爲 true 時不執行 observe 函數。
*/
  function defineReactive$$1 (
    obj,
    key,
    val,
    customSetter,
    shallow
  ) {
    var dep = new Dep();

    var property = Object.getOwnPropertyDescriptor(obj, key);
    if (property && property.configurable === false) {
      return
    }

    // 預約義的getter和setter
    var getter = property && property.get;
    var setter = property && property.set;
    if ((!getter || setter) && arguments.length === 2) {
      val = obj[key];
    }

    var childOb = !shallow && observe(val);
    Object.defineProperty(obj, key, {
      enumerable: true,
      configurable: true,
      get: function reactiveGetter () {
        var value = getter ? getter.call(obj) : val;
        if (Dep.target) {
          dep.depend();
          if (childOb) {
            childOb.dep.depend();
            if (Array.isArray(value)) {
              dependArray(value);
            }
          }
        }
        return value
      },
      set: function reactiveSetter (newVal) {
        var value = getter ? getter.call(obj) : val;
        /* eslint-disable no-self-compare */
        if (newVal === value || (newVal !== newVal && value !== value)) {
          return
        }
        /* eslint-enable no-self-compare */
        if (customSetter) {
          customSetter();
        }
        // #7981: for accessor properties without setter
        if (getter && !setter) { return }
        if (setter) {
          setter.call(obj, newVal);
        } else {
          val = newVal;
        }
        childOb = !shallow && observe(newVal);
        dep.notify();
      }
    });
  }

 

 訂閱observe對象的變化,通知觸發update,參考訂閱-發佈模式。

  /**
   * Dep 是能夠有多個指令訂閱的可觀察對象,目的就是對一個目標深層處理
   */
  
  var uid = 0;
  var Dep = function Dep () {
    this.id = uid++;    //  添加了2個實例屬性,id 用於排序和 subs 數組統計sub
    this.subs = [];
  };
  
  // 在 subs中添加 sub
  Dep.prototype.addSub = function addSub (sub) {
    this.subs.push(sub);
  };
  
  // 從 subs中移除 sub
  Dep.prototype.removeSub = function removeSub (sub) {
    remove(this.subs, sub);
  };


  Dep.prototype.depend = function depend () {
    if (Dep.target) {
      Dep.target.addDep(this);  // addDep 是 Watcher 的原型方法,用於指令增長依賴
    }
  };

  Dep.prototype.notify = function notify () {
    // 穩定訂閱列表
    var subs = this.subs.slice();
    if (!config.async) {
      // 若是不是在異步運行,在程序調度中 subs 不能夠被排序!
      // 而後排序以確保正確的順序。
      subs.sort(function (a, b) { return a.id - b.id; });
    }
    for (var i = 0, l = subs.length; i < l; i++) {
      // 而後順序觸發 Watcher 原型的 update 方法
      subs[i].update();
    }
  };

  // 當前目標程序被評估,這個評估全局惟一,一次只要一個觀察者能夠被評估
  Dep.target = null;
  var targetStack = [];

  // 將目標程序推送到目標棧
  function pushTarget (target) {
    targetStack.push(target);
    Dep.target = target;
  }
  // 執行出棧先去掉
  function popTarget () {
    targetStack.pop();
    Dep.target = targetStack[targetStack.length - 1];
  }

 

 

 全局def方法,定義對象。

  // def 方法比較簡單
  /**
   * 定義一個原型
   * obj 
   * key 對象的key關鍵字
   * val  對象的value值
   * 是否可枚舉,默承認寫可配置
   */

  function def (obj, key, val, enumerable) {
    Object.defineProperty(obj, key, {
      value: val,
      enumerable: !!enumerable,
      writable: true,
      configurable: true
    });
  }
相關文章
相關標籤/搜索