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