Getting Into JS

Introduction

https://raw.githubusercontent.com/coodict/javascript-in-one-pic/master/js%20in%20one%20pic.png

Primer

  • Values
  • Operations
  • Variables
  • Expressions and Statements
  • Decisions
  • Loops
  • Functions

Values

  1. The difference between primitive values and non-primitive values is that one is actual just value and another is collection of values.
  2. You often come across null and undefined more often. Both them are mean to define emptiness in a programming language. The fact that JS has both of those is due to it’s history.
  3. The main difference between an Array and Object is that: In Arrays, developer doesn’t get to decide the position but while in Object, developer has the ability name the position.

Programming Primer

Operations

  1. If a operator operates on two operands. It’s Binary Operator 3+4. Likewise, if a operator operates on one operand, it’s Unary Operator !true

  2. It is called overloaded when same operator behaves in more than one way. For example, + is a operator, that can operate on operands which are both numbers and both strings.

    3 + 4;
    "saif " + "shines";
    
    // Both are valid in JS
    
  3. Operators

// Binary
+
-
<
||

// Unary
!
typeof

; is also a operator

typeof 42; // "number"

typeof "kyle"; // "string"

typeof kyle; // "undefined" | Because it is a varible?

typeof undefined; // "undefined"

typeof { age: 21 }; // "object"

typeof null; // "object" | This is a bug in JS for 20+ years ago. Unfortunately the way the webworks, we can't always fix it.

typeof [1, 2, 3]; // "object" |
interpolated string or template string

### Variables

It's a representation of some place in memory(which we don't care), for the developer to use it by name.

# Three Pillars of JS

1. Types and Coercion
2. Scope and Closures
3. this and prototype

## Types and Coercion

1. Primitive Types
    1. undefined
    2. string
    3. number
    4. boolean
    5. bigint
    6. object (not primitive)
    7. symbol
    8. *null ? (ît behaves different)*
    9. *function?*

    ```jsx
    var v;
    typeof v; // "undefined"

    v = "1";
    typeof v; // "string"

    v = 2;
    typeof v; // "number"

    v = true;
    typeof v; // "boolean"

    v = {}
    typeof v; // "object"

    v = Symbol()
    typeof v; // "symbol"

    typeof doesntExist; // "undefined"

    var v = null;
    typeof v;    // "object" OOPS! as #7

    v = function(){};
    typeof v;          // "function" hmm ?
    /*
    Although the function is not on it's own primitive type here, but just a type of Object;
    Turns out it so useful, because we need to know that value is whether a function
    which can be useful.
    */

    v = [1,2,3];
    typeof v; // "object" hmm?
    ```

2. Converting Types
3. Checking Equality

"I~~n JavaScript, everything is an object~~"

## NaN

There is very high misconception that `NaN` is not a number. But that is not literally true. In the below piece of code, `greeting` is clearly a string primitive type. But when did `Number.isNaN(greeting)` it returns `false`. This proves that, `NaN` i**s the value assigned only when illogical mathematical operations performed.**

```jsx
var greeting = "Hello, class!";

var something =  greeting/2; // ?!?!!

something; // NaN
Number.isNaN(something); // true

Number.isNaN(greeting); // false

Builtin fundamental Objects that we want to look at.

Use new:

  • Object()
  • Array()
  • Function()
  • Date()
  • RegExp()
  • Error()

Don’t use new:

  • String()
  • Number()
  • Boolean()
var yesterday = new Date("March 6, 2019");
yesterday.toUTCString();

var GPA = String(transcript.gap);
// "3.54"1

Converting Types

When working with + operator:

Number + Number = Number

String + Number = String

Number + String = String

String + String = String

/* Must Practice: When you are grabbing a number form DOM, you are actually getting
it in the form of a String primitive type. Mostly, JS does a implicit coercion on the
values, but when you need to perform operations always explicitly coerce it.
*/

function addAstudent(numStudents) {
  return numStudents + 1;
}

addAstudent(Number(studentsInputElem.value));

Falsy & Truthy

Which values if try to convert them into a boolean would become true/false ?

Boolean converstion

Example

Boolean("") would give false

Boolean(0) would give false

if (studentsInputElem.value) {
  // is this value is "" or a string
  numStudents = Number(studentsInputElem.value); // numStudents will b NaN
}

while (newStudents.length) {
  enrollStudent(newStudents.pop());
}

// BETTER WAY - More explicitly way of converting
if (!!studentsInputElem.value) {
  // explicitly giving in unary oprator
  numStudents = Number(studentsInputElem.value);
}

while (newStudents.length > 0) {
  enrollStudent(newStudents.pop());
}

Where, Implicit coercion can sometimes be useful,

var workshopEnrollment1 = 16;
var workshopEnrollment2 = workshopElem.value;

if (Number(workshopEnrollment1) < Number(workshopEnrollment2)) {
  // It ensure, both are converted into numbers, but it kind of looks cluttered
}

if (workshopEnrollment < workshopEnrollment) {
  // Implicitly converted.
}

If a feature is sometimes useful and sometimes dangerous and if there is a better option then always use the better option” — JS “The Good Parts” Crockford

Useful: when the reader is focused on what’s important

Dangerous: when the reader can’t tell what will happen

Better: when the reader understands the code

Checking Equality == vs ===

== checks value (loose)

=== checks value and type (strict)

🍏**==** allows coercion (types different)

🍏**===** disallows coercion (types same)

var studentName1 = "Frank";
var studentName2 = `${studentName1}`;

var workshopEnrollment1 = 16;
var workshopEnrollment2 = workshopEnrollment1 + 0;

studentName1 == studentName2; // true
studentName1 === studentName2; // true

workshopEnrollment1 == workshipEnrollment2; // true
workshopEnrollment1 === workshopEnrollment2; // true

📍When the types are already the same, == or === behave the same. No corner cases, whatsoever.

Let’s see another example,

var workshop1 = { topic: null };
var workshop2 = {};

if (
  (workshop1.topic === null || workshop1.topic === undefined) &&
  (workshop2.topic === null || workshop2.topic === undefined)
) {
  // ...
}

if (workshop1.topic == null && workshop2.topic == null) {
  //...
}

// Coericive Equality: null == undefined

❗The above code can be bit harder to understand. So I am trying to take notes for my future self.

  1. If both the workshops, 1 & 2 objects have topic property assigned with value null then if condition evaluates to True and executes the code in between.
  2. From what I have learnt so far
    1. === disallows coercion. So you are required to perform check whether each workshop1.topic / workshop2.topic is either null or undefined
    2. == allows coercion. So javascript itself will try to coerce —> workshop1.topic values null and workshop2.topic values undefined. Now, that coercion is done, workshop1.topic == null evaluates to true or workshop2.topic == null evaluates to false
  3. Finally, The second if conditional turns out to be more efficient and readable. This proves the point that when values involved are going to be different types (number, string, object, boolean,symbol,undefined ) then you will need to check equality wise.

👉== is not about comparisons with unknown types

👉== is about comparisons with known type(s). optionally where conversions are helpful.

StackOverflow

Which equals operator (== vs ===) should be used in JavaScript comparisons?

'' == '0'           // false
0 == ''             // true
0 == '0'            // true

false == 'false'    // false
false == '0'        // true

false == undefined  // false
false == null       // false
null == undefined   // true

' \t\r\n ' == 0     // true

Scope

Where to let JS look for things? Things like variables in the memory. Is it local execution context? Or is it in global execution context?

  1. Nested Scope
  2. Closure

undefined vs. undeclared

These are two concepts around emptiness. undefined and undeclared looks like they are more or less the same things. But they a significantly different.

  1. undefined is probably a variable that is declared somewhere in the code but is not has been assigned with any value.
  2. undeclared is not even declared. JS will try to look for the value under scope rules during the run time.

Function expressions

Themselves being assigned as values. They can be passed around.

While I already know, the difference between Anonymous and non-Anonymous functions are Kyle prefers to suggest to use named or non-anonymous functions as much as possible.

For example,

var ids = person.map((person) => person.id);

var ids = person.map(function getId(person) {
  // This is more understandable to the reader.
  return person.id;
});

//*********************

getPerson()
  .then((person) => getData(person.id))
  .then(renderData);

getPerson() // Preferred.
  .then(function getDatafrom(person) {
    return getData(person.id);
  })
  .then(renderData);

IIFE - Immediately Invoked Function Expressions

var teacher = "Kyle ";

// way 1
(function anotherTeacher() {
  var teacher = "Suzy";
  console.log(teacher); // Suzy
})();

// way 2
(function () {
  teacher = "Saif "; // Notice that teacher is not re-declared
})();

console.log(teacher);

Here are super interesting findings

  1. In way 1, when teacher variable is declared again, it is created under a newer scope and when you log teacher, the console prints Suzy
  2. Yet if way 2 is not there, and way 1 only exists, it will not effect teacherin global scope at all. Console will still log "Kyle"
  3. In way 2 if its’ there, then teacher will be overwritten from "Kyle " to "Saif "

Now the above 3 steps are not needed as ES6 now supports block scoping. Simply use let

Preferably, use "var " at function level and "let " at block level.

Another Best practice,

function formatStr(str) {
  {
    let prefix, rest;
    prefix = str.slice(0, 3);
    rest = str.slice(3);
    str = prefix.toUpperCase() + rest;
  }

  if (/^FOO:/.test(str)) {
    return str;
  }

  return str.slice(4);
}

If you look at the above code, there is a simply a block inside which prefix and rest are used. It is better to limit them to that particular block scope because, they are not needed at function level anyway.

Closure

Closure is when a function “remembers” the variables outside of it, even if you pass that function else where.

function ask(question) {
  setTimeout(function waitAsec() {
    console.log(question);
  }, 100);
}

ask("what is closure?");

this & Prototypes

  1. this
  2. Prototypes
  3. class

this

You may have seen the this keyword at many places. Maybe within functions.

A function’s this references the execution context for that call, determined entirely by how the function was called. So a this - aware function can thus have a different context each time it’s called, which makes is so much flexible and reusable.

var workshop = {
  teacher: "kyle",
  ask(question) {
    console.log(this.teacher, question);
  },
};

workshop.ask("What is implicit binding?");

// kyle What is implicit binding

So here, How the function was called broadly sums up into 4 ways,

  1. Implicit Binding
  2. Explicit Binding
  3. Yet to know (Not covered in the course) — Looks like new keyword
  4. Yet to know (Not covered in the course)

Implicit Binding

The above code example very clearly demonstrates this. The ask()method here is a this-aware function. Which means function definition has thisin it.

  1. workshop is a object. workshop.ask() is a statement. This statement is how the ask() function is called.
  2. workshop object .(dot) ask will implicitly bind the this in ask() function definition to contain the context of workshop object

Explicit Binding

// Let's look at another example

function ask(question){
	console.log(this.teacher,question);
}

function otherClass(){
		var myContext = {
			teacher: "Suzy";
		};
	ask.call(myContext,"Why?");
}

otherClass();
  1. In the above case, Observe that: how the this aware function is called is different.
  2. function ask() is present in the global scope. otherClass() has a statement that invokes the ask method.
  3. ask.call(myContext,"why?"); explicitly binds the context object to ask() method. So this will contain the context of myContext object.

Prototypes

function Workshop(teacher){
	this.teacher = teacher;
}

Workshop.prototype.ask(question){
	console.log(this.teacher,question);
}

var deepJS = new Workshop("Kyle");
var reactJs = new Workshop("Suzy");

deepJS.ask(" Is 'prototype' a class?");
// Kyle Is prototype a class?

reactJS.ask("Isn't 'prototype' ugly?");
// Suzy Isn't 'prototype' ugly

This is very clear and interesting example.

  1. You create a Workshop function in global scope. It is thisaware function.
  2. To this Workshop’s prototype method, developer adds a method ask() . Notice that ask()method references to this.teacher which is part of Workshop function.
  3. deepJS and reactJS are just two variable which are assigned with respective Instances.
  4. Now when for example, deepJS.ask() is invoked. Notice ask() is not part of Workshop function’s instance but it is part of Workshop’s prototype object’s instance. When ask()executes, it is called with deepJS context(which is instantiated by “Kyle”).
  5. Thus it prints, // Kyle Is prototype a class?

Class keyword

class Workshop {
  constructor(teacher) {
    this.teacher = teacher;
  }
  ask(question) {
    console.log(this.teacher, question);
  }
}

var deepJS = new Workshop("Kyle");
var reactJS = new Workshop("Suzy");

deepJS.ask("Is 'prototype a class?");
reactJS.ask("Isn't protoype  ugly?");

Practice

class Bookshelf {
  constructor() {
    this.favoriteBooks = [];
  }

  addFavoriteBook(bookName) {
    if (!bookName.includes("Great")) {
      this.favoriteBooks.push(bookName); // this
    }
  }

  printFavoriteBooks() {
    console.log(`Favorite Books: ${this.favoriteBooks.length}`);
    for (let bookName of this.favoriteBooks) {
      console.log(String(bookName)); // Types and coercion
    }
  }
}

function fakeAjax(url, cb) {
  setTimeout(function fakeLoadingDelay() {
    cb([
      "A Song of Ice and Fire",
      "The Great Gatsby",
      "Crime & Punishment",
      "Great Expectations",
      "You Don't Know JS",
    ]);
  }, 500);
}

var myBookshelf = new Bookshelf();
var BOOK_API = "https://some.url/api";

function loadBooks(myBookshelf) {
  fakeAjax(BOOK_API, function getBook(books) {
    // cb has closure over getBook
    for (let book of books) {
      myBookshelf.addFavoriteBook(book);
    }
    myBookshelf.printFavoriteBooks();
  });
}

loadBooks(myBookshelf);

Wrapping Up

Only way to learn JavaScript is by Writing it.