Diving Deep into
JavaScript Functions
By: Colin DeCarlo
colindecarlo@colindecarlo
About Me
colindecarlo@colindecarlo
About Me
colindecarlo@colindecarlo
Colin DeCarlo
Software Developer
PHP & JavaScript
colindecarlo@colindecarlo
colindecarlo@colindecarlo
Why?
colindecarlo@colindecarlo
Other people need to know this
colindecarlo@colindecarlo
–Johnny Appleseed
“Type a quote here.”
colindecarlo@colindecarlo
–Johnny Appleseed
“Type a quote here.”
colindecarlo@colindecarlo
–Johnny Appleseed
“Type a quote here.”
colindecarlo@colindecarlo
Warning
This talk will be technical
colindecarlo@colindecarlo
Agenda
Agenda
(function printAgenda() {
var topics = [
'What is a Function?',
'How do I create these "functions"?',
'What happens when I invoke a Function?',
'How do I wield this power?'
];
return topics.join("n");
})();
// => 'What is a Function?',
// => 'How do I create these "functions"?,
// => 'What happens when I invoke a Function?',
// => 'How do I wield this power?"
What is a JavaScript Function?
colindecarlo@colindecarlo
Functions are Objects
With properties, methods and a {{Prototype}}
Functions are Objects
With properties, methods and a {{Prototype}}
function add(x, y) {
return x + y;
}
Functions are Objects
With properties, methods and a {{Prototype}}
function add(x, y) {
return x + y;
}
Own Properties Inherited Methods
name: add
length: 2
Function.prototype
call
apply
bind
Object.prototype
toString
How do I create these “functions”?
colindecarlo@colindecarlo
Named Function
function named_function() {
return "I'm a named function!"
}
Function Expression
var function_expression = function () {
return "I'm a function expression!";
}
Immediately Invoked Function Expression
(function () {
var button = document.getElementById(
'submit_button'
);
button.addEventListener('click',
function (e) {
this.classList.add('disabled');
}
);
})();
colindecarlo@colindecarlo
colindecarlo@colindecarlo
What happens when I invoke a
function?
colindecarlo@colindecarlo
add(2, 3);
add(2, 3);
Execution Context
add(2, 3);
Execution Context
scope chain
add(2, 3);
Execution Context
scope chain
Activation
Object
add(2, 3);
Execution Context
scope chain
Activation
Object
add(2, 3);
Execution Context
scope chain
Activation
Object
colindecarlo@colindecarlo
colindecarlo@colindecarlo
Scopes
Scopes
var globalVar = 'found in global scope';
function global_function() {
var localVar = 'found in global_function';
function inner_function() {
var innerVar = 'found in inner_function';
console.log(globalVar, localVar, innerVar);
console.log(notFoundAnywhere);
}
inner_function();
}
global_function();
global scope
globalVar: ‘found in global scope’
global_function: [Function]
Scopes
var globalVar = 'found in global scope';
function global_function() {
var localVar = 'found in global_function';
function inner_function() {
var innerVar = 'found in inner_function';
console.log(globalVar, localVar, innerVar);
console.log(notFoundAnywhere);
}
inner_function();
}
global_function();
global scope
globalVar: ‘found in global scope’
global_function: [Function]
global_function scope
Scopes
var globalVar = 'found in global scope';
function global_function() {
var localVar = 'found in global_function';
function inner_function() {
var innerVar = 'found in inner_function';
console.log(globalVar, localVar, innerVar);
console.log(notFoundAnywhere);
}
inner_function();
}
global_function();
global scope
globalVar: ‘found in global scope’
global_function: [Function]
localVar: ‘found in global_function’
inner_function: [Function]
global_function scope
Scopes
var globalVar = 'found in global scope';
function global_function() {
var localVar = 'found in global_function';
function inner_function() {
var innerVar = 'found in inner_function';
console.log(globalVar, localVar, innerVar);
console.log(notFoundAnywhere);
}
inner_function();
}
global_function();
global scope
globalVar: ‘found in global scope’
global_function: [Function]
localVar: ‘found in global_function’
inner_function: [Function]
global_function scope
inner_functon scope
Scopes
var globalVar = 'found in global scope';
function global_function() {
var localVar = 'found in global_function';
function inner_function() {
var innerVar = 'found in inner_function';
console.log(globalVar, localVar, innerVar);
console.log(notFoundAnywhere);
}
inner_function();
}
global_function();
global scope
globalVar: ‘found in global scope’
global_function: [Function]
localVar: ‘found in global_function’
inner_function: [Function]
innerVar: ‘found in inner_function’
global_function scope
inner_functon scope
Scopes
var globalVar = 'found in global scope';
function global_function() {
var localVar = 'found in global_function';
function inner_function() {
var innerVar = 'found in inner_function';
console.log(globalVar, localVar, innerVar);
console.log(notFoundAnywhere);
}
inner_function();
}
global_function();
// => found in global scope, found in global_function, found in inner_function
// => undefined
global scope
globalVar: ‘found in global scope’
global_function: [Function]
localVar: ‘found in global_function’
inner_function: [Function]
innerVar: ‘found in inner_function’
global_function scope
inner_functon scope
colindecarlo@colindecarlo
colindecarlo@colindecarlo
Activation Object
colindecarlo@colindecarlo
this
contains a reference to some object
add(2, 3); this: {{ object }}
var that = this;
colindecarlo@colindecarlo
A function which is simply executed
var debug = true;
function something_complicated() {
if (this.debug) {
console.log('Starting something_complicated');
}
// ...
return complex_result;
}
something_complicated();
A function which is simply executed
var debug = true;
function something_complicated() {
if (this.debug) {
console.log('Starting something_complicated');
}
// ...
return complex_result;
}
something_complicated();
this === window;
// => true
Functions as object properties
var calculator = {
debug: true,
something_complicated: function () {
if (this.debug) {
console.log('Starting something_complicated');
}
// ...
return complex_result;
}
};
calculator.something_complicated();
Functions as object properties
this === calculator;
// => true
var calculator = {
debug: true,
something_complicated: function () {
if (this.debug) {
console.log('Starting something_complicated');
}
// ...
return complex_result;
}
};
calculator.something_complicated();
As element attributes
<button id="calculate" onmouseover="this.style['border-color'] = 'blue';">
Calculate
</button>
As element attributes
this === calculate;
// => true
<button id="calculate" onmouseover="this.style['border-color'] = 'blue';">
Calculate
</button>
Functions as event handlers
<script>
var calculate = document.getElementById('calculate');
calculate.addEventListener('click', function (e) {
this.innerHTML = 'Calculating...';
});
</script>
Functions as event handlers
this === calculate;
// => true
<script>
var calculate = document.getElementById('calculate');
calculate.addEventListener('click', function (e) {
this.innerHTML = 'Calculating...';
});
</script>
In a constructor function
function Circle(radius) {
this.radius = radius;
}
var myCircle = new Circle(42);
In a constructor function
this instanceof Circle;
// => true
function Circle(radius) {
this.radius = radius;
}
var myCircle = new Circle(42);
colindecarlo@colindecarlo
colindecarlo@colindecarlo
arguments
“Array like” object with numerical properties
this: window
add(2, 3);
arguments
“Array like” object with numerical properties
this: window
add(2, 3);
arguments: {
0: 2, 1: 3
}
Named Formal Parameters
this: window
add(2, 3); arguments: {
0: 2, 1: 3
}
Named Formal Parameters
this: window
add(2, 3); arguments: {
0: 2, 1: 3
}
x: 2
y: 3
Declaration and Hoisting
function concat() {
return " ".join(to_array(arguments));
function to_array(args) {
var words = [];
for (var i = 0; i < args.length; i++) {
words.push(args[i]);
}
return words;
}
}
Declaration and Hoisting
functions are declared
function concat() {
return " ".join(to_array(arguments));
function to_array(args) {
var words = [];
for (var i = 0; i < args.length; i++) {
words.push(args[i]);
}
return words;
}
}
Declaration and Hoisting
functions are declared
variables are declared
function concat() {
return " ".join(to_array(arguments));
function to_array(args) {
var words = [];
for (var i = 0; i < args.length; i++) {
words.push(args[i]);
}
return words;
}
}
Declaration and Hoisting
function concat() {
return " ".join(to_array(arguments));
function to_array(args) {
var words = [];
for (var i = 0; i < args.length; i++) {
words.push(args[i]);
}
return words;
}
}
function concat() {
function to_array(args) {
var words = [],
i;
for (i = 0; i < args.length; i++) {
words.push(args[i]);
}
return words;
}
return " ".join(to_array(arguments));
}
Hoisting: One Simple Rule
colindecarlo@colindecarlo
Hoisting: One Simple Rule
colindecarlo@colindecarlo
Don’t. Declare all variables and functions at the top.
colindecarlo@colindecarlo
colindecarlo@colindecarlo
What happens when I invoke a
function?
colindecarlo@colindecarlo
What happens when I invoke a
function?
colindecarlo@colindecarlo
What happens when I invoke a
function?
colindecarlo@colindecarlo
• The Execution Context is created and placed on top of the execution stack
What happens when I invoke a
function?
colindecarlo@colindecarlo
• The Execution Context is created and placed on top of the execution stack
• this and arguments are declared and assigned
What happens when I invoke a
function?
colindecarlo@colindecarlo
• The Execution Context is created and placed on top of the execution stack
• this and arguments are declared and assigned
• Named formal parameters are declared and assigned
What happens when I invoke a
function?
colindecarlo@colindecarlo
• The Execution Context is created and placed on top of the execution stack
• this and arguments are declared and assigned
• Named formal parameters are declared and assigned
• Named functions are declared
What happens when I invoke a
function?
colindecarlo@colindecarlo
• The Execution Context is created and placed on top of the execution stack
• this and arguments are declared and assigned
• Named formal parameters are declared and assigned
• Named functions are declared
• Local variables are declared
How may I wield this power?
colindecarlo@colindecarlo
Variadic Functions
var colors = enumer('RED', 'GREEN', ‘BLUE’),
selectedColor = ‘RED’;
colors.contains(selectedColor);
// => true
selectedColor = ‘YELLOW’;
colors.contains(selectedColor);
// => false
Variadic Functions
var colors = enumer('RED', 'GREEN', ‘BLUE’),
selectedColor = ‘RED’;
colors.contains(selectedColor);
// => true
selectedColor = ‘YELLOW’;
colors.contains(selectedColor);
// => false
function enumer() {
function contains (value) {
return this.hasOwnProperty(value);
}
var i, numValues = arguments.length,
values = {};
for (i = 0; i < numValues; i++) {
values[arguments[i]] = arguments[i];
}
values.contains = contains;
return values;
}
Variadic Functions
var colors = enumer('RED', 'GREEN', ‘BLUE’),
selectedColor = ‘RED’;
colors.contains(selectedColor);
// => true
selectedColor = ‘YELLOW’;
colors.contains(selectedColor);
// => false
function enumer() {
function contains (value) {
return this.hasOwnProperty(value);
}
var i, numValues = arguments.length,
values = {};
for (i = 0; i < numValues; i++) {
values[arguments[i]] = arguments[i];
}
values.contains = contains;
return values;
}
Variadic Functions
var colors = enumer('RED', 'GREEN', ‘BLUE’),
selectedColor = ‘RED’;
colors.contains(selectedColor);
// => true
selectedColor = ‘YELLOW’;
colors.contains(selectedColor);
// => false
function enumer() {
function contains (value) {
return this.hasOwnProperty(value);
}
var i, numValues = arguments.length,
values = {};
for (i = 0; i < numValues; i++) {
values[arguments[i]] = arguments[i];
}
values.contains = contains;
return values;
}
Variadic Functions
var colors = enumer('RED', 'GREEN', ‘BLUE’),
selectedColor = ‘RED’;
colors.contains(selectedColor);
// => true
selectedColor = ‘YELLOW’;
colors.contains(selectedColor);
// => false
function enumer() {
function contains (value) {
return this.hasOwnProperty(value);
}
var i, numValues = arguments.length,
values = {};
for (i = 0; i < numValues; i++) {
values[arguments[i]] = arguments[i];
}
values.contains = contains;
return values;
}
Variadic Functions
colindecarlo@colindecarlo
colindecarlo@colindecarlo
Modifying Context
Taking control of this
call(thisArg, arg1, …, argN)
use to set the value of this when you know the number
of arguments and what they are
Modifying Context
Taking control of this
apply(thisArg, argsArray)
use to set the value of this when you don’t know the number
of arguments
Modifying Context
Taking control of this
bind(thisArg, arg1, …, argN)
use to set the value of this but without executing the function
colindecarlo@colindecarlo
colindecarlo@colindecarlo
function memoize(func, context) {
var results = {},
wrapper = function () {
var args = Array.prototype.slice.call(arguments, 0),
key = ':'.join(args);
if (!results.hasOwnProperty(key)) {
results[key] = func.apply(context, args);
}
return results[key];
};
return wrapper;
}
function memoize(func, context) {
var results = {},
wrapper = function () {
var args = Array.prototype.slice.call(arguments, 0),
key = ':'.join(args);
if (!results.hasOwnProperty(key)) {
results[key] = func.apply(context, args);
}
return results[key];
};
return wrapper;
}
Partial Application with bind
the process of taking a function which accepts N arguments and turning it

into a function which accepts N - x arguments
Partial Application with bind
function add(x, y) {
return x + y;
}
var plus42 = add.bind(undefined, 42);
plus42(42);
the process of taking a function which accepts N arguments and turning it

into a function which accepts N - x arguments
Partial Application with bind
function add(x, y) {
return x + y;
}
var plus42 = add.bind(undefined, 42);
plus42(42);
// => 84
the process of taking a function which accepts N arguments and turning it

into a function which accepts N - x arguments
colindecarlo@colindecarlo
colindecarlo@colindecarlo
Closures
A function which “closes over” a variable from an outer scope
Closures
A function which holds a reference to another function’s Activation Object
thus allowing that function to access variables and functions defined
within it
Closures
function plusX(x) {
return function(n) {
return n + x;
};
}
A function which holds a reference to another function’s Activation Object
thus allowing that function to access variables and functions defined
within it
Closures
function plusX(x) {
return function(n) {
return n + x;
};
}
var plus42 = plusX(42);
plus42(1);
// => 43
A function which holds a reference to another function’s Activation Object
thus allowing that function to access variables and functions defined
within it
colindecarlo@colindecarlo
colindecarlo@colindecarlo
Trick Question
colindecarlo@colindecarlo
var funcs = [];
for (var i = 0; i < 5; i++) {
funcs.push(function () {
console.log(i);
});
}
for (var j = 0; j < 5; j++) {
funcs[j]();
}
// => 5
// => 5
// => 5
// => 5
// => 5
var funcs = [];
for (var i = 0; i < 5; i++) {
funcs.push(function () {
console.log(i);
});
}
for (var j = 0; j < 5; j++) {
funcs[j]();
}
// => 5
// => 5
// => 5
// => 5
// => 5
var funcs = [];
for (var i = 0; i < 5; i++) {
funcs.push(function () {
console.log(i);
});
}
for (var j = 0; j < 5; j++) {
funcs[j]();
}
// => 0
// => 1
// => 2
// => 3
// => 4
var funcs = [];
for (var i = 0; i < 5; i++) {
funcs.push((function (i) {
return function () {
console.log(i);
};
})(i));
}
for (var j = 0; j < 5; j++) {
funcs[j]();
}
// => 0
// => 1
// => 2
// => 3
// => 4
var funcs = [];
for (var i = 0; i < 5; i++) {
funcs.push((function (i) {
return function () {
console.log(i);
};
})(i));
}
for (var j = 0; j < 5; j++) {
funcs[j]();
}
colindecarlo@colindecarlo
colindecarlo@colindecarlo
Encapsulation: A Better enumer
Encapsulation: A Better enumer
var colors = enumer('RED', 'GREEN', 'BLUE');
colors.contains('RED');
// => true
colors.contains('YELLOW');
// => false
function enumer() {
function contains (value) {
return this.hasOwnProperty(value);
}
var i, numValues = arguments.length,
values = {};
for (i = 0; i < numValues; i++) {
values[arguments[i]] = arguments[i];
}
values.contains = contains;
return values;
}
Encapsulation: A Better enumer
var colors = enumer('RED', 'GREEN', 'BLUE');
colors.contains('RED');
// => true
colors.contains('YELLOW');
// => false
colors.YELLOW = 'YOLO';
colors.contains('YELLOW');
// => true
function enumer() {
function contains (value) {
return this.hasOwnProperty(value);
}
var i, numValues = arguments.length,
values = {};
for (i = 0; i < numValues; i++) {
values[arguments[i]] = arguments[i];
}
values.contains = contains;
return values;
}
Encapsulation: A Better enumer
var colors = enumer('RED', 'GREEN', 'BLUE');
colors.contains('RED');
// => true
colors.contains('YELLOW');
// => false
colors.YELLOW = 'YOLO';
colors.contains('YELLOW');
// => true
delete colors.RED;
colors.contains('RED');
// => false
function enumer() {
function contains (value) {
return this.hasOwnProperty(value);
}
var i, numValues = arguments.length,
values = {};
for (i = 0; i < numValues; i++) {
values[arguments[i]] = arguments[i];
}
values.contains = contains;
return values;
}
Encapsulation: A Better enumer
var colors = enumer('RED', 'GREEN', 'BLUE');
colors.contains('RED');
// => true
colors.contains('YELLOW');
// => false
colors.YELLOW = 'YOLO';
colors.contains('YELLOW');
// => true
colors.contains('contains');
// => true
delete colors.RED;
colors.contains('RED');
// => false
function enumer() {
function contains (value) {
return this.hasOwnProperty(value);
}
var i, numValues = arguments.length,
values = {};
for (i = 0; i < numValues; i++) {
values[arguments[i]] = arguments[i];
}
values.contains = contains;
return values;
}
Encapsulation: A Better enumer
var colors = enumer('RED', 'GREEN', 'BLUE');
colors.contains('RED');
// => true
colors.contains('YELLOW');
// => false
function enumer() {
function contains (value) {
return this.hasOwnProperty(value);
}
var i, numValues = arguments.length,
values = {};
for (i = 0; i < numValues; i++) {
values[arguments[i]] = arguments[i];
}
values.contains = contains;
return values;
}
Encapsulation: A Better enumer
var colors = enumer('RED', 'GREEN', 'BLUE');
colors.contains('RED');
// => true
colors.contains('YELLOW');
// => false
function enumer() {
var slice = Array.prototype.slice,
values = slice.call(arguments, 0);
return {
contains: function (value) {
return values.indexOf(value) >= 0;
}
};
}
Encapsulation: A Better enumer
var colors = enumer('RED', 'GREEN', 'BLUE');
colors.contains('RED');
// => true
colors.contains('YELLOW');
// => false
colors.YELLOW = 'YOLO';
colors.contains('YELLOW');
// => false
delete colors.RED;
colors.contains('RED');
// => true
function enumer() {
var slice = Array.prototype.slice,
values = slice.call(arguments, 0);
return {
contains: function (value) {
return values.indexOf(value) >= 0;
}
};
}
Encapsulation: A Better enumer
var colors = enumer('RED', 'GREEN', 'BLUE');
colors.contains('RED');
// => true
colors.contains('YELLOW');
// => false
colors.YELLOW = 'YOLO';
colors.contains('YELLOW');
// => false
delete colors.RED;
colors.contains('RED');
// => true
colors.contains('contains');
// => false
function enumer() {
var slice = Array.prototype.slice,
values = slice.call(arguments, 0);
return {
contains: function (value) {
return values.indexOf(value) >= 0;
}
};
}
colindecarlo@colindecarlo
colindecarlo@colindecarlo
colindecarlo@colindecarlo
Questions?
https://joind.in/15748
colindecarlo@colindecarlo
Questions?
https://joind.in/15748
var someObject = Class.extend({
init: function () {
$('.some-selector').on('click', this.onClick.bind(this));
},
onClick: function (e) {
var result = this.helperFunction();
return this.format(result);
}
};
Modifying Context
Taking control of this
Modifying Context
Taking control of this
`call`
`apply`
`bind`
Modifying Context
Taking control of this
`call`
`apply`
`bind`
Modifying Context
Taking control of this
`call`
`apply`
`bind`
use to set the value of `this` when you know the number
of arguments and what they are
Modifying Context
Taking control of this
`call`
`apply`
`bind`
use to set the value of `this` when you know the number
of arguments and what they are
use to set the value of `this` when you don’t know the
number of arguments
Modifying Context
Taking control of this
`call`
`apply`
`bind`
use to set the value of `this` when you know the number
of arguments and what they are
use to set the value of `this` when you don’t know the
number of arguments
use to set the value of `this` but without executing the
function
Modifying Context
Taking control of this
Modifying Context
Taking control of this
`call`
`apply`
`bind`
Modifying Context
Taking control of this
`call`
`apply`
`bind`
Modifying Context
Taking control of this
`call`
`apply`
`bind`
use to set the value of `this` when you know the number
of arguments and what they are
Modifying Context
Taking control of this
`call`
`apply`
`bind`
use to set the value of `this` when you know the number
of arguments and what they are
use to set the value of `this` when you don’t know the
number of arguments
Modifying Context
Taking control of this
`call`
`apply`
`bind`
use to set the value of `this` when you know the number
of arguments and what they are
use to set the value of `this` when you don’t know the
number of arguments
use to set the value of `this` but without executing the
function
Trick Question
colindecarlo@colindecarlo
function getEventListener(sniffer) {
function on(event, callback) {
window.addEventListener(event, callback);
}
if (sniffer.isIE()) {
function on(event, callback) {
window.attachEvent(event, callback);
}
}
return on;
}
var browserSniffer = {
isIE: function () {
return false;
}
};
getEventListener(browserSniffer).toString();
function getEventListener(sniffer) {
function on(event, callback) {
window.addEventListener(event, callback);
}
if (sniffer.isIE()) {
function on(event, callback) {
window.attachEvent(event, callback);
}
}
return on;
}
var browserSniffer = {
isIE: function () {
return false;
}
};
getEventListener(browserSniffer).toString();
function getEventListener(sniffer) {
function on(event, callback) {
window.addEventListener(event, callback);
}
if (sniffer.isIE()) {
function on(event, callback) {
window.attachEvent(event, callback);
}
}
return on;
}
var browserSniffer = {
isIE: function () {
return false;
}
};
getEventListener(browserSniffer).toString();
function getEventListener(sniffer) {
function on(event, callback) {
window.addEventListener(event, callback);
}
if (sniffer.isIE()) {
function on(event, callback) {
window.attachEvent(event, callback);
}
}
return on;
}
var browserSniffer = {
isIE: function () {
return false;
}
};
getEventListener(browserSniffer).toString();
function getEventListener(sniffer) {
function on(event, callback) {
window.addEventListener(event, callback);
}
if (sniffer.isIE()) {
function on(event, callback) {
window.attachEvent(event, callback);
}
}
return on;
}
var browserSniffer = {
isIE: function () {
return false;
}
};
getEventListener(browserSniffer).toString();
function getEventListener(sniffer) {
function on(event, callback) {
window.addEventListener(event, callback);
}
if (sniffer.isIE()) {
function on(event, callback) {
window.attachEvent(event, callback);
}
}
return on;
}
var browserSniffer = {
isIE: function () {
return false;
}
};
getEventListener(browserSniffer).toString();
function on(event, callback) {
window.attachEvent(event, callback);
}
Encapsulation the JavaScript Way
Encapsulation the JavaScript Way
function Vault(passcode) {
var locked = true,
data = {};
function checkPasscode(attemptedPasscode) {
return attemptedPasscode == passcode;
}
function grantAccess() {
locked = false;
return true;
}
this.isLocked = function () {
return locked;
};
this.lock = function () {
locked = true;
};
// ...
Encapsulation the JavaScript Way
function Vault(passcode) {
var locked = true,
data = {};
function checkPasscode(attemptedPasscode) {
return attemptedPasscode == passcode;
}
function grantAccess() {
locked = false;
return true;
}
this.isLocked = function () {
return locked;
};
this.lock = function () {
locked = true;
};
// ...
// ...
this.unlock = function (attemptedPasscode) {
checkPasscode(attemptedPasscode) && grantAccess();
};
this.store = function (key, value) {
if (this.isLocked()) {
throw new Error('The vault is locked');
}
data[key] = value;
};
this.retrieve = function (key) {
if (this.isLocked) {
throw new Error('The vault is locked');
}
return data[key];
};
}
$ node encapsulation.js
var v = new Vault('sekret');
v.isLocked();
true
v.unlock('sekret')
v.store('js_functions', 'Awesome')
v.retrieve(‘js_functions')
Awesome
v.lock()
v.isLocked()
true
v.retrieve('js_functions')
/Users/colin/Documents/talks/js_functions/encapsulation.js:40
throw new Error('The vault is locked');
^
Error: The vault is locked
at Vault.retrieve (/Users/colin/Documents/talks/js_functions/encapsulation.js:40:19)
at Object.<anonymous> (/Users/colin/Documents/talks/js_functions/encapsulation.js:68:3)
at Module._compile (module.js:456:26)
at Object.Module._extensions..js (module.js:474:10)
at Module.load (module.js:356:32)
at Function.Module._load (module.js:312:12)
at Function.Module.runMain (module.js:497:10)
at startup (node.js:119:16)
at node.js:906:3

JavaScript Functions

  • 1.
    Diving Deep into JavaScriptFunctions By: Colin DeCarlo colindecarlo@colindecarlo
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
    Other people needto know this colindecarlo@colindecarlo
  • 8.
    –Johnny Appleseed “Type aquote here.” colindecarlo@colindecarlo
  • 9.
    –Johnny Appleseed “Type aquote here.” colindecarlo@colindecarlo
  • 10.
    –Johnny Appleseed “Type aquote here.” colindecarlo@colindecarlo
  • 11.
    Warning This talk willbe technical colindecarlo@colindecarlo
  • 12.
  • 13.
    Agenda (function printAgenda() { vartopics = [ 'What is a Function?', 'How do I create these "functions"?', 'What happens when I invoke a Function?', 'How do I wield this power?' ]; return topics.join("n"); })(); // => 'What is a Function?', // => 'How do I create these "functions"?, // => 'What happens when I invoke a Function?', // => 'How do I wield this power?"
  • 14.
    What is aJavaScript Function? colindecarlo@colindecarlo
  • 15.
    Functions are Objects Withproperties, methods and a {{Prototype}}
  • 16.
    Functions are Objects Withproperties, methods and a {{Prototype}} function add(x, y) { return x + y; }
  • 17.
    Functions are Objects Withproperties, methods and a {{Prototype}} function add(x, y) { return x + y; } Own Properties Inherited Methods name: add length: 2 Function.prototype call apply bind Object.prototype toString
  • 18.
    How do Icreate these “functions”? colindecarlo@colindecarlo
  • 19.
    Named Function function named_function(){ return "I'm a named function!" }
  • 20.
    Function Expression var function_expression= function () { return "I'm a function expression!"; }
  • 21.
    Immediately Invoked FunctionExpression (function () { var button = document.getElementById( 'submit_button' ); button.addEventListener('click', function (e) { this.classList.add('disabled'); } ); })();
  • 22.
  • 23.
  • 24.
    What happens whenI invoke a function? colindecarlo@colindecarlo
  • 25.
  • 26.
  • 27.
  • 28.
    add(2, 3); Execution Context scopechain Activation Object
  • 29.
    add(2, 3); Execution Context scopechain Activation Object
  • 30.
    add(2, 3); Execution Context scopechain Activation Object
  • 31.
  • 32.
  • 33.
  • 34.
    Scopes var globalVar ='found in global scope'; function global_function() { var localVar = 'found in global_function'; function inner_function() { var innerVar = 'found in inner_function'; console.log(globalVar, localVar, innerVar); console.log(notFoundAnywhere); } inner_function(); } global_function(); global scope globalVar: ‘found in global scope’ global_function: [Function]
  • 35.
    Scopes var globalVar ='found in global scope'; function global_function() { var localVar = 'found in global_function'; function inner_function() { var innerVar = 'found in inner_function'; console.log(globalVar, localVar, innerVar); console.log(notFoundAnywhere); } inner_function(); } global_function(); global scope globalVar: ‘found in global scope’ global_function: [Function] global_function scope
  • 36.
    Scopes var globalVar ='found in global scope'; function global_function() { var localVar = 'found in global_function'; function inner_function() { var innerVar = 'found in inner_function'; console.log(globalVar, localVar, innerVar); console.log(notFoundAnywhere); } inner_function(); } global_function(); global scope globalVar: ‘found in global scope’ global_function: [Function] localVar: ‘found in global_function’ inner_function: [Function] global_function scope
  • 37.
    Scopes var globalVar ='found in global scope'; function global_function() { var localVar = 'found in global_function'; function inner_function() { var innerVar = 'found in inner_function'; console.log(globalVar, localVar, innerVar); console.log(notFoundAnywhere); } inner_function(); } global_function(); global scope globalVar: ‘found in global scope’ global_function: [Function] localVar: ‘found in global_function’ inner_function: [Function] global_function scope inner_functon scope
  • 38.
    Scopes var globalVar ='found in global scope'; function global_function() { var localVar = 'found in global_function'; function inner_function() { var innerVar = 'found in inner_function'; console.log(globalVar, localVar, innerVar); console.log(notFoundAnywhere); } inner_function(); } global_function(); global scope globalVar: ‘found in global scope’ global_function: [Function] localVar: ‘found in global_function’ inner_function: [Function] innerVar: ‘found in inner_function’ global_function scope inner_functon scope
  • 39.
    Scopes var globalVar ='found in global scope'; function global_function() { var localVar = 'found in global_function'; function inner_function() { var innerVar = 'found in inner_function'; console.log(globalVar, localVar, innerVar); console.log(notFoundAnywhere); } inner_function(); } global_function(); // => found in global scope, found in global_function, found in inner_function // => undefined global scope globalVar: ‘found in global scope’ global_function: [Function] localVar: ‘found in global_function’ inner_function: [Function] innerVar: ‘found in inner_function’ global_function scope inner_functon scope
  • 40.
  • 41.
  • 42.
  • 43.
    this contains a referenceto some object add(2, 3); this: {{ object }}
  • 44.
    var that =this; colindecarlo@colindecarlo
  • 45.
    A function whichis simply executed var debug = true; function something_complicated() { if (this.debug) { console.log('Starting something_complicated'); } // ... return complex_result; } something_complicated();
  • 46.
    A function whichis simply executed var debug = true; function something_complicated() { if (this.debug) { console.log('Starting something_complicated'); } // ... return complex_result; } something_complicated(); this === window; // => true
  • 47.
    Functions as objectproperties var calculator = { debug: true, something_complicated: function () { if (this.debug) { console.log('Starting something_complicated'); } // ... return complex_result; } }; calculator.something_complicated();
  • 48.
    Functions as objectproperties this === calculator; // => true var calculator = { debug: true, something_complicated: function () { if (this.debug) { console.log('Starting something_complicated'); } // ... return complex_result; } }; calculator.something_complicated();
  • 49.
    As element attributes <buttonid="calculate" onmouseover="this.style['border-color'] = 'blue';"> Calculate </button>
  • 50.
    As element attributes this=== calculate; // => true <button id="calculate" onmouseover="this.style['border-color'] = 'blue';"> Calculate </button>
  • 51.
    Functions as eventhandlers <script> var calculate = document.getElementById('calculate'); calculate.addEventListener('click', function (e) { this.innerHTML = 'Calculating...'; }); </script>
  • 52.
    Functions as eventhandlers this === calculate; // => true <script> var calculate = document.getElementById('calculate'); calculate.addEventListener('click', function (e) { this.innerHTML = 'Calculating...'; }); </script>
  • 53.
    In a constructorfunction function Circle(radius) { this.radius = radius; } var myCircle = new Circle(42);
  • 54.
    In a constructorfunction this instanceof Circle; // => true function Circle(radius) { this.radius = radius; } var myCircle = new Circle(42);
  • 55.
  • 56.
  • 57.
    arguments “Array like” objectwith numerical properties this: window add(2, 3);
  • 58.
    arguments “Array like” objectwith numerical properties this: window add(2, 3); arguments: { 0: 2, 1: 3 }
  • 59.
    Named Formal Parameters this:window add(2, 3); arguments: { 0: 2, 1: 3 }
  • 60.
    Named Formal Parameters this:window add(2, 3); arguments: { 0: 2, 1: 3 } x: 2 y: 3
  • 61.
    Declaration and Hoisting functionconcat() { return " ".join(to_array(arguments)); function to_array(args) { var words = []; for (var i = 0; i < args.length; i++) { words.push(args[i]); } return words; } }
  • 62.
    Declaration and Hoisting functionsare declared function concat() { return " ".join(to_array(arguments)); function to_array(args) { var words = []; for (var i = 0; i < args.length; i++) { words.push(args[i]); } return words; } }
  • 63.
    Declaration and Hoisting functionsare declared variables are declared function concat() { return " ".join(to_array(arguments)); function to_array(args) { var words = []; for (var i = 0; i < args.length; i++) { words.push(args[i]); } return words; } }
  • 64.
    Declaration and Hoisting functionconcat() { return " ".join(to_array(arguments)); function to_array(args) { var words = []; for (var i = 0; i < args.length; i++) { words.push(args[i]); } return words; } } function concat() { function to_array(args) { var words = [], i; for (i = 0; i < args.length; i++) { words.push(args[i]); } return words; } return " ".join(to_array(arguments)); }
  • 65.
    Hoisting: One SimpleRule colindecarlo@colindecarlo
  • 66.
    Hoisting: One SimpleRule colindecarlo@colindecarlo Don’t. Declare all variables and functions at the top.
  • 67.
  • 68.
  • 69.
    What happens whenI invoke a function? colindecarlo@colindecarlo
  • 70.
    What happens whenI invoke a function? colindecarlo@colindecarlo
  • 71.
    What happens whenI invoke a function? colindecarlo@colindecarlo • The Execution Context is created and placed on top of the execution stack
  • 72.
    What happens whenI invoke a function? colindecarlo@colindecarlo • The Execution Context is created and placed on top of the execution stack • this and arguments are declared and assigned
  • 73.
    What happens whenI invoke a function? colindecarlo@colindecarlo • The Execution Context is created and placed on top of the execution stack • this and arguments are declared and assigned • Named formal parameters are declared and assigned
  • 74.
    What happens whenI invoke a function? colindecarlo@colindecarlo • The Execution Context is created and placed on top of the execution stack • this and arguments are declared and assigned • Named formal parameters are declared and assigned • Named functions are declared
  • 75.
    What happens whenI invoke a function? colindecarlo@colindecarlo • The Execution Context is created and placed on top of the execution stack • this and arguments are declared and assigned • Named formal parameters are declared and assigned • Named functions are declared • Local variables are declared
  • 76.
    How may Iwield this power? colindecarlo@colindecarlo
  • 77.
  • 78.
    var colors =enumer('RED', 'GREEN', ‘BLUE’), selectedColor = ‘RED’; colors.contains(selectedColor); // => true selectedColor = ‘YELLOW’; colors.contains(selectedColor); // => false Variadic Functions
  • 79.
    var colors =enumer('RED', 'GREEN', ‘BLUE’), selectedColor = ‘RED’; colors.contains(selectedColor); // => true selectedColor = ‘YELLOW’; colors.contains(selectedColor); // => false function enumer() { function contains (value) { return this.hasOwnProperty(value); } var i, numValues = arguments.length, values = {}; for (i = 0; i < numValues; i++) { values[arguments[i]] = arguments[i]; } values.contains = contains; return values; } Variadic Functions
  • 80.
    var colors =enumer('RED', 'GREEN', ‘BLUE’), selectedColor = ‘RED’; colors.contains(selectedColor); // => true selectedColor = ‘YELLOW’; colors.contains(selectedColor); // => false function enumer() { function contains (value) { return this.hasOwnProperty(value); } var i, numValues = arguments.length, values = {}; for (i = 0; i < numValues; i++) { values[arguments[i]] = arguments[i]; } values.contains = contains; return values; } Variadic Functions
  • 81.
    var colors =enumer('RED', 'GREEN', ‘BLUE’), selectedColor = ‘RED’; colors.contains(selectedColor); // => true selectedColor = ‘YELLOW’; colors.contains(selectedColor); // => false function enumer() { function contains (value) { return this.hasOwnProperty(value); } var i, numValues = arguments.length, values = {}; for (i = 0; i < numValues; i++) { values[arguments[i]] = arguments[i]; } values.contains = contains; return values; } Variadic Functions
  • 82.
    var colors =enumer('RED', 'GREEN', ‘BLUE’), selectedColor = ‘RED’; colors.contains(selectedColor); // => true selectedColor = ‘YELLOW’; colors.contains(selectedColor); // => false function enumer() { function contains (value) { return this.hasOwnProperty(value); } var i, numValues = arguments.length, values = {}; for (i = 0; i < numValues; i++) { values[arguments[i]] = arguments[i]; } values.contains = contains; return values; } Variadic Functions
  • 83.
  • 84.
  • 85.
    Modifying Context Taking controlof this call(thisArg, arg1, …, argN) use to set the value of this when you know the number of arguments and what they are
  • 86.
    Modifying Context Taking controlof this apply(thisArg, argsArray) use to set the value of this when you don’t know the number of arguments
  • 87.
    Modifying Context Taking controlof this bind(thisArg, arg1, …, argN) use to set the value of this but without executing the function
  • 88.
  • 89.
  • 90.
    function memoize(func, context){ var results = {}, wrapper = function () { var args = Array.prototype.slice.call(arguments, 0), key = ':'.join(args); if (!results.hasOwnProperty(key)) { results[key] = func.apply(context, args); } return results[key]; }; return wrapper; }
  • 91.
    function memoize(func, context){ var results = {}, wrapper = function () { var args = Array.prototype.slice.call(arguments, 0), key = ':'.join(args); if (!results.hasOwnProperty(key)) { results[key] = func.apply(context, args); } return results[key]; }; return wrapper; }
  • 92.
    Partial Application withbind the process of taking a function which accepts N arguments and turning it
 into a function which accepts N - x arguments
  • 93.
    Partial Application withbind function add(x, y) { return x + y; } var plus42 = add.bind(undefined, 42); plus42(42); the process of taking a function which accepts N arguments and turning it
 into a function which accepts N - x arguments
  • 94.
    Partial Application withbind function add(x, y) { return x + y; } var plus42 = add.bind(undefined, 42); plus42(42); // => 84 the process of taking a function which accepts N arguments and turning it
 into a function which accepts N - x arguments
  • 95.
  • 96.
  • 97.
    Closures A function which“closes over” a variable from an outer scope
  • 98.
    Closures A function whichholds a reference to another function’s Activation Object thus allowing that function to access variables and functions defined within it
  • 99.
    Closures function plusX(x) { returnfunction(n) { return n + x; }; } A function which holds a reference to another function’s Activation Object thus allowing that function to access variables and functions defined within it
  • 100.
    Closures function plusX(x) { returnfunction(n) { return n + x; }; } var plus42 = plusX(42); plus42(1); // => 43 A function which holds a reference to another function’s Activation Object thus allowing that function to access variables and functions defined within it
  • 101.
  • 102.
  • 103.
  • 104.
    var funcs =[]; for (var i = 0; i < 5; i++) { funcs.push(function () { console.log(i); }); } for (var j = 0; j < 5; j++) { funcs[j](); }
  • 105.
    // => 5 //=> 5 // => 5 // => 5 // => 5 var funcs = []; for (var i = 0; i < 5; i++) { funcs.push(function () { console.log(i); }); } for (var j = 0; j < 5; j++) { funcs[j](); }
  • 106.
    // => 5 //=> 5 // => 5 // => 5 // => 5 var funcs = []; for (var i = 0; i < 5; i++) { funcs.push(function () { console.log(i); }); } for (var j = 0; j < 5; j++) { funcs[j](); }
  • 107.
    // => 0 //=> 1 // => 2 // => 3 // => 4 var funcs = []; for (var i = 0; i < 5; i++) { funcs.push((function (i) { return function () { console.log(i); }; })(i)); } for (var j = 0; j < 5; j++) { funcs[j](); }
  • 108.
    // => 0 //=> 1 // => 2 // => 3 // => 4 var funcs = []; for (var i = 0; i < 5; i++) { funcs.push((function (i) { return function () { console.log(i); }; })(i)); } for (var j = 0; j < 5; j++) { funcs[j](); }
  • 109.
  • 110.
  • 111.
  • 112.
    Encapsulation: A Betterenumer var colors = enumer('RED', 'GREEN', 'BLUE'); colors.contains('RED'); // => true colors.contains('YELLOW'); // => false function enumer() { function contains (value) { return this.hasOwnProperty(value); } var i, numValues = arguments.length, values = {}; for (i = 0; i < numValues; i++) { values[arguments[i]] = arguments[i]; } values.contains = contains; return values; }
  • 113.
    Encapsulation: A Betterenumer var colors = enumer('RED', 'GREEN', 'BLUE'); colors.contains('RED'); // => true colors.contains('YELLOW'); // => false colors.YELLOW = 'YOLO'; colors.contains('YELLOW'); // => true function enumer() { function contains (value) { return this.hasOwnProperty(value); } var i, numValues = arguments.length, values = {}; for (i = 0; i < numValues; i++) { values[arguments[i]] = arguments[i]; } values.contains = contains; return values; }
  • 114.
    Encapsulation: A Betterenumer var colors = enumer('RED', 'GREEN', 'BLUE'); colors.contains('RED'); // => true colors.contains('YELLOW'); // => false colors.YELLOW = 'YOLO'; colors.contains('YELLOW'); // => true delete colors.RED; colors.contains('RED'); // => false function enumer() { function contains (value) { return this.hasOwnProperty(value); } var i, numValues = arguments.length, values = {}; for (i = 0; i < numValues; i++) { values[arguments[i]] = arguments[i]; } values.contains = contains; return values; }
  • 115.
    Encapsulation: A Betterenumer var colors = enumer('RED', 'GREEN', 'BLUE'); colors.contains('RED'); // => true colors.contains('YELLOW'); // => false colors.YELLOW = 'YOLO'; colors.contains('YELLOW'); // => true colors.contains('contains'); // => true delete colors.RED; colors.contains('RED'); // => false function enumer() { function contains (value) { return this.hasOwnProperty(value); } var i, numValues = arguments.length, values = {}; for (i = 0; i < numValues; i++) { values[arguments[i]] = arguments[i]; } values.contains = contains; return values; }
  • 116.
    Encapsulation: A Betterenumer var colors = enumer('RED', 'GREEN', 'BLUE'); colors.contains('RED'); // => true colors.contains('YELLOW'); // => false function enumer() { function contains (value) { return this.hasOwnProperty(value); } var i, numValues = arguments.length, values = {}; for (i = 0; i < numValues; i++) { values[arguments[i]] = arguments[i]; } values.contains = contains; return values; }
  • 117.
    Encapsulation: A Betterenumer var colors = enumer('RED', 'GREEN', 'BLUE'); colors.contains('RED'); // => true colors.contains('YELLOW'); // => false function enumer() { var slice = Array.prototype.slice, values = slice.call(arguments, 0); return { contains: function (value) { return values.indexOf(value) >= 0; } }; }
  • 118.
    Encapsulation: A Betterenumer var colors = enumer('RED', 'GREEN', 'BLUE'); colors.contains('RED'); // => true colors.contains('YELLOW'); // => false colors.YELLOW = 'YOLO'; colors.contains('YELLOW'); // => false delete colors.RED; colors.contains('RED'); // => true function enumer() { var slice = Array.prototype.slice, values = slice.call(arguments, 0); return { contains: function (value) { return values.indexOf(value) >= 0; } }; }
  • 119.
    Encapsulation: A Betterenumer var colors = enumer('RED', 'GREEN', 'BLUE'); colors.contains('RED'); // => true colors.contains('YELLOW'); // => false colors.YELLOW = 'YOLO'; colors.contains('YELLOW'); // => false delete colors.RED; colors.contains('RED'); // => true colors.contains('contains'); // => false function enumer() { var slice = Array.prototype.slice, values = slice.call(arguments, 0); return { contains: function (value) { return values.indexOf(value) >= 0; } }; }
  • 120.
  • 121.
  • 122.
  • 123.
  • 124.
    var someObject =Class.extend({ init: function () { $('.some-selector').on('click', this.onClick.bind(this)); }, onClick: function (e) { var result = this.helperFunction(); return this.format(result); } };
  • 125.
  • 126.
    Modifying Context Taking controlof this `call` `apply` `bind`
  • 127.
    Modifying Context Taking controlof this `call` `apply` `bind`
  • 128.
    Modifying Context Taking controlof this `call` `apply` `bind` use to set the value of `this` when you know the number of arguments and what they are
  • 129.
    Modifying Context Taking controlof this `call` `apply` `bind` use to set the value of `this` when you know the number of arguments and what they are use to set the value of `this` when you don’t know the number of arguments
  • 130.
    Modifying Context Taking controlof this `call` `apply` `bind` use to set the value of `this` when you know the number of arguments and what they are use to set the value of `this` when you don’t know the number of arguments use to set the value of `this` but without executing the function
  • 131.
  • 132.
    Modifying Context Taking controlof this `call` `apply` `bind`
  • 133.
    Modifying Context Taking controlof this `call` `apply` `bind`
  • 134.
    Modifying Context Taking controlof this `call` `apply` `bind` use to set the value of `this` when you know the number of arguments and what they are
  • 135.
    Modifying Context Taking controlof this `call` `apply` `bind` use to set the value of `this` when you know the number of arguments and what they are use to set the value of `this` when you don’t know the number of arguments
  • 136.
    Modifying Context Taking controlof this `call` `apply` `bind` use to set the value of `this` when you know the number of arguments and what they are use to set the value of `this` when you don’t know the number of arguments use to set the value of `this` but without executing the function
  • 137.
  • 138.
    function getEventListener(sniffer) { functionon(event, callback) { window.addEventListener(event, callback); } if (sniffer.isIE()) { function on(event, callback) { window.attachEvent(event, callback); } } return on; } var browserSniffer = { isIE: function () { return false; } }; getEventListener(browserSniffer).toString();
  • 139.
    function getEventListener(sniffer) { functionon(event, callback) { window.addEventListener(event, callback); } if (sniffer.isIE()) { function on(event, callback) { window.attachEvent(event, callback); } } return on; } var browserSniffer = { isIE: function () { return false; } }; getEventListener(browserSniffer).toString();
  • 140.
    function getEventListener(sniffer) { functionon(event, callback) { window.addEventListener(event, callback); } if (sniffer.isIE()) { function on(event, callback) { window.attachEvent(event, callback); } } return on; } var browserSniffer = { isIE: function () { return false; } }; getEventListener(browserSniffer).toString();
  • 141.
    function getEventListener(sniffer) { functionon(event, callback) { window.addEventListener(event, callback); } if (sniffer.isIE()) { function on(event, callback) { window.attachEvent(event, callback); } } return on; } var browserSniffer = { isIE: function () { return false; } }; getEventListener(browserSniffer).toString();
  • 142.
    function getEventListener(sniffer) { functionon(event, callback) { window.addEventListener(event, callback); } if (sniffer.isIE()) { function on(event, callback) { window.attachEvent(event, callback); } } return on; } var browserSniffer = { isIE: function () { return false; } }; getEventListener(browserSniffer).toString();
  • 143.
    function getEventListener(sniffer) { functionon(event, callback) { window.addEventListener(event, callback); } if (sniffer.isIE()) { function on(event, callback) { window.attachEvent(event, callback); } } return on; } var browserSniffer = { isIE: function () { return false; } }; getEventListener(browserSniffer).toString(); function on(event, callback) { window.attachEvent(event, callback); }
  • 144.
  • 145.
    Encapsulation the JavaScriptWay function Vault(passcode) { var locked = true, data = {}; function checkPasscode(attemptedPasscode) { return attemptedPasscode == passcode; } function grantAccess() { locked = false; return true; } this.isLocked = function () { return locked; }; this.lock = function () { locked = true; }; // ...
  • 146.
    Encapsulation the JavaScriptWay function Vault(passcode) { var locked = true, data = {}; function checkPasscode(attemptedPasscode) { return attemptedPasscode == passcode; } function grantAccess() { locked = false; return true; } this.isLocked = function () { return locked; }; this.lock = function () { locked = true; }; // ... // ... this.unlock = function (attemptedPasscode) { checkPasscode(attemptedPasscode) && grantAccess(); }; this.store = function (key, value) { if (this.isLocked()) { throw new Error('The vault is locked'); } data[key] = value; }; this.retrieve = function (key) { if (this.isLocked) { throw new Error('The vault is locked'); } return data[key]; }; }
  • 147.
    $ node encapsulation.js varv = new Vault('sekret'); v.isLocked(); true v.unlock('sekret') v.store('js_functions', 'Awesome') v.retrieve(‘js_functions') Awesome v.lock() v.isLocked() true v.retrieve('js_functions') /Users/colin/Documents/talks/js_functions/encapsulation.js:40 throw new Error('The vault is locked'); ^ Error: The vault is locked at Vault.retrieve (/Users/colin/Documents/talks/js_functions/encapsulation.js:40:19) at Object.<anonymous> (/Users/colin/Documents/talks/js_functions/encapsulation.js:68:3) at Module._compile (module.js:456:26) at Object.Module._extensions..js (module.js:474:10) at Module.load (module.js:356:32) at Function.Module._load (module.js:312:12) at Function.Module.runMain (module.js:497:10) at startup (node.js:119:16) at node.js:906:3