I've been exploring scope within JavaScript. Sources point out that scope is function-delimited, not block-delimited as in most languages.
I put some display code in some code I'm writing, because some of my functions-within-functions are not clear (to me), and I wanted to see just how the scope works for these.
The big surprise is that in the $.each function within the $.getJSON function the if(){} clause is, evidently, treated as a function. I would have assumed it to be a block.
function displayInfo(nKey) {
if(!nKey) var nKey = 0;
var objFilm = {};
var imgRef;
//iterate through all object properties; display their attributes
// Object.keys() returns an array of all property names
// for most entries, the object is ...film; check first for array of multiple films
jqxhr = $.getJSON('dbMovies.json', function(data) {
var xx = "xx";
$.each(data.disc, function(i, xdata) {
if(xdata.key == nKey) {
objFilm = xdata.film;
var yy = "yy";
imgRef = xdata.img;
return false;
}
console.log("in jqxhr, xx: " + typeof xx); //this shows
console.log("in jqxhr, yy: " + typeof yy); //this does NOT
}); // $.each
})
.done(function() {...}
If if(){} is a function, what is a block?
You have a return false inside the if, therefore the only time those two log statements will be reached is if the if condition wasn't true and the block controlled by the if didn't run. And if the block didn't run, then yy wasn't assigned a value. It's in scope, but uninitialized.
The variable the function is assigned to is limited only by its scope, which is guaranteed to include the scope where the function is declared in.
if() { } is not a function... it is a block. the reason it is not showing up in your example is because you have return false which breaks before the log happens, and when the logs do happen then the variable is still undefined.
(function () {
if (true) var x = "if declared variable"; /* block declaration */
document.getElementById('if').innerHTML = x ? x : 'undefined';
(function() {
var y = "function declared variable";
})();
if (typeof y != 'undefined')
document.getElementById('func').innerHTML = y
else
document.getElementById('func').innerHTML = 'undefined';
})();
if: <span id="if"></span>
<br>
func: <span id="func"></span>
Related
It might be a dumb question. I googled it but can't find the answer. Variable declaration is not allowed as parameter of a function as below.
function t(a) {
alert(a);
}
t(var x = 1); // Uncaught SyntaxError: Unexpected token var
t(let x = 1); // Uncaught SyntaxError: missing ) after argument list
t(x = 1); // working fine and later I am able to access x also
console.log(x); // printing 1
But function declaration is being allowed as a parameter of a function as below.
function callback(str, f1, f2) {
if(str == "")
f1();
else
f2();
};
callback("", function b1() { alert("empty") }, function b2() { alert("not empty") }); // working fine
b1(); // Throwing error Uncaught ReferenceError: b1 is not defined
Can anyone please help me to understand
Why variable declaration is not allowed as a parameter of a function
but function declaration is allowed as parameter?
Why we can't access the function which is declared as a function
parameter outside of the function call?
Good question! I'll divide this into parts. There's going to be a lot of material to google here, since your question touches multiple deep subjects.
1. Statements have no value
Declarations are statements. They have no value, and thus they cannot be parameters. That this code...
let a = 1
... has no value, means none of these will work:
doStuff(let a = 1)
let b = (let a = 1)
(let a = 1) + 5
The name a, or a + 5, or f(a) are expressions, and unlike statements, expressions have value. But the declaration of a itself does not.
Note that your intuition about this was not absurd: in other languages, let a = 1 is an expression that evaluates to 1. Not in Javascript.
2. Functions are objects
However, the function keyword does have value: the Function object it defines. Unlike variables, which are language constructs for your convenience, Functions are actual objects that exist in the running program. We say that functions are first-class objects.
You can do all of these:
doStuff(function f() {})
let a = function f() {}
let b = (function f() {}) + 5 // the result of this is funny
Back to your examples, then:
callback(
"", // a String object
function b1() { alert("empty") }, // a Function object
function b2() { alert("not empty") } // a Function object
);
Is similar to this:
function b1() { alert("empty") }
function b2() { alert("not empty") }
callback("", b1, b2)
But not quite. Let's talk about scopes.
3. Names are defined within a scope
The scope of a name, such as a variable or function, is the section(s) of code that have that definition available.
For example:
// Top-level scope:
let a = 1
if (a == 1) {
// Inner block scope:
let b = 2
console.log(a, b) // 1, 2
}
console.log(a, b) // 1, undefined
Scopes live inside larger scopes. Inner scopes can access surrounding scopes, (so a and b are visible inside the block) but not the other way round (so b is not visible outside).
When you created your function objects inside the call...
f(function a() { })
... they were trapped inside an inner scope, and cannot be referenced from outside.
4. Assignments are expressions
In your sample code, you noted that declaring a like this worked:
f(a = 5)
This is... unfortunate. A product of Javascript's history, really. In modern code, you should always use let or const to define variables.
So why does it work? Two reasons. First, because it's an assignment, not a declaration. Like so:
let x = 1
f(x = 2)
Assignments are expressions. They evaluate to the assigned value. The value of x = 2 is 2, and x changes as a side-effect.
5. There is a global scope
The second reason is the unfortunate one. When you avoid the let, var or const keywords, you're implicitly using the global scope.
This is the mother of all scopes, and names that live there are accessible from any point in the code. So, if you just do this...
f(a = 5)
... without having declared a anywhere in the current scope, it's implicitly declared in the global scope, and the assignment takes place. Think of it as this (pseudo-code):
global let a
f(a = 5)
That is, of course, not valid Javascript. But you get the point.
The shortest way I can think of explaining the difference is if we convert the named function expression argument (the context where it is called is very important) in your example:
callback("", function b1() { console.log("empty"); }, ...)
to a variable declaration argument:
callback("", var b1 = function() { console.log("empty"); }, ...)
This gives an error:
function callback(str, f1, f2) {
if(str == "")
f1();
else
f2();
};
callback(
"",
var b1 = function () { console.log("empty") },
var b2 = function () { console.log("not empty") }
); // Error: expected expression, got keyword 'var'
This works fine:
function callback(str, f1, f2) {
if(str == "")
f1();
else
f2();
};
callback(
"",
function b1() { console.log("empty") },
function b2() { console.log("not empty") }
); // logs 'empty'
Function declarations vs function expressions
They're almost the same, the MDN reference hierarchy is quite informative for the difference:
Operators/function (expression):
function [name]([param1[, param2[, ..., paramN]]]) {
[statements]
}
Statements/function (definition):
function name([param1[, param2,[..., paramN]]]) {
[statements]
}
See how an expression is listed under 'operators', whereas declaration is listed under 'statements'.
Note: I made slight adjustments to the MDN syntax to show how similar they are - they both have numbers after their parameters and for both declaration and expression the statements inside are optional.
What is important is where you call them:
function b1() {console.log('empty');} // declaration
callback(
""
function b1() {console.log('empty');} // expression
)
Similarly a ternary (Operators/Conditional_Operator) str == "" ? f1 : f2 is an operator/expression where as if (Statements/if...else) if (str == "") { return f1 } else { return f2 } is a statement/declaration.
function callCallback(cb) {
cb()
}
function callback(str, f1, f2) {
callCallback(str == "" ? f1 : f2)
}
callback(
"",
function b1() { console.log("empty") },
function b2() { console.log("not empty") }
); // logs 'empty'
function callCallback(cb) {
cb()
}
function callback(str, f1, f2) {
callCallback(if (str == "") { return f1 } else { return f2 })
}
callback(
"",
function b1() { console.log("empty") },
function b2() { console.log("not empty") }
); // Error: expected expression, got keyword 'if'
Something seems wrong with the following code.
It declares a function with eval, calls it — until then, everything is fine — and, calls it again but, though a function and thus gets an error.
var fn = function() {
return isTwo(2);
};
var check = function() {
eval("var isTwo = function(value) { return value == 2 }")
console.log(isTwo(2)); // returns true
console.log(fn()); // returns isTwo is not defined
}
check();
Unwrapping the check function made things works, but seems wrong. Why using eval inside of a function should change its behavior?
var fn = function() {
return isTwo(2);
};
eval("var isTwo = function(value) { return value == 2 }")
console.log(isTwo(2)); // returns true
console.log(fn()); // returns true
Because eval acts as if you had replaced the line with the code to be evaluated. Therefore, var isTwo = function(value) { return value == 2 } defines a local variable, and it can't be accessed by your other function. The reason it works in the outer block is because it is then a global variable, and can be accessed by your other function.
I want to know how the function has been initialized, with the expression or declaried as fuction. _ Amazon interview question
expression : var a = function (){ }
declaration: function a (){ }
You could just do a.toString() and parse out the name. Or do the same with regular expressions
a.toString().test(/^\s*function\s*\(/);
function a(){ }; // gives false
var a = function (){ }; // gives true
Of course as Grundy pointed out this fails with named functions. Something like
var a = function b() {};
or
function b() {};
var a = b;
And ES6 has .name (see the Browser table at the bottom for the current state of affairs) - https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Function/name
I don't think it's possible to do so. The only difference between:
var func = function(){ };
and:
function func() { };
Is that the first one gets assigned on runtime. The way I see it, is that both function statements return a reference to their respective function objects. In that sense they are both the same. The only thing you could argue is that one is not named and the other one is, but you could have assigned a named function to a variable too.
However, there seems to be a difference on how they get assigned. The second one seems to get assigned to a variable that its named after, right at the start of the execution context. The first one has to wait for the explicit assignment within the execution context.
So you'd be testing for when they get assigned. You might think that's is possible to do so within the global object like:
//some protected vars that can't work without same-origin
var protected = ['caches', 'localStorage', 'sessionStorage', 'frameElement'];
var definedAtInit = [];
for(prop in window){
if(!isSandboxed(prop) && typeof window[prop] === 'function'){
definedAtInit.push(prop);
}
};
function isSandboxed(prop){
return protected.indexOf(prop) !== -1;
}
function isItDefinedAtInit(funcName){
return definedAtInit.indexOf(funcName) !== -1;
}
var func = function() {
console.log('test');
}
var results = { isItDefinedAtInit : isItDefinedAtInit('isItDefinedAtInit'),
func : isItDefinedAtInit('func')
};
document.getElementById('results').innerHTML = JSON.stringify(results, '/t');
<pre id="results"></pre>
However, you could still do something like:
var isItDefinedAtInit = function() { };
//After this, isItDefinedAtInit('isItDefinedAtInit') would be wrong.
And you still have the problems with other execution contexts, I don't think functions declared within a function execution context get attached to any object.
I think these kind of checks are a bad idea to be honest.
There is only way, we can determine function has defined with function declarations not as expression.
as Grundy mentioned name property of the respective function gives require information, if it has been defined with expression name property holds undefined value, else it holds function name.
Here is the code :
var isDefinedAsFunction = function(fn){
return fn.name !== undefined
}
In javascript I have an option for log output that allows an element/selector to be used to specify where to output log/error messages. The output is formatted upon initialization like so:
var $messageOutput = options.messageOutputElement ? $(options.messageOutputElement) : null;
and it is used later on through a log function:
function outputMessage(msg)
{
if ($messageOutput !== null)
{
messageNum++;
var $messageOutput = $(options.messageOutputElement);
var html = $messageOutput.html();
html += '<b>' + messageNum + '</b><br/>' + msg + '<br/>';
$messageOutput.html(html);
$messageOutput.scrollTop($messageOutput[0].scrollHeight);
}
}
The problem is that even when $messageOutput === null, the if statement inside outputMessage() falls through. I've verified with Chome's debugger that $messageOutput indeed equals null. And indeed it steps into the if statement anyway.
Can anyone give me any insight as to why this might be happening? Thanks in advance for any input on this matter.
By declaring "var $messageOutput" in the scope of a function, you stop referencing the variable in the global scope. So, instead of referring to that, it refers to a variable that is yet to be set within the function body, which is undefined, not null. You should probably remove the "var" part of "var $messageOutput = $(options.messageOutputElement);", which will instead make the function refer to the $messageOutput outside the function.
To elaborate a bit:
"var" creates a new variable within the scope you are in, and to add to the confusion something like "var foo = function" behaves differently than "function foo". Here are a few fun cases & explanations for your reading pleasure:
var x = 5;
function f () {
console.log(x):
if (!x) {
var x = 22;
}
return x;
}
function g () {
console.log(x):
if (!x) {
x = 22;
}
return x;
}
f(); // prints nothing, and returns 22
g(); // prints 5, and returns the same value
What you also sometimes have to watch out for is that you can reference a function before declaration outside of the global scope, depending on how it's declared. For example:
function f () {
console.log(f_sub);
function f_sub () {
return "subfunction of f";
}
return f_sub;
}
function g() {
console.log(g_sub);
var g_sub = function () {
return "subfunction of g";
}
return g_sub;
}
f(); // prints and returns f_sub
g(); // prints undefined and returns g_sub
A conventional function declaration can be made anywhere and referenced anywhere, but assigning a function to a variable means it can't be referenced until the line has been executed.
I want to check if a function is defined (I don't care how, I mean it is callable)
sample code:
var functions = {
'alert':'alert',
'undefinedFunction':'undefinedFunction',
'ff.showAlert':'ff.showAlert'
};
var ff = {
showAlert: function() {
alert('the function works');
}
};
for (i in functions) {
console.log(i+' : '+typeof window[functions [i]]);
}
this returns:
alert : function
undefinedFunction : undefined
ff.showAlert : undefined
console.log(typeof window.ff.showAlert); return function
Live demo
Is there a way to programmatically check if a function exists?
The code:
window[functions [i]]
Is checking for window['ff.showAlert'] but what you really want to check for is:
window['ff']['showAlert']
or
window.ff.showAlert
For this, you need to traverse the namespace (window->ff->...):
function methodIsDefined(fn, obj) {
var ns = fn.split('.');
fn = ns.pop();
do {
if (!ns[0]) {
return typeof obj[fn] === 'function';
}
} while(obj = obj[ns.shift()]);
return false;
}
E.g.
methodIsDefined('ff.showAlert', window); // => true
methodIsDefined('ff.foo', window); // => false
Your problem lies within the namespacing. The string "ff.showAlert" does not reference to the function window['ff']['showAlert'], but to window['ff.showAlert'], which is an important difference. The function you declare is actually referenced by window['ff']['showAlert']. So the place, that you are checking for its existance, is wrong.
If you want to check for the existance of such namespaced functions, you first have to split the string and then walk through the DOM to find the correct property. The code would be something like the following (not tested!):
function checkFunction( name ) {
var path = "ff.showAlert".split( '.' ),
runner = window;
for( var i=0; i<path.length; i++ ) {
if( path[i] in runner ) {
runner = runner[ path[i] ];
} else {
return false;
}
}
return runner;
}
edit
As pointed out by #VirtualBlackFox in the comments: The above solution only works if the function is declared below the window-scope. If it's inside another function's scope, you would have to pass that scope as an additional parameter and search there.
Even in that case you can't check for the existance of functions that are, e.g., defined inside some closure constructs.
You need to split multi-part function names like 'ff.showAlert'. Also, because you specified ff as a var, it won't be a member of window unless it's outside of any function scope. It's not really clear whether it is or not from your code example.
Anyway, the function below allows you to pass in a base object, in case you need to specify one other than window, and it splits multi-part function names:
function isFnDefined(fnName, baseObj) {
try {
var parts = fnName.split('.'),
ii;
// If no baseObj was provided, default to 'window'.
baseObj = baseObj || window;
for (ii in parts) {
baseObj = base[parts[ii]];
}
return typeof baseObj === 'function';
}
catch (e) {
return false;
}
}
isFnDefined('alert'); // returns true
isFnDefined('undefinedFunc'); // returns false
isFnDefined('ff.showAlert'); // returns true, if ff is a member of window
isFnDefined('showAlert', ff); // returns true
you need to use eval(): http://www.w3schools.com/jsref/jsref_eval.asp
You have a string that represent something in the current scope and you want to know what it is, the solution is to eval the string.
var f = undefined;
try
{
f = eval(functions[i]);
}
catch(ReferenceError) {}
console.log(typeof f);
But why are you storing strings instead of function themselves in your input object ?
Also if you could force every string to be a reference relative to window (No 'var' in the current scope or something coming from the closure of another scope) then #Sirko solution might be the best one.