V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐关注
Meteor
JSLint - a JavaScript code quality tool
jsFiddle
D3.js
WebStorm
推荐书目
JavaScript 权威指南第 5 版
Closure: The Definitive Guide
dnxbf321
V2EX  ›  JavaScript

深度拷贝循环引用的问题

  •  
  •   dnxbf321 · 2017-02-21 14:13:19 +08:00 · 3440 次点击
    这是一个创建于 2836 天前的主题,其中的信息可能已经有所发展或是发生改变。

    对象 a 、 b 存在循环引用,现在要编写一个 copy 函数,能够正常拷贝 a

    var a = {}
    var b = {}
    a['b'] = b
    b['a'] = a
    
    console.log(copy(a))
    

    以下代码能够正常运行

    var store = [] // store 写在 copy 函数外
    
    function copy(data) {
      var isType = function(v, type) {
        return Object.prototype.toString.call(v).toLowerCase() === '[object ' + type + ']'
      }
    
      var checkInStore = function(checkV) {
        for (var i = 0, len = store.length; i < len; i++) {
          if (store[i] === checkV) {
            return true
          }
        }
        return false
      }
    
      var _copy = function(_data) {
        if (checkInStore(_data)) {
          return _data
        } else {
          store.push(_data)
          var ret = isType(_data, 'array') ? [] : {}
          for (var k in _data) {
            var v = _data[k]
            if (typeof v === 'object') {
              ret[k] = copy(v)
            } else {
              ret[k] = v
            }
          }
          return ret
        }
      }
    
      return _copy(data)
    }
    

    以下代码会抛错

    function copy(data) {
      var isType = function(v, type) {
        return Object.prototype.toString.call(v).toLowerCase() === '[object ' + type + ']'
      }
    
      var store = [] // store 写在函数里
      var checkInStore = function(checkV) {
        for (var i = 0, len = store.length; i < len; i++) {
          if (store[i] === checkV) {
            return true
          }
        }
        return false
      }
    
      var _copy = function(_data) {
        if (checkInStore(_data)) {
          return _data
        } else {
          store.push(_data)
          var ret = isType(_data, 'array') ? [] : {}
          for (var k in _data) {
            var v = _data[k]
            if (typeof v === 'object') {
              ret[k] = copy(v)
            } else {
              ret[k] = v
            }
          }
          return ret
        }
      }
    
      return _copy(data)
    }
    

    将 store 写在 copy 函数外、函数内会有不同的结果。这是为什么啊?半天想不出来,求大神指教,感觉是个作用域问题

    4 条回复    2017-02-21 19:09:10 +08:00
    xss
        1
    xss  
       2017-02-21 14:43:25 +08:00
    store 在外面, 位于全局作用域, 无论递归调用多少次 copy, 最终都会顺着原型链找到全局域上的 store.
    store 写在里面, 在递归调用 copy 的时候, 每个调用栈内部都有一个单独的 store 状态, 所以, 你确定每次的 store 的状态都是正确的?
    fyh1807008
        2
    fyh1807008  
       2017-02-21 14:44:09 +08:00
    递归循环调用?
    第二个函数每次 store 都被初始化为[],就没有临界值结束循环了
    fyh1807008
        3
    fyh1807008  
       2017-02-21 15:17:26 +08:00
    其实第二段代码只要把 `ret[k] = copy(v)` 改成 `ret[k] = _copy(v)` 就会形成一个闭包,始终保存住 store,isType, checkInStore
    dnxbf321
        4
    dnxbf321  
    OP
       2017-02-21 19:09:10 +08:00
    @xss 打断点的时候我也发现 store 每次都初始化过了。
    @fyh1807008 惊醒梦中人,手误,我也是想递归时调用 _copy 函数,误写

    谢谢
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1342 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 31ms · UTC 17:41 · PVG 01:41 · LAX 09:41 · JFK 12:41
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.