【问题标题】:Understanding prototype object creation with 'Object.create()' instead of 'new' keyword使用“Object.create()”而不是“new”关键字来理解原型对象的创建
【发布时间】:2015-08-11 07:52:45
【问题描述】:

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

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 object constructor prototype


    【解决方案1】:

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

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

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

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

    在这一行中:

    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() documentaion 此处的另一个示例找到更多相关信息。

    【讨论】:

    • meta instanceof Backend 也应该是 true
    • @Leo 很好指出,是的,当然,我只是错过了,我编辑了我的答案,谢谢。
    【解决方案2】:

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

    var Data = function() { ... }

    要创建此类的实例,我们使用 new 关键字将对象类型的结果分配给变量。

    var data = new Data()

    从 ECMA 脚本 6 开始,我们可以使用实例化方法 Object.create() 创建具有指定原型对象和属性的未启动对象。它接受应该是新创建对象的原型的对象。 (它也复制了构造函数)

    所以下面几行是让 Metadata 扩展 Backend 对象并保留自己的构造函数的一种方法:

    // 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();
    }
    

    这是显示两种方法之间差异的工作代码 sn-p

    //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>

    【讨论】:

    • 谢谢。这非常令人困惑,我很高兴 ES6 继承语义(“extends”、“constructor”、“super”)更容易理解。
    【解决方案3】:

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

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

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

    您可以在Inheritance and the prototype chain 找到更多信息。

    【讨论】:

    • Metadata.prototype = Object.create(Backend.prototype);Metadata.prototype = new Backend()有什么区别?
    • 当你使用Object.create时,构造函数不会被调用。所以如果你只想扩展Backend的原型,那么Object.create是有意义的。
    【解决方案4】:

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

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

    所以上面这行意味着Metadata继承了Backend的所有属性和方法。它在某种程度上类似于 ES6 之前的以下行:

    Metadata.prototype = new Backend();
    

    不过,Metadata 也从Backend 继承constructor 属性:

    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
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多