3

我在下面创建了两个员工类,一个使用构造函数,另一个使用 JSON 表示法。在构造函数中,该print函数是由原型创建的,因此只保留一份副本,对象(emp1emp2)共享该print函数。

问题:在 JSON 表示法 ( EmployeeNew) 中,print函数是否会仅作为一份副本保存在内存中?还是每个对象都会保留自己的副本?这两种方法的根本区别是什么?哪个最适合哪个场景?

var Employee = function (name) {
    this.name = name;
};

Employee.prototype.print = function () {
    console.log(this.name);
}


var emp1 = new Employee("jack"),
    emp2 = new Employee("mark");

emp1.print();
emp2.print();

var EmployeeNew = {
    init: function (name) { this.name = name; },
    print: function () {
        console.log(this.name);
    }
};

var empNew1 = Object.create(EmployeeNew),
    empNew2 = Object.create(EmployeeNew)

empNew1.init("jack")
empNew1.print();
empNew2.init("jack");
empNew2.print();
4

2 回答 2

3

您的两个代码示例通常是等效的(除了一些与问题无关的小细节)

这个...

Object.create(EmployeeNew)

...创建一个以EmployeeNew对象为原型的新对象。所以printinit函数是共享的。

console.log(empNew1.init === empNew2.init); // true
console.log(empNew1.print === empNew2.print); // true

为了进一步说明,这是一个采用以下步骤的示例...

  1. 创建一个EmployeeNew对象以供使用Object.create
  2. 使用创建 2 个独特的对象Object.create
  3. 验证新对象是否可以使用提供的功能EmployeeNew
  4. 添加一个新函数到EmployeeNew
  5. 查看步骤 2 中的对象是否可以使用该新功能

第 1 步:创建EmployeeNew对象

var EmployeeNew = {
    init: function (name) { this.name = name; },
    print: function () {
        console.log(this.name);
    }
};

第 2 步:使用创建 2 个唯一对象Object.create

var empNew1 = Object.create(EmployeeNew),
    empNew2 = Object.create(EmployeeNew)

第 3 步:验证新对象是否可以使用EmployeeNew

empNew1.init("jack");
empNew1.print();
empNew2.init("jack");
empNew2.print();

第四步:添加新功能EmployeeNew

EmployeeNew.foo = function() {
    console.log( 'Foo was invoked' );
};

第 5 步:查看第 2 步中的对象是否可以使用该新功能

empNew1.foo();  // logs 'Foo was invoked'
empNew2.foo();  // logs 'Foo was invoked'

因此,您可以看到empNew1empNew2能够观察到 的变化EmployeeNew。这是因为当我们EmployeeNew作为第一个参数传递给 时Object.create,我们创建了一个新对象,并EmployeeNew设置prototype为该对象的。

简单来说,当我们查找一个属性时,例如 on empNew1,如果empNew1没有该属性,它会自动查找其原型以查看该属性是否存在于该对象上。如果是这样,它会使用它。


关于你的评论...

“......假设,如果创建 this.name 作为属性(名称:“”)是名称属性也将被视为原型......”

是的,如果我们这样做...

EmployeeNew.name = "unknown"

...然后该属性将在所有具有EmployeeNew原型对象的实例之间共享。

因为.name原型上的属性是不可变的原始(字符串),如果我们尝试写入该属性,会发生该.name属性自动直接添加到实例的情况。

继续上面的例子......

EmployeeNew.name = "unknown";

现在之前创建的实例将引用该属性...

empNew1.name;  // "unknown"
empNew2.name;  // "unknown"

...但是现在让我们在一个实例上更新属性...

empNew1.name = "bubba";

empNew1.name;  // "bubba"
empNew2.name;  // "unknown"

这是因为empNew1now 有自己的.name属性来引用"bubba". 这会在 的原型上隐藏属性,因此对该属性的搜索永远不会扩展到原型对象。.nameempNew1

由于empNew2没有分配 a .name,它仍然会查找该属性的原型。

于 2012-04-12T17:47:32.463 回答
0

这两种情况是等价的,如果使用new及其所有初始参数或使用init方法创建新对象,您只需要考虑对您的编码风格更直观的方法。

Class实现的一个优点是,如果您在Class定义中编写对象(我知道,它不是原型),您可以创建内部私有属性,而仅使用原型您不能(除非您使用特殊符号,例如下划线前缀)。最后只是风格问题,你觉得什么更舒服。

于 2012-04-12T17:53:46.110 回答