Skip to content

Instantly share code, notes, and snippets.

@mmena1
Last active May 4, 2018 20:57
Show Gist options
  • Save mmena1/964cf86b9a991597bedf05c9987b7295 to your computer and use it in GitHub Desktop.
Save mmena1/964cf86b9a991597bedf05c9987b7295 to your computer and use it in GitHub Desktop.
JavaScript Notes

1.1.1. Basic Syntax

Variables are function scoped

function foo() {
    var x = -512;
    if (x < 0) {
        var tmp = -x;
        ...
    }
    console.log(tmp);  // 512
}

Variable tmp is created inside an if condition, but it continues to live until the end of the function. That is because:

Variables are hoisted

Each variable declaration is moved to the begining of the function at execution time:

function foo() {
    console.log(tmp); // undefined
    if (false) {
        var tmp = 3;  // (1)
    }
}

Internally this function is executed like this:

function foo() {
    var tmp;  // hoisted declaration
    console.log(tmp);
    if (false) {
        tmp = 3;  // assignment stays put
    }
}

Closures

Each function stays connected to the variable that surrounds it, for example:

function createIncrementor(start) {
    return function () {
        start++;
        return start;
    }
}

> var inc = createIncrementor(5);
> inc()
6
> inc()
7
> inc()
8

Each call to the function retains the previous value, the function createIncrementor returns another function which is connected to the variable inc. This is called a closure.

The example above has the same result as this:

var start = 5;
function inc() {
    return start++;
}

> inc()
6
> inc()
7
> inc()
8

But the variable start is globally accesible to anyone and can get changed. With a closure we can protect the variable by making it locally scoped, but at the same time avilable in the parent scope accessible only to the variable inc.

1.1.2. Functions

The Function Constructor

The constructor Function() evaluates javascript code in the provided strings.

The following function decaration:

var add = function (x, y) {
    return x + y;
};

Is the same as:

var add = new Function('x', 'y', 'return x + y');

But, it's much better to use a function declaration as it is easier to read and the code isn't stored in strings, which can be difficult to mantain (inaccessible to tools for syntax check).

1.1.3. Scopes, Environments and Closures

IIFE: Immediately Invoked Function Expression

Given this exampe:

function f() {
    if (condition) {
        var tmp = ...;
        ...
    }
    // tmp still exists here
    // => not what we want
}

We dont want the variable tmp to exist past the if block. We can use the IFFE pattern:

function f() {
    if (condition) {
        (function () {  // open block
            var tmp = ...;
            ...
        }());  // close block
    }
}

Immediately invoking the function body we can create a new scope inside the if block.

IIFE Variation: Prefix Operators

!function () { // open IIFE
    // inside IIFE
}(); // close IIFE

void function () { // open IIFE
    // inside IIFE
}(); // close IIFE

The advantage of using prefix operators is that forgetting the terminating semicolon does not cause trouble.

IIFE Variation: Already Inside Expression Context

var File = function () { // open IIFE
    var UNTITLED = 'Untitled';
    function File(name) {
        this.name = name || UNTITLED;
    }
    return File;
}(); // close IIFE

When the IFFE is inside the expression context (var File = function () {...), there is no need to enforce expression ((function () { ... })();) context for the IFFE because its already inside one.

IIFE Variation: An IIFE with Parameters

This:

var x = 23;
(function (twice) {
    console.log(twice);
}(x * 2));

Is similar to:

var x = 23;
(function () {
    var twice = x * 2;
    console.log(twice);
}());

1.1.4 Objects and inheritance

Bracket Operator ([]): Accessing Properties via Computed Keys

The bracket operator allows us to refer to a property via an expression.

> var obj = { someProperty: 'abc' };

> obj['some' + 'Property']
'abc'

> var propKey = 'someProperty';
> obj[propKey]
'abc'

> var obj = { 'not an identifier': 123 };
> obj['not an identifier']
123

> var obj = { '6': 'bar' };
> obj[3+3]  // key: the string '6'
'bar'

Function.prototype.bind(thisValue, arg1?, ..., argN?)

The bind function creates a new function that calls the receiver of bind. This three invokations are similar:

jane.sayHelloTo('Tarzan');

var func1 = jane.sayHelloTo.bind(jane);
func1('Tarzan');

var func2 = jane.sayHelloTo.bind(jane, 'Tarzan');
func2();

apply() for Constructors

The apply() function is useful to convert an array into actual arguments.

> Math.max.apply(null, [13, 7, 30])
30

Prototype-based Inheritance

var proto = {
    describe: function () {
        return 'name: '+this.name;
    }
};
var obj = {
    [[Prototype]]: proto,
    name: 'obj'
};

> obj.describe
[Function]

Sharing data between objects via prototype

var PersonProto = {
    describe: function () {
        return 'Person named '+this.name;
    }
};
var jane = {
    [[Prototype]]: PersonProto,
    name: 'Jane'
};
var tarzan = {
    [[Prototype]]: PersonProto,
    name: 'Tarzan'
};

> jane.describe()
Person named Jane
> tarzan.describe()
Person named Tarzan

Creating a new object with a given prototype

var PersonProto = {
    describe: function () {
        return 'Person named '+this.name;
    }
};
var jane = Object.create(PersonProto, {
    name: { value: 'Jane', writable: true }
});

> jane.describe()
'Person named Jane'

Or, create an empty object then add a property:

var jane = Object.create(PersonProto);
jane.name = 'Jane';

Reading a prototype from an object

> Object.getPrototypeOf(jane) === PersonProto
true

Checking whether one object is a prototype of another one

> var A = {};
> var B = Object.create(A);
> var C = Object.create(B);
> A.isPrototypeOf(C)
true
> C.isPrototypeOf(A)
false

Finding the object where a property is defined

function getDefiningObject(obj, propKey) {
    obj = Object(obj); // make sure it’s an object
    while (obj && !{}.hasOwnProperty.call(obj, propKey)) {
        obj = Object.getPrototypeOf(obj);
        // obj is null if we have reached the end
    }
    return obj;
}

Listing all property keys of an object (own and inherited)

Given this inheritance:

var proto = Object.defineProperties({}, {
    protoEnumTrue: { value: 1, enumerable: true },
    protoEnumFalse: { value: 2, enumerable: false }
});
var obj = Object.create(proto, {
    objEnumTrue: { value: 1, enumerable: true },
    objEnumFalse: { value: 2, enumerable: false }
});

We can loop through the properties like this:

> for (var x in obj) console.log(x);
objEnumTrue
protoEnumTrue
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment