ソースを参照

Fix error when recursively traverse an object (#2686)

* fix 'Maximum call stack size exceeded' when recursively traverse an object

* remove semicolon

* simplify  assignment expression

* add unit test for circular references detected in Watcher#traverse
flytreeleft 10 年 前
コミット
e314db1af1
2 ファイル変更31 行追加3 行削除
  1. 14 3
      src/watcher.js
  2. 17 0
      test/unit/specs/watcher_spec.js

+ 14 - 3
src/watcher.js

@@ -334,14 +334,25 @@ Watcher.prototype.teardown = function () {
  * @param {*} val
  * @param {*} val
  */
  */
 
 
-function traverse (val) {
+function traverse (val, walkedObjs) {
   var i, keys
   var i, keys
+
+  walkedObjs = walkedObjs || {}
   if (isArray(val)) {
   if (isArray(val)) {
     i = val.length
     i = val.length
-    while (i--) traverse(val[i])
+    while (i--) traverse(val[i], walkedObjs)
   } else if (isObject(val)) {
   } 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)
     keys = Object.keys(val)
     i = keys.length
     i = keys.length
-    while (i--) traverse(val[keys[i]])
+    while (i--) traverse(val[keys[i]], walkedObjs)
   }
   }
 }
 }

+ 17 - 0
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) {
   it('fire change for prop addition/deletion in non-deep mode', function (done) {
     new Watcher(vm, 'b', spy)
     new Watcher(vm, 'b', spy)
     Vue.set(vm.b, 'e', 123)
     Vue.set(vm.b, 'e', 123)