【问题标题】:Object.create isn't creating a new object using the module patternObject.create 没有使用模块模式创建新对象
【发布时间】:2013-04-21 02:06:09
【问题描述】:

如果我在对象文字上使用 Object.create() 创建多个对象,我会得到多个不共享属性值的唯一对象。但是,当我在从模块返回的对象上使用 Object.create() 时,看起来它们共享相同的引用?这是为什么?

#1 模块:

var objModule = (function () {
  var name = "module 1";
  var setName = function( strName ) {
    name = strName;
  };
  var getName = function() {
    return name;
  };

  return {
    setName: setName,
    getName: getName
  };
}());

var objModule2 = Object.create(objModule);
objModule2.setName("module 2");

console.log(objModule.getName()); // WRONG prints "module 2"
console.log(objModule2.getName()); // prints "module 2"

#2 字面量:

var objLiteral = {
  name : "literal 1"
};
var objLiteral2 = Object.create(objLiteral);
objLiteral2.name = "literal 2";

console.log(objLiteral.name); // prints "literal 1"
console.log(objLiteral2.name); // prints "literal 2"

编辑

#3 模块“构造函数”:

var module = function () {
  var name = "module 1";
  var setName = function( strName ) {
    name = strName;
  };
  var getName = function() {
    return name;
  };

  return {
    setName: setName,
    getName: getName
  };
};

var objModule1 = module();
var objModule2 = module();
objModule2.setName("module 2");

console.log(objModule1.getName()); // prints "module 1"
console.log(objModule2.getName()); // prints "module 2"

编辑

如果我像构造函数一样使用模块(如@Matt Browne 建议的那样)并创建 2 个对象,结果就像使用对象字面量一样。我想了解的是为什么模块示例 #1 的行为与模块示例 #3 不同?

编辑 2

正如@ben336 解释的,代码:

var objModule2 = Object.create(objModule); 

将 objModule2 的原型设置为 objModule。这在示例 #3 中不会发生,因此这两个对象不共享相同的闭包属性。

【问题讨论】:

    标签: javascript


    【解决方案1】:

    示例 1

    Object.create 的第一个参数指定正在创建的新对象的原型对象。由于在您的第一个示例中,您将现有对象设置为新对象的原型,因此当您调用函数时,它们会修改您存储在闭包中的变量,然后您的现有对象和新的。

    这里要理解的两个关键点是:

    1. 这段代码

      var objModule = function () {
        var name = "module 1";
        var setName = function( strName ) {
          name = strName;
        };
        var getName = function() {
          return name;
        };
      
        return {
          setName: setName,
          getName: getName
        };
      }();
      

      使用 getName 和 setName 函数创建一个闭包,该函数持有对 name 变量的访问权限。

    2. 那些函数是 objModule 的属性,当你调用 Object.create 时,你将 objModule 设置为 objModule2 的原型,它也可以访问这些函数。

    由于 2 个对象共享这些函数,并且它们可以访问模块创建的闭包,而不是将 name 属性本地存储在任一对象上,因此当您使用一个对象调用 set 函数时,它将更新闭包,因此更新两个对象。

    示例 2

    在第二个示例中,您还将对象设置为新对象的原型,但您在本地对象上声明了一个属性,该属性将覆盖原型上的属性。

    【讨论】:

    • 因此,当 objModule2 调用 setName("module 2") 时,它会调用其原型上的方法(objModule2 将使用原型方法,除非它通过创建自己的 setName() 来“覆盖”/隐藏它方法)哪个是 objModule 的方法,并且该方法可以访问闭包的 setName() 方法?如果那是正确的,那我明白了。
    • 那个方法是闭包的setname函数。否则完全正确
    • 我错误地将模块的 setName 调用为可以访问闭包的 setName 的方法。更准确地说,它是一个指向闭包方法的指针。如果我稍后在代码中将模块的 setName 分配给其他东西,我可以破坏该指针。作为一个实验,我将 setName 分配给另一个函数,该函数试图访问闭包属性,但由于没有指向该属性的指针引用而无法访问。
    【解决方案2】:

    objModule2.setNameobjModule.setName 指代相同的功能,但具有不同的this。 (Object.create 不会复制任何内容)
    调用它会设置相同的局部变量。

    objLiteral2.name = "literal 2"objLiteral2 上创建一个新属性,它会隐藏继承的objLiteral.name

    【讨论】:

    • 非常简洁的高级答案 - 谢谢。 @ben336 通过解释 objModule2 从 objModule 获取原型来帮助我理解为什么会发生这种情况。
    【解决方案3】:

    我认为通过删除 () 函数末尾的括号 () 并将其重命名为明确它是一个构造函数的东西,我认为你在这里要做的事情会更好地完成,即改为这样做:

    var makeObjModule = function() {
      var name = "module 1";
      var setName = function( strName ) {
        name = strName;
      };
      var getName = function() {
        return name;
      };
    
      return {
        setName: setName,
        getName: getName
      };
    }; //removed parentheses here
    

    (就我个人而言,我也会使用function objModule() 语法,但这是一个单独的问题,取决于个人喜好。)

    然后你可以创建你的实例:

    var objModule = makeObjModule();
    var objModule2 = makeObjModule();
    

    Object.create 方法当然也是解决此问题的有效方法;有关使这项工作的更多详细信息,请参阅@ben336 的答案。

    我个人发现Object.create 在进行继承时比在进行这样的简单构造时更有用。 (在使用原型继承时,有些人也更喜欢使用 new 关键字,但由于您使用的是模块模式,我认为使用上面显示的“制造商”/构造函数更容易。)

    【讨论】:

    • P.S.您可能会发现我的回答 here 也很有用。
    • 谢谢!我已将您的建议添加为示例 3。在这种情况下,objModule2 不共享 objModule 的原型,因此它们不共享相同的闭包变量 - 对吗?
    • 那是正确的-使用我的答案(以及您的示例 3)中的方法,没有任何原型共享。可以修改它以使用原型,这样可以通过不为每个实例重新定义方法来节省一点内存,但是节省的内存几乎可以忽略不计,而且看起来有点尴尬,这就是为什么我建议不要使用原型的原因使用模块模式时这样的简单情况。
    • 顺便说一句,在大多数情况下,我个人认为模块模式并没有比更传统的基于原型的 OOP 提供显着优势,因此请确保您已经考虑过这种方法(其中使用“新”创建实例关键词)。还有一些使用 Object.create 但不使用模块模式的方法值得考虑。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多