4

如果我在对象文字上使用 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 中不会发生,因此这两个对象不共享相同的闭包属性。

4

3 回答 3

3

示例 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

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

于 2013-04-21T02:10:04.480 回答
3

objModule2.setName指与 相同的功能objModule.setName,但具有不同的this。(Object.create不复制任何东西)
调用它将设置相同的局部变量。

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

于 2013-04-21T02:10:06.870 回答
3

()我认为通过删除函数末尾的括号objModule并将其重命名为清楚地表明它是构造函数的内容,可以更好地完成您在这里要做的事情,即这样做:

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关键字,但由于您使用的是模块模式,我认为使用上面显示的“制造商”/构造函数更容易。)

于 2013-04-21T02:36:28.993 回答