JavaScript

JavaScript = ECMAScript + DOM (window.document) + BOM (window.document, window.navigator, window.location, window.history, window.screen, etc)

Good Tutorials

Type

JavaScript Types = Primitive types + Reference Type

Primitive type

  • null, undefined, boolean, number, string, symbol (ES6)

  • immutable, fixed size memory

Reference Type

  • Object, array, function

  • dynamic size

typeof returns string.

Array

var a = Array(4); // array length of 4, but elements are all undefined
var a = Array('4'); // returns [4]
var a = Array()
var a = Array('a', 'b');
var a = ['a', 'b']
let arr = Array.from($('a')); // create from nodelist
  • can change length, like C# list: a.push('c')

  • can hold different data type

  • the length of the array is one more than the highest index. it's not always counting how many members in the array!

  • If you query a non-existent array index, you get undefined

Callback is usually: element, index, array while element is required

Value Most of the operations will mutate the array.

  • copy itself to itself, [start, end) not including the endIndex: copyWithin(targetIndex, startIndex, endIndex)

  • Reverse: Array.prototype.reverse()

  • Sort: arr.sort()

    • if no function is provided, element is converted to string to sort, so [10, 5].sort() is still [10, 5]

    • sort numbers: arr.sort((a, b) => a - b);

  • head: shift() + unshift()

  • tail: pop() + push()

  • Fill with value, [start, end) arr.fill

  • *Remove and insert in the middle (when deleteCount is 0): arr.splice(start, deleteCount[, insert args]), it will return the removed

  • clear content arr.length = 0; or arr.splice(0, arr.length);

immutable operation:

  • Concat

    • return new array: arr.concat(arr2)

      • has flatten effect: [1].concat(2, [3,4]) -> [1,2,3,4]

    • We could make it mutating too: in place (append b to a): Array.prototype.push.apply(a,b)

  • Slice, return shallow copy [start, end): arr.slice

Iterate

  • for: for(let i = 0, l = list.length; i < l; i++) {console.log(list[i]); }. Use break to break out.

  • for in values(): for (let elem in arr.values())

  • arr.forEach: cannot break unless throw an exception

Transform

  • filter: array.filter or _.filter or _.select

  • join to string: arr.join

  • reduce and reduceRight(from end): arr.reduce(callback, initial) # call back is (accumulator, currentValue, index, array)=> {})

    • if no initial value given, first call, previousValue is arr[0] and currentValue is arr[1]

    • the callback function returns value that'll pass to next accumulator. There is no need to assign acc = acc + cur in the callback function.

Query/Test

  • Check if it's array: Array.isArray()

    • polyfill: return Object.prototype.toString.call(arg) === '[object Array]';

  • Test all: arr.every((element) => true) or _.all or _.every

  • Test some: arr.some or _.some or _.any

  • Find first, else return undefined: array.find or array.findIndex

  • Includes:

    • (ES7): arr.includes or _.contains

    • indexOf or lastIndexOf

Tips:

  • create array:

    • ES6: Array(length).fill().map((_,i)=>i+1)

    • ES6: Array.from({length: 10}, (v, i) => i);

    • Lodash: _.fill(Array(10), 1);

Number

  • Special Numbers:

    • Infinity and -Infinity

    • NaN

      • any operation with NaN will rerturn Nan

      • NaN doesn't equal to anything, including itself

  • Hex and Octal:

    • If number starts with 0 and is a valid octal number, it'll be a octal number: var octalNum = 070; // 56

    • hex number: var hexNum = 0xA2 or 0xf1

  • Convert anything to number:

    • Number()

      • Number(null) === 0

      • Number(undefined) === NaN

      • Number('') === 0

      • Number('a') === NaN

      • Number('0x1') === 1

    • parseInt

      • parseInt parses until found invalid character, + simply convert the string to int/float and it returns NaN if there is ANY invalid character.

        • parseInt(' 1') === 1

        • parseInt('') === NaN returns NaN if cannot convert

        • parseInt(' 123abc4') === 123

      • always use radix: parseInt('09', 10) Otherwise '09' will be treated as hex and result as 0

    • paserFloat

      • It always convert to oct. If starts with 0x, it returns 0.

  • Get digits: String(321)split('')

  • Check if it's integer: Number.isInteger polyfill on MDNarrow-up-right

Tips: Random number

  • Math.random() -> [0, 1)

Tips: format number

link: https://codesandbox.io/s/js-tips-format-number-frd6varrow-up-right

String

  • string is value tpye.

  • It's unicode of 16bit

  • u1235 is unicode nnnn

  • charAt(index), indexOf

  • match(regex) returns matches

  • replace(regex/g, 'to replace') specials '$1', etc.

  • substr(index, length) vs substring(index, endIndex) not including endIndex`

    • if negative number:

      • slice(index) index will become index+length: 'abc'.slice(-1) -> 'abc'.slice(2) -> 'c'

      • substr(index, length) index becomes index+length; length will become 0

      • substring will treat negative as 0

  • Can mix different type using '+' since JavaScript is weak typed. The non-string type is auto converted to string.

  • So, if you add a string to a number (or other value) everything is converted in to a string first

  • To convert to string, use either toString or String()

    • String(null) === 'null'

    • String(undefined) === 'undefined'

  • In place sort string with numbers: '8902'.split('').sort().join('')

  • Use String.prototype.localeCompare() for string comparison in sort. Return negative number if it's before. You can also use 'A' > 'a'

Special reference type: String, Boolean, Number

  • it's created on the fly then get destroyed.

Date

Only using new will return the object; Others will return the number or string.

  • new Date()

  • new Date(2005, 0, 3) local time; year and month is required, month is 0 based

  • Date.UTC(2005, 0, 3) UTC time; Return number; year and month is required, month is 0 based

Boolean

Sometimes you'll need to check boolean, it's better to force converting to boolean by:

null undefined

Special objects: null and undefined

  • null: a pointer to nothing (that's why typeof null === 'object'. Most case it can be replaced by undefined;

  • undefined: it's like null in other program.

    • If you access a.name and a is {} then it returns undefined

    • If arr[out_of_bound_index], it returns undefined

boolean: any value can be converted to boolean

Check is null or empty:

_.isEmpty():

  • check the length of array or string

  • CANNOT check number: _.isEmpty(10) === true

  • check if object has enumerable own-properties

Check null:

  • use typeof instance.currentPosition !== 'undefined'

  • Why not using instance.currentPosition === undefined? it can throw errorarrow-up-right

  • CoffeeScript:

    • coffeescript: ? or ?. (the latter can soak up so a.address?.zip returns undefined instead of typeerror)

    • CoffeeScript's existential operator ? returns true unless a variable is null or undefined, which makes it analogous to Ruby's nil?

Comparison

  • Equality operator ==: doesn't compare type, i.e, it converts the type first (performs type coercion) then compare

    "" == false // returns true

  • Strict Equality Operator ===: doesn't not perform type coercion recommended

    '' == false // return false

  • when comparison includes reference type, the comparison (both == and ===) performs pointer comparison.

    {} != {}

Global Objects / Methods

  • Set: unique values of any type, has

  • Map: dictionary, any type can be key, size, get/set

Methods:

  • isNaN, etc

  • let id = setTimeout(()=>console.log('future'), 1000), clearTimeout(id)

  • let id = setInterval(()=>console.log('again'), 1000), clearInterval(id) Use setTimout to emulate interval, as timeout is pushed to the queue, where interval might run in overlap.

  • window.print display print window

  • location: window.location is the same as document.location

  • encodeURI

    • replaces all characters except: ; , / ? : @ & = + $ - _ . ! ~ * ' ( ) # a-z 0-9

  • encodeURIComponent

    • replaces all characters except- _ . ! ~ * ' ( ) a-z 0-9

    • Use it to encode the value part: because it'll encode = and & so it's not supposed to encode the whole param string encodeURIComponent("var1=value1&var2=value2").

Object

  • the property name will always be string, even you created like this {a:'b'}

    • Use dot notation when accessing properties

    • Use subscript notation [] when accessing properties with a variable.

  • for (let prop in obj) will iterate all enumerable props (including the prototype ones) in arbitrar order

    • All properties that we create by simply assigning to them are enumerable.

    • The standard properties in Object.prototype are all nonenumerable. Ex, toString is in prototype but it's not enumerable 'toString' in {} // -> true

    • use hasOwnProperty(key) to check if it's in instance

  • for (let value of arr) (ES6). It's using iterator (next: ()=> {value: x, done: false})

  • The in operator check if the specified property is in the specified object.

    • use hasOwnProperty(key) to check if it's in instance

      ```js

      let obj = {

      a: 'a'

      };

      console.log('a' in obj); // true

    obj.a = undefined; console.log('a' in obj); // true

    delete obj.a; console.log('a' in obj); // false

    ```

  • typeof, check if it's a basic type plus others, it only returns those string: 'undefined', 'null', 'boolean', 'string', 'number', 'object', 'symbol', 'function'

    • so typeof [] === 'object

    • BUT typeof null === 'object'; See MDN explanationarrow-up-right

    • usage: check method defined in prototype: if (typeof this.sayName !== 'function')

  • a instanceof Constructor, check if it's a reference type

    • Use constructor, not string: so [] instanceof Object, not 'object'

  • (TODO) Check if an object is a type

    • Why not instanceof?

    • Why not typeof? Because typeof returns only those 5 types, and array is Object type. For example, typeof [] === 'object', you cannot tell if it's array. See herearrow-up-right

  • object detection, check if it has property or method

  • JSON.stringify(obj, ['fliter', 'list]) can filter

  • Custom getter setter:

  • FP operations

    • Freeze: const frozen = Object.freeze(obj)

    • Create shallow copy:

Prototype

Constructor

A constructor is just a function called with new.

What happens?

  • create new object

  • this bound to new object

  • call the constructor function

  • return the object

Prototype Chain

  • every instance created with the same constructor will share the same prototype: object derived from Object.prototype.

  • How to create prototype-less object? (so that for .. in works without check)

Example

Prototype chain:

  • call parent constructor

  • set prototype to parent prototype

ES6, it's just syntax sugar, remains prototype based.

Function

arguments

  • Always has arguments (note it's not this.arguments).

    • arguments is a build-in object, it's array like.

    • To convert it to array:

      • Array.from(arguments)

      • const a = [...arguments];

      • Array.prototype.slice.call(arguments)

  • Because of 'arguments', there is no function overloading.

  • function.length required params length

  • Function.prototype.bind(thisArgs, arg1, arg2...) creates a new function that binds this and currying parameters.

    • so 'this' is always 'thisArgs'

    • use when in setTimeout callback to refer this, that's also why => in ES6 don't need to bind anymore

  • Funnction.prototype.apply(scope, paramsArray) and Function.prototype.call(scope, param1, param2, etc)

    • apply uses array

    • call uses comma

    • Math.max.apply(null, [1,10,-1])

  • Assignment makes a copy of the value only if it's a primitive type (like Number, Boolean, String, etc...). Otherwise, assignment just copies a reference to the same object (Object, Array, etc...). A new object is not created with assignment.

  • Arguments are always copied by value. Even if the type is reference.

Return value

  • Function output: funciton always return a value. If no return statement or return;, it returns undefined.

Declaration vs Expression

Declaration (will be hoisted)

Expression:

IIFE: immediately-invoked function expression

  • Why? It's useful when you have some work to do, some initialization maybe. You need to do it only once and you don't want to leave any globals lying around after the work is finished.

  • How? It's used to avoid hoisting and creating scope. A function creates a scope.

Function Name

Why you can pass function name to map function?

Actually you’re NOT omitting function parenthesis, what you mean here is passing the function reference so that the map knows which function to call. See http://stackoverflow.com/questions/5520155/settimeout-callback-argument/5520190#5520190arrow-up-right

Closure

Closure: when the inner function makes reference to a variable to the outer/surrounding function, this is called closure.

  • More formal definition: A closure = a function + lexical environment within which that function was declared.

  • Usage: promise chain, currying function, this-that pattern,'private' data in module or function (because function creates scope)

Common error when creating closure in loop:

Scope

Who can create scope?

  • function scope: created by funciton.

    • var is limited in the scope.

    • the scope implicitly defines a reference to this

  • ES6: block scope

    • let is local to the block scope, not the function scope.

    • the scope does not implicitly defines a reference to this

  • ES6: lambda scope

    • the scope does not implicitly defines a reference to this. So this refers to enclosing scope.

Declared variable is a property of the scope. Undeclared variable too. So, both var a = 10; b = 11; is accessible in global window.

for loop:

  • var creates one binding as well as a variable (and the declaration part will be hoisted), so when i is accessed in a callback function, it always refered to the final i value.

  • let creates binding each iteration so it works normally

How does js lookup variable?

  • Similar to stack, look for local then global. Local variables 'shallow' the global ones.

  • If there is a local same name identifier, the search will stop.

Examples:

Hoisted

What will be hoisted?

  • var

    • only the declaration part

    • and it'll be initialized as undefined

  • function declaration: function foo() {}

  • let const

this

Rules:

  1. Normally, this is bind to 'Call-site', the invoking object.

  2. In arrow function, this is bind to the context 'Where is defined'. If no parent defines scope, then the context is window

  3. You can use bind to change this manually.

  4. in event handler, this is bind to invoking target. (NOTE if you define event handler using arrow function then it's bind to window)

Examples:

  • Normally, 'this' is the invoking object. If no invoking object, is the global object. (window or global in Node).

  • When using bind, this is set to a fixed value when it's defined. Or use apply or call to dynamically change context.

  • Arrow function doesn't bind this, arguments.

  • Arrow function this is determined by where is defined. And it refers to the enclosing execution context. You can think it's using the this-that pattern. (Can use babel to verify) 箭头函数从封闭它的(函数或全局)作用域采用 this 绑定.

  • Arrow function is not suitable to define methods:

  • So arrow function cannot be used as constructor, because there is no this in it.

  • Default binding. It means binding loss: it happens whenever you’re accessing a method through a reference instead of directly through its owner object.

    • Why? Because the invoking site is window.

    • So it also means, when you pass function as callback (which implicitly do the assignment in local scope, and when invoking, none is invoking it.), it's window.

    • How to fix? this-that pattern or arrow function.

Reference:

Error Exception

  • catch: there is no selective catch. catch(e)

  • You can create your own error and use instanceOf Constructor

  • Assertion

i18n

  • navigator.languages returns the user's preferred languages //["en-US", "zh-CN", "ja-JP"]

  • navigator.language is the first element of the above returned array

How to test?

Use chrome://settings/languages#lang and (important) make sure that the language you selected is the top choice (the preferred language). The navigator value will change accordingly

Pattern

default value if it doesn't exist

Use ES6 default parameter instead. Previously: Use || NOTE this pattern doesn't work if the value is ''.

Console & Debug

debugger;

If the result is 0, the elements are not in the DOM: console.log($(".theElements").length);

_ Debug JQuery: http://fixingthesejquery.com/#slide23arrow-up-right

How to open a new window without being blocked?

If the window is not a user click resullt, it'll be blocked.

Reference:

How to read binary file from the browser?

https://github.com/jDataView/jDataViewarrow-up-right

Last updated