Выбор шаблона ООП в JavaScript - PullRequest
8 голосов
/ 14 июня 2011

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

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

Шаблон 1 (на основе объектов):

var mouseDiff = {
    "startPoint" : {"x" :0, "y" : 0},
    "hypotenuse" : function(a,b) {
        // do something
    },
    "init"       : function(){
        // do something
    }
}

mouseDiff.init();

Шаблон 2 (самый традиционный, насколько мне известно):

function MouseDiff() {
    this.startPoint = {"x" :0, "y" : 0};
}

MouseDiff.prototype.hypotenuse = function(a,b) {
    // do something
}

MouseDiff.prototype.init = function() {
    // do something
}

var myMouse = new MouseDiff;
myMouse.init();

Шаблон 3 (Использование замыкания):

var MouseDiff2 = (function() {
    var startPoint = {"x" :0, "y" : 0};
    var hypotenuse = function(a,b) {
        // do something
    };
    return {
        hypotenuse: hypotenuse,
        init : function(){
            // do something
        }
    };

}());
MouseDiff2.init();

Ответы [ 6 ]

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

Шаблон 1 - синглтон.Если вам нужен только один такой объект, это нормально.

Шаблон 2 создает новые объекты и использует объект prototype, так что при создании нового объекта MouseDiff он не будет создавать новыйкопии функций (которые сами по себе являются данными в JavaScript).

Шаблон 3 требует больше памяти по сравнению с обычным синглтоном, но обеспечивает статическую конфиденциальность.охватывает различные функции, хотя на самом деле это комбинация конструктора (шаблон 2) и замыкания (шаблон 3):

var MouseDiff = (function () {

    var aStaticVariable = 'Woohoo!';
    // And if you really need 100% truly private instance
    // variables which are not methods and which can be
    // shared between methods (and don't mind the rather
    // big hassle they require), see
    // http://brettz9.blogspot.com/search?q=relator
    // (see also the new plans for a Map/WeakMap in ECMAScript)

    function _APrivateStaticMethod () {
        alert(aStaticVariable);
    }

    // An instance method meant to be called on the
    //   particular object as via ".call(this)" below
    function _APrivateInstanceMethod () {
        alert(this.startPoint.x);
    }

    // Begin Constructor
    function MouseDiff() {
        this.startPoint = {"x" :0, "y" : 0};
    }

    MouseDiff.prototype.hypotenuse = function(a,b) {
        // do something
    };

    MouseDiff.prototype.init = function() {
        // do something
        _APrivateStaticMethod(); // alerts 'Woohoo!'
        _APrivateInstanceMethod.call(this); // alerts 0 (but if not
        // called with this, _APrivateInstanceMethod's internal
        // "this" will refer (potentially dangerously) to the
        // global object, as in the window in the browser unless
        // this class was defined within 'strict' mode in which
        // case "this" would be undefined)
    };

    return MouseDiff;
}());

var myMouse = new MouseDiff;
myMouse.init();
1 голос
/ 14 июня 2011

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

Шаблон 1 создает один объект с этими свойствами, включая присоединенные методы.Шаблон 2 позволяет нам легко создавать множество объектов с присоединенными одинаковыми методами, не переписывая их.

Шаблон 3 похож на фабрику.Вместо того чтобы полагаться на прототип для автоматического присоединения этих методов, фабрика просто создает их заново и возвращает объект.Использование замыкания позволяет нам скрыть «переменные-члены» объекта.Нет никакого способа получить доступ к startPoint или hypotenuse(), кроме как через возвращенный "публичный" интерфейс.

Всякий раз, когда я отвечаю на эти теоретические вопросы JavaScript, я всегда боюсь, что есть некоторые технические детали, которые я забылвид.Если это так, дайте мне знать, и я исправлю ответ.

0 голосов
/ 20 августа 2014

На это уже много раз отвечали, но только для того, чтобы предложить немного разнообразия. ds.oop - это хороший способ объявить классы с конструкторами в javascript. Он поддерживает каждый возможный тип наследования (включая 1 тип, который даже c # не поддерживает), а также интерфейсы, что приятно.

var Color = ds.make.class({
    type: 'Color',
    constructor: function (r,g,b) { 
        this.r = r;                     /* now r,g, and b are available to   */
        this.g = g;                     /* other methods in the Color class  */
        this.b = b;                     
    }
});
var red = new Color(255,0,0);   // using the new keyword to instantiate the class
0 голосов
/ 20 августа 2014

http://www.jsoops.net/ вполне достаточно для упс в Js. Если предоставить частную, защищенную, публичную переменную и функцию, а также функцию наследования. Пример кода:

var ClassA = JsOops(function (pri, pro, pub)
{// pri = private, pro = protected, pub = public

    pri.className = "I am A ";

    this.init = function (var1)// constructor
    {
        pri.className += var1;
    }

    pub.getData = function ()
    {
        return "ClassA(Top=" + pro.getClassName() + ", This=" + pri.getClassName()
        + ", ID=" + pro.getClassId() + ")";
    }

    pri.getClassName = function () { return pri.className; }
    pro.getClassName = function () { return pri.className; }
    pro.getClassId = function () { return 1; }
});

var newA = new ClassA("Class");

//***Access public function
console.log(typeof (newA.getData));
// function
console.log(newA.getData());
// ClassA(Top=I am A Class, This=I am A Class, ID=1)

//***You can not access constructor, private and protected function
console.log(typeof (newA.init));            // undefined
console.log(typeof (newA.className));       // undefined
console.log(typeof (newA.pro));             // undefined
console.log(typeof (newA.getClassName));    // undefined
0 голосов
/ 16 февраля 2014

Есть еще один возможный способ сделать это.

var MouseDiff = {};
(function(context) { 
    var privateVarialble = 0;

    context.hypotenuse = function() {
         //code here    
    };

    context.int = function() {
      //code here    
    }
})(MouseDiff); 

Здесь мы просто передаем пространство имен в качестве аргумента для самопризываемой функции. Переменная privateVarialble является закрытой, поскольку она не присваивается контексту.

Мы можем даже установить контекст для глобального объекта (с одним изменением слова!). Внутри скобки (MouseDiff) сделай это (this). Это большой актив для поставщиков библиотек, которые могут обернуть свои функции в самопризывающуюся функцию и предоставить пользователю самим решать, должны ли они быть глобальными или нет.

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

Шаблон два - мое личное предпочтение, поскольку он наиболее близок к традиционному объектно-ориентированному программированию и позволяет легко наследовать.

Этот пост Джона Резига (парня из JQuery) использует этот метод ... http://ejohn.org/blog/simple-javascript-inheritance/

[править]

На самом деле, я не думаю, что методы 1 или 3 имеют какие-либо преимущества. 1, как кто-то еще сказал, является синглтоном и не допускает нескольких экземпляров, а 3 ... я не знаю, с чего начать с 3.

...