OO Шаблон конструктора Javascript: неоклассический или прототип - PullRequest
43 голосов
/ 27 ноября 2009

Я смотрел разговор Дугласа Крокфорда о хороших деталях в Javascript и моих глазах были открыты. В какой-то момент он сказал что-то вроде: «Javascript - единственный язык, на котором хорошие программисты считают, что могут использовать его эффективно, не изучая его». Тогда я понял: Я тот парень.

В этом выступлении он сделал несколько заявлений, которые для меня были довольно удивительными и проницательными. Например, JavaScript - самый важный язык программирования на планете. Или это самый популярный язык на планете. И что это сломано во многих серьезных отношениях.

Самое удивительное заявление, которое он сделал для меня, было «новое опасно». Он больше не использует это. Он также не использует this.

Он представил интересный шаблон для конструктора в Javascript, который допускает закрытые и открытые переменные-члены и не использует ни new, ни this. Это выглядит так:

// neo-classical constructor
var container =  function(initialParam) {
    var instance = {}; // empty object 

    // private members
    var privateField_Value = 0;
    var privateField_Name = "default";

    var privateMethod_M1 = function (a,b,c) {
        // arbitrary
    }; 

    // initialParam is optional
    if (typeof initialParam !== "undefined") {
        privateField_Name= initialParam;
    }

    // public members
    instance.publicMethod = function(a, b, c) {
        // because of closures,
        // can call private methods or
        // access private fields here. 
    };

    instance.setValue = function(v) {
        privateField_Value = v;
    };

    instance.toString = function(){
        return "container(v='" + privateField_Value + "', n='" + privateField_Name + "')";
    };

    return instance;
}


// usage
var a = container("Wallaby");
WScript.echo(a.toString()); 
a.setValue(42);
WScript.echo(a.toString()); 

var b = container();
WScript.echo(b.toString()); 

РЕДАКТИРОВАТЬ : код обновлен для переключения на строчное имя класса.

Эта модель произошла от более ранних моделей использования Крокфорда .

Вопрос: Используете ли вы этот тип шаблона конструктора? Вы находите это понятным? У тебя есть лучший?

Ответы [ 7 ]

16 голосов
/ 27 ноября 2009

Это похоже на не-синглтонную версию шаблона модуля , в которой частные переменные можно моделировать, используя преимущества «замыканий» JavaScript.

Мне это нравится ( вроде ... ). Но я на самом деле не вижу преимущества в частных переменных, выполняемых таким образом, особенно когда это означает, что любые новые добавленные методы (после инициализации) не имеют доступа к закрытым переменным.

Кроме того, он не использует преимущества прототипной модели JavaScript. Все ваши методы и свойства должны быть инициализированы КАЖДЫЙ раз, когда вызывается конструктор - этого не произойдет, если у вас есть методы, хранящиеся в прототипе конструктора. Дело в том, что использование обычного шаблона конструктор / прототип намного быстрее! Неужели вы думаете, что частные переменные оправдывают себя?

Этот тип модели имеет смысл с модульным шаблоном, потому что он инициализируется только один раз (для создания псевдо-синглтона), но я не уверен, что здесь имеет смысл.

Используете ли вы этот тип шаблона конструктора?

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

Вы находите это понятным?

Да, он читабелен и довольно понятен, но мне не нравится идея объединить все в конструкторе таким образом.

У вас есть лучший?

Если вам действительно нужны приватные переменные, то всегда придерживайтесь их. В противном случае просто используйте обычный шаблон конструктора / прототипа (, если вы не разделяете страх Крокфорда по поводу new / this комбо ):

function Constructor(foo) {
    this.foo = foo;
    // ...
}

Constructor.prototype.method = function() { };

Другие подобные вопросы, относящиеся к взглядам Дуга по теме:

10 голосов
/ 27 ноября 2009

Я избегаю этого паттерна, так как большинству людей труднее читать. Я обычно придерживаюсь двух подходов:

  1. Если у меня есть только одна вещь, я использую анонимные объекты:

    var MyObject = {
        myMethod: function() {
            // do something
        }
    };
    
  2. Для более чем одного я использую стандартное наследование Javascript-прототипа

    var MyClass = function() {
    };
    MyClass.prototype.myMethod = function() {
        // do something
    };
    
    var myObject = new MyClass();
    

(1) намного легче читать, понимать и писать. (2) более эффективно, когда есть несколько объектов. Код Крокфорда будет каждый раз создавать новую копию функций внутри конструктора. Закрытие также имеет недостаток в том, что его сложнее отлаживать.

Несмотря на то, что вы теряете по-настоящему приватные переменные, в качестве соглашения вы ставите префикс «якобы для приватности» с _.

this является, по общему признанию, трудной проблемой в javascript, но ее можно обойти, используя .call и .apply, чтобы правильно ее настроить. Я также часто использую var self = this;, чтобы создать переменную замыкания для использования в качестве this внутри функций, определенных в функции-члене.

MyClass.prototype.myMethod = function() {
    var self = this;

    // Either
    function inner1() {
        this.member();
    }
    inner1.call(this);

    // Or
    function inner2() {
        self.member();
    }
    inner2();
};
7 голосов
/ 28 ноября 2009

Используете ли вы этот тип шаблона конструктора?

Неа

Вы находите это понятным?

Да, это очень прямо вперед.

У вас есть лучший?

Я еще не смотрел этот разговор, но скоро к нему вернусь. До тех пор я не вижу опасности использования new и this, и вот почему:

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

В любом случае, когда я не использую какую-то платформу, которая поддерживает автоматическое наследование (например, dojo.declare), или когда пишу объект, независимый от каркаса, я в настоящее время использую следующий подход.

Определение:

var SomeObject = function() {
    /* Private access */
    var privateMember = "I am a private member";
    var privateMethod = bindScope(this, function() {
        console.log(privateMember, this.publicMember);
    });

    /* Public access */
    this.publicMember = "I am a public member";

    this.publicMethod = function() {
        console.log(privateMember, this.publicMember);
    };
    this.privateMethodWrapper = function() {
        privateMethod();
    }
};

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

var o = new SomeObject();
o.privateMethodWrapper();

Где bindScope - служебная функция, аналогичная dojo.hitch Додзё или Function.prototype.bind прототипа *.

6 голосов
/ 27 ноября 2009

В его книге это называется функциональное наследование (стр. 52). Я еще не использую это. Это понятно, если вы узнаете JavaScript от Дугласа. Я не думаю, что есть лучший подход. Это хорошо, потому что это:

  • позволяет программисту создавать личные элементы

  • защищает частных участников и исключает этот в отличие от притворной конфиденциальности (частные участники начинаются с _)

  • делает наследование гладким и без лишнего кода

Однако у него есть некоторые недостатки. Подробнее об этом вы можете прочитать здесь: http://www.bolinfest.com/javascript/inheritance.php

1 голос
/ 06 мая 2015

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

Также мы можем хранить частные объекты; посмотрите на этот подход:

var ConstructorName = (function() { //IIFE
    'use strict';

    function privateMethod (args) {}

    function ConstructorName(args) {
        // enforces new (prevent 'this' be the global scope)
        if (!(this instanceof ConstructorName)) {
            return new ConstructorName(args);
        }
        // constructor body
    }

    // shared members (automatic delegation)
    ConstructorName.prototype.methodName = function(args) {
        // use private objects
    };

    return ConstructorName;
}());

Я рекомендую просмотреть этот ответ, где мы можем найти интересный способ создания функции конструктора: Другой способ создания объекта Javascript

Это пример, где вы можете использовать подход конструктора-прототипа: Как создать пользовательскую ошибку в JavaScript?

1 голос
/ 27 ноября 2009

Используете ли вы этот тип шаблона конструктора?

Я использовал этот шаблон раньше, когда впервые изучал JavaScript и наткнулся на литературу Дугласа Крокфорда.

Вы находите это понятным?

Пока вы понимаете замыкания, этот метод понятен.

У вас есть лучший?

Это зависит от того, чего вы пытаетесь достичь. Если вы пытаетесь написать библиотеку, которая будет настолько идиотской, насколько это возможно, я могу полностью понять использование частных переменных. Это может помочь предотвратить непреднамеренное нарушение целостности вашего объекта пользователями. Да, временные затраты могут быть немного больше (примерно в 3 раза больше), но временные затраты являются спорным аргументом, пока они фактически не влияют на ваше приложение. Я заметил, что тест, упомянутый в предыдущем посте ( test ), не учитывает, сколько времени требуется для доступа к функциям объекта.

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

Я, как правило, не люблю использовать закрытые переменные, если я не пишу свой код для большой аудитории. Если я работаю с командой людей, которые все способны к программированию, я, как правило, могу поверить, что они будут знать, как использовать мой код, если я сам буду кодировать и комментировать. Затем я использую что-то похожее на шаблон Крокфорда без частных членов / методов.

0 голосов
/ 08 октября 2011

Подумайте, это может помочь обсуждению сообщить здесь, что является основным моментом в этом паттерне, как объяснил Дуглас Крокфорд в своем видео-презентации «Расширенный JavaScript».

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

В этом суть паттерна Powerconstructor.

Частная / привилегированная часть является вторичной. Это нормально делать по-другому. Так что члены-прототипы не используются.

HTH LuKa

...