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 }