Javascript – Concepts – Scope



Javascript – Concepts – Scope

3 2


javascript-training

Javascript training slides and examples.

On Github Wintellect / javascript-training

Javascript

Concepts

Scope

  • Global Scope
  • Function Scope

Global Scope

// The current URL
console.log(document.location.href);

// Others can see this (or overwrite it)
var myGlobalVar = 123;

Function Scope

function myFunction() {
    var myLocalVar = 456;
}

console.log(myLocalVar);
// Error: myLocalVar is not defined

IIFE

Immediately-Invoked Function Expression
(function() {

    var x = 123;
    var y = "abc";

    console.log(x, y);
    // 123 abc

}());

Function Scope

var myGlobalVar = 123;

function myFunction1() {

    var myLocalVarOne = 456;
    myFunction2();

    function myFunction2() {
        var myLocalVarTwo = 789;
        console.log(myGlobalVar, myLocalVarOne, myLocalVarTwo);
    }
}

myFunction1(); // 123 456 789

Closures

function addSuffix(suffix) {
    return function(text) {
        return text + " " + suffix;
    };
}

var addPhD = addSuffix("PhD");
var addMasters = addSuffix("M.A.");

console.log(addPhD("Jane Smith")); // Jane Smith PhD
console.log(addMasters("Bob Smith")); // Bob Smith M.A.

No Block Scope!

var myValue = 123;

function getMyValue() {
    if(myValue) {
        var myValue = 456;
    }
    return myValue;
}

console.log(getMyValue()); // undefined

Block Scope in ES6

var myValue = 123;

function getMyValue() {
    if(myValue) {
        let myValue = 456;
    }
    return myValue;
}

console.log(getMyValue()); // 123

Hoisting

function test() {
    console.log(myValue);
    var myValue = 456;
    console.log(myValue);
}

function testActual() {
    var myValue;
    console.log(myValue);
    myValue = 456;
    console.log(myValue);
}

test(); // undefined 456

Functions Hoisted First

function getMyValue() {
    var getValue = function() {
        return 123;
    };

    return getValue();

    function getValue() {
        return 456;
    }
}

console.log(getMyValue()); // 123

Loops and Closures

function test() {
    for(var i=1; i<=3; i++) {
        setTimeout(function() {
            console.log(i);
        }, 10);
    }
}

test(); // 4 4 4

Loops and Closures

function test() {
    for(var i=1; i<=3; i++) {
        setTimeout(doSomething(i), 10);
    }

    function doSomething(i) {
        return function() {
            console.log(i);
        }
    }
}

test(); // 1 2 3

What is "this"?

  • Special identifier
  • Automatically defined in current scope
  • A context that can be changed

Simple Object

var myObject = {
    myValue: 123,
    getMyValue: function() {
        return this.myValue;
    }
};

console.log(myObject.getMyValue()); // 123

Simple Object - Function

var myObject = {
    myValue: 123,
    getMyValue: function() {
        return this.myValue;
    }
};

var testFxn = myObject.getMyValue;
console.log(typeof testFxn); // function
console.log(testFxn()); // undefined

Simple Object - Function

var myObject = {
    myValue: 123,
    getMyValue: function() {
        return this.myValue;
    },
    tryConsole: function() {
        this.console.log("hello");
    }
};

var tryConsole = myObject.tryConsole;
tryConsole(); // hello

Call Method

var myObject = {
    myValue: 123,
    getMyValue: function() {
        return this.myValue;
    }
};

var myOtherObject = {
    myValue: 456
};

var testFxn = myObject.getMyValue;
console.log(testFxn.call(myOtherObject)); // 456

Object Method w/Args

var myObject = {
    myValue: 123,
    getMyValue: function(prefix, suffix) {
        return [prefix, this.myValue, suffix].join(" ");
    }
};

console.log(myObject.getMyValue("<!--", "-->")); // <!-- 123 -->

Object Method w/Args - Call

var myObject = {
    myValue: 123,
    getMyValue: function(prefix, suffix) {
        return [prefix, this.myValue, suffix].join(" ");
    }
};

var otherObject = {
    myValue: 456
};

var test = myObject.getMyValue;
console.log(test.call(myObject, "<!--", "-->")); // <!-- 123 -->
console.log(test.call(otherObject, "<!--", "-->")); // <!-- 456 -->

Object Method w/Args - Apply

var myObject = {
    myValue: 123,
    getMyValue: function(prefix, suffix) {
        return [prefix, this.myValue, suffix].join(" ");
    }
};
var otherObject = {
    myValue: 456
};

var test = myObject.getMyValue;
var myArgs = ["<!--", "-->"];
console.log(test.apply(myObject, myArgs)); // <!-- 123 -->
console.log(test.apply(otherObject, myArgs)); // <!-- 456 -->

Object Method - Bind

var myObject = {
    myValue: 123,
    getMyValue: function(prefix, suffix) {
        return [prefix, this.myValue, suffix].join(" ");
    }
};
var otherObject = {
    myValue: 456
};

var test1 = myObject.getMyValue.bind(myObject);
var test2 = myObject.getMyValue.bind(otherObject);
console.log(test1("<!--", "-->")); // <!-- 123 -->
console.log(test2("<!--", "-->")); // <!-- 456 -->

Objects

  • Encapsulate functionality
  • Key/Value Pairs
  • Prototypal Inheritance

Object Literal

var myObject = {
    myValue: 123,
    getMyValue: function() {
        return this.myValue;
    }
};

Object using Function

function MyObject(initialValue) {
    this.myValue = initialValue;
    this.getMyValue = function() {
        return this.myValue;
    };
}

var v = new MyObject(123);
console.log(v.getMyValue()); // 123

Object using Function

function MyObject(initialValue) {
    this.myValue = initialValue;
    this.getMyValue = function() {
        return this.myValue;
    };
}

var v1 = new MyObject(123);
var v2 = new MyObject(456);
console.log(v1.getMyValue()); // 123
console.log(v2.getMyValue()); // 456
console.log(v1.getMyValue === v2.getMyValue); // false

Object Prototype

function MyObject(initialValue) {
    this.myValue = initialValue;
}

MyObject.prototype.getMyValue = function() {
    return this.myValue;
};

var v = new MyObject(123);
console.log(v.getMyValue()); // 123

Object Prototype

function MyObject(initialValue) {
    this.myValue = initialValue;
}
MyObject.prototype.getMyValue = function() {
    return this.myValue;
};

var v1 = new MyObject(123);
var v2 = new MyObject(456);
console.log(v1.getMyValue()); // 123
console.log(v2.getMyValue()); // 456
console.log(v1.getMyValue === v2.getMyValue); // true

Object Prototype

function MyObject(initialValue) {
    MyObject.prototype.myValue = initialValue;
}
MyObject.prototype.getMyValue = function() {
    return this.myValue;
};

var v1 = new MyObject(123);
var v2 = new MyObject(456);
console.log(v1.getMyValue()); // 456
console.log(v2.getMyValue()); // 456

Object Type

var v1 = {
    getValue: function() { return 123; }
};

function MyObject() { }
MyObject.prototype.getValue = function() { return 123; };

var v2 = new MyObject();

console.log(v1 instanceof MyObject); // false
console.log(v2 instanceof MyObject); // true

Prototype Object Syntax

function MyObject(initialValue) {
    this.myValue = initialValue;
}

MyObject.prototype = {
    getMyValue: function() {
        return this.myValue;
    }
};

var v = new MyObject(123);
console.log(v.getMyValue()); // 123

Prototype Property Syntax

function MyObject(initialValue) {
    this._myValue = initialValue;
}

MyObject.prototype = {
    get myValue() { return this._myValue; },
    set myValue(v) { this._myValue = v; },
};

var v = new MyObject(123);
console.log(v.myValue); // 123
v.myValue = 456;
console.log(v.myValue); // 456

Static Properties

function MyObject() {
}

MyObject.ConstantValue = 123;
MyObject.GetConstantValue = function() {
    return MyObject.ConstantValue;
};

console.log(MyObject.GetConstantValue()); // 123

Inheritance - ES5

function Shape(x, y) {
    this.x = x;
    this.y = y;
}
Shape.prototype.getPositionText = function() {
    return ['[', this.x, ',', this.y, ']'].join("");
};

function Rectangle(x, y, width, height) {
    Shape.call(this, x, y);
    this.width = width;
    this.height = height;
}
Rectangle.prototype = new Shape();
Rectangle.prototype.getSizeText = function() {
    return ['[', this.width, 'x', this.height, ']'].join("");
};

function Square(x, y, size) {
    Rectangle.call(this, x, y, size, size);
    this.size = size;
}
Square.prototype = new Rectangle();

var sq = new Square(10, 20, 40);

console.log(sq instanceof Square); // true
console.log(sq instanceof Rectangle); // true
console.log(sq instanceof Shape); // true

console.log(sq.getPositionText()); // [10,20]
console.log(sq.getSizeText()); // [40x40]

Inheritance - ES6

class Shape {
    constructor(x, y) {
        this.x = x;
        this.y = y;
    }
    getPositionText() {
        return ['[', this.x, ',', this.y, ']'].join("");
    }
}

class Rectangle extends Shape {
    constructor(x, y, width, height) {
        super(x, y);
        this.width = width;
        this.height = height;
    }
    getSizeText() {
        return ['[', this.width, 'x', this.height, ']'].join("");
    }
}

class Square extends Rectangle {
    constructor(x, y, size) {
        super(x, y, size, size);
        this.size = size;
    }
}

var sq = new Square(10, 20, 40);

console.log(sq instanceof Square); // true
console.log(sq instanceof Rectangle); // true
console.log(sq instanceof Shape); // true

console.log(sq.getPositionText()); // [10,20]
console.log(sq.getSizeText()); // [40x40]

Mixin

var myObject = {
    value: 123
};

function MyMixin() {
    this.doubleIt = function() {
        return this.value * 2;
    };
}

MyMixin.call(myObject);

console.log(myObject.doubleIt()); // 246

Lodash

  • Started with Underscore
  • Utility Belt
  • Functional Paradigm

Each w/Array

var myArray = [1, 1, 2, 3, 5, 8, 13, 21];

_.each(myArray, function(value, index) {
    console.log("[%d] %s", index, value);
});

// [0] 1
// [1] 1
// [2] 2
// [3] 3
// [4] 5
// [5] 8
// [6] 13
// [7] 21

Each w/Object

var myObject = { a: 1, b: 2, c: 3 };

_.each(myObject, function(value, key) {
    console.log("%s: %s", key, value);
});

// a: 1
// b: 2
// c: 3

Map

var data = [
    { id: 1 },
    { id: 2 },
    { id: 3 }
];

var result = _.map(data, function(v) {
    return { dataId: v.id, text: v.id.toString() };
});
console.log(result);

// [ { dataId: 1, text: '1' },
//   { dataId: 2, text: '2' },
//   { dataId: 3, text: '3' } ]

Filter

var data = [
    { id: 1 },
    { id: 2 },
    { id: 3 }
];

var result = _.filter(data, function(v) {
    return v.id % 2 === 1; // Only odd id values
});
console.log(result);

// [ { id: 1 }, { id: 3 } ]

Find

var data = [
    { id: 1 },
    { id: 2 },
    { id: 3 }
];

var result = _.find(data, function(v) {
    return v.id % 2 === 1; // Only odd id values
});
console.log(result);

// { id: 1 }

Chaining

var data = [ 'bob', 'ted', 'ann', 'sue', 'ned' ];

var result = _(data)
    .map(function(v) { return { name: _.capitalize(v) }})
    .sortBy(function(v) { return v.name; })
    .reverse()
    .value();
console.log(result);

// [ { name: 'Ted' },
//   { name: 'Sue' },
//   { name: 'Ned' },
//   { name: 'Bob' },
//   { name: 'Ann' } ]

Async Operations

  • Timeout / Interval
  • HTTP Call
  • Reading/Writing File (NodeJS)

Async Handling

  • Javascript is "Single Threaded"
  • Blocking
  • Web Workers

Callback

var v = "Not Yet!";
setTimeout(myCallBack, 0);
console.log(v);

function myCallBack() {
    v = "Now!";
    console.log(v);
}

Nested Callbacks

var count = 0;

setTimeout(function() {
    count += 1;
    setTimeout(function() {
        count += 1;
        setTimeout(function() {
            count += 1;
            console.log("Count:", count); // Count: 3
        });
    });
});

console.log("Count:", count); // Count: 0

Orchestrating Callbacks

var didFirst = false;
var didSecond = false;

setTimeout(function() { didFirst = true; checkForBothDone(); }, 0);
setTimeout(function() { didSecond = true; checkForBothDone(); }, 0);

function checkForBothDone() {
    if(didFirst && didSecond) {
        console.log("Finished!");
    }
}

Callbacks and Error Handling

  • Error is thrown by async operation.
  • Error is thrown by handler.

Promise

An object that represents an operation expected to be completed ("resolved") or in error ("rejected") at some point in the future.

Promise States

  • fulfilled - The action relating to the promise succeeded
  • rejected - The action relating to the promise failed
  • pending - Hasn't fulfilled or rejected yet
  • settled - Has been fulfilled or rejected

Simple Promise

var v = "Not Yet!";
wait()
    .then(function() {
        v = "Now!";
        console.log(v);
    });
console.log(v);

function wait() {
    var deferred = Q.defer();
    setTimeout(deferred.resolve, 0);
    return deferred.promise;
}

Resolved Promise

wait()
    .then(function() {
        console.log("Resolved!");
    });

function wait() {
    var deferred = Q.defer();
    deferred.resolve();
    return deferred.promise;
}

Rejected Promise

wait()
    .then(function() {
        console.log("Resolved!");
    }, function() {
        console.log("Rejected!");
    });

function wait() {
    var deferred = Q.defer();
    deferred.reject();
    return deferred.promise;
}

Error in Handler

wait()
    .then(function() {
        throw new Error("Something bad happened!");
    }, function() {
        console.log("Rejected!"); // ... not called
    });

function wait() {
    var deferred = Q.defer();
    deferred.resolve();
    return deferred.promise;
}

Catch

wait()
    .then(function() {
        throw new Error("Something bad happened!");
    })
    .catch(function() {
        console.log("Rejected!");
    });

function wait() {
    var deferred = Q.defer();
    deferred.resolve();
    return deferred.promise;
}

Resolved Result

wait()
    .then(function(result) {
        console.log(result); // { value: true }
    });

function wait() {
    var deferred = Q.defer();
    deferred.resolve({ value: true });
    return deferred.promise;
}

Rejected Result

wait()
    .then(function(result) {
        console.log(result); // ... Not called
    })
    .catch(function(result) {
        console.log(result); // { value: false }
    });

function wait() {
    var deferred = Q.defer();
    deferred.reject({ value: false });
    return deferred.promise;
}

Promise Chaining

wait(1)
    .then(function(v) { return wait(v); })
    .then(function(v) { return v + 1; })
    .then(function(v) { return wait(v); })
    .then(function(v) { return v + 1; })
    .then(function(v) { console.log("res:", v); /* res: 6 */ })
    .catch(function(v) { console.log("rej:", v); /* not called */ });

function wait(v) {
    var deferred = Q.defer();
    deferred.resolve(v + 1);
    return deferred.promise;
}

Don't Nest Promises

wait(1).then(function(v) {
    wait(v).then(function(v) {
        v = v + 1;
        wait(v).then(function(v) {
            v = v + 1;
            console.log("res:", v); // res: 6
        }).catch(function(v) {
            console.log("rej:", v); /* not called */
        });
    });
});
function wait(v) {
    var deferred = Q.defer();
    deferred.resolve(v + 1);
    return deferred.promise;
}

Promise Chaining w/Reject

wait(1)
    .then(function(v) { return wait(v); })
    .then(function(v) { return v + 1; })
    .then(function(v) { return wait(v); })
    .then(function(v) { return v + 1; })
    .then(function(v) { console.log("res:", v); /* not called */ })
    .catch(function(v) { console.log("rej:", v); /* rej: 4 */ });

function wait(v) {
    var deferred = Q.defer();
    if(v === 4) { deferred.reject(v); } else { deferred.resolve(v+1); }
    return deferred.promise;
}

Promise Chaining w/Throw

wait(1)
    .then(function(v) { return wait(v); })
    .then(function(v) { throw new Error(v); })
    .then(function(v) { return wait(v); })
    .then(function(v) { return v + 1; })
    .then(function(v) { console.log("res:", v); /* not called */ })
    .catch(function(v) {
        console.log("rej:", v); /* rej: [Error: 3] */
    });

function wait(v) {
    var deferred = Q.defer();
    deferred.resolve(v + 1);
    return deferred.promise;
}

Promise Resolved Only Once

var rv = 0;
var deferred = Q.defer();
function wait() {
    deferred.resolve(rv += 1);
    console.log("rv", rv);
    return deferred.promise;
}

wait().then(function(v) { console.log("v", v); });
wait().then(function(v) { console.log("v", v); });
wait().then(function(v) { console.log("v", v); });

// rv 1
// rv 2
// rv 3
// v 1
// v 1
// v 1

Promise Rejected Only Once

var rv = 0;
var deferred = Q.defer();
function wait() {
    deferred.reject(rv += 1);
    console.log("rv", rv);
    return deferred.promise;
}

wait().catch(function(v) { console.log("v", v); });
wait().catch(function(v) { console.log("v", v); });
wait().catch(function(v) { console.log("v", v); });

// rv 1
// rv 2
// rv 3
// v 1
// v 1
// v 1

Serial Promises

console.time("run");
wait()
    .then(function(v) { return wait(v); })
    .then(function(v) { return wait(v); })
    .then(function(v) { console.timeEnd("run"); /* run: 3000ms */ });

function wait() {
    return Q.delay(1000);
}

Parallel Promises

console.time("run");
Q.all([
    wait(),
    wait(),
    wait()
]).then(function(v) {
    console.timeEnd("run"); /* run: 1000ms */
});

function wait() {
    return Q.delay(1000);
}

Chained Catch

someWebRequest()
    .then(function(data) {
        return data.map(function(d) {
            return d; // Some transformation ...
        });
    })
    .catch(function(err) {
        return { error: "Something happened!" };
    })
    .then(function(data) {
        console.log(data); // { error: "Something happened!" }
    });

function someWebRequest() {
    return Q.reject();
}

Javascript Resources

Javascript Concepts