function statement and function expression inside if statement - javascript

Any reason why the following snippet shows different output in Firefox and Chrome:
var sayHo;
console.log(typeof(sayHey));
console.log(typeof(sayHo));
if(true) {
function sayHey() {
console.log("Hey, inside if");
};
sayHo = function() {
console.log("Ho, inside if");
};
}
else {
function sayHey() {
console.log("Hey, inside else");
};
sayHo = function() {
console.log("Ho, inside else");
};
}
sayHey();
sayHo();
Chrome(v31) outputs
function
undefined
Hey, inside else
Ho, inside if
Firefox(v26) outputs
undefined
undefined
Hey, inside if
Ho, inside if
I expected the same output what Chrome gave. During parse time, the function declaration inside will override the function declaration inside if. Because JavaScript will try to hoist both the function declarations, hence the override.

Firefox and Chrome use different JavaScript engines (SpiderMonkey and V8 respectively). The Chrome behaviour can be considered either a 'bug' or an 'ease of use feature'. In V8 function definitions inside if statements (rather than assignment of an anonymous function to a variable) are performed pre-execution. It's a design decision.

It all boils down to hoisting differences :
Function declarations and variable declarations are always moved (“hoisted”) invisibly to the top of their containing scope by the JavaScript interpreter. Function parameters and language-defined names are, obviously, already there. This means that code like this:
function foo() {
bar();
var x = 1;
}
is actually interpreted like this:
function foo() {
var x;
bar();
x = 1;
}
Consider this example :
function test() {
foo(); // TypeError "foo is not a function"
bar(); // "this will run!"
var foo = function () { // function expression assigned to local variable 'foo'
alert("this won't run!");
}
function bar() { // function declaration, given the name 'bar'
alert("this will run!");
}
}
test();
this code is equivalent to :
function test() {
function bar() { // function declaration, given the name 'bar'
alert("this will run!");
}
foo(); // TypeError "foo is not a function"
bar(); // "this will run!"
var foo = function () { // function expression assigned to local variable 'foo'
alert("this won't run!");
}
}
test();
Look what happened to the function declaration : it is hoisted.
Leet's look at simplier sample : http://jsbin.com/UKICENE/4/edit
console.log(typeof(sayHey));
if(true) {
function sayHey() {
console.log("Hey, inside if");
};}
sayHey();
This will yield function in chrome.
But it will yield undefined in FF.
Hoeever - deferring the calculationm via setTimeout will yield the same result as chrome :
console.log(typeof(sayHey));//undefined
setTimeout(function (){console.log(typeof(sayHey));},1000); //function
if(true) {
function sayHey() {
console.log("Hey, inside if");
};}
sayHey();
So , like Šime said , it delay the evaluation.
If you ask me , I think chrome is doing the right thing

Related

JS variable hoisting inside closure

JS hoists all variables within scope so why does this code throw an exception:
(function() {
function i() {
console.log(a.a);
}
i();
var a = { a:1 };
})()
While this one works:
(function() {
var a = { a:1 };
function i() {
console.log(a.a);
}
i();
})()
What behavior of interpreter causes this?
The variable a is already visible, but its value {a:1} is not assigned until the var is reached by the execution. Things are somewhat different for local functions declared with function foo(){...} because the names are bound to the respective functions/closures before the execution starts.
If you call the closure i before assigning a a value you get the problem, however with
(function(){
function i(){
console.log(foo());
}
i();
function foo() { return 42; }
})();
the output is 42 and there's no error.
Note that this is true because the function statement is used... changing the code to
(function(){
function i(){
console.log(foo());
}
i();
var foo = function(){ return 42; }
})();
gives an error because in this case when i() is executed foo is not yet bound to the function (like in your case).

How to monkey-patch a function declared inside another function

Say I have the following code:
window.foo = function() {
bar();
function bar() {
console.log('hello');
}
}
/* insert monkey-patching code here */
foo();
What code can I replace /* insert monkey-patching code here */ with in order to make this e.g. write goodbye instead of hello on the console?
I have tried the following in order to override bar, but it does not work:
window.foo = function() {
bar();
function bar() {
console.log('hello');
}
}
window.bar = function() {
console.log('goodbye');
}
window.foo.bar = function() {
console.log('goodbye');
}
foo();
You can't.
The function is stored in a local variable inside foo. It isn't accessible from outside that function.
You would need to either:
Replace the whole of foo
Refactor foo so bar was declared in a wider scope (and accessible from where you want to change it)

JavaScript Function Overloading / Overwriting

I'm trying to understand
function test() {
return 'foo';
}
console.log(test());
test = function() {
return 'bar';
}
console.log(test());
function test(a, b) {
return 'baz';
}
console.log(test());
console.log(test(true));
console.log(test(1, 2));
the above code which consoles
baz
bar
bar
bar
bar
But being JavaScript a single-threaded language and function overloading concept I was expecting
foo
bar
bar
baz
baz
Could anyone explain why is this happening?
Step by step :
function test() {
return 'foo';
}
This is a function declaration. A test variable is declared when it's interpreted, before runtime.
test = function() {
return 'bar';
}
This is a function expression. The test variable will be overwritten when this line is executed.
function test(a, b) {
return 'baz';
}
This is another function declaration. The test variable is overwritten, again before runtime.
That's why your first version of the test function is never called. Because the second function declaration overwrote it before runtime.
More about function declaration vs. function expressions.
Yep. I think what was happening is the following:
Functions declared with function test(...) { ... } are hoisted to the top of current scope. So both definitions of your function using that syntax were hoisted to the top, but the second one defined overwrote the first one, thus the result 'baz.'
Function expressions are not hoisted, e.g. test = function (...) {...}. So when you reassigned the identifier test to that function expression, it became the value for test for the remainder of your script.
As pointed out already, you cannot overload vars or functions in JavaScript. You can overwrite vars with new values, which is what you did in your example. What is confusing is the way that JavaScript hoisting works.
If you want to avoid hoisting use let myFn = function (...) { ... }.
Here's a line by line, as I understand it:
// `test` defined with func declaration, hoisted to top
function test() {
return 'foo';
}
console.log(test);
console.log(test());
// `test` overwritten with function expression, hoisting has already occurred,
// `test` identifier will have this value for remainder of script
test = function() {
return 'bar';
}
console.log(test);
console.log(test());
// `test` overwritten with new func declaration, hoisted to top, but after first declaration
function test(a, b) {
return 'baz';
}
console.log(test);
console.log(test());
console.log(test(true));
console.log(test(1, 2));
Javascript function can't have overloads, they just get overwritten. To get the same effect you need to distinguish the different overloads inside your method.
function test(a, b) {
if(b)
return 'baz';
return 'foo';
}

Can isNaN be used as identifier in a named function expression?

The context:
I am reading the book You Don't Know JS: Up & Going, Chapter 2: Into JavaScript. In the Polyfilling section, the author gives the following example:
if (!Number.isNaN) {
Number.isNaN = function isNaN(x) {
return x !== x;
};
}
So, what is being done here is creating a polyfill so that the ES6 method Number.isNaN can run in older browsers (ie, pre-ES6 versions). As I expected, he makes use of the older isNaN method. The former method is actually meant to deprecate the latter by design.
The question:
Why is isNaN being used as the identifier of the named function expression? I would have expected it to be used in the body somehow. Why? Because all of the identifiers usually seen in examples around the web, when comparing function expressions vs function declarations take the following form:
Function declaration:
function foo() {
// ..
}
Function expression:
var foo = function bar() {
// ..
};
So in this example bar is being defined within the brackets after the 'bar()' string. So why use 'isNaN' above when isNaN is an already defined function within JavaScript? Are we actually overwriting it for the purpose of the polyfill? What am I missing/misunderstanding here?
When you have a named function in an expression, that value is not hoisted but is saved in expression scope. So original value is not changed.
Sample
function bar(){
console.log("In Bar")
}
var foo = function bar(){
console.log("In Foo")
}
bar();
foo();
Why should you use it?
In case of exception, that will help you in debug. An anonymous function will not show any name in stack trace and if you have say 4-5 anonymous functions in 1 function, it becomes difficult as to which one failed. Having a named functions makes it little simple.
var bar = function Test() {
(function() {
(function() {
(function() {
throw new Error("I have no Name2")
})()
})()
})()
}
var foo = function Test() {
(function inFoo1() {
(function inFoo2() {
(function inFoo3() {
throw new Error("I have no Name2")
})()
})()
})();
}
function init() {
try {
foo();
} catch (ex) {
console.log(ex.stack)
}
try {
bar();
} catch (ex) {
console.log(ex.stack)
}
}
function start() {
init();
}
start();
You can also refer to Why using named function expressions? for more information.

Scope of variables (Hoisting) in Javascript

One of my friends was taking an online quiz and he asked me this question which I could not answer.
var global = false;
function test() {
global = true;
return false;
function global() {}
}
console.log(global); // says false (As expected)
test();
console.log(global); // says false (Unexpected: should be true)
If we assume that functions are hoisted at the top along with var variables, let's try this one.
var foo = 1;
function bar() {
return foo;
foo = 10;
function foo() {}
var foo = 11;
}
bar();
console.log(foo); //says 1 (But should be 11) Why 1 this time ??
Here is a JSBin Demo and JSBIN Demo2 to play with.
PS: If we remove function global() {} from test(), then it runs fine. Can somebody help me understand why is this happening ?
var statements and function declaration statements are "hoisted" to the top of their enclosing scope.
Therefore, the function global(){} in your function creates a local global name.
Assigning to global inside your functions binds to this local name. Here's how you can "rewrite" it using hoisting to understand how the compiler sees it:
function test() {
var global = function() {}; // hoisted; 'global' now local
global = true;
return false;
}
I'll answer the second part of your question,
If we assume that functions are hoisted at the top along with var variables
bar();
console.log(foo); //says 1 (But should be 11) Why 1 this time ??
You should try console.log(bar()); console.log(foo); instead. However, what hoisting does to your function is this:
function bar() {
var foo;
function foo() {}
return foo;
foo = 10;
foo = 11;
}
So you should expect to get the function returned, since your variable assignments are after the return statement. And both the var and the function declaration make foo a local variable, so the global foo = 1 is never changed.

Categories

Resources