Been working on a js module pattern for a while to meet the escalating needs of “some website”. Essentially, I just need a good way of grouping / encapsulating scripts, along with the sometime need for OO patterns.
I’ve got a structure that works ok, but I’m not happy with certain parts of it… so I’m in the process of refactoring it. Here is the updated pattern:
(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);
This produces a satisfactory DOM structure (on inspection via firebug) – but I’m losing scope of this in one specific area = calling the “public” methods of the returned object, internally.
publicMethod() //fails
this.publicMethod() //fails, this scopes to DOM window
M.Class.API.publicMethod() // works, but kludgy syntax
In the previous iteration of this pattern, the “class” object is self-executing, and reference to this is maintained :
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();
However, in the refactored pattern, I wish instantiate these “classes” via new, to gain more control over execution order.
I’ve read many, many articles on the fairly mind numbing subject of lexical scope in js… yet come up with no conclusions.
How should I maintain the scope of this in my updated module pattern?
This is one of those philosophical questions that everyone asks when writing a library or module: should functions reference the container object using this or the variable name? The answer is: it depends.
If you know that the function will always be called with the correct value of this (e.g. a method on a prototype), then use this. However, if the function might be called any other way, then use the variable name. If you decide at some later stage to change the name, it’s a pretty simple search and replace exercise. And calling myLib.utils.someFn is a lot clearer than calling this.someFn to me. And if you feel it’s too much typing, you can always revert to
var sF = myLib.utils.someFnand go from there.Edit
To answer your questions:
There is no
publicMethodidentifier in the current scope, it will fail.If the call was M.Class(), then this is a reference to M. If you are getting window, then you are calling the function some other way.
Because that is how you have set it up. If you don’t like it, set it up some other way.
Lastly:
seems to be one of those mystic incantations that seem to proliferate on the web. What is the purpose? If the intent is to pass a reference to the global object to the function, then:
is sufficient everywhere. The purpose of the above is to ensure that the function has a reference to the global object because referencing window might resolve to some other object. Including logic that may or may not pass in the global object seems like a backward step. In what scenario is it preferable to reference the (possibly re-assigned) window property of the global object rather than the global object itself?