Skip to content

Instantly share code, notes, and snippets.

@themoxman
Last active August 29, 2015 13:57
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save themoxman/9391909 to your computer and use it in GitHub Desktop.
Save themoxman/9391909 to your computer and use it in GitHub Desktop.

JS THE GOOD PARTS -- SCREENCAST

GET THE BOOK "THINKING FAST AND SLOW"

JSLint.com code quality. tells me when i'm using the bad parts of JS

always put curly braces on the right:

return {
  ok: true
};
// works well.

not

return
{
  ok: false
};
// SILENT ERROR
// automatic semi-colon insertion -- one of the BAD parts

avoid switch statements -- fallthrough hazard.

a good programming style can help produce better programs. Style is not about personal preference and self-expression, it's about reducing errors or the opportunity/frequecy for/of errors.

programs must communicate clearly to people. they need to be written so that someone knows how to modify or improve it.

style guidelines:

  • No space btwn a function name and ().
  • One space between all other names and ().

one of the good parts is "immediately invocable function expressions".

avoid with statements.

confusion must be avoided. it is where bugs come from.

avoid == operator. does type coersion before it's comparison. you can also lose transitivity. use === instead.

avoid multiline string literals.

avoid if (a = b) {...} which could be either:

a = b;
if (a) {...}

// or

if (a === b) {...}

Decide which one of those two you want to use and then use it.

scope is one of the best conventions of programming languages. block scope and function scope.

js does not have block scope. it does have function scope. the problem is that js has syntax identical to those languages that have block scope. people get confused thinking they have block scope when in fact js does not give them block scope.

in a function scoped language, you want to declare all the variables at the top of the function cause in this function scope is where they're actually available.

for (var i ...) {...} -- variable i is not scoped to the loop.

avoid global variables. sometimes you will need to use them so be explicit, UPPER_CASE.

constructor functions should be named with InitialCaps and nothing else should be named with InitialCaps. This is because a missing new causes a constructor to clobber globial variables without warning instead of initializing a new object.

avoid var a = b = 0; which equates to:

b = 0;
var a = b;
//which implicitly sets the `b` global variable. 

// not

var a = 0, b = 0;
// decide which one you want and use it.

avoid using x++ as an increment. use x += 1

JSLint style was driven by the need to automatically detect defects. forms (of code style) that can hide defects are considered defective. so, he took the approach of language subsetting.

js is an object-oriented language. an object is a "dynamic collection of properties". new properties can be added to the object at any time. every property has a keystring that is unique within that object.

the three basic operations on an object are:

  • get
    • object.name
    • object[expression]
  • set
    • object.name = value;
    • object[expression] = value;
  • delete
    • delete object.name
    • delete object[expression] NOTE: the dot and bracket notation can be used interchangeably.

js has 'object literals' which are an expressive way for creating new objects -- var my_object = {foo: bar};

object literals were also the inspiration for the JSON data interchange format.

js doesn't have 'classes' it has 'prototypes'. you can simulate classes in a prototype system but it is very difficult to go the other way. but, don't write js in a java style.

working with prototypes is easier than working with classes:

  • make an object what you like
  • create new instances that inherit from that object with object.create
  • customize the new objects
  • taxonomy and classification are not necessary

in js, you don't worry about classes, you just make objects. objects can inherit directly from each other (see delegation and differential inheritance).

inheritance can sometimes work against you.

every object inherits the 'constructor'

the 'for in' problem: functions inherited from a prototype are included in the forin enumeration.

another issue is that keys must be strings. when you pass in a key it will convert it to a string.

there are lots of other Types in the js language that all inherit from type Object:

  • Number
  • Boolean
  • String
  • Array
  • Date
  • RegExp
  • Function

There's only one Number type in JS. so, no possibility of picking wrong Number type. unfortunately it's the wrong number type! 64-bit floating point.

unfortunately the Associative Law does not hold. (a + b) + c === a + (b + c) produces 'false' for some values of a,b,c.

(a + 1) - 1 === a can be false for some values of a. depends on int size n such. after 9 quadrillion, 0 and 1 are pretty much equal.

most reported bug in JS is some variation of this:

a = 0.1;
b = 0.2;
c = 0.3;

(a + b) + c === a + (b + c)
// returns false in some cases

this is because of the ieee binary floating point approximation. js should have used a decimal floating point format.

js has a number of methods on Number objects:

  • toExponential
  • toFixed
  • toLocalString
  • toPrecision
  • toString
  • valueOf

You can add methods to Number.prototype

Numbers are first class objects in js, they can be:

  • stored in a variable
  • passed as a parameter
  • returned from a function
  • stored in an object
  • have methods

js has two Boolean values, 'true' and 'false'

js Strings:

  • a sequence of 0 or more 16-bit Unicode characters
  • strings are immutable
  • similar strings are equal (===)
  • string literals can be written with single or double quotes. they both function the same way.
    • he uses double quotes for external strings
    • he uses singel quotes for internal (native to the proram itself) strings and characters

js is loosely typed as opposed to java which is strongly typed.

js Operators:

  • + adds and concatenates. so be careful, you might try to add a string to a number without even knowing it, 'string' + 65 tries the addition opperator, not concatenation (i think).

ways to convert Number to String:

  • numbers 'toString' method -- str = num.toString();
  • String function -- `str = Stirng(num);

you can also convert strings to numbers.

strings have a 'length' property. this is a property, not a method. string.length -- determines the number of 16-bit characters in a string. extended characters are counted as 2.

Strings have lots of methods such as:

  • charAt
  • charCodeAt
  • compareLocale
  • concat
  • indexOf
  • lastIndexOf
  • localeCompare
  • match
  • replace
  • search
  • slice
  • split
  • substring
  • toLocaleLowerCase
  • toLocaleUpperCase
  • toLowerCase
  • toString
  • toUpperCase
  • trim
  • valueOf

js doesn't have Arrays in the traditional sense of an Array.

Arrays:

  • Array inherits from Object.
  • Indexes are converted to strings and used as names for retrieving values.
  • Very efficient for sparse arrays -- where some of the values are empty.
  • Not very efficient in most other cases.
  • One advantage: No need to provide a length or type when creating an array.

Arrays have a special 'length' property.

  • Arrays, unlike objects, have a special length property.
  • It is always 1 larger than the hightest integer subscript. which is fine for dense arrays (without empty values), but bad for sparse arrays. it does however allow you to use for (see below) in much the same way it would be used in other languages.
  • It allows use of the traditional for statment -- for (i = 0; i < a.length; i += 1) {...}
  • Do not use for with arrays. for in does not guarantee the sequence of returned values from the array.

Array Literals:

  • An array literal uses []
  • it can contain any number of expressions, separated by commas, myList = ['oats', 'peas', 'beans'];
  • New items can be appended, myList[myList.length] = 'barley'; because the arrays length is always one larger than the highest numbered element.
  • The dot notation should not be used with arrays because of something about how it gets confused with numbers and decimal points?? use brackets [] except when calling methods.

Some Array methods:

  • concat
  • every
  • filter
  • forEach
  • indexOf
  • join
  • lastIndexOf
  • map
  • pop
  • push
  • reduce
  • reduceRight
  • reverse
  • shift
  • slice
  • some
  • splice
  • toLocaleString
  • toString
  • unshift

the 'sort' function does horrible things. it turns the number into strings and then compares the strings.

Deleting elements from an Array:

  • delete array[number] -- removes the element, but leaves a hole in the numbering
  • array.splice(number, 1) -- removes teh element and renumbers all the following elements. example:
myArray = ['a', 'b', 'c', 'd'];

delete myArray[1];
// ['a', undefined, 'c', 'd']

myArray.splice(1, 1);
// ['a', 'c', 'd']
// .splice is super slow tho. it goes through and deletes and
// then re-inserts elements after the one removed.

bc Arrays and Objects are made of the same thing in js, there can be confusion.

Arrays vs Objects:

  • Use objects when the names are arbitrary strings
  • Use arrays when the names are sequential integers
  • don't get confused by the term Associative Array -- in js the associative array is object on array (whatever that means).

js has Dates.

js has RegExp.

all values discussed up until now are objects. there are only two values in js that are not objects: 'null' and 'undefined'.

null is the value that isn't anything.

undefined is the value that isn't even that. it is the default value for variables and parameters. it is the value of missing members in objects.

the 'typeof' prefix operator returns a string identifying the type of a value (see table below): type typeof object 'object' function 'function' array 'object' number 'number' string 'string' boolean 'boolean' null 'object' undefined 'undefined'

checking for type is suprisingly hard, checking for null is not.

js Falsy values:

  • false
  • null
  • undefined
  • "" (empty string)
  • 0
  • NaN
  • all other values (including all objects) are truthy.

Any of these values in the condition part of an 'if' statement will cause you to take the 'false' branch. any other value is considered truthy. for example, an empty object (say an empty Array) is truthy, the string "0" is truthy, the string "false" is truthy.

js is a 'loosely typed' language meaning that you can take any value of any type and stick it in any variable, or pass it as a parameter to any function. BUT, js is not 'untyped', we have a lot of types in the language, it (js) is just 'loose' in how it allows us to use these types.

objects are passed by 'reference' in js. objects are never copied:

  • objects can be passed as arguments to functions and can be returned by functions
    • objects are passed by reference
    • objects are not passed by value
  • the === operator compares object references, not values
    • '===' returns true only if both operands are the same object.

JS is syntactically a C family member. it differs from C mainly in its type sytem, which allows functions to be values.

Identifiers in JS (syntax):

  • start with a letter or _ or $
  • followed by zero or more letters, digits, _ or $
  • by convention, all variables, parameters, members, and function names start with lower case
  • except for constructor functions which start with upper case and global variables (which should be all caps).
  • initial _ should be reserved for implementations
  • $ should be reserved for machines.

Comments in js: // slashslash line comment

*/ slashstar block comment */

recommended to use the '//' format since js uses some regex stuff that can conflict with the block notation (i think).

Operators

  • Arithmetic: + - / %
    • remember, + is concatenation and addition. if both operands are numbers, add them. else, convert them both to strings and concatenate them.
    • the / operator can divide two integers and not necessarily produce an integer result (not sure why)
    • % is the remainder operator, not the modulo operator. the difference is that 'remainder' takes teh sign of the first argument not teh second argument.
  • Comparison: == < > <= >=
    • don't use == and !==. use === and !=== which do not type coerecion.
  • Logical: && || !
    • && is known as the guard operator, aka 'logical and'. if the first operand is truthy, then result is the second operand else result is first operand. it doesn't return 'true' or 'false', it returns one of the two values it was given.
    • && can be used for 'short-circuiting'. if it sees a 'falsy' first value, it will stop at that point.
    • && can be used to avoid null pointer references:
    if (a) {
      return a.member;
    } else {
      return a;
    }
    // which is important since calling a method on 'null' and 'undefined' would fail since they are falsy values.
    
    // we can use && instead of the above if statment:
    return a && a.member;
    
    • || the default operator, aka 'logical or' operator. if the first result is truth then result is first operand, else result is second operand.
    • || can be used to fill in default values: var last = input || nr_items;. if input is truthy, then last is input, otherwise set last to nr_items. Be careful. This may not work as expected if the first operand is a number, because 0 is falsy. so using this for default values only works for strings.
    • ! is the 'logial not' operator. if the operand is truthy, the result is false, otherwise the result is true.
    • !! produces booleans. so it turns truthy into true and falsey into false.
  • Bitwise: & | ^ >> >>> <<
  • the bitwise operators convert the operand to a 32-bit singed integer, and turn the result back into 64-bit floating point.
  • Ternary: ?:

Statments in js:

  • expression
  • if
  • switch -- avoid generally.
    • multiway branch.
    • the switch value does not need to be a number. it can be a string
    • the case values can be expressions.
    • Danger: cases fall through to teh next case unless a disruptive statement like break ends the case.
  • while
  • do
  • for
  • break
  • continue
  • return
  • try/throw

Statments can have 'labels'. Break statements can refer to those labels:

loop: for (;;) {
  ...
    if (...) {
      break loop;
    }
  ...
}

for statement:

  • iterate through all the elements of an array:
for (i = 0; i < array.length; i += 1) {
  // within the loop,
  // i is the index of the current member
  // array[i] is the current element
}

for in statment:

  • iterate through all of the members of an object:
  for (name in object) {
    if (object.hasOwnProperty(name)) {
      // within the loop,
      // name is the key of current member
      // object[name] is the current value
    }
  }

throw statement:

  • in js you can throw anything. you could throw an object literal, a string, anything you like. example throwing error:
throw new Error(reason);

throw {
  name: exceptionName,
  message: reason
};

Evils of type coercion:

  • '' == '0' // false
  • 0 == '' // true
  • 0 == '0' // true
  • false == 'false' // false
  • false == '0' // true
  • false == undefined // false
  • false == null // false
  • false == undefined // true
  • ' \t\r\n ' == 0 // true NOTE: the moral of teh story is don't use ==, use ===

js has functions. In other languages you have methods, classes, constructors, modules. js just has functions.

function syntax:

  • we have a function expression which creates a function value (a function object, a function executable) that you can then invoke.
  • functions start with the keyword 'function'. it can take an optional 'name' which is used to allow the function to call itself recursively. the 'name' is also used by the debugger. useful when you're looking at the stack trace.
  • the function then takes a set of parameters wrapped in () parenthesis (comma separated).
  • and finally a body wrapped in curly braces containing some statements.
  • it (the above) produces an instance of a function object.
  • function objects are 'first class' meaning they may be:
    • passed as an argument to a function
    • returned from a function
    • assigned to a variable
    • stored in an object or array
  • so it's very easy to pass functions around in JS
  • and, functions being objects inherit from Function.prototype
    • and, since Function.prototype has functions on it, essentially that means that every function has methods.
  • var statement - functions have a declare statement which declares and initializes variables within a function.
    • we don't delcare type for those variables since js is loosely typed. we just declare that something is a variable, we don't declare what kinds of variables.
    • a variable declared anywhere within a function is visible everywhere within that function (discussed earlier). so if a variable was declared within an inner block of the function, it would still be available anywhere within the function.
  • there's also a 'function statement' and this is often the thing that people use. the thing that is unfortunate is that they look identical (the 'function statement' and 'function expression' that is). the 'function statement' has a keyword 'function', the 'name' is mandatory and it has the same parameters and body as the 'function expression'
  • the function statement is short-hand for a var statment with a function value (see below code). function foo() {}; is shortand for creating a variable receiving the function expression of function foo (see below).
function foo() {};

// expands to:

var foo = function foo() {};

// which further expands to:

var foo = undefined;
foo = function foo() {};

// the assignment of the function is also hoisted.

function expression vs. function statement

  • so how do you tell the difference between a 'function statement' and 'function expression'?
    • if the first token in a statement is function then it is a function statement.
  • one of the limitations of a function statement is that they have to be declared at the top level of a function, they can't be inside a block. this is because they do this weird hoisting thing.

Scope:

  • remember, in js blocks {"this is a block"} don't have scope.
  • only functions have scope
  • variables defined in a function are not visible outside the function.
  • So
    • declare all variables at the top of the function.
    • declare all functions before you call them.

Return statements:

  • if there is no expression, then the return value is undefined. except for constructors, whose default return value is this.
return expression;

// or

return:

psuedo parameters:

  • in addition to variables and parameters, each function receives two pseudo parameters: 'arguments' and 'this'
  • arguments is an array like thing containing all the arguments (parameters) that were actually passed to the function (all the arguments from the invocation, parameters). it is array like in that it has a 'length' property (which returns the number of argments passed), but other than that it is not like an array.
  • the this parameter (or pseudo-parameter) contains a reference to the object of invocation. we can have a single inherited objec that can work on many objects.
    • this allows a method to know what object it is concerned with.
    • this allows a single function object to service many objects.
    • this is key to prototypal inheritance.

Invocation operator:

  • the parenthesis () which surrounds zero or more comma separated arguments.
    • The arguments will be bound to parameters.
  • if a function is called with too many arguments, the extra arguments are ignored.
  • if a function is called with two few arguments, the missing values will be undefined.
  • there is no implicit type checking on the arguments.
  • but all the arguments will be passed through the arguments array even if there were more or less than indicated by the parameters.

Invocation:

  • There are four ways to call a function:
    • Function form -- functionObject(arguments)
    • Method form -- thisObject.methodName(arguments) or thisObject['methodName'](arguments)
    • Constructor form -- new FunctionObject(arguments)
    • Apply form -- functionObject.apply(thisObject, [arguments])

Method form

  • we have an object.someMethod``(arguments) thisObject.methodName(arguments)
    `thisObject"methodName"
  • when a function is called in the method form, this is set to thisObject, the object containing the function.
  • which allows methods to have a reference to the object of interest.

Function form

  • looks the same as Method form except without the prefix indicating a specific object.
  • when a function is called in the Function form, this is set to the global object (which it turned out was a big mistake in the language).
    • it was fixed in ES5/Strict
    • in the future, this will be bound to undefined instead.
  • an inner function does not get access to the this of an outer function... which can sometimes be a problem if you have a method (function) with an internal helper function. the helper function does not have access to the outer function this. the solution is to declare var that = this; in the outer function and that is now available to the inner helper function.

Constructor form

  • looks the same as Function form except for the new operator prefix.
  • when a function is called with the new operator, a new object is created and assigned to this.
  • if there is not an explicit return value, then this will be returned.
  • this way is used in the Pseudoclassical style (discussed later)

Question: so methods are just functions of functions right??

Apply form functionObject.apply(thisObject, arguments)
functionObject.call(thisObject,argument...)

  • a function's apply or call method allows for calling the function, explicitly specifying thisObject
    • so you can say, "now for this function" this is what this will be.
  • it can also take an array of parameters or a sequence of parameters

'This' chart:

  • this is an bonus parameter. its value depends on the calling form.
  • this gives methods access to their objects.
  • this is bound at the invocation time. Invocation form this function the global object undefined method the object constructor the new object apply argument

Side Effects

  • functions in js have side effects. which makes some things harder to reason about and some things easier to reason about.

Subroutines -- some piece of code that could be called from multiple places (known as subs, procedures, procs, funcs, functions, and lambdas)

Why are there Subroutines?

  • Code reuse -- patterns that happen over and over again. you want to be able to isolate and reuse those patterns.
  • Decomposition -- take a complicated problem and break it up into pieces, each piece a function.
  • Modularity -- interfaces, black boxes, they can defend.
  • Expressiveness -- design your own language based on a library of things you can collect and put together as you like.
  • Higher Order -- functions as parameters and as return values.

Recursion

  • when a function calls itself
  • a function defined in terms of itself
  • quicksort
    • divide the array into two groups, low and high
    • call Quicksort on each group containing more than one element.

Tennent's Principle of Correspondence

  • params and variables are virtually the same thing, act teh same.

Closure

  • one of the central ideas to js
  • the context of an innner function includes the scope of the outer function.
  • the inner function enjoys that context even after the parent functions have returned.
  • function scope works like block scope
  • example:
var digit_name = (function() {
  var names = ['zero', 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine'];

  return function(n) {
    return names[n];
  };
});

alert(digit_name(3)); // 'three'

So, what's being assigned to digit_name is not names n all that stuff, digit_name is assigned to the return function(n) {...}.

So, every time you call digit_name, it does not re-create var names. names is already available to the returned function even after the enclosing function returned and is gone. The context of the inner function enjoys the scope of the outer function and the name var.

NOTE: HE SAYS THIS IS THE MOST IMPORTANT THING HE'LL SAY ALL DAY. It's in the Functions as Subroutines video at 5:30 - 8:30.

Prototypal Inheritance

function Gizmo(id) {
  this.id = id;
}

Gizmo.prototype.toString = function() {
  return "gizmo " + this.id;
}
  • a Gizmo is an object that has an id and has a toString method.
  • functions in js are objects. every function object has a "prototype" property (which is another object) which contains a constructor and by it we create the toString property.

A Module Pattern

var singleton = (function(){
  var privateVariable;
  function privateFunction(x){
    ...privateVariable...
  }
  return {
    firstMethod: function(a, b){
      ...privateVariable...
    },
    secondMethod: function(c){
      ...privateFunction()...
    }
  };
}());
  • module pattern is easily transformed into a powerful constructor pattern.

Power Constructors:

  • make an object.
    • object literal
    • new
    • Object.create
    • call another power constructor
  • define some variables and functions
    • these become private members of the object we're constructing.
  • augment the object with privileged methods.
    • privileged methods are methods which are public, but which close over the private state.
  • return the object.

Example of Power Constructors

  • step one:
    • note that myPowerConstructor is spelled with the lower case initial letter because it doesn't need the new prefix.
function myPowerConstructor(x) {
  var that = otherMaker(x);
}

Part-way into Prototypal Inheritance(B) is where I left off.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment