Спадщина 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.