2012-04-16 1 views
4

Пожалуйста, помогите мне решить, следует ли использовать объект-прототип функции и «новое» ключевое слово или полностью избегать функций-конструкторов.Архитектура кода JavaScript - использование функций конструктора или не

Ситуация:

Функция называется widget(), который будет вызываться в 10-15 раз для инициализации каждого виджета на странице. widget() содержит немало внутренних методов.

Каждый раз, когда вызывается widget(), функция должна возвращать объект, который действует как API для работы с виджетами.

Вопрос

1) Должен ли я поставить все внутренние методы внутри Widget() под его собственности прототипа? Это не имеет смысла, но главная причина этого заключается в том, чтобы не повторно создавать внутренние функции каждый раз, когда вызывается widget().

Но если я ввожу внутренние функции в прототипе, каждый экземпляр объекта w () имеет доступ к внутренним частным методам.

2) Если я останусь в стороне от функций конструктора и ключевого слова new и структурирую свой код, как показано ниже, как я могу исправить проблему производительности внутренних функций, получающих повторное создание экземпляров каждый раз, когда вызывается widget().

function widget() 
{ 
    var returnObj = {}; 

    /* Add internal functions but this will be re-instantiated every time */ 

    return returnObj; 

} 
+0

15 объектов вряд ли вызовут проблему. Я, однако, предпочитаю оставить методы на прототипе –

+0

Зачем беспокоиться об инкапсуляции? Это javascript, а не .NET или Java. –

+0

@CesarCanassa encapsulation может быть просто организационным инструментом. Кто-то, смотрящий на код, может легко сказать, что инкапсулированные вещи используются только вокруг вещей. Конечно, это не проблема, если вы используете несколько исходных файлов и каким-то образом склеиваете их, вы можете просто минимизировать все и обернуть их в IIFE. –

ответ

4

У вас есть немного компромисс здесь. Как вы, кажется, уже понимаете, методы, которые вы ставите на .prototype, общедоступны, но это наиболее эффективные места для размещения методов, поскольку они автоматически добавляются ко всем новым копиям этого объекта очень эффективным образом. При использовании методов .prototype существует только одна копия ваших методов, и ссылка на эту единственную копию автоматически добавляется ко всем новым экземплярам этого объекта.

Но javascript не имеет встроенных встроенных методов, и единственное обходное решение для этого включает в себя не использование .prototype для них или для любых методов, которые необходимо вызвать частными методами.

Это article by Doug Crockford - довольно хорошее описание того, как вы можете создавать конфиденциальность для данных или методов в любом объекте.

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

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

В моем кодировании я обычно не применяю конфиденциальность, и я использую .prototype и new. Я назначил «непубличные» методы на прототипе, начав их имя с подчеркивания. Это условное соглашение, а не схема принудительного доступа.

В ответ на ваш второй вопрос об исключении оператора new и способах восстановления, я бы просто спросил, почему вы это делаете? Что вы набираете? Я не знаю о каких-либо недостатках использования new. Насколько я понимаю, ваше решение о том, следует ли использовать .prototype против ручного создания/назначения методов в вашем конструкторе, должно быть связано с необходимостью использования частных методов.

FYI, 15 объектов вряд ли создадут существенную разницу в производительности в любом случае здесь. Я бы оценил вашу потребность в подлинной конфиденциальности и принял решение на основе этого. Если вам необходимо обеспечить конфиденциальность, перейдите к методу Crockford для реализации частных методов. Если у вас нет настоящей конфиденциальности, используйте .prototype. Я не вижу здесь причины, чтобы избежать использования new в любом случае.

+0

Спасибо за ваш очень проницательный ответ. Чтобы ответить на ваш вопрос относительно отклонения «нового» ключевого слова, я понял, что наибольшее преимущество «нового» ключевого слова заключалось в передаче объектов-прототипов конструктора для каждой переменной, которая вызывает функцию с «новым». Если я решила не использовать прототип, я подумал, зачем даже беспокоиться о «новом» и «виджете вызова»() без него. Не использовать «новый» кажется более родным JS. – user715489

+1

Использование 'new' отлично« родной »JS. Я нахожу использование 'new' более семантически описательным в моем коде. Для читателя кода более очевидно, что создается новый объект определенного типа. Когда вы не используете 'new', читатель должен знать, что возвращает ваша функция, чтобы знать, что делает эта строка кода. – jfriend00

0

Вы можете использовать шаблон метаконструктора *, чтобы обойти это.

function defineCtor(metaCtor) { 

    var proto = new metaCtor(); 

    var ctor = proto.hasOwnProperty('constructor') ? 
      proto.constructor : new Function(); 

    return (ctor.prototype = proto).constructor = ctor; 

} 

Теперь у вас есть функция, которая строит конструктор (или более точно строит прототипы и возвращает конструктор).

var Widget = defineCtor(function() { 

    function doInternalStuff() { 

    // ...cant see me 

    } 

    // this function ends up on the prototype 

    this.getFoo = function() { return doInternalStuff(); }; 

}); 

// ... 

var myWidget = new Widget(); 

Объяснение

defineCtor принимает один анонимную функцию как свойство. Он вызывает функцию с new, создавая объект. Он присваивает объект в качестве свойства прототипа новой функции конструктора (либо пустой функции, либо собственного свойства объекта constructor собственного объекта прототипа), и возвращает эту функцию.

Это обеспечивает замыкание для внутренних функций, адресации вопроса 1 и настраивает пару конструктор/прототип для вас, обращаясь к Question 2.


Сравнение

Сравнить defineCtor техники к следующим двум примерам.

В этом примере используется прототип и имеет проблему 1: внутренняя информация не инкапсулирована.

function Widget(options) { 
    this.options = options; 
} 

Widget.prototype = { 

    getFoo: function() { 
    return doInternalStuff(); 
    } 

}; 

// How to encapsulate this? 
function doInternalStuff() { /* ... */ } 

Этот пример устанавливает все в конструкторе, и имеет проблемы 2: каждый раз, когда он создает объект, он конкретизирует новые функциональные объекты для каждого свойства.

function Widget(options) { 

    this.options = options; 

    function doInternalStuff() { /* ... */ } 

    this.getFoo = function() { 
    return doInternalStuff(); 
    }; 

} 

В этом примере используется метод, описанный выше, чтобы обеспечить герметизацию при этом используя прототип:

var Widget = defineCtor(function() { 
    //     ^
    // This function runs once, constructing the prototype. 

    // In here, `this` refers to the prototype. 

    // The real constructor. 
    this.constructor = function(options) { 

    // In function properties, `this` is an object instance 
    // with the outer `this` in its prototype chain. 

    this.options = options; 

    }; 

    function doInternalStuff() { /* ... */ } 

    this.getFoo = function() { return doInternalStuff(); }; 

}); 

// ... 

var myWidget = new Widget(); 

Этот подход имеет несколько преимуществ, некоторые более очевидными, чем другие.

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

  • Это расширяемый. Вы можете дать своим «метаконструкторам» свои собственные прототипы, с такими функциональными свойствами, как «extends», «mixin» и т. Д. Затем внутри тела metaCtor вы можете писать такие вещи, как this.extends(BaseWidget). API defineCtor никогда не нуждается в изменении, чтобы это произошло.

  • Это «трюки» компилятора Google Closure, Eclipse, jsdoc и т. Д., Чтобы думать, что вы определяете фактическую функцию-конструктор, а не «мета-функцию». Это может быть полезно в определенных ситуациях (код «самодокументирован», как это понимают эти инструменты).

* Насколько я знаю, слово "metaconstructor" полностью составлен.

+0

Кажется, я очень смущен тем, что пытается сделать defineCtor. Пожалуйста, уточните или укажите на какой-то ресурс, который обсуждает этот шаблон метаконтерструктора. Большое спасибо. – user715489

+0

@ user715489 Я сделал некоторые изменения, дайте мне знать, если это вообще очистит. Я не видел, чтобы этот метод обсуждался много, я не уверен, что это за имя, или если оно есть. Я просто называю это «метаконструктором», потому что это звучит круто. –

Смежные вопросы