Limiting the scope of a shared variable (Javascript) - javascript

I would like to know if it is possible to have a variable with shared scope between two separate javascript files but not within the root document that references those javascript files.
For example, if I wanted to have a variable that held information about the size of an html element on page load, I could do something like this...
Within the root (index.html) document, I could declare the variable and give it a global scope:
<script>
var elemWidth;
</script>
<script src="first.js"></script>
<script src="second.js"></script>
Within the first javascript file (first.js):
var elem = document.getElementById('myElem');
elemWidth = elem.style.width;
Within an additional javascript file (second.js):
var someDynamicValue = n; /* where 'n' is an integer that is given a calculated value */
if(elemWidth > someDynamicValue) { ... }
Using the above convention, the variable elemWidth is accessible in all three documents (index.html, first.js, second.js). But what if, for whatever reason, I did not want that variable to be accessible in the root document (index.html)? I only want it to be accessible and manipulatable in the two .js files. After all, it's really not needed in the root document (index.html) and I wouldn't necessarily need it in additional .js files (e.g. third.js).
Certain possibilities have come to me, but they all seem to break down at a certain point:
Declaring the variable within one of the two .js files would limit
its scope to that document only. This just doesn't work.
Store the variable as a private property of an object that is declared in the root, and allow that property to be set/get only by the approved .js files
This might be possible by extending the prototype of the ancestor object within the external .js files to include getter/setter methods, but not providing those methods in the document root (which is where the ancestor would have to be declared)
I am not an OOP expert, so this approach might be fundamentally flawed. Feel free to correct me on this!
Consolidate the two javascript files into one, and declare the
variable within that script. This would work, but I may not
necessarily want to or be able to consolidate the separate .js files
in certain circumstances.
Other than that, I am stumped by this question. At this time, I don't have a specific reason to need this functionality, but I can imagine it being useful in certain situations (i.e. additional security, quarantining variable values) and, of course, I am curious and would like to learn something new about JS & OOP through this example.
Perhaps it's just not possible in JavaScript - if so, please explain why :)
Update: I would like to expand on the idea of using object prototypal inheritance to possibly achieve this result. If we declare a class constructor, such as:
function ancestorObj() {
var privateVar = 'secret';
}
And then extend this prototype with another constructor:
function inheritedObj() {
this.getSecret = function() {
return privateVar;
}
}
inheritedObj.prototype = new ancestorObj();
We have now created an object class constructor that is an inherited instance of ancestorObj which contains a getter method that returns the private property of the prototype object. The default class constructor does not contain a getter method. The logic that follows is that only instances of the inheritedObj can get the value of this private property. Can we not then declare instances of inheritedObj only where needed (i.e. 'first.js' and 'second.js'), thereby limiting the scope of the privateVar property?
Again, OOP is not my forte, so this may be easily dismissed as not possible or otherwise incorrect. If so, please explain why.

Ultimately there's nothing you can do to make the symbol completely private to two code units imported from separate <script> blocks. The only relationships such code can have are via global symbols, and if code in two scripts can find your "private" property, any other code can too.
Probably the best thing to do is keep it as a property of some global namespace you claim, or under one you're already using (like a framework).

In your case it'd wrap it into a jQuery method like $.fn.methodName = function { var leaveMeAlone = true; };

Related

Need advise/help on getting rid of global variables

Yes, this is another topic about global variables. I've searched quite a bit about them. But most of the topics is just about WHY not to use global variables and I'm convinced that I shouldn't, and I am more wondering HOW not to use them, and that I'm still unsure about.
I am working on a project, and it works wonders, but I am using about 50 global variables at the moment and that number keeps rising. Right now I've split things up in multiple .js files. Like load_chunks.js, load_images.js, render_map.js, player_actions.js, input.js, to spread the functions based on what they are for. And I've put all global variables inside settings.js.
Currently I use these global variables for these reasons:
1. Calculation based on some other global variables that doesn't change much or at all after loading. By doing this once, and store it in a global variable, I no longer have to repeat the calculation ever again. If I didn't store it in a (global) variable, Javascript would have to do the calculation many times each second, in some cases even up to few thousand times per second.
2. When the global variable is needed in many functions. Like there's this World-variable, that I use to hold data for the appearance of the world. This variable is a multidimensional array. World[y][x] for example. Load_chunks.js adds more data to this variable, or remove data if you move too far. This variable is also needed in render_map.js, to create the map, and it's also needed in player_action.js, to see if you can step on that particular location.
3. Settings; So a number in a variable that remains the same unless I change them in my script. Instead of going through my scripts, and change the numbers manually after a long search and thinking what the number was, I've put that number in 1 variable and call that variable numerous times in my scripts. These variables are in some cases also needed elsewhere.
Also I like to mention that I don't use classes, and perhaps for that reason I never got around using global variables...?
So how do I get rid of my global variables, or shouldn't I?
I hope you can show me or write for me a script example (or a link to it) of how I should do it. That's the fastest way I learn.
Put Variables Inside a Function Closure
One common way to eliminate a global is to put is inside a function closure:
(function() {
var declareYourFormerGlobalHere = 0;
// put all code that uses the variable inside this closure
// the variable persists for this code to use, but is not actually global
})();
Here's are some usage examples:
// example keeping track of a running count and a cached DOM element
(function() {
var cntr = 0, obj = document.getElementById("test");
setInterval(function() {
++cntr;
if (obj.value < cntr) {
// do something
} else {
// do something else
}
}, 1000);
})();
// example keeping track of a time of last click
(function() {
var timeOfLastClick = 0;
document.getElementById("run").addEventListener("click", function(e) {
var now = new Date().getTime();
// only process click if more than 2 seconds have passed since last click
if (now - timeOfLastClick > 2000) {
// OK to process the click
}
timeOfLastClick = now;
});
})();
Sometimes, you can actually enclose all your code or nearly all your code in a single closure like this and all your current globals become local variables inside the closure rather than actual globals. jQuery uses this technique to declare lots of persistent variables it uses, none of which are actual globally scoped.
Use a Single Namespace Object
Another common method of reducing the number of globals is to use the namespace concept. In this concept, you declare a single global object and put other persistent variables as properties of this single global object. This still leaves you with a single global variable, you can have as many properties as you want off this single global.
var myNamepaceObject = {};
myNamespaceObject.var1 = "foo";
myNamespaceObject.var2 = "whatever";
This technique is also used by jQuery as all globally accessible functions that jQuery offers are available off the jQuery object such as jQuery.extend() or jQuery.contains(). jQuery exposes a single global variable and many other globally accessible functions are then available as properties of that single global object.
Module Pattern
What is commonly known as the "module pattern" uses a combination of these above two techniques, where you have a single module variable that both uses properties and closure variables.
var MODULE = (function () {
var my = {},
privateVariable = 1;
function privateMethod() {
// ...
}
my.moduleProperty = 1;
my.moduleMethod = function () {
// ...
};
return my;
}());
You can see a discussion of this design pattern in these references:
JavaScript Module Pattern: In-Depth
Learning Javascript - The Module Pattern
Mastering the Module Pattern
JavaScript module pattern with example
I'm convinced that I shouldn't, and I am more wondering HOW not to use them, and that I'm still unsure about.
load_chunks.js, load_images.js, render_map.js, player_actions.js, input.js, strongly hint to a procedural implementation, that is your architecture probably has several functional pieces and you pass data around between these functions. That's where your global variables are coming from.
So how do I get rid of my global variables (...)
To change that, you need to structure your system in an object or component based approach, that is:
encapsulate data + respective functions by objects in your problem domain, e.g. have a World object that contains Avatar, Buildings, Airborne objects etc. (or whatever your project is about).
separate the problem domain's logic from view logic (e.g. use a MVC architecture)
then your project organises the interactions between view and model objects by essentially exchanging messages between them.
To facilitate this, I suggest these fine frameworks, or some equivalents suitable for your run-time environment:
requirejs - to encapsulate modules into globally managed components
backbonejs - to have an efficient, proven model/view, class/object model (actually built for use with a REST-style backend, but that's not a strict requirement)
Code structure
Typically, I structure my applications such that there is one .js file per each object component/module. A module in this sense contains both the class definition and the corresponding collection. The modules are managed by requirejs and the class definitions are done using backbonejs.
I hope you can show me or write for me a script example
/* define a class e.g. in someclass.js */
define(['backbone'], function($B) {
var SomeClass = $B.Model.extend({
// all instance variables, methods etc. follow
someVar : value,
someMethod : function() { ... },
});
return SomeClass;
});
/* use the class, e.g. in app.js */
require(['someclass'], function(SomeClass) {
var instance = new SomeClass({ initial model attributes });
var value = instance.get('attribute');
instance.someMethod();
...
});
i had the same problem before but it was a big problem as we had a third party javascript code and our code live in clients websites so we should find a way to omit the appearance of global variable so to be short:
Use 1 global variable namespace and make all other to be properities for it:
GlobalVariable = {}
GlobalVariable.var1 = "var1"
GlobalVariable.var2 = "var2"
You can also make it more useful and divide it into groups:
GlobalVariable.helper = {}
GlobalVariable.helper.var1 = "var1"
GlobalVariable.helper.var2 = "var2"
so you can use same names under different groups.
and if you want to Omit it totally you can use:
(function(document, window) {
// all variables here is global only inside the scope
}(document, window));

How do you load 2 javascript files that could have common variables between them and accessing it on a HTML page?

I got this HTML page. I wanna access variables from 2 javascript files that has common variables. I gotta get values from these variables pertaining to the respective javascript. I tried loading them using <script> but when i access the js variables, it only get the values from the last js file, in other words, the last loaded js file overwrites the variable values and only these values are available to access. I'd really appreciate help on this one.
Thanks!!!
You need to namespace your variables or use variables with different names. What it sounds like you're doing is the equivalent of:
var x = 'foo';
var x = 'bar';
console.log(x);
> bar
Which should make sense. If you use namespaces though, things will work better for you. Consider two files, script-1.js and script-2.js:
// script-1.js
var s1 = {
x:'foo'
}
// script-2.js
var s2 = {
x:'bar'
}
...then...
console.log(s1.x);
> foo
console.log(s2.x);
> bar
That's a rather trite example, of course.
EDIT
If you can't change the files themselves do this - save off the values to some other name/object after loading script-1 but before loading script-2:
<script type="text/javascript" src="script-1.js"></script>
<script type="text/javascript">
var s1 = {
// Save off values here
x:x,
y:y
}
</script>
<script type="text/javascript" src="script-2.js"></script>
// Then...
console.log(s1.x); // will output the value from script-1.js
console.log(x); // will output the value from script-2.js
Not pretty, but should work fine.
If your problem is that your variables are colliding, and you want their values maintained separately, you should wrap the code in each file in a closure. Here you can apply the Immediately-Invoked Function Expression pattern.
(function() {
// your code here
// all of it!
}());
This prevents global variables altogether, and if you don't need to share anything across files, this is the way to go.
If you need to create a shared global, declare it this way:
var x = x || {}; // empty object as an example value
Do that at the beginning of each file. If x is new, it will be declared. If it has already been declared, it will retain its previous value.
You can do the same thing using namespaces, which I highly recommend as an easy way to reduce global variables.
var APP = APP || {};
APP.x = APP.x || {}; // example property of APP, in this case initializing an object
You can always combine techniques too. Setup a namespace object outside a closure, and access them within. This way you only expose one global and limit the scope on everything else.
I hope that helps!
If you have some control over the javascript files, Madbreak's answer is they way to go: whenever you define a javascript library you should be using anonymous functions and namespaces to avoid collisions.
Otherwise, though, you could load them asynchronously and eval them one at a time (There may be a less evil way to do this, but I'm not aware of it) - that way you can use the first in the callback of the download before it is overwritten by the second. If you need them both long term, though, you might be out of luck.
If you need one to run before the other, though remember that you'll need to load the second in the callback of the first download (where doesn't matter unless you're doing more async stuff) otherwise it's a race condition as to which will go first.

Can I add attributes to 'window' object in javascript?

Can I add any random attribute to 'window' object in javascript? Some thing like:
window.my_own_attr = "my_value"
Does it have any side effects with any libraries? And is it cross-browser compatible?
Can I add any random attribute to 'window' object in javascript?
Yes, just like you've shown.
Does it have any side effects with any libraries?
No, not unless you use a library which sets a property you then overwrite.
And is it cross-browser compatible?
Yes, completely.
Having said that, this practice is generally frowned upon. You could end up overwriting something you don't want to.
In all browsers, window is the javascript global namespace. Every property or method 'lives' in that namespace. So if you assign a property to window, it is in effect a global variable.
example:
window.myConstant = 5;
// note: a var declaration will be a window property too
// note 2: 'let' or 'const' will not be window properties
// below can also be done without 'var'.
var myConstantGlobal = 4;
console.log(`multiply(10) => ${multiply(10)}`);
console.log(`multiply() => ${multiply()}`);
console.log(`multiplyGlobalVar(10)) => ${multiplyGlobalVar(10)}`);
console.log(`multiplyGlobalVar() => ${multiplyGlobalVar()}`);
function multiply(val){
return myConstant * (val || 1);
}
function multiplyGlobalVar(val){
return window.myConstantGlobal * (val || 1);
}
You have to be cautious with javascript frameworks. For instance, if you declare window.JQuery, and use the JQuery framework, the JQuery namespace will be replaced by your assignment, rendering it useless.
Yes, you can, but in general you shouldn't.
The window object is also the JS default "global" object, so all global variables get added there.
You're unlikely to break anything unless you overwrite a property that's already there, but it's considered bad practise to dump variables on window, or otherwise create lots of global variables.
I won't repeat what others have said re: the hackyness of this practice. But it can be incredibly useful when using a rigid framework like Angular mixed with vanilla HTML / JS (or jQuery) code. Which is also hacky and frowned upon, but sometimes there are good reasons, like if you have lots of pre-existing JS code that will be challenging to integrate into the framework.
The more interesting question to me is HOW to make use of the ability to add properties to the global window object. There is a pattern I use when I want to expose the methods of an Angular provider (service) to code that otherwise would be unable to inject the service, probably because it runs outside of the Angular DI framework. The way I do it is as follows:
Define your service as a provider in your top level module.
In the constructor or onInit of app.component.js (or whatever your top level component is that imports the provider), inject the provider normally, perform any one time initialization that it needs, and then call window['MyServiceName'] = this
Assuming you have designed the provider to follow a Singleton pattern, your provider's methods can now be safely called from anywhere. A non-Angular script need simply call window['MyServiceName'].methodName()
That 'll work fine, no conflict with any library until used same variable name, will work in all browsers, but not recommended as this will create global JS variable.
In IE if an element has an id, then that node is accessible on the window object as a property:
<div id="num"></div>
alert(num); //Element
num = 3; //throws exception
var num = 3; //ok
As pointed out by others yes you can and yes it means adding "global variables".
Having global variables is not best practice but sometimes it is the simplest thing to do. For instance if you use IFrames and want the child-window to access some property of the parent window. Such a property must be "global" for a child to access it. But this also shows that it is not "truly global", it is just a property of a specific window. But it can conflict with other property-names such as names of functions defined in the same window, perhaps by some libraries you use.
So we know global variables are bad. If we decide to use them anyway is there anything we can do to minimize their badness?
Yes there is: Define a SINGLE global variable with a more or less unique name, and store an OBJECT into it. Store all other "variables" you need as fields of that single object.
This way you don't need to ever add more than a single global variable (per window). Not TOO bad.
Name it more or less uniquely so it is unlikely to conflict with anybody else's single global. For instance you could use your name + 's' as the variable name:
window.Panus = {};
:-)

Isolate execution of JavaScript

One of the limitation of JS that bugs me the most is the poor ability to isolate code's execution.
I want to be able to control the context in which the code is executed, Something that achieve a similar effect to what Script.createContext & Script.runInContext in node.js does (node is using binding to the V8 engine, so i can't emulate their implementation).
Here is the some reason why I want to isolate code execution:
Isolate the code from the global namespace (the window object and the also the DOM) , but I however need to be able reference function call on objects exposed in the context which must be executed synchronous which makes it almost impossible using a WebWorker for isolation.
By isolate the execution of code it would possible also be able to deallocate its definitions when no longer needed (memory management).
I know one may achieve partly isolated execution by loading script into a iframe, this approach is however very heavy and uses a lot memory for a second instance of the DOM which isn't needed for what I'm trying to do.
I need to share constructor definition and also definitions of object which are shared between the isolated containers/contexts which both must run on the main UI thread. Mainly i want to use these isolated containers to host plugins/modules (mini-applications) which each presents and dynamically updates a viewport by calling drawing commands on their own Context2D object.
If these containers are not running on the main UI thread it wold be painfully hard to proxy calls such as ctx.measureText() and ctx.drawImage() would be all useless as image objects can't be created in a Worker.
Does someone know of future specification that would make this possible?
Are there any current (hidden) browser-side APIs that could be used to achieve this?
Would it be better utilize a virtual machine like Goggle's Dart VM and also re-implement my current codebase?
My current codebase is slightly above 20 000 lines of code.
Would it be better to re-implement the framework in *
The closest library I've seen for this is Caja.
Basically, in non-strict javascript code, there are many ways to get access to the global object (window in browsers), making true isolation a very hard problem. Caja does some iframing trickery to patch this, but to be honest I'm not exactly sure how it works.
You can isolate your code from the global namespace with a simple self executing function object:
(function() {
// all your code goes here
// nobody outside of your code can reach your top level variables here
// your top level variables are not on the window object
// this is a protected, but top level variable
var x = 3;
// if you want anything to be global, you can assign it to the window object.
window.myGlobal = {};
function myTopLevelFunction(x,y,z) {
// code here
}
})();
If you want to have multiple ones of these execution contexts and be able to share between them, then you will have to rendezvous via one publicly known location, either a truly global variable or a property on a known DOM object or something like that. It is relatively common to declare one global namespace object and use properties off that for any access to things you're sharing among modules. I know it isn't completely perfect, but it works. Here's an example of the rendevous using a single global namespace object:
// module AAA
(function() {
// module AAA code goes here
// set up global namespace object and whatever references we want to be global
window.myModuleTop = window.myModuleTop || {};
myModuleTop.AAA = {};
myModuleTop.AAA.myFuncA = function() {};
})();
// module BBB
(function() {
// module BBB code goes here
// set up global namespace object and whatever references we want to be global
window.myModuleTop = window.myModuleTop || {};
myModuleTop.BBB = {};
myModuleTop.BBB.myFuncB = function() {};
})();
Couldn't you use a closure like other answers mentioned and then use a shadow dom to ensure that the user can't get to the rest of the dom? Something like this:
var containerNode = someDomNode
var root = containerNode.createShadowRoot()
;(function(root){
var window = null, document = null, history = null,
screen = null, navigator = null, location = null
// isolated code goes here
})(root)
Caveats:
If you create other global objects outside the context of the isolated code, you need to explicitly shadow the variable like I did with window, document, etc, otherwise the isolated code will be to access it.
This won't work in browsers that don't have shadow dom obviously, unless your isolated code doesn't need to interact with the dom at all.
You have to be very careful that objects you do give the isolated code access to doesn't contain references to things you don't want it to have access to. Sometimes this is super error prone to do.
I'm making this suggestion because its plausible that it works, but I have no idea if there are additional ways to get to things like the window and document objects.
Is "standard" namespacing an option? Like:
var myNamespace = {};
myNamespace.myFunc = function() { return true; }
This approach is the simplest I can think of and may be the solution to many problems. Although not a real sandbox, it can let the code less error prone.
There is a proposal for Realms API, which seems to solve similar problems. It is still under discussion, but there is already a polyfill for it - realms-shim.

Calling a class which contains a constructor to setup click events etc?

I am trying to organize my JavaScript. What I would like to do is have a class for each PAGE rather then just entering JavaScript straight into a js - is this good practice?
Let me explain: thinking along the lines of is it a good or bad idea on my variable naming. Remember that this a kind of class so I have to create the class but then I must call it (see below). I am using the prototype and constructor pattern.
// THIS FILE WILL BE SAVED as RESERVATIONPAGE
function ReservationPage(){
// Setup click events in this constructor
$('submit-button').bind('click',this.submit_click);
$('clear-button').bind('click',this.clear_click);
// setup other variables
}
ReservationPage.prototype = {
constructor: ReservationPage,
submit_click : function () {
alert(' you just clicked the submit button');
},
clear_click : function () {
alert('button for clearning the form fields!!');
}
};
Then each page will need a standard js file i.e. not a class / object to instantiate their Page js like above, so a kind of entry point - so hence:
// THIS FILE WILL BE SAVED as ReservationLoad - notice the word Load :-) -- don't know if this is good naning convetion, anybody suggest anything different??
var reservationPage = new ReservationPage(); // CREATES NEW INSTANCE OF THE CLASS/OBJECT ..
I don't any other way of doing it; I would really appreciate any input. I will be using jQuery extensively but this shouldn't matter. I need a standard JS file - no objects to instantiate my objects hence I called in load. This in turn instantiates the object and calls the constructor.
The constructor sets up the click events and then calls the methods.
Is this good?
Any idea where I am going wrong?
I really appreciate some input as I am little lost and want to do it right. I am coming from a C# .NET background, and I know js isn't a proper OOP language but I am trying to treat it like this for better structuring.
This way, all my js files are separate from my HTML and my CSS; there are no events entered into the html, they are all entered via code
Look forward to any info
JavaScript is a proper OOP language. It's not class-based like you're used to (it's prototype-based), but it is absolutely object-oriented -- very much so.
You can simulate classes and class-based inheritance in JavaScript (it's that powerful), which can be useful for people like you (and me) with backgrounds in that world. But classes are not the only way to modularize, which is really what you're talking about doing.
The solution presented in your question creates a function backed by an object (the prototype) which you can then use to create another object (the instance) that then does what you want. That fits the class-based mindset (which may be a good enough reason to do it!), and it's what I do for things I may need to create more than one of, but in some sense it may be overkill for this purpose, since you're not going to have multiple instances of your page objects at any given time. It also means that your click handlers won't work as defined in your example code -- when they're called in response to a click, the this value will be wrong, it won't point to your ReservationPage instance. (Functions in JavaScript are just functions, they're not methods; this is defined by how the function is called, not how it's defined. Surprising to people like us, but it turns out that it's incredibly useful.) That can be worked around, of course, but it requires...working around it.
Since you're only going to need one instantiated object for the page, rather than all the hassle of constructor functions and prototypes such, why not just have one object for the page? And you can use a closure (function) to put scope around that object so it can have easily-accessed variables without having to worry about this and without polluting the global namespace. Here's an example of doing that:
// Create a closure (function) to scope our variables, assign its
// return value to a variable for our page
var reservationPage = (function() {
var somePageData; // A private variable for our page
// Our load function; we'll export that below
function onLoad() {
// Hook some elements
$('submit-button').bind('click', submit_click);
$('clear-button').bind('click', clear_click);
// Set up some page data
somePageData = 42;
}
// A private function for handling the click of the submit button
function submit_click() {
alert('You just clicked the submit button; page data is ' + somePageData);
}
// A private function for handling the click of the clear button
function clear_click() {
alert('You just clicked the clear button; page data is ' + somePageData);
}
// Export any functions we may need outside the closure
return {onLoad: onLoad};
})();
$(document).ready(reservationPage.onLoad);
Note that all of the functions have access to the somePageData variable, and to each other, but we didn't have to worry about this. That's because the functions are declared within the scope in which the somePageData is declared, and so they "close over" it (have access to it). That access is "live", it's not a copy or something.
Note that I've wrapped the entire thing in a function. I'm doing that because we want to avoid just dumping everything in the global namespace, because A) it's the Right Thing(tm), and B) you're combining all of your pages into one file you reuse. You could have four or five (or 20) page objects in your combined file, all definining a var with that same name, and they'd be kept distinct because they're declared inside separate functions. Similarly, the functions for the page (onLoad and the like) are scoped to the function. Using a scoping function like this is called the "module pattern."
I also didn't bother to export any more than I had to make public (just the onLoad function). There's no need for our handlers to be available outside the closure.
Another aspect of the above that can be useful: All of the functions we care about have names. Named functions help tools help you, by telling you where an exception occurred, etc. There's a big difference, from a tool's point of view, between an anonymous function assigned to a variable:
var fname = function() { /* ... */ };
...and a named function:
function fname() { /* ... */ };
This is just a side-note, though; you can do that with the code in your question, too:
ReservationPage.prototype = (function(){
function submit_click() {
alert(' you just clicked the submit button');
}
function clear_click() {
alert('button for clearning the form fields!!');
}
return {
constructor: ReservationPage,
submit_click: submit_click,
clear_click: clear_click
};
})();
Now, I wouldn't use the "one object" pattern for anything general purpose that I might want more than one of; that's what classes (or things like classes) are for. Hence the links above to simulating classes and solving the this problem. :-) But for modularizing and organizing the script that's specific to the page, classes may be overkill.

Categories

Resources