JavaScript Fundamentals:

Functions

Slides:
https://slides.com/rebootjeff/js-functions

Classwork:
https://github.com/RebootJeff/jsFunctions

Original Curriculum by:
Bianca Gandolfo

Edited and Presented by:
Jeff Lee

Hoisting

Scope

Closures

Callbacks

Higher-Order Functions

Who is Jeff?

Full-Stack JavaScriptivore
Hack Reactor cohort #7

Who are you?

  • Have a good understanding of
    JS arrays, objects, and loops.
  • Have a basic understanding of
    JS functions.
  • Want to solidify fundamentals
  • Seek more practice

Class Format

  • Silence your friggin' ringtones (please)
  • Ask questions (between slides)
  • Answer my questions
  • Show thumbs
  • Ask for help during exercises
  • Lunch at some point (unless your phone rings during lecture)

Review:

Basics of Functions

and Hoisting

Function Anatomy

Thumbs?

2 ways to make function vars

var spitASickRhyme = function() {
  console.log('I\'m in the pursuit of Princess Peach and I know');
  console.log('When I think I\'ve won, she\'s in another castle');
};

// function EXPRESSION <(•_•^) and (v•_•)> function DECLARATION

function spitASickRhyme() {
  console.log('I\'m in the pursuit of Princess Peach and I know');
  console.log('When I think I\'ve won, she\'s in another castle');
}

Another look at Hoisting

// function expression
var haveFun = function() {};

// function declaration
function learnJS() {}

// variable declaration
// and assignment
var greeting = 'hello, world';

if(false) {
  var jeffIsAmazing = 'duh';
}
// declarations
var haveFun;
function learnJS() {}
var greeting;
var jeffIsAmazing;

// definitions
// and assignments
haveFun = function() {};
greeting = 'hello, world';

if(false) {
  jeffIsAmazing = 'duh';
}

console.log(jeffIsAmazing);
// will log `undefined`

Hoisting

Let's explore through live coding!

Scope

  • Local
  • Global
  • Nested Scopes
    • Precedence
  • Block Scope

SCOPE-RELATED CONCEPTS

Local Scope

var func = function(){
  var local = true;
};

console.log(local);
var x = 'global!';
//inside a function
function addToGlobalScope(){
  y = 'global here, too!';
}
//inside a function
function addToGlobalScope(){
  y = 'global here, too!';
  window.z = 'also global!';
  // `window` is for browsers only
  // (not Node.js)
}

Global Scope

REMEMBER: z and y are not declared until addToGlobalScope is called.

Parent vs Child Scope

var g = 'global';

// Side note: `blender` is in global scope too
function blender(fruit) { 
  var y = 'yogurt';
 
  function mix() { 
    alert( fruit + ' and ' + y + ' makes ' +
      fruit + ' swirl'); 
  } 
  mix(); 
} 

blender('blueberry');

What happens when there is a child function nested within a parent function??

creating scopes

When are scopes actually created?

  • The scope of a function is created when the function is called.
  • Hoisting occurs when scopes are created.

Precedence

var g = "global";

function go() { 
  var l = "local";
  var g = "in here!";

  alert(l + " inside local");
  alert(g + " inside go");
}

go();
alert(g + " outside go");

Block Scope

var message = 'yes';

for(var i = 0; i < 5; i++){
  message = 'no';
};

console.log('Is there block scope? ' + message);

JavaScript only has LEXICAL scoping

Lexical Scoping = Function Scope with Nesting

Questions

Exercise Time!

  • Repo?
  • SpecRunner?
  • TAs?
  • Pairs?
  • References?

https://github.com/RebootJeff/jsFunctions

  • Slides
  • DevDocs.io

CLOSURES

Closure Example #1

var closureAlert = function(){
  var x = 'Help! I\'m a variable '+
    'stuck in a closure!';
  
  var alerter = function(){
    alert(x);
  };
  
  alerter();
};

Closure Example #2

var closureAlert = function(){ 
  var x = 0; 
  var alerter = function(){ 
    x++;
    alert(x); 
  }; 
  return alerter; 
}; 

var alerter1 = closureAlert(); 
var alerter2 = closureAlert(); 
alerter1();
alerter2();

Closure Example #3

var add = function(num){
  var num1 = num;

  var addToNum1 = function(num2){
    return num1 + num2;
  };

  return addToNum1;
};
var addEleven = add(11);
var result = addEleven(22);
console.log(result);

Closure Example: counter

function counter() {
  var n = 0;

  return {
    count: function() { n++; return n; },
    reset: function() { n = 0; }
  };
}
var c = counter();
var d = counter();

c.count();
d.count();

c.reset();
c.count();
d.count();

Recipe: creation

1. Create your parent function.

2. Define some variables in the parent's local scope.

     (they can be accessed by the child function)

3. Define a function inside the parent function.

     (aka defining a "child" function or "nested" function)

4. Return that child from inside the parent.

function outerFunc() {
  var parentVar = "local to parent";
  function innerFunc() {
    return parentVar + ' but accessed by child!';
  };
  return innerFunc;
}
1.
2.
3.


4.

RECIPE: Execution

function outerFunc() {
  var parentVar = "local to parent";
  function innerFunc() {
    return parentVar + ' but accessed by child!';
  };
  return innerFunc;
}

// STEP 1 - Run the parent function.
var example = outerFunc();
console.log(example);
// We see example now stores the child.

// STEP 2 - Run the child function.
var result = example();
console.log(result);
// What's the end result?

Gotcha!?

var sayAlice = function(){

  var makeLog = function() {
    console.log(alice);
  };

  var alice = 'Why hello there, Alice!';

  return makeLog;
};

var log = sayAlice();

log();

Example #2,350,987

var makeStopwatch = function(){
  var elapsed = 0;

  var stopwatch = function(){
    return elapsed;
  };

  var increase = function(){ elapsed++; };
  setInterval(increase, 1000);

  return stopwatch;

};

var x = makeStopwatch();

Module Pattern - Boilerplate

var Module = function(){

  var privateProperty = 'I like big fonts,' +
    ' and I cannot lie';

  function privateMethod(params){
    // do something
  };

  return {
    publicProperty: 'JS rulez!!',
    publicMethod: function(params){
      // do something
    },
    privilegedMethod: function(params){ 
      privateMethod(args); 
    }
  };
};

Module Pattern - Example

var makeNewCar = function(){
  var brakeLightsOn = false, seatbeltLocked = false;

  function useABS(pressure) {
    // 1. Use Electronic Brakeforce Distribution.
    // 2. If necessary, activate Stability Control.
    // 3. Check if wheels are locking up.
    // 4. If necessary, release and re-apply EBD.
  }

  return {
    color: 'blue',
    honkHorn: function() {
      console.log('HONK!!!');
    },
    hitBrakes: function(pressure){
      brakeLightsOn = true;
      seatbeltLocked = true;
      useABS(pressure);
    }
  };

};

Questions

Exercises

Different format & new partners!

Quick Tip: When talking with your partner and TAs, practice using technical terminology. Be specific! Be precise!

var hello;
hello = 'hiiii!';

function foo() { }
var foo = function() {};

foo;
foo();

var woohoo = [1, 2, 3];
woohoo[i]
woohoo[0]

var yay = {
  kool: 'aid'
};
kool
'aid'
kool: 'aid'
variable declaration (undefined variable)
assignment (value assigned to variable)

function definition/declaration
function definition/expression

function, function reference
function call/invocation


element of woohoo, woohoo-sub-i
first element, woohoo-sub-0




key, property name
value, property value
key-value pair, property of yay

E.g., never say "thing";
specify which element of an array;
mention line numbers.

Higher-Order
Functions

and Callbacks

1. Take a function as an input (argument)

Higher-Order Functions

var time = 1;
setInterval(function(){
  console.log(time++ + ' seconds have passed.');
}, 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.

Higher-Order Functions

In JavaScript...

"higher-order" functions can exist because

JS treats functions as "first-class objects".

In other words...

functions can be:

  • created    (defined)
  • stored      (into variables)
  • passed     (as arguments)
  • returned  (by functions)

...just like any other object in JS

(just like numbers, strings, booleans, arrays, etc).

Callbacks

var ifElse = function(condition, isTrue, isFalse){
  if(condition){
    isTrue();
  } else {
    isFalse();
  }
};

ifElse(true,
 function(){ console.log(true); },
 function(){ console.log(false); }
);
var ifElse = function(condition, isTrue, isFalse){
  if(condition){
    isTrue();
  } else {
    isFalse();
  }
};

var logTrue = function(){ console.log(true); };
var logFalse = function(){ console.log(false); };

ifElse(true, logTrue, logFalse);

Passing Arguments

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.
  }
};

Passing Arguments

var increment = function(n){
  return n + 1;
};

var square = function(n){
  return n*n;
};

var doMathSoIDontHaveTo = function(n, func){
  return func(n);
};

doMathSoIDontHaveTo(5, square);

doMathSoIDontHaveTo(4, increment);

Questions

Exercises

New partners!

(again)

Underscore.js

underscore.js

A utility library that provides functional methods.

 

Where is it?

http://underscorejs.org

 

Annotated Source:

http://underscorejs.org/docs/underscore.html

Let's make some of the methods from scratch!

_.each() usage

var pocketmons = ['Charisaur', 'Bulbazard', 'Twomew'];
var logger = function(val){
  console.log(val);
};
_.each(pocketmons, logger);
//_.each(list, iterator)
aka _.forEach()
_.forEach(pocketmons, logger);
http://underscorejs.org/#each
  • Iterates over a list of elements, yielding each in turn to an iterator function.
  • Each invocation of iterator is called with three arguments: element, index, list. If list is a JavaScript object, iterator's arguments will be value, key, list.
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);
    }
  }
};

_.EACH() DEFINED

Looping with _.each 

function AnimalMaker(name) {
  return {   
    speak: function () { 
      console.log("my name is ", name); 
    } 
  }; 
}; 

var animalNames = ['Frog', 'Falcon', 'Fox'];
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'];
var farm = [];

_.each(animalNames, function (name) {
  farm.push(AnimalMaker(name));
});

Nested Looping with _.each()

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:

each-loop vs for-loop

Simplicity vs Flexibility

var arr = ['a', 'b', 'c'];

for(var i = arr.length - 1; i >= 0; i--) {
  // do stuff with elements in reverse order
}

for(var i = 0; i < arr.length; i += 2) {
  // do stuff with even-indexed elements
}

each

  • simple syntax
  • straightforward and most commonly needed behavior

for

  • ugly syntax
  • flexible setup for customized behavior
var pocketmon = ['Charisaur', 'Bulbazard', 'Twomew'];
var stokedArr = function(val){
  return val + '!!!';
};
var stokedPocketmon = _.map(pocketmon, stokedArr);

_.MAP() usage

//_.map(list, iterator)

_.map() defined

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;
};
  • Produces a new array of values by mapping each value in list through a transformation function (iterator).
  • Each invocation of iterator is called with three arguments: (element, index, list). If list is a JavaScript object, iterator's arguments will be (value, key, list).

 

Looping with _.map

function AnimalMaker(name) {
  return { 
    speak: function () { 
      console.log("my name is ", name); 
    } 
  };
};

var animalNames = ['Frog', 'Falcon', 'Fox'];

var farm = [];

_.each(animalNames, function (name) {
  farm.push(AnimalMaker(name));
});
function AnimalMaker(name) {
  return { 
    speak: function () { 
      console.log("my name is ", name); 
    } 
  };
};

var animalNames = ['Frog', 'Falcon', 'Fox'];

var farm = _.map(animalNames, function (name) {
  return AnimalMaker(name);
});
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])); 
}

_.map vs _.each 

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.

Questions

Exercises

New partners!

(of course)

 

Extra Credit is available if you finish early.

DEBRIEF

But wait! There's more!

  • Underscore.js  vs  Lodash.js  vs  vanilla JS
  • Go check out other Underscore methods/tools!
  • Want to be a TA?
  • Feedback:
    • Difficulty level? Topics?
    • Class format? Exercises?
  • Organize some study groups amongst yourselves
  • ...and don't forget THE MOST IMPORTANT THING!!!

Follow @RebootJeff

 

Read words on RebootJeff.com

(for coding + career advice)

If there is enough time

 

then we can discuss other topics such as...

  • Other JavaScript topics
    • Mutability
    • Anonymous Functions vs Named Functions
  • Other Software Engineering topics
    • Privacy and Interfaces
    • What does "API" really mean?
  • Career stuff
    • Daily life of a software engineer
    • Roadmap / career path
    • Hack Reactor

BUT... there are no slides or prepared materials for these topics.

(Treat this like a Q&A session)

JS Fundamentals 2: Scope, Closures, Higher-Order Functions

By hrjr

JS Fundamentals 2: Scope, Closures, Higher-Order Functions

3/22/2015

  • 1,317