Javascript function part 4/4: generator, high-order function and handy syntax

This tutorial is a part of the Learn everything about Javascript in one course.

Generator function, function can be paused and continued

Generator function:

  • is defined like normal function with an additional * (function*).
  • can be paused and continued up to the next yield key word.
  • yield keyword can return a value (like return but does not finish the function).
  • when invoked will return a Generator object (is used to control the pause or continue).
generator_function.js
// Generator function, function can be paused and continued

function* createGenerator() {
  console.log('run to yield 1')
  yield { name: 'Bruce' } // 1st pausable point
  yield // 2nd pausable point
  console.log('run to yield 3')
  yield 3 // 3rd pausable point
  console.log('finish')
  return 'final'
}

// Generator object
const generator = createGenerator() // invoking Generator function will return a Generator object

console.log(generator.next()) // function is paused at the 1st `yield`
// run to yield 1
// { value: { name: 'Bruce' }, done: false }

console.log(generator.next())
// { value: undefined, done: false }, the 2nd `yield` does not return anything

console.log(generator.next()) // { value: 3, done: false }

console.log(generator.next())
// finish
// { value: 'final', done: true }

console.log(generator.next()) // { value: undefined, done: true }

Generator object, object to control to pause and continue Generator function

Method Return Example
generator.next() an object with value, done key:
- value: yielded by the yield expression.
- done: boolean, true if function finishes.
{ value: { name: 'Bruce' }, done: false }
generator.return(val) the val and finish the function { value: val, done: true }
generator.throw(new Error('your message')) throw exception within the function and finish the function. This will crash the program. Error: your message
gen_object_return.js
// Generator function, function can be paused and continued

function* createGenerator() {
  console.log('run to yield 1')
  yield { name: 'Bruce' } // 1st pausable point
  yield // 2nd pausable point
  console.log('run to yield 3')
  yield 3 // 3rd pausable point
  console.log('finish')
  return 'final'
}

// Generator object
const generator = createGenerator() // invoking Generator function will return a Generator object

console.log(generator.next()) // function is paused at the 1st `yield`
// run to yield 1
// { value: { name: 'Bruce' }, done: false }

console.log(generator.return('lala'))
// { value: 'lala', done: true }, the return method return the passed in value and finish the function

console.log(generator.next()) // { value: undefined, done: true }
gen_object_throw.js
// Generator function, function can be paused and continued

function* createGenerator() {
  console.log('run to yield 1')
  yield { name: 'Bruce' } // 1st pausable point
  yield // 2nd pausable point
  console.log('run to yield 3')
  yield 3 // 3rd pausable point
  console.log('finish')
  return 'final'
}

// Generator object
const generator = createGenerator() // invoking Generator function will return a Generator object

console.log(generator.next()) // function is paused at the 1st `yield`
// run to yield 1
// { value: { name: 'Bruce' }, done: false }

console.log(generator.throw(new Error('your message'))) // Error: your message

High-order function

is function that either:

  • accepts other function as an argument.
  • returns a function.

Some examples are: function that accepts callback, memoization function, ... It's nothing new to us but the name "high-order function" is worth mentioned here because a lot of documents and blogs refer to this name.

The "arguments" object in function holds all of its arguments

arguments object within a function:

  • available to use within the function.
  • contains all arguments passed into that function.
  • is an array-like object (have limited methods and properties of array).
argument_object.js
// The "arguments" object in function holds all of its arguments

function printAllArguments(a, b, c, d) {
  console.log(arguments) // (i)
  console.log(arguments[0]) // (ii)
  console.log(arguments[1]) // (iii)
  console.log(arguments[2]) // (iv)
  console.log(arguments.length) // (v)
  console.log(a,b,c) // (vi)
}

printAllArguments(1,2,3, 'd')
// [Arguments] { '0': 1, '1': 2, '2': 3, '3': 'd' }, (i)
// 1, (ii)
// 2, (ii)
// 3, (iv)
// 4, (v)
// 1 2 3, (vi)

Spread syntax ..., spead given value into list

Spread syntax ... turns array, string, object => list of arguments.

spread_syntax.js
// Handy syntax: spread syntax ..., spead given value into list

// spread array to list of arguments: ...array
const arr = [1,2,3, 'd']
console.log(arr) // [ 1, 2, 3, 'd' ]

console.log(...arr) // spread syntax
// 1 2 3 d
// equals to: console.log(arr[0], arr[1], arr[2], arr[3])
// hence "spread"

const arr2 = [...arr, 'a', 'b']
console.log(arr2) // [ 1, 2, 3, 'd', 'a', 'b' ]

function print(a, b, c, d) {
  console.log(d)
}
print(...arr) // d


// spread string, ...string
const str = 'abc'
console.log(...str) // a b c
// equals to: console.log(str[0], str[1], str[2])


// spread object, ...object
const obj = {
  a: 'b',
  c: 1
}
const obj2 = {
  ...obj, // spread syntax
  d: 2
}
console.log(obj2) // { a: 'b', c: 1, d: 2 }

Destructuring assignment syntax to unpack values into distinct variables

Values to destructure can be arrays or object properties. Destructuring also called "treeshaking". We do variable assignment at the same time.

destructuring.js
// Destructuring assignment syntax to unpack values into distinct variables

// unpack and assign from array
const arr = [1,2,3,'a']
const [,val1,, val3] = arr // destructuring
console.log(val1) // 2
console.log(val3) // a

// unpack and assign from object properties
const obj = {
  a: 'b',
  'x-y': 1,
  c: 'd'
}
const { a, c } = obj // destructuring and assign variable the same names with object key
console.log(a) // b
console.log(c) // d

const { 
  a:val5,
  c: val6,
  'x-y': xy,
} = obj // destructuring and assign variable the DIFFERENT names with object key
console.log(val5) // b
console.log(val6) // d
console.log(xy) // 1

Set default argument value for a function

When an argument is missing, the default value is used. When argument is present, default value is ignored.

default_value.js
// Set default argument value for function

// in this function, the default values for a is 1,
// default b is false
// default c is 'abc'
function print(a = 1, b = false, c = 'abc') {
  console.log(a)
  console.log(b)
  console.log(c)
}

print() // no argument present will use default values
// 1
// false
// abc

print(2,2,2) // no default value used
// 2
// 2
// 2

Destructuring and default value for function argument

It's very common that destructuring and default value are used at the same time where we define a function. Here is an example to help you get familiar with this pattern.

destructuring_default.js
// Set default argument value for function

// in this function, the argument passed in is an object, we then:
// 1. destructure it
// 1. give destructured input default values
function print({ a: val1 = 1, b: val2 = {}, c: val3 = false }) {
  console.log(val1)
  console.log(val2)
  console.log(val3)
}

print({}) // no argument present will use default values
// 1
// {}
// false

print({ a: 3, b: { d: 'f'}, c: true}) // no default value used
// 3
// { d: 'f' }
// true

Summary

  • Generator function is function which can be paused and continued.
  • High-order function is function that either accepts other function as an argument and/or returns a function.
  • Spread, destructuring assignment and default value syntax are widely used for shorthand.