JavaScript, the weird parts
link to notes https://git.io/vgpKc
about Sher Minn
- front-end web engineer
- funemployed, but joining Viki.com in a week
- recently spent 3 months in NYC at the Recurse Center
- retreat for programmers
- where people go to be better at what they do
- i learned a lot about vanilla JavaScript there
- i like making art with code
contact
- subscribe to @piratefsh for tweets on tech, art and bad jokes.
- sherminn.chong@gmail.com
why am i here?
- i'm not here to bash JavaScript
- i actually
<3
it - but it's kinda quirky and easily misunderstood
- this is about what i wish i knew when i stared learning
- also, fun facts
how this is run
- keep it conversational
- i will introduce a thing, and ask you things
- feel free to ask questions anytime
- we can discuss on the go
- for YOU to get as much understanding
agenda
- 30m - primer, types, equality, scope
- 10m - break, q&a
- 5m - hoisting
- 10m - the good parts, make friends with JavaScript
- end, q&a
primer
comfort level check
- have you written JavaScript?
- dictionary check
- callbacks
- scope
- had flip table moments with JavaScript?
some history
- JavaScript != Java
- back in era of Netscape Navigator (1990s)
- Netscape decided the web needed to be more dynamic
- a prototype was written by Brendan Eich in 10 days
- and was shipped with the next version of Netscape Navigator
- think about that the next time you ship hacky code
- ...but it has come a long way since
typeof
Let's talk about JavaScript types.
typeof 1 //=> number
typeof 'a' //=> string
typeof {} //=> object
typeof [] //=> ???
takeaway
JavaScript has types, and they can be kinda unexpected.
null, undefined, and NaN
null vs undefined
undefined
is the default value assigned to a variable that hasn't been assigned one
var foo; // value is undefined
null
is used to explicitly say "there is no value here"
var person = {
name: 'Sher Minn',
opinionOnJustinBieber: null,
}
person.opinionOnJustinBieber //=> null
What ARE they, though?
typeof null //=> ???
typeof undefined //=> ???
NaN
Stands for 'Not a number'. Used in math operations when result is invalid
Math.sqrt(-1) //=> NaN
Well, guess what type it is?
typeof NaN //=> ???
Let's test for NaN...
NaN == NaN //=> ???
best ways to check for each type
// undefined
typeof foo == 'undefined';
// NaN
isNaN(foo);
// null
foo === null;
bonus
What happens when you divide by zero in JavaScript?
2/0
takeaway
In JavaScript, null
, undefined
and NaN
are all different things. treat them with respect.
equality
So you think you know all about equivalence, right?
strict Equality ===
1 === 1
//=> true. same value.
1 === '1'
//=> false. different types.
var a = []
var b = []
a === b
//=> false, only true if is same object
a === a
//=> true, is same object
loose equality ==
Allows you to compare values of different types. Pretty handy, right?
1 == 1
//=> true
1 == '1'
//=> true. the string gets converted into a number.
var a = []
var b = []
a == b
// => false
When comparing two values of same type (e.g. object), JavaScript secretly uses the '===' operator. Which is reasonable.
Type conversion only happens when values are of different types.
it gets a little weird
Then again, type conversion also gives us unexpected results...
var a = {}
var b = {}
a == b
// => false
a == '[object Object]'
// => true
b == '[object Object]'
// => true
Values of different types get converted into the same type before comparing.
For example, when comparing object
to string
, the object a
gets converted into a string before comparison. a.toString()
gives us "[object Object]"
which is equal to the string.
even weirder...
And then there's this.
What do you think this does?
var c;
if(!c){
console.log('c is undefined')
}
else{
console.log('c is damn fine');
}
But how about this?
if(c == false){
console.log('c is undefined')
}
else{
console.log('c is damn fine');
}
Guess what, !c
is not the same as c == false
(!!)
JavaScript converts our variable into a boolean (which is falsey), then applies the !
to reverse the value (to be truthy).
Whereas c == false
, does it's own special thing where it compares undefined
and false
and decides it's not the same thing (which is falsey).
undefined
or null
to true
and false
don't ever compare not that you should be doing that in the first place.
undefined == null // rules of JavaScript say these are equal
null == undefined
=> true
undefined == true // expected, undefined isn't a true value
=> false
undefined == false // wait, but why?
=> false
null and undefined are their own special values.
Nothing equals to null
or undefined
except for null
and undefined
.
But both have 'falsey' values, i.e. if(undefined/null){}
will not execute.
takeaways
!c
is not the same asc == false
.null
andundefined
have their own rules about truth and falsity- when comparing values of different types, values get converted to the same type, according to some rules (see further reading)
- just use strict equality already, geez
further reading
- Equality comparisons and sameness link - check out the table for comparisons to find out what conversions happen when comparing different types.
- Truthy and falsey - link
scope
where does a variable live?
there are two types of scopes in JavaScript.
global
var hello = 2;
function hi(){
console.log(hello); //=> 2
}
local
function hi(){
var hi = 8;
}
console.log(hi); //=> undefined
JavaScript is function scoped
what happens here?
function foo(){
if(true){
var x = 100;
}
console.log(x) //???
}
foo();
how about this?
function foo(){
function inner(){
var x = 100;
}
console.log(x) //???
}
foo();
object scope
objects have this special thing called this
which refers to that instance of an object.
here is an example where you might use it.
var me = {
name: 'Sher Minn',
sayName: function(){
console.log(this.name);
}
}
console.log(me.name); //=> Sher Minn
me.sayName(); //=> Sher Minn
sometimes, you might want to use this
in a callback.
var me = {
name: 'Sher Minn',
sayName: function(){
window.setTimeout(function(){
console.log(this.name);
}, 1000);
}
}
console.log(me.name); //=> Sher Minn
me.sayName(); //=> undefined, 'this' is the window object
My this.name
is undefined!
In JavaScript, the this
object will refer to the object that is calling the callback. Not the object where the callback is created, as you would expect.
In this case, the callback is created in the me
object, but called by window
in setTimeout
.
binding 'this'
You can get JavaScript to use a specific this
.
var me = {
name: 'Sher Minn',
sayName: function(){
window.setTimeout(function(){
console.log(this.name);
}.bind(this), 1000);
}
}
console.log(me.name);
me.sayName(); //=> Sher Minn
more callback scoping weirdness
this code
for(var i = 0; i < 4; i++){
setTimeout(function(){
console.log(i);
}, 100);
}
is expected to log 0 1 2 3
. but instead logs 4 4 4 4
.
why leh?
solutions commonly seen on StackOverflow:
just try Googling 'callbacks in loops javascript'. so many articles and questions.
version 1 (ugly)
create new function for every call
for(var i = 0; i < 4; i++){
(function(ii){setTimeout(function(){
console.log(ii);
}, 100)})(i);;
}
version 2 (efficient)
function only created once and reused
function doThing(ii){
setTimeout(function(){
doThing(i);
}, 100);
}
for(var i = 0; i < 4; i++){
doThing(i);
}
another alternative
create new function via bind. less efficient, but also less code.
for(var i = 0; i < 4; i++){
setTimeout(function(ii){
console.log(ii);
}.bind(null, i), 100);
}
takeaway
- hey, y'alls, JavaScript is function scoped, not block scoped
- watch out for 'this' and variable references in a callback
10 MIN BREAK
freestyle chat. ask me about:
- Recurse Center
- generative art
- impostor syndrome
hoisting
Suppose we try to use a non-existent variable
console.log(foobar); //=> ReferenceError: foobar is not defined
Suppose we try to use a variable BEFORE it was declared
console.log(foobar); //=> undefined. but the variable exists?
var foobar = 'hi peoples';
why ???????
What really happens when var foobar = 'hi peoples'
is run?
- the
foobar
variable is declared foobar
is initialized the value'hi peoples'
var foobar = 'hi peoples';
is equivalent to
var foobar;
foobar = 'hi peoples';
Turns out JavaScript breaks it down to those two steps down. Then it does this thing called hoisting.
In action:
console.log(foobar);
var foobar = 'hi peoples';
is interpreted as:
var foobar; // variable DECLARATION hoisted
console.log(foobar);
foobar = 'hi peoples'; // variable initialization happens here
hoisting happens for variable and function declarations.
Back to the example I gave. This works:
foo(); //=> 'hi foo'
function foo(){
console.log('hi foo');
}
because JavaScript hoists the whole function to the top before it runs
// foo is hoisted up!
function foo(){
console.log('hi foo');
}
foo(); //=> 'hi foo'
And this doesn't...
foo(); // throws TypeError: foo is not a function
var foo = function(){
console.log('hi foo');
}
Because only the variable declaration is hoisted.
var foo;
foo(); // throws TypeError: foo is not a function (because foo is still undefined)
foo = function(){
console.log('hi foo');
}
takeaways
var
andfunction
declarations get hoisted to the top before code is run- but values still get assigned at their original locations
JavaScript, the nice parts
JavaScript allows for flexible use of functions
this one weird trick
How do you find the largest number in an array?
var arr = [4, 6, 9, 0, 1, 2];
Have you heard about Math.max()
?
Math.max(4, 6) //=> 6
It takes as many arguments as you want.
Math.max(4, 6, 9) //=> 9
But.. our numbers are in an array? Well...
Math.max(arr) //=> doesn't work. tries to sort the array as an element
In JavaScript though, you can do this (kinda like unpacking, if you're familiar with that):
Math.max.apply(null, arr) //=> ???
JavaScript is eloquent
forEach
how would you iterate through an array?
for(var i = 0; i < arr.length; i++){
console.log(arr[i]);
}
you can do this the functional way. look at how nice and clean it is!
arr.forEach(function(elem){
console.log(elem);
}
map, reduce, filter
also check these out as clean ways to manipulate arrays
given an array, how would you square each element?
map to perform an operation on each element
[1,2,3].map(function(elem){ return elem*elem }) // => [1, 4, 9]
how would you total numbers?
reduce to 'reduce' an array in some way (e.g. total)
[1,2,3].reduce(function(total, elem){ return elem+total }, 0) // => 6
how would you only pick out even numbers?
filter to filter out based on a condition
[1,2,3,4].filter(function(elem){return elem % 2 == 0; }) // => [2, 4]
takeaway
- JavaScript can be hella sexy if you want it to be.
closing words
ahhh JavaScript is so weird! why do you even like it?
- JavaScript is just a misunderstood teenager everyone likes to poke fun at
- but actually, isn't that bad when you try to understand it
- beacase it's so weird, you get to dive really deep
- and turns out it's secretly cool
- it's hella dynamic
- does cool visual things in-browser
- JavaScript engines (such as V8 that runs in Chrome) are pretty efficient
- so, make JavaScript your friend
how to make JavaScript your friend
- write vanilla JavaScript
- frameworks are great, but they don't allow you to understand the basics
- i didn't have the chance to properly understand JavaScript during my JQuery/AngularJS days
- e.g. try implementing a tabs library in vanilla JavaScript
- you'd be surprised at how much you can do without JQuery.
- understanding things like these will make you a better programmer (i promise):
- scope
- closures
- hoisting
- prototype, classes, inheritance
- event callback loop
- call, bind, apply
- there are many ways to do one thing. pick one that makes sense to you and stick with it.
- use
'use strict'
mode. it will get your browser to check for potentially dangerous JavaScript code - use ES6 JavaScript, a newer spec of JavaScript that is a lot more standardized.
nice job, thank you sir, get a bird's eye view of your github profile summary here - https://alamnr.github.io/profile.html?user=piratefsh, it will be great if you review my js code of this project's repo (https://github.com/alamnr/alamnr.github.io/blob/master/js/new_git_promise.js) and suggest me for improvement - thanks.