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

Difference between prototype function and json notation function?

本文关键字:函数 之间 区别 表示 json 原型      更新时间:2023-09-26

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

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

这。。。

Object.create(EmployeeNew)

。创建一个以EmployeeNew对象作为其原型的新对象。因此,printinit功能是共享的。

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

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

  1. 创建供Object.create使用的EmployeeNew对象
  2. 使用 Object.create 创建 2 个唯一对象
  3. 验证新对象是否可以使用 EmployeeNew 提供的函数
  4. EmployeeNew添加新函数
  5. 查看步骤 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没有该属性,它会自动查看其原型以查看该属性是否存在于该对象上。如果是这样,它会使用它。


关于您的评论...

"...假设,如果创建 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"

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

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

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

Class

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