I'm reading this article
and there is a paragraph:
If you ever find yourself needing to explicitly scope a variable
inside a function you can use an anonymous function to do this. You
can actually create an anonymous function and then execute it straight
away and all the variables inside will be scoped to the anonymous
function:
(function() {
var myProperty = "hello world";
alert(myProperty);
})();
alert(typeof(myProperty)); // undefined
I met with this already but still need some clarification why should I need to explicitly scope a variable inside a function when variables are implicitly scoped inside a function in Javascript.
Could you explain the purpose of this?
thank you
for (var i = 0; i < 10; i++) {
setTimeout(function() { console.log(i) }, 10);
}
// alerts 10, 10 times
for (var i = 0; i < 10; i++) {
(function(i) {
// explicitly scope i
setTimout(function() { console.log(i) }, 10);
})(i);
}
When generating functions inside other functions and accessing variables up the scope chain through closure scope it may be useful to "explicitly scope" a variable inside the outer function.
Although this is an anti pattern. The correct solution would be
var generateLogger = function(i) {
return function() { console.log(i); };
}
for (var i = 0; i < 10; i++) {
setTimeout(generateLogger(i), 10);
}
Since generating functions in a loop is inefficient and bad practice.
There are no real use cases of "explicitly scoping" variables that can't be avoided by not creating functions inside other functions.
Related
This has always been a lingering question for me: If block scopes are created when a let or const identifier is enclosed within curly brackets, then how is the let identifier in the initialization statement of a for loop not available in the enclosing scope but is instead available inside the curly brackets of the for loop?
(function() {
for (let i = 0; i < 5; i++) {
console.log(i) // logs current value of i
}
console.log(i) // referenceError
})()
That's just how things work. A variable declared at the top of a for loop like that is only visible inside the current iteration's for block. You can think of it a bit like this:
<loop> {
let i = getCount();
console.log(i) // logs current value of i
}
where getCount runs the logic that increments i.
Variables declared with let are block-scoped - it wouldn't make sense for i to be referenceable outside. If the i was visible outside of the for, what would you expect its value to be? You already have a separate i binding for every iteration of the loop. It wouldn't make sense to somewhat-arbitrarily pick one of those bindings to be visible outside.
Because it's a local variable in the for loop and not outside it. If you had declared it outside the loop, you could access it correctly.
(function() {
let i;
for (i = 0; i < 5; i++) {
console.log(i);
}
console.log(i);
})();
If you don't want to change your syntax, you could use var.
(function() {
for (var i = 0; i < 5; i++) {
console.log(i);
}
console.log(i);
// this should work.
})()
Otherwise, a variable declared with let or const has what's called Block Scope meaning that it's only visible inside the curly braces it was defined in. That's essentially the fundamental difference between let and var. A variable declared with var has Function Scope (It's visible throughout the function, even before it's lexical definition, it's value will just be undefined)
(function() {
console.log(i); // undefined
var i = 10;
console.log(i); // 10
})();
But the example above will only work if there is no "use strict" anywhere.
calValue: function (data) {
var self = this;
var values = data.values;
for (var i = 0; i < data.length; i++) {
if(data.condition == 0){
(function (values) {
for (i = 0; i < values.length; i++) {
}
})(values)
}
else{
//do sth else
}
}
}
my understanding is each funtion has its own context and the variable being declared inside it, will only be effetive inside. Like above code snippet, i expect the "i" variable inside the inner for-loop won't impact the outer one "i" variable. However, the fact is it do affect.
Could someone please help explain? Thanks.
With var declarations, scope is at the function level. Such declarations are interpreted as if they appeared at the very start of the enclosing function.
In modern JavaScript environments, the let declaration allows you to create variables scoped to local blocks of statements. (Also const for non-modifiable symbols.)
In your case, that inner i in the nested function just refers to that i declared externally. From inside a function, you can always "see" out, but you can't "see" in. That's how scope works.
This is my code:
function func(){
for(i=0; i < 5; i++){
alert('g');
}
}
for(i=0; i < 5; i++){
func();
alert('h');
}
What I expected was: gggghgggghgggghgggghggggh
but what received was just ggggh
I found out that's because there is function scope, not block scope in JS. What I'd like to know is how to preserve such a behavior. I mean to force something like block scope. Otherwise it's very easy to make really nasty bugs - e.g. while using a function somebody else wrote or the one you wrote yourself, but a few months earlier.
This actually doesn't have to do with function vs. block scope - it has to do with implicit global variables.
The short story: You are accidentally creating a global variable in your for loop - if you use for(var i=0; rather than for(i=0; you'll get the expected results.
The slightly longer version:
alert("typeof i: " + typeof i);
// Alerts "typeof i: undefined"
function func() {
// References the *global* variable `i`
for(i=0; i < 5; i++){
alert('g');
}
}
// Creates a *global* variable `i` and sets it to 0
for(i=0; i < 5; i++) {
alert("i at start of iteration: " + i);
// Alerts "i at start of iteration: 0"
func();
// `func` has just altered *global* state - here's the proof
alert("i after call to func: " + i);
// Alerts "i at start of iteration: 5"
alert('h');
}
alert("typeof i: " + typeof i);
// Alerts "typeof i: number
// `i` is now in the global scope.
// Left as an exercise for the reader:
// try it again with `var i=0;` in both of the `for` loops.
Issue with variable i's scope.
function func(){
for(var i=0; i < 5; i++){
alert('g');
}
}
for(var i=0; i < 5; i++){
func();
alert('h');
}
Try declaring i as a local variable in your function:
function func(){
var i;
for(i=0; i < 5; i++){
alert('g');
}
}
As already stated in other answers, your code doesn't work because you don't declare the i variable with var, which automatically makes it global. So not really a function versus block scope issue. If you use var to declare your variable(s) then the scope of the variable(s) will be limited to the same function where the declaration is including within any nested functions, or global if not in a function.
If you use the same variable name in nested functions then the one in the inner scope is said to "shadow" the outer one; the inner function can't access the outer function's variable of that name in that case since using that name will only give it its own variable. So to speak. Though you can access global variables by treating them as properties of the window object.
"What I'd like to know is how to preserve such a behavior. I mean to force something like block scope. Otherwise it's very easy to make really nasty bugs..."
Well no, once you start using var correctly it isn't difficult to avoid those bugs. But you can force block scope by introducing an immediately invoked anonymous function:
(function() {
// variables created in this function are accessible
// only within this function
var x, y, z;
})();
console.log(typeof x); // "undefined" (x can't be seen from outside the function)
The parentheses around function(){} are required so that the function is interpreted as an expression and not a function statement; the extra () at the end is to cause that function expression to be executed immediately. A common use for this structure is to enclose an entire script in it so that no variables or functions in it become global and thus don't interact with other scripts loaded from other JS include files. (You can have pseudo-global variables within the block shared between several functions also declared within the block.)
So for example if you wanted to make the body of a for statement into a "block" with scope you could do this:
for (var i = 0; i < something; i++) {
(function() {
var x = i;
// do something with x
})();
}
How would I do something along these line? Do I need to use new Function()?
var bunchOfFunctionNames = ["functions", "of", "functions"];
for (var i = 0; i < eventTypes.length; i++) {
myObject.prototype[bunchOfFunctionNames[i]] = function() {
// do some stuff
};
};
In re: my comment about getting at the value of "i" in your function creation loop, the problem is going to be that "i" will be shared by all the functions. In other words, there's just one "i" variable in the outer function (where the loop is), so if you reference "i" in the functions you're creating, well, it won't work out properly (probably). All of them will see "i" as being the "length" of your array of names, because that's what it'll be at the end of the loop.
There are several ways to deal with that. If you were a Scheme programmer at heart, you'd do this:
for (var i = 0; i < eventTypes.length; i++) {
myObject.prototype[bunchOfFunctionNames[i]] = (function(copy_of_i) {
return function() {
// do some stuff, merrily referencing "copy_of_i"
}
})(i);
};
That "anonymous" function inside the loop provides a new scope, so because "i" is passed in as a parameter (called "copy_of_i", even though I hate underscores in variable names), it's safe for the real function to use it. The anonymous function returns the real function, so that's the function that ends up in the object prototype.
Another option would be to pull that anonymous function out of the loop:
function makeFunctionForName(i) {
return function() {
// do something, and now "i" is the parameter and "safe"
};
}
for (var i = 0; i < eventTypes.length; i++) {
myObject.prototype[bunchOfFunctionNames[i]] = makeFunctionForName(i);
}
Here's some JavaScript:
linkElem.click(function () {
var data = linkElem.data();
alert(''+data.mls + ' ' + data.id);
});
It works.
linkElem is a local variable that I create in a loop inside a function. I assign some data to it with jQuery's .data(). If I did not call .click(), linkElem would be reassigned during the loop and then recycled after the function returns. However, I have created an anonymous function which references linkElem. So I am no longer sure what is going on.
My guess is that all of the anonymous functions and linkElems created during the loop are given UIDs of some kind and moved to persistent/global scope. Is this correct? Gratuitous detail would be much appreciated.
Yes, your description is pretty close. The local storage for a Javascript function call is just a block of memory allocated for local variables. If you "capture" that by creating another function inside a called function, then the storage is retained and the local variables carry on with their lives, unaware that the function that gave them birth might be long dead.
It's important to keep in mind that only functions create such storage — things like brace-enclosed loop bodies are not separate storage areas. Thus a common error is to declare a variable in a function and re-use it among several functions created in a loop. That's not inherently wrong, but the effect can be surprising:
function whatever() {
for (var i = 0; i < 3; ++i) {
setTimeout(function() { alert(i); }, 5000);
}
}
If you run that, you'll see three alerts that all say "3". Why? Because they all share the same "i" variable. You can avoid that by introducing another function layer:
function whatever() {
for (var i = 0; i < 3; ++i) {
setTimeout((function(private_i) { return function() { alert(private_i); }; })(i), 5000);
}
}
The "wrapper" function is just there to provide a local variable (the parameter "private_i") whereto the loop variable "i" can be copied.
However, I have created an anonymous function which references linkElem. So I am no longer sure what is going on.
It still gets reassigned, unless you are wrapping it in another level of scope (NB: another function).
Consider the following:
for (var j = 0;j < 10;j += 1) {
arrayOfLinks[j].onclick = function () {
alert(j);
};
}
In this case, all of those links would alert 10 when clicked, because j is outside of the scope and is being updated.
If you're creating linkElem in the same way, you are likely to only get the result of the last linkElem in the loop.
This is a better way:
linkElem.click(function () {
var data = $(this).data(); // no longer dependent on `linkElem` reference
alert(''+data.mls + ' ' + data.id);
});
Please refer to this How do JavaScript closures work?
This may help you understanding closures.
Whenever you see the function keyword within another function, the inner function has access to variables in the outer function.
function foo(x) {
var tmp = 3;
function bar(y) {
alert(x + y + (++tmp));
}
bar(10);
}
foo(2)
This will always alert 16, because bar can access the x which was defined as an argument to foo, and it can also access tmp from foo.
That is a closure. A function doesn't have to return in order to be called a closure. Simply accessing variables outside of your immediate lexical scope creates a closure.
function foo(x) {
var tmp = 3;
return function (y) {
alert(x + y + (++tmp));
}
}
var bar = foo(2); // bar is now a closure.
bar(10);
The above function will also alert 16, because bar can still refer to x and tmp, even though it is no longer directly inside the scope.
However, since tmp is still hanging around inside bar's closure, it is also being incremented. It will be incremented each time you call bar.
The simplest example of a closure is this:
var a = 10;
function test() {
console.log(a); // will output 10
console.log(b); // will output 6
}
var b = 6;
test();
When a Javascript function is invoked, a new execution context is created. Together with the function arguments and the parent object, this execution context also receives all the variables declared outside of it (in the above example, both 'a' and 'b').
It is possible to create more than one closure function, either by returning a list of them or by setting them to global variables. All of these will refer to the same x and the same tmp, they don't make their own copies.
[you]: Fascinating, tell me more!
Here the number x is a literal number. As with other literals in JavaScript, when foo is called, the number x is copied into foo as its argument x.
On the other hand, JavaScript always uses references when dealing with Objects. If say, you called foo with an Object, the closure it returns will reference that original Object!
function foo(x) {
var tmp = 3;
return function (y) {
alert(x + y + tmp);
x.memb = x.memb ? x.memb + 1 : 1;
alert(x.memb);
}
}
var age = new Number(2);
var bar = foo(age); // bar is now a closure referencing age.
bar(10);
As expected, each call to bar(10) will increment x.memb. What might not be expected, is that x is simply referring to the same object as the age variable! After a couple of calls to bar, age.memb will be 2! This referencing is the basis for memory leaks with HTML objects.