Спадщина 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 за допомогою нового ключового слова

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
Привіт Шейху. Дякуємо за ваші зусилля щодо цього. Так, відмінності полягають у тому, що конструктор запускається у другому прикладі, але не в першому. (що в моєму випадку бажано). Що стосується невизначеної загальнодоступної власності, яка не успадковується від суперреалізації, вам просто потрібно зателефонувати супер у дитячому конструкторі: SomeBaseClass.call (це). Перевірте цю скрипку: jsfiddle.net/NhQGB
ChrisRich

Я шукав надзвичайно простий спосіб належного успадкування в JS без використання бібліотек / фреймворків. Я думаю, що цей приклад (у моїй скрипці вище) є найкращим підходом для сучасних браузерів. Можливо, поліфайл Object.Create міг би додати підтримку застарілих браузерів?
ChrisRich

в основному підсумовуючи, якщо ви робите .prototype = new, то ви успадковуєте будь-які значення, які ви присвоїли цьому в базовому класі, а коли ви робите object.create, ви ВСІМО наслідуєте те, що є на прототипі в базовому класі, правда?
davidjnelson

Це означає "MyClass.prototype = новий 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 за допомогою нового ключового слова ..

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 (батьківська функція) і матимуть доступ до його методів, але якщо ви створите дочірню функцію за допомогою нової, вона поверне абсолютно новий об'єкт з усіма батьківськими властивостями, які нам не потрібні, а також коли ви створюєте будь-який об'єкта або функції, використовуючи "Нове", виконується батьківська функція, якою ми не хочемо бути.

Ось винос

якщо ви просто хочете делегувати деякі методи в батьківській функції і не хочете створювати новий об'єкт, найкращим способом є використання Object.create.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.