Functions (Closures, Optional Arguments)

A Quick Review on Hoisting

What's hoisting? →

  • hoisting is the processing of declarations before any code is executed.
  • what's a declaration?
  • declaration is a way of telling the interpreter (or compiler) that a name or identifier exists
  • Soooo… Hoisting basically brings declarations to the top of the current scope
  • which means declarations do not have to occur before they are used depending on how you declare identifiers!
 

Hoisting Continued?

What are some hoisting rules? →

  • let and const declarations are sort of hoisted (their names are created, but they can't be used until they're actually declared)
  • var declarations are brought to the top of the scope
  • assignment / initialization is not hoisted (it happens at the original location of that line, so these var variables are undefined)
  • function declarations are hoisted (including their definition)

What is the output of the following code? Error or no output are possible. →

let x;
console.log(x);
undefined

let variables are initialized to undefined

console.log(x);
let x;
ReferenceError

Temporal Dead Zone - can't access let variable before actual declaration

console.log(x);
var x;
undefined

the var declaration for x is hoisted

What's the Output?

What is the output of the following code? Error or no output are possible. →

f(5); 
var f = function(x) {
	console.log(x);
}
TypeError: undefined is not a function

variable declaration is hoisted, but the initialization of its value is not…. the value, undefined is being used as a function!?

About var

A quick summary on using var →

  • if you drop var (and don't have a const or let either), the declaration is not hoisted
  • if you're in a function then var will create a local variable … and the scope of it will be that function
  • within a function, but without constlet or varthe interpreter will look up the scope chain until it finds that variable or hits the global scope (at which point it will create it)

Aaaand… Back to Using / Not Using constlet or var

What's the output of this code? →

let g = 7;

function f() {
	g = 5;
}
f();
console.log(g);
// the variable, g, within the function, f...
// changes the global variable, g
5
 

A Tricky One…

What's the output of this code? →

let g = 7;
function f() {
    g = 5;
    function g() {}
}
f();
console.log(g);
7 (!?)
  • function g is hoisted and is local to function f
  • g = 5 reassigns the local variable g!

And From the Previous Slides

This illustrates going up the scope chain… what's the output? →

let x = 10;
function f() {
  function g() {
    x = 20; 
  }
  g(); 
}
console.log(x);
f();
console.log(x);
10 // global
20 // nearest x is global, so global is changed by x = 20
 

Scope Chain Continued

A minor change in code…. (declaring a local in f). What's the output this time? →

let x = 10;
function f() {
  let x = 30;
  function g() {
    x = 20; 
  }
  g(); 
}
f();
console.log(x);
// nearest x is in f, so global x is not changed
10
 

Optional Arguments

You can pass as many or as few arguments to functions as you like!

Wait… what!? →

  • if there are aren't enough arguments, the remaining parameters are undefined
  • if there are too many arguments passed in, they're ignored
  • there's also an arguments variable added to the function's context (along with a this variable) … maybe we'll check these out later


Let's check this out →

 

Optional Arguments Continued

Given the following function…

function f(a, b) {
	console.log(a, b);
}

What is output of this function if called with the following arguments? →

f(1, 2);
f(1);
f();
1 2
1 undefined
undefined undefined
 

Rest Parameters

Using the following syntax for rest parameters … you can create functions that have an indefinite number of arguments represented as a real Array mixed in with initial positional arguments →

function hiEveryone(greeting, ...names) {
    console.log(greeting);
    console.log(names);
}
hiEveryone('Hello', 'Alice', 'Bob', 'Carol')
Hello
[ 'Alice', 'Bob', 'Carol' ]

names is an actual Array, so you can use Array methods on it (unlike the arguments object, which is a fakeArray-like object)

Arguments Object

When a function is called, it gets an arguments object in its context, along with its defined parameters (and this, but we'll talk about that later). Let's see this in action. →

const f = function() {
    console.log("number of args " + arguments.length);
    for (let i = 0, j = arguments.length; i < j; i++) {
        console.log(arguments[i]);
    }
};
f(1, 2, 3);
 

Arguments Object Continued

The arguments object is array-like, but not an array. (Let's see. →)

  • you can index into it
  • you can get its length
  • you can loop over it (with a regular for loop)
  • no methods, though (no slicepopforEach, etc.)
 

Using the Arguments Object

Create a function called mySum that takes an arbitrary number of numbers as separate arguments and returns the sum of all of the arguments

console.log(mySum(1, 2, 3)); // --> 6
var mySum = function() {
	var total = 0;
	for(var i = 0; i < arguments.length; i++) {
		total += arguments[i];
	}
	return total;
}
 

Arguments vs Rest Parameters

Both arguments and rest parameters allow for an arbitrary number of arguments. Which should be used? →

  1. prefer using rest parameters
  2. you get access to a real Array
  3. you can name the resulting Array
  4. however, rest parameters are only available as of ES6

Default Values

What operator could we use to give parameters default values if they're not passed in? →

For example, how would we create a function called greetTheWorld

  • has one parameter, greeting
  • prints out the greeting, followed by "world"
  • greetTheWorld("Hi") → Hi world!
  • however, if an argument is not passed in, greeting should default to "Hello"
  • greetTheWorld() → Hello world!


Let's see an implementation in the next slide. →

 

Default Values Continued

We can use the || operator to give a default value if the value on the left is false-y:

function greetTheWorld(greeting) {
	console.log((greeting || "Hello") + " world!");
}
 

Default Values in ES6

You can also add default values directly in the function header in ES6 →

function f(x=1) {
    console.log(x);
}
f(); // no args, result is 1!

 

Default Parameter Value Details

These values are evaluated at call time, so a new object is created each time (that way, changes won't be persisted across function calls for default arguments that are mutable) →

function extraSauce(condiments = []) {
	condiments.push('ketchup');
	console.log(condiments);
}
extraSauce() // ['ketchup']
extraSauce() // still just ['ketchup']

You can even reference other parameters in your expression!

function foo(a = 1, b = (2 * a)) {
	console.log(a, b);
}
foo();  // 1 2
foo(7); // 7 14
 

More Default Parameter Value Details!

Parameters with default values can be anywhere in parameter list →

function foo(a, b = 'it me!', c) {
	console.log(a, b, c);
}
foo(1, 2, 3)  // 1 2 3
foo(1)        // 1 'it me!' undefined

The value, undefined, is what actually triggers the default value

foo(1, undefined, 3)  // 1 it me! 3
 

Closure

Functions retain access to their original scope, even when the outer function they were defined in has returned. What happens here? →

let gimmeFunction = function() {

	let a = "I'm in here!";

	return function() {
		console.log(a);
	}
}

let myFunction = gimmeFunction();
myFunction();

I'm in here!

 

Wait. What Happened?

(Via MDN)…

  • normally, the local variables within a function only exist for the duration of that function's execution
  • once the outer function gimmeFunction finishes executing, you'd expect that its local variable, a, would no longer be accessible
  • however… a closure is created when it returns a function!
  • closure is a special kind of object that combines two things:
    • a function
    • the environment in which that function was created
    • the environment consists of any local letiables that were in-scope at the time that the closure was created
posted @ 2023-02-02 00:34  M1stF0rest  阅读(19)  评论(0编辑  收藏  举报