Reputation: 4234
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?
Upvotes: 2
Views: 448
Reputation: 147373
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.someFn
and go from there.
To answer your questions:
publicMethod() //fails
There is no publicMethod
identifier in the current scope, it will fail.
this.publicMethod() //fails, this scopes to DOM window
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.
M.Class.API.publicMethod() // works, but is kludgy
Because that is how you have set it up. If you don't like it, set it up some other way.
Lastly:
)(typeof window === 'undefined' ? this : window);
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:
)(this);
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?
Upvotes: 2
Reputation: 14983
What if all of your methods were "private" except the ones you manually expose through 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
You should also be aware that returning an anonymous object from a function may have unintended consequences when calling that function with the new
keyword. See this Stack Overflow question.
Upvotes: 1