- Last week we talked about functions
- function
- scope
- closures
- Tonight, we'll cover
- Passing functions as arguments
- Higher Order Functions
- working with Array data
- This is important when working with API data
- Asynchronous programming patterns
- These are super common in UI development like React, jQuery, or even vanilla JS.
Sometimes you want to create a scope so that you can have private variables. One way to do that is to create an anonymous function and immediately execute it.
(function(){
var myVar = "look mom, private variables!";
//Do some stuff with myVar...
})();
console.log(myVar); //undefined;Another note about IIFE's is that the code is executed immediately, which can serve particular benefits when used at the appropriate time.
Because functions are objects, they can be passed in to other functions as arguments.
==//Declare an add function
function add(number1, number2) {
return number1 + number2;
}
//Declare a function that takes a function as an argument
function doMath(operation, number1, number2) {
return operation(number1,number2);
}
//Pass a function into another.
var sum = doMath(add, 1, 2);
In the above example, doMath is a higher order function
Higher Order Functions:
- Take another function as an argument
or
- Return a function
Additionally, add is considered a callback function (more on that later), because it has been passed as an argument to doMath
More than likely, in your own development, you've already been using Higher Order Functions. Now you have a name for it!
Sometimes you want to filter a data set based on some criteria. The Array object supports a .filter function. It takes a filter function that returns a pass value of true or false.
var students = [
{name: 'Cam Newton', average: 90},
{name: 'Ted Ginn', average: 58},
{name: 'Greg Olsen', average: 82}
];
var passingStudents = students.filter(function(student){
return student.average > 50;
});
passingStudents === [
{name: 'Cam Newton', average: 90},
{name: 'Greg Olsen', average: 82}
];Sometimes you want to find the first item in the array that meets a certain criteria. Its the single version of filter.
var students = [
{name: 'Cam Newton', average: 90},
{name: 'Ted Ginn', average: 58},
{name: 'Greg Olsen', average: 82}
];
var passingStudents = students.find(function(student){
return student.average > 50;
});
passingStudents === [
{name: 'Cam Newton', average: 90}
];Sometimes you want to transform data. The Array's Map function lets you iterate over an array and produce another array with a new value for each item.
var students = [
{firstName: 'Cam', lastName: 'Newton'},
{firstName: 'Ted', lastName: 'Ginn'},
{firstName: 'Greg', lastName: 'Olsen'}
]
var fullNames = students(function(student){
return student.firstName + ' ' + student.lastName;
})
fullNames === ["Cam Newton", "Ted Ginn", "Greg Olsen"]Sometimes you want to calculate a single value based on all the items in an array The Array's Reduce function let's you iterate over an array and calculate a value.
var students = [
{name: 'Cam Newton', assignmentsCompleted: 6},
{name: 'Ted Ginn', assignmentsCompleted: 10},
{name: 'Greg Olsen', assignmentsCompleted: 8}
]
var totalAssignments = students.reduce(function(sum,current){
return sum + current.assignmentsCompleted;
}, 0);
totalAssignments === 24;Sometimes you want to create a scope so that you can have private variables. One way to do that is to create an anonymous function and immediately execute it.
(function(){
var myVar = "look mom, private variables!";
//Do some stuff with myVar...
})();
console.log(myVar); //undefined;-
Create an Array named
superHeroes- Inside the superHeroes array create the following arrays:
["Batman", "Bruce Wayne"], ["Spiderman", "Peter Parker"], ["The Flash", "Barry Allen"]
- Inside the superHeroes array create the following arrays:
-
Create a
secretIdentityvariable -
Assign
superHeroes.map()to thesecretIdentityvariable -
Assign and anonymous function to
superheroes.map()as an argument -
Your anonymous function should have one parameter named
revealArray -
Inside the block of your anonymous function:
- You'll be working with revealArray as an argument
- Using
revealArrayreturna string that willjointhe two array items. jointhem together with the string"is"
ie: "Batman is Bruce Wayne"
-
Log the results to the screen and
jointhem with a line break.
Exercise Answer:
var superHeroes = [ ["Batman", "Bruce Wayne"],
["Spiderman", "Peter Parker"],
["The Flash", "Barry Allen"]];
var secretIdentity = superHeroes.map(function(revealArray){
return revealArray.join(" is ");
});
console.log(secretIdentity.join("\n"));Using the following data:
var players = [
{firstName: 'Cam', lastName: 'Newton', position: 'QB', touchdowns: 32},
{firstName: 'Derek', lastName: 'Anderson', position: 'QB', touchdowns: 0},
{firstName: 'Jonathan', lastName: 'Stewart', position: 'RB', touchdowns: 12},
{firstName: 'Mike', lastName: 'Tolbert', position: 'RB', touchdowns: 8},
{firstName: 'Fozzy', lastName: 'Whittaker', position: 'RB', touchdowns: 3},
{firstName: 'Ted', lastName: 'Ginn', position: 'WR', touchdowns: 9},
{firstName: 'Devin', lastName: 'Funchess', position: 'WR', touchdowns: 2}
];- Find a player with the name 'Mike'
- Get an array of all players with position 'RB'
- Get an array of all the players lastNames
- Get an array of the full names of all the runningbacks with more than 5 touchdowns
- Get the number of touchdowns scored by Runningbacks
//Players named 'Mike'
players.find(function(p){
return p.firstName === 'Mike'
})
//Running backs
players.filter(function(p){
return p.position === 'RB';
})
//LastNames
players.map(function(p){
return p.lastName;
})
//Full names of all running backs with more than 5 touchdowns.
players
.filter(function(player){
return player.touchdowns > 5 && player.position == 'RB';
})
.map(function(player){
return player.firstName + ' ' + player.lastName;
});
//Number of touchdowns by runningbacks
players
.filter(function(player){
return player.position =='RB';
})
.reduce(function(total, player){
total += player.touchdowns;
},0)
In computer programs, asynchronous operation means that a process operates independently of other processes, whereas synchronous operation means that the process runs only as a result of some other process being completed or handing off operation.
Asynchronus programming:
- Allows for the user to continue interacting with the application while request is being handled
- Improves efficiency
- Saves us from expensive full-page reloads
- Allows for multiple requests to be sent and fulfilled
The passing of a function into another function to be run at a later time is called the "callback pattern". It's prominent in event based programming, which is what UI Development is all about.
setTimeout(function(){
console.log('later')
},2000);
console.log('now');button.addEventListener('click', function(){
alert('click');
})getData('/my-api/data', function(data) {
console.log('got data', data)
});The above example is pretty straight forward, but can get tricky when callback functions are passed around in asynchronous functions. You will, without doubt, make a mistake like this the first time you write a JS application:
var teacher = {
name: 'Shane',
speak: function() {
//Maybe you're fetching data from an API, or getting user input
setTimeout(function(){
console.log('later my name is ' + this.name);
},1000)
//Runs immediately
console.log('Now my name is ' + this.name);
}
}
teacher.speak();When you register a function to happen later using something like setTimeout(), the window object ends up running the function.
Often times, like the example above, you need access to the calling scope or want to assign a specific scope. There are a few different ways to do this.
var teacher = {
name: 'Shane',
speak: function() {
//Save this to a variable
var self = this;
//self is visible inside function because of closure
setTimeout(function(){
console.log('later my name is ' + self.name);
},1000);
}
}Explicity sets the this value at function defintion time.
var teacher = {
name: 'Shane',
speak: function() {
//Bind a function to a specific context
var boundFunction = function(){
console.log('later my name is ' + this.name);
}.bind(this);
//boundFunction will always run in bound context
setTimeout(boundFunction,1000);
}
}Explicitly set the this value at execution time.
var teacher = {name: 'Shane'};
var speak = function(item1, item2){
console.log(this.name, item1, item2);
}
speak.call(teacher, 'coffee', 'ramen'); //'Shane', 'coffee', 'ramen'
speak.apply(teacher, ['coffee', 'ramen']); //'Shane', 'coffee', 'ramen'The only difference is that .apply() takes an array of arguments to pass to the function. .call() passes arguments one by one.
Going back to our slideshow object, let's add some functionality.
- create an empty property named
playInterval - A
play()function that moves to the next photo ever 2000ms until the end.
Tip - useplayInterval = setInterval(fn,ms). - A
pause()function that stops the slideshow
Tip - useclearInterval(playInterval) - Automatically pause the slideshow if it gets to the end of the photolist while playing.
- Complete Exercise #3 Async Programming
- Create a branch off of your existing slideshow
- For your branch use the naming convention
async_YOUR_NAME_HERE
- Complete the Functional Javascript module at NodeSchool
- Upload a screenshot to Slack