Scope or function hoisting issue? I am not sure - javascript

As below, I made a simple high scores array that is saved to local storage and added to with user prompts.
As an independent file by itself it works great. Or at least it seems to be.
However, when I try to integrate this into my larger application I seem to be having scope issues with my global variable, allScores . The length of the array stays at 0. I checked to see if I have any variable duplicates and I do not.
I've been trying to read about function hoisting and scope. What I am not sure about is why the below code works as an independent file, but when I integrate it into my larger application I have scope issues.
How should I be doing this differently? As I am new to JavaScript my best practices are most likely off. Your guidance is appreciated. Thanks.
var allScores = [];
function saveScore() {
if (allScores.length === 0) {
allScores[0]= prompt('enter score', '');
localStorage ['allScores'] = JSON.stringify(allScores);
}
else if (allScores.length < 3) {
var storedScores = JSON.parse(localStorage ['allScores']);
storedScores = allScores;
var injectScore = prompt('enter score', '');
allScores.push(injectScore);
allScores.sort(function(a, b) {return b-a});
localStorage ['allScores'] = JSON.stringify(allScores);
}
else {
var storedScores = JSON.parse(localStorage ['allScores']);
storedScores = allScores;
var injectScore = prompt('enter score', '');
allScores.pop();
allScores.push(injectScore);
allScores.sort(function(a, b) {return b-a});
localStorage ['allScores'] = JSON.stringify(allScores);
}
document.getElementById('readScores').innerHTML = allScores;
}**

I have refactored your code in an effort to display some practices which may help you and others in the future, since you mentioned best practices in the question. A list of the concepts utilized in this refactoring will be below.
var saveScore = (function () { /* Begin IIFE */
/*
The variables here are scoped to this function only.
They are essentially private properties.
*/
var MAX_ENTRIES = 3;
/*
Move the sorting function out of the scope of saveScore,
since it does not need any of the variables inside,
and possibly prevent a closure from being created every time
that saveScore is executed, depending upon your interpreter.
*/
function sorter(a, b) {
return b - a;
}
/*
As far as your example code shows, you don't appear to need the
allScores variable around all the time, since you persist it
to localStorage, so we have this loadScore function which
pulls it from storage or returns a blank array.
*/
function getScores() {
var scores = localStorage.getItem('scores');
return scores ? JSON.parse(scores) : [];
/*
Please note that JSON.parse *could* throw if "scores" is invalid JSON.
This should only happen if a user alters their localStorage.
*/
}
function saveScore(score) {
/* Implicitly load the scores from localStorage, if available. */
var scores = getScores();
/*
Coerce the score into a number, if it isn't one already.
There are a few ways of doing this, among them, Number(),
parseInt(), and parseFloat(), each with their own behaviors.
Using Number() will return NaN if the score does not explicitly
conform to a textually-represented numeral.
I.e., "300pt" is invalid.
You could use parseInt(score, 10) to accept patterns
such as "300pt" but not anything with
leading non-numeric characters.
*/
score = Number(score);
/* If the score did not conform to specifications ... */
if (isNaN(score)) {
/*
You could throw an error here or return false to indicate
invalid input, depending on how critical the error may be
and how it will be handled by the rest of the program.
If this function will accept user input,
it would be best to return a true or false value,
but if a non-numeric value is a big problem in your
program, an exception may be more appropriate.
*/
// throw new Error('Score input was not a number.');
// return false;
}
scores.push(score);
scores.sort(sorter);
/*
From your code, it looks like you don't want more than 3 scores
recorded, so we simplify the conditional here and move
"magic numbers" to the header of the IIFE.
*/
if (scores.length >= MAX_ENTRIES) {
scores.length = MAX_ENTRIES;
}
/* Limiting an array is as simple as decreasing its length. */
/* Save the scores at the end. */
localStorage.setItem('scores', JSON.stringify(scores));
/* Return true here, if you are using that style of error detection. */
// return true;
}
/* Provide this inner function to the outer scope. */
return saveScore;
}()); /* End IIFE */
/* Usage */
saveScore(prompt('Enter score.', ''));
As you can see, with your score-handling logic encapsulated within this function context, virtually nothing could tamper with the interior without using the interface. Theoretically, your saveScore function could be supplanted by other code, but the interior of the IIFE's context is mutable only to those which have access. While there are no constants yet in standardized ECMAScript, this methodology of the module pattern provides a decent solution with predictable outcomes.
IIFE, an Immediately-Invoked Function Expression, used to create our module
DRY, Don't Repeat Yourself: code reuse
Magic numbers, and the elimination thereof
Type coercion of a string to a number
Error handling, and discernment between exception or return code use. Related: 8, 9

Have you considered JS closures?
Here is some piece to give you an idea..
var scoreboard = (function () {
var _privateVar = "some value"; //this is never exposed globally
var _allScores = [];
return {
getAllScores: function() { //public
return _allScores;
},
saveScore: function(value) { //public
_allScores.push(value);
}
};
})();
alert(scoreboard.getAllScores().length); //0
scoreboard.saveScore(1);
alert(scoreboard.getAllScores().length); //1
alert(scoreboard._privateVar); //undefined
alert(scoreboard._allScores); //undefined
This way your variables and functions are never exposed to the window object and you don't need to worry about duplicates or scopes. The only variable that has to be unique is the name of your closure function (scoreboard in this example case).

Without having access to your environment, the best thing you can do is get use the firefox dev tools (or get firebug) to put a breakpoint in your saveScore function. You can step through line-by-line and check values and even evaluate expressions within the current scope in a console window(REPL).
https://developer.mozilla.org/en-US/docs/Tools/Debugger - with firefox
http://getfirebug.com/javascript - with firebug(a firefox plugin)
If you're doing web-development, these are priceless resources, so invest some time into learning how to use them.(They will save you much more time down the road!)

Related

Is it bad practice to use a function that changes stuff inside a condition, making the condition order-dependent?

var a = 1;
function myFunction() {
++a;
return true;
}
// Alert pops up.
if (myFunction() && a === 2) {
alert("Hello, world!");
}
// Alert does not pop up.
if (a === 3 && myFunction()) {
alert("Hello, universe!");
}
https://jsfiddle.net/3oda22e4/6/
myFunction increments a variable and returns something. If I use a function like that in an if statement that contains the variable which it increments, the condition would be order-dependent.
Is it good or bad practice to do this, and why?
Conditions are order-dependent whether you change the variables used in the condition or not. The two if statements that you used as an example are different and will be different whether you use myFunction() or not. They are equivalent to:
if (myFunction()) {
if (a === 2) {
alert("Hello, world!")
}
}
// Alert does not pop up.
if (a === 3) {
if (myFunction()) {
alert("Hello, universe!")
}
}
In my opinion, the bad practice in your code is not the fact that you change the condition's operands value inside the condition, but the fact that your application state is exposed and manipulated inside a function that does not even accept this state changing variable as a parameter. We usually try to isolate the functions from the code outside their scope and use their return value to affect the rest of the code. Global variables are 90% of the time a bad idea and as your code base gets larger and larger they tend to create problems that are difficult to trace, debug and solve.
It's bad practice, for the following reasons:
The code is far less readable than well-constructed code. This is very important if the code is later examined by a third party.
If myfunction is changed later, the code flow is completely unpredictable, and might require a major documentation update.
Small and simple changes can have drastic effects on the execution of the code.
It looks amateur.
If you have to ask, it's hardly a good practice. Yes, it's a bad practice for exactly the reason you mentioned: changing the order of operands of a logical operation should not affect the outcome, and therefore side effects in conditions should generally be avoided. Especially when they are hidden in a function.
Whether the function is pure (only reads state and does some logic) or whether it mutates state should be obvious from its name. You have several options to fix this code:
put the function call before the if:
function tryChangeA() {
a++;
return true;
}
var ok = tryChangeA();
if (ok && a == 2) … // alternatively: if (a == 2 && ok)
make the mutation explicit inside the if:
function testSomething(val) {
return true;
}
if (testSomething(++a) && a == 2) …
put the logic inside the called function:
function changeAndTest() {
a++;
return a == 2;
}
if (changeAndTest()) …
MyFunction violates a principle called Tell, Don't Ask.
MyFunction changes the state of something, thus making it a command. If MyFunction succeeds or somehow fails to increment a, it shouldn't return true or false. It was given a job and it must either try to succeed or if it finds that job is impossible at the moment, it should throw an exception.
In the predicate of an if statement, MyFunction is used as a query.
Generally speaking, queries should not exhibit side-effects (i.e. not changing things that can be observed). A good query can be treated like a calculation in that for the same inputs, it should produce the same outputs (sometimes described as being "idempotent").
It's also important to know that these are guidelines to help you and others reason about the code. Code that can cause confusion, will. Confusion about code is a hatchery for bugs.
There are good patterns like the Trier-Doer pattern which can be used like your code example, but everyone reading it must understand what's happening though names and structure.
The code presents more then one bad practice actually:
var a = 1;
function myFunction() {
++a; // 1
return true;
}
if (myFunction() && a === 2) { // 2, 3, 4
alert("Hello, world!")
}
if (a === 3 && myFunction()) { // 2, 3, 4
alert("Hello, universe!")
}
Mutates a variable in a different scope. This may or may not be a problem, but usually it is.
Calls a function inside an if statement condition.
This do not cause problems in itself, but it's not really clean.
It's a better practice to assign the result of that function to a variable, possibly with a descriptive name. This will help whoever reads the code to understand what exactly you want to check inside that if statement. By the way, the function always return true.
Uses some magic numbers.
Imagine someone else reading that code, and it is part of a large codebase. What those numbers mean? A better solution would be to replace them with well named constants.
If you want to support more messages, you need to add more conditions.
A better approach would be to make this configurable.
I would rewrite the code as follows:
const ALERT_CONDITIONS = { // 4
WORLD_MENACE: 2,
UNIVERSE_MENACE: 3,
};
const alertsList = [
{
message: 'Hello world',
condition: ALERT_CONDITIONS.WORLD_MENACE,
},
{
message: 'Hello universe',
condition: ALERT_CONDITIONS.UNIVERSE_MENACE,
},
];
class AlertManager {
constructor(config, defaultMessage) {
this.counter = 0; // 1
this.config = config; // 2
this.defaultMessage = defaultMessage;
}
incrementCounter() {
this.counter++;
}
showAlert() {
this.incrementCounter();
let customMessageBroadcasted = false;
this.config.forEach(entry => { //2
if (entry.condition === this.counter) {
console.log(entry.message);
customMessageBroadcasted = true; // 3
}
});
if (!customMessageBroadcasted) {
console.log(this.defaultMessage)
}
}
}
const alertManager = new AlertManager(alertsList, 'Nothing to alert');
alertManager.showAlert();
alertManager.showAlert();
alertManager.showAlert();
alertManager.showAlert();
A class with a precise function, that use its own internal state, instead of a bunch of functions that rely on some variable that could be located anywhere. Whether to use a class or not, it's a matter of choice. It could be done in a different way.
Uses a configuration. That means that would you want to add more messages, you don't have to touch the code at all. For example, imagine that configuration coming from a database.
As you may notice, this mutates a variable in the outer scope of the function, but in this case it does not cause any issue.
Uses constants with a clear name. (well, it could be better, but bear with me given the example).
A function that changes stuff. What is the world coming too? This function must change stuff and return different values each time its called.
Consider the dealCard function for a deck of playing cards. it deals the cards 1-52. Each time it is called it should return a different value.
function dealCard() {
++a;
return cards(a);
}
/* we'll just assume the array cards is shuffled */
/* for the sake of brevity we'll assume the deck is infinite and doesn't loop at 52*/

Garbage-collected cache via Javascript WeakMaps

I want to cache large objects in JavaScript. These objects are retrieved by key, and it makes sense to cache them. But they won't fit in memory all at once, so I want them to be garbage collected if needed - the GC obviously knows better.
It is pretty trivial to make such a cache using WeakReference or WeakValueDictionary found in other languages, but in ES6 we have WeakMap instead, where keys are weak.
So, is it possible to make something like a WeakReference or make garbage-collected caches from WeakMap?
There are two scenarios where it's useful for a hash map to be weak (yours seems to fit the second):
One wishes to attach information to an object with a known identity; if the object ceases to exist, the attached information will become meaningless and should likewise cease to exist. JavaScript supports this scenario.
One wishes to merge references to semantically-identical objects, for the purposes of reducing storage requirements and expediting comparisons. Replacing many references to identical large subtrees, for example, with references to the same subtree can allow order-of-magnitude reductions in memory usage and execution time. Unfortunately JavaScript doesn't support this scenario.
In both cases, references in the table will be kept alive as long as they are useful, and will "naturally" become eligible for collection when they become useless. Unfortunately, rather than implementing separate classes for the two usages defined above, the designers of WeakReference made it so it can kinda-sorta be usable for either, though not terribly well.
In cases where the keys define equality to mean reference identity, WeakHashMap will satisfy the first usage pattern, but the second would be meaningless (code which held a reference to an object that was semantically identical to a stored key would hold a reference to the stored key, and wouldn't need the WeakHashMap to give it one). In cases where keys define some other form of equality, it generally doesn't make sense for a table query to return anything other than a reference to the stored object, but the only way to avoid having the stored reference keep the key alive is to use a WeakHashMap<TKey,WeakReference<TKey>> and have the client retrieve the weak reference, retrieve the key reference stored therein, and check whether it's still valid (it could get collected between the time the WeakHashMap returns the WeakReference and the time the WeakReference itself gets examined).
is it possible to make WeakReference from WeakMap or make garbage-collected cache from WeakMap ?
AFAIK the answer is "no" to both questions.
It's now possible thanks to FinalizationRegistry and WeakRef
Example:
const caches: Record<string, WeakRef<R>> = {}
const finalizer = new FinalizationRegistry((key: string) =>
{
console.log(`Finalizing cache: ${key}`)
delete caches[key]
})
function setCache(key: string, value: R)
{
const cache = getCache(key)
if (cache)
{
if (cache === value) return
finalizer.unregister(cache)
}
caches[key] = new WeakRef(value)
finalizer.register(value, key, value)
}
function getCache(key: string)
{
return caches[key]?.deref()
}
As the other answers mentioned, unfortunately there's no such thing as a weak map, like there is in Java / C#.
As a work around, I created this CacheMap that keeps a maximum number of objects around, and tracks their usage over a set period of time so that you:
Always remove the least accessed object, when necessary
Don't create a memory leak.
Here's the code.
"use strict";
/**
* This class keeps a maximum number of items, along with a count of items requested over the past X seconds.
*
* Unfortunately, in JavaScript, there's no way to create a weak map like in Java/C#.
* See https://stackoverflow.com/questions/25567578/garbage-collected-cache-via-javascript-weakmaps
*/
module.exports = class CacheMap {
constructor(maxItems, secondsToKeepACountFor) {
if (maxItems < 1) {
throw new Error("Max items must be a positive integer");
}
if (secondsToKeepACountFor < 1) {
throw new Error("Seconds to keep a count for must be a positive integer");
}
this.itemsToCounts = new WeakMap();
this.internalMap = new Map();
this.maxItems = maxItems;
this.secondsToKeepACountFor = secondsToKeepACountFor;
}
get(key) {
const value = this.internalMap.get(key);
if (value) {
this.itemsToCounts.get(value).push(CacheMap.getCurrentTimeInSeconds());
}
return value;
}
has(key) {
return this.internalMap.has(key);
}
static getCurrentTimeInSeconds() {
return Math.floor(Date.now() / 1000);
}
set(key, value) {
if (this.internalMap.has(key)) {
this.internalMap.set(key, value);
} else {
if (this.internalMap.size === this.maxItems) {
// Figure out who to kick out.
let keys = this.internalMap.keys();
let lowestKey;
let lowestNum = null;
let currentTime = CacheMap.getCurrentTimeInSeconds();
for (let key of keys) {
const value = this.internalMap.get(key);
let totalCounts = this.itemsToCounts.get(value);
let countsSince = totalCounts.filter(count => count > (currentTime - this.secondsToKeepACountFor));
this.itemsToCounts.set(value, totalCounts);
if (lowestNum === null || countsSince.length < lowestNum) {
lowestNum = countsSince.length;
lowestKey = key;
}
}
this.internalMap.delete(lowestKey);
}
this.internalMap.set(key, value);
}
this.itemsToCounts.set(value, []);
}
size() {
return this.internalMap.size;
}
};
And you call it like so:
// Keeps at most 10 client databases in memory and keeps track of their usage over a 10 min period.
let dbCache = new CacheMap(10, 600);

Is it possible to 'import' part of a namespace (a module) into the current scope?

I have one module called functionalUtilities which contains a number of utility functions. An abbreviated version looks something like this:
MYAPP.functionalUtilities = (function() {
function map(func, array) {
var len = array.length;
var result = new Array(len);
for (var i = 0; i < len; i++)
result[i] = func(array[i]);
return result;
}
return {
map:map,
};
})();
I then have a second module which contains core code:
MYAPP.main = (function() {
//Dependencies
var f = MYAPP.functiionalUtilities;
//Do stuff using dependencies
f.map(...)
})()
It seems messy and annoying having to remember to type f.map each time I want to use map. Of course, in the dependencies, I could go though each of my functionalUtilities typing:
var map = f.map,
forEach = f.forEach,
etc.
but I wondered whether there is a better way of doing this? A lot of articles on namespacing that I've read mention aliasing, but don't suggest a way to sort of 'import all of the contents of an object into scope'.
Thanks very much for any help,
Robin
[edit] Just to clarify, I would like to use my functional utilities (map etc) within MYAPP.main without having to preface them every time with f.
This is possible by going through each function in MYAPP.functionalUtilities and assigning to a locally scoped variable within MYAPP.main. But the amount of code this requires doesn't justify the benefit, and it's not a general solution.
As I said in the comment. There is no real way of automatically defining local variables out of object properties. The only thing that comes to my mind is using eval:
for (var i in MYAPP.functiionalUtilities) {
eval("var " + i + " = MYAPP.functiionalUtilities[i];");
}
But I wouldn't use this method, since you could have object properties with strings as keys like this:
var obj = {
"my prop": 1
};
"my prop" might be a valid key for an object property but it's not a valid identifier. So I suggest to just write f.prop or define your local variables manually with var prop = f.prop;
EDIT
As Felix Kling mentioned in the comment section, there is in fact another way of achieving this, using the with statement, which I don't really know much about except for that it is deprectated.
Here's a late answer - I feel like adding to basilikum's answer.
1) The with keyword could be useful here!
with(MYAPP.functiionalUtilities) {
map(console.log, [ 'this', 'sorta', 'works', 'quite', 'nicely!' ]);
// Directly reference any properties within MYAPP.functiionalUtilities here!!
};
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/with
The with keyword is, in some ways, intended for exactly this situation. It should, of course, be noted that the mozilla developer link discourages use of with, and with is also forbidden in strict mode. Another issue is that the with statement causes its parameter to become the head of the scope chain, which means that it will always be checked first for all identifiers for all statements within the with block. This could be a performance hit.
2) An improvement to basilikum's answer
While a function call cannot add items to its parent-frame's scope, there is a way to avoid typing out a for-loop each time you wish to add a list of items to a namespace.
// First, define a multi-use function we can use each time
// This function returns a string that can be eval'd to import all properties.
var import = function(module) {
var statements = [];
for (var k in module) statements.push('var ' + i + ' = module["' + i + '"]');
return statements.join(';');
};
// Now, each time a module needs to be imported, just eval the result of import
eval(import(MYAPP.functiionalUtilities));
map(console.log, [ 'this', 'works!' ]);
The idea here is to replace the need to write a for-loop with something like eval(import(MYAPP.functiionalUtilities));.
The danger here, as basilikum has stated, is that module properties need to be valid identifier names.

getting the name of a variable through an anonymous function

Is it possible to find the name of an anonymous function?
e.g. trying to find a way to alert either anonyFu or findMe in this code http://jsfiddle.net/L5F5N/1/
function namedFu(){
alert(arguments.callee);
alert(arguments.callee.name);
alert(arguments.callee.caller);
alert(arguments.caller);
alert(arguments.name);
}
var anonyFu = function() {
alert(arguments.callee);
alert(arguments.callee.name);
alert(arguments.callee.caller);
alert(arguments.caller);
alert(arguments.name);
}
var findMe= function(){
namedFu();
anonyFu();
}
findMe();
This is for some internal testing, so it doesn't need to be cross-browser. In fact, I'd be happy even if I had to install a plugin.
You can identify any property of a function from inside it, programmatically, even an unnamed anonymous function, by using arguments.callee. So you can identify the function with this simple trick:
Whenever you're making a function, assign it some property that you can use to identify it later.
For example, always make a property called id:
var fubar = function() {
this.id = "fubar";
//the stuff the function normally does, here
console.log(arguments.callee.id);
}
arguments.callee is the function, itself, so any property of that function can be accessed like id above, even one you assign yourself.
Callee is officially deprecated, but still works in almost all browsers, and there are certain circumstances in which there is still no substitute. You just can't use it in "strict mode".
You can alternatively, of course, name the anonymous function, like:
var fubar = function foobar() {
//the stuff the function normally does, here
console.log(arguments.callee.name);
}
But that's less elegant, obviously, since you can't (in this case) name it fubar in both spots; I had to make the actual name foobar.
If all of your functions have comments describing them, you can even grab that, like this:
var fubar = function() {
/*
fubar is effed up beyond all recognition
this returns some value or other that is described here
*/
//the stuff the function normally does, here
console.log(arguments.callee.toString().substr(0, 128);
}
Note that you can also use argument.callee.caller to access the function that called the current function. This lets you access the name (or properties, like id or the comment in the text) of the function from outside of it.
The reason you would do this is that you want to find out what called the function in question. This is a likely reason for you to be wanting to find this info programmatically, in the first place.
So if one of the fubar() examples above called this following function:
var kludge = function() {
console.log(arguments.callee.caller.id); // return "fubar" with the first version above
console.log(arguments.callee.caller.name); // return "foobar" in the second version above
console.log(arguments.callee.caller.toString().substr(0, 128);
/* that last one would return the first 128 characters in the third example,
which would happen to include the name in the comment.
Obviously, this is to be used only in a desperate case,
as it doesn't give you a concise value you can count on using)
*/
}
Doubt it's possible the way you've got it. For starters, if you added a line
var referenceFu = anonyFu;
which of those names would you expect to be able to log? They're both just references.
However – assuming you have the ability to change the code – this is valid javascript:
var anonyFu = function notActuallyAnonymous() {
console.log(arguments.callee.name);
}
which would log "notActuallyAnonymous". So you could just add names to all the anonymous functions you're interested in checking, without breaking your code.
Not sure that's helpful, but it's all I got.
I will add that if you know in which object that function is then you can add code - to that object or generally to objects prototype - that will get a key name basing on value.
Object.prototype.getKeyByValue = function( value ) {
for( var prop in this ) {
if( this.hasOwnProperty( prop ) ) {
if( this[ prop ] === value )
return prop;
}
}
}
And then you can use
THAT.getKeyByValue(arguments.callee.caller);
Used this approach once for debugging with performance testing involved in project where most of functions are in one object.
Didn't want to name all functions nor double names in code by any other mean, needed to calculate time of each function running - so did this plus pushing times on stack on function start and popping on end.
Why? To add very little code to each function and same for each of them to make measurements and calls list on console. It's temporary ofc.
THAT._TT = [];
THAT._TS = function () {
THAT._TT.push(performance.now());
}
THAT._TE = function () {
var tt = performance.now() - THAT._TT.pop();
var txt = THAT.getKeyByValue(arguments.callee.caller);
console.log('['+tt+'] -> '+txt);
};
THAT.some_function = function (x,y,z) {
THAT._TS();
// ... normal function job
THAT._TE();
}
THAT.some_other_function = function (a,b,c) {
THAT._TS();
// ... normal function job
THAT._TE();
}
Not very useful but maybe it will help someone with similar problem in similar circumstances.
arguments.callee it's deprecated, as MDN states:
You should avoid using arguments.callee() and just give every function
(expression) a name.
In other words:
[1,2,3].forEach(function foo() {
// you can call `foo` here for recursion
})
If what you want is to have a name for an anonymous function assigned to a variable, let's say you're debugging your code and you want to track the name of this function, then you can just name it twice, this is a common pattern:
var foo = function foo() { ... }
Except the evaling case specified in the MDN docs, I can't think of any other case where you'd want to use arguments.callee.
No. By definition, an anonymous function has no name. Yet, if you wanted to ask for function expressions: Yes, you can name them.
And no, it is not possible to get the name of a variable (which references the function) during runtime.

Javascript "Variable Variables": how to assign variable based on another variable?

I have a set of global counter variables in Javascript:
var counter_0 = 0;
var counter_1 = 0;
var counter_2 = 0;
etc
I then have a Javascript function that accepts an 'index' number that maps to those global counters. Inside this function, I need to read and write to those global counters using the 'index' value passed to the function.
Example of how I'd like it to work, but of course doesn't work at all:
function process(index) {
// do some processing
// if 'index' == 0, then this would be incrementing the counter_0 global variable
++counter_+index;
if (counter_+index == 13)
{
// do other stuff
}
}
I hope what I'm trying to accomplish is clear. If not I'll try to clarify. Thanks.
EDIT Clarification:
I'm not trying to increment the name of the counter, but rather the value the counter contains.
Looks like an array to me, or am I missing something?
var counters = [0,0,0];
function process(index) {
++counters[index];
/* or ++counters[index]+index, not sure what you want to do */
if (counters[index] === 13) {
/* do stuff */
}
}
function process(index) {
// do some processing
var counter;
eval('counter = ++counter_'+index);
if (counter == 13)
{
// do other stuff
}
}
Make sure that index really is an integer, otherwise mayhem could ensue.
Edit: Others have pointed out that you should use an array if you can. But if you are stuck with the named global variables then the above approach will work.
Edit: bobince points out that you can use the window object to access globals by name, and so deserves any credit for the following:
function process(index) {
// do some processing
var counter = ++window['counter_' + index];
if (counter == 13)
{
// do other stuff
}
}
Other answers have said "don't use eval()", but not why. Here's an explanation from MDC:
Don't use eval!
eval() is a dangerous function, which
executes the code it's passed with the
privileges of the caller. If you run
eval() with a string that could be
affected by a malicious party, you may
end up running malicious code on the
user's machine with the permissions of
your webpage / extension.
There are safe alternatives to eval()
for common use-cases.
The eval() javascript function will allow you to accomplish this. However it's generally frowned upon. Your question didn't explicitly exclude arrays. Arrays would definitely be more appropriate for the pattern you've described.

Categories