javascript - ajayupreti/Tech.Blogs GitHub Wiki
Statements and Expressions
The MDN documentation defines JavaScript expressions as any valid unit of code that resolves to a value.
a = 1;
3 + 4;
The first expression has value 1 which is then assigned to variable a while the second has value 7 which is not assigned to anything.
Since expressions evaluate to values (numbers, strings, functions etc), they can be substituted anywhere these values are expected. Programmers just have to ensure expressions resolve to expected values because of JavaScript’s weak typing.
Consider the following simple statement:
var x = "statement";
This is a statement and not an expression. Statements do something while expressions evaluate to values.
Dr. Axel has an excellent post which differentiates both: “you can use expressions where statements are expected (these are called expression statements) however you cannot use a statement where an expression (i.e. something that evaluates to a value) is expected”.
To simplify; you cannot use the statement above as a condition for an if branch (which expects an expression or value).
Variable Scope
Scope determines the lifetime of variables and where they can be used. Most programming languages have block scope, i.e. variables only exist within enclosing blocks (usually delimited by braces). However, JavaScript has other ideas… Check the code snippet below.
function JSBlockScope() {
var noBlockScope = "BlockScope";
if(true) {
var noBlockScope = "FuncScope";
}
console.log(noBlockScope);
};
JSBlockScope();
//logs FuncScope
The output is “FuncScope”! Surprised? Block scope implies that declarations inside blocks do not overwrite external variables. JavaScript has function scope and consequently, noBlockScope gets overwritten to ‘FuncScope’.
Function scope also means that variables declared inside functions are only available inside those functions, once the function exits, the variables are inaccessible anymore (well, unless you use closures…).
function scopeCheck() {
var functionScope = "inside Func";
console.log(functionScope);
};
scopeCheck();
//logs inside Func
console.log(functionScope);
//ReferenceError: functionScope is
// not defined
The functionScope variable defined inside the scopeCheck function only exists inside the body of the function.
Variables are first looked up in the current scope (i.e. the enclosing function), if not found, then the interpreter continues walking up enclosing scopes until it gets to the global scope. If the variable is not defined still; then the well-known “ReferenceError: variable is not defined” error occurs.
Hoisting
JavaScript has this weird but fascinating concept called hoisting. All variable declarations are hoisted – every variable gets declared immediately even though their values might not be set immediately.
function hoisting() {
console.log(hoistedVar);
var hoistedVar = "hoisted!";
console.log(hoistedVar);
}
hoisting();
//logs undefined
//hoisted!
Explanation
Confused? Well, what happens is that all declarations are hoisted to the top before any code execution begins. The above code is totally similar to the one below:
function hoisting() {
var hoistedVar; //undefined
console.log(hoistedVar);
hoistedVar = "hoisted!";
console.log(hoistedVar);
}
hoisting();
//logs undefined
//logs hoisted!
The log statements show ‘undefined’ since this is the default value for uninitialized JavaScript variables. If they weren’t hoisted, the interpreter would have thrown a ReferenceError.
Hoisting also applies to functions (JavaScript functions are values after all…) however there is a subtle difference between function declarations and expressions. Function declarations get hoisted to the top and are immediately available within the scope while function expressions are only declared and not hoisted (much like the same case for variables above).
function isFunc(fn) {
var isFn = (typeof fn === 'function');
console.log(isFn);
}
function isUndef(fn) {
var ntDef = (typeof fn === 'undefined');
console.log(ntDef);
}
function funcHoisting() {
isUndef(declaredFn); //false
isUndef(hoistedFn); //true
function declaredFn () {};
var hoistedFn = function () {};
isFunc(declaredFn); //true
isFunc(hoistedFn); //true
}
funcHoisting();
Declaring variables at the top of functions helps to avoid hoisting surprises.