Понимание того, почему истинное наследование прототипов лучше, чем классическое /псевдо-прототипное наследование, и почему я не должен использовать «новый»

Чтение некоторых статей из Аадит М Шах , например, Почему вопросы наследования прототипов или Прекратить использование функций конструктора в JavaScript от Эрика Эллиотта Я думаю, что я понимаю все их аргументы, в теории. Но на практике я не вижу реальных преимуществ этого шаблона.

Давайте рассмотрим две реализации из двух фрагментов, чтобы создать наследование.

  1. Первый использует augment.js /> сценарий от Аадит М Шах
  2. В этом примере мы собираемся использовать этот сценарий , Сделано также Aadit M Shah.

Реализация 1 :

    var AugmentPerson = Object.augment(function() {
      this.constructor = function(name) {
          this.name = name;
      };
      this.setAddress = function(country, city, street) {
          this.country = country;
          this.city = city;
          this.street = street;
      };
    });
    var AugmentFrenchGuy = AugmentPerson.augment(function(base) {
      this.constructor = function(name) {
          base.constructor.call(this,name);
      };
      this.setAddress = function(city, street) {
          base.setAddress.call(this, "France", city, street);
      };
    });
    var AugmentParisLover = AugmentFrenchGuy.augment(function(base) {

      this.constructor = function(name) {
          base.constructor.call(this, name);
      };

      this.setAddress = function(street) {
          base.setAddress.call(this, "Paris", street);
      };
    });
    var t = new AugmentParisLover("Mary");
    t.setAddress("CH");
    console.log(t.name, t.country, t.city, t.street); //Mary France Paris CH

В этом примере мы используем конструкторы функций вместо того, чтобы наследовать непосредственно от объекта.

Реализация 2 :

    var CreatePerson = {
        create: function (name) {
            this.name = name;
            return this.extend();
        },
        setAddress: function(country, city, street) {
             this.country = country;
             this.city = city;
             this.street = street;
        }
    };
    var CreateFrenchGuy  = CreatePerson.extend({
        create: function (name) {
            return CreatePerson.create.call(this,name);
        },
        setAddress: function(city, street) {
            CreatePerson.setAddress('France', city, street);
        }
    });
    var CreateParisLover  = CreateFrenchGuy.extend({
        create: function (name) {
            return CreateFrenchGuy.create.call(this,name);
        },
        setAddress: function(street) {
            CreateFrenchGuy.setAddress('Paris', street);
        }
    });

    var t = CreateParisLover.create("Mary");
    t.setAddress("CH");
    console.log(t.name, t.country, t.city, t.street); //Mary France Paris CH

Если честно, я пытаюсь увидеть преимущества второй реализации. Но я не могу. Единственное, что я вижу, является более гибким, потому что мы можем создать экземпляр, используя apply:

var t = CreateParisLover.create.apply(CreateParisLover, ["Mary"]);

Это дает нам больше гибкости, это правда. Но мы можем сделать то же самое с этим :

  Function.prototype.new = function () {
     function functor() { return constructor.apply(this, args); }
     var args = Array.prototype.slice.call(arguments);
     functor.prototype = this.prototype;
     var constructor = this;
     return new functor;
  };

Тогда мы можем:

var t = AugmentParisLover.new.apply(AugmentParisLover, ["Mary"]);

Каковы реальные преимущества с точки зрения гибкости, возможности повторного использования, сложности ... Потому что если вы проверите производительность обоих случаях. Object.create () работает намного медленнее, чем новый: http://jsperf.com/наследования с помощью создать-VS-новому Я путаю.

9 голосов | спросил EnZo 28 42013vEurope/Moscow11bEurope/MoscowThu, 28 Nov 2013 16:33:47 +0400 2013, 16:33:47

2 ответа


0

Подобные вопросы задавались и отвечали много раз прежде. См:

Функция конструктора и функции фабрики Наследование прототипов классического Vs

Подробнее: https://medium.com/javascript-scene/3-different-kinds-of-prototypal-inheritance-es6-edition-32d777fa16c9#.s0r3i5w6t http://vimeo.com/69255635

ТЛ; дг

  • Конструкторы нарушают принцип открытия /закрытия
  • Конструкторы связывают создание объекта с инициализацией объекта - иногда затрудняя возможность повторного использования кода
  • Конструкторы выглядят немного как классы, что сбивает с толку. JavaScript не нуждается в классах (я рекомендую избегать использования ключевого слова class в ES6). В JavaScript есть что-то лучше классов.
  • Сочетание делегирования прототипа и динамического расширения объекта (конкатенативного наследования) гораздо более мощное и гибкое, чем классическое наследование.
  • Связи между Constructor.prototype и экземплярами являются слабыми и ненадежными в JavaScript. Использование конструкторов может создать иллюзию рабочего экземпляра, который может сбивать с толку, когда он не работает в разных контекстах выполнения или не работает, если прототип конструктора заменяется.
  • Поменять прототип на конструкторы сложнее. Вы можете сделать это, чтобы включить полиморфную конструкцию объекта. На фабриках прототипы с «горячей» заменой просты и могут быть выполнены с помощью .call () и .apply ().

Изменить - ответ на "ответ", опубликованный ОП:

Самое лучшее в Object.create - это то, что это специальный низкоуровневый инструмент, который позволяет вам создавать новый объект и назначать ему любой прототип без использования функции конструктора. Существует множество причин, по которым следует избегать конструкторов, которые подробно рассматриваются здесь: Функция конструктора vs Заводские функции

  1. Код, который вы используете для демонстрации "меньшего количества кода", на самом деле вообще не демонстрирует разницы между классическим и прототипным наследованием. Более типичный пример может выглядеть следующим образом:

Классическая

var Animal = function Animal(name) {
  this.name = name;
};

Animal.prototype.walk = function walk() {
  console.log(this.name + ' goes for a walk.');
};

var Rabbit = function Rabbit(/* name */) {
  // Because construction and instantiation are conflated, you must call super().
  Animal.prototype.constructor.apply(this, arguments);
};

// Classical inheritance is really built on top of prototypal inheritance:
Rabbit.prototype = Object.create(Animal.prototype);

// Fix the .constructor property:
Rabbit.prototype.constructor = Rabbit;

Rabbit.prototype.jump = function jump() {
  console.log(this.name + ' hops around a bit.');
};

var myRabbit = new Rabbit('Bunny George');

myRabbit.walk();
// Bunny George goes for a walk.

прототипичный

var animalMethods =  {
  walk: function walk() {
    console.log(this.name + ' goes for a walk.');
  }
};

var animal = function animal(name) {
  var instance = Object.create(animalMethods);
  instance.name = name;
  return instance;
};

var rabbitMethods = {
  jump: function jump() {
    console.log(this.name + ' hops around a bit.');
  }
};

var rabbit = function rabbit(name) {
  var proto = rabbitMethods;

  // This is more commonly done like mixin({}, animalMethods, rabbitMethods);
  // where mixin = $.extend, _.extend, mout.object.mixIn, etc... It just copies
  // source properties to the destination object (first arg), where properties from
  // the last argument override properties from previous source arguments.
  proto.walk = animalMethods.walk;
  var instance = Object.create(rabbitMethods);

  // This could just as easily be a functional mixin,
  // shared with both animal and rabbit.
  instance.name = name;
  return instance;
};

var rabbit2 = rabbit('Bunny Bob');

rabbit2.walk();
// Bunny Bob goes for a walk.

Количество требуемого кода довольно схоже, но для меня гораздо яснее, что делают прототипы, и оно гораздо более гибкое, и в нем нет классического наследственного артритического багажа первого примера.

ответил Eric Elliott 2 MonEurope/Moscow2013-12-02T20:21:46+04:00Europe/Moscow12bEurope/MoscowMon, 02 Dec 2013 20:21:46 +0400 2013, 20:21:46
0

Спасибо за ваш удивительный ответ. Но я не согласен с большинством ваших утверждений. Без видеть эквивалент в обоих шаблонах. Некоторые аргументы субъективны для меня. Поэтому я хотел бы сосредоточиться на фактах. И в этом ответе я собираюсь прокомментировать, какие из них являются лучшими. И без внешних библиотек /фрагментов, потому что тогда мы можем бороться, какая библиотека лучше.

Хорошие моменты о Object.create

1. Создайте экземпляр /объект, используя call /apply

var Shape = {
    Rectangle: function (p1, p2) {
        var rectangle = Object.create(this);
        rectangle.p1 = p1;
        rectangle.p2 = p2;
        return rectangle;
    },
    Circle: function (p, r) {
        var circle = Object.create(this);
        circle.p = p;
        circle.r = r;
        return circle;
    }
};

var rectangle = Shape.Rectangle.call(Shape, zeroPoint, Point.new(3, 4));
var circle = Shape.Circle.call(Shape, origin, 3);

Это невозможно, используя new.

Хорошие отзывы о new

1. Меньше кода

function Rectangle(p1, p2) {
    this.p1 = p1;
    this.p2 = p2;
}
function Circle(p, r) {
    this.p = p;
    this.r = r;
}

против

Rectangle: function (p1, p2) {
    var rectangle = Object.create(this);
    rectangle.p1 = p1;
    rectangle.p2 = p2;
    return rectangle;
},
Circle: function (p, r) {
    var circle = Object.create(this);
    circle.p = p;
    circle.r = r;
    return circle;
}

В любом случае вам всегда нужно написать больше кода для реализации того же самого.

2. Проще поддерживать

var rectangle = Shape.Rectangle(zeroPoint, Point.new(3, 4));
var circle = Shape.Circle(origin, 3);

Что произойдет, если завтра вы захотите изменить имя Shape на Geometry? Вы должны просмотреть весь свой код и изменить все слова Shape для каждого экземпляра Cicle или прямоугольника. Этот момент более примечателен, когда вы занимаетесь наследованием. Потому что вам всегда нужно вызывать одно и то же имя конструктора, чтобы получить доступ к super.methods

Bonus. Легче читать или понимать (это субъективно)

Если в моем коде я вижу new Rectangle(...), я знаю, что создаю новый экземпляр объекта Rectangle. Однако Shape.Rectangle(...) не сообщает мне, является ли это новый объект, или если это просто функция, или Shape является уникальным экземпляром, таким как var Shape = new Whatever()

3. Частная собственность и методы

var Person = function() {
  var a = 5;
  this.method = function (b) { return a*b; };
};
var obj = new Person;
obj.method(5); // return 25
obj.a; // return undefined

против

var Person = {
    a: 5,
    method: function (b) { return this.a*b; }
};
var obj  = Object.create(Person);
obj.method(5); // return 25
obj.a; // return 5

Вы всегда можете иметь закрытые методы и свойства в шаблоне new. Вы можете использовать шаблон object.create, если ваша реализация специфична Если вы сделаете это, ваш код будет сложнее и многословнее написать (но это личное мнение).

4. Я могу передать параметр на конструктор

var Person = function(a) {
  this.method = function (b) { return a*b; };
};
var obj = new Person(5);
obj.method(5); // return 25

против

var Person = {
    method: function (b) { return a*b; }
};
var obj  = Object.create(Person);
obj.method(4); //Error

5. InstanceOf

Нет способа сделать instanceof изначально с шаблоном Object.create.

6. Производительность

Я оставляю это для последнего, потому что большинство других вопросов могут быть решены с помощью дополнительного JavaScript. Но в этом случае нельзя совпасть с шаблоном new. Также я думаю, что это самое важное преимущество для шаблона new. Потому что, если вы программируете для браузера, производительность иногда не имеет значения. Но если вы работаете с бэкэндом и создаете очень большое и масштабируемое приложение, производительность имеет значение. Почему Paypal покидает Java и переходит на node.js? Поскольку производительность очень высока важно в больших проектах.

Заключение

Итак, если new, это в 10 раз быстрее, чем Object.create. Я думаю только по этой причине, и только по этой причине стоит продолжать программировать с новым. Кроме того, дайте мне больше гибкости, и я могу делать что-то с шаблоном new, чего я не могу с Object.create. И я согласен, что природа Javascript заключается в использовании Object.create. Но я думаю, что получу больше преимуществ, если буду использовать new.

Извините за мой английский.

ответил EnZo 1 SunEurope/Moscow2013-12-01T19:40:55+04:00Europe/Moscow12bEurope/MoscowSun, 01 Dec 2013 19:40:55 +0400 2013, 19:40:55

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132