• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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