使用 lodash 的 extend 方法扩展 JS 对象实例会导致奇怪的结果

Extending JS object instance using lodash's extend method causes weird results

我有一个具有一些默认属性的 JS class。我希望能够传入一个选项对象来覆盖默认值以及一些未默认的附加对象。我在构造函数中使用 lodash 的 extend 方法来完成此操作。

默认属性之一是数组。如果我实例化第一个实例并将一个新值推入数组,当我实例化第二个对象时,默认数组包含我推入第一个实例的对象。

以这段代码为例...

MyClass = (function() {
  var defaults = {
    myArr: [],
    myProp: 'I\'m a default value'
  };

  function MyClass(options) {
    // console.log(defaults)
    if(typeof options === 'object') {
      return _.extend(this, defaults, options);
    }
    else {
      return _.extend(this, defaults);
    }
  }

  return MyClass;
})();

var myObj = new MyClass({foo: 'bar'})
myObj.myArr.push('baz')
var mySecondObj = new MyClass();
console.log(mySecondObj) // MyClass {myArr: Array[1]}

我希望 mySecondObj.myArr 是一个空数组。

当我将一个值推入 myObj.myArr 时,defaults.myArr 以某种方式被更改并用于 class 的后续实例,这是怎么回事?

当您将 defaultsthis 的对象属性与 _.extend 合并时,您只是在创建对数组的引用。因此,当更新一个实例中的数组引用时,所有引用都会反映该更改。

您可以克隆该对象来解决这个问题:

_.cloneDeep(defaults)

DEMO

或者使用更简单的构造函数:

function MyClass(options) {
  var defaults = {
    myArr: [],
    myProp: 'I\'m a default value'
  }
  if (typeof options === 'object') {
    _.extend(this, defaults, options);
  } else {
    _.extend(this, defaults);
  }
}

DEMO

简单地说:您创建了一个 closure.

在你的自执行函数中你创建了另一个函数,你创建的函数继承 defaults 从它的父自执行函数,当你 return 函数到 MyClass任何对象创建都将对这些默认值 (the defaults object) 具有相同的引用,每次创建 class 时都不会创建 defaults 对象,这就是为什么它总是相同的。