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

123

В 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
}
ChrisRich
источник
23
Какого черта это помечено как дубликат!?! Другой вопрос и ответы даже не упоминайте Object.create. Это ошибка, и ее следует открыть повторно.
Скотт Риппи
2
Во всяком случае, это дубликат stackoverflow.com/questions/4166616/…
Скотт Риппи
1
13 голосов за этот комментарий и до сих пор не открыты ..?!
TJ

Ответы:

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);​

В этом случае его 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самом деле делает. Надеюсь, это каким-то образом поможет.

Альфа
источник
6
Привет, Шейх. Спасибо за ваши усилия. Да, разница в том, что конструктор запускается во втором примере, но не в первом. (что желательно в моем случае). Что касается неопределенного общедоступного свойства, которое не наследуется от супер-реализации, вам просто нужно вызвать super в дочернем конструкторе: SomeBaseClass.call (this). Проверьте эту скрипку: jsfiddle.net/NhQGB
ChrisRich
Я искал супер простой способ правильного наследования в JS без использования каких-либо библиотек / фреймворков. Я думаю, что этот пример (в моей скрипке выше) - лучший подход для современных браузеров. Возможно, полифилл Object.Create может добавить поддержку устаревших браузеров?
ChrisRich
Итак, вкратце, если вы используете .prototype = new, тогда вы наследуете любые значения, которые вы присвоили this в базовом классе, а когда вы делаете object.create, вы наследуете ТОЛЬКО то, что находится на прототипе в базовом классе, верно?
davidjnelson
Означает ли этот «MyClass.prototype = new SomeBaseClass ()», что новый экземпляр SomeBaseClass создается, когда экземпляр MyClass еще не создан?
Нет, только когда вы создаете основной объект, прототип устанавливается на него SomeBaseClass.
Альфа
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()является то , что она может выглядеть более естественно , чтобы смешать / * * унаследует от других объектов, чем использование JavaScripts пути по умолчанию .

JANDY
источник
7
Object.createдействительно не может заменить классический подход тем, newкогда требуется функция-конструктор
Берги
1
Определенно может @Bergi. Посмотрите это сообщение в блоге: davidwalsh.name/javascript-objects-deconstruction . Вы также можете передать объект инициализации в качестве второго параметра в Object.create ().
LocalPCGuy
@LocalPCGuy: Нет, не может, потому Object.createчто не вызывает функцию, которая была бы необходима для создания замыканий. Конечно, с Object.createвами можно положить initметод на ваших объектах и вызова , которые немедленно и это , возможно , даже возвращает thisтак , что вы получите краткий синтаксис - но ждать, что это конструктор тогда.
Берги
1
Вызов функции init работает аналогично конструктору, но не является конструктором. И этот метод позволяет в точности заменить классический подход на Object.create. И мысленная модель объектной связи намного проще, чем модель, созданная с помощью «нового».
LocalPCGuy
Моя ментальная модель newиспользует «объектную связь», так что особой разницы нет. Фактически, есть только две вещи: .constructorвызывается .init, и эта функция не имеет .prototypeсвойства, указывающего на ваш объект-прототип. В остальном это просто синтаксис - и я предпочитаю new Xболее Object.create(x).init().
Берги,
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: создайте дочернюю функцию (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.

Venkat
источник