Skip to content

Commit e6851e9

Browse files
committed
clean up observer.js
1 parent 010cda6 commit e6851e9

File tree

2 files changed

+108
-79
lines changed

2 files changed

+108
-79
lines changed

src/directives/repeat.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,6 @@ module.exports = {
142142

143143
// listen for collection mutation events
144144
// the collection has been augmented during Binding.set()
145-
if (!collection.__emitter__) Observer.watchArray(collection)
146145
collection.__emitter__.on('mutate', this.mutationListener)
147146

148147
// create new VMs and append to DOM
@@ -358,7 +357,7 @@ module.exports = {
358357
val = vm.$value || vm.$data
359358
if (action > 0) { // new property
360359
obj[key] = val
361-
Observer.convert(obj, key)
360+
Observer.convertKey(obj, key)
362361
} else {
363362
delete obj[key]
364363
}
@@ -398,6 +397,8 @@ function objectToArray (obj) {
398397
def(data, '$key', key)
399398
res.push(data)
400399
}
400+
Observer.convert(res)
401+
Observer.watch(res)
401402
return res
402403
}
403404

src/observer.js

Lines changed: 105 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -2,74 +2,92 @@
22

33
var Emitter = require('./emitter'),
44
utils = require('./utils'),
5-
65
// cache methods
76
typeOf = utils.typeOf,
87
def = utils.defProtected,
98
slice = [].slice,
10-
119
// types
1210
OBJECT = 'Object',
1311
ARRAY = 'Array',
14-
15-
// Array mutation methods to wrap
16-
methods = ['push','pop','shift','unshift','splice','sort','reverse'],
17-
1812
// fix for IE + __proto__ problem
1913
// define methods as inenumerable if __proto__ is present,
2014
// otherwise enumerable so we can loop through and manually
2115
// attach to array instances
2216
hasProto = ({}).__proto__,
23-
2417
// lazy load
2518
ViewModel
2619

20+
// Array Mutation Handlers & Augmentations ------------------------------------
21+
2722
// The proxy prototype to replace the __proto__ of
2823
// an observed array
2924
var ArrayProxy = Object.create(Array.prototype)
3025

26+
// intercept mutation methods
27+
;[
28+
'push',
29+
'pop',
30+
'shift',
31+
'unshift',
32+
'splice',
33+
'sort',
34+
'reverse'
35+
].forEach(watchMutation)
36+
37+
// Augment the ArrayProxy with convenience methods
38+
def(ArrayProxy, 'remove', removeElement, !hasProto)
39+
def(ArrayProxy, 'set', replaceElement, !hasProto)
40+
def(ArrayProxy, 'replace', replaceElement, !hasProto)
41+
3142
/**
32-
* Define mutation interceptors so we can emit the mutation info
43+
* Intercep a mutation event so we can emit the mutation info.
44+
* we also analyze what elements are added/removed and link/unlink
45+
* them with the parent Array.
3346
*/
34-
methods.forEach(function (method) {
47+
function watchMutation (method) {
3548
def(ArrayProxy, method, function () {
36-
var mutation = applyMutation(this, method, slice.call(arguments))
37-
linkArrayElements(this, mutation.inserted)
38-
unlinkArrayElements(this, mutation.removed)
39-
this.__emitter__.emit('mutate', null, this, mutation)
40-
return mutation.result
41-
}, !hasProto)
42-
})
4349

44-
/**
45-
* Mutate the Array and extract mutation info
46-
*/
47-
function applyMutation (arr, method, args) {
48-
var result = Array.prototype[method].apply(arr, args),
49-
mutation = {
50+
var args = slice.call(arguments),
51+
result = Array.prototype[method].apply(this, args),
52+
inserted, removed
53+
54+
// determine new / removed elements
55+
if (method === 'push' || method === 'unshift') {
56+
inserted = args
57+
} else if (method === 'pop' || method === 'shift') {
58+
removed = [result]
59+
} else if (method === 'splice') {
60+
inserted = args.slice(2)
61+
removed = result
62+
}
63+
// link & unlink
64+
linkArrayElements(this, inserted)
65+
unlinkArrayElements(this, removed)
66+
67+
// emit the mutation event
68+
this.__emitter__.emit('mutate', null, this, {
5069
method: method,
5170
args: args,
5271
result: result
53-
}
54-
if (method === 'push' || method === 'unshift') {
55-
mutation.inserted = args
56-
} else if (method === 'pop' || method === 'shift') {
57-
mutation.removed = [result]
58-
} else if (method === 'splice') {
59-
mutation.inserted = args.slice(2)
60-
mutation.removed = result
61-
}
62-
return mutation
72+
})
73+
74+
return result
75+
76+
}, !hasProto)
6377
}
6478

79+
/**
80+
* Link new elements to an Array, so when they change
81+
* and emit events, the owner Array can be notified.
82+
*/
6583
function linkArrayElements (arr, items) {
6684
if (items) {
6785
var i = items.length, item
6886
while (i--) {
6987
item = items[i]
70-
if (typeOf(item) === 'Object') {
88+
if (isWatchable(item)) {
7189
convert(item)
72-
watchObject(item)
90+
watch(item)
7391
if (!item.__ownerArrays__) {
7492
def(item, '__ownerArrays__', [])
7593
}
@@ -79,14 +97,17 @@ function linkArrayElements (arr, items) {
7997
}
8098
}
8199

100+
/**
101+
* Unlink removed elements from the ex-owner Array.
102+
*/
82103
function unlinkArrayElements (arr, items) {
83104
if (items) {
84105
var i = items.length, item
85106
while (i--) {
86107
item = items[i]
87108
if (typeOf(item) === 'Object') {
88109
var owners = item.__ownerArrays__
89-
owners.splice(owners.indexOf(arr))
110+
if (owners) owners.splice(owners.indexOf(arr))
90111
}
91112
}
92113
}
@@ -142,10 +163,48 @@ function replaceElement (index, data) {
142163
}
143164
}
144165

145-
// Augment the ArrayProxy with convenience methods
146-
def(ArrayProxy, 'remove', removeElement, !hasProto)
147-
def(ArrayProxy, 'set', replaceElement, !hasProto)
148-
def(ArrayProxy, 'replace', replaceElement, !hasProto)
166+
// Watch Helpers --------------------------------------------------------------
167+
168+
/**
169+
* Check if a value is watchable
170+
*/
171+
function isWatchable (obj) {
172+
ViewModel = ViewModel || require('./viewmodel')
173+
var type = typeOf(obj)
174+
return (type === OBJECT || type === ARRAY) && !(obj instanceof ViewModel)
175+
}
176+
177+
/**
178+
* Convert an Object/Array to give it a change emitter.
179+
*/
180+
function convert (obj) {
181+
if (obj.__emitter__) return false
182+
var emitter = new Emitter()
183+
def(obj, '__emitter__', emitter)
184+
emitter.on('set', function () {
185+
var owners = obj.__ownerArrays__, i
186+
if (owners) {
187+
i = owners.length
188+
while (i--) {
189+
owners[i].__emitter__.emit('set', '')
190+
}
191+
}
192+
})
193+
emitter.values = utils.hash()
194+
return true
195+
}
196+
197+
/**
198+
* Watch target based on its type
199+
*/
200+
function watch (obj) {
201+
var type = typeOf(obj)
202+
if (type === OBJECT) {
203+
watchObject(obj)
204+
} else if (type === ARRAY) {
205+
watchArray(obj)
206+
}
207+
}
149208

150209
/**
151210
* Watch an Object, recursive.
@@ -161,11 +220,6 @@ function watchObject (obj) {
161220
* and add augmentations by intercepting the prototype chain
162221
*/
163222
function watchArray (arr) {
164-
var emitter = arr.__emitter__
165-
if (!emitter) {
166-
emitter = new Emitter()
167-
def(arr, '__emitter__', emitter)
168-
}
169223
if (hasProto) {
170224
arr.__proto__ = ArrayProxy
171225
} else {
@@ -223,15 +277,6 @@ function convertKey (obj, key) {
223277
}
224278
}
225279

226-
/**
227-
* Check if a value is watchable
228-
*/
229-
function isWatchable (obj) {
230-
ViewModel = ViewModel || require('./viewmodel')
231-
var type = typeOf(obj)
232-
return (type === OBJECT || type === ARRAY) && !(obj instanceof ViewModel)
233-
}
234-
235280
/**
236281
* When a value that is already converted is
237282
* observed again by another observer, we can skip
@@ -303,22 +348,7 @@ function ensurePath (obj, key) {
303348
}
304349
}
305350

306-
function convert (obj) {
307-
if (obj.__emitter__) return false
308-
var emitter = new Emitter()
309-
def(obj, '__emitter__', emitter)
310-
emitter.on('set', function () {
311-
var owners = obj.__ownerArrays__
312-
if (owners) {
313-
var i = owners.length
314-
while (i--) {
315-
owners[i].__emitter__.emit('set', '')
316-
}
317-
}
318-
})
319-
emitter.values = utils.hash()
320-
return true
321-
}
351+
// Main API Methods -----------------------------------------------------------
322352

323353
/**
324354
* Observe an object with a given path,
@@ -374,12 +404,7 @@ function observe (obj, rawPath, observer) {
374404
// emit set events for everything inside
375405
emitSet(obj)
376406
} else {
377-
var type = typeOf(obj)
378-
if (type === OBJECT) {
379-
watchObject(obj)
380-
} else if (type === ARRAY) {
381-
watchArray(obj)
382-
}
407+
watch(obj)
383408
}
384409
}
385410

@@ -404,6 +429,8 @@ function unobserve (obj, path, observer) {
404429
observer.proxies[path] = null
405430
}
406431

432+
// Expose API -----------------------------------------------------------------
433+
407434
var pub = module.exports = {
408435

409436
// whether to emit get events
@@ -413,7 +440,8 @@ var pub = module.exports = {
413440
observe : observe,
414441
unobserve : unobserve,
415442
ensurePath : ensurePath,
416-
convertKey : convertKey,
417443
copyPaths : copyPaths,
418-
watchArray : watchArray
444+
watch : watch,
445+
convert : convert,
446+
convertKey : convertKey
419447
}

0 commit comments

Comments
 (0)