Learn JavaScript: Summarising You Don’t know JS (Up & Going) Book 1
Hi, I’m a frontend developer working with JS, and planning to read YDKJS by Kyle Simpson for a long time and never succeeding, this is another try, I’ll be summarising the books in these posts as I read them.
This is the first post, and in this one I’m sharing my learnings from the first book of the series, “Up & Going”.
This book is more like an introduction to JavaScript, and emphasizes on how understanding JavaScript in deep can benefit the people who use it and are not aware of the concepts, features and caveats of the language or choose to ignore what they don’t understand and go on working with the incomplete knowledge that they have of the language.
Some statements from the book that ring a bell for me are,
One of the most important lessons you can learn about writing code is that it’s not just for the computer. Code is every bit as much, if not more, for the developer as it is for the compiler.
Your computer only cares about machine code, a series of binary 0s and 1s, that comes from compilation. There’s a nearly infinite number of programs you could write that yield the same series of 0s and 1s. The choices you make about how to write your program matter — not only to you, but to your other team members and even to your future self.
The preface of the book emphasizes on how important it is to understand the “tough parts” of JavaScript. I believe that, and thus trying to understand these so called tough parts through these books.
The first chapter is a introduction to programming and JS, there is basic intro to these concepts,
Variables, conditionals, expressions, statements, loops, functions, scopes.
One key point from this chapter is, JavaScript is dynamically typed.
Some programming languages have variables which can hold only a specified type of value like number, string etc, called static typed languages.
While some emphasize on the type of values rather than variables, meaning a variable can hold any type of value at any time.
JavaScript follows the second one, thus it is dynamically typed.
What are types in JavaScript?
“JavaScript has typed values and not typed variables”.
There are 7 types in JavaScriptnumber
, string
, boolean
, object
, null
, undefined
, symbol
.
Any variable can hold a value of any of these types at any time.
One long standing bug in JavaScript, that is probably never going to be fixed is that typeof null returns object and not null.
object
is one of the most useful value type.
There are couple other types which are very useful and commonly used, array
and function
, these are not types as such but are specialised or subtypes of the object
type.
Built in type methods
The built in types and subtypes have behaviours exposed as properties and methods, for example a string
type has a toUpperCase()
method exposed which returns a new string which is the uppercase version of the string on which toUpperCase()
was called.
let x = "hello world"; // x has a string type value stored in it
let y = x.toUpperCase() //y now has string "HELLO WORLD" stored in it
There is a lot going on internally in this example, for the primitive types string
, number
, boolean
there are wrapper objects String
, Number
, Boolean
respectively, which have these behaviours exposed as methods and properties.
When you try to use these behaviours on the primitive values, JavaScript automatically boxes the value in it’s respective wrapper object.
Conversion of types in JavaScript (Coercion)
Types in JavaScript can be converted from one type to another, this process of converting types is called coercion.
Coercion can be implicit or explicit, when it is clear from the code that there will be a conversion is explicit coercion, for instance
let numberString = "55"; //typeof numberString == "string"
let number = Number(numberString); //typeof number == "number"
// the string is converted to number explicitly
when it’s not clear from the code that value conversion is happening, it’s called implicit coercion, for instance
let numberString = "55"; //typeof numberString == "string"
let twiceNumber = numberString * 2; //typeof twiceNumber == "number"
//the string is converted to a number implicitly
Truthy and Falsy values in JavaScript
When converting other value types to boolean type, only 6 values are converted to false
, these values including false are called falsy values.
""
0
-0
undefined
null
NaN
false
only these values(except false) are converted to false
during a coercion, all other values are converted to true
.
Comparing values in JavaScript
There are two types of comparisons, equality and inequality.
The result of any comparison is strictly a boolean i.e. true
or false
, regardless of the types that are being compared.
The comparison operators available in JavaScript are
------------- equality operators --------------
== // loose equality
!= // loose non-equality
=== // strict equality
!== // strict non-equality
------------- inequality operators ------------
> // greater than
< // lesser than
>= // greater than or equals to
<= // lesser than or equals to
==
, ===
, !=
and !==
are equality comparison operators, don’t confuse !=
and !==
as inequality operators, they are non-equality operators and are just negated counterparts of their respective equality operators.
Loose-equality vs strict-equality in JavaScript (== vs ===)
It is believed that the difference in ==
and ===
is that, ==
checks for value equality and ===
checks for value and type equality, the author states that this is inaccurate, and the accurate difference is
==
checks for equality with coercion allowed, and === checks for equality with coercion not allowed
What this means is that when the types of the operands are different the values are not equal in case of ===
(strict equality) but in the case of ==
(loose equality) the values are coerced to a common type and then the values are compared for equality.
The algorithms for both these operators can be found here, I urge you to read it.
The ==
operator is not transitive, i.e.
// a == b and b == c does not mean a ==c. for example
new String("a") == "a" // true
"a" == new String("a") //true
new String("a") == new String("a") // false
This is because when comparing the new string object with primitive string “a”
, the object is coerced to it’s primitive value as per the loose equality algorithm, so the result is true, but when comparing two new string objects, the references to the objects are compared and not the values, and the references of both these objects are not same, so the comparison yields false.
The ===
(strict equals) and sameValue
algorithm, which is used in Object.is()
differ in-
- their treatment with signed zeroes, 0 and -0 and +0 are equal according to
===
but notObject.is()
and, NaN
values,NaN === NaN
is false butObject.is(NaN, NaN)
is true
Scopes in JavaScript
You can declare a variable that will belong to the current function scope, or the global scope if it is declared outside of any function.
function foo() {
var a = 100; //the variable a belongs to the scope of function foo
/*
function body
*/
}
var b = 1000;
//the variable b is outside any function so it belongs to the global scope
var c = a * 100; //Error:: a cannot be accessed outside foo
this is function scope in JS, one important concept is Hoisting.
Hoisting in JavaScript
In the above code example, the variable a
is declared in the function foo’s scope and also it is declared at the top, so it is accessible inside the complete scope of the function.
But, if it were to be declared not at the top, then also it will be accessible throughout the scope of the function foo, because
the var declaration is moved to the top of the enclosing scope.
This is called Hoisting.
The code is not rearranged or anything of that sort, it just appears that the declaration is moved to the top of the scope, but the details of this lie in the compilation process which we will discuss later.
Just the declaration is moved to the top, not the assignment. So if you try to use variable
a
before it’s declared, then you’ll get the value asundefined
, only after it’s assignment can you get the actual value of the variable.
Strict mode in JavaScript
The “strict mode” is basically a strict mode only, when strict mode is enabled the rules are tightened for certain behaviours, and these restrictions are seen to keep the code safer and also strict mode’s restrictions make your code more optimizable by the engine.
strict mode can be applied to any level, be it the entire file or a function scope, it’s determined by where you put the strict mode pragma
"use strict";
// this enforces strict mode
function foo() {
"use strict";
}
// this way strict mode is enabled for function scopes
The author recommends using strict mode.
Functions in JavaScript
We know what functions are and how they are declared and called and stuff.
But one interesting thing about functions in JavaScript is that functions are just values that are assigned to variables, just like other values (numbers, strings etc). Functions can be assigned to variables, passed to functions as parameters, returned from functions.
Immediately invoked function expressions (IIFE) in JavaScript
One way to invoke a function is by adding a () to the name of the function, there is another way
(function foo() {
/*
function body
*/
})();
this is an example of an IIFE, there are many use cases for IIFEs, they are discussed in subsequent books of the series.
Closures in JavaScript
Closure is a very important part of the language, it’s everywhere, and you might be using it without even knowing that your are using it.
Closure is basically when we are able to access a function’s scope’s variables even after it has finished executing, and all it’s variables’ memories should have been cleared.
Closures are experienced mostly when a function returns another function which uses the variables from the outer function’s scope, for instance
function outer() {
var x = 10;
return function inner(value) {
return x + value;
}
}
var y = outer();
// the execution of outer has finished here
// the memory assigned to variable x should be freed
// and we must not be able to access the value of x from here on
y(100);
// because of closures we can access x when we are calling y()
// this call returns 110 because x is accessible to the inner function
There are so many uses of closures, we will surely discuss them in the post of the second book Scopes & Closures. One example discussed in this book is this
function makeAdder(valueToAdd) {
function add(x) {
return x + valueToAdd;
}
return add;
}
var addOne = makeAdder(1);
var addFive = makeAdder(5);
addOne(2) // returns 3
addFive(2) // returns 7
Try to understand the above code, this is what makes closures so powerful.
Modules in JavaScript
One major use of closures is modules, modules help you create functionalities in which you can hide the private implementation details like functions and variables, and expose only the public API which is accessible from the outside. for instance
function App() {
function somePrivateMethod() {
// function body
}
function somePublicMethod() {
// function body
}
var somePublicVariable = 100;
var somePrivateVariable = "key";
var publicApi = {
somePublicVariable,
somePublicMethod,
}
return publicApi;
}
var appAPI = App();
// only public api is exposed to the user
// rest of the implementation details are hidden
appAPI.somePublicMethod() // is accessible
appAPI.somePrivateMethod() // ERROR: is not accessbile so is undefined
this
identifier in JavaScript
this
is another widely misunderstood concept of JS, people with knowledge of other languages like Java may believe that this
is related to object oriented behaviours, but it’s not the case in JavaScript.
this
reference in a function usually refers to an object, which object depends on how the function was called. It does not refer to the function itself.
There are various ways a function is called, we will discuss how this
identifier’s value differs in these cases briefly, this is a code snippet from the book
function foo() {
console.log(this.bar);
}
bar = "hello";
var obj1 = {
bar: "hi",
foo: foo,
};
var obj2 = {
bar: "obj2 hi",
};
foo();
/* logs hello without strict mode
** without strict mode the this identifier defaults to the global object
** in strict mode the this identifier defaults to undefined
*/
obj1.foo();
// logs hi -> this way of calling sets this identifier to obj1
foo.call(obj2)
// logs obj2 hi -> this way of calling sets this identifier to obj2
new foo();
// logs undefined -> this way of calling sets this identifier to an empty object
these are basic rules related to this
, there are chapters dedicated to it in next books, will discuss in detail in future posts.
Prototypes in JavaScript
Prototype mechanism in JavaScript is related to objects, when you try accessing a property on an object, if that property is not present in the object, JS tries to find another object from the internal prototype reference of the object to search for that property, it is like a fallback.
This internal prototype linking happens when the object is created, an example stated in the book is
var obj1 = {
bar: "bar",
}
var obj2 = Object.create(obj1);
obj2.foo = "foo";
obj2.foo; // "foo"
obj2.bar; // "bar"
// bar does not exist directly in obj2 but is present in obj1
// and obj1 is added as the prototype of obj2 using object.create()
// so JS looks for bar in obj1 and finds "bar"
This is just an introduction, prototypes is discussed in detail in the third book this & Object Prototypes.
Polyfilling and transpiling in JavaScript
There are new versions of the language released with new features every now and then, but the support for these features are not necessarily available instantly or sometimes even later.
To tackle this issue we have polyfilling, it works by adding a piece of code that is equivalent to the behaviour of the new feature, for instance the book shows how Number.isNaN()
is a new feature and might not work in old browsers, so to polyfill it we add this piece of code
if (!Number.isNaN) {
Number.isNaN = function isNaN(x) {
return x !== x;
};
}
It checks if the feature is available, if not it adds it.
Polyfilling works with new features, but in case of syntax changes that come with new updates of the language, polyfilling cannot be used, in that case transpiling the code is used.
Converting the new syntax code to old syntax code is transpiling, transpilers are used to transpile code, babel is a widely known and used transpiler.
Some interesting stuff about using JavaScript
As we know JS is mostly written to interact with environments like the browser. A lot of code that we write is not controlled by JavaScript because when we use stuff like document.getElementById()
it looks like document
is a JS object but it is not, it’s a global variable which is a special object which are called host objects
.
The getElementById
function we are calling on it also looks like a JS function, but it’s just an interface exposed to us by the DOM from the browser. In some browsers it might be implemented in JS itself but most commonly it is implemented in languages like C/C++ etc.
console.log()
and alert
are also not JS.
What the series holds for us developers?
This first book was a introduction to the language, all these concepts are explained extensively in the following books, the list of the books in YDKJS is-
- Scopes & Closures- this book aims to clear the fundamentals of scopes and closures in JS which are crucial for understanding JS. The book starts by debunking the misconception that JS is interpreted, it is not, and thus we dive deep into how the compiler reads our code and deals with function and variable declarations, then the book moves to understanding lexical scope and then finally closures and some use cases like the module pattern which we discussed a little bit earlier.
- this & Object Prototypes- talks about the this keyword which we discussed earlier, and the four rules that determine what will be it’s value in a function call, then it moves on to discuss prototypal inheritance in JS, and that it is not the same as classes and inheritance but rather behaviour delegation, the author argues that behaviour delegation is a better design pattern than classes and inheritance.
- Types & Grammar- this book talks about coercion, debates how it’s easy and essential to understand rather than ignoring and avoiding coercion and labelling it as a flaw of the language
- Async & Performance- This book discusses asynchronous programming in JS, clears out concurrent, async and parallel, discusses callbacks as a method for async programming then the issues with it and moves to discuss the solutions to those issues provided by promises and generators, Also covers web workers and SIMD, this book is designed to give you the tools and skills to write reasonable and performant JS code
- ES6 & Beyond- this book states that JS is ever evolving and thus focuses on new features added to the language in ES6
This is it for the first book of the series, It was a good read and learning for me, I hope this was helpful. Hoping to keep up and read the full series and get to know JS.
Done with the second book of the series, here is the link to the post.