Методы определения объектов Javascript, плюсы и минусы - PullRequest
14 голосов
/ 01 июня 2011

Каковы основные способы определения повторно используемых объектов в Javascript? Я говорю, что можно многократно использовать, чтобы исключить одноэлементные методы, такие как непосредственное объявление переменной с литеральной нотацией объекта. Я где-то видел, что Крокфорд определяет четыре таких способа в своих книгах, но я бы предпочел не покупать книгу для этой короткой информации.

Вот способы, с которыми я знаком:

  • Использование this и построение с new (я думаю, это называется классическим?)

    function Foo() {
        var private = 3;
        this.add = function(bar) { return private + bar; }
    }
    
    var myFoo = new Foo();
    
  • Использование прототипов, аналогичных

    function Foo() {
        var private = 3;
    }
    Foo.prototype.add = function(bar) { /* can't access private, correct? */ }
    
  • Возвращение литерала без использования this или new

    function Foo() {
        var private = 3;
        var add = function(bar) { return private + bar; }
        return {
            add: add
        };
    }
    
    var myFoo = Foo();
    

Я могу думать об относительно незначительных вариациях, которые, вероятно, не имеют никакого значения. Какие стили мне не хватает? Что еще более важно, каковы плюсы и минусы каждого? Рекомендуется ли придерживаться того или иного, или это вопрос предпочтений и священной войны?

Ответы [ 5 ]

14 голосов
/ 01 июня 2011

Используйте прототип.Возврат определенных объектов из конструкторов делает их неконструкторами, а присвоение методов this делает наследование менее удобным.

Возвращение литерала объекта

Плюсы:

  • Если человек забудет new, он все равно получит объект.

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

Минусы:

  • Это не настоящий конструктор.Добавление чего-либо к его прототипу не изменит возвращаемых объектов, new или нет new.new Foo() instanceof Foo также приведет к false.

Использование prototype

Плюсы:

  • Вы оставите конструктор незагроможденным.

  • Это стандартный способ выполнения задач в JavaScript, и все встроенные конструкторы помещают свои методы в свои прототипы.

  • Наследование становитсяпроще и правильнее;Вы можете (и должны) использовать Object.create(ParentConstructor.prototype) вместо new ParentConstructor(), а затем позвонить ParentConstructor из Constructor.Если вы хотите переопределить метод, вы можете сделать это на прототипе.

  • Вы можете «изменить» объекты после того, как они уже созданы.

  • Вы можете расширить прототипы конструкторов, к которым у вас нет доступа.

Минусы:

  • Можетбыть слишком многословным, и если вы хотите изменить имя функции, вы должны также изменить все функции, добавленные в прототип.(Либо это, либо определите прототип как один литерал большого объекта с дескриптором совместимого свойства для constructor.)

  • Они не разделяют специфичную для экземпляра область, поэтому вы можете 'у него действительно есть частные переменные.

Назначение this.* в конструкторе

Плюсы:

  • Вы можете использовать замыкания и, следовательно, частныепеременные-члены.

Минусы:

  • Утиная печать отсутствует;Вы не можете вызвать метод сразу из прототипа с любым старым объектом.Например, Array.prototype.slice.call(collectionLikeObject).
4 голосов
/ 01 июня 2011

Это в основном вопрос предпочтений.Не существует одного способа приготовления супа с куриной лапшой, и однородные объекты одинаковы.

Я не использую ни одного из этих трех, хотя они все работают в своих собственных целях.Я использую пользовательскую функцию под названием Object: deploy, и использую ее следующим образом ..

var a = { hey: 'hello' },
    b = {}.deploy(a);

console.log(b.hey); // 'hello'

Использование prototype лучше всего подходит для большинства людей из-за автоматического ручного ввода.

function A() {};
A.prototype.hello = "Hey";

var a = new A();
console.log(a.hello); // 'Hey'

A.prototype.hello = "Hello";

console.log(a.hello); // 'Hello'

И вопреки распространенному мнению, вы можете использовать закрытые переменные в prototype.

function Hello() {};

(function () {
    var greeting = "Hello";

    Hello.prototype.greet = function () { return greeting };
}).apply(this);

Но даже если это возможно, обычно это лучше сделать ..

function Hello() {};
Hello.prototype.greeting = "Hello";
Hello.prototype.greet = function () { return this.greeting };
2 голосов
/ 01 июня 2011

Ну, второй подход (prototype) больше похож на стандартные классы в других языках, таких как Python, в том, что у вас есть общий объект «prototype», которым делятся все экземпляры.Чтобы сравнить первый и второй подходы:

  • В подходе 1 каждый раз, когда вы вызываете "new Foo()", вы создаете новый объект и вставляете все методы.Это не очень эффективно по времени или пространству, поскольку каждый экземпляр Foo будет иметь свою собственную таблицу всех методов.Вы можете проверить это, создав два объекта Foo и задав foo1.add == foo2.add (false).Подход 3 очень похож на этот;Я не уверен, какова семантическая разница (если есть) между подходами 1 и 3.
  • В подходе 2 вы создали общий объект-прототип, содержащий все методы.Если вы спросите foo1.add == foo2.add, вы получите правду.Это более экономно по времени и времени.Это также позволяет вам добавить больше методов к прототипу после создания экземпляров, и они увидят новые методы.

Проблема с подходом 2, как вы говорите, заключается в том, что вы не можете получить доступ к частномучлены.Но вы все равно можете добавить не приватные члены к самому объекту и получить доступ к тем, используя методы-прототипы:

function Foo() {
    this.private = 3;
}
Foo.prototype.add = function(bar) { return this.private + bar }

Предостережение заключается в том, что foo.private виден снаружи.

1 голос
/ 01 июня 2011

У прототипа нет накладных расходов на экземпляр объекта, а только накладных расходов на класс для функции add. Это одно из причин, почему мне не нравятся два других подхода.

0 голосов
/ 01 июня 2011

Не забывай о наследовании.Когда-нибудь тебе понадобится.А для организации наследования лучшим подходом является комбинированная техника:

function Foo() {
    this.array = [1, 2, 3];
    this.add = function(bar) { return private + bar; }
}

Foo.prototype.add = function(bar) {  }

var myFoo = new Foo();

Установка полей в конструкторе полезна, чтобы избежать их изменения дочерними объектами.Устанавливать методы для создания прототипов быстрее, чем делать это каждый раз в конструкторе.

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