Reputation: 1070
I've just configured gulp / bower to minify & bundle my javascript files and I've created a handful of individual JS files that follow the format of a self-executing anonymous function. e.g. let's say I have a file called "mobile-functions.js" which contains a function called "isViewportInMobile".
mobile-functions.js
(function ($) {
function isViewportInMobile(mobileWidthOverride) {
var widthToCheckAgainst = mobileWidthOverride || 768;
return window.innerWidth < widthToCheckAgainst;
}
})(jQuery);
How do I call that function from a different Javascript file (that also follows the self-executing anonymous function format)? When I'm trying to use the function in my other file, it says that isViewportInMobile is undefined.
Upvotes: 0
Views: 163
Reputation: 1516
There is no way to directly access that function, it is defined within the scope of another function and thus invisible to the outside world. However there is very good way that you can access the function whilst not bloating global scope or accessing them through some global object.
TL;DR It is doable, with anonymous wrappers and a few tricks. Cannot be shortened. Brace yourself for a wall of text.
I will try to make one example of how to do this neatly without exposing globals, will take some time though.
Above is a fiddle example which I made as 1 fiddle but represents 4 of your files, I will post them here as separated files. Also make sure you read through the comments because there are some lines that are there 'JUST TO SHOW' it works. They should be removed when you are making your product. To test it really works read line 113.
How it works:
All the modules that you make, and put in anonymous JS functions, need to give their Hooks to the methods you use before they are initialized. Then on initialization, the hooks are captured within the this
object of the anonymous functions's scope for you to use.
The pattern for modules is very simple, you need to have the following:
(function() {
var wrapper = this;
function InitializeModule(hookObject) {
var hooks = Object.keys(hookObject);
var i;
for (i = 0; i < hooks.length; i++) {
wrapper[hooks[i]] = hookObject[hooks[i]];
}
delete window['methodRegistrationName-config.js'];
}
//MAKE YOUR FUNCTIONS BELOW
//------------------------------------------------------------------
//------------------------------------------------------------------
function InitializeHooks(hooksObject) {
//REGISTER YOUR FUNCTIONS LIKE THIS:
hooksObject.addHook('globalyUniqueID', functionName)
window['methodRegistrationName-config.js'] = InitializeModule;
}
window['methodRegistrationName-config.js'] = InitializeHooks;
window['activeModules']--;
})();
All the modules follow the exact same pattern, you are free to make and reference any function created in any other module (providing it is loaded). All you need to do is make config.js and fileHandler.js as I have specified below and add as many modules as you want. All configuration is done within the config.js.
I have made two modules (module1.js and module4.js) and posted them below so you can see this works. Check out the fiddle linked at the start.
Make a config.js like this:
//CONFIG.js
(function() {
//User manually configures which modules he wants to use
var config = {
// Name : [ load? , path, methodRegistrationName ]
Module1: [true, 'js/module1.js', 'InitializeModule1'],
Modele2: [false, 'js/module2.js', 'InitializeModule2'],
Module3: [false, 'js/module3.js', 'InitializeModule3'],
Module4: [true, 'js/module4.js', 'InitializeModule4']
}
Initialize();
//-------------------------
// DO NOT TOUCH BELOW (a note for your users)
//-------------------------
function Initialize() {
//Required module to make all of this work (this one always has to be added);
config['fileHandler'] = [true, 'js/filehandler.js'];
//-----------------------------------------------------------------------------
var modules = Object.keys(config);
var activeModules = 0;
var i;
var bodyElement = document.getElementsByTagName('body')[0];
var scriptString = "";
for (i = 0; i < modules.length; i++) {
if (config[modules[i]][0]) {
scriptString = "<script type=\"text/javascript\" scr=\"" + config[modules[i]][1] + "\">";
// Now this next line is going to be commented because we dont want fiddle to use it,
// instead we will alert. In your project however you want to add these and NOT alert.
// bodyElement.innerHTML += scriptString;
activeModules++;
console.log(scriptString);
//These scripts will be added to the body;
}
}
window['activeModules'] = activeModules;
var interval = setInterval(scriptsLoadedCheck, 500)
function scriptsLoadedCheck() {
if (window['activeModules'] === 0) {
//all scripts loaded, clear interval you are ready to continue
clearInterval(interval);
delete window['activeModules'];
InitializeFileHandler(config);
}
}
}
})();
now make fileHandler.js like so (this is the only one that cannot be wrapped, though it could be merged with config, in which case it is wrapped):
//FILEHANDLER.js
function InitializeFileHandler(configObject) {
var moduleHooks = {
addHook: function(id, callback) {
if (this.checkHook)
moduleHooks[id] = callback;
else
console.error("Hook already registered.")
},
checkHook: function(id) {
if (this[id] !== undefined)
return true;
return false;
}
}
initializeActiveModules();
function initializeActiveModules() {
var modules = Object.keys(configObject);
var i;
for (i = 0; i < modules.length; i++) {
if (modules[i] === 'fileHandler')
continue;
if (configObject[modules[i]][0]) {
console.log(configObject[modules[i]]);
window[configObject[modules[i]][2]](moduleHooks);
}
}
//------------------------------------------------------------HOOKS INITIALIZED, NOW REAL
for (i = 0; i < modules.length; i++) {
if (modules[i] === 'fileHandler')
continue;
if (configObject[modules[i]][0]) {
window[configObject[modules[i]][2]](moduleHooks);
}
}
}
}
window['activeModules']--;
Now you can make your modules, like this module1.js:
//MODULE1.js
(function() {
var wrapper = this;
function InitializeModule(hookObject) {
var hooks = Object.keys(hookObject);
var i;
for (i = 0; i < hooks.length; i++) {
wrapper[hooks[i]] = hookObject[hooks[i]];
}
delete window['InitializeModule1'];
//This next line should NOT be here, it is here just to test/show it works
Module1Function1();
}
function Module1Function1() {
//Change the arguement to 'Module4Function1ID' to simulate a call from Module4;
console.log(Module1Function2ID());
}
function Module1Function2() {
return "Worked!";
}
function InitializeHooks(hooksObject) {
hooksObject.addHook('Module1Function1ID', Module1Function1)
hooksObject.addHook('Module1Function2ID', Module1Function2)
//Once hooks are initialized, the initialization process switches to the module
window['InitializeModule1'] = InitializeModule;
}
window['InitializeModule1'] = InitializeHooks;
window['activeModules']--;
})();
and for example module4.js like this:
//MODULE4.js
(function() {
var wrapper = this;
function InitializeModule(hookObject) {
var hooks = Object.keys(hookObject);
var i;
for (i = 0; i < hooks.length; i++) {
wrapper[hooks[i]] = hookObject[hooks[i]];
}
delete window['InitializeModule4'];
}
function Module4Function1() {
return "Function from module4";
}
function InitializeHooks(hooksObject) {
hooksObject.addHook('Module4Function1ID', Module4Function1)
//Once hooks are initialized, the initialization process switches to the module
window['InitializeModule4'] = InitializeModule;
}
window['InitializeModule4'] = InitializeHooks;
window['activeModules']--;
})();
Upvotes: 0
Reputation: 301
Depending on how much access you have to the file in question, you could modify it slightly to pass the scope of the self invoking function to a variable. However, I'd assume that if you had such access you probably wouldn't need to create this thread, but none the less here's how you can do it...
var something = (function($) {
this.isViewportInMobile(mobileWidthOverride) {
var widthToCheckAgainst = mobileWidthOverride || 768;
return window.innerWidth < widthToCheckAgainst;
};
return this;
})(jQuery);
This would then allow you to call the function like this:
something.isViewportInMobile(params);
I hope this was of some use.
Upvotes: 1