I have wrote a code in javascript like
function onClic(){
var i = 0;
if ( i==2 ){
var m = function(){
return 1;
}
}
else {
var m = function(){
return 2;
}
}
alert(m());
}
the alert shows 2;
can you please explain me the behavior in this statement i.e. Why am I able to access m outside the If statement when I declared it inside If statement scope.
Also why is this working as ECMAScipt 5 specifies that we donot put the function declaration inside IF-ElSE block.
Why am I able to access m outside the If statement when I declared it inside If statement scope.
Because JavaScript (ES5 at least, has only function scope, not block scope. The if statement does not create scope. Your code is equivalent to:
function onClic(){
var i = 0;
var m; // m is "hoisted"
if ( i==2 ){
m = function(){
return 1;
}
}
else {
m = function(){
return 2;
}
}
alert(m());
}
Also why is this working as ECMAScipt 5 specifies that we donot put the function declaration inside IF-ElSE block.
Right, but what you have are function expression, not declarations. A function declaration is something like
function foo() { }
Function expressions can be put everywhere where an expression is valid to use.
While you are right that, according to the spec, function declarations cannot be used inside blocks, browser still allow it and actually produce different results. Run this in Firefox and Chrome:
function foo() {
alert(1);
}
if (false) {
function foo() {
alert(2);
}
}
foo();
However, this will change in ES6 where this behavior will be properly defined.
Your variable i is set to 0 at the start of your function. You never change it. You IF statement is testing if i is equal to 2; it isn't. So it jumps to the ELSE statement, and returns 2.
If you want it to do something, you never to change the value of i in some way that makes sense to the problem you are trying to solve.
function onClic(){
var i = 0;// set i=0
if ( i==2 ){ // so this condition always give false and control go to else statement
var m = function(){
return 1;
}
}
else {
var m = function(){
return 2;
}
}
alert(m());
Just saw what you were actually asking.
The variable is still inside the onClic() function so it is still in the local scope even though it is within an if statement.
First you are checking for value of variable i in your function
If condition check the value of i , if it is 2 it makes a function assignment to variable m the function returns 1
if value of i is not 2 it makes a function assignment to variable m the function returns 2
In the end you call m that return either 2 or 1.
my variable is inside the scope of If statement then how am I able to
get its value outside the scope in the last round
The answer is in js you dont have block level scope , you have either global scope or local scope. Here I is local varaible so it can be accessed any where in this function
Related
I heard that every function remembers(?) the lexical environment where the function had created.
In this code, The function function () { console.log(i);},
I want to know where this function had been created. If some function is a parameter of other function, where is the created(?)/generated point?
function countSeconds(howMany) {
for (var i =1; i <= howMany; i++) {
setTimeout(function () {
console.log(i);
}, i * 1000 );
}
};
In this code, The function function () { console.log(i);}, I want to know where this function had been created.
This function is defined as a function expression. Such an expression is evaluated at runtime, much like an expression { y: x*x } would be evaluated at runtime. But in this case the evaluation result is a function object. And that function is then passed as argument to setTimeout. This means there are just as many functions created as there are iterations of your for loop.
Now, even though the function expression is evaluated at the moment setTimeout is executed, this does not mean that the body of the function is executed at that same time. It is not. It will only be executed when the timeout expires. At that time the function body executes, and at that time only it will evaluate expressions used in that code, such as variable i. In your example, the variable i will already have reached the value howMany+1, because that for loop already ran to completion before the timer expired, and the callback got called.
If that is something you want to avoid, then use a separate variable i for each iteration of the for loop. With let instead of var you create such distinct variables, which only live inside the for loop's block. And so then each of the function expressions will reference its "own" i:
function countSeconds(howMany) {
for (let i =1; i <= howMany; i++) {
setTimeout(function () {
console.log(i);
}, i * 1000 );
}
};
countSeconds(10);
If some function is a parameter of other function, where is the created(?)/generated point?
Like all arguments to a function call, they're evaluated right before the function is called. You can rewrite this with two temporary variables into the equivalent
function countSeconds(howMany) {
for (var i=1; i<=howMany; i++) {
const __arg1 = function() {
console.log(i);
};
const __arg2 = i * 1000;
setTimeout(__arg1, __arg2);
}
}
The function is created when the function expression is evaluated.
(Also notice that this code has the famous closure in a loop problem.)
I dont understand what you mean?
You mean where the function console.log() ist defined?
class console {
log(text) {
[I dont know]
}
}
Then you can make "console.log()" to call the function "Log" from Claas console? Did you mean that?
function test(flag){
if (flag) {
return function test1(){console.log(1)}
}else{
return function test1(){console.log(2)}
}
}
test(true)()
test()()
it log 1 and 2,why not double 2?
how does this works
my english is not very good, thank you
this also works with 1 and 2
function test(flag){
if (flag) {
function test1(){console.log(1)}
return test1
}else{
function test1(){console.log(2)}
return test1
}
}
test(true)()
test()()
The function in this line:
return function test1(){console.log(2)}
Is not a function declaration. It is a named function expression, because it is part of a statement.
Function expressions are not hoisted. Only function declarations are hoisted, like this:
function test(){
return test1;
function test1() { console.log(1); }
function test1() { console.log(2); }
}
test()();
Edit: Regarding the question you added after the fact, function declarations inside conditional expressions have undefined behavior and you can see different results depending on your JavaScript engine. Functions inside if-else statements may not be hoisted to the top of the scope, and you should not put function declarations inside conditional expressions. More about this
In the first call test(true)() it goes through:
if (flag) {
return function test1(){console.log(1)}
}
because the flag has value true.
In the second call test()() it goes through the else path:
else{
return function test1(){console.log(2)}
}
because in that instance value of flag is undefined and it evaluates as false.
You can get an idea about truthy and falsy using these links.
Hope you got the idea here. Please let me know if you have any questions.
i have a simple function called Range that creates an array of integers based on start, step and end value...
function Range (start, end, step) {
// default step is 1..
if (step === undefined ) step = 1;
// creating an array...
var arr = [], index = 0;
while(start <= end) {
arr[index] = start ;
index += 1;
start += step;
}
// simple function expressions
var getAll = function () {
return arr ;
};
var getOne = function(n) {
return arr[n] ;
};
// returns a unnamed function ..
return function(i) {
if (i === undefined) { return getAll() ;}
else {return getOne(i); }
}; // not an iife
}
so basically Range is a function which returns a unnamed function which again returns a named function expression declared in the function Range.. err.. i dont know.. something like that...
now the below code...
var first10 = Range (1,10) ; // no new ..() here, so no instance should be created.. only Range is called..
var first10Odd = Range(1,20,2) ; // and Range is called again..
alert(first10); // alerts - function(i) { ... }
alert(first10Odd); // alerts- function(i) { ... }
alert(first10()) ; // alerts - 1,2,3,...10
alert(first10Odd()); // alerts - 1,3,5,...19
alert(first10(0)); // alerts - 1
alert(first10Odd(9)); // alerts- 19
why do the alerts alert as specified in the comments??... i think Range is a just a function and not a object constructor and also no instance was created... shouldn't the local variables of function be destroyed as soon as the function is completed??
or is my logic wrong?? what is going on in the above code?? can anyone please explain....
i have made a fiddle of my code here..
sorry for asking this stupid question..
Welcome to the land of closures in Javascript. They can be very powerful and extremely useful once you understand them. But, if your prior experience is with languages that do not have them, they can feel a bit foreign at first.
Some answers/explanation:
Calling Range(x, y) returns a function that can then be called later.
Because that function that is returned is inside another function scope that has variables, a closure is created.
That closure stays alive (even though the outer function has finished executing) because there is a lasting reference to the inner function saved in your variables and that inner function has a reference to the local variables in the outer function. These references keep the closure from being garbage collected (so it stays alive).
That inner function can then reference the variables in the outer function, including the arguments originally passed to it.
This construct allows you to create these custom functions that have arguments pre-built into them.
The notion of this type of closure only exists in some languages. It does not exist in C++, for example.
When the function returned by calling Range(x,y) is itself executed later, it can use any of the variables that were originally in scope to it.
Each call to Range(x,y) causes a new closure to be created.
getAll and getOne are local variables in the outer function that are assigned a function. They access other local variables in the outer function. All of these are in the previously mentioned closure that is created each time Range() is called.
There is lots written about what a closure is (which you can Google and read), but I like to think of it as an execution context that contains everything that was in scope at the time a function is called (including all variables). Each time a function is called, such an execution context it created. Since everything in javascript is garbage collected and will only be freed/destroyed when there are no references left to it, this is true for this execution context too (e.g. closure). As long as something has a reference to it or something in it, then the execution context will stay alive and can be used by any code that might run into that execution context.
Line by line annotation:
// first10 is assigned the anonymous function that the call to Range()
// returned. That anonymous function has access to the original arguments
// passed to the Range(1,10) call and other local variables in that function.
var first10 = Range (1,10) ; // no new ..() here, so no instance should be created.. only Range is called..
// same as the call before, except this also includes the step argument
var first10Odd = Range(1,20,2) ; // and Range is called again..
// this makes sense because Range(1,10) returns a function so
// when you alert it's value, it tells you it's a function
alert(first10); // alerts - function(i) { ... }
alert(first10Odd); // alerts- function(i) { ... }
// When you execute the function in first10, it runs that function
// and the alert shows the return value from that function
// This particular function is set to return the entire array if nothing is passed
// to it
alert(first10()) ; // alerts - 1,2,3,...10
alert(first10Odd()); // alerts - 1,3,5,...19
// This particular function is set to return a specific index from the array
// if an argument is passed to it
alert(first10(0)); // alerts - 1
alert(first10Odd(9)); // alerts- 19
If you know how to use the javascript debugger, you can set a breakpoint on this line if (i === undefined) { return getAll() ;} in the inner function and you will be able to inspect all the variables that are in scope, including start, end and step from the outer function.
You may find this article useful reading as it encapsulates some of the ways that closures can be used with object declarations: http://javascript.crockford.com/private.html (not exactly what is being done here, but might help you understand them).
Welcome to javascript closures. Lets take line by line.
var first10 = Range(1,10);
var first10Odd = Range(1,20,2);
We know that Range is just a function. So, in these two lines we are just calling Range function with 2 and 3 arguments respectively.
Now, what happens when you call a function. The obvious answer is, the body of the function gets executed. What do we have in the body of the function.
if (step === undefined ) step = 1;
var arr = [], index = 0;
while(start <= end) {
arr[index] = start ;
index += 1;
start += step;
}
I hope that the above seen lines are pretty obvious and you don't have any problems with them.
var getAll = function () {
return arr;
};
What does this line do? It creates a function at run time. Why runtime? Lets see an example.
<script>
func1();
var func1 = function() {
alert("Hi");
}
</script>
<script>
func1();
function func1() {
alert("Hi");
}
</script>
If you use the first script block, it will throw error. Why? You are calling a function which hasn't been defined yet. The second case, you are defining the function during javascript parsing time itself. The type of function which was created in the first case is called anonymous function. Let us get back to getAll. Now we know that getAll is simply a variable which points to an anonymous function, lets look at what it does. It returns arr. How does it have access to arr? It is declared outside the function and so it still has access to it. Same case with
var getOne = function(n) {
return arr[n] ;
};
Now the very important part,
return function(i) {
if (i === undefined) {
return getAll();
} else {
return getOne(i);
}
};
What does it do? It returns a function. To be precise, it returns an anonymous function. Whenever Range is called, it creates a new anonymous function, which accepts one parameter and returns it. So, now what do first10 and first10Odd have? Yes. You are right, they have functions. I hope that explains
alert(first10); // alerts - function(i) { ... }
alert(first10Odd); // alerts - function(i) { ... }
Let us examine both the functions. When first10 is called with nothing, I mean, first10(), the parameter i takes the value undefined. So, we are actually making a call to the anonymous function with no parameters and it is supposed to return getAll(). If you remember, first10 was created with Range(1,10);. So, the arr will now have [1, 2, 3, 4, 5, 6, 7, 8, 9, 10].
You might ask, when we return from the function, wont the variables declared inside the function go out of scope. The answer is Yes and No. Yes, when you simply return a value. No, when you return a function. When you return a function, the state of the variables will be maintained. This property is called closures. That is why it returns
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10] for alert(first10())
[1, 3, 5, 7, 9, 11, 13, 15, 17, 19] for alert(first10Odd())
1 for alert(first10(0))
19 for alert(first10Odd(9))
Please read more about Closure here https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Closures
My code:
for (var i = 0; i < mapInfos.length; i++) {
var x = function () { doStuff(i); };
google.maps.event.addListenerOnce(mapInfos[i].map, 'tilesloaded', x);
}
The doStuff method simply alerts the value of i. mapInfos has two entries, so you'd expect it to alert 0 and 1, but instead it alerts 2 and 2. I can appreciate vaguely why it is doing this (although var i should keep it local to the scope of the loop?) but how can I make it work as intended?
edit — note that when first posted, the original question included a link to a jsfiddle that seemed to be a relevant example of what the current question is trying to achieve, only it appears to work ...
The code in the jsfiddle works because there's only one "i" in that code. The "i" used in the second loop (where the functions are actually called) is the same "i" as used in the first loop. Thus, you get the right answer because that second loop is running "i" through all the values from zero through four again. If you added:
i = 100;
functions[0]();
you'd get 100 printed out.
The only way to introduce a new scope in JavaScript is a function. One approach is to write a separate "function maker" function:
function makeCallback(param) {
return function() {
doStuff(param);
};
}
Then in your loop:
for (var i = 0; i < mapInfos.length; i++) {
var x = makeCallback(i);
google.maps.event.addListenerOnce(mapInfos[i].map, 'titlesloaded', x);
}
That'll work because the call to the "makeCallback" function isolates a copy of the value of "i" into a new, unique instance of "param" in the closure returned.
Create a new scope for it.
Functions create scope.
function doStuffFactory(i) {
return function () { doStuff(i); };
}
for (var i = 0; i < mapInfos.length; i++) {
var x = doStuffFactory(i);
google.maps.event.addListenerOnce(mapInfos[i].map, 'tilesloaded', x);
}
Change it to
var x = function (param) { doStuff(param); };
Obviously what is going on is that you are alerting a variable that is changing. With the above change it copies it so even if i changes it will still alert the right value.
Javascript doesn't have block scope, so you don't get an x that's local to the loop. Yea!
It has function scope, though.
Yep, weird isn't it!Pointy has an explanation
I have no idea why your first example worked (I wasn't expecting it to) Pointy has an explanation of why your first example worked - The reason why your second one doesn't is because i is scoped to the function containing the for loop, not to the scope defined by the for loop. In fact the only things that have scope in JavaScript are functions. This means that by the time your function gets executed i is 2.
What you need to do is create a scope, for example:
for (var i = 0; i < mapInfos.length; i++) {
var x = (function() {
return function () { doStuff(i); };
})(i);
google.maps.event.addListenerOnce(mapInfos[i].map, 'tilesloaded', x);
}
See JavaScript closures in for-loops for more.
Is it possible to pass variables into a js function? See code:
function checkStartPrice (){
if ($('#StartingPrice')[0].value.length == 0){
alert("The 'Starting Price' cannot be left empty!");
return false;
} else {
var BuyItNowPrice = parseFloat($('#BuyItNowPrice').val());
var StartingPrice = parseFloat($('#StartingPrice').val());
var Reserve = parseFloat($('#Reserve').val());
if((BuyItNowPrice <= StartingPrice) && (StartingPrice > 0)){
alert("The 'Buy It Now' price must be higher...");
return false;
}
if((Reserve <= StartingPrice) && (StartingPrice > 0)){
alert("Your 'Reserve Price' must be higher...");
return false;
}
return true;
}
}
I'd like to take the var BuyItNowPrice = parseFloat($('#BuyItNowPrice').val()); etc and place them outside the function, then call them in.
The reason is I have 3 of these functions so these 3 variables (BuyItNowPrice, StartingPrice, Reserve) are repeated 9 times, 3 in each function.
Is there a way of doing this?
Any help would be Greatly Appreciated, Thanks
A simple example of passing variables into a function is below. When you declare a function, you can also declare some arguments for the function by putting them inside the function() brackets. You can use whatever letter/name you like to represent the arguments - when you CALL the function, you pass through the variables.
// put arguments into the function declaration - a,b,c
function myfunction(a, b, c) {
// use these arguments within the function
return (a+b+c);
}
// declare the variables outside the function
var first_variable = 1;
var second_variable = 2;
var third_variable = 3;
// returns '6'
myfunction(first_variable, second_variable, third_variable);
You can make these variables global by taking it out of the fn
BuyItNowPrice = parseFloat($('#BuyItNowPrice').val());
StartingPrice = parseFloat($('#StartingPrice').val());
Reserve = parseFloat($('#Reserve').val());
fn1(){}
fn2(){}
fn3(){}
Now you can use these variables in all the three functions.
Make those three variables global. To make them global you just need to have those variables outside any function. In javascript if any variable which doesnot appear in any functions, then its scope is global.
To answer your first question, Yes it is possible to pass variables to functions in javascript. you can just send the variables as arguments to the function.
sample: function myfucn(a, b) { //you implementation here.}