Quinthar

Why Javascript Creeps Me Out

With all that asynchronicity, it behaves in... unnatural ways. Consider the following:

var message = "Hello";
var blah0 = function() { alert( message ); }

message = "World";
var blah1 = function() { alert( message ); }

blah0();
blah1();

In any reasonable language, it would first alert "Hello", and then alert "World". But noooo. Javascript's gotta get all fancy and defer execution until later. And at that point message is equal to "World" for both. So it's "World" "World", which ain't right at all.

I still don't know how to do the above, but thankfully Tyler turned me on to a new path with "this":

var div0 = document.getElementById("div0");
var values = [ "Hello", "World" ];
for( var c in values )
{
var input = document.createElement("input");
input.type = "checkbox";
input.value = values[c];
input.onclick = function() { alert( this.value ); }
div0.appendChild( input );
div0.appendChild( document.createTextNode( values[c] ) );
div0.appendChild( document.createElement( "br" ) );
}

Frickin' Javascript...

29 comments:

Tyler Karaszewski said...

Here's the C equivalent of your Javascript:

#include <stdio.h>
int main(){
char * message = "Hello";
void blah0(){ printf( message ); }
message = "World";
void blah1(){ printf( message ); }
blah0();
blah1();
}

Which will compile in gcc with the "-fnested-functions" flag and run just fine. In javascript, like in C, the content of "message" in blah(0|1) is checked when the function is run, not at compile time.

quinthar said...

Whoa. I had no idea C could even do that. Wow.

Tyler Karaszewski said...

I'm pretty sure that it doesn't have any effect except on the scope from which blah0() and blah1() are accessible. Regardless, it has the exact same behavior as the javascript example you posted.

Declaring new functions in Javascript doesn't save any state about the rest of the program at the time of their declaration.

If you want to specifically invoke the compiler you can use eval():
var x = 10;
var f;
eval( "f = function(){ alert("+x+") }" );
x = 20;
f();

Or do something like this (which is basically the same thing):
var x = 10;
var f = new Function( "alert(" + x + ")" );
x = 20;
f();

where the new function that gets compiled is based on the stringification of the current value of x, rather than the x variable that's currently in scope.

Or you could attach extra data to your function and do this:
var x = 10;
var f = function(){ alert( arguments.callee.copyOfX ) };
f.copyOfX = x; // do deep copy on reference types.
x = 20;
f();

So that when the function is called, it looks itself up ( its local "argumetns.callee" variable), and grabs its "copyOfX" property.

So, whichever way you want to do it, you have to explicitly save the state of any variable you want to come back to later -- When you create a new function in javascript, the compiler will give it references to all the variables that are currently in scope, but it doesn't make copies of them.

var a = function(){
var b = 1;
var c = function(){
var d = 2;
var e = function(){
alert( "b: " + b + ", d: " + d + ", typeof(c): " + typeof(c) );
}
return e;
}
return c();
}
a()(); // <-- haha.

quinthar said...

This is super helpful, thank you. I remember playing with "eval()" but it never occurred to me to wrap the whole *function declaration* in the eval: I was making the function call val(), but that obviously didn't work. Similarly, I tried "new Function()", but I put the variable inside the string rather than stringifying it as you do. I just couldn't quite get over the mental hurdle; thanks for spelling it out!

Andrea Giammarchi said...

you have two choice:

1 - call the alert before you change the external message variable value

2 - understand JavaScript closures, and obtain what you are looking for simply doing something like that:


function createAlert(message){
return function(){alert( message )};
};

var message = "Hello";
var blah0 = createAlert( message );

message = "World";
var blah1 = createAlert( message );

blah0();
blah1();

Daniel said...

You and the first commenter are idiots.

To you: That's how all languages essentially behave unless it's a shitty language.

The first commenter (Tyler):
No, dick; you can't declare a function within another function. That's not the fucking equivalent.

The equivalent of your shitty-ass code would be:

#include < stdio.h >

char* message;

void blah0()
{
printf(message);
}

void blah1()
{
printf(message);
}

int main()
{
message = "Hello";
message = "World";
blah0();
blah1();
}

______

Though that's shitty code; it's better to have global variables as constants.

quinthar said...

Listen douchebag, it's bad form to insult the owner of the blog in his comments, especially because he can simply ignore your future comments and thereby always get the last laugh. On top of that, read the fucking post before attacking it -- did you miss the "-fnested-functions" flag? Way to look like an idiot, moron.

Phatlip12(administrator) said...

You guys realize you're arguing over a programming language right?

quinthar said...

Heh, good point.

JFSIII said...

I think this is the what you're after:

var message = "Hello";
var blah0 = function(msg) { alert( msg ); }(message);

message = "World";
var blah1 = function(msg) { alert( msg ); }(message);

blah0();
blah1();

Chase Saunders said...

You need to use a closure dude...

Collin said...

alert("Hello");
alert("World");

But... yeah. This is how Javascript works. It shouldn't be creepy.

Look up closures and first-class functions.

Instead of getting "creeped out" by a language it serves one better to investigate and learn before complaining about new concepts.

Ryan said...

You mention "asynchronicity" and "deferred execution", but this is neither - it's simply a closure.

http://www.javascriptkit.com/javatutors/closures.shtml

n_gage said...

Same thing with Ruby, or any language I can think of...

http://www.grabup.com/uploads/2da287af1c9fa0df7de40ba428e9a3fb.png

Ed said...

I'm not sure in what "reasonable language" it would print "Hello" and then "world."

The functions blah0() and blah1() both print the value of the same variable. How could they *not* print the same thing?

The functions aren't "Print the value this variable had at some time in the past, when the function was defined" functions. That would be complicated (though jfsiii shows a comparatively simple way to do it -- go Javascript!).

They're simple and straightforward -- "print the value of this variable" and they do the simple and straightforward thing -- print the value of the variable...

perl:

my $message = "Hello";
sub blah0 { print "$message\n"}
$message = "World";
sub blah1 { print "$message\n"}
blah0(); blah1();

python:

message = "Hello"
def blah0(): print message
message = "World"
def blah1(): print message
blah0()
blah1()

ruby:

message = "Hello"
def blah0; puts message; end
message = "World"
def blah1; puts message; end
blah0; blah1

All of these give you "world" twice.

I can't think of any commonly used language that *doesn't* do it that way.

Liquid said...

Wow... I think I'll quote Billy here:

what you've just said is one of the most insanely idiotic things I have ever heard. At no point in your rambling, incoherent response were you even close to anything that could be considered a rational thought. Everyone in this room is now dumber for having listened to it. I award you no points, and may God have mercy on your soul.

Jesse said...

Yes, it's called lexical scoping.

Andrey Fedorov said...

It's called dynamic scope... not that big of a deal.

jfsiii: no, that code executes the functions right away and leaves blah0 and blah1 as undefined, causing errors when you try to execute them.

What you're looking for is a different language. Or, alternatively, a closure:

var message = "Hello ";
var foo = function (msg) { return function () { alert(msg); }; }(message);

message = "World!"
var bar = function (msg) { return function () { alert(msg); }; }(message);

foo();
bar();

For some really fun scoping hickups, check out: http://calculist.blogspot.com/2005/12/dynamic-scope.html

Jessta said...

why aren't you parsing the message to the function?
That is a terrible use of global variables. No sane programmer would do that.

Bas said...

I think the behavior of Javascript is completely reasonable. The function isn't a closure over the the value of message, just over a reference to message.

What I don't like is what it takes to capture the value in a closure. For example, in the following code, why do I have to declare and call an extra function? Why can't I simply declare a block?

var message = "Hello";
(function(){
var m = message;
blah0 = function() { print(m); }
})()

message = "World";
(function(){
var m = message;
blah1 = function() { print(m); }
})()

blah0();
blah1();

In Lua, code similar to the original example behaves like Javascript, but the code for example above is simpler:

local message = "Hello"
do
local m = message
blah0 = function() print(m) end
end

message = "World"
do
local m = message
blah1 = function() print(m) end
end

blah0()
blah1()

The do...end is a block which captures the value of message.

Benjamin Higgins said...

what javascript is doing here makes perfect sense, because the alternative is even more unreasonable. imagine if it worked the way you suggest. then what would happen here:

var message = "hello";

function get_message() {
return message;
}

message = "world";

var blah = get_message();

should blah now hold "hello" or "world"? To me, it should hold "world".

Now, what if we change the code to this:

var message = "hello";
var get_message = function() { return message; }

message = "world";

var blah = get_message();

blah should still hold "world", not "hello"

...point is that I'm trying to make it look more like your example. I get what you're trying to say, that this seems weird, but really the alternative would be weirder.

Simon said...

var blah0 = function() { alert( message ); }

is the same writing:

function blah0() {
alert( message );
}

I don't understand what is so creepy about that.

--
Simon

quinthar said...

Yikes! Yes, it's creepy precisely because I misunderstand it, not because it's evil. You're right, I'm wrong, I get that. Indeed, I sorta thought that was the point of the original post.

Anyway, while I realize it's incorrect logic based on not being a JavaScript programmer, in the interests of anybody who is curious how I misunderstood which is apparently obvious to so many, I think the whole notion of nested functions + garbage collection had me screwed up.

Basically, I wasn't thinking of "message" as a global variable being passed by reference. Rather, I was thinking of it as a string variable being copied by value -- like an stl::string. Similarly, I wasn't thinking of the nested function as alerting a global variable; I was thinking it would alert a copy of a string.

Anyway, I get that now, thanks to all your very helpful comments. The whole point of me posting was to draw attention to my ignorance in order to correct it. I welcome your feedback. But I'd appreciate it being a bit more... friendly, in the future.

Eat_My_Shortz said...

> You guys realize you're arguing over a > programming language right?

Yes ... this is the Internet. What else are we going to do?

mbbh said...

Generally you learn one thing here, that is understanding the language is really a key thing. I've come across this problem myself and found my solution later on with using a createAlert(); like structure once I've figured out there's a scope problem
here. But then, if you really read it, you are defining a function with _no_ parameters and asking it to print some external parameters... therefore ending you with a variable that is essentially undefined. Whats bad about javascript is that this behaviour doesn't create an error (as message is not anything global at this point, it just happens to be in the same scope as the calling function) but actually references the variable.

Anyway, thanks for sheding some light onto the issue.

quinthar said...

Ya, I think I basically expected my original code to do something like this:

--------------------------
var message = "Hello";
eval( "var blah0 = function() { alert( " + message + " ); }" );

message = "World";
eval( "var blah1 = function() { alert( " + message + " ); }" );

blah0();
blah1();
--------------------------

Basically, I was thinking that any variables passed in to a nested function declaration would be evaluated at that time and copied into the function itself. Everything being an untyped, garbage-collected object passed by reference was screwing me up. Anyway, a fantastic learning exercise. Thanks everyone!

Yitz said...

You said:
In any reasonable language, it would first alert "Hello", and then alert "World".

What "reasonable languages" where you thinking of, specifically?

Further reading on the underlying problem.

quinthar said...

Well, I guess the "reasonable language" I was thinking of would be some fictional variant of C++ where "message" was passed by value (rather than by reference) to some kind of closure object. Basically, something like:

-----------------
string message = "Hello";
Closure* blah0 = new Closure( "alert('" + message + "');" );

message = "World";
Closure* blah1 = new Closure( "alert('" + message + "');" );

blah0->execute();
blah1->execute();
---------------------

But yes, your point stands -- the problem is my ignorance, not the language.

Yitz said...

Don't worry about it - there are very valid weirdnesses in JavaScript. Example:

var myObj = { whoami: function() { return this; } };
myObj.whoami() == myObj; // true
var myFunc = myObj.whoami;
myFunc() == myObj; // false
myFunc() == window; // true

Whee! I mean... it makes sense when you understand how functions get their scope, but it's still odd behaviour, especially when you pass a method reference to, say, setTimeout.

Incidentally, I'd ignore the people who advocate "eval()" or "new Function()" (which is really eval() in disguise) - those are easy ways to shoot yourself in the foot. The preferred way would be:

var message = "Hello";
var blah0 = function (m) { return function () { alert(m); }; };

... and so on, which creates proper closures. Remember, in JavaScript new scopes are only created by function calls - not by blocks!

*sigh* All in all it's still a nice language. Once you learn the quirks, it's a bit like Lisp with C-syntax... and code isn't really data... so no macros... okay, I guess it's not really Lisp, but it often helps to think a bit more like Lisp than like C when writing JS.

(defvar message "hello")
(defvar blah0 (lambda () message))
(setq message "world")
(defvar blah1 (lambda () message))
(funcall blah0) ;; "world"
(funcall blah1) ;; "world"

So yeah, same deal.

- Jan 2014 (1) - Mar 2012 (1) - Nov 2011 (1) - Oct 2011 (1) - Apr 2011 (1) - Mar 2011 (3) - Feb 2011 (2) - Jan 2011 (9) - Nov 2010 (1) - May 2010 (1) - Mar 2010 (1) - Feb 2010 (1) - Jan 2010 (1) - Dec 2009 (1) - Nov 2009 (1) - Oct 2009 (1) - Sep 2009 (1) - Aug 2009 (2) - Jul 2009 (1) - Jun 2009 (4) - May 2009 (3) - Apr 2009 (3) - Mar 2009 (10) - Feb 2009 (5) - Jan 2009 (3) - Dec 2008 (5) - Nov 2008 (5) - Oct 2008 (5) - Sep 2008 (4) - Aug 2008 (5) - Jul 2008 (11) - Jun 2008 (8) - Feb 2008 (1) - Aug 2007 (1) -