variable undefined in closure? - javascript

I have a function like this:
function foo(canvas) {
canvas.mousedown(function(e) {
console.log(canvas); //undefined
});
}
I'm calling foo on mouse click in a certain spot of the page.
Why is canvas undefined?

The debugger may not show the variables in the closure until they are used.
Consider this example where the a variable is defined but never used:
(function() {
var x = 1;
$(function () {
debugger; // debugger stopped here, `x` is `undefined` in Chrome and IE but `1` in Firefox
console.log("hi");
}
})();
Same code except the variable is printed out instead of the string literal:
(function() {
var x = 1;
$(function () {
debugger; // debugger stopped here, all three browsers show `x` as `1`
console.log(x);
}
})();

Your own answer is correct, once you gave the whole code example. You encountered a quirk of Javascript known as "variable hoisting." Your code is interpreted as:
function foo(canvas) {
canvas.mousedown(function(e) {
var i, canvas; //variable declarations moved to top of function scope
console.log(canvas); //undefined
//...
for (i in array) {
canvas = array[i].canvas;
//...
}
});
}
See:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/var#var_hoisting
http://www.adequatelygood.com/JavaScript-Scoping-and-Hoisting.html

The problem was this:
function foo(canvas) {
canvas.mousedown(function(e) {
console.log(canvas); //undefined
//...
for (var i in array) {
var canvas = array[i].canvas;
//...
}
});
}
I haven't time to investigate the exact reason. My guess is that the compiler puts a "var canvas" declaration at the start of the anonymous function, such that the variable is undefined when output in the console. Otherwise don't understand it yet.

Would just like to confirm that in chrome, what Charlie said is correct. I needed to make a reference to the variable in question before I could use it within the closure, when using the debugger statement!

Related

Why doesn't document.ready work in this case?

If I use this:
<script>
$(document).ready(function() {
if (window.localStorage.getItem('createTicket')) {
var createTicketInfo = JSON.parse(window.localStorage.getItem('createTicket'));
}
});
</script
I see an error in the console:
Uncaught ReferenceError: createTicketInfo is not defined
But if I remove document.ready:
<script>
if (window.localStorage.getItem('createTicket')) {
var createTicketInfo = JSON.parse(window.localStorage.getItem('createTicket'));
}
</script>
Everything is ok:
Why?
It's because, in the first example, createTicketInfo is only visible within the callback (function) of ready.
function test() {
var boo = "Hi! I'm boo.";
// ...
console.log(boo); // visible here
}
test();
console.log(boo); // not visible here (even if we call test first)
In the second example, createTicketInfo is not wrapped in a function, thus it is global (visible everywhere).
if(true) {
if(true) {
if(true) {
var foo = "Hi! I'm foo.";
// ...
console.log(foo); // visible here
}
}
}
console.log(foo); // visible here too (because javascript doesn't have block scopes)
Always put in mind that javascript has function scopes, but not block scopes.
Note: As mentioned by #nnnnnn in the comment bellow, ECMAScript 6 introduced a new way of declaring variables using let or const that respect block scopes. So:
if(true) {
let zoo = "Hi! I'm zoo.";
// ...
console.log(zoo); // only visible inside this block
}
console.log(zoo); // not visible here
Something else isn't right in another section of your code.
Look at this fiddle I created.
It accurately reads and writes the value. Exactly the same code:
https://jsfiddle.net/nxka5h7y/2/
<head>
<script src = "https://code.jquery.com/jquery-3.2.1.min.js"></script>
<script>
window.localStorage.setItem('createTicket', '{"a":"1"}');
$(document).ready(function() {
if (window.localStorage.getItem('createTicket')) {
var createTicketInfo = JSON.parse(window.localStorage.getItem('createTicket'));
console.log(createTicketInfo);
} else { console.log("none"); }
});
</script>
</head>
<body>
<script>
</body>
The only thing I added, was actually setting the createTicket item in localStorage, just to ensure the "if" statement inside document/ready will pass.
My semi-logical guess is that JSON stored in createTicket is malformed, based on how it was set (which is not shown in your example.) I don't really think that's the issue. But, the fact is your example produces accurate results in jFiddle. Look elsewhere in your code. Like, the part where you actually set the createTicket. Or that your jQuery is actually included, or something silly like that.

What does "Functions can only be declared at the top level of a scope" mean? [duplicate]

I have this error when using FireFox with strict mode. But am unsure what it means. I assumed it meant that the function had to be declared before it was called upon but the error still occurs.
SyntaxError: in strict mode code, functions may be declared only at top level or immediately within another function
This is my snippet of code where it is causing the error:
var process = new function(){
var self = this;
self.test = function(value,callback){
var startTime = Date.now();
function update(){ //<--- error is here
value++;
startTime = Date.now();
if(value < 100){
setTimeout(update, 0);
}
callback(value);
}
update();
}
};
So i'm wondering how would I write this snippet of code out correctly with strict ? What does it mean by top level ? Does that mean globally defined and not locally within a function ?
Also given I have use strict why does this problem not occur in Chrome?
You must put local functions BEFORE other code within the parent function in strict mode:
var process = function () {
var self = this;
self.test = function (value, callback) {
function update() {
value++;
startTime = Date.now();
if (value < 100) {
setTimeout(update, 0);
}
callback(value);
}
var startTime = Date.now();
update();
}
};
This is described in this articles:
New ES5 strict mode requirement: function statements not at top level of a program or function are prohibited
MDN Strict Mode
In my own testing though (and counter to the articles I've read), I find that current versions of both Chrome and Firefox only complain about a local function definition if it is inside a block (like inside an if or for statement or a similar block.
I guess I need to go find an actual spec to see what is says.
The Internet explorer error explicitly states functions names cannot be "declared" within a function. So using a self-invoking function expression has worked for me.
This code fails:
c.prototype.isitReal=function(t){
var matched = false;
this.each(function(item){
// declaring a new function within a function fails
function getChildren(el){
....
getChildren(el[a].children);
....
}
getChildren(el.children); // invoke function
});
return matched;
};
This code works:
c.prototype.isitReal=function(t){
var matched = false;
this.each(function(item){
// convert the function to an expression of a function
// by wraping the entire function in ( )
(function getChildren(el){
....
getChildren(el[a].children);
....
// then invoke the function expresion by adding ()
// to the end. Pass in any arguments here also
})(el.children,arg2,arg3);
});
return matched;
};
Tested in FF 76, MSIE 10, Chrome Canary 81

can't get jqPlot chart to show line from Variable

I'm using jqPlot to plot some points in my webApp, so I'm trying this:
var plot10 = $.jqplot ('heightChartDiv', [[3,7,9,1,5,3,8,2,5]]);
and it works fine, I this exact chart here
but when I take it out, to give it a value, like so:
$(document).ready(function(){
var serie1 = [[3,7,9,1,5,3,8,2,5]];
}
function doGraph(){
var plot10 = $.jqplot ('heightChartDiv', serie1);
}
It doesn't work. am I declaring the variable wrong? please HELP!
~Myy
Your variable scoping is all off. The variable serie1 has local scope to the anonymous function defined in $(document).ready event. Read up on javascript scope here and here.
Perhaps something like this:
// the document ready will fire when the page is finished rendering
// inline javascript as you've done with your doGraph will fire as the page renders
$(document).ready(function(){
// first define graph function
// make the series an argument to the function
doGraph = function(someSeries){
var plot10 = $.jqplot ('heightChartDiv', someSeries);
}
// now call the function with the variable
var serie1 = [[3,7,9,1,5,3,8,2,5]];
doGraph(serie1);
}
EDITS IN RESPONSE TO COMMENT
See this below example:
$(document).ready(function(){
var a = 1;
someFunc = function(){
var b = 2;
alert(a);
}
someFunc(); // this works
alert(b); // this produces an error
});​
Here the variable a is considered global to the function someFunc. A variable declared in someFunc, though, does not persist outside of it.

Different ways of writing a function bringing undesired effects in JavaScript?

UPDATE:
QUESTION SOLVED! I realized that the reason for this is due to 'hoisting.' Basically, the JavaScript interpreter parses the code and declares all variables (but does not initialize them) at the beginning of the function. That's why the second examples isn't working. Because JavaScript interpreter declares var changed; at the beginning of the function, but does not initialize it until it reaches the body of the code.
For function declaration like the first example, instead of JavaScript moving up just the variable name like the second example, it moves up (or 'hoists') up the entire function at the beginning of the parent function, which is why it works!
Anyway, I wrote this for personal reference and thanks for the answers...
This one works: http://jsbin.com/emarat/7/edit
$(function(){
$name = $('#test');
$name.change(changedName);
function changedName (e){
console.log('e: ', e);
console.log('e.currentTarget: ', e.currentTarget);
console.log('$(e.currentTarget).val(): ', $(e.currentTarget).val());
$('#test-display').text($(e.currentTarget).val());
}
});
but this one doesn't: http://jsbin.com/emarat/9/edit
$(function(){
$name = $('#test');
$name.change(changed);
var changed = function(e){
console.log('e: ', e);
console.log('e.currentTarget: ', e.currentTarget);
console.log('$(e.currentTarget).val(): ', $(e.currentTarget).val());
$('#test-display').text($(e.currentTarget).val());
};
});
Why?
The latter one is equivalent to:
$(function(){
var changed;
$name = $('#test');
$name.change(changed);
changed = function(e){
//...
};
});
which makes it obvious why it doesn't work. At the time of usage, the changed variable is not yet initialized (undefined).
But if you declare a function using the function yourFunctionName() syntax, it is available in the whole scope. (Which, in JavaScript, is the parent function.) Otherwise it wouldn't be possible to use functions prior to their declaration. It is called hoisting.
See also:
var functionName = function() {} vs function functionName() {}
Surprised that global variable has undefined value in JavaScript
Because the variable is defined after its use.
var a = 1;
var c = a + b;
var b = 2;
You wouldn't expect that code to run.
The first one defines a function in the scope. The second one creates a inline function and stores a reference to it in the local variable changed. The problem is that you fill the variable after you use it.
This would work:
$(function(){
var changed = function(e){
console.log('e: ', e);
console.log('e.currentTarget: ', e.currentTarget);
console.log('$(e.currentTarget).val(): ', $(e.currentTarget).val());
$('#test-display').text($(e.currentTarget).val());
};
$name = $('#test');
$name.change(changed);
});
http://jsbin.com/emarat/11/edit

Javascript anonymous functions in Firefox 7

After updating to Firefox 7, I am getting the following error:
function statement requires a name
This particular functions is defined as
fun = eval("function (item) { //Function body }");
If I rewrite it as:
fun = eval("function view(item) { //Function body }");
The error does not show up any more, but the program still does not work.
Ps.- I know that evaluating a string is not a good idea. This is a legacy application that I have to fix in which some functions are downloaded from a database as strings on demand.
Wrap it in brackets
eval("(function (item) { alert('hello'); })");
But that doesn't make sense as it does nothing. Maybe you want:
eval("(function () { alert('hello'); })()");
Or
eval("var func = function (item) { };");
A function declaration (is what you've got there) requires an identifier by spec.
function() {
}
just like that is not allowed by ES specification (even if some browsers might allow it anyway). Only function expression may be anonymous.
just a guess, maybe try with:
fun = eval("return function (item) { //Function body }");
(I just added the return statement)
if the function is defined as a string and you wish to use it without calling eval everytime, you could do this:
var myFunc = 'function(){alert("myFunc");}';
var fun = eval('(function(){return '+myFunc+'})()');
fun();
Or just
var myFunc = 'function(){alert("myFunc");}';
var fun = eval('('+myFunc+')');
fun();

Categories

Resources