Bug in my Javascript Code: Scope Issue or Logic Issue? - javascript

My snake game is leaving a trail everywhere it goes, rather than moving properly: http://jaminweb.com/Snake.html
(Move it with the arrow keys)
I'm wondering whether this might be a problem of scope.
The relevant code is
var temp = this.body[0].clone();
// ....
this.body[0].translate(this.dx, this.dy);
for (var i = 1, j = this.body.length; i < j; ++i)
{
var lastBB = temp.getBBox();
var thisBB = this.body[i].getBBox();
temp = this.body[i].clone();
this.body[i].translate(lastBB.x-thisBB.x,lastBB.y-thisBB.y);
}
I'm wondering, dees
temp = this.body[i].clone();
inside the for loop create a new variable or does Javascript look outside to see if there's already one? I assumed the latter.

temp = this.body[i].clone(); creates a new object, thus you are creating new objects that don't get translated.
And as #MikeW pointed out in the comments, there is no for block scope in JavaScript; scoping is either global or limited to a function. So, temp in your code is the same variable both inside and outside of the for loop.

the this variable always points to the context that it is defined in.
function Constructor1
{
this.x = 1; // points to the objects instantiated by Constructor1
}
var a = new Constructor1;
a.x = 1; // `this` points to a
var b = new Constructor1;
b.x = 1; // `this` points to b
function func()
{
return this; // points to the calling function, returns itself
}
func() will return the function itself
Javascript uses functional scope as opposed to block scope.

Related

variabiles accessibiity when a function launch an other function - javascript

i'm writing a script to manage HTML menu interactions with clicks from user so this my situation:
menuScripts('site-header-branding', 'menu-mobile-toggle', 'site-header-navigation', 'main-navigation', 'sub-menu');
function menuScripts(siteHeaderBrandingCSSClass, hamburgerCSSClass, wrapperOfMainNavigationCSSClass, ulMainNavigationCSSClass, subMenuCSSClass) {
var classeSiteHeaderBranding = siteHeaderBrandingCSSClass; //site-header-branding
var classeHamburger = hamburgerCSSClass; //menu-mobile-toggle
var classeWrapperOfMainNavigation = wrapperOfMainNavigationCSSClass;//site-header-navigation
var classeMainNavigation = ulMainNavigationCSSClass; //main-navigation
var classeUlSubMenus = subMenuCSSClass;//sub-menu
const siteHeaderBrandingDOM = document.getElementsByClassName(classeSiteHeaderBranding);
for (let i = 0; i< siteHeaderBrandingDOM.length; i++) {
siteHeaderBrandingDOM[i].addEventListener("click", HeaderBrandingInteractive);
};
const menu = document.getElementsByClassName(classeMainNavigation);
for (let i = 0; i< menu.length; i++) {
menu[i].addEventListener("click", SubMenuInteractive);
};
}
function HeaderBrandingInteractive(e) {
//magic
}
function SubMenuInteractive(e) {
//magic
}
And it give me an error , because inside the last two function i need to have access to some of the variables declared in menuScripts(){}
These variables don't exist inside the last two function.
But if i remove "var" from declaration, so like this
menuScripts('site-header-branding', 'menu-mobile-toggle', 'site-header-navigation', 'main-navigation', 'sub-menu');
function menuScripts(siteHeaderBrandingCSSClass, hamburgerCSSClass, wrapperOfMainNavigationCSSClass, ulMainNavigationCSSClass, subMenuCSSClass) {
classeSiteHeaderBranding = siteHeaderBrandingCSSClass; //site-header-branding
classeHamburger = hamburgerCSSClass; //menu-mobile-toggle
classeWrapperOfMainNavigation = wrapperOfMainNavigationCSSClass;//site-header-navigation
classeMainNavigation = ulMainNavigationCSSClass; //main-navigation
classeUlSubMenus = subMenuCSSClass;//sub-menu
const siteHeaderBrandingDOM = document.getElementsByClassName(classeSiteHeaderBranding);
for (let i = 0; i< siteHeaderBrandingDOM.length; i++) {
siteHeaderBrandingDOM[i].addEventListener("click", HeaderBrandingInteractive);
};
const menu = document.getElementsByClassName(classeMainNavigation);
for (let i = 0; i< menu.length; i++) {
menu[i].addEventListener("click", SubMenuInteractive);
};
}
function HeaderBrandingInteractive(e) {
//magic
}
function SubMenuInteractive(e) {
//magic
}
It works!
i tried also to pust "const" instead of "var", but same problem of accessibility.
In theory
var x = 'something';
should must be equal to
x = 'something' ;
What i didnt get from the theory of javascript??
The variables declared with var are scoped to the enclosing function.
When you do x = 'something', the variable x will be globally created at the time of assignment - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/var.
Assigning a value to an undeclared variable implicitly creates it as a
global variable (it becomes a property of the global object) when the
assignment is executed.
In your code, you are first assigning variables before making the function call to HeaderBrandingInteractive-
classeSiteHeaderBranding = siteHeaderBrandingCSSClass; //site-header-branding
classeHamburger = hamburgerCSSClass; //menu-mobile-toggle
classeWrapperOfMainNavigation = wrapperOfMainNavigationCSSClass;//site-header-navigation
classeMainNavigation = ulMainNavigationCSSClass; //main-navigation
classeUlSubMenus = subMenuCSSClass;//sub-menu
The above code will create global variables, like window.classeHamburger. So, it will be accessible to outside your function.
var x = 'something'; should NOT be equal to x = 'something';, because if you declare variable as var x = 'something'; in the scope of function, this variable is "visible" in the scope of particular function.
When you put simply x = 'something';, that variable becomes decrared in the global scope and will be accessible as window.x.
It is Not Recommended to declare a variable without var keyword. It can accidentally overwrite an existing global variable.
Scope of the variables declared without var keyword become global irrespective of where it is declared. Global variables can be accessed from anywhere in the web page. Visit Scope for more information.
Javascript use three kinds of variable declaration.
var: The scope of a variable declared with var is its current execution context, which is either the enclosing function or, for variables declared outside any function, global.
let: let allows you to declare variables that are limited in scope to the block, statement, or expression on which it is used. This is unlike the var keyword locally to an entire function regardless of block scope.
const: Constants are block-scoped, much like variables defined using the let statement. The value of a constant cannot change through reassignment, and it can't be re-declared.
Neither of the above: If you do not specify var or let then JS considers that variable as global which means it can be accessed throughout your script.
Note: The variable declarations are hoisted, which means the declarations will be pushed to the top of the code (Within that scope for let/const, within that function for var and to the top of the whole script for global) but the initialization will not. Learn More...
In your above code when you are using var the variables are only limited to the scope of menuScripts.
When you do not specify anything, the variables become global and hence can be accessed anywhere.
Thanks for all explications you provided.
Now it's more clear to me the var local/global concept.
Since it's worthwhile to use always "var" keyword to protect future edit of the variable from outside - thanks #G.M. Patel - i had the problem to be able to pass arguments to a function launched via .addEventListener .
I looked at How to pass arguments to addEventListener listener function? but those solutions didn't help me.
What i did is to shift up the two functions (HeaderBrandingInteractive and SubMenuInteractive) so they are parts of menuScript .
In this way the local variables of menuScript are accessible from HeaderBrandingInteractive and SubMenuInteractive without writing code.
Now it works.
Maybe will help somebody in the future.
menuScripts('site-header-branding', 'menu-mobile-toggle', 'site-header-navigation', 'main-navigation', 'sub-menu');
function menuScripts(siteHeaderBrandingCSSClass, hamburgerCSSClass, wrapperOfMainNavigationCSSClass, ulMainNavigationCSSClass, subMenuCSSClass) {
var classeSiteHeaderBranding = siteHeaderBrandingCSSClass; //site-header-branding
var classeHamburger = hamburgerCSSClass; //menu-mobile-toggle
var classeWrapperOfMainNavigation = wrapperOfMainNavigationCSSClass;//site-header-navigation
var classeMainNavigation = ulMainNavigationCSSClass; //main-navigation
var classeUlSubMenus = subMenuCSSClass;//sub-menu
const siteHeaderBrandingDOM = document.getElementsByClassName(classeSiteHeaderBranding);
for (let i = 0; i< siteHeaderBrandingDOM.length; i++) {
siteHeaderBrandingDOM[i].addEventListener("click", HeaderBrandingInteractive);
};
const menu = document.getElementsByClassName(classeMainNavigation);
for (let i = 0; i< menu.length; i++) {
menu[i].addEventListener("click", SubMenuInteractive);
};
function HeaderBrandingInteractive(e) {
//magic
}
function SubMenuInteractive(e) {
//magic
}
}
I've also tried to delete
var classeSiteHeaderBranding = siteHeaderBrandingCSSClass; //site-header-branding
var classeHamburger = hamburgerCSSClass; //menu-mobile-toggle
var classeWrapperOfMainNavigation = wrapperOfMainNavigationCSSClass;//site-header-navigation
var classeMainNavigation = ulMainNavigationCSSClass; //main-navigation
var classeUlSubMenus = subMenuCSSClass;//sub-menu
cause #James said that it's a waste of time, and it's correct.
But you need to change forward use of those variables so they match the new name.
For example my SubMenuInteractive was :
function SubMenuInteractive(e) {
//some code
if (e.toElement.parentElement.parentElement.getAttribute('class') == classeMainNavigation ) {
console.log("it's him");
}
//some other code
}
and now need to be like this, with "classeMainNavigatin" that become "ulMainNavigationCSSClass" like in the "menuScript" declaration:
function SubMenuInteractive(e) {
//some code
if (e.toElement.parentElement.parentElement.getAttribute('class') == ulMainNavigationCSSClass ) {
console.log("it's him");
}
//some other code
}

My for loop to create objects isnt working in .js with P5

I recently used P5 to make a Flappy bird like game and up until a day ago I used to define and call my objects for my game like this:
function setup(){
//**Define**
cloud1 = new Cloud(random(100, windowHeight - 500), random(0.7, 1.3));
cloud2 = new Cloud(random(100, windowHeight - 500), random(0.7, 1.3));
};
function draw(){
//**Draw Clouds**
cloud1.show();
cloud2.show();
};
This works fine, but when I tried to use a for loop in the following way:
function setup(){
//**Define**
for(var i = 0; i < 5; i++) {
cloud[i] = new Cloud(random(100, windowHeight - 500), random(0.7, 1.3));
}
}
function draw
//**Draw Clouds**
for(var i = 0; i < 5; i++) {
cloud[i].show();
}
}
The debugger says that my 'cloud' object isnt defined. I have used this kind of code in a very simmilar way before without any problems. Can anyone help me?
Sorry for my bad english.
First off, let's talk about why this works:
function setup(){
myVariable = 'hello';
}
function draw(){
console.log(myVariable);
}
Here, you're using the assignment operator to assign a value to a variable.
This works because when you hit the myVariable = 'hello'; line, JavaScript is going to look for a variable named myVariable in enlarging scopes (first in the setup() function, then in the global scope). When it doesn't find a variable with that name, it creates a new one in the global scope. More info on that here.
Now, let's talk about why this doesn't work:
function setup(){
myArray[0] = 'hello';
}
function draw(){
console.log(myArray[0]);
}
Here, you're using the index operator to index into an array named myArray. However, that variable hasn't been created yet! You could create the array first:
function setup(){
myArray = [];
myArray[0] = 'hello';
}
function draw(){
console.log(myArray[0]);
}
This will work for the same reason your first code worked. The myArray = [] line first looks for a variable named myArray, and then creates a new one when it can't find it.
However, all of the above is pretty hackish code, because it's relying on JavaScript to look through the various scopes. What if there's already a myVariable variable? You're now overwriting the value, which can lead to buggy behavior. At the very least, this is an unintended side effect, so it should be avoided.
Instead, what you want to do is declare the variable yourself, using the var keyword. Since you want to access the variable inside two functions, you'd want to do that outside of both functions, like this:
var myArray = [];
function setup(){
myArray[0] = 'hello';
}
function draw(){
console.log(myArray[0]);
}
Now this code creates a variable named myArray and sets it equal to an empty array. Then you can use the index operator on it to set an individual index, and you can access it in multiple functions. And even better, if there is a conflict with another variable, you'll get an error warning you about that.
var cloud=[];//must be global
function setup(){
for(var i = 0; i < 5; i++) {
cloud[i] = new Cloud(random(100, windowHeight - 500), random(0.7, 1.3));
}
}
function draw(){
for(var i = 0; i < cloud.length; i++) {//better like this
cloud[i].show();
}
}
If it has to be accessible by two functions you need to place the variable in a higher scope, and you may iterate over cloud.length elements, as hardcoded values can be overseen when changing.
Also, in such a case (without beeing in P5)my preferred setup would be like this:
function setup(num){
var cloud=[];
for(var i = 0; i < num; i++) {
cloud[i] = new Cloud(random(100, windowHeight - 500), random(0.7, 1.3));
}
cloud.show=function(){
this.forEach(el=>el.show());
};
return cloud;
}
So you could do:
clouds=setup(5);
clouds.show();
You need to create the empty array before you can assign to elements of it.
It's also best to declare your variables before using them.
var cloud;
function setup(){
//**Define**
cloud = [];
for(var i = 0; i < 5; i++) {
cloud[i] = new Cloud(random(100, windowHeight - 500), random(0.7, 1.3));
}
}
function draw
//**Draw Clouds**
for(var i = 0; i < 5; i++) {
cloud[i].show();
}
}

Why is non-static variable behaving like static?

function emergency() {
var ambulance = 100;
var callAmbulance = function() { alert(ambulance); }
ambulance++;
return callAmbulance;
}
var accident = emergency();
accident(); // alerts 101
I am referring to the variable 'ambulance'.
When I call accident(); it should call emergency() which should use the declared variable 'ambulance' [considering the global scope thing in javascript, still it could set the value to global] but its using old value 101 instead of setting again back to 100 - behaving more like static var.
What's the Explanation?
What you have there is called a closure. It means a function can access variables declared in outer functions (or in the global scope). It retains access even after the 'parent' function has returned. What you need to understand is that you don't get a copy. The changes performed on that variable are visible to your inner function, which in theory means you could have multiple functions sharing access to the same variable.
function f () {
var x = 0;
function a() { x += 1; }
function b() { x += 2; }
function show() { console.log(x); }
return {a:a, b:b, show:show};
}
var funcs = f();
funcs.a();
funcs.b();
funcs.show(); // 3
One thing to be aware of is that a subsequent call to f will create a new scope. This means a new x variable will be created (new a, b, show functions will be created as well).
var newFuncs = f();
newFuncs.a();
newFuncs.show(); // 1
funcs.a();
funcs.show(); // 4
So, how do you get a copy? Create a new scope.
function g () {
var x = 0;
var a;
(function (myLocal) {
a = function () { myLocal += 1; }
}(x));
x += 200;
return a;
}
JS only has pass-by-value so when you call the anonymous function, the xvariable's value will be copied into the myLocal parameter. Since a will always use the myLocal variable, not x, you can be certain that changes performed on the x variable will not affect your a function.
If, by any chance, you're coming from a PHP background you are probably used to do something like
use (&$message)
to allow modifications to be reflected in your function. In JS, this is happening by default.
You are creating a function definition which is not compiled at this time:
var callAmbulance = function() { alert(ambulance); }
And before you are sending it to the called function, you are incrementing the num:
ambulance++;
And then you are sending it to the called function:
return callAmbulance;
But whether you are sending it there or not, it doesn't matter. The below statement executes or compiles the function:
var accident = emergency();
And this takes in the current ambulance value which is 101 after the increment. This is an expected behaviour in creating function but not executing it. Please let me know, if you didn't understand this behaviour. I will explain it more clearly.

Javascript object's this scope, _this not fixing

Hello I am trying to create Tetris in JavaScript. I have map object with a variable called data. When I'm calling a function from the object, the 'this' is no longer the object but the local function I think. I have tried using declaring a variable _this = this in the objects constructor but doesn't help.
function Map(width, height){
var _this = this;
this.data = [];
for(var i = 0; i < width; i++){
var row = [];
for(var n = 0; n < height; n++){
row.push("0");
}
this.data.push(row);
}
}
Map.prototype.add = function(x, y, shape){
for(var i in shape){
for(var n in shape[i]){
_this.data[x+i][y+n] = shape[i][n];
}
}
}
var colMap = new Map(23,30);
var selectedShape = new FallingShape(0, 0, getShapeArray(randomShapeId(), 0));
colMap.add(selectedShape.x, selectedShape.y, selectedShape.shapeArray);
I'm trying to change the values of data but can't because of the scope, I've tried the '_this' trick but doesn't work. Can anyone help me understand?
Heres a link to a codepen page if you want to see the whole code. http://codepen.io/taylorlutjen/pen/OPGwoo?editors=001
Your _this local variable created in your constructor function is local to that function — it won't be available in the other function.
The good news is that you won't really need it - just use this in that .add() function.
Local variables in constructor functions are no different than local variables in any other function. They're not magic, and they definitely won't be available to other functions added to the constructor's prototype. The reason for creating a _this or that or self copy of the value of this is to be able to create things like callback functions that need a reference to the outer context object. None of your code (posted here) needs that.

How does this JavaScript closure work?

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.

Categories