- Declare global array to keep track of functions even after they have been returned in a lexical scope.
var sagas = [];
var hero = newHero();
var newSaga = function(){
var foil = newFoil();
sagas.push(function(){ // Push will store a function object to the sagas array which will persist.
var deed = newDeed();
log(deed + log + foil);
});
}
newSaga();
/*
Context of a function will always be created as a child of the context it was defined within
*/
sagas[0]; // Logs "BoyEyesRat"
/*
Creates new context in NEWSAGA1 context ... new deed var will be created
*/
sagas[0]; // Logs "BoyDigsRat"
/*
Creates new function to be pushed to sagas array in a new NEWSAGA2 context
*/
newSaga();
/*
Will still make lookup to deed var in NEWSAGA1 context
*/
sagas[0]; // Logs "BoyPinsRat"
/*
Will make lookup to deed var in NEWSAGA2 context
*/
sagas[1]; // Logs "BoyGetsET"
/*
DOES NOT CREATE NEW CONTEXT. uses SAGAS1.3 Context since sagas[1] has been called
*/
sagas[0]; // Logs "BoyPinsRat"
####MAIN
sagas = [{f}, {f}]
hero = "Boy"
newSaga = {f}
####NEWSAGA1
foil = "Rat"
####SAGAS1.1
deed = "Eyes"
####SAGAS1.2
deed = "Digs"
####sAGAS1.3
deed = "Pins"
####NEWSAGA2
foil = "ET"
####SAGAS2.1
deed = "Gets"
- this will be automatically bound to the correct object automatically
- How the interpreter knows which binding is correct, resembles the rules for positional function parameters
var obj = { // .. the object created by the literal this appears within
fn = function(a,b){ // .. the fn this appears within
log(this);
}
};
var obj2 = {method: obj.fn}; // .. an object that happens to have that fn as a property
obj.fn(3,4); // .. an "execution context"
obj.fn(3,4); // The object to the left of the dot (different from the literal definition!)
var fn = function(one, two){
log(this, one, two);
};
var r = {}, g = {}, b = {}, y = {};
r.method = fn;
/*
this becomes the object fn is bound to
*/
r.method(g,b) // r{}, g{}, b{}
/*
this becomes the global scope fn is within
*/
fn(g,b) // <global>, g{}, b{}
/*
.call overrides the <global> reference and sets it to the first param *
/
fn.call(r,g,b) // r{}, g{}, b{}
/*
.call also overrides the method access rules, will be bound to y
*/
r.method.call(y,g,b) // y{}, g{}, b{}
//USE IN CALLBACKS
/*
fn is forced to be invoked without parameters in setTimeout implementation. setTimeout has no way of knowing the input params here
*/
setTimeout(fn, 1000) // <global>, undefined, undefined
/*
Same as above
*/
setTimeout(r.method, 1000) // <global>, undefined, undefined
/*
Define a function without params (since it will be invoked without params anyway) and have it contain the params you desire.
*/
setTimeout(function(){ // r{}, g{}, b{}
r.method(g,b);
})
//Mock Implementation
var setTimeout = function(cb, ms){
arbitraryDelay(ms);
cb();
}
log(this) // <global>
new r.method(g,b) // brand new object
Can copy an object to another object s.t. second object has all properties first one has. OR Can make new object behave like the original by delegating failed lookups to the original one.
Why? To save memory/ share code.
var gold = {a:1};
log(gold.a); // 1
log(gold.z); // undef
/* This is a ONE-TIME PROPERTY COPY */
var blue = extend({},gold); // Assume there is for loop copying the values of gold to blue...
log(blue.z); // undef
/* this is ONGOING LOOKUP-TIME DELEGATION */
var rose = Object.create(gold); //Constantly uses gold as "fallback" for property checking
rose.b = 2;
log(rose.a) // 1
log(rose.z) // undef
gold.z = 3;
log(blue.z); // undef
log(rose.z); // 3
...Rose has a fallback too, the Object prototype. All objects in JS have a top level prototype to fall back to, the Object prototype. Arrays have an independant array prototype, which is delegated to by the Object prototype.
rose.toString(); //toString() is a top level function that all JS objects have
var array1 = [];
array1.constructor() = Array
var obj1 = {};
obj1.constructor() = Object
/*The two vars above have their own constructors dependant on their prorotype! */
Reusing code!
//run.js
var amy = {loc:1};
amy.loc++;
var ben = {loc:9};
ben.loc++;
//library.js
var move = function(car){ // This is a decorator pattern
car.loc++;
};
//run.js
var amy = {loc:1};
move(amy);
var ben = {loc.9};
move(ben);
//library.js
var carlike = function(obj, loc){ // This is a decorator pattern
obj.loc = loc;
return obj;
};
var move = function(car){
car.loc++;
};
//run.js
var amy = carlike({}, 1);
move(amy);
var ben = carlike({}, 9);
move(ben);
//library.js
var carlike = function(obj, loc){ // This is a decorator pattern
obj.loc = loc;
obj.move = function(){ // New closure scope is created
obj.loc++;
};
return obj;
};
//run.js
var amy = carlike({}, 1);
amy.move();
var ben = carlike({}, 9);
ben.move();
Abstract out code because
- Less code
- Faster refactoring
Classes build the object that it will augment VS. Decoraters accept the object that it will augment
/*Class*/
carlike = function(var){
//?...
};
/*Decorator*/
carlike = function(obj, var){
//?....
};
//library.js
var Car = function(loc){
var obj = {loc:loc};
extend(obj, Car.methods);
obj.move = move;
return obj;
};
Car.methods = {
move : function(){ //Prevents new UNIQUE function "move" to be created everytime the object is instantiated
this.loc++;
};
};
//run.js
var amy = Car(1);
amy.move();
var ben = Car(9);
ben.move();
Functions can store properties of objects.
Delegate lookup of move function to another prototypal class.
var Car = function(loc){
var obj = Object.create(Car.prototype); // Make new instance objects delegate their failed lookup to Car.methods - NO LONGER NEEDED TO COPY METHODS!
obj.loc = loc;
return obj;
};
Car.methods = {
move : function(){
this.loc++;
}
};
/*Built in ".methods object", this is ONLY naming.*/
Car.prototype.move = function(){
this.loc++;
};
console.log(amy.prototype.constructor) // Car
console.log(amy instanceof Car) // true , instance of looks to see if there is Car.prototype in amy object
- Function to create object
- Line to generate instance of object
- Delegation from new obj to some prototype object
- Something to augment this object to make it unique from other objects in same class
...Very common process, JS has built in .prototype object to serve EXACT same purpose as .methods
amy's prototype is Car.prototype means different than Car's prototype is Car.prototype. Car is a function object, thus failed lookups will fall back to a function prototype, where as amy's prototype is Car.prototype
When car functions run, it will delegate their failed lookups to Car.prototype.
We can re-write our code so as to use the new operator that so many other classes use...
Syntactic convienience!
var Car = function(loc){
var obj = Object.create(Car.prototype);
obj.loc = loc;
return obj;
};
Car.prototype.move = function(){
this.loc++;
};
Will now look like this during interpretation
//library.js
var Car = function(loc){
this = Object.create(Car.prototype); // interpreter inserted
this.loc = loc;
return this; // interpreter inserted
};
Car.prototype.move = function(){
this.loc++;
};
//run.js
var ben = new Car(9);
ben.move();
Will now finally look like this
//library.js
var Car = function(loc){ // Differences between all object instances of this class.
this.loc = loc;
};
Car.prototype.move = function(){ // Similarities between all object instances of this class.
this.loc++;
};
//run.js
var ben = new Car(9);
ben.move();
Create a functional super class Car that has subclass Van and Cop.
//library.js
var Car = function(){
var obj = {loc:loc};
obj.move = function(){
obj.loc++;
};
return obj
};
var Van = function(loc){
var obj = Car(loc);
obj.grab = function{/*...*/};
return obj;
};
var Cop = function(loc){
var obj = Car(loc);
obj.call = function{/*...*/};
return obj;
};
//run.js
var amy = Van(1);
amy.move();
var cal = Cop(9);
cal.move();
var Car = function(loc){
this.loc = loc;
};
Car.prototype.move = function(){
this.loc++;
};
var Van = function(loc){
Car.call(this, loc); // Call JUST the car constructor in this scope with loc var. No car prototype
};
Van.prototype = Object.create(Car.prototype); // Create delegation of Van.prototype to Car.prototype for failed lookup of .move()
Van.prototype.grab = function(){/*...*/};
Van.prototype.constructor = Van; // This is lost when we created Van.prototype as a delegation of Car.prototype
var zed = new Car(3);
zed.move();
var amy = new Van(9);
amy.move();
amy.grab();
N.B.: Van.prototype = new Car() is still used by alot of people for delegating failed calls from Van.protoytpe to Car.prototype