My Raw Notes for the Week of 12-13-2021
Tags: javascript, call, apply, bind
Published: 2021-12-24 15:56:26

```
ENGLISH:

The purpose of these notes is to become acquainted with three methods: call(), apply(), and bind().

The difference between 'call()' and 'apply()':
- 'call()' takes arguments separately
- 'apply()' takes arguments as an array
```
```javascript
CODE:

// JS
// The Basics of JS Function Constructors aka Object Constructors (mostly from W3S)

function Person(first, last, age, eye) {
  this.firstName = first
  this.lastName = last
  this.age = age
  this.eyeColor = eye
}

const myFather = new Person('John', 'Doe', 50, 'blue') // myFather is storing what's called "a new instance of Person"

console.log(myFather) // Person {firstName: 'John', lastName: 'Doe', age: 50, eyeColor: 'blue'}

myFather.nationality = 'English'

console.log(myFather) // Person {firstName: 'John', lastName: 'Doe', age: 50, eyeColor: 'blue', nationality: 'English'}

myFather.fullName = function() {
  return `${this.firstName} ${this.lastName}`
}

console.log(myFather) // Person {firstName: 'John', lastName: 'Doe', age: 50, eyeColor: 'blue', nationality: 'English', fullName: ƒ ()}
console.log(myFather.fullName) // ƒ () { return `${this.firstName} ${this.lastName}` }
console.log(myFather.fullName()) // John Doe

// Note that we can't add a new property to a constructor the same way we add a new property to an existing object.
// In other words, the following does not work: Person.nationality = 'English'
// To add a new property to a constructor, we must add the new property to the actual constructor function.

// Just FYI:
// Built-in JS constructors for native objects
// new String()    // A new String object
// new Number()    // A new Number object
// new Boolean()   // A new Boolean object
// new Object()    // A new Object object
// new Array()     // A new Array object
// new RegExp()    // A new RegExp object
// new Function()  // A new Function object
// new Date()      // A new Date object

//-------------------------------------------------------------------

// JS
// call() (mostly from MDN)

// The 'call()' method calls a function with a given 'this' value and arguments provided *individually*.

// Example 1
// 'call()' provides a new value of 'this' to the function/method (the 'Food' function in the below example).
// With 'call()', we can write a method once (the 'Product' function in the below example) and then inherit it in another object,
// without having to rewrite the method for the new object.
function Product(productName, productPrice) {
  this.name = productName
  this.price = `$${productPrice}`
}

console.log(new Product('chocolate', 3)) // Product {name: 'chocolate', price: '$3'}

function Food(foodName, foodPrice) {
  Product.call(this, foodName, foodPrice)
  this.category = 'food'
}

console.log(new Food('cheese', 5)) // Food {name: 'cheese', price: '$5', category: 'food'}

console.log(new Food('cheese', 5).name) // cheese
console.log(new Food('cheese', 5).price) // $5
console.log(new Food('cheese', 5).category) // food


// Example 2
function Product(productName, productPrice, productWeight) {
  this.name = productName
  this.price = `$${productPrice}`
  this.weight = `${productWeight} lb.`
}

function Food(foodName, foodPrice, foodWeight) {
  Product.call(this, foodName, foodPrice, foodWeight)
  this.category = 'food'
}

console.log(new Food('cheese', 5, 2)) // Food {name: 'cheese', price: '$5', weight: '2 lb.', category: 'food'}


// Example 3
// We can use 'call()' to chain constructors for an object (similar to Java).
// In the following example, the constructor for the 'Product' object is defined with two parameters: 'productName' and 'productPrice'.
// Two other functions, 'Food' and 'Toy', *** invoke 'Product' ***, passing 'this', 'foodName'/'toyName', and 'foodPrice'/'toyPrice'.
// *** 'Product' initializes the properties 'name' and 'price'. ***
// Both specialized functions ('Food' and 'Toy') define the 'category'.
function Product(productName, productPrice) {
  this.name = productName
  this.price = `$${productPrice}`
}

function Food(foodName, foodPrice) {
  Product.call(this, foodName, foodPrice)
  this.category = 'food'
}

function Toy(toyName, toyPrice) {
  Product.call(this, toyName, toyPrice)
  this.category = 'toy'
}

const cheese = new Food('feta', 5)
const fun = new Toy('robot', 40)

console.log(cheese) // Food {name: 'feta', price: '$5', category: 'food'}
console.log(fun) // Toy {name: 'robot', price: '$40', category: 'toy'}


// Example 4
const person = {
  fullName: function() {
    return `${this.firstName} ${this.lastName}`
  }
}

const person1 = {
  firstName: 'John',
  lastName: 'Doe'
}

const person2 = {
  firstName: 'Jane',
  lastName: 'Doe'
}

console.log(person.fullName.call(person1)) // John Doe
console.log(person.fullName.call(person2)) // Jane Doe


// Example 5
const person = {
  fullName: function(city, country) {
    return `${this.firstName} ${this.lastName} is from ${city}, ${country}`
  }
}

const person1 = {
  firstName: 'John',
  lastName: 'Doe'
}

console.log(person.fullName.call(person1, 'Oslo', 'Norway')) // John Doe is from Oslo, Norway


// For other examples, like for using 'call()' to invoke an anonymous function, go here:
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/call

//-------------------------------------------------------------------

// JS
// apply() (mostly from MDN)

// The 'apply()' method calls a function with a given 'this' value, and 'arguments' provided *as an array (or an array-like object)*.

// Example 1
const numbers = [5, 6, 2, 3, 7]
const max = Math.max.apply(null, numbers)
console.log(max) // 7
const min = Math.min.apply(null, numbers)
console.log(min) // 2

// Note that when the first argument of 'apply()' is 'undefined' or 'null', as in the above example,
// a similar outcome can be achieved using the array 'spread' syntax:
const numbers = [5, 6, 2, 3, 7]
const max = Math.max(...numbers)
console.log(max) // 7
const min = Math.min(...numbers)
console.log(min) // 2


// Example 2
// 'push()' appends the whole 'array2' array to 'array'.
// 'concat()' appends the elements from 'array2' to a new array.
// 'apply()' appends the elements from 'array2' to 'array'.

// With 'push()'
const array = ['a', 'b']
const array2 = [0, 1, 2]
array.push(array2)
console.log(array) // ['a', 'b', [0, 1, 2]]

// With 'concat()'
const array = ['a', 'b']
const array2 = [0, 1, 2]
const newArr = array.concat(array2)
console.log(newArr) // ['a', 'b', 0, 1, 2]

// With 'apply()'
const array = ['a', 'b']
const array2 = [0, 1, 2]
array.push.apply(array, array2)
console.log(array) // ['a', 'b', 0, 1, 2]


// Example 3
// Note that this example is identical to Example 4 of 'call()', but with 'apply()' instead.
const person = {
  fullName: function() {
    return `${this.firstName} ${this.lastName}`
  }
}

const person1 = {
  firstName: 'John',
  lastName: 'Doe'
}

const person2 = {
  firstName: 'Jane',
  lastName: 'Doe'
}

console.log(person.fullName.apply(person1)) // John Doe
console.log(person.fullName.apply(person2)) // Jane Doe


// Example 4
// Note that this example is identical to Example 5 of 'call()', except that:
// 'apply()' is used instead of 'call()' and 'apply()' accepts arguments in an *array*.

const person = {
  fullName: function(city, country) {
    return `${this.firstName} ${this.lastName} is from ${city}, ${country}`
  }
}

const person1 = {
  firstName: 'John',
  lastName: 'Doe'
}

console.log(person.fullName.apply(person1, ['Oslo', 'Norway'])) // John Doe is from Oslo, Norway


// For other examples, like for using 'apply()' to chain constructors for an object, go here:
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/apply

//-------------------------------------------------------------------

// JS
// bind() (mostly from MDN)

// The 'bind()' method creates a new function that, when called, has its 'this' keyword set to the provided value,
// with a given sequence of arguments preceding any provided when the new function is called.

// Example 1
// Creating a bound function
const module = {
  x: 42,
  getX: function() {
    return this.x
  }
}
console.log(module.getX()) // 42

const unboundGetX = module.getX
console.log(unboundGetX) // ƒ () { return this.x }
console.log(unboundGetX()) // undefined // the function gets invoked at the global scope

const boundGetX = unboundGetX.bind(module)
console.log(boundGetX()) // 42


// Example 2
// Creating a bound function, again
this.x = 9 // this 'this' refers to the global 'window' object

const module = {
  x: 81,
  getX: function() { return this.x }
}
console.log(module.getX()) // 81

const retrieveX = module.getX
console.log(retrieveX()) // 9 // the function gets invoked at the global scope

// Create a new function with 'this' bound to 'module'
const boundGetX = retrieveX.bind(module)
console.log(boundGetX()) // 81


// Example 3
// Partially applied functions (making a function with pre-specified initial arguments)
// These arguments (if any) follow the provided 'this' value and are then inserted at the start of the arguments
// passed to the target function, followed by whatever arguments are passed to the bound function at the time it
// is called.
function list() {
  return Array.from(arguments)
}

function addArguments(arg1, arg2) {
  return arg1 + arg2
}

const list1 = list(1, 2, 3)
console.log(list1) // [1, 2, 3]

const result1 = addArguments(1, 2)
console.log(result1) // 3

// Create a function with a preset leading argument
const leadingThirtysevenList = list.bind(null, 37)

// Create a function with a preset first argument.
const addThirtySeven = addArguments.bind(null, 37)

const list2 = leadingThirtysevenList()
console.log(list2) // [37]

const list3 = leadingThirtysevenList(1, 2, 3)
console.log(list3) // [37, 1, 2, 3]

const result2 = addThirtySeven(5)
console.log(result2) // 42 // from 37 + 5

const result3 = addThirtySeven(5, 10)
console.log(result3) // 42 // from 37 + 5 // the second argument is ignored


// For other examples, go here:
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind
```