Наследование JavaScript: Object.create vs new

В JavaScript в чем разница между этими двумя примерами:

Предпосылка:

function SomeBaseClass(){
}

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

    doThat : function(){
    }
}

Пример наследования A с использованием Object.create:

function MyClass(){
}

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

Пример наследования B с использованием ключевого слова new

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
}

person ChrisRich    schedule 23.10.2012    source источник
comment
Какого черта это помечено как дубликат!?! Другой вопрос и ответы даже не упоминают Object.create. Это ошибка, и ее следует открыть повторно.   -  person Scott Rippey    schedule 10.10.2014
comment
Во всяком случае, это дубликат понимания stackoverflow.com/questions/4166616/   -  person Scott Rippey    schedule 10.10.2014
comment
13 голосов за этот комментарий, и он до сих пор не открывается ..?!   -  person T J    schedule 18.11.2015


Ответы (4)


В своем вопросе вы упомянули, что 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);​

В этом случае его свойство publicProperty также доступно для объекта obj, например в этом примере.

Поскольку Object.create недоступен в некоторых старых браузерах, в этом случае вы можете использовать

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

Приведенный выше код просто добавляет функцию Object.create, если она недоступна, поэтому вы можете использовать функцию Object.create, и я думаю, что приведенный выше код описывает, что на самом деле делает Object.create. Надеюсь, это каким-то образом поможет.

person The Alpha    schedule 24.10.2012
comment
Привет, Шейх. Спасибо за ваши усилия. Да, разница в том, что конструктор запускается во втором примере, но не в первом. (что желательно в моем случае). Что касается неопределенного общедоступного свойства, которое не унаследовано от супер-реализации, вам просто нужно вызвать super в дочернем конструкторе: SomeBaseClass.call (this). Проверьте эту скрипку: jsfiddle.net/NhQGB - person ChrisRich; 28.10.2012
comment
Я искал супер простой способ правильного наследования в JS без использования каких-либо библиотек / фреймворков. Я думаю, что этот пример (в моей скрипке выше) - лучший подход для современных браузеров. Возможно, полифилл Object.Create может добавить поддержку устаревших браузеров? - person ChrisRich; 29.10.2012
comment
Итак, вкратце, если вы используете .prototype = new, тогда вы наследуете любые значения, которые вы присвоили this в базовом классе, а когда вы делаете object.create, вы наследуете ТОЛЬКО то, что находится на прототипе в базовом классе, верно? - person davidjnelson; 13.08.2014
comment
Означает ли этот MyClass.prototype = new SomeBaseClass (), что новый экземпляр SomeBaseClass создается, когда экземпляр MyClass еще не создан? - person ; 15.12.2014
comment
Нет, только когда вы создаете главный объект, прототип устанавливается на этот SomeBaseClass. - person The Alpha; 15.12.2014
comment
по этому поводу существует множество тем, но нет такого объяснения, как ваше. +1 - person snr; 01.09.2020

Оба примера, кажется, делают одно и то же.

Это правда в вашем случае.

Когда бы вы предпочли одно другому?

Когда SomeBaseClass имеет тело функции, это будет выполняться с ключевым словом new. Обычно это не предназначено - вам нужно только настроить цепочку прототипов. В некоторых случаях это может даже вызвать серьезные проблемы, потому что вы фактически создаете экземпляр объекта, переменные с частной областью видимости которого используются всеми MyClass экземплярами, поскольку они наследуют одни и те же привилегированные методы. Возможны и другие побочные эффекты.

Таким образом, вы обычно должны предпочесть Object.create. Тем не менее, он не поддерживается в некоторых устаревших браузерах; по этой причине вы слишком часто встречаетесь с new-подходом, поскольку он часто не приносит (очевидного) вреда. Также ознакомьтесь с этим ответом.

person Bergi    schedule 23.10.2012
comment
Это лучший ответ, хорошо объясненный - person spex; 22.01.2014

Разница становится очевидной, если вы используете 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() является то, что смешивание / * наследования * от других объектов может выглядеть более естественным, чем использование Javascripts способом по умолчанию.

person jAndy    schedule 23.10.2012
comment
Object.create не может заменить классический подход на new, когда требуется функция-конструктор - person Bergi; 24.10.2012
comment
Определенно может @Bergi. Посмотрите это сообщение в блоге: davidwalsh.name/javascript-objects-deconstruction. Вы также можете передать объект инициализации в качестве второго параметра в Object.create (). - person LocalPCGuy; 17.04.2015
comment
@LocalPCGuy: Нет, не может, потому что Object.create не вызывает функцию, которая была бы необходима для создания замыканий. Конечно, с Object.create вы можете поместить init метод в свои объекты и немедленно вызвать его, и он, возможно, даже вернет this, чтобы вы получили краткий синтаксис - но подождите, тогда является конструктором. - person Bergi; 18.04.2015
comment
Вызов функции init работает аналогично конструктору, но не является конструктором. И этот метод позволяет в точности заменить классический подход на Object.create. И ментальная модель объектной связи намного проще, чем модель, созданная с помощью «нового». - person LocalPCGuy; 18.04.2015
comment
Ну, моя ментальная модель new использует объектную связь, так что особой разницы нет. Фактически, есть только две вещи: .constructor называется .init, и эта функция не имеет свойства .prototype, указывающего на ваш объект-прототип. Остальное - всего лишь синтаксис, и я предпочитаю new X, а не Object.create(x).init(). - person Bergi; 18.04.2015

Я не эксперт в 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: создайте дочернюю функцию (PersonSalary), которая расширяется над функцией Person с помощью ключевого слова New ..

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

PersonSalary();

шаг 3: создайте вторую дочернюю функцию (PersonLeaves), которая расширяется над функцией Person, используя ключевое слово Object.create ..

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


PersonLeaves();

// Теперь проверяем прототипы обеих дочерних функций.

PersonSalary.prototype
PersonLeaves.prototype

обе эти дочерние функции будут связаны с прототипом Person (родительская функция) и могут получить доступ к его методам, но если вы создадите дочернюю функцию с помощью new, она вернет совершенно новый объект со всеми родительскими свойствами, которые нам не нужны, а также когда вы создадите какие-либо объект или функция, использующие "New", выполняется эта родительская функция, которой мы не хотим быть.

Вот выводы

если вы просто хотите делегировать некоторые методы в родительской функции и не хотите, чтобы создавался новый объект, лучше всего использовать Object.create.

person Venkat    schedule 13.01.2016