Наследование 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
}

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 пути по умолчанию .


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.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.