我在下面创建了两个员工类,一个使用构造函数,另一个使用JSON表示法。在构造函数中,print函数是由原型(prototype)创建的,因此将仅保留一个副本,并且对象(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();

最佳答案

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

这个...

Object.create(EmployeeNew)

...以EmployeeNew对象为其原型(prototype)创建一个新对象。因此,printinit函数是共享的。
console.log(empNew1.init === empNew2.init); // true
console.log(empNew1.print === empNew2.print); // true

为了进一步说明,这是一个采用以下步骤的示例...
  • 创建供EmployeeNew使用的Object.create对象
  • 使用Object.create创建2个唯一的对象
  • 验证新对象可以使用EmployeeNew
  • 提供的功能
  • EmployeeNew添加新函数
  • 查看步骤2中的对象是否可以使用该新函数


  • 步骤1:创建EmployeeNew对象
    var EmployeeNew = {
        init: function (name) { this.name = name; },
        print: function () {
            console.log(this.name);
        }
    };
    

    步骤2:使用Object.create创建2个唯一的对象
    var empNew1 = Object.create(EmployeeNew),
        empNew2 = Object.create(EmployeeNew)
    

    步骤3:验证新对象可以使用EmployeeNew提供的功能
    empNew1.init("jack");
    empNew1.print();
    empNew2.init("jack");
    empNew2.print();
    

    步骤4: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

    简单来说,当我们在empNew1上查找属性时,如果empNew1没有该属性,它将自动查找其原型(prototype)以查看该对象上是否存在该属性。如果是这样,它将使用它。

    关于您的评论...



    是的,如果我们这样做...
    EmployeeNew.name = "unknown"
    

    ...然后该属性将在所有以EmployeeNew作为其原型(prototype)对象的实例之间共享。



    因为原型(prototype)上的.name属性是一个不变的原始值(字符串),所以如果我们尝试对该属性进行写入,则会发生的情况是.name属性被自动直接添加到实例中。

    继续上面的示例...
    EmployeeNew.name = "unknown";
    

    现在,先前创建的实例将引用该属性...
    empNew1.name;  // "unknown"
    empNew2.name;  // "unknown"
    

    ...但是现在让我们在一个实例上更新属性...
    empNew1.name = "bubba";
    
    empNew1.name;  // "bubba"
    empNew2.name;  // "unknown"
    

    这是因为empNew1现在具有引用.name的自己的"bubba"属性。这遮盖了.name原型(prototype)上的empNew1属性,因此对该属性的搜索永远不会扩展到原型(prototype)对象中。

    由于没有为empNew2分配.name,因此它仍然会针对该属性使用其原型(prototype)。

    10-06 14:01