if (foo !== null) evaluates to true, even when foo is null - javascript

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.

Related

Why is variable declaration not allowed as a parameter, but function declaration is?

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'

Using same argument name as its default parameter in ES6

This ES6 code:
const log = () => console.log('hi');
const parent = (log = log) => log();
parent();
Transpiled to:
var log = function log() {
return console.log('hi');
};
var parent = function parent() {
var log = arguments.length <= 0 || arguments[0] === undefined ? log : arguments[0];
return log();
};
parent();
Gives error:
return log();
^
TypeError: log is not a function
The problem is this line:
const parent = (log = log) => log();
Because the argument name is same as its default parameter.
This works:
const log = () => console.log('hi');
const parent = (logNow = log) => logNow();
parent();
Is this a bug in Babel or is this not allowed in the spec itself?
Seems like this is the expected behavior of ES6.
Tested on the Chrome console, also got an error.
The ES6 spec is saying to that point:
Let parameterNames be the BoundNames of formals.
http://www.ecma-international.org/ecma-262/6.0/#sec-functiondeclarationinstantiation
This means when you create function, ES6 will do basically the same like babel is doing, it will manage the assignment of the params in the new context.
In javascript, when you create a variable a in a closed scope, global a, cannot be accessed anymore, because JS will take the a from the nearest possible scope, in AST.
Simple example:
var a = 1;
function test() {
// creates the variable a, and try to assign a value to it,
// because `a` is now available in the scope itself, it will take value of a in the inner scope, not the outer scope one
var a = a;
console.log(a)
}
test() // undefined
Why its not taking the value of outer a, and then assign it to the inner a, is because of hoisting, basically its doing this:
function test() {
var a; // the address for the variable is reserved at compile time
a = a; // run-time assignment
}
It takes all the variables declarations of a function and hoist it to the begin of the function.
This is the reason, why something like this will work:
function hoistingTest(n, m = 2) {
// return immediately
return multi(n);
// This declaration will be hoisted to the begin of the body
function multi(n) { return m * n }
}

Eval and function scope

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.

Is IF a function in JavaScript?

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>

Can I redefine a JavaScript function from within another function?

I want to pass a function reference "go" into another function "redefineFunction", and redefine "go" inside of "redefineFunction". According to Johnathan Snook, functions are passed by reference, so I don't understand why go() does not get redefined when I pass it into redefineFunction(). Is there something that I am missing?
// redefineFunction() will take a function reference and
// reassign it to a new function
function redefineFunction(fn) {
fn = function(x) { return x * 3; };
}
// initial version of go()
function go(x) {
return x;
}
go(5); // returns 5
// redefine go()
go = function(x) {
return x * 2;
}
go(5); // returns 10
// redefine go() using redefineFunction()
redefineFunction(go);
go(5); // still returns 10, I want it to return 15
​
Or see my fiddle http://jsfiddle.net/q9Kft/
Pedants will tell you that JavaScript is pure pass-by-value, but I think that only clouds the issue since the value passed (when passing objects) is a reference to that object. Thus, when you modify an object's properties, you're modifying the original object, but if you replace the variable altogether, you're essentially giving up your reference to the original object.
If you're trying to redefine a function in the global scope: (which is a bad thing; you generally shouldn't have global functions)
function redefineFunction(fn) {
window[fn] = function() { ... };
}
redefineFunction('go');
Otherwise, you'll have to return the new function and assign on the calling side.
function makeNewFunction() {
return function() { ... };
}
go = makeNewFunction();
Nothing is "passed by reference" in JS. There are times that references are passed, but they're passed by value. The difference is subtle, but important -- for one thing, it means that while you can manipulate a referenced object pretty much at will, you can't reliably replace the object (read: alter the reference itself) in any way the caller will see (because the reference was passed by value, and is thus merely a copy of the original reference; attempting to reassign it breaks in the same way it would if the arg were a number or string).
Some cowboys will assume you're redefining a global function and mess with it by name to get around the limitations of pass-by-value, but that will cause issues the second you decide not to have globals all over the place.
The real solution: return the new function, and let the caller decide what to do with it. (I'd argue that redefining functions right out from under the code that uses them is a pretty bad design decision anyway, but eh. I guess there could be a reason for it...)
Snook is wrong. And I don't think it's pedantic at all (#josh3736 :) to point out that EVERYTHING in JavaScript is pass by value. The article by Snook gets this COMPLETELY wrong. Passing a primitive and passing an object work the exact same way. These are equivalent:
var x = 2;
var y = x;
y = 3; //x is STILL 2.
function stuff(y){
y = 3; //guess what. x is STILL 2
}
stuff(x);
///////////////////
var x = {stuff: 2};
var y = x;
y = {stuff: 3}; //x.stuff is STILL 2
function stuff(y){
y = {stuff: 3}; //guess what. x.stuff is STILL 2
}
stuff(x);
This is important. Java, C#, and MOST languages work this way. That's why C# has a "ref" keyword for when you really do want to pass something by reference.
You can't modify the variable from inside the function, so the quick fix is to return the value and assign it outside the function, like this
// log() just writes a message to the text area
function log(message) {
$('#output').val($('#output').val() + message + "\n");
}
// redefineFunction() will take a function reference and
// reassign it to a new function
function redefineFunction() {
newvalue = function(x) { return x * 3; };
return newvalue;
}
// initial version of go()
function go(x) {
return x;
}
log(go(5)); // returns 5
// redefine go()
go = function(x) {
return x * 2;
}
log(go(5)); // returns 10
// redefine go() using redefineFunction()
go = redefineFunction();
log(go(5)); // returns 10, I want it to return 15
I believe functions are 'passed in by value'. If you put log(f(5)); inside your redefineFunction function it will output 15, but 10 when you call log(go(5)) afterwards.
If you change redefineFunction to return the function and then assign it to go (go = redefineFunction()) it will work as you expect.
This is equivalent to asking if you can redefine any variable by passing it as an argument to some function. No. You can reassign it by, uhh, reassigning it. In this case, if you make redefineFunction return a function, you can simply assign it to go:
function redefineFunction() {
var fn = function(x) { return x * e; };
return fn;
}
function go(x) {
return x;
}
go = redefineFunction();
go(5); // return 15
This is working in firefox:
function redefineFunction(fn) {
window[fn] = function(x) {
return x * 3;
}
};
function go(x) {
return x;
};
alert(go(5));
go=function(x) {
return x * 2;
}
alert(go(5));
redefineFunction('go');
alert(go(5));
The secret is that a global function called go also is called window.go and window["go"].
This can also be used at styles: element.style["overflow"] = "hidden", and in attributes:
element["value"] = "hello there".
This is a very useful knowlege.
Why dont use a object? something like this:
var o = {
go: function( x ) {
return x;
},
redefineFunction: function ( fn ) {
if (typeof fn === 'function') {
this.go = fn;
}
}
}
console.log(o.go(5)); // return 5
var fn = function (x) {
return x * 2;
};
o.redefineFunction(fn);
console.log(o.go(5));​ //return 10
Hope it helps!

Categories