Advanced Javascript

Updated 2020-06-22

Most of these notes come from Asim Hussain's course Advanced Javascript https://www.udemy.com/course/javascript-advanced/.

Useful links

Basics

  • There is a Chrome feature called snippets (under Dev Tools > Sources).

    • {} is a format button.
    • Ctrl + / comments or uncomments lines. Chrome Snippets
  • use polyfills to provide/guarantee new functionality in older browsers, e.g. Promise polyfill
  • 'use strict'; to enter strict mode where variables must be defined before use, there are restrictions on delete, and extra eval safety ensures eval effects don't leak out of the block
  • primitive types are passed by value, complex types are passed by reference
  • functions have a hidden arguments object (not actually an array)
  • [1,2,3,4].slice(1) => [2,3,4]
  • function restOperatorExample(a, ...b) { console.log('a', a, 'b', b); }

    • restOperatorExample('hello', 1, 2, 3); // a hello b (3) [1, 2, 3]
  • var spreadArr = [4, 5, 6]; var a2 = [1, 2, 3, ..spreadArr]; // has 1 to 6

    • can be used for by value copy var ar1 = [1, 2]; var ar2 = [...ar1]; // copied
  • function a(b, ...c) called by a(y, ...z) - use the operator in both definiton and when calling
  • template strings e.g. console.log(i18n${amount}:c in your bank account.)
  • create template string tag function h1(strings, ...values) {...} h1hello ${name}

Types & Equality

  • ES5: Boolean Number String Null Undefined Object
  • typeof returns a string.
  • typeof(null) is unusual because it returns "object".
  • undefined is used by javascript to indicate that a variable is a missing param, unknown property, uninitialised. null is more likely? to be used by developers.
  • === (strict equality) checks types as well as values
  • JavaScript coercsion is complex
  • == vs === https://dorey.github.io/JavaScript-Equality-Table/
  • typeof(NaN) is number
  • NaN === ANYTHING is false, so a !== a only when it has a value of NaN. (that also applies to NaN == NaN)
  • [1,2,NaN].indexOf(NaN) returns -1, (if you are checking for NaN in an array use ES7's Array.prototype.includes())

4. Scopes & variables

  • variables declared outside any function are global variables
  • alternatively add to window, window.moo = 1;
  • local scoped variables - only available within the function
  • if a for loop is in the global scope, the iterator and contained variables are global too
  • const and let define block scoped variables
  • var doesn't care
  • variable hoisting - javascript hoists the declaration but not initialisation to the top of the block
  • functions are also hoisted
  • scope chain is defined lexically (order written)
  • IIFE

    • global namespace is polluted by whatever files are loaded in
    • variables from different files can overwrite each other
    • wrap a function (function() { ... })(); to make it an IIFE - Immediately Invoked Function
    • we can wrap everything in IIFE in included files
  • Closures

    • Scope: Local, Closure, Global. These three categories can be seen in the browser debugger
    • Variable copying behaviour - closures keep a reference to anyting they need
    • It's NOT a copy of the variable, it's a reference to it
  • variables can be passed into an IIFE

5. destructuring and looping

  • destructuring

    • const obj = {first: 'Asim', last: 'Hussain', age: 42};
    • const { first: f, last: l} = obj;
    • const { first, last } = obj;
  • array destructuring

    • const arr = ['a', 'b'];
    • const [x, y] = arr;
    • works in function parameters
    • can define default values

      • function f({x=0}) { console.log(x); }
      • requires the function to be called with at least {}
  • loops

    • for
    • forEach(function(value) { ... }), no break or continue
    • for (let prop in obj) { console.log(prop); } // for-in loop
    • for (let value of array) { console.log(value); } // for-of loop

6. this

  • use strict stops the default this object being the window object
  • var self = this; or var that = this;
  • functions can be treated just like any other object
  • f.call(); // just calls function f
  • f.call(...); // passes ... as this
  • function a(b,c,d); a.call(1,2,3,4); // 1 is this, bcd =234
  • apply passes parameters as an array, useful when a function takes a variable number of parameters
  • bind can lock down the value used for this at the time of definition
  • fat arrow function, nicer syntax, but the key is the this keyword. fat arrow gets "this" from lexical? context not calling context. If you do want the calling context as the this property, make sure to use function(), not fat arrow.

7. object orientation

  • Object.create(prototype, propertiesObject);

    • select a protype object for this (inherit), and bootstrap properties
  • Classical: class, instance. Prototypal: instance, instance. Pseudo-Classical pattern: Prototypal pattern:
  • Pseudoclassical:

    • new keyword provides "this" to the callee
    • struggling with this one a bit
  • function is looked for locally, then prototype, then prototype's prototype ...
  • adding a function to a prototype can save memory - rather than adding to thousands of individual objects for example
  • by default evertyhing is public
  • full_name function - not sure i would spot this in real code
  • don't need to use "this." to refer a property provided through constructor, if you are within a closure in the body of the constructor. this allows simulation of private variables.
  • Professional.prototype = Object.create(Person.prototype)

    • Professional's prototype points to {}
    • {}'s proto points to the {} that Person was pointing at already
  • Prototype OO pattern - uses native JavaScript features rather than try to imitate other languages
  • Read more about the Object.Create function in docs
  • Can provide { 'name' : { 'value': 'bob' }} kind of parameter to Object.create bu tthere is another solution
  • Create a factory
  • Three options, create an init function on the objectitself, provide the params to Object.create, or create a factory.
  • Ways to add functions - bootstrap, at the time of .create(), add them via Professional.init for example, use a factory function
  • In Chrome when debugging we can see the prototype by looking at the current object's proto
  • Constructor pattern is most common. Coming from Java & C++ it feels more natural. Allows constructs like private variables.
  • Prototype pattern is very easy to understand, it's simple, and feels more natural to JavaScript.
  • ES6 class & extends:
  • Code within class X { } is always strict
  • declare variables on a class. don't need let or var.

    • class Person { _fNmae= ''; _lName = ''; }
  • "constructor" is a special function on a class
  • getter uses get keyword. Must be called as a property, without ()
  • implement a setter set firstName(name) we can implement guard clauses etc. here.
  • extends

    • class Student extends Person
    • call super(..., ...) to call base class constructor
    • can call base class methods inside using super.x()
    • can override base class methods just by making another with the same name
  • these keywords just help us avoid boilerplate that we had to use before. still using the same old prototypal inheritance chain under the hood.
  • Can mix and match ES5, ES6 classes

8. Asynchronous programming

  • callback isn't necessarily async
  • setTimeout(cb,0)
  • node standardised error first callbacks - recommended pattern.
  • callback hell - that nested horror has a name
  • const promise = new Promise((resolve, reject) => { });
  • doAsyncTask().then(() => {}, () => {}); success & error handler
  • can create immediately resolved Promise.resolve('done'); or immediately rejected promise Promise.reject('failed');
  • promises are truly asynchronous, callbacks aren't
  • promise chaining vs forking
  • throw will automatically reject a promise, and even if you are in a then. Even better than using reject, use a .catch handler.
  • finally will run no matter what happens.
  • Promise.all(promises).then((values) => console.log(values))
  • async/await
  • can attach a then handler to an async function
  • returning x in async is like calling resolve(x) in a promise
  • can't use try...catch with Promises but can with async/await
  • using async/await has potential performance hit compared to Promises (depending on how it is used of course)
  • no point using async without await

9. Networking

  • in the request header of a GET Origin: moo.com, response: Access-Control-Allow-Origin: ???

    • if this is moo.com, allowed
    • if it is google.com, blocked by browser
    • if it is *, it is allowed
  • only response gets blocked by CORS
  • for other types of request PUT, POST, it sends a preflight request
  • OPTIONS

    • Origin: moo.com
    • Access-Control-Request-Method: PUT
  • reponse

    • Access-Control-Allow-Origin: moo.com
    • Access-Control-Allow-Methods: PUT
  • test-cors.org
  • JSONP predates CORS. Only GET requests work.

    • script tags don't have a limitation on domain
    • jsonp.js must be wrapped in a function
    • run it as a callback
    • ususally you request a certain function name by parameter
    • format=jsonp&callback=weather (totally depending on server support)
    • usually you would use a library e.g. jQuery to do this
    • this approach is till used because it's so simple

10. Events

  • event bubbling vs event capturing
  • when you click somewhere on a page, event goes from

    • root->target (event capturing phase), then
    • target->root (event bubbling phase)
  • when you add an event listener by default it listens to event bubbling phase
  • addEventListener("click", function (event) { }, false); // true = capturing, false or undefined = bubbling
  • as the event is executing on each item, it's synchronous
  • stopPropagation stops the event moving to the next element
  • preventDefault doesn't stop the event propagation, but it prevents the default behaviour in whatever element you interacted with