I have object definition spanning multiple files and I use the following syntax to add more properties to namespace
var app = app || {};
// and then
app.namespace = {
...
}
But JSHint warns me with stuff like:
[L1:C5] W079: Redefinition of 'app'.
var app = app || {};
I'm not sure if this is really wrong as I've seen it used many times e.g. together with module pattern.
If that's ok, how can I globally supress that warning? I've found a way to supress given option for given file with
/* jshint: -W079 */
but is there a way to do it globally? Or is it considered bad practice?
use this:
window.app = window.app || {};
What you are trying is assigning the local variable app to the global variable app.
Related
I have some 'namespaced' syntax:
var APP = {};
then I have :
APP.one_sub_app = function () {}
etc.
I create instances of APP objects and I add them to APP.
APP.my_instance = new APP.one_sub_app();
I want to keep all my application inside APP. Ok, my app works fine. Now I apply an uglifier that change APP to Z. I need a way to know that Z is APP because (for example) I need a way to deal with Z ( really APP ). I need a way to check if the parent of any object is APP or other object.
Getting the name using a Z.constructor.name could be the solution but in my case Z (APP) is a unnamed object.
Any way to fix that? I dont want to create a custom property,because other external instances I could be looking can have not my custom property. Any way create an 'autoinstance' of global object with the same name ?
To prevent renaming the namespace variable by minifier, make it global, e.g. by declaring it outside (before) the immediately-invoked function the namespaced functions are defined in:
var APP = {};
(function() {
APP.foo = function() {/* ... */};
APP.bar = function() {/* ... */};
})();
or just use window.APP = {}; instead of var APP = {};.
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 have
var Schemas = {};
Meteor.isClient && Template.registerHelper("Schemas", Schemas);
Schemas.Person = new SimpleSchema({
fullName: {
type: String,
index: 1,
optional: true,
},
email: {
type: String,
optional: true
},
address: {
type: String,
optional: true
},
isActive: {
type: Boolean,
},
age: {
type: Number,
optional: true
}
});
in one file and
var Collections = {};
Meteor.isClient && Template.registerHelper("Collections", Collections);
Persons = Collections.Persons = new Mongo.Collection("Persons");
Persons.attachSchema(Schemas.Person);
in another file.
I get the error ReferenceError: Schemas is not defined. It's rather obvious that I have to define Schemas in my collections.js file instead of having them separate. But how does Meteor work with code in separate files? I can access some objects and variables while others are unaccessible.
When you define a variable in the classic JavaScript way :
var someVar = 'someValue';
at the root of your .js file Meteor scopes it to the file using an IIFE.
If you want to define a global variable, simply don't write the var, giving :
someVar = 'someValue';
This will define a variable in all your application by default, although you may restrict it by writing that declaration in a specific recognized folder (client or server folder for example).
However this variable won't be defined absolutely first. It will be defined when Meteor runs the actual code that defines it. Thus, it may not be the best practice because you're going to struggle with load order, and it will make your code dependent on how Meteor loads files: which folder you put the file in, the name of the file... Your code is prone to messy errors if you slightly touch your architecture.
As I suggested in another closely related post you should go for a package directly!
Variables in Meteor declared with the var keyword are scoped to the file they are declared in.
If you want to create a global variable do this
Schemas = {}
ReferenceError is a Node error. Meteor is a framework on top of Node.
Node has a global scope (aka Node's global variable). This error is thrown by Node (not Meteor) if you try to access an undefined global variable.
Browsers also have a global scope called window, and do not throw ReferenceErrors when undefined variables are accessed.
Here's a pattern I like for adding functionality to a class (it's very Meteor):
/lib/Helpers.js <-- Helpers for everyone (node+browser)
/server/Helpers.js <-- Server helpers (node)
/client/Helpers.js <-- Client helpers (browser)
Consider these implementations:
// /lib/Helpers.js
Helpers = {/* functions */}; // Assigned to window.Helpers and global.Helpers
// /server/Helpers.js
Helpers = _.extend(Helpers, {/*more functions*/}
// /client/Helpers.js
Helpers = _.extend(Helpers, {/*more functions*/}
This is a trivial example. What if I didn't want to worry about load order? Why not _.extend() in /lib/Helpers.js?
// /lib/Helpers.js
// Helpers = {/* functions */}; // Overwrites...
Helpers = _.extend(Helpers, {/* functions */}); // ReferenceError
Because you'll get a ReferenceError from Node if Helpers isn't defined - specifically the "Helpers" used as an argument. (Node knows to assign Helpers as global.Helpers).
Here are two ways to "fix" this:
1) Assign Helpers to something
// /lib/Helpers.js
// Helpers = Helpers || {} // would be another ReferenceError
if (typeof Helpers === 'undefined') Helpers = {};
Helpers = _.extend(Helpers, {/* functions */});
2) Use helpers from the global
// /lib/Helpers.js
Helpers = _.extend(global.Helpers, {/* functions */}); // works in node, but...
Both of which suck.
1)'s syntax is horrible.
2) works in node, but there is no global in browsers. So it fails it's purpose.
So I gave up and went back to overwriting it the first time in lib, and looking for runtime errors if anything was overwritten.
If you have a handy cross-browser syntax for this, do comment :-)
var something = something || {}
something.blah = foo;
Here's some other JS shorthand tips.
Session variables are global and can be accessed in different files/functions easily. Session.setPersistent is used to set the variable name persistently across all files. One might restrict from using session variables when their app is too big as they don't get deleted (hence possible memory leaks) and might give error in the console (if undefined or so). Link to the docs : https://docs.meteor.com/api/session.html
I understand that using closures (IIFE) is the best practice as it prevents polluting the global namespace. However, when I added the closures to my files, it prevented my 2nd file (controllers.js) from reading the first file (models.js). To give you an idea, here's what they look like:
models.js
;(function() {
function searchResult (obj) {
this.state = obj.State;
/*Do more stuff */
}
})();
controllers.js
;(function() {
function storeSearchResults(jsonObj) {
var instance = new searchResult(jsonObj.data[i]);
/* Do more */
}
})();
Now that I've added closures on them, I'm getting an error that searchResult is undefined in controllers.js -- because it can't see that it exists in the models.js. How do I get it to understand that it exists in the other file?
P.S. Yes, models.js is added in the HTML file before the controllers.js file.
For them to interact, they have to have some common symbol. You have a couple of choices:
Do it yourself (using a single global variable)
Use some kind of library that does it for you (using [ideally] just a single global symbol)
Do it yourself a different way that requires no global common symbol at all
Do it yourself
The DIY version is, typically, that you have a single global, for your entire app, which your various modules add properties to.
So for instance:
models.js:
;(function(globals) {
var MyApp = globals.MyApp = globals.MyApp || {};
MyApp.searchResult = searchResult;
function searchResult (obj) {
this.state = obj.State;
/*Do more stuff */
}
})(this);
That works because in loose mode, this at global scope is the global object (window on browsers). We pass it into the IIFE as the argument globals, and then either use or create a property on it called MyApp, and add searchResult to it as a property.
controllers.js:
;(function(globals) {
var MyApp = globals.MyApp = globals.MyApp || {};
function storeSearchResults(jsonObj) {
var instance = new MyApp.searchResult(jsonObj.data[i]);
/* Do more */
}
})(this);
We do the same thing, except that controllers.js is expecting that models.js has already been run. Although we still do the var MyApp = globals.MyApp = globals.MyApp || {}; bit, the new MyApp.searchResult would of course fail if models.js hadn't been run.
There are probably a dozen syntactic variations on this theme, this is just one of them.
Use some kind of library that does it for you
Your other option is to use a library like RequireJS (the one global symbol there is require, and it's a function) or any other asynchronous module definition lib.
Do it yourself another way
Another DIY option gets rid of globals entirely, you don't even need a single global.
To do that, your individual files don't have the IIFE (although they can use ones for things they don't want to share with other files):
;
function searchResult (obj) {
this.state = obj.State;
/*Do more stuff */
}
controllers.js:
;
function storeSearchResults(jsonObj) {
var instance = new searchResult(jsonObj.data[i]);
/* Do more */
}
Then you use a minifier to combine your scripts and wrap them in one big IIFE. You might have pre.js:
(function() {
and post.js:
})();
Then the minifier creates app.js by combining pre.js + models.js + controllers.js + post.js. The end result (un-minified and formatted here for readability) is:
(function() {
;
function searchResult (obj) {
this.state = obj.State;
/*Do more stuff */
}
;
function storeSearchResults(jsonObj) {
var instance = new searchResult(jsonObj.data[i]);
/* Do more */
}
})();
I called this DIY, but I wouldn't be surprised if there were tools to help with it.
Events and listeners passing and receiving data.
I don't use globals.
However I don't know how to use events in raw javascript or if data passing can be done. I use jquery/node which allow for passing data and just works beautifully.
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....