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.
Related
I find myself writing the following JavaScript more and more and I would like to know if this is a common pattern and if so, what is it called?
Part of the code and pattern:
var fruits = ["pear", "apple", "banana"];
var getNextFruit = function() {
var _index = 0,
_numberOfFruits = fruits.length;
getNextFruit = function() {
render(fruits[_index]);
_index = (_index + 1) % _numberOfFruits;
}
getNextFruit();
};
I have a function which takes no parameters, inside the function I redefine the function and immediately call it. In a functional language this might be a function being returned, JavaScript just makes it easier because you can reuse the name of the function. Thus you are able to extend the functionality without having to change your implementation.
I can also imagine this pattern to be very useful for memoization where your "cache" is the state we wrap around.
I even sometimes implement this with a get or a set method on the function where I can get the state if it's meaningful. The added fiddle shows an example of this.
Because this is a primarily JavaScript oriented question: The obligatory fiddle
I have a function which takes no parameters, inside the function I redefine the function and immediately call it.
Is this is a valid pattern and what is it called?
A function redefining itself is usually an antipattern, as it complicates stuff a lot. Yes, it sometimes can be more efficient to swap out the whole function than to put an if (alreadyInitialised) condition inside the function, but it's very rarely worth it. When you need to optimise performance, you can try and benchmark both approaches, but otherwise the advice is to keep it as simple as you can.
The pattern "initialises itself on the first call" is known as laziness for pure computations (in functional programming) and as a singleton for objects (in OOP).
However, most of the time there's no reason to defer the initialisation of the object/function/module whatever until it is used for the first time. The ressources taken for it (both time and memory) are insignificant, especially when you are sure that you will need it in your program at least once. For that, use an IIFE in JavaScript, which is also known as the module pattern when creating an object.
Creating a function via a closure is a pretty common pattern in JavaScript. I would personally do that differently:
var fruits = ["pear", "apple", "banana"];
var getNextFruit = function(fruits) {
var index = 0,
numberOfFruits = fruits.length;
function getNextFruit() {
render(fruits[_index]);
index = (_index + 1) % numberOfFruits;
}
return getNextFruit;
}(fruits);
There's no good reason (in my opinion) to clutter up the variable names with leading underscores because they're private to the closure anyway. The above also does not couple the workings of the closure with the external variable name. My version can be made a reusable service:
function fruitGetter(fruits) {
var index = 0, numberOfFruits = fruits.length;
function getNextFruit() {
render(fruits[_index]);
index = (_index + 1) % numberOfFruits;
}
return getNextFruit;
}
// ...
var getNextFruit = fruitGetter(someFruits);
var otherFruits = fruitGetter(["kumquat", "lychee", "mango"]);
I've some functions, stored in a collection/array and would like to get the key (function-name) without retyping it. Is there any short way to access it?
var functions_collection = {
"function_x": function() {
var name = "function_x";
// name = this.key; <- how to get the key/function-name "function_x"?
// some more code like:
$(".function_x .button").val();
alert(name);
}
}
Edit: I'd like to avoid retyping the "function_x" inside the function itself and prefer to call it like this.key.
Sorry for the weird topic and thanks in advance!
Solution: A lot of good answers, but I was just looking for this snipped:
Object.keys(this)
I'm not sure it's what you want but you can do this :
var functions_collection = {};
(function(name){
functions_collection[name] = function(){
// use name, which is the function
alert(name);
};
})("function_x");
I'm not really sure it's better. But depending on your (unspecified) goal, there's probably a better solution.
To get the name of the objects keys, you can use Object.getOwnPropertyNames(this) or in newer browsers just Object.keys(this), and that will get you an array of all and any keys the this object has :
var functions_collection = {
function_x: function() {
var name = Object.keys(this);
console.log(name);
}
}
FIDDLE
In my opinion you´d need to change you above code since you are having anonymous functions which have no name - a change like this should work:
var functions_collection = {
'function_x' : function function_x () {
var myName = arguments.callee.name;
alert(myName);
}
}
see http://jsfiddle.net/9cN5q/1/
There are several ways you could go here. Some are good ideas, some are not.
First, some bad ideas
Bad idea: arguments.callee.name
This translates most directly to what you ask. arguments.callee is
a reference to the function you're currently in. However, it's
considered bad
practice,
and you should avoid using it unless you have a really good reason.
Bad idea: Currying
After constructing the function, bind its own name into it as a parameter:
var functions_collection = {
"function_x": function(name) {
alert(name);
},
//more functions
};
for (var name in functions_collection) {
if (typeof functions_collection[name] === "function") {
functions_collection[name] =
functions_collection[name].bind(functions_collection, name);
}
}
Currying is useful for lots of things in JavaScript, and it's a great idea in many situations. Not here, though, and I'll explain why below.
Bad idea: Use a local parameter and iterate through the containing object
var functions_collection = {
"function_x": function(name) {
alert(name);
},
//more functions
};
for (var name in functions_collection) {
if (typeof functions_collection[name] === "function") {
functions_collection[name](name);
}
}
Of course, the obvious problem with this one is that you might not want to call every function in the collection at once. The more fundamental problem is that it continues the trend of dangerously tight coupling. This is a bad thing, potentially a Very Bad Thing that will cost you all kinds of headaches down the line.
Now the "right" way
Change your whole approach. Forget trying to recycle class names from your HTML; just keep it simple.
Good idea: Use a local variable
Who cares what you name your functions? If you know which HTML classes you want them to touch, just code them that way.
var functions_collection = {
"function_x": function() {
var name = "function_x"; //or "button" or any other class name
alert(name);
},
//more functions
};
functions_collection.function_x();
Good idea: Pass a parameter
You're already calling the function, right? So there's probably already code somewhere with access to the name you want.
var functions_collection = {
"function_x": function(name) {
alert(name);
},
//more functions
};
functions_collection.function_x("function_x"); //or any other class name
Now you can use function_x on any class in your HTML, even if it doesn't match the function name:
functions_collection.function_x("function_y");
functions_collection.function_x("class_z");
functions_collection.function_x("button");
I've saved the simplest for last because I think you're making a mistake by trying to be "clever", if that makes sense. There are significant risks in your approach, and the payoff isn't going to be worth it.
Why the bad ideas are bad and the good ideas are good
Other than the arguments.callee.name option, the reason 2 and 3 are bad in this case is tight coupling. You're coupling function_x to the structure of functions_collection; you're coupling behavior to a variable name; and worst of all, you're coupling JS variables to the class names of HTML elements. This will make your code extremely fragile, and when you want to change something (and you will), get ready for a world of hurt.
For example, what happens if you reorganize your HTML? The page probably breaks, since the structure of your JS has to match the classes in your HTML/CSS. You'll have to rename or rewrite functions_collection and all others like it, or else you'll have to carefully plan new HTML around the JS you already have.
What happens if you want to use a JS minifier? Depends, but if you allow it to change member names in object literals, it completely breaks everything and you have to start over with one of the "good" ideas.
Now, what do you get in exchange for this inflexibility? You save an extra line at the beginning of each function. Not worth it, IMHO. Just bite the bullet and keep it simple.
Supposing that the variable name has the same name as its containing function:
var keys = [];
for (var p in functions_collection) {
if (typeof(functions_collection[p]) == 'function') {
keys.push(p);
}
}
And there you have it, an array with all the function names.
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!)
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.
So I am just wondering why the following code dosen't work. I am looking for a similar strategy to put the for loop in a variable.
var whatever = for (i=1;i<6;i++) {
console.log(i)
};
Thanks!
Because a for loop is a statement and in JavaScript statements don't have values. It's simply not something provided for in the syntax and semantics of the language.
In some languages, every statement is treated as an expression (Erlang for example). In others, that's not the case. JavaScript is in the latter category.
It's kind-of like asking why horses have long stringy tails and no wings.
edit — look into things like the Underscore library or the "modern" add-ons to the Array prototype for "map" and "reduce" and "forEach" functionality. Those allow iterative operations in an expression evaluation context (at a cost, of course).
I suppose what you look for is function:
var whatever = function(min, max) {
for (var i = min; i < max; ++i) {
console.log(i);
}
}
... and later ...
whatever(1, 6);
This approach allows you to encapsulate the loop (or any other code, even declaring another functions) within a variable.
Your issue is that for loops do not return values. You could construct an array with enough elements to hold all the iterations of your loop, then assign to it within the loop:
arry[j++] = i;
You can do this, but it seems that you might want to check out anonymous functions. With an anonymous function you could do this:
var whatever = function(){
for (var i=1;i<6;i++) {
console.log(i);
}
};
and then
whatever(); //runs console.log(i) i times.