Functions
class: center, middle .title[ Front-end training # JS Functions (cont.) ] --- class: center, middle .title[ # Closures ? ] --- # Closures A closure is the combination of a function bundled together (enclosed) with references to its surrounding state (the lexical environment). In other words, a closure gives you access to an outer function’s scope from an inner function. In JavaScript, closures are created every time a function is created, at function creation time. To use a closure, simply define a function inside another function and expose it. To expose a function, return it or pass it to another function. The inner function will have access to the variables in the outer function scope, even after the outer function has returned. ``` function bind(fn, context) { return function() { return fn.apply(context, arguments); }; } var alwaysSameContext = bind(function() { console.log(this.value); }, { value: 'custom-object' }); alwaysSameContext(); // -> 'custom-object' ``` --- class: center, middle .title[ # 'this' keyword ? ] --- # 'this' keyword In javascript `this` indicates current __context__ in which function is called. ``` function showThis() { console.log(this); } ``` There can be total 4 contexts: - __window__ if you simply call function `showThis(); // -> window` - DOM element, if you call function as event listener ` document.body.onclick = showThis; // ... -> <body...` - object, if you call function as method: ``` var obj = { fn: showThis }; obj.fn(); // -> Object { fn: function } ``` - new object, if function used as constructor: `new showThis; // -> showThis {}` --- # More on calling functions There are methods which allow you to change context: ``` Function.prototype.call(context/*, comma-separated list of arguments */); Function.prototype.apply(context/*, list of arguments as array */); ``` ``` var obj1 = { value: 'obj1', showValue: function(arg, arg2) { console.log(this.value, arg + arg2); } }; var obj2 = { value: 'obj2' }; obj1.showValue(1, 2); // -> obj1 3 obj1.showValue.call(obj2, 1, 2); // -> obj2 3 obj1.showValue.apply(obj2, [1, 2]); // -> obj2 3 ``` If you pass `null` as context - your context will become `window`. --- # Bind that 'this' Bind does not call function immediately, it returns new function with "bound" context ``` var showObj2Value = obj.showValue.bind(obj2); showObj2Value(1, 2); // -> obj2 3 ``` --- class: center, middle .title[ # Call Stack ? ] --- # Call Stack A call stack is a mechanism for an interpreter (like the JavaScript interpreter in a web browser) to keep track of its place in a script that calls multiple functions — what function is currently being run, what functions are called from within that function and should be called next, etc. - When a script calls a function, the interpreter adds it to the call stack and then starts carrying out the function. - Any functions that are called by that function are added to the call stack further up, and run where their calls are reached. - When the main function is finished, the interpreter takes it off the stack and resumes execution it left off in the main code listing. - If the stack takes up more space than it had assigned to it, it results in a "stack overflow" error. --- class: center, middle .title[ # Call Stack Demos # http://latentflip.com/loupe/ ] --- # Call Stack Demos ``` function multiply(a, b) { return a * b; } function square(n) { return multiply(n, n); } function printSquare(n) { var squared = square(n); console.log(squared); } printSquare(4); ``` --- # Call Stack Demos In-browser - visualize error with a stack trace ``` function foo () { throw new Error('Opps!'); } function bar () { foo(); } function baz () { bar(); } baz(); ``` --- # Recursion A recursive function is a function which calls itself. ``` var factorial = function(number) { if (number <= 0) { // terminal case return 1; } else { // block to execute return (number * factorial(number - 1)); } }; factorial(6); // -> 720 ``` --- # Call Stack Demos "Blowing" the stack ``` function foo () { return foo(); } foo(); ``` --- # Recursion (cont.) Function that builds DOM according to given config: ``` function createEl(domObj) { var el = document.createElement(domObj.tag); if (domObj.children) { for (var i = 0; i < domObj.children.length; i++) { el.appendChild(createEl(domObj.children[i])); } } return el; } createEl({ tag: 'div', children: [ { tag: 'span' }, { tag: 'ul', children: [ { tag: 'li' } ] } ] }); ``` --- class: center, middle .title[ # Async function invokation ] --- # Callback functions A common JS practice is to pass some callback-function if result don't appear immediately (think about calling to server...): ``` const logTick => console.log(Date.now()); function executeLoop(fn, ms) { if (fn) { executeLoop.fns.push(fn); } else { setTimeout(function() { for (var i = 0; i < executeLoop.fns.length; i++) { executeLoop.fns[i](); } executeLoop(null, ms); }, ms); } } executeLoop.fns = []; // add some functions to execute executeLoop(logTick); executeLoop(logTick); // and start loop with 10 ms executeLoop(null, 100); ``` --- # Event Loop Main Parts: - Call Stack - Task Queue - Web APIs --- # Web APIs + Callback Queue Delayed execution ``` console.log('hi'); setTimeout(function cb() { console.log('there'); }, 5000); console.log('bye'); ``` --- # Web APIs + Callback Queue Non-blocking execution ``` console.log('hi'); setTimeout(function cb() { console.log('there'); }, 0); console.log('bye'); ``` --- # Web APIs + Callback Queue Delayed + Async execution ``` $.on('button', 'click', function onClick() { setTimeout(function timer() { console.log('You clicked the button!'); }, 2000); }); console.log("Hi!"); setTimeout(function timeout() { console.log("Click the button!"); }, 5000); console.log("Later!"); ``` --- # References - Event Loop - https://www.youtube.com/watch?v=8aGhZQkoFbQ - http://eloquentjavascript.net/05_higher_order.html