index.js 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205
  1. var _ = require('../util')
  2. var config = require('../config')
  3. var Dep = require('./dep')
  4. var arrayMethods = require('./array')
  5. var arrayKeys = Object.getOwnPropertyNames(arrayMethods)
  6. require('./object')
  7. /**
  8. * Observer class that are attached to each observed
  9. * object. Once attached, the observer converts target
  10. * object's property keys into getter/setters that
  11. * collect dependencies and dispatches updates.
  12. *
  13. * @param {Array|Object} value
  14. * @constructor
  15. */
  16. function Observer (value) {
  17. this.value = value
  18. this.dep = new Dep()
  19. _.define(value, '__ob__', this)
  20. if (_.isArray(value)) {
  21. var augment = config.proto && _.hasProto
  22. ? protoAugment
  23. : copyAugment
  24. augment(value, arrayMethods, arrayKeys)
  25. this.observeArray(value)
  26. } else if (_.isPlainObject(value)) {
  27. this.walk(value)
  28. }
  29. }
  30. // Static methods
  31. /**
  32. * Attempt to create an observer instance for a value,
  33. * returns the new observer if successfully observed,
  34. * or the existing observer if the value already has one.
  35. *
  36. * @param {*} value
  37. * @param {Vue} [vm]
  38. * @return {Observer|undefined}
  39. * @static
  40. */
  41. Observer.create = function (value, vm) {
  42. var ob
  43. if (
  44. value &&
  45. value.hasOwnProperty('__ob__') &&
  46. value.__ob__ instanceof Observer
  47. ) {
  48. ob = value.__ob__
  49. } else if (
  50. _.isObject(value) &&
  51. !Object.isFrozen(value) &&
  52. !value._isVue
  53. ) {
  54. ob = new Observer(value)
  55. }
  56. if (ob && vm) {
  57. ob.addVm(vm)
  58. }
  59. return ob
  60. }
  61. // Instance methods
  62. /**
  63. * Walk through each property and convert them into
  64. * getter/setters. This method should only be called when
  65. * value type is Object. Properties prefixed with `$` or `_`
  66. * and accessor properties are ignored.
  67. *
  68. * @param {Object} obj
  69. */
  70. Observer.prototype.walk = function (obj) {
  71. var keys = Object.keys(obj)
  72. var i = keys.length
  73. while (i--) {
  74. this.convert(keys[i], obj[keys[i]])
  75. }
  76. }
  77. /**
  78. * Try to carete an observer for a child value,
  79. * and if value is array, link dep to the array.
  80. *
  81. * @param {*} val
  82. * @return {Dep|undefined}
  83. */
  84. Observer.prototype.observe = function (val) {
  85. return Observer.create(val)
  86. }
  87. /**
  88. * Observe a list of Array items.
  89. *
  90. * @param {Array} items
  91. */
  92. Observer.prototype.observeArray = function (items) {
  93. var i = items.length
  94. while (i--) {
  95. this.observe(items[i])
  96. }
  97. }
  98. /**
  99. * Convert a property into getter/setter so we can emit
  100. * the events when the property is accessed/changed.
  101. *
  102. * @param {String} key
  103. * @param {*} val
  104. */
  105. Observer.prototype.convert = function (key, val) {
  106. var ob = this
  107. var childOb = ob.observe(val)
  108. var dep = new Dep()
  109. Object.defineProperty(ob.value, key, {
  110. enumerable: true,
  111. configurable: true,
  112. get: function () {
  113. if (Dep.target) {
  114. dep.depend()
  115. if (childOb) {
  116. childOb.dep.depend()
  117. }
  118. if (_.isArray(val)) {
  119. for (var e, i = 0, l = val.length; i < l; i++) {
  120. e = val[i]
  121. e && e.__ob__ && e.__ob__.dep.depend()
  122. }
  123. }
  124. }
  125. return val
  126. },
  127. set: function (newVal) {
  128. if (newVal === val) return
  129. val = newVal
  130. childOb = ob.observe(newVal)
  131. dep.notify()
  132. }
  133. })
  134. }
  135. /**
  136. * Add an owner vm, so that when $add/$delete mutations
  137. * happen we can notify owner vms to proxy the keys and
  138. * digest the watchers. This is only called when the object
  139. * is observed as an instance's root $data.
  140. *
  141. * @param {Vue} vm
  142. */
  143. Observer.prototype.addVm = function (vm) {
  144. (this.vms || (this.vms = [])).push(vm)
  145. }
  146. /**
  147. * Remove an owner vm. This is called when the object is
  148. * swapped out as an instance's $data object.
  149. *
  150. * @param {Vue} vm
  151. */
  152. Observer.prototype.removeVm = function (vm) {
  153. this.vms.$remove(vm)
  154. }
  155. // helpers
  156. /**
  157. * Augment an target Object or Array by intercepting
  158. * the prototype chain using __proto__
  159. *
  160. * @param {Object|Array} target
  161. * @param {Object} proto
  162. */
  163. function protoAugment (target, src) {
  164. target.__proto__ = src
  165. }
  166. /**
  167. * Augment an target Object or Array by defining
  168. * hidden properties.
  169. *
  170. * @param {Object|Array} target
  171. * @param {Object} proto
  172. */
  173. function copyAugment (target, src, keys) {
  174. var i = keys.length
  175. var key
  176. while (i--) {
  177. key = keys[i]
  178. _.define(target, key, src[key])
  179. }
  180. }
  181. module.exports = Observer