Weird function behaviour - javascript

I have a simple function:
var k = 10;
function getNumber(k){
if ((1<2)&&(k===10)) {
k = 11;
return 4;
}
return 2;
}
console.log(getNumber())
console.log(getNumber())
Why does it always give 2, ignorning if ()?

Parameter k is scoped to the function, remove that:
var k = 10;
function getNumber(){
if ((1<2)&&(k===10)) {
k = 11;
return 4;
}
return 2;
}
console.log(getNumber())
console.log(getNumber())

k variable is not given to getNumber().
In function scope, parameter is undefined

Why does it always give 2, ignorning if ()?
It doesn't ignore the if() its just the the condition is false - which can be checked using a console.log
var k = 10;
function getNumber(k){
console.log(k, k === 10);
if ((1<2)&&(k===10)) {
k = 11;
return 4;
}
return 2;
}
console.log(getNumber())
console.log(getNumber())
As you can see, k inside the function is undefined and as this variable is local to the function the k in outer scope is not the value you're working with that you expected to check by setting it to 11.

See the comments in the code
// This is a global variable. Similar to
// window.k = 10;
var k = 10;
// By setting k as parameter to the function you define it as
// a local variable in the function scope
// It's somewhat similar to this line
// var k = arguments[0];
// Since you call getNumber() without arguments, k is undefined in this scope
function getNumber(k) {
// the test for the k value is against the local value,
// which is undefined and not 10
if ((1 < 2) && (k === 10)) {
// This code is never executed
k = 11;
return 4;
}
// Always returns 2
return 2;
}
console.log(getNumber())
console.log(getNumber())
To make your code work, do this
var k = 10;
// function parameter with default value if argument is not given
function getNumber(k = window.k) {
if ((1 < 2) && (k === 10)) {
// set global k instead of local
window.k = 11;
return 4;
}
return 2;
}
console.log(getNumber())
console.log(getNumber())
BTW This is not a code strategy that I'd recommend. Global variables should be avoided, especially the use or change of global variables inside functions. Since I regard this as a demonstration code and not real life code, my code correction is for this demonstration purpose alone and should not be used in production code.

Related

Multiplication Loop not multiplying

Trying to loop the arguments entered and return the arguments as the total multiplication:
let lightCode = { //Creates Object.
Multiply: function() { //Multiplys all arguments.
const total = 0;
for(const i = 0; i < arguments.length; i++) {
console.log(arguments[i]);
total *= arguments[i];
}
return total;
}
}
lightCode.Multiply(12, 16)
You have two problems, one of which is that you can not reassign a value to a constant. Second you're setting total = 0 first. By doing that you'll be multiplying everything by 0.
So in order to solve your problem you need a if conditional to check if total is 0, if is 0 you assign the property total to the argument in the loop, if not you multiply it.
let lightCode = { //Creates Object.
Multiply: function() { //Multiplys all arguments.
let total = 0;
for(let i = 0; i < arguments.length; i++) {
if(total === 0) total = parseFloat(arguments[i]);
else total *= parseFloat(arguments[i]);
}
return total;
}
}
console.log(lightCode.Multiply(12, 16));
I recommend you to read about const, let, var and when to use them. There are (at least) two mistakes in your code:
const total = 0 => since total is declared using the const identifier, it means that its value is going to be constant during your program. And what does constant mean? That it stays the same. But the line total *= arguments[i]; wants to change it, resulting into an error. Also, initializing the total with 0 makes the final results to be 0 (remember that the multiplication identity element is 1).
const i = 0 => same thing; i++ wants to increment the value of i, but you declared it as const.
Running your code and opening the console you can clearly say an error message: "Uncaught TypeError: Assignment to constant variable.".
Cheers!
let lightCode = {
Multiply: function() {
var total = 1;
for (var i = 0; i < arguments.length; i++) {
console.log(arguments[i]);
total *= arguments[i];
}
return total;
}
}
lightCode.Multiply(12, 16)
As pointed in the comments, there is more than one error in the code, first you are assigning the variables as const which is not correct in this case, the rule of thumb is to use let every time you need to reassign a variable (meaning, when you need to use the =symbol again), otherwise use const. Also as pointed in the comments you should not initialize the variable with zero, otherwise the loop will always return zero.
Here is a working snippet:
const lightCode = { //Creates Object.
Multiply: function() { //Multiplys all arguments.
let total = 1; // can not be zero, otherwise the loop will always return zero
for(let i = 0; i < arguments.length; i++) {
console.log(arguments[i]);
total *= arguments[i];
}
return total;
}
}
lightCode.Multiply(12, 16)
Notice how I am using const for the lightCode variable, because this object is never reassigned (meaning, you won't use the = to assign a new value again) and instead for total I'm using let, because is reassigned on every loop interaction.

JS: Default function parameter values and scope

I'm a bit confused about how scope & variable assignment within a function seems to change when assigning a default parameter value for that function.
For example, when this function has a default value assigned to parameter i, the output array variable appears to be block-scoped when inspected with Chrome Dev Console,:
function steps(n, i = 40) {
var output = [n];
}
steps(10, 20);
However, by removing the default parameter value for i, the output array variable is scoped locally:
function steps(n, i) {
var output = [n];
}
steps(10, 20);
Why does assigning a default value to parameter i affect the scope of the output array variable?
I was initially made aware of this shift in function scope by attempting to run the following segment of code through pythontutor.com's live programming environment for Javascript. Even though the code executes as expected within an IDE, it fails to run due to scope issues on pythontutor:
function steps(n, i = 1) {
// declare base case
if (n === 0)
return;
var output = [];
print(i, "#");
print(n - 1, " ");
console.log(output.join(""));
// make recursive call
steps(n - 1, i + 1);
function print(num, char) {
for (let j = 0; j < num; j++) {
output.push(`${char}`);
}
}
}
steps(3);
The pythontutor processor halts execution three steps in, at the invocation of print() just after declaring the output variable. Pythontutor.com will, however, execute the code as expected if I declare the output variable globally first:
var output = [];
function steps(n, i = 1) {
// declare base case
if (n === 0)
return;
output = [];
print(i, "#");
print(n - 1, " ");
console.log(output.join(""));
// make recursive call
steps(n - 1, i + 1);
function print(num, char) {
for (let j = 0; j < num; j++) {
output.push(`${char}`);
}
}
}
steps(3);
It's because default initialisers run in their own scope. Only if there are none, the body code is evaluated in the top function scope. It would only make a difference if you put a function expression in a default initaliser, which may close over the other parameters but does not have access to the variables that will be declared in the body.
Basically it's the difference between
function steps() {
var n = arguments[0],
i = arguments[1];
var output = [n];
}
and
function steps() {
var n = arguments[0],
i = arguments.length > 0 ? arguments[1] : 40;
(() => {
var output = [n];
}());
}

javascript for-loop and timeout function

I am facing an issue with a for-loop running a setTimeout.
for (var x = 0; x < 5; x++) {
var timeoutFunction = function() {
return function() {
console.log(x)
}
}
setTimeout(timeoutFunction(), 1)
}
I expect the output
0
1
3
4
Yet, for some reason they all output 5.
Variable x is defined in the local scope of the for-loop, so I thought this may not count for the callback of setTimeout. I tested with defining x outside of the for-loop.
var x = 10
for (var x = 0; x < 5; x++) {
var timeoutFunction = function() {
return function() {
console.log(x)
}
}
setTimeout(timeoutFunction(), 1)
}
I figured this output would be giving 10, yet it didn't. Then I thought it would make sense to define x afterwards.
for (var x = 0; x < 5; x++) {
var timeoutFunction = function() {
return function() {
console.log(x)
}
}
setTimeout(timeoutFunction(), 1)
}
var x = 10
This does return only 10. This implies the callbacks are all called after the for-loop is executed? And why do they only conform to the parent scope of the for-loop once the variable is initialised after execution of the for-loop? Am I missing something?
I know how make this example working with
for (var x = 0; x < 5; x++) {
var timeoutFunction = function(x) {
return function() {
console.log(x)
}
}
setTimeout(timeoutFunction(x), 1)
}
Yet, I really wonder what is missing...
Note that specifying 1 as the delay value will not actually cause the function to execute after 1 millisecond. The fastest the function could execute is roughly 9 milliseconds (and that's based on the internals of the browser), but in reality, it can't run the function until there is no other code running.
As for the unexpected output, you are experiencing this behavior because the timeoutFunction includes a reference to the x variable that is declared in a higher scope. This causes a closure to be created around the x variable, such that each time the function runs it does not get a copy of x for itself, but it is sharing the x value because it is declared in a higher scope. This is the classic side effect of closures.
There are a few ways to adjust the syntax to fix the issue...
Make a copy of x and let each function use its own copy by passing x into the function. When you pass a primitive type (boolean, number, string) a copy of the data is created and that's what is passed. This breaks the dependence on the shared x scope. Your last example does this:
for (var x = 0; x < 5; x++) {
var timeoutFunction = function(x) {
return function() {
console.log(x)
}
}
// Because you are passing x into the function, a copy of x will be made for the
// function that is returned, that copy will be independent of x and a copy will
// be made upon each loop iteration.
setTimeout(timeoutFunction(x), 1)
}
Another example that does the same thing would be needed if your timeout function wasn't returning another function (because there would be no function to pass the value to). So, this example creates an extra function:
for (var x = 0; x < 5; x++) {
// This time there is no nested function that will be returned,
function timeoutFunction(i) {
console.log(i);
}
// If we create an "Immediately Invoked Funtion Expression" (IIFE),
// we can have it pass a copy of x into the function it invokes, thus
// creating a copy that will be in a different scope than x.
(function(i){
setTimeout(function(){
timeoutFunction(i); // i is now a copy of x
}, 1);
}(x));
}
If you are working with browsers that support the ECMAScript 2015 standard, you can simply change the var x declaration in your loop to let x, so that x gets block level scope upon each iteration of the loop:
// Declaring a variable with let causes the variable to have "block-level"
// scope. In this case the block is the loop's contents and for each iteration of the
// loop. Upon each iteration, a new "x" will be created, so a different scope from
// the old "x" is what's used.
for (let x = 0; x < 5; x++) {
var timeoutFunction = function() {
return function() {
console.log(x)
}
}
setTimeout(timeoutFunction(), 1)
}
You have to create new scope for your function to make it 'remember' value from given iteration:
for (var x = 0; x < 5; x++) {
var timeoutFunction = (function(x) {
return function() {
console.log(x)
}
})(x)
setTimeout(timeoutFunction(), 1)
}
Another solution is to use ES2015 let:
for (let x = 0; x < 5; x++) {
var timeoutFunction = function() {
console.log(x)
}
setTimeout(timeoutFunction(), 1)
}
use this snippet
for(let x = 0; x < 5; x++) {
var timeoutFunction = function() {
console.log(x);
};
setTimeout(timeoutFunction, 1);
};
console.log('For loop completed');
JavaScript is not multi threaded so with your code the timeoutFunction will execute after the for loop is completed since you are using the global variable (with respect to timeoutFunction context) the final result is printed

javascript closure in loop

Following code is given:
var a = [ ], i = 0, j = 0;
for (i = 0; i < 5; i += 1) {
(function(c) {
a.push(function () {
console.log(c); });
})(i);
};
for (j = 0; j < 5; j += 1) { a[j](); }
Why does i always get bigger by 1 instead of staying 5? Hasn't the foor loop already been passed, so the i parameter given to the anonymous function should be 5?
If you referenced i from the inner closure then yes, you would see the result being 5 in all cases. However, you pass i by value to the outer function, which is accepted as parameter c. The value of c is then fixed to whatever i was at the moment you created the inner closure.
Consider changing the log statement:
console.log("c:" + c + " i:" + i);
You should see c going from 0 to 4 (inclusive) and i being 5 in all cases.
chhowie's answer is absolutely right (and I upvoted it), but I wanted to show you one more thing to help understand it. Your inner function works similarly to a more explicit function call like this:
var a = [ ], i = 0, j = 0;
function pushFunc(array, c) {
array.push(function () {
console.log(c);
});
}
for (i = 0; i < 5; i += 1) {
pushFunc(array, i);
}
for (j = 0; j < 5; j += 1) { a[j](); }
Which should also help you understand how c comes from the function argument, not from the for loop. Your inner function is doing exactly the same thing as this, just without an externally declared named function.

Default values for objects in Javascript

Javascript Code
var a = {};
a.test += 1; //NaN
++a.test; //NaN
Instead
var a = {};
a.test = 0;
++a.test; //1
a.test += 1; //2
I wonder if there could be anyway that can make first code sample work the same as second, i.e without an explicit assignment to 0. As in assigning default value for any property of an object to 0 instead undefined. I'm trying to do this in node.js. So, no problem of cross browser things and old ECMA Specs.
var i;
for(i = 0; i<10; i++) {
if(a.test) {
++a.test;
} else {
a.test = 0;
++a.test;
}
//a.test = a.test || 0; (1)
//++a.test;
}
If it is possible then the inner if/else or the assignment statement(1) in the above code can be eliminated.
Javascript by default defines all new variables as undefined ( if not explicitly defined ) , which is different from Number object, which you are trying to define. So you should use smth like :
for (i=0; i<10; i++) {
a.test = a.test || 0;
++a.test
}
There's no way to do this for any arbitrary undefined property.
For a known property name, there is a way, but DO NOT USE IT !! 1
Object.prototype.test = 0;
This will give every object an implicit .test property with that value in it, and the first time you attempt to modify it the result will be stored in your own object:
> Object.prototype.test = 0
> a = {}
> a.test++
> a.test
1
1 Adding stuff to Object.prototype will break stuff, including for (key in obj)
This is where prototypes come in useful in javascript:
function Demo() {
}
Demo.prototype.x = 0;
> a = new Demo();
> a.x += 1
> a.x
1
> b = new Demo()
> b.x
0
In the first code, you can't. Any number added to a non-number or NaN value will always result in NaN
var a = {};
a.test += 1; // undefined + 1 = NaN
++a.test; // ++(undefined) = NaN
as for the inner if
for(i = 0; i<10; i++) {
a.test = a.test || 0; //use existing value, or if undefined, 0
++a.test; //increment
}
I think this would do:
var a = {"test":0};
a.test += 1;
Using standard JS it's not possible to do what you're asking. Unless you prototype I guess? But I'm not experienced with Prototype.And I don't think prototyping works with Node.js
The reason for this is because js is not a typed language (i.e. we only use var to declare a variable, not int a or string b), so in order to use the ++ operator, the variable needs to be given a type through assignment.
hope that helps

Categories

Resources