JavaScript Tips

2019-04-26

Tips for JavaScript.

What's weird

  • Number comparison

    console.log(1 < x < 3); // true (this syntax is valid)
    // (1 < x) < 3
    // (true or false) < 3
    // (1 or 0) < 3
    // true
    
    console.log(1 < 5 && 5 < 3); // false
    // true && false
    // false
    
  • Some unexpected behaviors

    console.log(1 + 2 + '3'); // 33
    console.log('1' + 2 + 3); // 123
    
    console.log(0.1 + 0.2); // 0.30000000000000004
    console.log(0.1 + 0.2 === 0.3); // false
    // you should use decimal.js: https://github.com/MikeMcl/decimal.js/
    
    console.log(true == 1); // true
    console.log(true == '1'); // true
    console.log(true === 1); // false
    
    console.log(Boolean([])); // true
    console.log([] == true); // false
    console.log([] == false); // true
    if ([]) { /* this will be executed */ }
    if ([] == true) { /* but this won't */ }
    
    console.log(Boolean({})); // true
    console.log({} == true); // false
    console.log({} == false); // false
    if ({}) { /* this will be executed */ }
    
  • Not so asynchronous

    setTimeout(() => {
      console.log('hi');
    }, 0);
    
    // It will say hi after this line finishes,
    // and this line takes about 1 second.
    for (let i = 0; i < 1e9; i++) {}
    

var, let and const

  • var and let

    { var a = 1; }
    console.log(a); // 1
    
    { let b = 2; }
    console.log(b); // error
    
    for (var i = 0; i < 10; i++);
    console.log(i); // 10
    
    for (let j = 0; i < 10; j++);
    console.log(j); // error
    
    var x = 1;
    console.log(window.x); // 1
    
    let y = 2;
    console.log(window.y); // undefined
    
  • const

    const c = 10;
    c = 20; // error
    
    const o = {c: 10};
    o.c = 20; // ok
    o.d = 30; // ok
    o = 40;   // error
    
    { const k = 10; }
    console.log(k); // error
    

What the fuck is this

  • If this is used in a function, this will be the object where the function is called from, else the global object.
  • Global object for browsers is window, for Node.js is global.
window.value = 'global';
let value = 'local';
let obj = {value: 'object'};


function printThisValue() {
  console.log(this.value);
}

function printValue() {
  console.log(value);
}

obj.printThisValue = printThisValue;


printValue();         // local
printThisValue();     // global
obj.printThisValue(); // object

[1].forEach(obj.printThisValue);        // global
[1].forEach(_ => obj.printThisValue()); // object

Promise

  • resolve() calls the function inside .then():

    function sleep(seconds) {
      return new Promise(resolve => {
        setTimeout(() => {
          resolve();
        }, seconds * 1000)
      });
    }
    
    sleep(1).then(() => {
      console.log('hello');
    });
    
    // chaining then()
    sleep(1)
      .then(() => 123)
      .then(console.log); // 123
    
  • reject() is used to handle exceptions:

    function divide(a, b) {
      return new Promise((resolve, reject) => {
        if (b == 0) {
          reject('divided by zero');
        }
        resolve(a / b);
      });
    }
    
    divide(1, 2)
      .then(console.log) // 0.5
      .catch(console.log);
    
    divide(1, 0)
      .then(console.log)
      .catch(console.log); // divided by zero
    
    divide(1, 0)
      .catch(() => 'oh my god')
      .then(console.log); // oh my god
    

Asynchronous

  • JavaScript code runs in one thread, so if theres an infinite loop in it, it blocks forever.
  • Sync function calls will not interrupt the execution.
  • An async function call will be interrupted whenever it reachs the await statement.
  • await is only available in async functions.
  • Promise is awaitable.
  • Actually what async functions return are just Promise.
  • In async functions, return is equivalent to resolve(), and throw is equivalent to reject().
function sleep(seconds) {
  return new Promise((resolve, reject) => {
    setTimeout(resolve, seconds * 1000);
  });
}

async function sleepAsync(seconds) {
  await sleep(seconds);
}

async function kitty() {
  return 'kitty';
}

async function main() {
  console.log('hello');
  console.log(await kitty());

  // Sleep 1 second
  await sleep(1);
  console.log('hi');

  // Sleep 1 second
  await sleepAsync(1)
  console.log('hihi');

  // Sleep 1 second
  await new Promise(r => setTimeout(r, 1000));

  if (Math.random() > 0.5)
    return 'hihihi'; // resolve('hihihi')
  else
    throw 'hahaha'; // reject('hahaha')
}

main() // this is a Promise
  .then(console.log)
  .catch(console.log);

console.log('this will appear between hello and kitty because main() awaits kitty()');

Generators

  • Basic usage:

    function* gen() {
      console.log('start');
    
      let x = yield 'one';
      yield x;
    
      return 'two';
      return 'three';
    }
    
    let g = gen();
    
    g.next(); // prints 'start'
    //=> {value: 'one', done: false}
    
    g.next('this is x');
    //=> {value: 'this is x', done: false}
    
    g.next();
    //=> {value: 'two', done: true}
    
    
  • Tips

    function* iter(n) {
      for (let i = 0; i < n; i++)
        yield i;
    }
      
    console.log(...iter(4));      // 0 1 2 3
    
    [...iter(4)];                 //=> [0, 1, 2, 3]
    Array.from(iter(4));          //=> [0, 1, 2, 3]
    
    [...Array(4).keys()];         //=> [0, 1, 2, 3]
    Array.from(Array(4).keys());  //=> [0, 1, 2, 3]
    

Copying

  • Array

    let a = [1, 2, 3];
    let b = [...a];     // [1, 2, 3]
    let c = a.slice();  // [1, 2, 3]
    
    a == a; // true
    a == b; // false
    a == c; // false
    
  • Object

    let o = {name: 'djosix'};
    let p = Object.assign({}, o); // {name: 'djosix'}
    
    p == o; // false
    

Class

  • The old way

    function Test(value) {
      this.value = value;
    
      this.print = function() {
        console.log(this.value);
      };
    }
    
    new Test('hello').print(); // hello
    Test(); //=> undefined
    
    // Inheritance
    function Child(...args) { Test.call(this, ...args); }
    new Child('kitty').print(); // kitty
    
  • The new syntactic sugar

    class Test {
      value = 'original value';
        
      constructor(value) {
        this.value = value;
      }
    
      print() {
        console.log(this.value);
      }
    }
    
    new Test('hello').print(); // hello
    Test(); // error
    
    
    class Child extends Test {}
    new Child('kitty').print(); // kitty