Skip to content

Instantly share code, notes, and snippets.

@wetmore
Last active December 29, 2015 03:49
Show Gist options
  • Star 6 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save wetmore/7610160 to your computer and use it in GitHub Desktop.
Save wetmore/7610160 to your computer and use it in GitHub Desktop.
Reviewing some parts of javascript I don't know well enough. And also just a dump of some current knowledge.

Javascript Review

Table of contents

  1. Objects
  2. Functions
  3. Closures
  4. The call and apply methods
  5. Prototypes and constructors
  6. Prototypes
  7. Creating objects
  8. The new keyword and constructors
  9. Arrays
  10. The in operator
  11. Use
  12. Description
  13. The with operator
  14. Use
  15. Description
  16. Sources

Objects

An object is essentially a map from keys (which are some properties of the object) to values. All property names (including keys) are strings; if you try using a non-string primitive as a key, it will cast the primitive to a string, and if you try to use an object as a key it will use the object's toString() method (which is really just another property of the object) to get a string to use. If object is some object, we can get and set a property in two ways:

var object = { prop1: 'Look at me' };
object.prop2 = 'I am a property';
object['prop3'] = 'Me too';
console.log( object['prop1'], object.prop2, object.prop3 ); // => 'Look at me', 'I am a property', 'Me too'

The . (dot) syntax is cleaner, but has some limitations: you can't look up properties that begin with a numerical digit, or with a - in them. You also can't do dynamic property lookups with the dot syntax, for instance accessing a property whose name is stored in a variable. That's what the bracket syntax is for.

Functions

One of the first things we learn about javascript is that functions are first-class objects. We can assign them to variables, pass them around as arguments to other functions, and all that good stuff. Another thing that most of us know is that everything that isn't a primitive is an object. This, naturally, means that functions are also objects, and do they get their own properties as well. If I have a function var f = function() {...};, I can assign properties to it in the usual way: f.prop = 'I am a property on the function';. This is how, for instance, the jQuery selector function $ also has methods (just properties that are functions), like $.ajax.

We can define functions with two different syntaxes: either a function assignment

var f = function(x) {...};

or a function declaration

function f(x) {...};

A function assignment (predictably) assigns a function to a variable. A function declaration, for most intents, does the same thing. However, function declarations are hoisted to the top of the current scope, whereas function assignments are not (in fact, assignments are never hoisted. It's the same for any type of var: the declaration is hoisted but any assignments are not).

Closures

The funny thing about closures is that even though there are dozens of articles on how they work, the main concept is so simple that when you see an example you realize you've been using them without knowing it. Closures are a concept that give functions a lot of their power. When we said that functions were first-class objects, we implied that functions can be "passed around" - that is, used as function arguments and returned. We can generate functions inside of other functions and return them, like so:

function makeGreeter(name) {
  var greeting = 'Hi there ';
  return function() {
    return greeting + name;
  };
}

var greetMatt = makeGreeter('Matt');
console.log(greetMatt()); // => 'Hi there Matt'

When makeGreeter is called, it returns a function which we can call to get a greeting. Notice, however, that when we call the function outside of the scope it was defined in, the function can still access the variables greeting and name. This is an example of a closure - the function that makeGreeter returns when called "closes over" the necessary variables in its lexical scope, so when it executes it still has access to them.

As I see it, the concept of closures in these situations is a natural one; it makes sense that such a feature would be in javascript given the functional nature. However, it's easy to ignore the actual mechanics, so to speak, of how closure works, and that's what leads to bugs and confusing behaviour. For example, consider the following slight rework of our previous example:

function makeGreeter(name) {
  var greeting = 'Hi there ';
  var ret = function() {
    return greeting + name;
  };
  greeting = 'Go away '; // I've changed my mind
  return ret;
}

var greetMatt = makeGreeter('Matt');
console.log(greetMatt()); // => 'Go away Matt'

One might think that since the return function is created before greeting is changed, the function will have closed over greeting when it was 'Hi there ', but the function is closing over a reference to the variable, which it makes use of after the function has returned. So the rest of the function body executes before the return function does. One particularly illuminating example of the fact that closures are references and not mere copies of variables is the following:

function makeCounterGlobal() {
  var num = 0;
  
  count = function() {
    num++;
  };
  
  getCount = function() {
    return num;
  }
}

makeCounterGlobal();
count();
count();
console.log(getCount()); // => 2

We create two global functions, each with their own closure over the same variable num. Since each is copying a reference, increases in num by calling count are reflected when we call getCount.

For more examples of tricky closure behaviours see this StackOverflow answer.

The call and apply methods

In a function body, one can use this to refer to the function's context. What exactly this refers to differs depending on where the function is called from. Every function object has the methods call and apply, which allow you to explicitly specify the context for a function, and therefore specify what this refers to in the body of the function. In either method, you first specify an object to use as the function context, and you can also specify arguments. The format for these arguments is where the two methods differ. If I have a function f, f.call(context, arg1, ..., argN) is like saying f(arg1, ..., argN), where any occurences of this are references to context. Meanwhile, apply takes the arguments in an array: f.apply(context, [arg1, ..., argN]) is another way of doing what we did with call.

It's easy to forget which method does what, but one helpful way of remembering is that apply is "applying" a function to an array, whereas call is calling the function in the normal way (comma separated arguments), and also allowing you to specify a context.

Prototypes and constructors

Most people come into javascript from an object-oriented language like Java (this might start changing as javascript is growing in popularity and is being used in learn-to-program efforts like Codecademy and Khan Academy). As a result they want to know how concepts like inheritance map to javascript. The common reply is that javascript is a prototype-based language, and instead of the concept of "classes" and inheritance via subclasses, javascript uses "protypical inheritance". You may hear the phrase "prototype chain" thrown around because it sounds cool. But what does this all mean?

Prototypes

A prototype is an object another object can use as a template of sorts. If an object parent is the prototype for some object child, then any instance of child will share properties with parent. Of course, parent also has some prototype - this is what we mean when we say "prototype chain" - and at the top of this chain is the basic Object, whose prototype is null (meaning the chain is at its end). So if arrows point from an object to its prototype, a typical prototype chain will look something like

child -> parent -> ... -> Object

When we try to retrieve a property from an object, the program will first look at the properties directly owned by the object instance (that is, properties set directly on the object). If it doesn't find them there, it will recursively look up the prototype chain until it finds the requested property. If it reaches the end without finding the property, undefined is returned. If you want to distinguish between properties the object instance owns, you can use object.hasOwnProperty(prop), which will return true if the instance of object has the property prop (and not if the interpreter needs to look at the prototype chain).

It's worth noting that when we talk about an object's prototype, there are two possible meanings. The prototype as I've explained it is (according to the standard) an inaccessible property of an object. It's often referred to as [[prototype]] to denote that it is hidden. In newer browsers, however, it is accessible as __proto__. The [[prototype]] is set on an object instance when it is created (see the section on the new keyword below). Theoretically the [[prototype]] should not change after being set/after the object instance is created. Throughout this document, when I refer to an object's prototype, I'm talking about [[prototype]]. The other meaning is the prototype property of some object. In general, an object's [[prototype]] and prototype are different. The use of prototype is explained below, alongside the new keyword.

Creating objects

In most cases you just use an object as a key-value store, in which case the standard for creating an object is to use the object-literal syntax: var object = {}. This is what you're probably used to, and it's preferred to the heavier var object = new Object(). This alternate method of creating objects raises some questions of its own, however. What's with the new? This method of creating objects naturally leads to a discussion of how to create objects with constructors and set up their prototype chain.

The new keyword and constructors

A constructor is just a function. When one defines a new function, say var MyObject = function() {...}, its prototype property - that is, MyObject.prototype is created as well. This object has the property constructor, which in turn points back to the MyObject function. This is different than MyObjects [[prototype]], which points to Function.prototype. Remember, in general an object's prototype differs from its [[prototype]]. We can use this function as a constructor for a new object by invoking the new keyword. When we say var object = new MyObject(), a few things happen. First, a new object is created, and its [[prototype]] property is set to MyObject.prototype. Then MyObject is called, where any uses of this in the function refer to the newly-created object. Finally, if MyObject returns a primitive value, the new object is returned. Otherwise the return value, which is some object, is returned. If MyObject is just some new function we created, then its prototype has a prototype of Object, but we can set up more complex prototype chains if MyObject.prototype is set to something with its own non-trivial prototype chain.

Arrays

Arrays are just objects with some nice methods in their prototype, as well as properties like length which they maintain. You can actually directly change length which will alter the array - lowering length will get rid of values at the end of the array, and raising it will add undefineds to make the array the proper length (at least in Chrome). One particularly clear way of seeing that arrays are just objects is to note that the indices of an array are just keys, so they are strings. In particular:

var arr = [ 'Gauss', 'Euler', 'Wetmore' ];
console.log( arr['0'] ); // => 'Gauss'

The in operator

Use

// on a standard object:
var classes = {
  'MATH235': 'Algebra 1',
  'MATH354': 'Analysis 3',
  'MATH350': 'Graph Theory',
  'COMP330': 'Theory of Computation'
};

// checking membership
console.log('MATH350' in classes); // => true

// using to iterate
for (classCode in classes) {
  console.log(classes[classCode]); // => e.g. 'Algebra 1'
}

// arrays can be misleading
var array = [ 1, 2 ];

// remember, it's the *property* you're testing for. The first is true because 1 refers to the index.
console.log(1 in array); // => true
console.log(2 in array); // => false

Description

The operator in can work in two ways: either you can use it to test for a property in some object, or you can use it in a for statement to iterate over an object's properties. In the former case, the expression prop in object will return true if prop is a property in objects prototype chain. This is different from the object.hasOwnProperty(prop) function, which will not return true for properties on the object's prototype, but not the object itself. In the latter case, the statement for (prop in object) {...} will iterate over all enumerable properties of the object. This does not include properties found higher up the object's prototype chain.

As arrays are just objects where the indices of the array are represented by corresponding integer keys, using the for (index in array) {...} statement to enumerate an array will set index to one of the keys in each iteration. However, order is not guaranteed when using for ... in, and any new properties you set on the array will also be enumerated. For example:

var arr = [ 1, 2 ];
arr.lol = 'lol';

for (index in arr) {
  console.log(arr[index]);
}
// => 1, 2, 'lol'

The with operator

Use

with (Math) {
  console.log(PI); // => 3.1415....
}

Description

The with operator allows you to make access to an object's properties without specifying the object each time. This may seem like a nice idea, because it allows you to make heavy use of an object's properties without writing object.propName for each property. However with is often frowned upon because it allows you to subvert the standard javascript lexical scoping, which is an easy way to write code whose behaviour is not transparent. For example, consider the following block of code:

// my french isn't very good
var translations = { 'monday': 'Mardi' };

with (translations) {
  monday = 'Lundi'; // let's fix that mistake
  tuesday = 'Mardi'; // this should fix it right?
}

console.log( translations.monday, translations.tuesday ); // => 'Lundi', undefined
console.log( tuesday ); // => 'Mardi'

The with statement does not merely set the context of the block to be the given object. Rather, the given object's properties (including those in the prototype chain) become accessible and writeable in the block, but you cannot interact with the object in any other ways. In fact, the context of the block is the same as the context in which the with blog is contained. The behaviour of with (object) {...} when accessing properties of object might seem to imply that you can set new properties on object by declaring variables without var, but instead that just creates global objects.

Sources

http://zeekat.nl/articles/constructors-considered-mildly-confusing.html

http://stackoverflow.com/questions/1646698/what-is-the-new-keyword-in-javascript

http://stackoverflow.com/questions/111102/how-do-javascript-closures-work

https://github.com/getify/You-Dont-Know-JS

Mozilla Developer Network

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