github.com/insionng/yougam@v0.0.0-20170714101924-2bc18d833463/public/libs/vue-1.0.24/src/observer/index.js (about)

     1  import Dep from './dep'
     2  import { arrayMethods } from './array'
     3  import {
     4    def,
     5    isArray,
     6    isPlainObject,
     7    hasProto,
     8    hasOwn
     9  } from '../util/index'
    10  
    11  const arrayKeys = Object.getOwnPropertyNames(arrayMethods)
    12  
    13  /**
    14   * By default, when a reactive property is set, the new value is
    15   * also converted to become reactive. However in certain cases, e.g.
    16   * v-for scope alias and props, we don't want to force conversion
    17   * because the value may be a nested value under a frozen data structure.
    18   *
    19   * So whenever we want to set a reactive property without forcing
    20   * conversion on the new value, we wrap that call inside this function.
    21   */
    22  
    23  let shouldConvert = true
    24  export function withoutConversion (fn) {
    25    shouldConvert = false
    26    fn()
    27    shouldConvert = true
    28  }
    29  
    30  /**
    31   * Observer class that are attached to each observed
    32   * object. Once attached, the observer converts target
    33   * object's property keys into getter/setters that
    34   * collect dependencies and dispatches updates.
    35   *
    36   * @param {Array|Object} value
    37   * @constructor
    38   */
    39  
    40  export function Observer (value) {
    41    this.value = value
    42    this.dep = new Dep()
    43    def(value, '__ob__', this)
    44    if (isArray(value)) {
    45      var augment = hasProto
    46        ? protoAugment
    47        : copyAugment
    48      augment(value, arrayMethods, arrayKeys)
    49      this.observeArray(value)
    50    } else {
    51      this.walk(value)
    52    }
    53  }
    54  
    55  // Instance methods
    56  
    57  /**
    58   * Walk through each property and convert them into
    59   * getter/setters. This method should only be called when
    60   * value type is Object.
    61   *
    62   * @param {Object} obj
    63   */
    64  
    65  Observer.prototype.walk = function (obj) {
    66    var keys = Object.keys(obj)
    67    for (var i = 0, l = keys.length; i < l; i++) {
    68      this.convert(keys[i], obj[keys[i]])
    69    }
    70  }
    71  
    72  /**
    73   * Observe a list of Array items.
    74   *
    75   * @param {Array} items
    76   */
    77  
    78  Observer.prototype.observeArray = function (items) {
    79    for (var i = 0, l = items.length; i < l; i++) {
    80      observe(items[i])
    81    }
    82  }
    83  
    84  /**
    85   * Convert a property into getter/setter so we can emit
    86   * the events when the property is accessed/changed.
    87   *
    88   * @param {String} key
    89   * @param {*} val
    90   */
    91  
    92  Observer.prototype.convert = function (key, val) {
    93    defineReactive(this.value, key, val)
    94  }
    95  
    96  /**
    97   * Add an owner vm, so that when $set/$delete mutations
    98   * happen we can notify owner vms to proxy the keys and
    99   * digest the watchers. This is only called when the object
   100   * is observed as an instance's root $data.
   101   *
   102   * @param {Vue} vm
   103   */
   104  
   105  Observer.prototype.addVm = function (vm) {
   106    (this.vms || (this.vms = [])).push(vm)
   107  }
   108  
   109  /**
   110   * Remove an owner vm. This is called when the object is
   111   * swapped out as an instance's $data object.
   112   *
   113   * @param {Vue} vm
   114   */
   115  
   116  Observer.prototype.removeVm = function (vm) {
   117    this.vms.$remove(vm)
   118  }
   119  
   120  // helpers
   121  
   122  /**
   123   * Augment an target Object or Array by intercepting
   124   * the prototype chain using __proto__
   125   *
   126   * @param {Object|Array} target
   127   * @param {Object} src
   128   */
   129  
   130  function protoAugment (target, src) {
   131    /* eslint-disable no-proto */
   132    target.__proto__ = src
   133    /* eslint-enable no-proto */
   134  }
   135  
   136  /**
   137   * Augment an target Object or Array by defining
   138   * hidden properties.
   139   *
   140   * @param {Object|Array} target
   141   * @param {Object} proto
   142   */
   143  
   144  function copyAugment (target, src, keys) {
   145    for (var i = 0, l = keys.length; i < l; i++) {
   146      var key = keys[i]
   147      def(target, key, src[key])
   148    }
   149  }
   150  
   151  /**
   152   * Attempt to create an observer instance for a value,
   153   * returns the new observer if successfully observed,
   154   * or the existing observer if the value already has one.
   155   *
   156   * @param {*} value
   157   * @param {Vue} [vm]
   158   * @return {Observer|undefined}
   159   * @static
   160   */
   161  
   162  export function observe (value, vm) {
   163    if (!value || typeof value !== 'object') {
   164      return
   165    }
   166    var ob
   167    if (
   168      hasOwn(value, '__ob__') &&
   169      value.__ob__ instanceof Observer
   170    ) {
   171      ob = value.__ob__
   172    } else if (
   173      shouldConvert &&
   174      (isArray(value) || isPlainObject(value)) &&
   175      Object.isExtensible(value) &&
   176      !value._isVue
   177    ) {
   178      ob = new Observer(value)
   179    }
   180    if (ob && vm) {
   181      ob.addVm(vm)
   182    }
   183    return ob
   184  }
   185  
   186  /**
   187   * Define a reactive property on an Object.
   188   *
   189   * @param {Object} obj
   190   * @param {String} key
   191   * @param {*} val
   192   */
   193  
   194  export function defineReactive (obj, key, val) {
   195    var dep = new Dep()
   196  
   197    var property = Object.getOwnPropertyDescriptor(obj, key)
   198    if (property && property.configurable === false) {
   199      return
   200    }
   201  
   202    // cater for pre-defined getter/setters
   203    var getter = property && property.get
   204    var setter = property && property.set
   205  
   206    var childOb = observe(val)
   207    Object.defineProperty(obj, key, {
   208      enumerable: true,
   209      configurable: true,
   210      get: function reactiveGetter () {
   211        var value = getter ? getter.call(obj) : val
   212        if (Dep.target) {
   213          dep.depend()
   214          if (childOb) {
   215            childOb.dep.depend()
   216          }
   217          if (isArray(value)) {
   218            for (var e, i = 0, l = value.length; i < l; i++) {
   219              e = value[i]
   220              e && e.__ob__ && e.__ob__.dep.depend()
   221            }
   222          }
   223        }
   224        return value
   225      },
   226      set: function reactiveSetter (newVal) {
   227        var value = getter ? getter.call(obj) : val
   228        if (newVal === value) {
   229          return
   230        }
   231        if (setter) {
   232          setter.call(obj, newVal)
   233        } else {
   234          val = newVal
   235        }
   236        childOb = observe(newVal)
   237        dep.notify()
   238      }
   239    })
   240  }