Setting the scope of a callback to where it is executed? - javascript

I'm trying to understand why this alerts to true? And how I would be able to alert false without passing arguments to the callback function (if possible)?
var a = true;
function foo(callback){
var a = false;
callback();
}
function bar(){
alert(a);
}
foo(bar); // Alerts true

Since you use var when you say a = false you create a new, locally scoped, a.
Get rid of the var if you want to modify the existing variable in the wider scope.

This:
var a = false;
...is local to the scope of the foo function.
This:
function bar(){
alert(a);
}
...was created in the variable scope where a = true, and as such, closed over that local variable environment, and thus over that specific a variable.
It comes down to the fact that whenever you create a function, it permanently retains the variable scope in which it was created.
It doesn't matter if you pass that function into another environment. It will always only reference its original variable environment.
var test0 = 0; // global variable environment
function a() {
var test1 = 1; // "a()" will always retain the global environment even if you
// send "a()" somewhere else
function b() {
var test2 = 2; // "b()" will always retain the environment of "a()" and the
// global environment, even if you send "b()" somewhere else
}
}
EDIT:
In order for bar to reference the variables local to foo, you could pass them in to bar as arguments:
var a = true;
function foo(callback){
var a = false;
callback( a );
}
function bar( a ){
alert( a );
}
foo(bar); // now it alerts false
Example: http://jsfiddle.net/dSZ4M/
...you'll notice that I gave the parameter in bar() the same name as the global a variable. Because parameters to the function are read before variables outside the function's own variable environment, the a parameter "shadows" the global a variable.
As such, you can no longer read the global a from inside bar. Of course, all you'd need to do is change the name of the parameter to something else, like arg or whatever, and then you'd be able to reference both the local arg parameter and the global a variable.

Related

How JS Scope / Closures is Passing Argument

Can someone explain why in the following function, I am able to pass an argument to a nested function? My understanding is that it has to do something with closures and scope(which I thought I had a decent understanding of), but I can seem to follow how exactly this argument is being passed.
The below function outputs 1,2 respectively. But how is the return statement doThis() getting the argument/parameter for "a"? I cant figure where/how this is accessed/passed.
function doSomething(){
var b = 1;
return function doThis(a){
console.log(b);//1
console.log(a);//2
}
}
var exec = doSomething();
exec(2);
The function doSomething() returns another function so when you execute
var exec = doSomething();
You can think of that exec as containing the following function
function doThis(a){ // <- takes an argument
console.log(1); // comes from the b argument in the outer scope
console.log(a); // not set yet
}
Thus when you call exec(2) you are actually calling doThis() with an argument 2 which becomes the value of a.
This is a slightly simplified version. To expand on that, the doSomething() function is described as closing over doThis() creating a closure. Conversely, the function doThis() is closed over or inside a closure. The closure itself is simply a limited state around the function:
function doSomething(){ // --> defines the closure
var b = 1; // variable only visible within doSomething()
return function doThis(a){ //<--> function has access to everything in doSomething(). Also defines another closure
console.log(b); // --> accesses the OUTER scope
console.log(a); // <-- comes from the INNER scope
} // <-- end INNER scope
} // --> end OUTER scope
When you execute doSomething() the returned result still retains access to the scope within it, this is why doThis() has access to the value b - it's simply reachable for it. It's similar how you can do
var foo = 40;
function bar(value) {
return foo + value;
}
console.log(bar(2));
Only in this instance any other code will have acces to foo as it's a global variable, so if you do foo = 100 in a different function, that will change the output of bar(). A closure prevents the code inside from being reachable from outside the closure.
When you assign var exec = doSomething();, exec is basically writing:
var doSomething = function(a) {
console.log(b);
console.log(a);
}
It became its own function. So passing in 2 like so exec(2) works like any normal function except that it has the variable b available to it because of the closure.

Is it possible to use non-global variables in function?

I've really looked all over for this and haven't found an answer that really explained this well...
I know how to access a global variable from within a function.
myGlobalVariable = [];
function myFunction() {
myGlobalVariable.push("somedata");
}
Now how do I access a variable one step up on the scope chain if it isn't global?
myGlobalVariable = [];
function myFunction() {
var notGlobalVariable = "somedata";
var myOtherFunction = function() {
myGlobalVariable.push(notGlobalVariable); // This is what I'd like to be able to do.
}
}
I know I could do something like
var notGlobalVariable = "somedata";
var myOtherFunction = function(arg) {
myGlobalVariable.push(arg);
}
myOtherFunction(notGlobalVariable);
But calling the function that way only works if I have the value of notGlobalVariable readily available. I'd like do be able to globally call the function and have it always use the value originally held by notGlobalVariable without having to pass that value in.
Edit
Alright, I kinda jumped the gun on this one. I though I was having a big variable scope issue, but apparently my issue was that in my specific code I was using the variable name arguments in place of notGlobalVariable, and as I should have known, arguments is shadowed and refers to the arguments passed in. I changed the name to args and it works fine. Sorry!
JavaScript does that automatically by creating closures.
Every function creates its own scope.
Scopes are nested, since functions can be defined within functions.
Every new nested scope ("function") can see all the variables that were defined in any parent scope at the point of its (i.e that new function's) creation. This is called a closure.
Every variable from any parent scope is "by reference" - child scopes ("functions") always see their current values.
The point in time when the function runs does not matter, only the point in time when it was declared (see first example).
The scope where a function runs in does not matter, only the scope a function was defined in (see second example).
Here
var myGlobalVariable = [];
// define function => creates closure #1
function myFunction() {
var notGlobalVariable = "somedata";
// define function => creates closure #2
var myOtherFunction = function() {
myGlobalVariable.push(notGlobalVariable);
}
// execute function
myOtherFunction();
}
myFunction(); // myGlobalVariable will be ["somedata"]
you create two scopes:
myFunction can see myGlobalVariable
the anonymous function stored in myOtherFunction can see myGlobalVariable and notGlobalVariable.
Now assume a small change:
var myGlobalVariable = [];
// define function => creates closure #1
function myFunction(callback) {
var notGlobalVariable = "somedata";
// define function => creates closure #2
var myOtherFunction = function() {
myGlobalVariable.push(callback());
}
// execute function
myOtherFunction();
}
// define function => creates closure #3
function foo() {
var thisIsPrivate = "really";
// define function => creates closure #4
return function () {
return thisIsPrivate;
}
}
myFunction(foo()); // myGlobalVariable will be ["really"]
You see that the callback function has access to thisIsPrivate because it was defined in the right scope, even though the scope it is executed in cannot see the variable.
Likewise the callback function will not be able to see notGlobalVariable, even though that variable is visible in the scope where the callback is executed.
Note that you always must use var on any variable you define. Otherwise variables will be silently global which can and will lead to hard to fix bugs in your program.
You do have access to notGlobalVariable inside of myOtherFunction. However you're only assigning myOtherFunction and never invoking it. Note that you'll have to invoke myOtherFunction inside of myFunction.
function myFunction() {
var notGlobalVariable = "somedata";
var myOtherFunction = function() {
myGlobalVariable.push(notGlobalVariable); // This is what I'd like to be able to do.
}
//Invoke myOtherFunction()
myOtherFunction()
}
Yes, you can - in javascript everything is possible - here is the fiddle http://jsfiddle.net/AZ2Rv/
myGlobalVariable = [];
function myFunction() {
var notGlobalVariable = "somedata";
// The following assigns a function to a variable
var myOtherFunction = function() {
myGlobalVariable.push(notGlobalVariable);
}
// The following line was missing in the code in the question so the function
// was never called and `notGlobalVariable` wasn't pushed into `myGlobalVariable`
myOtherFunction();
}
// And just to test if it works as expected
myFunction();
alert(myGlobalVariable[0]);
The problem of the OP laid actually in the code he didn't posted, but this sample code answers the original question - closures still work in javascript as expected.

Javascript function with undefined parameter and the parameter is defined in function body , is a global variable?

let's suppose we have a javascript function and we want to check if the second parameter is undefined, in that case we want to assign it a value such as
function myFunc(a,b){
if(typeof b==="undefined" ){
b=0;
}
//do stuff
}
Does the variable b in the if block still refer to the function's parameter or a global variable named b is created?
Cheers
Yes, b gets resolved in the lexicalEnvironment (ES5) respectively Activation Object (ES3). It will always get resolved locally, even if a global variable b exists aswell by accessing the pure name.
You would need to explicitly call window.b respectively global.b (node) to access that global variable with the same name.
It refers to the function's parameter. You can test yourself like so:
var b = 45;
function myFunc(a,b){
if(typeof b==="undefined" ){
b=0;
}
alert( b ); //It will output 0
//do stuff
}
myFunc('');
The answers by #Andre and #Nelson are both correct, but just for clarity's sake:
var c = 123;
var func = (function(a)//a === 1234
{
var b = c;//local b === global c
return function(a,c)
{
console.log(c === b);//false, unless second argument was 123
console.log(a === 1234);//false, unless first arg was 1234
console.log('a masks the a of the outer scope');
};
})(1234);
JS will first scan the scope of the called function, for any vars used, if it doesn't find them there, it moves on to the outer scope. This can be another function, or, eventually, the global scope. In this example, the inner (returned) function will not be able to access the value of a as defined in the outer function. It's GC'ed when that outer function returned, because it's not being referenced anywhere.
code:
function myFunc(a,b){
if(typeof b==="undefined" ){
b=0;
}
console.log('b from myFunc:'+b)
}
myFunc(5);
console.log('global:'+b)
output:
b from myFunc:0
b is not defined
Javascript first looks in the local scope for either a local variable or a named function argument. If it finds the symbol there, then that variable is the one that is used.
Only if a matching local symbol is not found is a global symbol used.
Thus, in your example, you will be setting the value of b the function argument.
Once you've defined a local symbol in either a local variable or a named function argument, you can no longer use that name by itself to access a global variable of the same name. If you specifically want to address the global variable, you can do so by prefixing it with window. as in window.b. This specifically targets the global scope so the local scope is not used.
The variable stays on the function if;
the variable is declared inside the function using var or without it
the variable is declared with this as it's parent node the function, example below
function foo(){
this.bar = 'hello';
}
the variable becomes available in the global scope when
the variable is declared outside any function using var or without it
the variable is declared as it's parent node is window example below
window.bar ='i am global'
YOU NEED DEFINE VARIABLE GLOBALLY :
In the below code t2 is undefined.
<script>
var t1="";
var t2;
function check(t1,t2){
if (t1===undefined)
{
alert("t1 is undefined");
}
if (t2===undefined)
{
alert("t2 is undefined");
}
}
</script>
REF :
http://www.w3schools.com/jsref/tryit.asp?filename=tryjsref_undefined

Variable scope problem

Since I am a JavaScript newbie, I started learning it but I got stuck just at the beginning.
I am following a Mozilla Tutorial and I have a problem with variable scope in JavaScript. I have some code:
var myvar = "my value";
var zmienna = "string";
(function () {
alert(myvar);
alert(zmienna);
})();
(function () {
alert(myvar); // undefined
var myvar = "local value";
alert(zmienna);
})();
In the tutorial, I've read that JavaScript variables are not visible from function blocks. Well, first two alerts says correct values. It's strange then, because third alert says "undefined", despite fact that nothing has changed from previous function block. The fourth one, again, prints right value.
Could anybody explain me, what is happening here? I would be very glad, because tutorial says nothing more about that.
The use of var is hoisted.
Since you have var myvar inside the function, there is a locally scoped myvar. Since you assign a value to it after you alert it, it is undefined when you alert it.
"I've read that JavaScript variables are not visible from function blocks."
That's not quite right. They are available from the nested functions.
Nested functions create a scope chain. A function created inside another function has access to its own variables as well as the variables of the function in which it was nested.
But function A can not see the variables of function B if function A was not nested inside function B.
var myvar = "my value"; // <-- global variable, seen by all functions
var zmienna = "string"; // <-- global variable, seen by all functions
(function () {
alert(myvar); // <-- referencing the global variable
alert(zmienna); // <-- referencing the global variable
})();
(function () {
// v--- Declaration of these "local" variables were hoisted to the top...
// var myvar; // <--- ...as though it was here.
// var new_var; // <--- ...as though it was here.
alert(myvar); // undefined (myvar is delcared, but not initialized)
alert(new_var); // undefined (new_var is delcared, but not initialized)
var myvar = "local value"; // <-- assign the value
alert(zmienna); // <-- referencing the global variable
alert(myvar); // <-- referencing the local variable
var new_var = "test"; // <-- another new local variable
// A nested function. It has access to the variables in its scope chain.
(function() {
alert(myvar); // <-- referencing the variable from its parent func
alert(new_var); // <-- referencing the variable from its parent func
})();
})();
/*
Here's a new function. It was not nested inside the previous function, so it
has access to the global variables, and not the locals of the previous func
*/
(function () {
alert(myvar); // <-- referencing the global variable
alert(new_var); // <-- ReferenceError
})();

No need to define a variable twice

If a variable could be defined in a function, even if no value is assigned, it becomes a local variable
so, is testB() better programming?
var test = 'SNAP!'
function testA(boolean) {
if (boolean) var test = 'OK';
else var test = null;
alert(test);
}
function testB(boolean) {
if (boolean) var test = 'OK';
alert(test);
}
testA(true); // 'OK'
testB(true); // 'OK'
testA(false); // null
testB(false); // undefined, no error
In my specific case test's global value ('SNAP!') is neither expected nor required.
You can't declare variables conditionally.
Why?
The variable instantiation process occurs before the actual code execution, at the time the function is executed, those variables will be already bound to the local scope, for example:
function foo () {
if (false) {
var test = 'foo'; // never executed
}
return test;
}
foo(); // undefined
When the function is about to be executed, identifiers of formal parameters, identifiers from variable declarations, and identifiers from function declarations within the function's body are bound to the local variable environment.
Variables are initialized with undefined.
Also, identifiers in the local scope shadow the others with the same name, higher in the scope chain, for example:
var test = 'global';
function bar () {
alert(test); // undefined, not "global", the local variable already declared
var test = 'xxx';
}
bar();
If the test variable were not declared anywhere, a ReferenceError will be thrown:
function foo () {
return test;
}
try {
foo(); // ReferenceError!!
} catch (e) {
alert(e);
}
That's one of the reasons about why for example, JSLint recommends only one var statement at the top of functions, because for example, the first snippet, will actually resemble this when executed:
function foo () {
var test; // var statement was "hoisted"
if (false) {
test = 'foo'; // never executed
}
return test;
}
foo(); // undefined
Another reason is because blocks don't introduce a new lexical scope, only functions do it, so having a var statement within a look might make you think that the life of the variable is constrained to that block only, but that's not the case.
Nested function declarations will have a similar behavior of hoisting, they will be declared before the code execution, but they are initialized in that moment also:
function foo () {
return typeof bar;
// unreachable code:
function bar() {
//..
}
}
foo(); // "function"
If the variable does not need to be manipulated by any other functions, keep the variable inside a function with var foo;.
Otherwise, if it does need to be accessed and read in multiple scopes, keep it outside. But remember that when you keep it outside, it becomes global. That is, unless you wrap everything in a self executing function, which is the best way:
(function() {
var president='bush';
function blah() {
president='reagan';
}
function meh() {
president= 'carter';
}
document.getElementById('reagan').onclick=blah;
document.getElementById('carter').onclick=meh;
})();
alert( president ) // undefined
The above is perfect for a variable accessed by functions defined inside of that scope. Since there are 2 elements i click to set the president, it makes sense to define it outside both functions because they set the same variable.
So, If you are not dealing with multiple functions changing the exact same variable, keep them local to the function.
Is testB better programming? No, because it gives an unexpected result of "undefined" (at least, I was surprised by that) and it is hard to read.
Generally, variables should be limited to the scope that requires them, so if the "test" variable is not needed outside the function it should be declared local. To avoid confusion, declare your variable before using it:
function testC(boolean) {
var test;
if (boolean) {
test = "OK";
}
else {
test = null;
}
alert(test);
}
Unless you genuinely want to change the global scope version of "test", in which case don't use the var keyword inside the function.
If you ever find yourself using the same name for a local variable and a global variable you might consider renaming one of them.

Categories