I had to use json2.js in my project as browser(IE8) JSON object was not available for parsing strings to JSON.
I ran through json2.js and am having a doubt with the variable declaration.
A JSON global variable is declared in json2.js like
var JSON;
if(!JSON){
JSON={};
}
What is the effect of declaration var JSON; on the global JSON object.
I hope the declaration should override the global JSON object in any browser (IE8/IE7).
But for my surprise it is not overriding when a global object is available.
Only a variable definition / initiation overrides a global variable?
Kindly clarify.
For each variable declaration (not initialization!). The following happens (section #10.5):
8. For each VariableDeclaration and VariableDeclarationNoIn d in code, in source text order do
Let dn be the Identifier in d.
Let varAlreadyDeclared be the result of calling env’s HasBinding concrete method passing dn as the argument.
If varAlreadyDeclared is false, then
Call env’s CreateMutableBinding concrete method passing dn and configurableBindings as the arguments.
Call env’s SetMutableBinding concrete method passing dn, undefined, and strict as the arguments.
So you see, whenever var x is encountered, it is tested whether a variable with name x already exists in the environment. If yes, it is just ignored, but if not, then the variable is declared and initialized with undefined.
Since the code is run in global scope it tests whether JSON exists in global scope. So if JSON already exists, var JSON; is just ignored.
Just some thoughts regarding testing/explaining this behaviour:
I don't know at which point in the JavaScript execution the global object is created, but I assume before all other scripts are evaluated. That means, JSON exists and has a value before any variable declaration, something you can only simulate if you include two scripts (can also be inline I guess, they are evaluated after another).
Try:
// script1.js
var foo = 'bar';
// script2.js
var foo;
if(!foo) {
foo = 'baz';
}
alert(foo);
// include script2.js after script1.js
What's the result? (cheaters look here).
Whenever you are in a single script file, all variable declarations are hoisted to the top anyways. So if you have
var foo = 'bar';
var foo;
if(!foo) {
foo = 'baz';
}
the script is actually executed as:
var foo;
var foo;
foo = 'bar';
if(!foo) {
foo = 'baz';
}
You could not actually test whether the second var foo; overwrites the first one, since at this point it has no value yet. So this is not a good example to demonstrate the behaviour quoted above.
The var keyword ensures there is a variable in the enclosing function or (like here) in window but it doesn't initialize one.
In fact, the order doesn't really impact.
Look at this code as an example :
a = 3;
var a;
alert(a);
It alerts "3".
So the var declaration in the code you show simply ensures there won't be errors in the test, and doesn't remove the existing value.
It's good to keep this in mind because a common error in javascript is to have more than one var declaration in a function and being surprised by the value set before even the var declaration. Run this for example :
if (true) {
a = 3;
}
// lot of code, at a different level
if (true) {
var a;
if (433/43==3) a=true;
if (a) alert('should not happen');
};
Yes, the var is attached to the function, not the block...
It may not be an option for you, but it is also possible to set IE8 to compatibility mode to get the native JSON object back:
// in your preinit page event
Response.AddHeader("X-UA-Compatible", "IE=8");
The other option would be to create the JSON variable only if it does not exist:
var JSON = JSON || {};
If you are in the global scope when executing that block of code, then var JSON will overwrite the global JSON object to undefined.
If you are in any other scope (like inside a function), then there will be no effect.
Related
I am learning the hoisting concept in javascript. One piece of code which blocked me is this:
function foo() {
console.log(8);
}
var foo;
console.log(foo);
Question 1:
I typed in this code in chrome console, print out is
function foo(){
console.log(8)
}
would the variable declaration overwrite the function declaration? I think the print out should be undefined
Question 2:
I typed in the same code in an online editer https://jsbin.com/dezixozewi/edit?js,console, it throws error as 'Identifier 'foo' has already been declared'. so it does not allow duplicate declaration at all.
Could any one help to explain the logic behind? Thanks a lot in advance!
The reason the function is the value of foo is because in this line:
var foo;
You're just stating foo - that's a statement that doesn't do anything.
If you actually set foo to something, for example "FooBar":
function foo() {
console.log(8);
}
var foo = "FooBar";
console.log(foo);
It overwrites the function, because as shown in this question, declaring anything with var that is a value will result in that value being "hoisted" to the top, and as such being the actual value of the identifier.
would the variable declaration overwrite the function declaration?
No. Before any code is executed, the runtime finds all function and variable declarations and initializes the new scope with them. Things vary slightly between evaluating script code and evaluating function scope, but the result is the same:
Duplicate declarations are ignored.
Function declaration are evaluated at the end and assigned to their respective name.
In global scope, because foo is both a function name as well as a variable declaration, the variable declaration is ignored. And since it doesn't have an initializer, the value of foo doesn't change when that line is actually evaluated.
The details can be found in the language specification.
Question 2: I typed in the same code in an online editer https://jsbin.com/dezixozewi/edit?js,console, it throws error as 'Identifier 'foo' has already been declared'. so it does not allow duplicate declaration at all.
Unclear how jsbin evaluates the code, but it is valid.
var foo; statement declares foo variable and set it's value to undefined if it already has not a value. Your foo has a value, since the var foo; actually doesn't do anything.
If you remove the foo function and replace it with a simple variable, you will see the same behaviour.
var foo; simply points to the memory location of foo. It happens that you've already declared a function there, so that's why the value of foo is just still the function.
It's no different than if you did:
var x = 2;
var x; // We're not setting a value with `=`, so nothing gets overridden
x; // 2
Today, I got completely surprised when I saw that a global variable has undefined value in a certain case.
Example:
var value = 10;
function test() {
//A
console.log(value);
var value = 20;
//B
console.log(value);
}
test();
Gives output as
undefined
20
Here, why is the JavaScript engine considering global value as undefined? I know that JavaScript is an interpreted language. How is it able to consider variables in the function?
Is that a pitfall from the JavaScript engine?
This phenomenon is known as: JavaScript Variable Hoisting.
At no point are you accessing the global variable in your function; you're only ever accessing the local value variable.
Your code is equivalent to the following:
var value = 10;
function test() {
var value;
console.log(value);
value = 20;
console.log(value);
}
test();
Still surprised you're getting undefined?
Explanation:
This is something that every JavaScript programmer bumps into sooner or later. Simply put, whatever variables you declare are always hoisted to the top of your local closure. So, even though you declared your variable after the first console.log call, it's still considered as if you had declared it before that.
However, only the declaration part is being hoisted; the assignment, on the other hand, is not.
So, when you first called console.log(value), you were referencing your locally declared variable, which has got nothing assigned to it yet; hence undefined.
Here's another example:
var test = 'start';
function end() {
test = 'end';
var test = 'local';
}
end();
alert(test);
What do you think this will alert? No, don't just read on, think about it. What's the value of test?
If you said anything other than start, you were wrong. The above code is equivalent to this:
var test = 'start';
function end() {
var test;
test = 'end';
test = 'local';
}
end();
alert(test);
so that the global variable is never affected.
As you can see, no matter where you put your variable declaration, it is always hoisted to the top of your local closure.
Side note:
This also applies to functions.
Consider this piece of code:
test("Won't work!");
test = function(text) { alert(text); }
which will give you a reference error:
Uncaught ReferenceError: test is not defined
This throws off a lot of developers, since this piece of code works fine:
test("Works!");
function test(text) { alert(text); }
The reason for this, as stated, is because the assignment part is not hoisted. So in the first example, when test("Won't work!") was run, the test variable has already been declared, but has yet to have the function assigned to it.
In the second example, we're not using variable assignment. Rather, we're using proper function declaration syntax, which does get the function completely hoisted.
Ben Cherry has written an excellent article on this, appropriately titled JavaScript Scoping and Hoisting.
Read it. It'll give you the whole picture in full detail.
I was somewhat disappointed that the problem here is explained, but no one proposed a solution. If you want to access a global variable in function scope without the function making an undefined local var first, reference the var as window.varName
Variables in JavaScript always have function-wide scope. Even if they were defined in the middle of the function, they are visible before. Similar phenomena may be observed with function hoisting.
That being said, the first console.log(value) sees the value variable (the inner one which shadows the outer value), but it has not yet been initialized. You can think of it as if all variable declarations were implicitly moved to the beginning of the function (not inner-most code block), while the definitions are left on the same place.
See also
Javascript function scoping and hoisting
Javascript variable declarations at the head of a function
There is a global variable value, but when control enters the test function, another value variable is declared, which shadows the global one. Since variable declarations (but not assignments) in JavaScript are hoisted to the top of scope in which they are declared:
//value == undefined (global)
var value = 10;
//value == 10 (global)
function test() {
//value == undefined (local)
var value = 20;
//value == 20 (local)
}
//value == 10 (global)
Note that the same is true of function declarations, which means you can call a function before it appears to be defined in your code:
test(); //Call the function before it appears in the source
function test() {
//Do stuff
}
It's also worth noting that when you combine the two into a function expression, the variable will be undefined until the assignment takes place, so you can't call the function until that happens:
var test = function() {
//Do stuff
};
test(); //Have to call the function after the assignment
The simplest way to keep access to outer variables (not just of global scope) is, of course, to try to not re-declare them under the same name in functions; just do not use var there. The use of proper descriptive naming rules is advised. With those, it will be hard to end up with variables named like value (this aspect is not necessarily related to the example in the question as this variable name might have been given for simplicity).
If the function might be reused elsewhere and hence there is no guarantee that the outer variable actually defined in that new context, Eval function can be used. It is slow in this operation so it is not recommended for performance-demanding functions:
if (typeof variable === "undefined")
{
eval("var variable = 'Some value';");
}
If the outer scope variable you want access to is defined in a named function, then it might be attached to the function itself in the first place and then accessed from anywhere in the code -- be it from deeply nested functions or event handlers outside of everything else. Notice that accessing properties is way slower and would require you to change the way you program, so it is not recommended unless it is really necessary: Variables as properties of functions (JSFiddle):
// (the wrapper-binder is only necessary for using variables-properties
// via "this"instead of the function's name)
var functionAsImplicitObjectBody = function()
{
function someNestedFunction()
{
var redefinableVariable = "redefinableVariable's value from someNestedFunction";
console.log('--> functionAsImplicitObjectBody.variableAsProperty: ', functionAsImplicitObjectBody.variableAsProperty);
console.log('--> redefinableVariable: ', redefinableVariable);
}
var redefinableVariable = "redefinableVariable's value from someFunctionBody";
console.log('this.variableAsProperty: ', this.variableAsProperty);
console.log('functionAsImplicitObjectBody.variableAsProperty: ', functionAsImplicitObjectBody.variableAsProperty);
console.log('redefinableVariable: ', redefinableVariable);
someNestedFunction();
},
functionAsImplicitObject = functionAsImplicitObjectBody.bind(functionAsImplicitObjectBody);
functionAsImplicitObjectBody.variableAsProperty = "variableAsProperty's value, set at time stamp: " + (new Date()).getTime();
functionAsImplicitObject();
// (spread-like operator "..." provides passing of any number of arguments to
// the target internal "func" function in as many steps as necessary)
var functionAsExplicitObject = function(...arguments)
{
var functionAsExplicitObjectBody = {
variableAsProperty: "variableAsProperty's value",
func: function(argument1, argument2)
{
function someNestedFunction()
{
console.log('--> functionAsExplicitObjectBody.variableAsProperty: ',
functionAsExplicitObjectBody.variableAsProperty);
}
console.log("argument1: ", argument1);
console.log("argument2: ", argument2);
console.log("this.variableAsProperty: ", this.variableAsProperty);
someNestedFunction();
}
};
return functionAsExplicitObjectBody.func(...arguments);
};
functionAsExplicitObject("argument1's value", "argument2's value");
I was running into the same problem even with global variables. My problem, I discovered, was global variable do Not persist between html files.
<script>
window.myVar = 'foo';
window.myVarTwo = 'bar';
</script>
<object type="text/html" data="/myDataSource.html"></object>
I tried to reference myVar and myVarTwo in the loaded HTML file, but received the undefined error.
Long story/day short, I discovered I could reference the variables using:
<!DOCTYPE html>
<html lang="en">
<!! other stuff here !!>
<script>
var myHTMLVar = this.parent.myVar
/* other stuff here */
</script>
</html>
I have the following code in one file:
function refreshGridSuccess(responseText, entity) {
oTable = $('#dataTable').dataTable({
"sScrollX": "100%",
In another file I have:
$('#detailData')
.on('click', '.sort-up', function (event) {
event.preventDefault();
var column = $(this).closest('th'),
columnIndex = column.parent().children().index(column.get(0));
oTable.fnSort([[columnIndex, 'asc']]);
return false;
})
I have no definition for oTable except here. The scripts seem to work so does that mean that oTable was somehow made into a global variable?
Now I am trying to start using Typescript and it will not accept Otable without it being declared. Is there a way I can declare oTable as an object or do I have to declare it as an object to be the same type as is returned by $('#dataTable').dataTable({}) ?
If you're in the global scope then there's no difference.
If you're in a function then "var" will create a local variable, "no var" will look up the scope chain until it finds the variable or hits the global scope (at which point it will create it).
Found here: What is the purpose of the var keyword and when to use it (or omit it)?
Yes, a variable not declared with var becomes global. When used outside of a function's scope it is not necessarily required, however, using a globally defined variable which was defined in a function results in unpredictable results.
"Failure to declare the variable in these cases will very likely lead to unexpected results. For that reason, in ECMAScript 5 strict mode, assigning a value an undeclared variable inside a function throws an error." - https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Statements/var
You are correct. Variable without a var declaration is treated as global. If you are in a browser, that scope is window
Also note that Javascript variables are function-scoped. Meaning if you have a code like this:
function test()
{
for (var key in someObject)
{
var foo = 'bar';
}
}
It is equivalent to this:
function test()
{
var foo, key;
for (key in someObject)
{
foo = 'bar';
}
}
It doesn't matter which block the var declaration is placed in, the actual declaration happens at the beginning of the function. This is also called variable hoisting if you want to google it.
Now about Typescript, I haven't had a chance to play with it. But I assume you can still do var oTable; right before you use the variable?
I have a 3rd party JavaScript application that declares several global functions and needs some variables to be defined for those functions to work. I would rather not define those variables in the global scope. Here's an example:
Suppose you have a globally defined function foo that prints out the value of an externally-defined variable named x.
function foo() {
console.log(x);
}
I think the only way for x to have a value within foo is if x is also a global. I hope I'm wrong. What I would really like to do is this:
(function () {
var x = 'someValue';
var bar = magicallyFixSoXIsntGlobal(foo);
bar();
}());
I guess I could do:
(function () {
var x = 'someValue';
var bar = Function(foo.toString());
bar();
}());
But that seems pretty much like eval. (If it comes down to that, I'd rather have global spam than use eval.)
Is there any way to "fix" the global functions so they can refer to the enclosed values?
Assign a value to a window's property, call the function, then use the delete operator to truly remove the variable.
window.foo = 'bar';
magic();
delete window.foo; // Because `foo` is defined without `var`, it can be deleted
The obvious solution is to modify foo to take x as a variable. But, assuming you can't / don't want to do that, I can think of two options:
declare x as a global variable.
Wrap foo's declaration in a closure.
The first will certainly make foo work, but please don't take Rob W's approach. Remember that the reason people tell you "don't clutter the global namespace" isn't because they're worried about unused variables hanging around (that can be a problem, but it's not specific to the global namespace). It's because having multiple functions using the same namespace increases the chances that one will overwrite the other's variables. Here's how you would use the global x without bludgeoning the rest of your code.
!function(x, exists) {
window.x=10;
foo();
if(exists)
window.x=x;
else
delete window.x;
}(window.x, 'x' in window);
The second option shouldn't be too hard if foo is currently just a function declaration within your code. In that case, change it to:
!function() {
var x=10;
function foo() {
[...]
}
// foo can only be called within this block
}();
Or if foo must be global, but you still don't want x to be global:
!function() {
var x=10;
window.foo=function() {
[...]
};
// now foo can be called anywhere, and will still use the x declared above
}();
But if foo is included via an external script then it gets more complicated, and I don't think you could use this method without modifying foo. (You could also use ajax and eval, or just eval, but I think you're right in avoiding that function.)
This is what function parameters are for
function foo(x) {
console.log(x)
}
You could hack it horrible if you wanted by converting the function to a string and creating a new function which accepts parameters, but that's dirty as hell
Today, I got completely surprised when I saw that a global variable has undefined value in a certain case.
Example:
var value = 10;
function test() {
//A
console.log(value);
var value = 20;
//B
console.log(value);
}
test();
Gives output as
undefined
20
Here, why is the JavaScript engine considering global value as undefined? I know that JavaScript is an interpreted language. How is it able to consider variables in the function?
Is that a pitfall from the JavaScript engine?
This phenomenon is known as: JavaScript Variable Hoisting.
At no point are you accessing the global variable in your function; you're only ever accessing the local value variable.
Your code is equivalent to the following:
var value = 10;
function test() {
var value;
console.log(value);
value = 20;
console.log(value);
}
test();
Still surprised you're getting undefined?
Explanation:
This is something that every JavaScript programmer bumps into sooner or later. Simply put, whatever variables you declare are always hoisted to the top of your local closure. So, even though you declared your variable after the first console.log call, it's still considered as if you had declared it before that.
However, only the declaration part is being hoisted; the assignment, on the other hand, is not.
So, when you first called console.log(value), you were referencing your locally declared variable, which has got nothing assigned to it yet; hence undefined.
Here's another example:
var test = 'start';
function end() {
test = 'end';
var test = 'local';
}
end();
alert(test);
What do you think this will alert? No, don't just read on, think about it. What's the value of test?
If you said anything other than start, you were wrong. The above code is equivalent to this:
var test = 'start';
function end() {
var test;
test = 'end';
test = 'local';
}
end();
alert(test);
so that the global variable is never affected.
As you can see, no matter where you put your variable declaration, it is always hoisted to the top of your local closure.
Side note:
This also applies to functions.
Consider this piece of code:
test("Won't work!");
test = function(text) { alert(text); }
which will give you a reference error:
Uncaught ReferenceError: test is not defined
This throws off a lot of developers, since this piece of code works fine:
test("Works!");
function test(text) { alert(text); }
The reason for this, as stated, is because the assignment part is not hoisted. So in the first example, when test("Won't work!") was run, the test variable has already been declared, but has yet to have the function assigned to it.
In the second example, we're not using variable assignment. Rather, we're using proper function declaration syntax, which does get the function completely hoisted.
Ben Cherry has written an excellent article on this, appropriately titled JavaScript Scoping and Hoisting.
Read it. It'll give you the whole picture in full detail.
I was somewhat disappointed that the problem here is explained, but no one proposed a solution. If you want to access a global variable in function scope without the function making an undefined local var first, reference the var as window.varName
Variables in JavaScript always have function-wide scope. Even if they were defined in the middle of the function, they are visible before. Similar phenomena may be observed with function hoisting.
That being said, the first console.log(value) sees the value variable (the inner one which shadows the outer value), but it has not yet been initialized. You can think of it as if all variable declarations were implicitly moved to the beginning of the function (not inner-most code block), while the definitions are left on the same place.
See also
Javascript function scoping and hoisting
Javascript variable declarations at the head of a function
There is a global variable value, but when control enters the test function, another value variable is declared, which shadows the global one. Since variable declarations (but not assignments) in JavaScript are hoisted to the top of scope in which they are declared:
//value == undefined (global)
var value = 10;
//value == 10 (global)
function test() {
//value == undefined (local)
var value = 20;
//value == 20 (local)
}
//value == 10 (global)
Note that the same is true of function declarations, which means you can call a function before it appears to be defined in your code:
test(); //Call the function before it appears in the source
function test() {
//Do stuff
}
It's also worth noting that when you combine the two into a function expression, the variable will be undefined until the assignment takes place, so you can't call the function until that happens:
var test = function() {
//Do stuff
};
test(); //Have to call the function after the assignment
The simplest way to keep access to outer variables (not just of global scope) is, of course, to try to not re-declare them under the same name in functions; just do not use var there. The use of proper descriptive naming rules is advised. With those, it will be hard to end up with variables named like value (this aspect is not necessarily related to the example in the question as this variable name might have been given for simplicity).
If the function might be reused elsewhere and hence there is no guarantee that the outer variable actually defined in that new context, Eval function can be used. It is slow in this operation so it is not recommended for performance-demanding functions:
if (typeof variable === "undefined")
{
eval("var variable = 'Some value';");
}
If the outer scope variable you want access to is defined in a named function, then it might be attached to the function itself in the first place and then accessed from anywhere in the code -- be it from deeply nested functions or event handlers outside of everything else. Notice that accessing properties is way slower and would require you to change the way you program, so it is not recommended unless it is really necessary: Variables as properties of functions (JSFiddle):
// (the wrapper-binder is only necessary for using variables-properties
// via "this"instead of the function's name)
var functionAsImplicitObjectBody = function()
{
function someNestedFunction()
{
var redefinableVariable = "redefinableVariable's value from someNestedFunction";
console.log('--> functionAsImplicitObjectBody.variableAsProperty: ', functionAsImplicitObjectBody.variableAsProperty);
console.log('--> redefinableVariable: ', redefinableVariable);
}
var redefinableVariable = "redefinableVariable's value from someFunctionBody";
console.log('this.variableAsProperty: ', this.variableAsProperty);
console.log('functionAsImplicitObjectBody.variableAsProperty: ', functionAsImplicitObjectBody.variableAsProperty);
console.log('redefinableVariable: ', redefinableVariable);
someNestedFunction();
},
functionAsImplicitObject = functionAsImplicitObjectBody.bind(functionAsImplicitObjectBody);
functionAsImplicitObjectBody.variableAsProperty = "variableAsProperty's value, set at time stamp: " + (new Date()).getTime();
functionAsImplicitObject();
// (spread-like operator "..." provides passing of any number of arguments to
// the target internal "func" function in as many steps as necessary)
var functionAsExplicitObject = function(...arguments)
{
var functionAsExplicitObjectBody = {
variableAsProperty: "variableAsProperty's value",
func: function(argument1, argument2)
{
function someNestedFunction()
{
console.log('--> functionAsExplicitObjectBody.variableAsProperty: ',
functionAsExplicitObjectBody.variableAsProperty);
}
console.log("argument1: ", argument1);
console.log("argument2: ", argument2);
console.log("this.variableAsProperty: ", this.variableAsProperty);
someNestedFunction();
}
};
return functionAsExplicitObjectBody.func(...arguments);
};
functionAsExplicitObject("argument1's value", "argument2's value");
I was running into the same problem even with global variables. My problem, I discovered, was global variable do Not persist between html files.
<script>
window.myVar = 'foo';
window.myVarTwo = 'bar';
</script>
<object type="text/html" data="/myDataSource.html"></object>
I tried to reference myVar and myVarTwo in the loaded HTML file, but received the undefined error.
Long story/day short, I discovered I could reference the variables using:
<!DOCTYPE html>
<html lang="en">
<!! other stuff here !!>
<script>
var myHTMLVar = this.parent.myVar
/* other stuff here */
</script>
</html>