原型函数和json表示法函数之间的区别?

时间:2012-04-12 17:39:27

标签: javascript json prototype javascript-objects object-literal

我在下面创建了两个员工类,一个使用构造函数,另一个使用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();

2 个答案:

答案 0 :(得分:3)

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

此...

Object.create(EmployeeNew)

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

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

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

  1. 创建EmployeeNew
  2. 使用的Object.create对象
  3. 使用Object.create
  4. 创建2个唯一对象
  5. 验证新对象是否可以使用EmployeeNew
  6. 提供的功能
  7. EmployeeNew
  8. 添加新功能
  9. 查看步骤2中的对象是否可以使用该新功能

  10. 第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没有该属性,会自动查找其原型查看该对象上是否存在该属性。如果是这样,它就会使用它。


    关于你的评论...

      

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

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

    EmployeeNew.name = "unknown"
    

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

    BUT

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

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

    EmployeeNew.name = "unknown";
    

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

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

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

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

    这是因为empNew1现在有自己的.name属性引用"bubba"。这个阴影 .name原型上的empNew1属性,因此对该属性的搜索永远不会扩展到原型对象中。

    由于empNew2未分配.name,因此它仍会查找该属性的原型。

答案 1 :(得分:0)

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

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