This answer shows how to share a scope across several JS files.
But I'm wondering how one might go about having two (or more) levels of wider scope, shared between multiple files.
Experimenting, I've found that a slight tweak to the above answer involves sharing an object between files, and then initiating it the first time it is found to be an empty object like so:
var MODULE = (function( widerScopeVariables )
{
if( $.isEmptyObject( widerScopeVariables ) ){
// projectWide:
widerScopeVariables[ 0 ] = {};
// formsWide:
widerScopeVariables[ 1 ] = {};
}
const projectWide = widerScopeVariables[ 0 ];
...
... but the trouble is that projectWide and formsWide (intended to be of more restricted scope) are then both available to every file sharing this MODULE variable, even if by convention you restrict use by files which aren't intended to have access to the formsWide variables.
Furthermore, every file which might be the first to load has to include the above code.
Is there a better way to accomplish this sort of multi-level multi-file scoping in JS?
I have found a sort of way to help accomplish this. It's not elegant, and merely encourages the right kind of use... it will not please JS purists, but it works for me.
For the avoidance of doubt I am aware that JS has only two scopes: global and function.
What I have done is to put a bunch of files in a subdirectory called "generic_elements". Using the design pattern in the answer I referenced I then go like this in these files:
var HOLDER = (function( holderState )
{
const genericDir = getDir( -2 );
const genericWide = holderState[ genericDir ] = holderState[ genericDir ] || {};
...
return holderState;
}( HOLDER || {} ));
where getDir looks like this (global function):
function getDir( pathElement ){
let info = new Error().stack;
const infoLines = info.replace( /\n/g, ' ' ).split( ' ' );
const delimiter = '/';
// [0] or [1] would return the line for this file, not the calling file
const tokens = infoLines[ 2 ].split(delimiter);
// get a directory in this path. -2 means penultimate token = string between penultimate and last '/'
return tokens.slice( pathElement )[ 0 ];
}
... it's not the "full" version in that it only returns the final directory rather than the complete path from the main URL directory.
The idea is pretty simple: a bunch of files in a subdirectory use the path of the subdirectory containing these "files in a module" as a key in the holderState object. They share the object genericWide. Modules higher up can still look at this HOLDER object, but coders (hopefully) agree not to use key-value pairs from modules they know nothing about. Conversely, files in subdirectories will be able to work out the paths of ancestor directories, calling getDir with -3 or -4 or whatever... so they will be able to access their ancestor modules' "directory-wide" objects.
Related
I have two folders next to each other as it follows ..
// Folder 1
users
-services
-index
-otherThings
= services
exports.A = (a,b,c)=> { // do something }
exports.B = (a,b,c)=> { // do something }
= index.js
const services= require('./services');
const otherThings= require('./otherThings');
module.exports = { otherThings, services};
// Folder 2
middlewares
-is-auth
-index.js
= is-auth
const { services } = require('../users');
// services here is undefined
// when i require the full index object it is an empty object
const indexObject = require('../users')
console.log(indexObject) // {}
the weird thing is when using services required above i am getting suggestions of the folder 1 services (A,B functions) but they are undefined !
console log services in folder 1 if it is coming there.
check the hierarchy of your folder structures when importing and
check the paths also,
other wise the code seems right.
edit 1:
not sure what exactly services type are, but it seems only function A and B are there, if you just need those functions you can export them individually and try,
if that works then you can back track to, why services was not working in the first place,
if not, then just try any random variable try importing if it doesn't work then it's may be related to hierarchy.
edit 2:
i think i got it,
if my assumption is right users is folder and services is file, then you need the path as require(../users/services);
edit 3:
then check out this link require folder as a module
If I want to span my JavaScript project across multiple source files, but have each file have access to the same private variable, how would one do that?
For example, if I have the following code:
APP = (function () {
var _secret = {},
app = {};
// Application part 01:
app.part01 = (function () { /* function that uses _secret */ }());
// Application part 02:
app.part02 = (function () { /* function that uses _secret */ }());
//
return app;
}());
How do I put app.part01 and app.part02 in seperate files, but still have access to _secret?
I don't want to pass it as an argument. That's just giving the secret away, as app.part01() could be replaced by any other function.
Maybe I am asking the impossible, but your suggestions might lead me in the right way.
I want to work with multiple files, but I don't know how. Copying and pasting everything inside a single function each time before testing is not something I want to do.
How do I put app.part01 and app.part02 in seperate files, but still have access to _secret?
That's impossible indeed. Script files are executed in the global scope, and don't have any special privileges. All variables that they will be able to access are just as accessible to all other scripts.
Copying and pasting everything inside a single function each time before testing is not something I want to do
What you are looking for is an automated build script. You will be able to configure it so that it bundles your files together, and wraps them in an IEFE in whose scope they will be able to share their private state. The most simple example:
#!/bin/sh
echo "APP = (function () {
var _secret = {},
app = {};" > app.js
cat app.part01.js >> app.js
cat app.part02.js >> app.js
echo " return app;
}());" >> app.js
The only way that you can share _secret is attaching it to the application object and then application object to the window object. Here is an example.
// FIRST JS FILE...
var application; // will be attached to window
(function(app) {
app.secret = "blah!"; // will be attached to application
})(application || (application = {}));
// ANOTHER JS FILE
var application;
(function(app) {
app.method1 = function(){ console.log(app.secret); }; // will be attached to application;
})(application || (application = {}));
console.log(application.method1()); // will display 'blah!' on the console
Working example on jsbin
One way I was able to accomplish this was to create a JS file that contained the global object.
// Define a global object to contain all environment and security variables
var envGlobalObj = {
appDatabase: process.env.YCAPPDATABASEURL,
sessionDatabase: process.env.YCSESSIONDATABASEURL,
secretPhrase: process.env.YCSECRETPHRASE,
appEmailAddress: process.env.YCAPPEMAILADDRESS,
appEmailPassword: process.env.YCAPPEMAILPASSWORD
}
module.exports = envGlobalObj
Then in the files I wish to reference this object, I added a require statement.
var envGlobalObj = require("./envGlobalObj.js");
This allowed me to centralize the environment and secrect variables.
I use UglifyJS to minify a concatenated set of files, which works fine but not good enough. The built lib uses namespaces, so classes, functions and constants are stored in a root namespace variable:
(function() {
var root = { api:{}, core:{}, names:{} };
/* util.js file */
root.names.SOME_LONG_NAMED_CONST='Angel';
/* Person.js file */
root.core.Person = function(name) { this.name = name };
/* API.js with the functions we want to expose */
root.api.perform = function(param_for_api) { /* do something */ }
window.lib_name.perform = root.api.perform;
})();
which is minified to the not-so-minimal version
(function(){var a={api:{},core:{},names:{}};a.names.SOME_LONG_NAMED_CONST="Angel",a.core.Person=function(a){this.name=a},a.api.perform=function(){},window.lib_name.perform=a.api.perform})();
I understand uglify probably thinks that root var is a data structure that must be kept as-is and can't be changed. Is there a way to let UglifyJS mangle the nested names in the root namespace?
When you minimize Javascript you can only change names of variables, the api, core and names are not variables but properties of an object. If these were changed by the minimizer, you would potentially get unexpected results. What if in your code you would call
root["api"].perform = function()...
or even something like
function doIt(section, method, argument) {
root[section][method](argument);
}
doIt('api','perform', 101);
All perfectly legal JS, but a minimizer could never figure out what's going on.
I have been trying to use --mangle-props of UglifyJS2 and can tell you: 'it makes a mess'.
As someone pointed out: 'Developer should decide what properties to mangle, not uglifyjs'
I am approaching the problem using this options:
--mangle-props
--mangle-regexp="/_$/"
The regex matches any property with a underscore at the end.
You asked to mangle nested names in the root namespace. So, your code:
(function() {
var root = { api:{}, core:{}, names:{} };
root.names.SOME_LONG_NAMED_CONST_='Angel';
root.core.Person_ = function(name) { this.name = name };
root.api.perform_ = function(param_for_api) { }
window.lib_name.perform = root.api.perform;
})();
Would result in this:
(function() {
var n = {
api: {},
core: {},
names: {}
};
n.names.a = "Angel";
n.core.b = function(n) {
this.name = n;
};
n.api.c = function(n) {};
window.lib_name.perform = n.api.c;
})();
Command:
uglifyjs --beautify --mangle --mangle-props --mangle-regex="/_$/" -- file.js
If you want to mangle first level of root namespace (api, core, names) just put a underscore on them (api_, core_, names_), you are in control ;)
Just a side note: when you are mangling properties usable by other js files, you should mangle all files together with the same command, so the same identifier will be used over all files.
Aside from #JanMisker 's point (which is completely valid), rewriting properties is unsafe because they can be exposed to code outside the scope of the minification.
Although the self executing function has a scope, and if the code is only
(function() {
var root = { api:{}, core:{}, names:{} };
root.names.SOME_LONG_NAMED_CONST='Angel';
alert(root.names.SOME_LONG_NAMED_CONST); // some code that does something
})();
It is true that outside of the function, there is no way to access the root object, so rewriting the property names is safe, and the following code would result in the same:
(function() {
var a = { b:{}, c:{}, d:{} };
a.d.e='Angel';
alert(a.d.e);
})();
But even if you are inside your private scope you can access, and more importantly assign to variables from an outer scope! Imagine this:
(function() {
var root = { api:{}, core:{}, names:{} };
root.api.perform = function(param_for_api) { /* do something */ }
window.lib_name = root.api;
})();
You are not only exposing a function but an object with a function on it. And the function will be visible from any place where window is visible.
So, for example writing the following in the javascript console would yield different results with and without minification:
window.lib_name.perform(asdf);
With minification you would have to write:
window.lib_name.f(asdf);
Or something similar.
Remember that there can always be code outside your minification.
It is not that crucial to have the absolute minimal JS, but if IT IS that crucial for some reason (for example: aliens abducted your stepdaughter, and the only way to have her back is to minify this below 100 characters or so), you can manually replace an undesirably long property name to a shorter one, just be sure that it will not be exposed anywhere, and isn't be accessed through associative array notation (root['api']).
as #Jan-Misker explained in his answer, property name mangling is NOT an good idea because it could potentially break your code.
However, you can workaround it by define the property names as local variables, and modify all .properties to [keys], to make smaller file size:
(function() {
var API = 'api';
var CORE = 'core';
var NAMES = 'names';
var SLNC = 'SOME_LONG_NAMED_CONST';
var root = {};
root[API]={};
root[CORE]={};
root[NAMES]={};
/* util.js file */
root[NAMES][SLNC] ='Angel';
/* Person.js file */
root[CORE].Person = function(name) { this.name = name };
/* API.js with the functions we want to expose */
root[API].perform = function(param_for_api) { /* do something */ }
window.lib_name.perform = root[API].perform;
})();
Because now all the properties became a local variable, uglify js will mangle/shorten the variable names and as consequence you overall file size reduced:
!function(){var a="api",b="core",c="names",d="SOME_LONG_NAMED_CONST",e={};e[a]={},e[b]={},e[c]={},e[c][d]="Angel",e[b].Person=function(a){this.name=a},e[a].perform=function(){},window.lib_name.perform=e[a].perform}();
However, reduced file size doesn't mean you will get shorter downloading time on real server, because usually our http transport is gzipped, most of the repetitions will be compressed by your http server and it does a better job than human.
The latest release of uglify (today) has object property mangling, see v2.4.18. It also supports reserved files for excluding both object properties and variables that you don't want mangled. Check it out.
Use the --mangle-props option and --reserved-file filename1.json filename2.json etc....
The problem I have is that there are a set of variable values / properties in one file and a library in another file. I have started refactoring the code but still need to keep variable values(dynamic) and library(static) differently.
I am using namespacing and overall want only one global namespace.
The problems I have at the moment:
1. How can I still keep one global namespace
2. What is the best way to read the values from one file and use it in the library present in another file.
e.g I came up with something like
//File ONE with values
var main.dynamicvalues = (function(){
var a = 10,
b = 20,
c = 30;
return {
a:a,
b:b,
c:c
}
}());
//File TWO with core Library
var main.library = (function(){
//Various Private functions that need to use a,b,c variables from above main.dynamicvalues namespace
return {
//Public functions again need to use a,b,c from above namespace.
}
}());
Is there a way I can have a pattern so that I keep only one global namespace and can refer to variables directly without having to use maincode.values.a, maincode.values.b, maincode.values.c or something like this in maincode.library.functions
Thanks
Sparsh Gupta
This approach is a little better, but it's not exactly what you're looking for.
var main = {};
main.dynamicvalues = (function() {
// same as before
})();
main.library = (function(dyn){
// use dyn.a, dyn.b etc
return {
// same in here
}
}(main.dynamicvalues));
Create a new file (maybe name it something like "common.js") and put the values there.
You can try RequireJS. This will let you do what you want with no global namespace at all (if you'd like to). In addition it will give you non-blocking script loading, easy way to handle dependencies and a build tool.
On the other hand, it can deprive you of the joy of investigating things for your own and better understanding of js architectural patterns.
Your code with RequireJs could have looked like this:
// File one with values, let's name it values.js
define([], function() {
var a = 10,
b = 20,
c = 30;
return {
a: a,
b: b,
c: c
}
})
// File two with library
define([
// load values.js as a dependency
'values'
// what is returned in values.js can be passed as an argument to the callback
], function( values ) {
values.a === 10 // true
})
I usually break down the parts of my app into their own modules and files that exist under one global object. For the production code I combine all the files on one master.js file. Because I'm writing the code in different files and don't want to worry about the order they're loaded I check if the global object 'App' exists, and if it doesn't create an empty object to store the module in.
So if I have 10 modules then in the combined file it will run var App = App || {}; 10 times when it only needs to run once (at the start). My question is, is that a big deal in terms of performance, is it worth going in at the end and removing the unnecessary code? For smaller apps I don't think it's a big deal, but just wondering if this is a bad strategy to use for bigger apps.
// module_1.js
var App = App || {};
App.Module_1 = (function(){
// code
}());
// module_2.js (different file)
var App = App || {};
App.Module_2 = (function(){
// code
}());
Even if var App = App || {} runs 100 times, you will not notice any difference (only on very slow systems), so I'd say: just leave it.
Generally, don't optimize prematurely, look where the real bottlenecks are and optimize those.
BTW, those extra anonymous, self-invoking functions are not necessary: when using
App.Module_X = function(){
// code
};
you variables declared using var already are within the scope of App.Module_X.