JavaScript is a single-threaded language that can be non-blocking.
For the code below:python
const f()=>{ const ff()=>{ console.log('a'); } } f();
f()
, ff()
and console.log('a')
will be push in the call stack one by one and be popped out in the reverse order (first in last out).web
We can do asynchronous programming with setTimeout()
, for example:express
console.log('1'); setTimeout(() => { console.log('2'); }, 2000); console.log('3'); //>> 1, 3, 2
console.log('1')
is pushed in the call stack and executed, then popped out;setTimeout()
was pushed in the call stack. Bc it is not part of JavaScript but part of Web APIs, it triggers the Web APIs, then is popped out of the call stack. And the Web APIs starts a timer of 2 seconds.console.log('3')
is pushed in, executed and pushed out.setTimeout
to the callback queue, ready to rune the content inside of setTimeout()
.console.log('2')
in the call stack, when it is done, the callback is popped out. Now everything is empty.⚠️ if the time in setTimeout()
is set to 0, the output order is still the same, bc only if the call stack is empty, it will push the first thing in the queue to the stack and do some work.app
⚠️ JavaScript is synchronous in the sense that it is single-threaded but can have asynchronous code by using the event queue on things like AJAX requests.async
The latest ECMAScript standard defines seven data types (built-in types):ide
Six primitive data types:oop
Array and Function are two specialized version of the Object type. Use typeof
operator to figure out the type of the value that held by a variable (only values have types, variables are only containers):ui
var a; typeof a; // >>"undefined" var b = null; typeof b; // >>"object" var c = undefined; typeof c;// >>"undefined" typeof function() {} === 'function'; // >>"True" var arr = [1, 2, 3]; typeof arr; // >>"object" // use Array.isArray or Object.prototype.toString.call // to differentiate regular objects from arrays
⚠️ typeof null
returns Object
instead of null
is a long-standing bug in JS, but one that is likely never going to be fixed. Too much code on the Web relies on the bug and thus fixing it would cause a lot more bugs!this
⚠️ The typeof
of arrays is object.spa
⚠️ A variable value can be undefined
by 1. explicitly assignment, 2. declared with no assignment, 3. function return with no value, 4. usage of void
operator
A non-boolean value can always be coerced to a boolean value. The specific list of "falsy" values are as follows:
There are four equality operators: ==, ===, !=, and !==.
Overall, the difference between == and === is that == checks the equality with coercion allowed, and === checks the equality without coercion allowd ("strictly equality").
check Ecma-262 Edition 5.1 Language Specification here
false
true
, else:elif: typeof x is 'number'
false
(⚠️NaN === nothing)true
true
false
elif: typeof x is 'string'
true
false
elif: typeof x is 'boolean'
true
false
elif: typeof x is 'object'
true
false
true
ToNumber(x) == y
, and vice versaToNumber(x) == y
, and vice versa ⁉️ToPremitive(x) == y
, and vice versa ⁉️false
Relational Comparison operators incluede <, >, <=, and >=.
var a = 32; var b = "31"; var c = "c"; var d = "111"; a > b; // >>true c > b; // >>true d > b; // >>false a > c; // >>false a < c; // >>false a == c; // >>false
As for x > y, concluded from the cases above:
if: one of the operands is 'number' and the other one is 'string':
The arrays in JS is similar with those in python with use square brackets []. Some predefined methods are listed below:
var li = [1, 2, 3, 4, 5]; var empty_li = []; //shift() var first_of_li = li.shift(); //removes the first element and returns that element console.log(li) //>> [2, 3, 4, 5] //pop() var last_of_li = li.pop(); //removes the last element and returns that element console.log(li) //>> [2, 3, 4] //push() var new_length = li.push(6, 7, 8); //adds items to the end and returns the new lenght of that array console.log(li) //>> [2, 3, 4, 6, 7, 8] //concat() var merged_li = li.concat([9, 10]);//concatenate two arrays without changging them //sort() //sorts the elements of an array in place and returns the array. The default sort order is according to string Unicode code points. var months = ['March', 'Jan', 'Feb', 'Dec']; months.sort(); console.log(months) //>> ["Dec", "Feb", "Jan", "March"] var array1 = [1, 30, 4, 21]; array1.sort(); console.log(array1) //>> [1, 21, 30, 4] //trim() //removes whitespace from both ends of a string. Whitespace in this context is all the whitespace characters (space, tab, no-break space, etc.) and all the line terminator characters (LF, CR, etc.). var greeting = ' Hello world! '; console.log(greeting.trim()); //>> "Hello world!"; //join(separator) //creates and returns a new string by concatenating all of the elements in an array (or an array-like object), separated by commas or a specified separator string. //The separator is converted to a string if necessary. If omitted, the array elements are separated with a comma (","). If the separator is an empty string, all elements are joined without any characters in between them. //If an element is undefined or null, it is converted to the empty string. var a = ['Wind', 'Rain', 'Fire']; a.join(); // 'Wind,Rain,Fire' a.join(', '); // 'Wind, Rain, Fire' a.join(' + '); // 'Wind + Rain + Fire' a.join(''); // 'WindRainFire' //The following example joins array-like object (arguments), by calling Function.prototype.call on Array.prototype.join. function f(a, b, c) { var s = Array.prototype.join.call(arguments); console.log(s); } f(1, 'a', true); //>> "1,a,true"
As for looping, instead of using forEach
which simply loop an array but does nothing to its elements, map
, filter
and reduce
are way more convenient.
map
It iterates elements and applying the operations to each of them, in the expectation that the operations return elements. e.g.:
/* double the elements in array */ var array = [5, 2, 10, 26]; var mapArray = array.map(ele => ele * 2);//shortform of 'array.map(ele => {return ele*2;})' coz there is only a single line //using forEach var double = []; array.forEach(ele => {double.push(ele * 2);});
ATTENTION: usaully 'map()' function will not change the original array bc the first argument stores the value of the current element; however, when the elements are objects, the first argument stores its address, so the original array will be changed. e.g: in following codes, array
's value will changed into map_Array
after using the map() function.
const array = [{ username: "john", team: "red", score: 5, items: ["ball", "book", "pen"] }, { username: "becky", team: "blue", score: 10, items: ["tape", "backpack", "pen"] }, { username: "susy", team: "red", score: 55, items: ["ball", "eraser", "pen"] }, { username: "tyson", team: "green", score: 1, items: ["book", "pen"] }, ]; //create a new list with all user information, but add "!" to the end of each items they own. var mapArray = array.map(t => {t.items = t.items.map(itt => itt+"!"); return t;})
filter
As its name suggests, it filters elements with conditions. As with map
, it has to return something.
/* find the elements > 5 in an array */ var array = [5, 2, 10, 26]; var filterArray = array.filter(ele => ele > 5);//shortform of 'array.filter(ele => {return ele > 5;})'
reduce
It is another powerful function which you can even use to do 'map' and 'filter'. It use an 'accumulator' to store the results caused in the body of the function.
/* sum all the elements of an array */ var array = [5, 2, 10, 26]; var reduceArray = array.reduce((accumulator, ele) => ele + accumulator, 0);//shortform for 'array.reduce((accumulator, ele) => {return ele + accumulator;}, 0)
The objects in JS are declared similarly with dicts in Python, and other than using '[]' to query a property, '.' is also applicable. However, when using '[]', the expressing within should be a string. e.g.:
var empty_user = {}; var user = { name: "John",//property name can be unquoted if there is no space between words age: 34, isMarried: False spells: ["blabla", "blablabla"], //a function inside an object is a method shout: function(){ console.log("aaaaaaa")//no comma }; var user_age = user["age"]; var user_name = user.name;
Add a property:
Adding a property is just like query a propety using '[]' or '.':
user.sex = "male"; user["hobby"] = "soccer";
Different from the primitive types defined by the programming language, objects are of reference type. Arrays are also objects.
'Context' is an object-based concept whereas 'scope' is function based.
'Context' refers to an object. Within an object, the keyword 「this」 refers to that object (i.e. 「self」), and provides an interface to the properties and methods that are members of that object. When a function is executed, the keyword 「this」 refers to the object that the function is executed in.
'Scope' pertains to the variable access of a function when it is invoked and is unique to each invocation
An example:
From the example above, we know that 'this' in the JavaScript refers to the window, so that console.log()
and a()
are both its methods and can be called with 'window.' or 'this.'.
obj.a()
refers to obj
.
The instantiation of an object is the creation of a real instance of an object and reuse of codes. Before the instantiation, we shall create a class with a constructor which defines the template. As the code shows below, the constructor allow us to customize proprieties 'name' and 'type' every time we create a Player object like what a consturctor does in C++.
class Player{ constructor(name, type){ this.name = name; this.type = type; } introduce(){ console.log(`Hi, I'm ${this.name}, I'm a ${this.type}`) //syntax template string with `` } }
To create a Wizard class which includes all the proprietyies and methods that Player has (a child of Player):
class Wizard extends Player{ constructor(name, type){ super(name, type); //call functions on the parent } play(){console.log(`I'm a wizard of ${this.type}`);} }
Now we can do instantiation with the key word new.
To get a better interpretation of context, lets run the code below:
class Player{ constructor(name, type){ console.log('player',this); this.name = name; this.type = type; console.log('player',this); } introduce(){ console.log(`Hi, I'm ${this.name}, I'm a ${this.type}`) //syntax template string with `` } } class Wizard extends Player{ constructor(name, type){ super(name, type); //When used in a constructor, the super keyword appears alone and must be used before the this keyword is used console.log('wizard', this); } play(){console.log(`I'm a wizard of ${this.type}`);} } var player = new Player("Dali", "painter"); var wizard = new Wizard("Elio", "blackMagic");
In JavaScript, variable names (including function names) must be valid identifiers.
certain words cannot be used as variables, but are OK as property names. These words are called "reserved words," and include the JS keywords ( for, in, if, etc.) as well as null, true, and false.
As JavaScript does not have a module system, we have lots of ways to import and export modules:
Inline scripts -> Script tags -> IFFE -> ES6 + Webpack2
Inline scripts:
Script tags:
IIFE (Immediately-Invoked Function Expressions):
Pronounced as "Iffy", the key to IIFE pattern is taking a function and turning it into an expression and executing it immediately.
!function() { alert("Hello from IIFE!"); }();// there is () // ! can be replaced by basically any unary operators such as +, -, ~ // ! can be replaced by "void" // those unary operators and "void" forces JavaScript to treat whatever coming after ! as expression
The classical IIFE style:
// Variation 1 (recommanded) (function() { alert("I am an IIFE!"); }()); // Variation 2 (function() { alert("I am an IIFE, too!"); })(); // These tow stylistic variations differ slightly on how they work. // IIFE with parameters (function IIFE(msg, times) { for (var i = 1; i <= times; i++) { console.log(msg); } }("Hello!", 5)); // IIFE with returns var result = (function() { return "From IIFE"; }()); alert(result); // alerts "From IIFE"
Cases where the parentheses can be omitted (not recommended)
// Parentheses around the function expression basically force the function to become an expression instead of a statement. // But when it’s obvious to the JavaScript engine that it’s a function expression, we don’t technically need those surrounding parentheses as shown below. var result = function() { return "From IIFE!"; }(); // In the above example, function keyword isn’t the first word in the statement. So JavaScript doesn’t treat this as a function statement/definition.
Any variables and functions declared inside the IIFE are not visible to the outside world. e.g.:
(function IIFE_initGame() { // Private variables that no one has access to outside this IIFE var lives; var weapons; init(); // Private function that no one has access to outside this IIFE function init() { lives = 5; weapons = 10; } }());
In this way, we can add variables and functions to a global object ((the only one exposed to the global scope) inside an IIFE:
var myApp = {}; (function(){ myApp.add = function(a, b) { return a + b; } })();
So the advantages of using IIFE includes:
Reduce the look-up scope. Without using IIFE, global objects such as window
, document
and jQuery
in the scope defined by the function have to look up their properties from inside to outside. IIFE can pass them inside of the function directly.
(function($, global, document) { // use $ for jQuery, global for window }(jQuery, window, document));
An Example of module pattern using IIFE and closure:
var Sequence = (function sequenceIIFE() { // Private variable to store current counter value. var current = 0; // Object that's returned from the IIFE. return { getCurrentValue: function() { return current; }, getNextValue: function() { current = current + 1; return current; } }; }()); console.log(Sequence.getNextValue()); // 1 console.log(Sequence.getNextValue()); // 2 console.log(Sequence.getCurrentValue()); // 2
Where IIFE makes the variable 'current' private and the closure makes it accessble.
// js1 "./add" module.exports = function add(a, b){ return a+b; } // js2 var add = require("./add");
Browserify is a module bundler that runs before putting the website online, it bundles all JS files into one massive file. The common JS syntax instructs it to do it automatically.
// js1 "./add" export const add = (a, b) => a + b; // or (set the function as default function) export default function add(a, b){ return a + b; } // js2 import {add} from './add'; // destructuring // or (no need to destructuring since we import hte default function) import add from './add';
Webpack is a bundler like Browserify which allows us using ES6 and bundle the files into one or multiple filesbased on your needs.