Speaking Javascript

Basic Javascript

Semicolons terminate statements but not blocks.

Only time a semicolon follows a block is a function expression

var fun1 = function( x ) { return x + 1 };  has semicolon
var fun2 = ( x ) => x + 1;  has semicolon
log( fun1(1), fun2(1) )  2 2
 
if(true){ log("no semicolon")}  no semicolon

Compount assignment operator += work on strings, yay!.

var str = ''
str += 'one'
str += ' two'
str += ' three'
log(str)  one two three

Primitive values vs objects

log( true === true )  true
log( 0 === 0 )        true
log( {} === {} )      false!
 
var arr = 'abc'
arr.length = 100  Cannot assign to read only property 'length' of abc ( 'use strict' verion )
arr.here = 'there'  Cannot assign to read only property 'here' of abc ( 'use strict' version )

All nonprimitive values are objects

The most common kinds are

Categorizing values using typeof and instanceof

log( typeof true )  boolean
log( typeof 'abc' )  string
log( typeof {} )  object
log( typeof [] )  object (lame)
log( typeof null )  object (lame)
 
 
log( {} instanceof Object )  true
log( [] instanceof Object )  true ( oh thanks )
log( [] instanceof Array )  true
 
// underscore or lodash
log(_.isBoolean(true))  true
log(_.isString('abc'))  true
log(_.isObject({}))  true
log(_.isObject([]))  true
log(_.isNull(null))  true

Basic String Methods

log( 'abc'.slice( 0 ) )  abc
log( 'abc'.slice( 1 ) )  bc
log( ' abc '.trim() )  'abc'

Conditionals

// its "else if"
if ( myvar === 0 ) {
  // then
else if ( myvar === 1 ) { 
  // else-if
else if ( myvar === 2 ) { 
  // else-if
} else {
  // else
}
 
// I'll never remember this syntax
switch (myVar) {
  case 'one'// compared via ===
    do_one()
    break
  case 'two':
    do_two()
    break
  default:
    do_default()
}
 
// there's a do-while loop!
 
do {
  do_something()
} while ( true )

Enforcing function arity

function pair( x, y ) {
  if ( arguments.length !== 2 ) {
    throw new Error( 'Need exactly 2 arguments' );
  }
}
 
pair()

Exception handling, most common way

My note: try/catch is sketchy for async programming, but uncaught throw is probably fine

function pair( x, y ) {
  if ( arguments.length !== 2 ) {
    throw new Error( 'Need exactly 2 arguments' );
  }
}
 
try {
  pair(1)  
} catch(ex) {
  log(ex.name)  Error
}

Extracting methods

var obj = {
  first: 'john',
  last: 'doe',
  data: [1,2,3,4,5],
  full: function(){
    return [this.first, this.last].join(' '
  },
 
  all: function(){
 
    var dataString = function(){
      return this.data.join(' ')
    }
 
    return [this.first, this.last, dataString.bind(this)()] 
 
  },
 
}
 
log(obj.full())  john doe
 
var fun = obj.full.bind(obj)
log(fun())  john doe
 
log( obj.all() )  [ 'john''doe''1 2 3 4 5' ]

Multiple places to pass context to some iterators

var obj = {name:'kevin', data:[1,2,3,4,5]}
 
log( obj.data.map( function( x ) { return this.name + x }, obj ) ) 
log( obj.data.map( function( x ) { return this.name + x }.bind( obj ) ) ) 

Functions called with new become constructors for objects

function MyObject(name){
  this.name = name 
}
 
var obj = new MyObject('bob')
log(obj.name)  bob

Regex

// literal definition
var r = /^a/
log( r.test( 'abc' ) )  true
 
log( /a(b+)c/g.exec( 'abc abbc abbbc' ) )  [ 'abc''b', index: 0, input: 'abc abbc abbbc' ] 
 
log('abc abc abc'.replace(/b/g, 'B'))  aBc aBc aBc

Wrapper objects for primitives

// as constructor
log( typeof 'abc' )  string
log( typeof new String( 'abc' ) )  object (?)
log( 'abc' === new String( 'abc' ) )  false
log( 'abc' === 'abc' )  true
 
// as function
log( 123 + '4' 1234
log( 123 + Number('4'))  127

There is no way of comparing objects in javascript

The useful Math functions

log( Math.floor( 1.9 ) )  1
log( Math.ceil( 1.1 ) )   2
 
log( Math.round( 1.4 ) )  1
log( Math.round( 1.5 ) )  2

parseInt

log( parseInt( '123junk'))  123
log( parseInt( 1000000000000000000000.5, 10 ) )  1, not even close
log( _.parseInt( 1000000000000000000000.5, 10 ) )  1, not even close
Strings

charAt()

log( 'abc'.charAt( 2 ) )  c
log( 'abc' [ 2 ] )        c

Converting to string

log( String( 123 ) )  '123'
log( '' + 123 )  '123'
 
var value = 123
log( value.toString())  '123
 
var value = null
log( String( value ) )  null
log( value.toString())  TypeError: Cannot read property 'toString' of null

String.prototype.slice(start, end?)

log( 'abc'.slice(-1))  c
log( 'abc'.slice(-2))  bc
log( 'abc'.slice(1))  bc
log( 'abc'.slice(0,2))  ab

String.prototype.split(separator?, limit?)

log('fred, barney, & pebbles'.split(/[, ]+/))  [ 'fred''barney''&''pebbles' ]
log('fred, barney, & pebbles'.split(/([, ]+)/))  [ 'fred'', ''barney'', ''&'' ''pebbles' ]

String.prototype.trim()

log( ' abc '.trim() )  'abc'

String.prototype.concat(str1, str2, ...)

log( ''.concat( 'a''b''c' ) )  'abc'

String.prototype.indexOf(str, position?)

log( 'abcabc'.indexOf( 'c' ) )  2
log( 'abcabc'.indexOf( 'c', 2+1 ) )  5
 
log( 'abcabc'.lastIndexOf( 'c' ) )  5
Test, Match, and Replace with Regular Expressions

String.prototype.search(regexp)

log( 'abc'.search(/b/))  1
log( 'abc'.search(/(b)/))  1

String.prototype.match(regexp)

log( '-abb--aaab-'.match( /a+b?/ ) )  [ 'ab', index: 1, input: '-abb--aaab-' ]
log( '-abb--aaab-'.match( /a+b?/g ) )  [ 'ab''aaab' ]
 
log( '-abb--aaab-'.match( /(a+)b?/ ) )  [ 'ab''a', index: 1, input: '-abb--aaab-' ]
log( '-abb--aaab-'.match( /(a+)b?/g ) )  [ 'ab''aaab' ]

String.prototype.replace(search, replacement)

log( 'abc'.replace( /./g, ( x ) => x.toUpperCase() ) )  ABC
Exception handling

Use the Error constructor

// Don't do this
if ( somethingBad ) {
  throw "Something bad happened"
}
 
// Do this instead
if ( somethingBad ) {
  throw new Error( "Something bad happened" )
}

Finally is always executed

try {
  throw new Error('whoops!')
} catch ( ex ) {
  log('caught ' + ex)  caught Error: whoops!
} finally {
  log('always' always
}

Finally doesn't need catch

try {
  throw new Error( 'whoops!' )  Error: whoops!
} finally {
  log( 'always' )  always
}
Functions

func.apply( thisValue, *array )

log( Math.max( 1, 2, 3 ) )  3
log( Math.max.apply( null, [ 1, 2, 3 ] ) )  3
 
// using ...rest operator (needed babel-node to not throw error)
log( Math.max( ...[ 1, 2, 3 ] ) )  3

func.bind( thisValue, arg1, ..., argN )

// data and its formatter
var obj = {
  format: function( x ) {
    return [ '-', x, '-' ].join( '' )
  },
  data: [ 1, 2, 3, 4, 5 ]
}
 
// using bind instead of passing this to forEach
obj.data.forEach( function( x ) {
  print( this.format(x) )  -1-  -2-  -3-  -4-  -5-  
}.bind( obj ) ) 
 
 
// passing this to forEach instead of using bind
obj.data.forEach( function( x ) {
  print( this.format(x) )  -1-  -2-  -3-  -4-  -5-  
}, obj ) 

arguments

// enforce arity by checking arguments.length
if ( arguments.length < 1 ) {
  throw new Error( 'You need to provide at least 1 argument' );
}
 
// idiomatic way to converting arguments to real array
function fun() {
  var args = [].slice.call( arguments ) // one way
  var args = Array.prototype.slice.call( arguments ) // another
  args.forEach( ( x ) => log( x ) )
}
 
fun('a''b''c')
 a
 b
 c

pass by reference

function fun(arg){
  arg.replace(/./g, (x) => x.toUpperCase() )
}
 
var str = 'abc'
fun(str)
log( str )  abc
 
function fun(arg){
  log('arg=', arg)
  arg[0] = arg[0].replace(/./g, (x) => x.toUpperCase() )
}
 
// this doesn't work, I don't know why!
var str = 'abc'
fun( [ str ] )
log( str )  abc
 
// this does, I don't know why!
var str = ['abc']
fun( str )
log( str[0] )  ABC
 
// Maybe answer here http://docstore.mik.ua/orelly/webprog/jscript/ch11_02.htm
 
// Since strings are immutable, our original question is moot: there is no way
// to tell if strings are passed by value or by reference. We can assume that,
// for efficiency, JavaScript is implemented so that strings are passed by
// reference, but in actuality it doesn't matter, since it has no practical
// bearing on the code we write.
Objects
  • Don't forget functions are object too

Object.keys and hasOwnProperty

var obj = { a: 1, b: 2, c: 3 }
 
Object.keys( obj ).forEach( function( x ) {
  log( x, obj.hasOwnProperty( x ) )
} )
 
 a true
 b true
 c true
 
for( var x in obj) {
  log( x, obj.hasOwnProperty( x ) )
}
 
 a true
 b true
 c true

Avoid object constructor

var obj = new Object(); // avoid
var obj = {}; // prefer

"But you frequently just create an empty object and then manually add properties, because descriptors are verbose"

var proto = { a: 1 }
var obj = Object.create( proto )
obj.b = 2
log( obj.a )  1
log( obj.b )  2

Object.getPrototypeOf()

var obj = Object.create( String.prototype )
log( Object.getPrototypeOf( obj ) )  [String: '']
log( String.prototype.isPrototypeOf( obj ) )  true
 
// not doing it right (String is a constructor function)
var obj = Object.create( String )
log( Object.getPrototypeOf( obj ) )  [Function: String]
log( String.isPrototypeOf( obj ) )  true
 
// basic case
var proto = {}
var obj = Object.create( proto )
log( proto.isPrototypeOf( obj ) )  true

Avoid invoking hasOwnProperty() directly on an object, as it may be overridden

Object.prototype.hasOwnProperty.call(obj, 'foo')
 
{}.hasOwnProperty.call(obj, 'foo'// shorter

Getters and Setters

var obj = {
  x: 1,
  get getter() {
    return this.x
  },
  set setter( x ) {
    this.x = x
  }
}
 
obj.setter = 100
log(obj.getter)  100

The only operations affected by enumerability are

Arrays
  • Arrays are maps and not touples because they can have holes.
  • Note that most JavaScript engines optimize arrays without holes internally and store them contiguously.
  • Arrays can also have properties (duh length)

Use array length to remove and append elements

var a = [1,2,3]
a.length = 2
log(a)  [ 1, 2 ]
 
a[a.length] = 3
log(a)  [ 1, 2, 3 ]

in operator works

var a = [1,2,3]
log( 1 in a )  true
log( _.include( a, 1 ) )  true
 
a.forEach((x,y) => log(x))
Regular expressions

Literal vs Constructor