Skip to content

Commit 010cda6

Browse files
committed
array linking rough pass
1 parent 55ae8dc commit 010cda6

File tree

2 files changed

+85
-23
lines changed

2 files changed

+85
-23
lines changed

src/compiler.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -632,7 +632,7 @@ CompilerProto.defineProp = function (key, binding) {
632632
// if the data object is already observed, but the key
633633
// is not observed, we need to add it to the observed keys.
634634
if (ob && !(key in ob.values)) {
635-
Observer.convert(data, key)
635+
Observer.convertKey(data, key)
636636
}
637637

638638
binding.value = data[key]

src/observer.js

Lines changed: 84 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -28,19 +28,70 @@ var Emitter = require('./emitter'),
2828
// an observed array
2929
var ArrayProxy = Object.create(Array.prototype)
3030

31-
// Define mutation interceptors so we can emit the mutation info
31+
/**
32+
* Define mutation interceptors so we can emit the mutation info
33+
*/
3234
methods.forEach(function (method) {
3335
def(ArrayProxy, method, function () {
34-
var result = Array.prototype[method].apply(this, arguments)
35-
this.__emitter__.emit('mutate', null, this, {
36-
method: method,
37-
args: slice.call(arguments),
38-
result: result
39-
})
40-
return result
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
4141
}, !hasProto)
4242
})
4343

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+
method: method,
51+
args: args,
52+
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
63+
}
64+
65+
function linkArrayElements (arr, items) {
66+
if (items) {
67+
var i = items.length, item
68+
while (i--) {
69+
item = items[i]
70+
if (typeOf(item) === 'Object') {
71+
convert(item)
72+
watchObject(item)
73+
if (!item.__ownerArrays__) {
74+
def(item, '__ownerArrays__', [])
75+
}
76+
item.__ownerArrays__.push(arr)
77+
}
78+
}
79+
}
80+
}
81+
82+
function unlinkArrayElements (arr, items) {
83+
if (items) {
84+
var i = items.length, item
85+
while (i--) {
86+
item = items[i]
87+
if (typeOf(item) === 'Object') {
88+
var owners = item.__ownerArrays__
89+
owners.splice(owners.indexOf(arr))
90+
}
91+
}
92+
}
93+
}
94+
4495
/**
4596
* Convenience method to remove an element in an Array
4697
* This will be attached to observed Array instances
@@ -101,7 +152,7 @@ def(ArrayProxy, 'replace', replaceElement, !hasProto)
101152
*/
102153
function watchObject (obj) {
103154
for (var key in obj) {
104-
convert(obj, key)
155+
convertKey(obj, key)
105156
}
106157
}
107158

@@ -122,14 +173,15 @@ function watchArray (arr) {
122173
def(arr, key, ArrayProxy[key])
123174
}
124175
}
176+
linkArrayElements(arr, arr)
125177
}
126178

127179
/**
128180
* Define accessors for a property on an Object
129181
* so it emits get/set events.
130182
* Then watch the value itself.
131183
*/
132-
function convert (obj, key) {
184+
function convertKey (obj, key) {
133185
var keyPrefix = key.charAt(0)
134186
if (keyPrefix === '$' || keyPrefix === '_') {
135187
return
@@ -238,19 +290,36 @@ function ensurePath (obj, key) {
238290
sec = path[i]
239291
if (!obj[sec]) {
240292
obj[sec] = {}
241-
if (obj.__emitter__) convert(obj, sec)
293+
if (obj.__emitter__) convertKey(obj, sec)
242294
}
243295
obj = obj[sec]
244296
}
245297
if (typeOf(obj) === OBJECT) {
246298
sec = path[i]
247299
if (!(sec in obj)) {
248300
obj[sec] = undefined
249-
if (obj.__emitter__) convert(obj, sec)
301+
if (obj.__emitter__) convertKey(obj, sec)
250302
}
251303
}
252304
}
253305

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+
}
322+
254323
/**
255324
* Observe an object with a given path,
256325
* and proxy get/set/mutate events to the provided observer.
@@ -260,15 +329,8 @@ function observe (obj, rawPath, observer) {
260329
if (!isWatchable(obj)) return
261330

262331
var path = rawPath ? rawPath + '.' : '',
263-
alreadyConverted = !!obj.__emitter__,
264-
emitter
265-
266-
if (!alreadyConverted) {
267-
def(obj, '__emitter__', new Emitter())
268-
}
269-
270-
emitter = obj.__emitter__
271-
emitter.values = emitter.values || utils.hash()
332+
alreadyConverted = !convert(obj),
333+
emitter = obj.__emitter__
272334

273335
// setup proxy listeners on the parent observer.
274336
// we need to keep reference to them so that they
@@ -351,7 +413,7 @@ var pub = module.exports = {
351413
observe : observe,
352414
unobserve : unobserve,
353415
ensurePath : ensurePath,
354-
convert : convert,
416+
convertKey : convertKey,
355417
copyPaths : copyPaths,
356418
watchArray : watchArray
357419
}

0 commit comments

Comments
 (0)