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 = {};.
Related
I am new to JavaScript and trying to make a simple node server. Here is my code:
var activeGames = {}
exports.initialize = function(){
var gameID = "game12345"
activeGames.gameID = new Game(gameID, "player1", "player2")
}
I call the initialize function from another module, and I get an error stating that activeGames is undefined. activeGames is at the outermost scope of the file. I tried adding 'this' before activeGames.gameID but that did not fix it. Why is activeGames undefined? Thanks in advance.
EDIT: Here's how I'm calling this code.
In my base index file I have
const handler = require("./request-handler.js")
handler.initialize()
In request-handler.js, I have
var gameManager = require('./game-manager')
exports.initialize = function(){
gameManager.initialize()
}
JavaScript has lexical scope, not dynamic scope.
ref: https://en.wikipedia.org/wiki/Scope_(computer_science)#Lexical_scoping
Lexical scope means that whether a variable is accessible or not depends on where they appear in the source text, it doesn't depend on runtime information.
example:
function foo() {
var bar = 42;
baz();
}
function baz() {
console.log(bar); // error because bar is not in the scope of baz
}
the same problem happens in your code,
var activeGames
is not in scope.
try this variation:
exports.initialize = function(){
var activeGames = {}
var gameID = "game12345"
activeGames.gameID = new Game(gameID, "player1", "player2")
}
A good solution could be to use a class and export it:
--THIS CODE IS NOT TESTED--
class gamesManager {
var activeGames = {}
initialize() {
var gameID = "game12345"
activeGames.gameID = new Game(gameID, "player1", "player2")
}
}
exports.gamesManager = gamesManager
USE:
const activeGames = require('./game-manager');
const activeGamesInstance = new activeGames.gamesManager();
activeGamesInstance.initialize();
Need a code sample for this one. I ran this locally and it worked fine, although your code has a big issue which may be a part of your problem. It looks like you want to keep track of multiple games in activeGames. You need to use this syntax instead:
activeGames[gameID] = new Game(gameID, "player1", "player2")
Here's my working code:
index.js:
const handler = require("./request-handler");
handler.initialize('game-1234');
handler.initialize('game-5678');
request-handler.js:
var gameManager = require('./game-manager');
exports.initialize = function(gameID) {
gameManager.initialize(gameID);
}
game-manager.js:
var activeGames = {};
class Game {
constructor(id, player1, player2) {
this.id = id;
this.player1 = player1;
this.player2 = player2;
}
}
exports.initialize = function(gameID) {
activeGames[gameID] = new Game(gameID, "player1", "player2");
console.log(`game initialized! ${ Object.keys(activeGames).length } active games`);
}
Running node index results in this:
game initialized! 1 active games
game initialized! 2 active games
When you require a script file in Node.js, it is compiled as part of a function called with named parameters require, module, exports and other exposed variables as arguments1. Variables declared at file level within the required script become function level variables in the enclosing module wrapper and retained inside its closure.
Hence your "global variable" is no such thing: it's a variable defined inside a closure...
An important question then is does the module loader make variables declared in a parent module available to scripts required inside the parent. A quick test shows that the answer is general: no, modules do not have automatic access to variables declared in other modules - those variables are inside closures.
This indicates that to pass variable values to scripts that have been required, generally pass them as argument values to exported functions.
It is also possible to export any javascript value as a property of module.exports from within a required script, or add properties to an exports object after it has been returned from requiring a script. Hence it is technically feasible to pass information up and down between modules by adding properties to exports objects.
Redesigned code has multiple options to
define activeGames at the application level and pass it down as a parameter to modules needing access to it, or
export activeGames from game-manager.js by adding
exports.activeGames = activeGames
to the end of the file. This will not take care of exporting activeGames out of the parent module request-manager.js for use elsewhere, but it could be a start. Or
define activeGames as a global variable (in node) using
global.activeGames = {} // define a global object
Defining global variables is not encouraged as it can lead to collisions (and consequent program failure) between names used by applications, code libraries, future library updates and future versions of ECMAScript. Or,
Define an application namespace object for data global to the application. Require it wherever access to application data is needed:
create appdata.js as an empty file.
Optionally include a comment:
// this files exports module.exports without modification
require appdata.js wherever needed.
var appData = require('./appdata.js')
appData.gameData = {}; // for example
This relies on node.js maintaining a cache of previously required modules and does not recompile modules simply because they have been required a second time. Instead it returns the exports object of the previous require.
Happy festive season.
References
1The Node.js Way - How require() Actually Works
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 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.
I have a some JavaScript with a complex structure. Because I'm new comer to JavaScript (only understanding some basic concepts) I don't know how to use it properly.
I have two files : Circle.js and Line.js. In Circle.js, I want to use a class object defined in Line.js:
In file Circle.js :
Helper.using('py.Figures', function (ns) {
ns.Circle = function (params) {
// some additional methods and code here
}
}
And in Line.js is :
Helper.using('py.Figures', function (ns) {
ns.Line2Point = function (params) {
// some addition methods and code here
};
}
In Figures.Circle, in ns.Circle I want to use Line2Point but I don't know how.
I think it should be :
line = new ns.Line2Point(params);
But It seem doesn't work.
According to Helper Class, ns will point to helper.using, in this case py.Figures. Does it mean, ns is the same object/reference in both the files?
I don't think this is doable in Javascript directly across files. If they are part of the same namespace you could share some 'global' objects to achieve this have the line2points and circles attach themselves to that global object:
Ex:
var myShapesNameSpace = {};
Circle.js:
(function(){
var circle = {};
circle.method1 = function(){...}
circle.method2 = function(){...}
myShapesNameSpace.Circles = circle;
})(window.myShapesNameSpace = window.myShapesNameSpace || {}); //check if namespace object exists. Else create a new blank one.
Line.js:
(function(){
var line= {};
line.method1 = function(){...}
line.method2 = function(){...}
myShapesNameSpace.Lines= line;
})(window.myShapesNameSpace = window.myShapesNameSpace || {});
Now you can check for the existence of myShapesNameSpace.Circles or .Lines and call the corresponding methods accordingly.
You can include files in javascript and reference objects across files unless they are exported in some global form either via window or your define global
Welcome to Javascript, the shit parts. Require.js was designed precisely for this because the creators of JS, well, I guess thought that everyone would write every program in one file.
RequireJS
It was designed for web use but can be used elsewhere too (locally, with Node, etc.)
I am writing a web application which uses YUI3 for all it's JS needs. I need functionality such as Tooltips, Tooltips whose content is determined by AJAX queries, Toggle Buttons and so on.
I was not sure who to build an architecture to achieve all this. I have taken the following approach
var Myapp = function(){
this.toggleButton(node,config)
{
YUI().use(....,function(Y){
//code to convert NODE into a toggle button;
});
}
return this;
};
In my application I then just convert all the buttons into toggle buttons by calling
var app = Myapp();
app.toggleButton(Y.all('.toggle-buttons'),{'text1':'TOGGLE_ME','text2':'TOGGLED_ME'});
All this works. But I wanted to know from more experienced developers if there is anything fundamentally wrong with this approach.
Is this a good way to use JavaScript ?
return this;
This is unneccesary since function constructors return this by default.
var app = Myapp();
You forgot to call new Myapp() without the new keyword this will be the window object and you are effectively writing to global scope.
There's a fundamental problem in your code:
var MyApp = function(){
this.toggleButton(node,config)
{
...
You're not defining a function for MyApp. Instead, you try to invoke toggleButton each time you instantiate it. It should fail because the function is undefined
In your case, Class definition and instantiation is not needed because MyApp is being used as a utility.
You can define MyApp as a static Object:
var MyApp = {
toggleButton: function toggleButton() {
// your code
}
};
And you can use it anywhere by:
MyApp.toggleButton();