The Other Worlds Shrine

Your place for discussion about RPGs, gaming, music, movies, anime, computers, sports, and any other stuff we care to talk about... 

  • Things that annoy me about JavaScript

  • Somehow, we still tolerate each other. Eventually this will be the only forum left.
Somehow, we still tolerate each other. Eventually this will be the only forum left.
 #130255  by SineSwiper
 Fri Dec 19, 2008 4:25 pm
I've been working on a JavaScript project for a while. I've done some pretty major interactivity with web pages before, but not at this level. The amount of JavaScript code I've produced far outways the small amount of Perl code for the backend side.

Some things have really bothered me about this language, though:

1. The + operator.

I know that JavaScript is a mildly variable defined language. (Kupek, what's the word I'm looking for to describe that aspect of the language? An array is defined as such, a number is defined as a number, etc.) However, the conversion is sometimes not very smart.

Take the + operator. Please. Take it. Stop using it for string concatenation, like Perl has. If my statement says:
Code: Select all
foo = bar + 4;  // where bar = 8
I'd expect to see foo equaling 12. Instead, I often see it equaling 84, just because I happened to grab it from an XML set, and it's too stupid to realize that an unquoted 4 means a fucking number. I find myself being paranoid and putting in:
Code: Select all
foo = Number(bar) + Number(joe);
Just to make sure that it's going to actually do MATH, instead of string concats. I could do "bar - -4", but that's fugly.

2. No hashes.

I miss hashes. And I know what you're going to say: "But, Sine, JavaScript is an OO language. You don't need hashes." Fine, I perhaps don't need hashes, but if I'm going to get away without using hashes, I would like to have a .keys method. There is no way to transfer a set of object names to an array without doing:
Code: Select all
var arr = new Array;
for (var i in obj) { arr.push(i); }
Thus, wasting an array and taking a few more lines of code every time. Sometimes I just need it to check if an object has anything. This is blindly simple in Perl; not so much in JavaScript:
Code: Select all
Perl:
if (keys %foo) { do stuff; }

JavaScript:
var data = 0;
for (var i in foo) { data = 1; }
if (data) {
3. Var should mean "kill it!", but it doesn't

If I write a statement in a loop like:
Code: Select all
while (whatever) {
   var foo;
   ...some code...
   if (foo) { do something; }
}
I would expect that foo would equal undefined like it does with Perl's my. Except that it doesn't. For some strange reason, the variable is still local to the loop, but it doesn't get destroyed after the loop is done. It still holds the variable and uses the previous value after it sees the "var foo" statement. Instead, I have to use "var foo = undefined;" just to make sure that it's not going to burn me with this confusing rule.

4. Getting the last value of an array is bulky

JavaScript requires a lot of code to get to the last value of an array. It's more of a matter of the characters typed, but I just type them so often. Contrast and compare:
Code: Select all
Perl:
$rows[$#rows]

JavaScript:
someObj.rows[someObj.rows.length - 1]
This wouldn't be so bad if I didn't have to use it so often. There's no such thing as foreach loops, so everything is a typical C-based "for (var i; i < someObj.rows.length; i++)" loop. Maybe just give me a pop method without removing the value from the array.

5. No destructuring assignments

This is in JS 1.7, but I can't do that for compatibility reasons. It allows you to do this:
Code: Select all
var arr = new Array(5,10,15,30);
[a,b,c,d] = arr;
This is great for functions that returns multiple values.

6. God, document.getElementById is a lot of fucking characters.

I finally gave up and created a GEBI function.

 #130257  by Kupek
 Fri Dec 19, 2008 5:55 pm
JavaScript's type system is both dynamic and weakly typed. It's dynamic because type information is inferred and only known at run time (as opposed to explicit and at compile time for languages like C, C++ and Java). You don't declare a variable as a string, the runtime system knows it's a string because a string was assigned to it.

It's weakly typed because it doesn't require explicit conversions between types. Python is an example of a dynamic and strongly typed language. All type information is inferred at runtime, but if you want to use an integer as a string, you have to do an explicit conversion.

It's not that JavaScript is an OO language, so you don't need associative arrays (hashes). It's that objects are associative arrays. Which, if I remember correctly, is how they're implemented in Perl, too.

I've never actually used JavaScript, but I imagine some of your pains are because you don't know idiomatic JS. You probably still think mostly in Perl, so I imagine you're coming up with your algorithms using what you know Perl can do, then translating that to JS.

Also - and this goes for Perl, too - you can often avoid explicit looping if you think functionally. A gentle introduction to what I mean by that: http://www.joelonsoftware.com/items/2006/08/01.html

 #130260  by SineSwiper
 Fri Dec 19, 2008 10:12 pm
Kupek wrote:It's not that JavaScript is an OO language, so you don't need associative arrays (hashes). It's that objects are associative arrays. Which, if I remember correctly, is how they're implemented in Perl, too.
Well, rather, associative arrays are objects. There's the notation of:
Code: Select all
var arr = new Array;
arr["foo"] = "bar";
arr.bar = "foo";
But, it's an illusion. It's actually turning arr into an object, despite the array declaration. However, unlike Perl, there isn't the nice methods out there to deal with hashes.

And in Perl, there isn't true OO, but there is enough symbolism to pretend that it's OO. We've had this argument already. But, JavaScript is definitely an OO language. Unless, "parentWindow.document.implementation.createDocument" isn't considered to be a bunch of objects.
Kupek wrote:I've never actually used JavaScript, but I imagine some of your pains are because you don't know idiomatic JS. You probably still think mostly in Perl, so I imagine you're coming up with your algorithms using what you know Perl can do, then translating that to JS.
Granted, there's some of that there, but I'm actually using the object model quite a bit. About the only thing I'm missing is prototyping methods into objects, which might be a big deal. I'm not sure if I understand the benefits of global functions versus object prototyping, though. Seems like it's easier to just have the functions there when I need them, instead of worrying if I have to port that function into the object I'm accessing.
Kupek wrote:Also - and this goes for Perl, too - you can often avoid explicit looping if you think functionally. A gentle introduction to what I mean by that: http://www.joelonsoftware.com/items/2006/08/01.html
Heh, cmon, give me more credit than that. I'm a lunatic about duplication removal. I just spent some time today taking some repeated code I had and bring it down to a better size, despite the fact that the code was already working, and I didn't need to use the same set of repeated code again. It just bothered me, so I fixed it. And I didn't even have to create a new function.

See, I still have my reservations about creating too many functions for the purpose of unduplicating code. Having a function for two or three lines of code, if it's used only twice, is a bit much. After all, you have to spend time naming the function, and if you can't figure out a "purpose" that could define the name of the function, then the function isn't worth creating. And there's that whole usage versus lines-of-code ratio I was talking about.

(I really didn't want to create that GEBI function, for example, but I was using the function SO MUCH, and that document.getElementById is just really fucking ugly on an if statement.)

Back to the original point, there's no way to completely eliminate loops. Hell, loops are just a part of coding. In the case of JS, there are two types of for loops and the while loop. Most of what I'm using is array looping, so yeah, I get to use that length thing all the time.

I did figure out that I could do this for the "hash" checking if statement:
Code: Select all
Perl:
if (keys %foo) { do stuff; }

JavaScript:
var data = 0;
for (var i in foo) { data = 1; }
if (data) { do stuff; }

Better way:
for (var i in foo) {
   do stuff;
   break;
}
A little weird, but it looks less messy.

 #130267  by Kupek
 Fri Dec 19, 2008 11:52 pm
If you think functions need to be named, then you didn't get my point about functional programming. It's not what you think: http://en.wikipedia.org/wiki/Functional_programming

About explicit looping: let's use Perl. Assume I have an array @names with the values 'kupek', 'sine', 'zeus' and 'don'. How would you create an array @upper which contained 'Kupek', 'Sine', 'Zeus' and 'Don'? Here's how I'd do it:
Code: Select all
@upper = map(ucfirst, @names);
Look ma, no explicit loops.

 #130268  by SineSwiper
 Sat Dec 20, 2008 12:02 am
Wikipedia wrote:This article contains too much jargon and may need simplification or further explanation. Please discuss this issue on the talk page, and/or remove or explain jargon terms used in the article. Editing help is available. (October 2008)
No shit...

Yeah, that was the other thing I was going to point out. I use the map function quite a bit, and I miss not having it in JavaScript.
 #130301  by Andrew, Killer Bee
 Sun Dec 21, 2008 6:16 am
SineSwiper wrote:Take the + operator. Please. Take it. Stop using it for string concatenation, like Perl has.
Does Perl not have operator overloading? Every language I've used in recent years has it, so I have it ingrained in me not to assume particular behaviour for operators if I haven't forced the type of an object.
SineSwiper wrote:6. God, document.getElementById is a lot of fucking characters.

I finally gave up and created a GEBI function.
Is there a reason you're not using JQuery?

 #130302  by SineSwiper
 Sun Dec 21, 2008 10:38 am
I don't understand this contradiction. First, Kupek remarks:
Kupek wrote:I've never actually used JavaScript, but I imagine some of your pains are because you don't know idiomatic JS. You probably still think mostly in Perl, so I imagine you're coming up with your algorithms using what you know Perl can do, then translating that to JS.
And then both of you say that the solution is to use libraries to think like OTHER languages. That's essentially the same thing has thinking in one language and trying to convert that to work in JavaScript.

If this is the right way to program in JavaScript, then why aren't these routines in the language?

 #130324  by Kupek
 Sun Dec 21, 2008 9:24 pm
One philosophy of language design is to ingrain as little as possible into the language itself, and leave the rest - even important things - up to libraries. People have been working with JavaScript for a long time. They've probably developed libraries and techniques that can alleviate your problems.

Learning the common idioms of a language is not the same as trying trying to coerce one language to be another. Again, I don't use JavaScript, so I can't judge if your criticisms are valid - they very well may be. But I get the impression some of what you're encountering is you don't know how to do things the JavaScript way.

 #130325  by Andrew, Killer Bee
 Sun Dec 21, 2008 9:26 pm
I'm not sure where the contradiction is here. JQuery doesn't fundamentally alter Javascript as a language, it just makes much easier (and guarantees the portability of) common web development operations.

 #130326  by RentCavalier
 Sun Dec 21, 2008 9:41 pm
Jesus Christ, I didn't understand one iota of that.

 #130328  by Chris
 Sun Dec 21, 2008 10:23 pm
I don't know why he was talking to me with it either

 #130331  by Andrew, Killer Bee
 Mon Dec 22, 2008 4:02 am
Kupek wrote:About explicit looping: let's use Perl. Assume I have an array @names with the values 'kupek', 'sine', 'zeus' and 'don'. How would you create an array @upper which contained 'Kupek', 'Sine', 'Zeus' and 'Don'? Here's how I'd do it:
Code: Select all
@upper = map(ucfirst, @names);
Look ma, no explicit loops.
Python's got some lovely functional programming stuff built into it. List comprehensions, for example!
Code: Select all
upper = [name.capitalize() for name in names if name != 'bovine']

 #130333  by SineSwiper
 Mon Dec 22, 2008 10:32 am
Andrew, Killer Bee wrote:
Code: Select all
upper = [name.capitalize() for name in names if name != 'bovine']
Code: Select all
@upper = map(ucfirst, grep { !/bovine/ } @names);

 #130335  by bovine
 Mon Dec 22, 2008 12:46 pm
are you guys talking about me in a way that cannot understand? I see my name in there and am concerned.

 #130336  by Kupek
 Mon Dec 22, 2008 12:56 pm
While we're on the topic, Sine, you might get something out of Higher Order Perl. It's about functional programming, but uses Perl. To quote the author,
Higher-Order Perl is about functional programming techniques in Perl. It's about how to write functions that can modify and manufacture other functions.

Why would you want to do that? Because that way your code is more flexible and more reusable. Instead of writing ten similar functions, you write a general pattern or framework that can generate the functions you want; then you generate just the functions you need according to the pattern. The program doesn't need to know in advance which functions are necessary; it can generate them as needed. Instead of writing the complete program yourself, you get the computer to write it for you.
Given your background, it's probably the best introduction to functional programming. I haven't read the book, but I've heard good things about it.

Homework assignment: do the above example using nothing but map(), lambda() and filter().

 #130360  by SineSwiper
 Mon Dec 22, 2008 8:29 pm
Kupek wrote:Homework assignment: do the above example using nothing but map(), lambda() and filter().
Why? I wrote that in one line, using three built-in commands, including map.

 #130367  by Kupek
 Mon Dec 22, 2008 10:30 pm
Because doing so forces you to use anonymous functions - lambdas. That's a conceptual stepping stone. Anyway, check out the book - the entire thing is free and online in HTML and pdf formats.

 #130611  by SineSwiper
 Mon Dec 29, 2008 3:50 pm
I got around to re-reading the last few posts, because I never looked at that link you sent me. Then I realized that you were trying to give me an entire book to explain a concept. (I haven't read a book on programming in probably 15-20 years. I just plain work better with examples and references than a 250-page book trying to tell me something that could be explained in 10-30 minutes.)

So, I tried looking it up. The Wikipedia articles are incredibly dense and math heavy, though I found some other articles on the concept. Overall, I think I realized that there's absolutely nothing I need to change about the way I program. (Though, List::Util's reduce function looks kinda useful.)

You challenged me to write that uppercase example using nothing but map(), lambda(), and filter(). However, after looking at exactly what those functions do in Python, I realized that I already gave you the example:
Code: Select all
@upper = map(ucfirst, grep { !/bovine/ } @names);
The map is already there. Grep works almost exactly like filter. (Except for infinite lists; more on that later...) And where's the lambda? Well, lambda() is just an anonymous function, a construct which is already built into Perl. You can create anonymous functions on the fly, by using the { } set.

So, the only thing that map and grep won't do is infinite lists. However, functional concepts using infinite lists is dangerous thinking. Things like while loops should be used with care, and there needs to be proper flow control to make sure that the program doesn't run infinitely. True, for loops and the like could become "infinite" or "immensely large" for large amounts of data, but if you are expecting that type of situation, storing the entire data set into an array and for-looping it is not a good idea. (Use while loops with one line, or one chunk of data, at a time.)

Trying to mash an infinite loop into a single statement is simply a program without enough code. Overall, you want to program to output something in the end, and for the program to stop. Otherwise, it's a daemon, and you shouldn't run a daemon based on a single line of code.

Anyway, I'm still trying to figure out what you wanted me to learn. Maybe it's because you didn't know how much I knew, and admittedly I sometimes don't know the technical terminology, even though I'm familiar with the concept itself. I basically learned programming the hard way, by making the programs and re-engineering my way of thinking each time I push my boundaries, as opposed to reading the "proper" way to program.

 #130662  by SineSwiper
 Tue Dec 30, 2008 5:43 pm
I think I will take a serious look at JQuery, though. I didn't really think I needed it, but it does mean I don't have to bother with "does this object exist" checks over and over again. An empty set will return nothing and do nothing, if I'm reading the docs right. I have a need for that behavior in a little bit, when I convert this JS file to work with multiple HTML files (and some may not have certain objects).

Not sure if I like the idea of doing multiple actions on a string of methods ("Am I hiding the objects, or am I hiding the attribute I changed?"), but I guess the same is done on strings and arrays.