JavaScript Fundamentals:
and Callbacks
Why do we care about higher order functions and callbacks?
So that we can write more declarative and expressive code
// declarative style
var add10 = function(num) {
console.log(num + 10);
};
var nums = [1,2,3]
nums.forEach(add10);
// 11, 12, 13
// imperative style
var nums = [1,2,3];
for (var i=0; i<nums.length; i++) {
console.log(nums[i] + 10);
}
// 11, 12, 13
Which is more clear to you?
What is a function?
1. A block of code
2. We get to determine when this code is run (invoked)
3. Directions for doing something we have created but not executed yet (figuring out driving directions vs. actually driving there; writing down a recipe vs. actually cooking it).
This seems super basic, but thinking of functions this way will make higher order functions a lot easier.
A function is just a block of code, and we get to decide when it is run.
1. Take a function as an input (argument)
var count = 1;
setInterval(function(){
console.log('I am', count++, 'seconds old now');
}, 1000);
2. Return a function as the output
var add = function(num){
var num1 = num;
var addToNum1 = function(num2){
return num1 + num2;
};
return addToNum1;
};
If a function does at least one of the following two things, then it is a baller higher-order function.
In JavaScript...
"higher-order" functions can exist because
JS treats functions as "first-class objects".
In other words...
functions can be:
...just like any other object in JS
(just like numbers, strings, booleans, arrays, etc).
// higher order function
var ifElse = function(condition, isTrue, isFalse){
if(condition){
// callback function
isTrue();
} else {
// callback function
isFalse();
}
};
var logTrue = function(){ console.log(true); };
var logFalse = function(){ console.log(false); };
ifElse(true, logTrue, logFalse);
Callbacks are functions that you pass and invoke inside of higher order functions.
Remember from Week 1 that we had two main ways of creating a function?
//Anonymous function saved into a variable
var nameCreator = function (firstName, lastName) {
return firstName + ' ' + lastName
};
//named function
function nameImprover(firstName, lastName) {
return firstName + ' ' + lastName
}
//both work!
What if we didn't store the anonymous function into a variable? What if we just left it as is, a set of instructions we've created but not executed yet?
//Anonymous function
function (firstName, lastName) {
return firstName + ' ' + lastName
};
var ifElse = function(condition, isTrue, isFalse){
if(condition){
isTrue();
} else {
isFalse();
}
};
ifElse(true,
function(){ console.log(true); },
function(){ console.log(false); }
);
Let's refactor our code to just use anonymous functions, instead of declaring them in a separate part of our code and saving them into variables.
var ifElse = function(condition, isTrue, isFalse){
if(condition){
isTrue();
} else {
isFalse();
}
};
ifElse(true,
function(){ console.log(true); }, // this was logTrue()
function(){ console.log(false); }// this was logFalse()
);
var ifElse = function(condition, isTrue, isFalse){
if(condition){
isTrue();
} else {
isFalse();
}
};
ifElse(true,
function(){ console.log(true); },
function(){ console.log(false); }
);
This is a super common pattern in functional programming: to declare anonymous functions in-line as arguments.
Remember, a function is just a block of code we've defined but not run yet. If we're not using this code elsewhere in our program, there's no need to give it a name.
var ifElse = function(condition, isTrue, isFalse){
if(condition){
isTrue(); // no arguments
} else {
isFalse(); // no arguments
}
};
var ifElse = function(condition, isTrue, isFalse, arg){
if(condition){
isTrue(arg); // Look! An argument! Whoa.
} else {
isFalse(arg); // <(^.^<) OMG it's another one.
}
};
var ifElse = function(condition, isTrue, isFalse){
if(condition){
isTrue(); // no arguments
} else {
isFalse(); // no arguments
}
};
var ifElse = function(condition, isTrue, isFalse, arg){
if(condition){
isTrue(arg); // Look! An argument! Whoa.
} else {
isFalse(arg); // <(^.^<) OMG it's another one.
}
};
Or, if our higher order function will be giving us the arguments, we can just include those as parameters for our anonymous functions.
When would this happen? You can think of a higher-order function that might iterate through an array, and pass each item in the array to the callback function we pass in. In this case, we're not directly specifying the argument, we're letting the higher-order function give it to us each time.
var increment = function(n){
return n + 1;
};
var square = function(n){
return n*n;
};
var doMath = function(n, func){
return func(n);
};
doMath(5, square);
doMath(4, increment);
1. API requests (Get data from Yelp, and once you have that data some number of milliseconds later, invoke this function which is the code I've programmed to play with the Yelp data).
2. Event handlers- Whenever a user clicks on this square, do these things.
3. Functional programming! Next week, I promise :)
github.com/telegraphPrep/week-3-introToHigherOrderFunctions
New partners!
(again)
What we'll do today...
Concepts we'll cover:
Bring together all of the concepts we've learned to build handy functions using the underscore.js library.
What is it?
A popular library with many useful functional programming methods built in.
Where is it?
http://underscorejs.org
Annotated Source:
http://underscorejs.org/docs/underscore.html
//_ is just an empty object that we put methods on
var _ = {};
//_.each is just a property of that object
//that property is set equal to a function
//that function has two parameters
_.each = function(list, callback) {
//function body here
};
//when you load up the underscorejs library
//all you're doing is loading an enormous object
//that has 200+ properties that are functions
What is this _ object that I'm invoking methods on?
var pocketmons = ['Charisaur', 'Bulbazard', 'Twomew'];
var logger = function(val){
console.log(val);
};
_.each(pocketmons, logger);
//'Charisaur'
//'Bulbazard'
//'Twomew'
//_.each(list, iterator)
Each is just a fancy for loop that underscore provides for us.
var each = function(list, iterator) {
//function body here to make each work
};
//Example invocation:
_.each(pocketmons, logger);
//'Charisaur'
//'Bulbazard'
//'Twomew'
http://underscorejs.org/#each
//PSEUDOCODE!!
//declare a function that has two parameters
//determine if collection is an array or an object
//loop through the collection
//invoke the callback on each element in the collection
http://underscorejs.org/#each
var _ = {};
//declare a function that has two parameters
_.each = function(list, callback) {
//determine if collection is an array or an object
//loop through the collection
//invoke the callback on each element in the collection
};
http://underscorejs.org/#each
var _ = {};
_.each = function(list, callback) {
//determine if list is an array or an object
if(Array.isArray(list)) {
//use array for loop
//invoke the callback on each element in the list
} else {
//use object for loop
//invoke the callback on each element in the list
}
};
http://underscorejs.org/#each
var _ = {};
_.each = function(list, callback) {
if(Array.isArray(list)) {
for (var i = 0; i < list.length; i++) {
//invoke the callback on each element in the list
}
} else {
for (var key in list) {
//invoke the callback on each element in the list
}
}
};
http://underscorejs.org/#each
var _ = {};
_.each = function(list, callback) {
if(Array.isArray(list)) {
for (var i = 0; i < list.length; i++) {
callback(list[i], i, list);
}
} else {
for (var key in list) {
callback(list[key], key, list);
}
}
};
Which function from myFirstLibrary is _.each equivalent to?
var loopThrough = function(list, callback) {
if(Array.isArray(list)) {
for (var i = 0; i < list.length; i++) {
callback(list[i], i, list);
}
} else {
for (var key in list) {
callback(list[key], key, list);
}
}
};
The callback gets the index and array as well, but don't use them to access the current element.
var names = ['Jeff', 'Preston', 'Bianca'];
function logTwice(name, index, names) {
var currentName = names[index]; // correct
console.log(currentName);
currentName = name; // correct AND easier
console.log(currentName);
}
_.each(names, logTwice);
The callback gets the key and object as well, but don't use them to access the current property value.
var superheroes = {
batman : 'human',
superman : 'alien',
ironman : 'human',
groot : 'alien',
wolverine : 'mutant'
};
function logNameIfHuman(type, heroName, superheroes) {
var currentType = superheroes[heroName]; // correct
currentType = type; // correct AND easier
if(currentType === 'human') {
console.log(heroName);
}
}
_.each(superheroes, logNameIfHuman);
// Let's use _.each to solve this problem:
var addTwoToAllElements = function(arr){
_.each(arr, function(val, index, collection){
collection[index] = val + 2;
});
};
var arr = [1, 2, 3, 4, 5];
addTwoToAllElements(arr);
console.log(arr) // [3, 4, 5, 6, 7];
Let's solve a problem using each.
// Let's use _.each to solve this problem:
var addTwoToAllElements = function(arr){
_.each(arr, function(val, index, collection){
collection[index] = val + 2;
});
};
var arr = [1, 2, 3, 4, 5];
addTwoToAllElements(arr);
console.log(arr) // [3, 4, 5, 6, 7];
var studentA = {
firstName: 'Ryan',
lastName: 'Gosling'
};
var classA = {
subject: 'JavaScript',
teacher: '@RebootJeff',
students: [ /* studentA, studentB, etc... */ ]
};
var classes = [ /* classA, classB, etc... */ ];
for(var i = 0; i < classes.length; i++) {
for(var j = 0; j < classes[i].students.length; j++) {
var fullName = classes[i].students[j].firstName +
' ' + classes[i].students[j].lastname;
console.log(fullName);
}
}
_.each(classes, function(class) {
_.each(class.students, function(student) {
var fullName = student.firstName + ' ' + student.lastName;
console.log(fullName);
});
});
How do we print all the names
of all the students
of all the classes?
for(var i = 0; i < classes.length; i++) {
var students = classes[i].students; // This improves readability
for(var j = 0; j < students.length; j++) {
var fullName = students[j].firstName + ' ' + students[j].lastname;
console.log(fullName);
}
}
MUCH easier to read:
var pocketmon = ['Charisaur', 'Bulbazard', 'Twomew'];
var stokedFunc = function(val){
return val + '!!!';
};
var stokedPocketmon = _.map(pocketmon, stokedFunc);
//_.map(list, iterator)
console.log(stokedPocketmon);
//['Charisaur!!!','Bulbazard!!!','Twomew!!!']
var _ = { each: function(/*...*/) { /*...*/ } };
_.map = function(list, iterator) {
var result = []; // make a new array
_.each(list, function(item, index, list) {
result.push(iterator(item, index, list));
});
return result;
};
function copyBy = function(list, iterator) {
var result = []; // make a new array
loopThrough(list, function(item, index, list) {
result.push(iterator(item, index, list));
});
return result;
};
Which function from myFirstLibrary is synonymous with _.map?
function AnimalMaker(name) {
return {
speak: function () {
console.log("my name is ", name);
}
};
};
var animalNames = ['Frog', 'Falcon', 'Fox'];
// Remember coding WITHOUT Underscore?
var farm = [];
for(var i = 0; i < animalNames.length; i++){
farm.push(AnimalMaker(animalNames[i]));
}
function AnimalMaker(name) {
return {
speak: function () {
console.log("my name is ", name);
}
};
};
var animalNames = ['Frog', 'Falcon', 'Fox'];
// Mapping builds up an array and returns it.
var farm = _.map(animalNames, function (name) {
return AnimalMaker(name);
});
// An each loop returns nothing.
// Just use it to iterate over an array.
_.each(farm, function (animal) {
animal.speak();
});
I want to simply loop through an array or object.
I want a new array based on an existing one.
https://github.com/TelegraphPrep/week-4-underscoreAlgorithms