Passing a global array to a function overwrites array [duplicate] - javascript

This question already has answers here:
Copy array by value
(39 answers)
Closed 7 years ago.
I'm trying to pass a global defined array as an argument to a function.
I thougt this function would treat the argument as a local variable.
But it doesn't... Changing the (in my opinion) local variable also changes the values of the global array. What am I doing wrong?
clickX = [];
for(var i=0; i<10; i++) {
clickX[i] = i;
}
doThis(clickX);
function doThis(x) {
for(var i=0; i<x.length; i++) {
x[i]++;
alert(clickX[i]); // this alerts the changed value of x[i] and not the origin value of the global array
}
}
jsfiddle:
https://jsfiddle.net/n546rq89/

In Javascript, arrays are passed by reference by default. You can alternatively pass ARRAY.slice() to pass by value:
clickX = [];
for(var i=0; i<10; i++) {
clickX[i] = i;
}
doThis(clickX.slice());
function doThis(x) {
for(var i=0; i<x.length; i++) {
x[i]++;
alert(clickX[i]); // this alerts the changed value of x[i] and not the origin value of the global array
}
}
Look at this thread for an explanation of slice() and copying array in JS.

Related

for-loop not modifying a global variable? [duplicate]

This question already has answers here:
Is JavaScript a pass-by-reference or pass-by-value language?
(33 answers)
Closed 6 years ago.
I wrote a for-loop that for some reason refuses to modify a global variable. Instead, it seems to create a local variable that it modifies temporarily. A condensed version of my code is as follows.
var clubsArray = [obj, obj, obj];
//each obj contains a property of "goalsFor" which holds an integer
var madridTotalGoals = 0;
var barcaTotalGoals = 0;
function findTotalGoals(clubsArray, totalGoals) {
for(var i = 0; i < clubsArray.length; i++) {
totalGoals += clubsArray[i].goalsFor;
}
}
findTotalGoals(clubsArray, barcaTotalGoals);
// this loops properly and does the math, but it never changes the value of barcaTotalGoals
In the full code there are numerous arrays that hold "club" objects; each contain a property key "goalsFor", which hold an integer as a value. There are also numerous "totalGoals" variables (two are specified here) that have been declared globally.
Does anyone know why the global variable (e.g. barcaTotalGoals) is not being modified when passed through this function? When I console log each step of this loop, the math is taking place but the result is not being stored. I apologize if this has been asked before but I've searched thoroughly.
The variable you are trying to pass, is passed by value and not reference. So it wont affect the original variable
You can assign the value once the for loop is finished
function findTotalGoals(clubsArray, totalGoals) {
for(var i = 0; i < clubsArray.length; i++) {
totalGoals += clubsArray[i].goalsFor;
}
barcaTotalGoals = totalGoals;
}
You are passing by value instead of by reference...
Instead, you could try like this:
clubsArray = [obj, obj, obj];
var totalGoals = {
madrid: 0,
barca: 0
}
function goalsByCountry(clubsArray, totalGoalsClub) {
for(var i = 0; i < clubsArray.length; i++) {
totalGoals[totalGoalsClub] += clubsArray[i].goalsFor;
}
}
goalsByTeam(clubsArray, 'barca');

Javascript global variable getting set to a new value [duplicate]

This question already has answers here:
Change the value of an array changes original array JavaScript
(3 answers)
Closed 7 years ago.
I'm new to javascript and coding in general, and I could use some help.
I am setting a global variable (generatedNumbers) equal to another variable (numbers) so that I can do some validation on the array. However, when I change the value of numbers, my global variable generatedNumbers gets changed as well. Any help would be appreciated.
var generatedNumbers;
function generateNumbers(numberOfNumbers) {
'use strict';
var i;
generatedNumbers = [];
for (i = 0; i < numberOfNumbers; i = i + 1) {
generatedNumbers.push(generateRandomNumber(9).toString());
}
}
function checkEachValidNumberUsed(userExpression, numbers) {
'use strict';
var i, j;
for (i = 0; i < userExpression.length; i = i + 1) {
for (j = 0; j < numbers.length; j = j + 1) {
if (userExpression[i] === numbers[j]) {
numbers.splice(j, 1);
window.console.log(generatedNumbers);
}
}
}
if (numbers.length !== 0) {
return true;
}
}
function validateExpression(userExpression) {
'use strict';
var numbers, validUserInput;
numbers = generatedNumbers;
window.console.log(generatedNumbers);
if (checkEachValidNumberUsed(userExpression, numbers)) {
document.getElementById("feedbackText").innerHTML = "Each number must be used exactly once.";
} else {
return true;
}
Arrays (and all other non-primitive types) are pass-by-reference, not copied, when you use the assignment operator = or pass them to a function, so any changes made to numbers (or the values of the elements of numbers) will be reflected in generatedNumbers.
For your array here, numbers = generatedNumbers.slice(0); will sufficiently clone the array, but keep in mind that if the contents of the array is not a primitive type (e.g. any object that you use the new keyword to create) will not be cloned: both arrays will reference the same objects.
That's because they both refer to the same object. If you want to make a copy of generatedNumbers (which I think you want to do in validateExpression) use slice.
numbers = generatedNumbers.slice(0);
In Javascript if you have an array
var a = [1,2,3,4];
and assign a to another variable
var b = a;
the two are referring the very same array object... for example after
b.push(99);
a will also see the mutated array.
If you want to make a copy you need to do so explicitly for example with
var b = a.slice();

Why is result different (using var vs. let)?

This uses var
var a = [];
for (var i = 0; i < 10; i++) {
a[i] = function() {
console.log(i);
};
}
a[6](); // 10
This uses let
var a = [];
for (let i = 0; i < 10; i++) {
a[i] = function() {
console.log(i);
};
}
a[6](); // 6
I don't understand why the result is different. Can somebody guide me?
The resulting array consists of functions, each function body looks like this:
console.log(i);
The value of i depends on whether we used var or let to declare the variable.
var (ECMAScript 5 and 6)
Here i is a global variable whose value is 10 after exiting the loop. This is the value that is logged.
let (ECMAScript 6)
Here i is a local variable whose scope is restricted to the for statement. Moreover, this variable gets a fresh binding on each iteration. This is best explained by your code transpiled to ECMAScript 5:
"use strict";
var a = [];
var _loop = function(i) {
a[i] = function() {
console.log(i);
};
};
for (var i = 0; i < 10; i++) {
_loop(i);
}
a[6](); // 6
So, on seventh iteration for example, the value of i will be 6 (counting from zero). The function created inside the iteration will refer to this value.
I think it would be much better to not define functions in a loop, you could easily accomplish this with one function definition that returns a closure:
function logNumber(num) {
return function() {
console.log(num);
}
}
var a = [];
for (let i = 0; i < 10; i++) {
a[i] = logNumber(i);
}
a[6]();
Regarding the difference between the two examples, one is using let for block scoping. A better example that shows the difference would be:
ECMA5:
for (var i = 0; i < 10; i++) { }
console.log(i); // 10
ECMA6:
for (let i = 0; i < 10; i++) { }
console.log(i); // i is not defined
Edit: as I stated in my comment to your question, this is more likely a side-effect of the transpiler you are using. Firefox supports block scoping and both versions of your loop produce 10 as output (which they should).
This is correct behavior according to the spec. The behavior with var and let is defined to be different.
See the spec, at https://people.mozilla.org/~jorendorff/es6-draft.html#sec-forbodyevaluation. According to this, the relevant concepts, which make the function declared inside the loop close over the current value of the block-scoped loop index, are things called "per-iteration bindings" and "per-iteration environment".
Babel handles it correctly, producing the following code:
var a = [];
var _loop = function (i) {
a[i] = function () {
console.log(i);
};
};
for (var i = 0; i < 10; i++) {
_loop(i);
}
This implements the semantics of for (let by isolating the contents of the for loop into a separate function parameterized by the index. By virtue of doing that, the function no longer closes over the for loop index, and i is treated separately in each function created. Thus the answer is 6.
Traceur does not produce the correct result. It yields 10.
So the famous question that has been asked 100 times on SO, about why my function declared in a loop and closing over the index index is using the "wrong" value of the loop index, shall be asked no more?
The issue is a bit more nuanced that merely proclaiming that "of course, let is block-scoped". We know that. We get how it works in an if block, for example. But what's going on here is a bit of an twist on block scoping in the context of a for, hitherto unknown to many people including me. It's a variable actually declared outside the "block" (if you think of the block as the body of the for statement) but has a separate existence inside each iteration of the loop.
For more, see https://github.com/babel/babel/issues/1078.
Why is result different in ES6 and ES5?
Because let and var are different. let is block-scoped while var is function-scoped.
In your first example there is only a single variable i. Every function you create has a reference to the same variable i. At the moment you call a[6](), i has the value 10, because that was the termination condition for the loop.
In the second example, every iteration of the loop has it's own variable i. It works exactly like in other languages with block scope.

Passing array of functions to another function

I'm trying to pass arbitrary number of arguments to function. The arguments should be json type [function : arrayOfArgs]. The keys are functions and the values are arrays of the arguments, that should be passed to those functions.
At first, I considered function with only a number of argument
function _gl(f,args){ f.apply(null,args); }
function func(a,b){ alert(a+b); }
//calling the function
<input type="button" value="test" onclick="_gl(func,['2','3']);"/>
and it works pretty good .Now I'm trying to generalize that method
function _GL(){
var arguments = _GL.arguments;
var i=0;
for(i;i<arguments.length;i++)
{
var A=arguments[i];
for(j in A) j.apply(null,A[j]);
}
}
//and calling it
<input type="button" value="TEST" onclick="_GL({func:['2','3']});"/>
but i'm getting the following error "Uncaught TypeError: Object func has no method 'apply' ".
You can use this code jsFiddle:
_GL = function() {
var arguments = _GL.arguments;
var i = 0;
for (i = 0; i < arguments.length; i++) {
var arg = arguments[i];
for (j in arg) {
var f = window[j];
f.apply(null, arg[j]);
}
}
};
As you can see you have to get your function f first from the window element by its name. Then f has the right type an args can be applied.
{func:['2','3']}
You are making an object with a (string) key called "func" with value ['2','3']. Strings are not functions, so it doesn't have .apply().
In objects, your keys must be strings, you cannot use other types as keys.
To "generalize" it, you should pass it an array of functions and their arguments. Something like this:
[[func, ['2','3']], [func2, ['abc']]
So, if you did this:
onclick="_GL([[func, ['2','3']], [func2, ['abc']]);"
Then you could loop through and get the functions and call 'em.
function _GL(funcs){
for(var i=0, len=funcs.length; i < len; i++){
var func = funcs[i];
func[0].apply(null, func[1]);
}
}
One possible solution;
var _gl = function (callables) {
// Loop through each property in the passed in object
for (var fnName in callables) {
// Only apply for properties that is local to `callables`
if (callables.hasOwnProperty(fnName)) {
window[fnName].apply(callables[property]);
}
}
};
... onclick='_gl({"MyFunction": ["a", 1, []]});' ...
Instead of using the global namespace, you could (and should!) set up an object with your callable functions.

How to change this in object?

How to create method twice? I can't understand how to change this in body of function. Why it doesn't work?
function twice() {
var buf = [];
for ( var i = 0; i < this.length; i++ ) {
buf.push(this[i]);
}
for ( var i = 0; i < this.length; i++ ) {
buf.push(this[i]);
}
this = buf;
}
Array.prototype.twice = twice;
a = [1,2,3];
a.twice();
a; // [1,2,3,1,2,3]
I can't understand how to change this in body of function
If you mean the value of this, you can't. But you don't have to for what you're doing
You're very close, you just have a fair bit to remove:
function twice() {
var i, l;
for (l = this.length, i = 0; i < l; ++i) {
this.push(this[i]);
}
}
Remember, your array is an object. To change its contents, you just change its contents, you don't have to change the reference to it.
Note, though, that you can use this trick on any modern browser:
function twice() {
this.push.apply(this, this);
}
That works by using the Function#apply function, which calls the function you call it on (so, push in our case) using the first argument you give it as the object to operate on, and the second argument as the arguments to pass into that function (which it takes as an array). More on MDN and in the spec. It happens that push allows you to pass it an arbitrary number of arguments, and will push each one in order. So if you're trying to add the contents of the array to the array a second time, that one line will do it (on modern browsers, some older IE implementations don't like this use of push.apply).
You cannot assign a value to this. That's the rules. But you can modify the value of this. Try pushing some values into this.
function twice() {
var len = this.length;
for (var i = 0; i < len; i++) {
this.push(this[i]);
}
}
Array.prototype.twice = twice;
a = [1, 2, 3];
a.twice();
alert(a);
Here's a fiddle. http://jsfiddle.net/Qvarj/ As you can see, most of the logic is yours.

Categories