Шаблон модуля Javascript - теряет сферу этого - PullRequest
3 голосов
/ 17 февраля 2012

Некоторое время работал над шаблоном модуля js, чтобы удовлетворить растущие потребности "какого-то веб-сайта".По сути, мне просто нужен хороший способ группировки / инкапсуляции скриптов, а также необходимость в OO-шаблонах.

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

(function (global) {
    var M = {
        VERSION : 1.0
    };

    global.M = M;

    //jQ document.ready()
    global.onload = function(){
        console.log('M VERSION: %s', M.VERSION);
        var main = new M.Main();
    };

    M.Main = function(){
        var _class1;
        var _class2;
        var _class3;

        function _init(){
            _class1 = new M.Class('foo','baz');
            console.log('_class1.param2 : %s', _class1.getParam() ); //works

            _class2 = new M.OtherClass('faz','boo');
            _class2.setParam('Boozaz');
            console.log('_class2.param2 : %s', _class2.getParam() ); //works

            _class3 = new M.Class('fuz','boz')
            console.log('_class3.param2 : %s', _class3.getParam() ); //works

            _class3.prototype = new M.Super();
            console.log('_class3.__param : %s', _class3.prototype.getProtected() ) //works
        }

        _init();
        return true;
    };

    M.Super = function(){
        var __param = 'jQ';
        M.Super.API = {
            getProtected : function(){ return __param }
        }
        return M.Super.API;
    }

    M.Class = function( p1, p2){
        var _param1;
        var _param2;

        function _init(){
            _param1 = p1;
            _param2 = p2;
        }

        function _getParam(){
            return _param2;
        }

        function _setParam(e){
            _param2 = e;
        }

        M.Class.API = {
            getParam : function(){ return _getParam(); },
            setParam : function(e){ _setParam(e) },
            publicMethod : function(){  ...  }
        }

        publicMethod() //fails
        this.publicMethod() //fails, this scopes to DOM window
        M.Class.API.publicMethod() // works, but is kludgy

        _init();
        return M.Class.API;
    };

})(typeof window === 'undefined' ? this : window);

Это создает удовлетворительную структуру DOM (при проверке через firebug) - но я теряю область действия this в одной конкретной области = вызывая "публичные" методывозвращенный объект, внутренне.

publicMethod() //fails
this.publicMethod() //fails, this scopes to DOM window
M.Class.API.publicMethod() // works, but kludgy syntax

На предыдущей итерации этого шаблона объект "class" самопроизвольно выполняется, и ссылка на this сохраняется:

    M.Class = function( p1, p2){
        var _param1;
        var _param2;
        var _root; 

        function _init(){
            _root   = this; //this gets referenced for later usage
            _param1 = p1;
            _param2 = p2;
        }

        function _getParam(){
            return _param2;
        }

        function _setParam(e){
            _param2 = e;
        }

        M.Class.API = {
            init     : function(){ _init();    },
            getParam : function(){ return _getParam(); },
            setParam : function(e){ _setParam(e) },
        }

        console.log('getParam, internal :%s', _root.getParam() ) //success
        return M.Class.API;
    }();

  M.Class.init();

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

Я прочитал много, много статей на довольно ошеломляющую тему лексической области видимости в js ... но не пришёл к выводу.

Как мне поддерживать область действияthis в моем обновленном шаблоне модуля?

Ответы [ 2 ]

2 голосов
/ 17 февраля 2012

Это один из тех философских вопросов, которые все задают при написании библиотеки или модуля: должны ли функции ссылаться на объект контейнера, используя this или имя переменной?Ответ: это зависит.

Если вы знаете, что функция всегда будет вызываться с правильным значением this (например, метод на прототипе), тогда используйте this.Однако, если функция может быть вызвана любым другим способом, используйте имя переменной.Если на более позднем этапе вы решите изменить имя, это довольно простое упражнение по поиску и замене.И звонить myLib.utils.someFn намного понятнее, чем звонить мне this.someFn .И если вы чувствуете, что это слишком много печатать, вы всегда можете вернуться к var sF = myLib.utils.someFn и перейти оттуда.

Редактировать

Чтобы ответить на ваши вопросы:

publicMethod() //fails

Тамесли в текущей области нет идентификатора publicMethod, произойдет сбой.

     this.publicMethod() //fails, this scopes to DOM window

Если вызов был M.Class (), то этот является ссылкой на M.Если вы получаете окно , то вы вызываете функцию другим способом.

     M.Class.API.publicMethod() // works, but is kludgy  

Потому что именно так вы ее настроили.Если вам это не нравится, настройте его по-другому.

Наконец:

)(typeof window === 'undefined' ? this : window);

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

)(this);

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

1 голос
/ 17 февраля 2012

Что делать, если все ваши методы были "приватными", кроме тех, которые вы вручную выставляете через M.Class.API?

function _getParam(){
  return _param2;
}

function _setParam(e){
  _param2 = e;
}

function publicMethod(){
  console.log("public method");
}

M.Class.API = {
  getParam : _getParam,
  setParam : _setParam,
  publicMethod : publicMethod
}

publicMethod();              // succeeds
this.publicMethod();         // still fails
M.Class.API.publicMethod();  // still works, still is kludgy

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

...