From 44ff198636522b2733a314f9bf9c17862732dff7 Mon Sep 17 00:00:00 2001 From: flytreeleft Date: Thu, 24 Mar 2016 20:50:42 +0800 Subject: [PATCH 1/4] fix 'Maximum call stack size exceeded' when recursively traverse an object --- src/watcher.js | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/src/watcher.js b/src/watcher.js index 576bf472b2e..0ee744802ac 100644 --- a/src/watcher.js +++ b/src/watcher.js @@ -331,14 +331,25 @@ Watcher.prototype.teardown = function () { * @param {*} val */ -function traverse (val) { +function traverse (val, walkedObjs) { var i, keys + + !walkedObjs && (walkedObjs = {}) if (isArray(val)) { i = val.length - while (i--) traverse(val[i]) + while (i--) traverse(val[i], walkedObjs) } else if (isObject(val)) { + if (val.__ob__) { + var depId = val.__ob__.dep.id; + if (walkedObjs[depId]) { + return + } else { + walkedObjs[depId] = true + } + } + keys = Object.keys(val) i = keys.length - while (i--) traverse(val[keys[i]]) + while (i--) traverse(val[keys[i]], walkedObjs) } } From 2b644e13feaf26948dc08d0a7d824c76bcd9d3ad Mon Sep 17 00:00:00 2001 From: flytreeleft Date: Sat, 16 Apr 2016 09:32:19 +0800 Subject: [PATCH 2/4] remove semicolon --- src/watcher.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/watcher.js b/src/watcher.js index 0ee744802ac..d9f8bc60a13 100644 --- a/src/watcher.js +++ b/src/watcher.js @@ -340,7 +340,7 @@ function traverse (val, walkedObjs) { while (i--) traverse(val[i], walkedObjs) } else if (isObject(val)) { if (val.__ob__) { - var depId = val.__ob__.dep.id; + var depId = val.__ob__.dep.id if (walkedObjs[depId]) { return } else { From 50412c8d6a281792890ef0dd11c43ff56c449236 Mon Sep 17 00:00:00 2001 From: flytreeleft Date: Sat, 16 Apr 2016 19:03:24 +0800 Subject: [PATCH 3/4] simplify assignment expression --- src/watcher.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/watcher.js b/src/watcher.js index d9f8bc60a13..c72919cde61 100644 --- a/src/watcher.js +++ b/src/watcher.js @@ -334,7 +334,7 @@ Watcher.prototype.teardown = function () { function traverse (val, walkedObjs) { var i, keys - !walkedObjs && (walkedObjs = {}) + walkedObjs = walkedObjs || {} if (isArray(val)) { i = val.length while (i--) traverse(val[i], walkedObjs) From 07684e6f1fef6b1ce09aa611ca7b74d3729e5eeb Mon Sep 17 00:00:00 2001 From: flytreeleft Date: Thu, 14 Apr 2016 18:07:59 +0800 Subject: [PATCH 4/4] add unit test for circular references detected in Watcher#traverse --- test/unit/specs/watcher_spec.js | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/test/unit/specs/watcher_spec.js b/test/unit/specs/watcher_spec.js index 5e9c1a601ed..2450417ce90 100644 --- a/test/unit/specs/watcher_spec.js +++ b/test/unit/specs/watcher_spec.js @@ -286,6 +286,23 @@ describe('Watcher', function () { }) }) + it('deep watch with circular references', function (done) { + new Watcher(vm, 'b', spy, { + deep: true + }) + Vue.set(vm.b, '_', vm.b) + nextTick(function () { + expect(spy).toHaveBeenCalledWith(vm.b, vm.b) + expect(spy.calls.count()).toBe(1) + vm.b._.c = 1 + nextTick(function () { + expect(spy).toHaveBeenCalledWith(vm.b, vm.b) + expect(spy.calls.count()).toBe(2) + done() + }) + }) + }) + it('fire change for prop addition/deletion in non-deep mode', function (done) { new Watcher(vm, 'b', spy) Vue.set(vm.b, 'e', 123)