Lambda JavaScript¶
Writing Real Programs… with JavaScript!?
Andrew Montalenti, CTO
 
JavaScript iteration¶
var nums = [45, 23, 51, 32, 5];
for (var i = 0; i < nums.length; i++) {
    console.log(i, nums[i]);
}
// 0 45
// 1 23
// 2 51
// 3 32
// 4 5
But, what’s going on here?¶
window.nums = window.Array(45, 23, 51, 32, 5);
for (window.i = 0; window.i < window.nums.length; window.i++) {
    window.console.log(window.i, window.nums[window.i]);
}
Some first JS rules¶
[1, 2, 3] is like Array(1, 2, 3); what Python calls a “list”, JS calls an “Array”.
To iterate an array, you must get an index (i) and index into it (nums[i]).
Making variables with var outside of a function sets or overrides a global variable.
This even applies to constructs that seem to have block scope, like for loops.
Fixing it with function scope¶
(function() {
    // creates a "temporary" local function scope
    var nums = [1, 2, 3];
    for (var i = 0; i < nums.length; i++) {
        console.log(i, nums[i]);
    }
    z = "some value"; // warning!
})();
// now back in global scope
console.log(typeof nums === 'undefined');
console.log(typeof i === 'undefined');
console.log(typeof z === 'undefined');
// 0 1
// 1 2
// 2 3
// true
// true
// false
Some more JS rules¶
Check whether a variable was set with 'undefined', e.g.
console.log(typeof nums === 'undefined'); // true
console.log(typeof z === 'undefined'); // false
The pattern (function() { ... })(); creates a local scope by using a
“self-executing function”, aka IIFE, or “immediately invoked function
expression”. This weird-looking pattern can be explained here:
var someFunction = function() { /* this part defines => */
    var nums = [1, 2, 3];
    console.log(nums);
};
someFunction /* this part invokes => */ ();
Now, some style rules¶
When writing JS code, you need to heed the following:
- When JavaScript applications are combined via <script>tags in the browser, they all have access to the global scope.
- You must always be mindful of globals; your “application” should only use one global variable, use a good unique name for it, and then store everything else in there.
- For example, Parse.ly’s JavaScript tracker uses the global name PARSELY.
- jQuery uses $andjQuery(which are aliases).
As for scope¶
- Always use varto set local function-scoped variables.
- Always put vardefinitions at the top of your function.
- Always wrap your code in functions, either “self-executing” or “normal” ones.
Introducing functions¶
function printIndexed(arr) {
    for (var i = 0; i < arr.length; i++) {
        console.log(i, arr[i]);
    }
};
(function() {
    var nums = [1, 2, 3];
    printIndexed(nums);
})();
// 0 1
// 1 2
// 2 3
Named Functions¶
function name(arguments) {
    // ... body ...
};
// => basically does =>
window.name = function(arguments) {
    // ... body ...
};
// with a "twist"
In a lot of beginner JavaScript code, this is the only function
you’ll see used. Some tricky rules related to “function hoisting”.
What’s broken here?¶
(function() {
    function printIndexed(arr) {
        for (var i = 0; i < arr.length; i++) {
            console.log(i, arr[i]);
        }
    };
})();
(function() {
    var nums = [1, 2, 3];
    printIndexed(nums);
})();
Will this work?¶
(function() {
    function printIndexed(arr) {
        for (var i = 0; i < arr.length; i++) {
            console.log(i, arr[i]);
        }
    };
    var nums = [1, 2, 3];
    printIndexed(nums);
})();
What we’re learning¶
- Named function declarations operate similarly to var.
- If they are within another function, they will only set scope within that function.
- But this also raises another issue – nested scopes?
Will this work?¶
(function() {
    var nums = [1, 2, 3];
    function printIndexed() {
        for (var i = 0; i < nums.length; i++) {
            console.log(i, nums[i]);
        }
    };
    printIndexed();
})();
Can we make our function generic?¶
function iterateIndexed(arr, callback) {
    for (var i = 0; i < arr.length; i++) {
        callback(i, arr[i]);
    }
};
(function() {
    var nums = [1, 2, 3];
    iterateIndexed(nums, /* what goes here? */);
})();
Using an “in-line” function¶
function iterateIndexed(arr, callback) {
    for (var i = 0; i < arr.length; i++) {
        callback(i, arr[i]);
    }
};
(function() {
    var nums = [1, 2, 3];
    iterateIndexed(nums, function(idx, elem) {
        console.log(idx, elem);
    });
})();
Using a “higher-order” reference¶
function iterateIndexed(arr, callback) {
    for (var i = 0; i < arr.length; i++) {
        callback(i, arr[i]);
    }
};
(function() {
    var nums = [1, 2, 3];
    iterateIndexed(nums, console.log);
    // why does this work?
})();
A couple more each implementations¶
(function() {
    var nums = [1, 2, 3];
    // prints same thing
    jQuery.each(nums, console.log);
    // prints same thing
    nums.forEach(function(elem, idx, arr) {
        console.log(idx, elem);
    });
})();
Our first “browser compatibility issue”¶
- Should you use jQuery.each, or the “built-in”Array.forEach?
- Answer, like many things in JS: it depends.
- For full browser compatibility, you can’t.
- It’s not in IE8 and below, for example. And older browsers from same era.
- Array.forEachwas added in “ECMA-262 standard in the 5th edition”.
- You can Polyfill it using the standard itself.
- Issues like this are why Babel exists.
The Microsoft bottleneck¶
IE has 11.87% global market share (IE 6-11).
| Version | Share | JS spec | 
|---|---|---|
| IE 11 | 77% | ES5 | 
| IE 8 | 10% | ES3, some quirks | 
| IE 9 | 4.5% | ES5* | 
| IE 10 | 4% | ES5 | 
| IE 6 | 1.8% | ES3, many quirks | 
| IE 7 | 1.3% | ES3, many quirks | 
- 2012-2013: ES 5 compatible browsers released.
- 2017-2018: ES 6/7 compatible browsers released.
Explaining this with forEach¶
[1, 2, 3].forEach(function(elem, idx, arr) {
    console.log(idx, elem);
});
One interesting thing about this code is that the forEach function
must “know” about the input array merely by virtue of the . dot operator.
How does it know? Is there a JavaScript facility that lets you get access to “the object from which this function was called”?
Sort of. For this, we need to take a small detour.
Building our own Stack¶
var stack = Stack([1, 2, 3]);
stack.forEach(function(elem, idx, arr) {
    console.log(idx, elem);
});
Can we build our own Stack object that supports the forEach style call?
Building our own Stack¶
function Stack(arr) {
    this.arr = arr;
    this.forEach = function(callback) {
        for (var i = 0; i < this.arr.length; i++) {
            callback(this.arr[i], i, this.arr);
        }
    }
};
var stack = Stack([1, 2, 3]);
console.log(typeof arr === "undefined"); // false
stack.forEach(function(elem, idx, arr) {
    console.log(idx, elem);
});
// TypeError: cannot read property 'forEach' of undefined
What hath we wrought?
The new keyword¶
Our call to Stack is a “bare function call”.
It turns out, with these, this is bound to the global object, aka window.
That’s why arr was actually defined globally after we called Stack()!
To “fix this”, we need to use new Stack([1, 2, 3]);… here, JS will
bind this to a new Object before calling the Stack function.
The function will then return the new object (that is, return this).
A working Stack¶
function Stack(arr) {
    this.arr = arr;
    this.forEach = function(callback) {
        for (var i = 0; i < this.arr.length; i++) {
            callback(this.arr[i], i, this.arr);
        }
    }
};
var stack = new Stack([1, 2, 3]);
console.log(typeof arr === "undefined"); // true
stack.forEach(function(elem, idx, arr) {
    console.log(idx, elem);
});
// 0 1
// 1 2
// 2 3
What’s this in the callback?¶
We have a callback function, will this refer to the Stack in there?
If so, we should be able to access this.arr and compare it to arr.
var stack = new Stack([1, 2, 3]);
stack.forEach(function(elem, idx, arr) {
    console.log(this.arr === arr);
});
// false
// false
// false
Huh. What’s wrong here?
Go back to our rule: bare function call¶
function Stack(arr) {
    this.arr = arr;
    this.forEach = function(callback) {
        for (var i = 0; i < this.arr.length; i++) {
            callback(this.arr[i], i, this.arr);
            // ^^^^^ problematic call
        }
    }
};
There is a utility available to help us: Function.call.
We can translate this to:
callback.call(this, i, this.arr[i], this.arr);
Even better Stack¶
function Stack(arr) {
    this.arr = arr;
    this.forEach = function(callback) {
        for (var i = 0; i < this.arr.length; i++) {
            callback.call(this, this.arr[i], i, this.arr);
        }
    }
};
var stack = new Stack([1, 2, 3]);
stack.forEach(function(elem, idx, arr) {
    console.log(this.arr === arr);
});
// true
// true
// true
Can we “borrow” the Stack.forEach?¶
function Stack(arr) {
    this.arr = arr;
};
Stack.prototype.forEach = function(callback) {
    for (var i = 0; i < this.arr.length; i++) {
        callback(this.arr[i], i, this.arr);
    }
};
(new Stack([1])).forEach(function(elem) {
    console.log(elem);
});
// 1
var obj = {"arr": [1]}
Stack.prototype.forEach.call(obj, function(elem) {
    console.log(elem);
});
// 1
Quick comparison¶
| Idea | Python | JavaScript | 
|---|---|---|
| Binding | label = val | var label = val | 
| Default Scope | Local | Global | 
| Iteration | for | foror.forEach | 
| Functions | def, lambda | function()forms | 
| File Open | open() | It’s Complicated | 
| Classes | class | On your own | 
| Namespaces | Modules | On your own | 
| Imports | import | On your own | 
| Data Structs | {} [] (,) | {}* [] | 
JavaScript unique stuff¶
| Idea | Python | JavaScript | 
|---|---|---|
| Anonymous Functions | Limited lambda | Built-in | 
| Performance | via C, Cython, etc. | via V8, Node | 
| Language Evolution | Python 3 (10 years) | Babel (annual) | 
| Object Orientation | Trad’l, class-based | Prototypal | 
Module pattern (1)¶
window.Collections = Object.create(null);
(function(root) {
    function Stack(arr) {
        this.arr = arr;
    };
    Stack.prototype.forEach = function(callback) {
        for (var i = 0; i < this.arr.length; i++) {
            callback(this.arr[i], i, this.arr);
        }
    };
    // export
    root.Stack = Stack;
})(window.Collections);
var stack = new Collections.Stack([1, 2, 3]);
stack.forEach(console.log);
Module pattern (2)¶
In standard JS (even up to ES5), this is your only option for modularization.
In ES5, this went into a terrible direction as two “popular” module systems took hold in open source: CommonJS (from Node) and AMD (aka RequireJS).
This created a mess in the community, but ES6 introduced a formal and language-supported module system. This is one of the biggest reasons to adopt a Babel toolchain and ES6+: that modularization is a mess otherwise.
BUT: the cool thing is that all modules basically work the way described on the last slide.
Exercise!¶
Implement:
- Stack.push: add element to Stack
- Stack.peek: look at top element on Stack
- Stack.pop: pop top element and return it
- Stack.clear: clear this to empty; hint:- forEach => pop
- Stack.extend: join this to that; hint:- forEach/push
- Stack.copy: copy this into new; hint:- extend new
Till next time!¶
Still to come:
- Preview of ES6 scoping rules and modules.
- Objectvs- dictand dynamic dispatch.
- Binary search basic algorithm.
- Binary search recursive version with named function expressions.
- Big-O overview for lists (Array) vs hashes (Object).
- Implementing a Tree.
- How Treerelates to browser DOM.
