【问题标题】:deep extend (like jQuery's) for nodeJSnodeJS 的深度扩展(如 jQuery)
【发布时间】:2012-03-13 01:12:49
【问题描述】:

我正在努力处理 nodeJS 中对象的深层副本。我自己的扩展是废话。下划线的延伸是平的。 stackexchange 上有相当简单的扩展变体,但没有一个甚至接近 jQuery.extend(true, {}, obj, obj, obj) .. (大多数实际上很糟糕,并且搞砸了 asnyc 代码的好处。)

因此,我的问题是:NodeJS 有没有好的深拷贝?有人移植 jQuery 的吗?

【问题讨论】:

  • 避免这样做。深拷贝很糟糕。喜欢浅拷贝。
  • 你能解释一下原因吗?对我来说,当浅拷贝流入一系列异步回调时,它们简直就是一场噩梦……
  • 另外——我们的数据库结构(mongoDB)有相当深的对象,我真的不想乱搞和转换结构......在代码和数据库中使用相同的对象非常方便...
  • 当然可以。只是不要深度复制它们。我使用来自 mongo 的对象,我从不深度复制它们:\
  • itsatony 我不同意 Raynos 的观点,您应该自行判断此行为是否适合您的用例。请注意存在陷阱并使用您的头脑。这是关于 Underscore 项目的深拷贝/扩展问题的辩论:github.com/documentcloud/underscore/issues/162

标签: jquery node.js extend deep-copy


【解决方案1】:

只需安装扩展。 文档: node extend package 安装:

npm install extend

那就尽情享受吧:

extend ( [deep], target, object1, [objectN] )

深度是可选的。默认为假。如果切换为 true,它将递归合并您的对象。

【讨论】:

    【解决方案2】:

    node.extend 做的很深,并且有熟悉的 jQuery 语法

    【讨论】:

      【解决方案3】:

      请使用内置的 util 模块:

      var extend = require('util')._extend;
      
      var merged = extend(obj1, obj2);
      

      【讨论】:

      • 这不是一个记录的方法,并带有一个下划线前缀,这通常意味着它不打算供公众使用。
      • 还有util._extend不深。
      • @CraigYounkins 这就是隐私约定在现实世界中不起作用的原因;)
      【解决方案4】:

      在 Node.js 中,您可以使用 Extendify 创建一个 _.extend 函数,该函数支持嵌套对象扩展(深度扩展)并且对其参数不可变(因此深度克隆)。

      _.extend = extendify({
          inPlace: false,
          isDeep: true
      });
      

      【讨论】:

        【解决方案5】:

        它已经被移植了。 node-extend

        请注意,该项目没有测试,也没有太多人气,因此使用风险自负。

        如前所述,您可能不需要深拷贝。尝试改变你的数据结构,这样你就只需要浅拷贝。

        几个月后

        我写了一个较小的模块,建议你使用xtend。它没有包含 jQuery 包的实现,也没有像 node-extend 这样的错误。

        【讨论】:

        • 对不起,你怎么能仅仅因为没有使用过深拷贝就说它们不好,在所有情况下都应该避免?
        • @itsatony xtend 仅通过设计进行浅扩展
        • 在尝试了我为node.extend 选择的几个模块之后,因为它使用原型正确地克隆了对象。 xtend 和 node-extend(带有“-”)都无法这样做。
        • @Raynos 你应该告诉你是你推广的图书馆的作者。
        • 即使你是 100% 公正的,你也必须像 here 所说的那样“披露你的隶属关系”
        【解决方案6】:

        我知道这是一个老问题,但我只是想将lodash's merge 作为一个很好的解决方案。一般来说,我建议将 lodash 用于实用功能:)

        【讨论】:

        • 我喜欢 lodash,但是 lodash 的 extend 会改变对象,这很糟糕。
        • 使用空对象作为mergeextend 的第一个参数可以轻松避免Lodash 的突变。 var obj3 = lodash.extend(obj1, obj2) 会变异 obj1 var obj3 = lodash.extend({}, obj1, obj2) 不会变异 obj1
        【解决方案7】:

        你也可以使用我的扩展插件版本 https://github.com/maxmara/dextend

        【讨论】:

          【解决方案8】:

          这适用于深度对象扩展...请注意,它会替换数组而不是它们的值,但这显然可以按照您的喜好进行更新。它应该保持枚举功能以及您可能希望它做的所有其他事情

          function extend(dest, from) {
              var props = Object.getOwnPropertyNames(from), destination;
          
              props.forEach(function (name) {
                  if (typeof from[name] === 'object') {
                      if (typeof dest[name] !== 'object') {
                          dest[name] = {}
                      }
                      extend(dest[name],from[name]);
                  } else {
                      destination = Object.getOwnPropertyDescriptor(from, name);
                      Object.defineProperty(dest, name, destination);
                  }
              });
          }
          

          【讨论】:

            【解决方案9】:

            对深拷贝的快速而肮脏的答案就是用一点 JSON 作弊。它不是性能最好的,但确实做得非常好。

            function clone(a) {
               return JSON.parse(JSON.stringify(a));
            }
            

            【讨论】:

            • 如果它只是一个面向数据的对象,那就太好了,但如果您的对象来自具有自己的方法和继承的特定构造函数,那么您不会想要这样做,因为这一切都会丢失。
            • @marksyzm 这绝对是真的;它仅对复制简单的值对象有用;它对于日期、函数以及在某些情况下构造的对象都失败了。
            • 不幸的是函数丢失了。这适用于除功能以外的所有内容
            • 不,它不能完美地用于除功能之外的所有功能。要直接引用您之前的评论:it's only useful for copying simple objects of values; it fails for dates, functions, and in some instances constructed objects.
            • 克隆不一定是扩展。扩展需要一个目标。
            【解决方案10】:

            锐化版本称为whet.extend

            我用 CoffeeScript 重写了node-extend 并添加了 travis-ci 测试套件,因为我自己需要在 Node 中进行深度应对,所以现在就在这里。

            是的,我认为在某些情况下使用深度合并是绝对正确的,例如我在配置工作中使用它,当我们需要将默认分支和用户分支合并在一起时。

            【讨论】:

              【解决方案11】:

              你想要 jQuery,所以就用它吧:

              function extend() {
                  var options, name, src, copy, copyIsArray, clone, target = arguments[0] || {},
                      i = 1,
                      length = arguments.length,
                      deep = false,
                      toString = Object.prototype.toString,
                      hasOwn = Object.prototype.hasOwnProperty,
                      push = Array.prototype.push,
                      slice = Array.prototype.slice,
                      trim = String.prototype.trim,
                      indexOf = Array.prototype.indexOf,
                      class2type = {
                        "[object Boolean]": "boolean",
                        "[object Number]": "number",
                        "[object String]": "string",
                        "[object Function]": "function",
                        "[object Array]": "array",
                        "[object Date]": "date",
                        "[object RegExp]": "regexp",
                        "[object Object]": "object"
                      },
                      jQuery = {
                        isFunction: function (obj) {
                          return jQuery.type(obj) === "function"
                        },
                        isArray: Array.isArray ||
                        function (obj) {
                          return jQuery.type(obj) === "array"
                        },
                        isWindow: function (obj) {
                          return obj != null && obj == obj.window
                        },
                        isNumeric: function (obj) {
                          return !isNaN(parseFloat(obj)) && isFinite(obj)
                        },
                        type: function (obj) {
                          return obj == null ? String(obj) : class2type[toString.call(obj)] || "object"
                        },
                        isPlainObject: function (obj) {
                          if (!obj || jQuery.type(obj) !== "object" || obj.nodeType) {
                            return false
                          }
                          try {
                            if (obj.constructor && !hasOwn.call(obj, "constructor") && !hasOwn.call(obj.constructor.prototype, "isPrototypeOf")) {
                              return false
                            }
                          } catch (e) {
                            return false
                          }
                          var key;
                          for (key in obj) {}
                          return key === undefined || hasOwn.call(obj, key)
                        }
                      };
                    if (typeof target === "boolean") {
                      deep = target;
                      target = arguments[1] || {};
                      i = 2;
                    }
                    if (typeof target !== "object" && !jQuery.isFunction(target)) {
                      target = {}
                    }
                    if (length === i) {
                      target = this;
                      --i;
                    }
                    for (i; i < length; i++) {
                      if ((options = arguments[i]) != null) {
                        for (name in options) {
                          src = target[name];
                          copy = options[name];
                          if (target === copy) {
                            continue
                          }
                          if (deep && copy && (jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)))) {
                            if (copyIsArray) {
                              copyIsArray = false;
                              clone = src && jQuery.isArray(src) ? src : []
                            } else {
                              clone = src && jQuery.isPlainObject(src) ? src : {};
                            }
                            // WARNING: RECURSION
                            target[name] = extend(deep, clone, copy);
                          } else if (copy !== undefined) {
                            target[name] = copy;
                          }
                        }
                      }
                    }
                    return target;
                  }
              

              还有一个小测试表明它可以进行深度复制

              extend(true, 
                  {
                      "name": "value"
                  }, 
                  {
                      "object": "value",
                      "other": "thing",
                      "inception": {
                          "deeper": "deeper",
                          "inception": {
                              "deeper": "deeper",
                              "inception": {
                                  "deeper": "deeper"
                              }
                          }
                      }
                  }
              )
              

              但记得注明出处:https://github.com/jquery/jquery/blob/master/src/core.js

              【讨论】:

              • 请注意,我没有引入“isPlainObject”、“isArray”或任何其他 jQuery 文件,因为我想指出您可以直接捕获它们的源代码并直接使用。
              • 酷,非常感谢!我曾试图让自己克服它,但我一定把它搞砸了。你的作品,我的没有:(
              • 像魅力一样工作!无法在 Google Appscript 中使用 jQuery,这对我帮助很大!
              猜你喜欢
              • 1970-01-01
              • 2013-05-23
              • 1970-01-01
              • 2014-02-07
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2012-11-06
              相关资源
              最近更新 更多