JavaScript holds some oddities that always make me breathless. Things you are used to, from practising other programming languages, lead to bugs in JavaScript.
This Blog contains a number of useful try-outs that are never too late to learn about.
As preparation you can also read my passed Blog about JS gotchas.In all of the text-areas in the following, you can click on "Run" to see the result of the example script.
If you want to alter the script, simply edit it and then press "Run" again.
If you want to know how these text-areas work, you can read my latest Blog.
In many cases the JavaScript visibility scope is the problem. Try to guess what this script yields:
Example 1:
What happens here? We define a variable alfa
with a value.
Then we define an object wolf
with two members, first is a field alfa
,
second is a function getAlfa
that returns the alfa
-value of the wolf.
But which alfa
is the one of the wolf?
What is intuitively expected here: that the alfa-wolf is "the one and only" :-)
In fact, using JS, the result is "the real one", the value of the variable alfa
.
Why is this? How can we make it the expected "the one and only"?
Example 2:
This yields what was expected: "the one and only".
To reference the alfa
field in the wolf
object,
we need to write either this.alfa
or wolf.alfa
.
JS has no implicit object scope. When you want to access the members of an object in a member function,
you need to either use the name of the object, or the keyword "this". But "this" is another story.
First lets try out whether "this" is safe against embeddings.
Example 3:
Seems to be stable. This yields "the follow-up" for wolfie
,
so wolfie
has its own alfa
value.
Now imagine that we, after construction of the object wolf
,
add a function getWolfieAlfa()
to wolf
that outputs
wolfie
's alfa
-value:
Example 4:
Now wolfie
's alfa
-value has become "the one and only".
This is the "this" story, and it tells us that this
is always the caller of the function.
And when you set functions to point to other functions, which is easy in JS, this
does not work
any more like expected. For such pitfalls you search really long. I would recommend to not use this
.
But this is not the only scope-oddity.
Look at following script and try to guess what sheepCount
will be:
Example 5:
I use a self-executing anonymous function as encapsulation mechanism to
write my sheepCount
assignment. Nevertheless the alert shows "999".
Why is the sheepCount
variable known outside my capsule?
Because I forgot to write
var sheepCount = 999;
to make it a local variable. The var
keyword makes the difference.
Change the script to be
var sheepCountNew = 999
with alert(sheepCountNew)
and try it out, the JS interpreter will then report that sheepCountNew
is not defined.
Mind that if you do not change the name of the variable, the result always will be
999, because sheepCount
already has become a global variable.
By forgetting the var
keyword it is really easy to create lots of global variables.
Thus we always should write
"use strict";
on top of any JavaScript. JS interpreters of modern browsers will consider this and
deny the usage of variables that were not defined using the var
keyword.
Mind that variables are not fields. Fields are members of an Object
defined within {}
.
So far about scope and visibility of variables and fields. Now let's try out a little bit what functions are.
Example 6:
This script outputs "foo() was called" three times.
Neither foo(bar1, bar2)
nor foo(bar1)
are called.
They do not even exist.
They both were overwritten by the last definition of foo()
,
which is the only survivor of this unintended competition for the function-name foo
.
This example shows that there is no
function overloading
in JavaScript.
If we had written it the following way, we would have seen the pitfall immediately:
var foo = function(bar1, bar2) { alert("foo(bar1, bar2) received "+bar1+", "+bar2); }; var foo = function(bar1) { alert("foo(bar1) received "+bar1); }; var foo = function() { alert("foo() was called"); };
Because of that reason I recommend to define functions as var
.
Actually this is what the JS interpreter does when it finds function definitions
like in the source code above. In a strict way they are syntactically wrong.
But we are not done. JS is bursting at all seams. Have a look at "variable hoisting":
Example 7:
Here we define a function that outputs the value of a variable that seems to not yet exist,
then we declare a { block } containing the variable number
with value 1,
and after that block we again output the value of that very variable.
Anybody would expect the output to be undefined
both times.
But it isn't. JS "hoists" any variable definition to the top of the function body.
This is the reason why the script is not crashing due to an undefined variable usage
at the first alert
statement.
Down in the block the variable then gets its value, and, as it was actually "hoisted" to the function top,
it survives the block termination and the second alert
shows us that it
holds the value 1.
As last example lets look at a very common pattern to define default values,
to be seen in many scripts. I am talking about the ||
logical operator.
This is used to assign a value to a parameter when the caller of the function
did not pass a parameter, or passed undefined or null. The value at right side
of the ||
operator would be assigned to the variable only when
the left side is false, undefined or null. Assuming the parameter is called
ignoreCase
, it reads like
letignoreCase
beignoreCase
or true.
Example 8:
A function foo()
expects a boolean parameter and assigns a default value of
true
to it when it was not provided by the caller.
foo
calls 1 - 3 do as expected, the valuetrue
is reported- call 4 leaves the parameter at its own value, because it is
true
, and thus the right side of||
is not reached - call 5 then shows another embarrassing JS pitfall: the parameter is turned
to
true
although it was given asfalse
!
You see the problem? Instead of assigning a default we forced the parameter to be always true.
So the default mechanism with ||
can be used only
for objects and strings, not for booleans and numbers. Evaluating a number
would yield false for 0, and true for any other value, similar problem as with booleans.
You can dive into this on
stackoverflow
if you want.
So every JS programmer clearly has in mind that for boolean and number parameters (s)he must write
if (ignoreCase === undefined) ignoreCase = true;
Do you believe they all know?
Do you know that we are about to program user-interfaces in JavaScript now on a large scale?
Do you know that most efforts in software development go into user-interfaces,
and that software always has been much too expensive for normal people?
Keine Kommentare:
Kommentar veröffentlichen