使用'Object.create()'而不是'new'关键字了解原型对象创建



我找到了一个包含这些行的代码

var data = function() {
    function Metadata() { /*some initialization here*/ }
    Metadata.prototype = Object.create(Backend.prototype);
    Metadata.prototype.constructor = Metadata;
    return Metadata;
}

我很难理解到底发生了什么,以及如何使用返回的对象。如果我理解正确的话,data现在应该是一个对象,应该像这样初始化

var d = new data()

但是我不明白下面的行,为什么使用Object.create()而不是new关键字:

Metadata.prototype = Object.create(Backend.prototype);
Metadata.prototype.constructor = Metadata;

他们做什么?它们有必要吗?Object.createnew有什么区别?

JavaScript是基于原型的语言。这意味着它不像在其他语言中那样使用关键字。JavaScript使用函数作为类。在您的示例中,data变量可以被同化为:

var Data = function() { ... }

要创建该类的实例,可使用new关键字将object类型的结果赋值给变量。

var data = new Data()

从ECMA Script 6开始,我们可以使用实例化方法Object.create()来创建一个具有指定原型对象和属性的未初始化对象。它接受作为新创建对象原型的对象作为参数。(它也复制构造函数)

所以下面几行是一种使元数据扩展后端对象并保持自己的构造函数的方法:

// Metadata extends Backend
Metadata.prototype = Object.create(Backend.prototype);
Metadata.prototype.constructor = Metadata;

但是这段代码并不完全等同于 Metadata.prototype = new Backend();。请看下面的例子:

//Base class
var Backend = function(){ this.publicProperty='SomeValue'; }
//Extension class 1
var Metadata1 = function() { }
Metadata1.prototype = Object.create(Backend.prototype);
Metadata1.prototype.constructor = Metadata1;
//Extension class 2
var Metadata2 = function() { }
Metadata2.prototype = new Backend();

/*
 *  Then the results are different (see code snippet at the end of this post)
 */
//result1 = 'undefined'
var data1 = new Metadata1();
var result1 = data1.publicProperty;
//result2 = 'SomeValue'
var data2 = new Metadata2();
var result2 = data2.publicProperty;

实际上两者非常相似,主要区别在于new关键字实际运行构造函数代码,而Object.create不执行代码。

另一个区别是,Object.create可以创建一个不继承任何东西的对象(Object.create(null))。
然而,如果你执行Metadata.prototype = null,新创建的对象将继承Object.prototype


注意:在一些较旧的浏览器(IE8及以下版本)中,您可以使用Object.create:

Object.create = function(o){
    function F(){}
    F.prototype=o;
    return new F();
}

下面的工作代码片段显示了两种方法之间的差异

//Base class
var Backend = function(){ this.publicProperty='SomeValue'; }
//Extension class 1
var Metadata1 = function() { }
Metadata1.prototype = Object.create(Backend.prototype);
Metadata1.prototype.constructor = Metadata1;
//Extension class 2
var Metadata2 = function() { }
Metadata2.prototype = new Backend();
//result: 'undefined'
var data1 = new Metadata1();
$("#result1").text("result1: " +  (typeof data1.publicProperty=='undefined' ? 'undefined' : data1.publicProperty));
//result: 'SomeValue'
var data2 = new Metadata2();
$("#result2").text("result2: " +  (typeof data2.publicProperty=='undefined' ? 'undefined' : data2.publicProperty));
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="result1"></div>
<div id="result2"></div>

这两行是Metadata扩展Backend的典型方式。

JavaScript中的类被定义为函数。如果在函数上定义了prototype,则可以使用new关键字(或Object.create)来创建该类的实例。在原型上的函数/属性将在你创建的类的新实例上。

在上面的代码中,Metadata.prototype被设置为Backend的一个实例,因此Metadata.prototype将继承调用Backend.prototype的方法/属性。

您可以在继承和原型链中找到更多信息。

在JavaScript中没有类,相反,我们有可以用new关键字调用的构造函数来创建新对象,因此我们将获得与类和实例化相同的行为。

这两行用来表示继承,并使Metadata扩展Backend,在这一行:

Metadata.prototype = Object.create(Backend.prototype);

我们定义了Metadata, object的原型,它应该是新创建对象的原型。

而在这一行:

Metadata.prototype.constructor = Metadata;

我们定义了Metadata的构造函数,它将用于创建Metadata的新实例。

继承可以这样测试:

var meta = new Metadata(); 
console.log('Is meta an instance of Metadata? ' + (meta instanceof Metadata)); // true
console.log('Is meta an instance of Backend? ' + (meta instanceof Backend)); // true

您可以在Object.create()文档中找到更多关于它的示例

Object.create 是ES6的一个方法,它以给定的对象作为原型创建一个对象。

Metadata.prototype = Object.create(Backend.prototype);

所以上面的行意味着Metadata继承了Backend的所有属性和方法。它有点类似于ES6之前的下面一行:

Metadata.prototype = new Backend();

然而,Metadata也继承了Backendconstructor属性:

var metaData = new Metadata();
metaData.constructor; // function Backend()

这看起来很奇怪,因为metaData实际上是由Metadata构建的,所以我们这样修复它:

Metadata.prototype.constructor = Metadata;
var metaData = new Metadata();
metaData.constructor; // function Metadata()

注意,这不会与instanceof操作符混淆:

metaData instanceof Metadata // true
metaData instanceof Backend  // true

最新更新