Путаница о том, как создавать классы в JavaScript

В прошлом при создании "классов" в JavaScript я делал это следующим образом:

function Dog(name){
    this.name=name;
    this.sound = function(){
        return "Wuf";
    };
}

Однако я просто видел, что кто-то делает это так:

var Dog = (function () {
    function Dog(name) {
        this.name = name;
    }
    Dog.prototype.sound = function () {
        return "Wuf";
    };
    return Dog;
})();

Можете ли вы сделать это в обоих направлениях или так, как я сделал это неправильно? В таком случае, почему? И в чем же разница между двумя с точки зрения того, в чем мы заканчиваем? В обоих случаях мы можем создать объект, сказав:

var fido = new Dog("Fido");
fido.sound();

Надеюсь, кто-то просветит меня.

Ответ 1

Есть два важных отличия между вашим путем и их.

  • Обтекание функцией самозапуска ((function() { ... })();)
  • Использование свойства .prototype над this. для методов.

Облечение вещей в функцию self-invoking, а затем назначение результата (как определено в инструкции return для переменной, называется модулем pattern Это общий шаблон для обеспечения большей контролируемости области.

Использование Dog.prototype.sound = function() {} предпочтительнее до this.sound = function(). Разница в том, что Dog.prototype.sound определяется один раз для всех объектов с конструктором Dog, а this.sound = function() {} определяется снова для каждого созданного объекта.

Правило большого пальца: Вещи, которые являются индивидуальными для объекта (обычно его свойства), должны быть определены на this, тогда как вещи, которые являются общими для всех объектов одного и того же типа (обычно это функции), должны быть определены на прототипе.

Ответ 2

С помощью вашего кода вы создаете новую функцию sound для каждого нового создаваемого экземпляра Dog. Javascript prototype избегает этого, создавая только одну функцию, которую разделяют все экземпляры объекта; в основном классическое наследование.

Во втором коде вы показываете, что это просто дополнительно завернуто в IIFE, что в этом случае мало что делает.

Ответ 3

Первый - это традиционный метод создания конструктора. Второе сразу вызывается выражение функции, которое возвращает конструктор. Этот метод позволяет сохранять переменные в модуле без утечки в глобальную область, которая может быть проблемой.

И в чем же разница между двумя в терминах того, что мы заканчиваем?

Оба они, как вы видели, имеют одинаковый результат. Другие говорили о prototype, поэтому я не буду упоминать об этом здесь.

Ответ 4

Второй предпочтительнее, потому что он использует механизм прототипа на основе Javascript.

Прототипы

Наследование Javascript является причиной путаницы, но на самом деле это довольно просто: каждый объект имеет прототип, который является объектом, который мы будем проверять при попытке доступа к свойству, а не к исходному объекту. Прототип сам будет иметь прототип; в простом случае, например Dog, это, вероятно, будет Object.prototype.

В обоих примерах из-за того, как работает оператор new мы получим цепочку прототипов, которая выглядит так:: fido->Dog.prototype->Object.prototype. Итак, если мы попытаемся найти свойство name на Fido, мы найдем его прямо на объекте. Если, с другой стороны, мы ищем свойство hasOwnProperty, мы не сможем найти его на Fido, не найдем его на Dog.prototype, а затем достигнем Object.prototype, где мы его найдем.

В случае sound ваши примеры определяют его в двух разных местах: в первом случае fido и каждая другая собака, которую мы создаем, будет иметь свою собственную копию функции. Во втором случае Dog.prototype будет иметь одну копию функции, к которой будут обращаться отдельные собаки при вызове метода. Это позволяет избежать потерь ресурсов при сохранении дубликатов функции sound.

Это также означает, что мы можем расширить цепочку прототипов; возможно, нам нужен класс Corgi, который наследует функцию sound от Dog. Во втором случае мы можем просто убедиться, что Dog.prototype находится в цепочке прототипов Corgi.prototype; в первом случае нам нужно будет создать настоящую Собака и поместить ее в цепочку прототипов.