I gave a talk on Javascript recently at Javascript.my‘s unshift() miniconf in Kuala Lumpur. After regretting giving many talks without a recording to track my performance improvements when giving talks, I decided that starting this year, I would record and transcribe my talks. Here’s the first one for 2014. The slides are embedded here as well.
I had originally planned to record my talk and use an automatic transcriber to transcribe. But the recording quality was indeed quite poor as my phone battery was running out. I had to use Audacity to clean it up quite a bit. And despite that, automatic transcription with OS X’s Dictation didn’t work as well as I expected it to.
Anyhow, here’s the transcription of my talk. A lot of bits were filled in and edited (I really have a lot of uh, and ums when I speak) as the recording got quite garbled as time went on. Towards the end it was a totally unrecognizable warble. I was scheduled to give a 30 minute talk but I ended up talking for about 50 minutes. The last 20 minutes are not transcribed.
Transcription:
Slide 0
[Ruben:] … he’s here to teach us how to be an extremely bad Javascript programmer.
Or as I would put it – being a complete arsehole. OK let me just connect to Airplay.
[Airplay connected]
Ah, Excellent. So, uh, Ruben’s already introduced me, but I’m actually here sell you books. I’m here to sell my books. I’m being honest. Surprisingly flexible book about the quirks of Javascript with two titles [Javascript Interview Questions or
Underhanded Javascript.]
[Audience Laughter]
Now, uh, why should you listen to me? Well, I’m not exactly a Javascript developer. But I happen to know a lot of Javascript internals. Long story on that.
So this talk is going to be about the WTF parts of Javascript. Javascript is full of WTFs – so full that I can write a book on it – oh wait. I’m going to walk you through them and show you how to avoid them – and if you want to be an arsehole, you can specifically write code that way. Now if you want to tweet, that’s my twitter handle – follow me – and that’s my hashtag but; of course no one is doing that.
Slide 1
So, the agenda today is to go through a bunch of small examples and hopefully if we have time, go through the big example. And throughout I’ll explain to you how to write arsehole code.
We’ll start by actually talking about expressions. Um, everyone here knows what an expression is? In a general programming context, that is.
An expression is something that generates values uh.. evaluates a a value, as opposed to – as opposed to statements, which do not evaluate to values. Statements do things. Right. if
is a statement, 1 + 1
is… an expression.
So, in Javascript there is something called, um, Function Expressions. When people talk about um, first class functions, they usually mean something like a function expression, that is to say a function definition that can function as a well, an expression. As strange as it sounds, that actually makes sense. First class functions are functions that can be used as values. Expressions can be used as values, therefore Function Expressions are pretty much what makes first class functions in Javascript first class functions.
[Audience:] So sciency!
Slide 2
Um. So to start with, let’s have a look at this. What does this return? This is um, what you call a Function Expression. You can see function foo()
. If you type that out on its own, it’s called a Function Declaration. And Function Declarations cannot be unnamed, they cannot be anonymous – the thing about Function Expressions is that names are optional. In here, bar
is an optional name. So what happens if you run this?
Let’s have a look shall we? This is what happens when you do it in Chrome? Boom. ReferenceError
. Now, why does this happen?
Slide 3
Well, why this happens is quite simple. Because it’s a Function Expression. Welp. That’s helpful. And, the name of a function expression can only be referenced inside a function body. It is only useful when referenced from inside a function body. So the name bar
can only be accessed within the function expression of bar
. Right?
Slide 4
So now you’re gonna ask me why is this useful. What’s the point of assigning a function name bar
when the name is only accesible to itself and the function is going to be assigned to a variable named foo
? Why is this useful?
It’s actually useful when you want to do recursive stuff. Now ignore the code on the screen, I wrote it on the plane and I had a shortage of sleep. It’s not the best example. I may sound rambly actually. The reason why you want to have names, is so that you can do your recursive functions. Now if you have no name here and you call foo
, you will get a lot of undefined
.
And another thing that I personally found useful is that you can use this to replace old ES3 code. In ES3 you could use something called arguments.callee
to do recursive functions. That has been diabled in ES5.1 Strict Mode – it will throw errors – and what can be used to replace it are named function expressions. Right, am I losing everyone here? Haha. So, not very arseholish, but this is part of the bigger picture.
Slide 5
So let’s go on to the next thing which is Immediately Invoked Function Expression. Everyone here knows what an IIFE does, so can anyone tell me what Example 1 returns in Chrome.
[Audience:] The array
Er, yeah. Actually, it logs the array, but returns 1. Speaking of arrays, arguments
are not arrays. They’re simple objects but look like arrays when outputted.
Er, so what does Example 2 return?
[Audience:] Same thing as example 1
And example 3?
[Audience:] Same thing?
Let’s try this out shall we?
[Example 1 is being run on Chrome]
OK. So, this is Example 1, and we’ll see what Jensen says is correct. It logs the the arguments
object as [1, 2, 3, 4, 5]
, and then it returns x
, which is the first element of the arguments object. So it returns 1.
Now if we go to the second example, and
[Example 2 is being run on Chrome]
Oh shit. It doesn’t make sense! Why does it return 5
!?
Aaand so we go on to the third example!
[Example 3 is being run on Chrome]
Right so, as you can see the answer is as expected.
So, as you can see the difference between Example 2 and Example 3 is simply the parentheses in front of the function definition. OK? And that changes everything.
So let’s tabulate the results. But before that. Let’s also see what the Node.JS REPL says. OK so,
[Example 2 being typed into Node.js REPL]
Aaand you run this. Anyone wants to make any prediction?
[Audience:] 5
!
[I press enter and this result shows up on screen: [1, 2, 3, 4, 5] 1
]
Slide 6
Wrong. So what the fuck is happening here? This is the tabulated result. And.. oh ya, this even more fun. What happens when you do foo)(
in NodeJS’s REPL? Yes I know this looks a little strange, but bear with me for a bit.
Let me show you what happens when you call foo
like this.
[I defined foo
and called foo)(
in Node.js REPL]
WHAT??!!! IT ACTUALLY RETURNS SOMETHING!
[Audience Laughter]
Uh, so, Welcome to the weird and wonderful world of Javascript.
Slide 7 and 8
Let me tabulate the results. And I want you to think about this for a bit. Let it sink in for a bit. NodeJS runs on V8. I ran Examples 1-3 in Chrome and NodeJS. And yet, the results wildly differ from one another. And even in Chrome itself, Example 2 has a different result from 1 and 3. What is happening? What!?
So the answer is simply this: Example 2 is not actually a Function Expression. Example 2 is something an arsehole Javascript programmer would do. Why?
Slide 9
The function definition in Example 2 is actually a Function Declaration. You are basically declaring a function. And then you’re basically running it like this:
function foo(){}; (1, 2, 3, 4, 5)
You declare a function, automatic semicolon insertion happens, then the second part is evaluated as a group expression. And since (1, 2, 3, 4, 5)
is evaluated as 5
, therefore 5
is returned!
It’s that simple! What makes function declaration a function expression is simply the parenthesis. Or any form of expression. If your function dec- if your function definition is part of an expression, it’s automatically evaluated as a function expression. OK? That’s it!
Slide 10
So, now let’s explain what happens in NodeJS, specifically the REPL. OK? So when you call foo)(
like this, the REPL actually wraps it in parenthesis, which is why when I call foo, it is parsed as (foo)()
, which is parsed as a function expression. Which is why when we tried Example 2 in the REPL, it returned the same result as Example 3 – BECAUSE IT WAS WRAPPED IN PARENTHESES AND PARSED AS A FUNCTION EXPRESSION!
Now, with the issue of the weird parentheses, this has been fixed in the latest version of NodeJS.
[Ruben:] It was actually fixed in a very very very recent fix.>
Slide 11
So, how do you be an arsehole with this code? Let me show you an example that I’ve actually seen in real life. In fact all the sample code you see today are excerpted from real life examples that I have seen while working in the advertising industry. People write bad code all the time.
So you define a function like this, which will never be called. Then you write an IIFE in the latter part, doing what you want to do. Here you can drop all your payload: drop tracking everpixels, et cetera. This is what you do.
When people read code they’re not going to read the call, they’re going to read the function! Arseholes.
Right, so you might be wondering why I started this talk with named function expressions then. That’s because the most evil way you can write code is to name the Function Declaration and IIFE the same name. People will ignore the IIFE despite it having a different body.
Slide 12
So.. quick quiz:
What does this snippet returns? Bear in mind that names in Function Expressions are optional. Bear in mind that only Function Expressions can be immediately invoked.
[Audience:] 1
Correct! This is something to remember is if you have a name that is the same as a variable in the body, the name gets overwritten in a function expression. And that’s where you can do some really nasty stuff if you know what you’re doing.
So, that’s enough about Function Expressions. Let’s talk about arrays. Arrays are – arrays especially in array literal expressions.
Slide 13
What does this returns? Does this even make sense to the human parse? Anyone care to guess what this returns?
[Audience:] Is it a 2 dimensional array?
No, it’s not. This does not actually create a 2 dimensional array. In fact it returns… 4. You can try that, it returns 4.
[The example is being run on Chrome]
[Audience:] what the fuck?
Anyone here knows what the comma operator does?
OK, so this is what happens when the comma operator comes into play. But first let’s break this down:
Slide 14
First you’ve got the array literal. This is an array literal, and if you ever write a parser, anything that is a square bracket that comes after an array is known as the member operator, also known as an accessor syntax, and we all know how to access elements in an array.
To access element 0, just put `` in the array, and you access the zeroth element of the array. By the way array accessors will be coerced to strings. Just so you know.
Because you put commas into your array accessor, it’s parses as if you did this.
Slide 15
Now if you don’t know what the comma operator does, it takes a pair of operands which are basically expressions, evaluates the first subexpression, and discards the result. Then it evaluates the second subexpression, and returns the results.
Slide 16
So this is exactly what happens in this example. (1, 2, 3)
is evaluataed as ((1, 2), 3)
. In order to evaluate that, (1,2)
has to first be evaluated. It evaluates the number 1
as a number literal 1, and chucks the result away. Then it moves on to evaluate the second operand – and evaluates 2
as a number literal 2, and returns the number 2.
This gives us (2, 3)
. The same thing happens, and 3
is returned. Therefore the member operator expression is to access member 3, which is number.
Happy isn’t it? Isn’t Javascript just beautiful and fantastic.
One thing you can do is to take advantage of the fact that the comma operator executes and evaluates the first subexpression and doesn’t return the result. So if you have for example, an array here, and you can have a function that changes the array, you can wreak havoc on other programmers’ lives.
Slide 17
So, ah, well… enough about expressions. Let’s talk about statements. Statements are far more interesting. Statements don’t actually return results. Statements are more like directives or instructions but I cannot use those words because it’s actually confusing. People get confused with CPU instructions or angularjs directives. But let’s just say that statements tell the computer to do things.
These are examples of statements. It’s by no means comprehensive a list of statements in Javascript. What I want to bring attention to is this. That block statements are delimited by curly braces. This is quite important because if you look at any function code – any function body starts with a curly brace and ends with a curly brace. It’s essentially telling the computer “this is a list of statements for you to execute.”
This is also important because Javascript object literal expressions also start and end with the same thing – curly braces. And, you’re going to see some fun stuff.
But not yet.
Slide 18
We’re going to go to truth and false first. Everyone knows this right? What does console.log(x)
return
[Audience:] true
Really?
[Audience:] true
[Audience:] it’s truthy.
REALLY??
OK let’s have a look.
[Example is being run on Chrome. It returns false
]
You didn’t expect that right? Well, I actually expected you guys to expect that. But "true" == true
returns false
.
Despite being a truthy statement, it’s not equal to true
. It does not go into the false branch. It’s a truthy value! You say ==
coerces values. But if it coerces, why is "true"
not equal to true
??!
"true"
is a string – and true
is a boolean, and this is actually why I love to use this example – even in my book I use this example. It’s because people are lulled into thinking, “hmm, okay, makes sense. Python does this, most other dynamically typed languages do this. It’s a different type, why should it be equal – a string does not have the same type as a boolean. Makes sense.”
And then you come to somehting that trips you up, like this. It’s a similar example, except it trips you up.
Slide 19
Right, x
is a truthy value. But x
IS EQUAL TO `` HERE! If you run this right – like right now, it’s true
!!!
[Example is run on Chrome. It returns true
]
WHY!? WHAT?!
Now there is one simple rule to all this, really. When considering truthy and falsey things in Javascript, basic rule: if it’s in a statement, it coerces to a boolean. If it’s in a relational expression, it coerces into a number.
Slide 18
When I did this, it coerces string "true"
to a number. It coerces the boolean into a number. True in boolean is 1. But “true” in string, is NaN. 1 is not equal to NaN.
Slide 19
Same with the latter example. It coerces [0]
into – It's a multi step process to coerce `[0]` by the way. Then it's compared with
, which is true
!
Fun isn’t it?!
Slide 20
Ah, the slide. Now this does not mean that you shouldn’t use ==
. I know a lot of experienced Javascript developers tell you: don’t use ==
, because it coerces – just use ===
. But really, if you know how it works – and I just told you how it works, there is no reason not to use it.
[Audience:] HERESY!
Haha, heresy indeed. So, here’s a quick quiz: what do these examples return? Now that I’ve explained to you –
Slide 21
[Audience:] first one is false
First one is false
, OK!
[Audience:] second one is true
Second one is true
[Audience:] third one one is opposite of the first
[Audience:] fourth one is opposite of the second one
Third and fourth one is the opposite of the first 2? Well, you’re correct. For the first two.
Alright, so this is a bit interesting. So we’re try this out.
[Example 1 is being run on Chrome]
You were right. This is false
. Why is this false
? Can you explain?
[Audience:] empty object is false, [0]
is an array is false
, true
false
makes false
OKAY, that’s close, but no cigar.
[Example 2 is being run on Chrome]
If that’s the case then why is Example 2 true
. Both arrays have 1 element.
[Audience:] but now the second one is going to be coerced into 1
, therefore it’s now true
What about the object in front?
[Audience:] oh
[Example 3 is being run on Chrome]
Right, this is the best part because it’s easy to confuse. And that’s the intention. We go here, and we type a !
in front of it. Which would return false
. As expected.
[Example 4 is typed into Chrome’s console]
So what would this return?
[Audience:] false
False? Because it’s the opposite of true in the previous example?
[I press enter. Example 4 returns true
]
Lovely isn’t it Javascript?
Slide 22
Now, remember what I said earlier – statements are delimited by braces. At the same time JS Object Lits are also delimited by braces.
Slide 21
So, to put it in simple terms, the first braces in the first two are not objects. They’re statements – empty statements.
[Audience:] Wut
You literally are comparing [0] == true
. That’s what you were doing. The curly braces make an empty statement – as if you typed {}; [0] == true
.
Now, so what happens when you put a !
in front of it?
[Audience:] It does nothing?
It does nothing? No. Not really. When you put a !
in front of the curly braces, they’re now parsed as Javascript object literals expression, and then your explanation comes in.
This is now an object. It’s as if you typed !({}[0]) == true
. You’re now accessing member 0 of an empty object. That is a falsey value. And if you !
a falsey value, it is true
. Same explanation goes for example 4.
Lovely isn’t it, Javascript.
So, we’re nearing the end of the talk. Do we have time?
Slide 23
Remember now, that truthy and falsey values are coerced into booleans in statements, and in expressions they’re coerced to numbers. OK? So what does this switch statement return?
It’s not very commonly used in Javascript. But ah, what does this return?
[Audience:] "but here's my number"
OK, you would be correct. Only half though. But why?
[Audience:] because there are no quotes around the 1. It’s a number not a string
[Audience:] there’s no break
HAH! YOU ARE CORRECT! Now, the first thing to know about switch statements is that the values are not coerced. 1
is equal to 1
, not a string of "1"
. If you run this, and there’s no break
statement, it will return "but here's my number, so call me maybe"
In fact, let’s try that.
[Example is being run on Chrome]
You should be aware that if you use switch statements in Javascript, if you do not have a break
statement, or a return
, or a throw
, the statements will continue executing to the next case and the next case until the switch statement ends.
Fun isn’t it?
Slide 24
The question is this: WHY, JAVASCRIPT! WHY ARE YOU SO INCONSISTENT WITH YOUR OWN SELF?!
Now, let’s go on – do we have time? It’s been 28 minutes. I’ll be very quick.
Slide 25
So.. Do you know that chrome has block level scoping without using ES6?
When I run these examples in Chrome and Firefox, you’ll see something very interesting. It will be as if Chrome has block level scoping.
[Example 1 and 2 are being run on Chrome – Garbled]
As you can see you don’t have a var
and yet x
is inacessible! this leads people to think that Chrome has block level scoping. I actually explain this in much more in depth in my book.
But really it doesn’t really have block scoping. It only appears to have that.
[Garbled – the last 15 minutes are unclear]