JavaScript继承:Object.create与New


123

在JavaScript中,这两个示例之间有什么区别:

先决条件:

function SomeBaseClass(){
}

SomeBaseClass.prototype = {
    doThis : function(){
    },

    doThat : function(){
    }
}

使用Object.create的继承示例A:

function MyClass(){
}

MyClass.prototype = Object.create(SomeBaseClass.prototype);

使用new关键字的继承示例B

function MyClass(){
}

MyClass.prototype = new SomeBaseClass();

这两个例子似乎做同样的事情。您何时会选择一个?

另一个问题:考虑下面链接(第15行)中的代码,其中对函数自己的构造函数的引用存储在原型中。为什么这有用?

https://github.com/mrdoob/three.js/blob/master/src/loaders/ImageLoader.js

摘录(如果您不想打开链接):

THREE.ImageLoader.prototype = {

    constructor: THREE.ImageLoader
}

23
为什么将这个标记为重复!?!另一个问题和答案甚至都没有提及Object.create。这是一个错误,应重新打开。
Scott Rippey 2014年


1
对该评论有13票,但仍未重新开放..?!
TJ

Answers:


111

在您的问题中,您提到Both examples seem to do the same thing,这根本不是真的,因为

你的第一个例子

function SomeBaseClass(){...}
SomeBaseClass.prototype = {
    doThis : function(){...},
    doThat : function(){...}
}
function MyClass(){...}
MyClass.prototype = Object.create(SomeBaseClass.prototype);

在这个例子中,你只是继承SomeBaseClass' prototype,但如果你在你有一个属性SomeBaseClass

function SomeBaseClass(){ 
    this.publicProperty='SomeValue'; 
}

如果你像这样使用它

var obj=new MyClass();
console.log(obj.publicProperty); // undefined
console.log(obj);​

obj对象将没有此示例中的publicProperty属性。

你的第二个例子

MyClass.prototype = new SomeBaseClass();

它正在执行该constructor函数,并创建一个实例SomeBaseClass并继承整个SomeBaseClass对象。所以,如果您使用

    var obj=new MyClass();
    console.log(obj.publicProperty); // SomeValue
    console.log(obj);​

在这种情况下publicPropertyobj本示例一样它的属性也可用于对象。

由于Object.create在某些旧版浏览器中无法使用,因此您可以使用

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

上面的代码只是Object.create在不可用时添加了功能,因此您可以使用Object.create功能,我认为上面的代码描述了Object.create实际的功能。希望它会有所帮助。


6
嗨,谢赫。感谢您为此付出的努力。是的,不同之处在于构造函数是在第二个示例中运行的,而不是在第一个示例中运行的。(在我的情况下这是理想的)。对于未继承自super实现的未定义公共属性,您只需要在子级的构造函数中调用super:SomeBaseClass.call(this)。检查这个小提琴:jsfiddle.net/NhQGB
ChrisRich 2012年

我一直在寻找一种不使用任何库/框架的JS中正确继承的超级简单方法。我认为这个例子(在我上面的小提琴中)是现代浏览器的最佳方法。也许Object.Create polyfill可以增加对旧版浏览器的支持?
克里斯里奇(ChrisRich)2012年

基本上就是这样,如果您执行.prototype = new,那么您将继承基类中分配给它的任何值,而当您执行object.create时,您仅继承基类中原型的内容,对吗?
davidjnelson

难道这个“ MyClass.prototype = new SomeBaseClass()”意味着尚未创建MyClass实例时就创建了SomeBaseClass的新实例吗?

不,仅当创建主对象时,原型才设置为那个SomeBaseClass
Alpha

39

这两个例子似乎做同样的事情。

您的情况是这样。

您何时会选择一个?

SomeBaseClass具有函数体时,将使用new关键字执行。这通常不是故意的-您只想设置原型链。在某些情况下,它甚至可能导致严重的问题,因为您实际上实例化了一个对象,该对象的私有作用域变量在MyClass继承相同的特权方法时被所有实例共享。其他副作用是可以想象的。

因此,通常应该选择Object.create。但是,某些旧版浏览器不支持该功能。这就是您看到- new方法过于频繁的原因,因为它通常没有(明显)危害。也看看这个答案


这是最好的答案,很好的解释
SPEX

8

如果Object.create()按预期使用,差异将变得明显。实际上,它确实完全隐藏了prototype代码中的单词,将在幕后完成工作。使用Object.create(),我们可以像

var base =  {
    doThis : function(){
    },

    doThat : function(){
    }
};

然后我们可以从中扩展/继承其他对象

var myObject = Object.create( base );
// myObject will now link to "base" via the prototype chain internally

因此,这是另一个概念,一种更“面向对象”的继承方式。例如,没有开箱即用的“构造函数” Object.create()。但是,当然,您可以在这些对象中创建并调用一个自定义的构造函数

使用一个论据Object.create()是,它可能看起来更自然,以混合 / * *继承其他对象,不是使用Javascript角默认的方式


7
Object.createnew需要构造函数时,不能真正替代经典方法
Bergi 2012年

1
它绝对可以@Bergi。查看此博客文章:davidwalsh.name/javascript-objects-deconstruction。您还可以将初始化对象作为第二个参数传递给Object.create()。
LocalPCGuy 2015年

@LocalPCGuy:不可以,因为Object.create它不会调用创建闭包所需的函数。当然,Object.create您可以init在对象上放置一个方法并立即调用该方法,它甚至可能返回,this以便获得简洁的语法-但是,等等,那就是构造函数。
Bergi 2015年

1
调用init函数与构造函数的工作原理类似,但它不是构造函数。而且该方法确实允许您将Object.create完全替换为经典方法。而且对象链接的心理模型比通过“ new”创建的模型简单得多。
LocalPCGuy 2015年

好吧,我的心理模型new使用“对象链接”,所以没有太大区别。实际上,只有两件事:.constructor被称为.init,并且该函数没有.prototype指向原型对象的属性。剩下的只是语法-我喜欢new XObject.create(x).init()
Bergi 2015年

0

我不是Java脚本专家,但是这里有一个简单的示例,可以理解“ Object.create”和“ new”之间的区别。

步骤1:使用某些属性和操作创建父函数。

function Person() {

this.name = 'venkat';

this.address = 'dallas';

this.mobile='xxxxxxxxxx'

}

Person.prototype.func1 = function () {

    return this.name + this.address;
}

第2步:使用New关键字创建一个子函数(PersonSalary),该子函数扩展到Person函数之上。

function PersonSalary() {
    Person.call(this);
}
PersonSalary.prototype = new Person();

PersonSalary();

步骤3:使用Object.create关键字创建第二个子函数(PersonLeaves),该子函数在Person函数之上扩展。

function PersonLeaves() {
 Person.call(this);
}
PersonLeaves.prototype = Object.create(Person.prototype);


PersonLeaves();

//现在检查两个子函数原型。

PersonSalary.prototype
PersonLeaves.prototype

这两个子函数都将链接到Person(父函数)原型,并且可以访问它的方法,但是如果您使用new创建子函数,它将返回一个全新的对象,该对象带有我们不需要的所有父属性,并且在创建任何父属性时也是如此使用“ New”的对象或函数,即我们不想执行的父函数。

这是外卖

如果您只想委托给父函数中的某些方法而不希望创建一个新对象,则最好使用Object.create。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.