JavaScriptの関数の高度な機能について

# 初めに
今回はJavaScriptの関数への振り返りです。基礎概念の勉強、やっと一段落だと思います。とてもいいチュートリアルに出会えて心から感謝しています。いつも読んでくださる皆さんもありがとうございます。
これからほかの勉強（ブラウザやWEBの基礎、React.js等々）まだ、もっと、たくさん勉強してまとめていきたいと思います！

今回振り返りの参考文章はこちらです。

https://javascript.info/advanced-functions

# Recursion
## Recursion vs. for Loop
```jsx
// recursion and stack
console.time('for Loop');
function pow(x, n) {
  let result = 1;
  for (let i = 0; i < n; i++) {
    result *= x;
  }
  return result;
}
console.log(pow(2, 1000))
console.timeEnd('for Loop');
// for Loop: 4.052ms

console.time('Recursion');
function pow(x, n) {
  if (n === 1) {
    return x;
  } else {
    return x * pow(x, n - 1);
  }
}
console.log(pow(2, 1000))
console.timeEnd('Recursion');
// Recursion: 3.997ms
```
この書き方では計算量どちらも$O(n)$なので、あまり差がありませんが。
気になるのはメモリのほうです。`for`ループ内部コードは毎回実行後`GC`により回収されるが、再帰関数は自分自身再利用する時点ではクロージャにもなり、最後の呼び出しから返り値を受け取るまで回収されずメモリリークになりがちな原因の一つでもあります。

https://www.ditdot.hr/en/causes-of-memory-leaks-in-javascript-and-how-to-avoid-them#closures

クロージャ、再帰関数どちらも重要で避けられない概念です。全要素の簡単な走査なら`for`ループ、一部の検定から再度呼び出しなら再帰関数。
```jsx
// recursive traversals
let company = {
  sales: [ // case1: [{}, {}]
    { name: 'John', salary: 1000 },
    { name: 'Alice', salary: 1600 }
  ],
  development: { // case2: {[{},{}],[]}
    sites: [
      { name: 'Peter', salary: 2000 },
      { name: 'Alex', salary: 1800 },
    ],
    internals: [
      { name: 'Jack', salary: 1300 },
    ]
  }
};
function sumSalaries(department) {
  if (Array.isArray(department)) { // case1
    return department.reduce((previous, current) => {
      return previous + current.salary;
    }, 0);
  } else { // case2
    let sum = 0;
    for (let sub of Object.values(department)) {
      sum += sumSalaries(sub);
    }
    return sum;
  }
}
console.log(sumSalaries(company));
```
## Task
```jsx
// sum all numbers till the given one
// formula // n * ( n + 1 ) / 2
function sumTo(num) {
  return num * (num + 1) / 2;
}
console.log(sumTo(100)); // 5050

// calculate factorial
// recursion
function factorial(num) {
  return (num !== 1) ? num * factorial(num - 1) : 1
}
console.log(factorial(5)); // 120

function factorial(num) {
  // n = 0 => false
  return num ? num * factorial(num - 1) : 1;
}
console.log(factorial(5)); // 120

// Output a single-linked list
let list = {
  value: 1,
  next: {
    value: 2,
    next: {
      value: 3,
      next: {
        value: 4,
        next: null
      }
    }
  }
};
function printList(list) {
  console.log(list.value);
  if (list.next) {
    printList(list.next);
  }
}
printList(list);
function sumList(list) {
  let sum = 0;
  sum += list.value;

  if (list.next) {
    sum += sumList(list.next);
  }
  return sum;
}
console.log(sumList(list)); // 10

// Output a single-linked list in the reverse order
function printReverseList(list) {
  if (list.next) {
    printReverseList(list.next);
  }
  console.log(list.value);
}
printReverseList(list);
// 4
// 3
// 2
// 1
// 
function printReverseList2(list) {
  let arr = [];
  let temp = list;
  while (temp) {
    arr.push(temp.value);
    temp = temp.next;
  }
  for (let i = arr.length - 1; i >= 0; i--) {
    console.log(arr[i]);
  }
}
printReverseList2(list);
// 4
// 3
// 2
// 1
```

# Rest parameters and spread syntax
分割代入構文からの宣告された変数としての残余引数
残余引数／イテレータできるオブジェクトを展開するスプレット構文
```jsx
let [...args] = [1, 2, 3]; // rest parameters
console.log(args); // [ 1, 2, 3 ]
console.log(...args); // 1 2 3 // spread syntax
```

## Rest parameters vs. arguments object
残余引数と`arguments`オブジェクトの違い。
```jsx
function sumAll(...args) { // rest parameters
  console.log(args); // [ 1, 2, 3 ]
  console.log(...args); // 1 2 3 // spread syntax

  // arguments object
  console.log(arguments); // [Arguments] { '0': 1, '1': 2, '2': 3 }

  let sum = 0;
  // args is array, it is iterable
  for (let arg of args) sum += arg;
  return sum;
}
console.log(sumAll(1, 2, 3));
```
配列風オブジェクトである`arguments`オブジェクト要素へのアクセスは配列と同じです。
```jsx
function showName(firstName, lastName, ...titles) {
  console.log(firstName + ' ' + lastName); // Julius Caesar
  console.log(titles[0], titles[1]); // Consul Imperator
  console.log(titles.length); // 2
}
showName('Julius', 'Caesar', 'Consul', 'Imperator');

// THe "arguments" variable
function showName() {
  console.log(arguments.length); // 2
  console.log(arguments[0], arguments[1]); // Julius Caesar
}
showName('Julius', 'Caesar');
```

### Arrow function don't have "arguments"
アロー関数には`arguments`持っていません。
```jsx
// arrow function don't have "arguments"
let arrowFn = () => {
  console.log(arguments);
  // ReferenceError: arguments is not defined
}
arrowFn(1, 2, 3);
```

## Spread syntax
イテレータできるオブジェクトもしくは`[Symbol.iterator]`プロパティを持つオブジェクトならスプレット構文が使えます。
```jsx
// Spread syntax
console.log(Math.max(1, 2, 3)); // 3
let arr = [1, 2, 3];
console.log(Math.max(...arr)) // 3

let arr1 = [1, -2, 3, 4];
let arr2 = [8, 3, -8, 1];
console.log(Math.max(...arr1, ...arr2)); // 8
console.log(Math.max(...arr1, ...arr2, 25)); // 25
console.log([0, ...arr1, 2, ...arr2]);
// [
//   0, 1, -2, 3, 4,
//   2, 8, 3, -8, 1
// ]
```
### Shallow Copy
スプレット構文からの要素がオブジェクトタイプであれば浅いコピーになります。浅いコピーでオブジェクトタイプのメモリロケーションをコピーするだけなので、厳密等価ではいつも`true`です。
単純な要素内容の比較なら、`JSON.Stringify()`で文字列にしてから比較するのもいいです。
```jsx
// Copy an array
let arr = [[1], 2, 3];
let arrCopy = [...arr];
console.log(arr[0] === arrCopy[0]); // true
// note: Spread syntax is shallow copy, if value was array/object, they are equal

// use JSON.stringify() to convert to String
console.log(JSON.stringify(arr) === JSON.stringify(arrCopy)); // true
arr.push(4);
console.log(arr); // [ [ 1 ], 2, 3, 4 ]
console.log(arrCopy); // [ [ 1 ], 2, 3 ]

// Copy an object
let obj = { a: 1, b: 2, c: ['3', '4'] };
let objCopy = { ...obj };
console.log(JSON.stringify(obj) === JSON.stringify(objCopy)); // true
console.log(obj === objCopy); // false
console.log(obj.c === objCopy.c); // true // shallow copy
```

### Filter through function
`arr.filter(callbackFn()(element))`
インラインコールバックのように書くのもいいと思いますが、コールバックならもっと簡潔に書けます。
```jsx
// filter through function
let comparedArr = [1, 2, 3, 4, 5, 6, 7];

// filter inBetween
function inBetween(a, b) {
  return function (x) {
    return x >= a && x <= b;
  }
}
// Callback function
console.log(comparedArr.filter(inBetween(3, 6))); // [ 3, 4, 5, 6 ]
// Inline callback function
console.log(comparedArr.filter((x) => x >= 3 && x <= 6)); // [ 3, 4, 5, 6 ]
// note: inBetween(3, 6)(x)

// filter inArray
function inArray(arr) {
  return function (element) {
    return arr.includes(element);
  };
}
console.log(comparedArr.filter(inArray([1, 2, 10]))); // [ 1, 2 ]
console.log(comparedArr.filter((x) => [1, 2, 10].includes(x))); // [ 1, 2 ]
// note: inArray([1, 2, 10])(x)
console.log(inArray([1, 2, 10])(1)); // true
console.log(inArray([1, 2, 10])(2)); // true
console.log(inArray([1, 2, 10])(7)); // false
```

### Sort by field
プロパティ別で要素をソートしたいときもありますね。
```jsx
// sort by field
let users = [
  { name: "John", age: 20, surname: "Snow" },
  { name: "Pete", age: 18, surname: "Pen" },
  { name: "Ann", age: 19, surname: "Hathaway" },
  { name: "Emma", age: 19, surname: "Watson" },
  { name: "Harry", age: 19, surname: "Potter" },
];

function byField(fieldName) {
  return (a, b) =>
    a[fieldName] > b[fieldName] ? 1 : b[fieldName] > a[fieldName] ? -1 : 0;
}

console.log(users.sort(byField("name")));
console.log(users.sort(byField("age")));
console.log(users.sort(byField("surname")));
```
ひらがなや漢字のソートなら`localeCompare()`関数が必要です。

https://qiita.com/hu-yu/items/d25396d7a31d3383e8d2#stringprototypelocalecompare

（`JSON.Stringify()`にしてからソートするのもいいと思います。（別の工夫が必要です。））


# Function object & NFE(Named Function Expression)
## The `name` property
関数や関数式（アロー関数、匿名関数も含め）には`name`プロパティがあります。
```jsx
function sayHi() {
  console.log('Hi');
}
console.log(sayHi.name); // sayHi

// function expression as default value
function foo(anotherFn = function () { }) {
  console.log(anotherFn.name);
}
foo(); // anotherFn

// in object
let user = {
  sayHi() { },
  sayBye() { }
};
console.log(user.sayHi.name); // sayHi
console.log(user.sayBye.name); // sayBye

// in array
let arr = [function test() { }, function () { }];
console.log(arr[0].name); // test
console.log(arr[1].name); // <empty string>

// function expression: arrow function
let arrowFn = () => { };
console.log(arrowFn.name); // arrowFn

// function expression: anonymous function
let anonymousFn = function () { };
console.log(anonymousFn.name); // anonymousFn
```

## The "length" property
`length`プロパティはパラメータの個数による決めます。しかし残余引数は含まれていません。（独立した配列になるから。）
```jsx
function f1(a) { }
console.log(f1.length); // 1
function f2(a, b) { }
console.log(f2.length); // 2
function many(a, b, ...more) { }
console.log(many.length); // 2
```
この特性を用いて、`for...of`と`if`条件式を通して自動ハンドラーの実装ができます。
```jsx
function ask(question, ...handlers) {
  let isYes = confirm(question);

  // handlers = [() => console.log('You said yes'),  (result) => console.log(result)]
  for (let handler of handlers) {
    // zero-argument function will be called when the answer is positive
    // function with arguments will always be called and returns an answer
    if (handler.length === 0) {
      // Ok => both handlers are called
      // cancel => only the second one
      if (isYes) handler();

    } else {
      handler(isYes);
    }
  }
}

ask(
  'Question?',
  () => console.log('You said yes'),
  (result) => console.log(result)
);
```
（この参考文章の例が自分にとって少しわかりづらかったのですが、ブラウザで実行させてみたほうが理解が早いと思います。）

## Custom properties
関数にカスタムプロパティを持たせて、外部からアクセスすることができます。（`this`との違いは後でまとめます。）
```jsx
function sayHi() {
  console.log('Hi');
  sayHi.counter++;
  // console.log(this.counter === sayHi.counter);
  // TypeError: Cannot read properties of undefined (reading 'counter')
}
sayHi.counter = 0;

console.log(sayHi); // [Function: sayHi] { counter: 0 }
sayHi(); // Hi
sayHi(); // Hi
console.log(`Called ${sayHi.counter} times`); // Called 2 times

let test = new sayHi();
console.log(test); // sayHi {}
console.log(test.counter); // undefined
```
（関数のカスタムプロパティはインスタンスには持たせられない、継承できないプロパティです。）

外部からアクセスできるというのは、勝手に変えられる、情報が暴かれている状態です。いったんカスタムプロパティを内部関数のプロパティにして、内部関数から返り値として返してもらえば外部アクセスを制限できます。
```jsx
// function object
// closure storing
function makeCounter() {
  function counter() {
    // store as function counter's own property
    return counter.count++;
  }
  counter.count = 0; // initial value
  return counter;
}
// external function is unable to see internal function's property
console.log(makeCounter); // [Function: makeCounter]
let counter = makeCounter(); // returns function counter
console.log(counter()); // 0
console.log(counter()); // 1

counter.count = 10;
console.log(counter()); // 10
```

### vs. this(context)
カスタムプロパティと`this`（コンテキスト）プロパティの比較です。
```jsx
function thisCounter() {
  this.count = 0;
  // arrow function doesn't have its own scope("this"),
  // it will inherit scope from outer scope
  this.add = () => {
    return ++this.count;
  }
}
console.log(thisCounter); // [Function: thisCounter] // doesn't have function object

let counter2 = new thisCounter();
// with keyword "new", this.property will convert to instance's own property
console.log(counter2); // thisCounter { count: 0, add: [Function (anonymous)] }
console.log(counter2.add()); // 1
console.log(counter2.add()); // 2
```
カスタムプロパティとは違い、`this`はコンテキストの参照を示すプロパティです。インスタンスオブジェクトが創られるたび、`this`を通してインスタンスにプロパティを付与する。（`this`は動的で、コンストラクタ関数もインスタンスも自身の`this`は常に自身に参照している。）

## Task
```jsx
// task
// Set and decrease for counter
function makeCounter() {
  let count = 0;
  function counter() {
    return ++count;
  }
  counter.set = (value) => count = value;
  counter.decrease = () => --count;
  return counter;
}
let counter = makeCounter();
console.log(counter);
// [Function: counter] {
//   set: [Function(anonymous)],
//   decrease: [Function(anonymous)]
// }
console.log(counter()); // 1
console.log(counter.set(10)); // 10
console.log(counter.decrease()); // 9

// Sum with an arbitrary amount of brackets
function sum(a) {
  let currentSum = a;
  function innerSum(b) {
    currentSum += b;
    // for next calling // currying
    return innerSum;
  }

  // custom method
  innerSum.showValue = function () {
    return currentSum;
  }
  // innerSum.showValue = () => currentSum;

  return innerSum;
}

console.log(sum(1)(2).showValue()); // 3
console.log(sum(5)(-1)(2).showValue()); // 6
console.log(sum(6)(-1)(-2)(-3).showValue()); // 0
```

# Scheduling: setTimeout and setInterval
`setTimeout()`：グローバルのタイマーメソッドです。指定されたミリ秒を待ちコールバックを実行する。
`clearTimeout()`：`setTimeout()`から返した識別子を特定の`setTimeout()`を解除できます。
```jsx
// Canceling with clearTimeout
let timerId = setTimeout(() => console.log('never happens'), 1000);
console.log(timerId); // Timeout {...}
clearTimeout(timerId);
console.log(timerId); // still exist
```

`setInterval()`：指定されたミリ秒間隔を待ちコールバックを繰り返し実行する。
`clearInterval()`：指定された`setInterval()`の識別子によって特定の`setInterval()`を解除する。
```jsx
// setInterval
let timerId = setInterval(() => console.log('tick'), 2000);
setTimeout(() => {
  clearInterval(timerId);
  console.log('stop');
}, 5000);
// tick
// tick
// stop
```

## Task
```jsx
// task
// Output every second
// Using setInterval
function printNumbers(from, to) {
  let current = from;
  let timerId = setInterval(function () {
    console.log(current);
    if (current === to) {
      clearTimeout(timerId);
    }
    current++;
  }, 1000);
}
printNumbers(1, 5);

// Using nested setTimeout
function printNumbers(from, to) {
  let current = from;
  setTimeout(function go() {
    console.log(current);
    if (current < to) {
      // recursion
      setTimeout(go, 1000);
    }
    current++;
  }, 1000);
}
printNumbers(5, 10);
```

# Decorators and forwarding, call/apply
`call()`：コンテキスト`this`の参照を強制的に変えるメソッドです。

オブジェクトからメソッド呼び出しでは下のように参照先を明記すれば簡単にできます。
```jsx
let obj = {
  returnOne() {
    return 1;
  },
  myMethod() {
    return this.returnOne();
  }
}
console.log(obj.myMethod()); // 1
```
しかし別のスコープの中では、外部のメソッドを呼び出すと**現在のスコープではこのような名前のプロパティ（メソッド）が存在しない**というエラーができてきます。
```jsx
function test(fn) {
  let result = fn()
  return result;
}
console.log(test(obj.myMethod));
// TypeError: Cannot read properties of undefined (reading 'objMethod')
```
`call()`や`apply()`を通せばコンテキストの参照を変え、正しい参照先からメソッドを見つけて利用できるようになる。
```jsx
// call()
function test(fn) {
  let result = fn.call(obj);
  return result;
}
console.log(test(obj.myMethod)); // 1

// nested function
function test(fn) {
  return function () {
    let result = fn.call(obj);
    return result;
  }
}
console.log(test(obj.myMethod)()); // 1
```
しかし現在のスコープにすでに同じ変数が存在したら、最短ルートの法則により一番最初に見つけた変数（プロパティ）を利用し、望まない結果が生じるかもしれません。
```jsx
function test(fn) {
  let obj = null;
  return function () {
    let result = fn.call(obj);
    return result;
  }
}
// TypeError: Cannot read properties of null(reading 'returnOne')
```

## Transparent caching
参考文章から取った例です。簡単に説明すると、`worker`オブジェクトのように関数の結果に及ぼす変数なら、ほかの関数（やプロパティ）に任せば機能分離というコードをシンプルにすることができる。
```jsx
let worker = {
  someMethod() {
    return 1;
  },
  slow(x) {
    console.log(`Called with ${x}`);
    return x * this.someMethod();
  }
};
```
下の`cachingDecorator()`はデコレータの応用例です。デコレータはあるオブジェクトをベースに、新しい処理を追加や変更するためのデザインパターンです。
```jsx
function cachingDecorator(fn) {
  let cache = new Map();
  return function (x) {
    if (cache.has(x)) {
      return cache.get(x);
    }
    let result = fn.call(worker, x);
    cache.set(x, result);
    return result;
  };
}
console.log(cachingDecorator(worker.slow));
// [Function (anonymous)]
console.log(cachingDecorator(worker.slow)(2));
// Called with 2
// 2

// or
function cachingDecorator(fn) {
  let cache = new Map();
  return function (x) {
    if (cache.has(x)) {
      return cache.get(x);
    }
    let result = fn.call(this, x);
    cache.set(x, result);
    return result;
  };
}
worker.slow = cachingDecorator(worker.slow);
// use function parameter fn to store original worker.slow
console.log(worker.slow(2));
// Called with 2
// 2
```
`let result = fn.call(this, x);``call()`が`this`の指定で正しく動作できたのは、`worker.slow = cachingDecorator(worker.slow);`で、元の`worker.slow`メソッドを`cachingDecorator(worker.slow)`上書きされたのです。そして元の`worker.slow`メソッドは消されたわけではなく`cachingDecorator(worker.slow)`引数として`fn`に保存されています。

これでスコープ内で同じ変数が宣告されても正しく参照したり、ベースのメソッドも暴かれることがなくなる。（上書きしないままオブジェクト`worker.slow`を入れ替えることで結果が改ざんされたり、データ漏洩する可能性があります。外部への参照は内部への入口とも言えるので。）

下はほかの例です。
```jsx
let anotherWorker = {
  slow(min, max) {
    console.log(`Called with ${min}, ${max}`);
    return min + max;
  }
};

function cachingHash(fn, hash) {
  let cache = new Map();
  return function () {
    let key = hash(arguments);
    if (cache.has(key)) {
      return cache.get(key);
    }

    let result = fn.call(this, ...arguments); // spread
    cache.set(key, result);
    return result;
  };
}
function hash(args) {
  return `${args[0]}, ${args[1]}`;
}
// add new property in object "anotherWorker"
// note: if we will use anotherWorker.slow in other function, then it shouldn't be covered
// but if we want to protect from being called by other functions or accessed, we should overwrite itself
anotherWorker.slow = cachingHash(anotherWorker.slow, hash);
console.log(anotherWorker.slow(3, 5));
// Called with 3, 5
// 8
console.log(`Again ${anotherWorker.slow(3, 5)}`);
// Again 8
```

組み込みメソッドを`call()`するなら一定の前提条件があります。
```jsx
// borrowing a method
function arrCallArgus() {
  // console.log(arguments); // [Arguments] { '0': 1, '1': 2 }
  // arguments is iterable
  return Array.prototype.join.call(arguments, '');
}
console.log(arrCallArgus(1, 2)) // 12
```
まず別の例を見ていきたいと思います。
```jsx
function arrCallStr() {
  // console.log(arguments); // [Arguments] { '0': 1, '1': 2 }
  // arguments is iterable
  let str = '123';
  return Array.prototype.join.call(str);
}
console.log(arrCallStr()); // 1,2,3
```
変数`str`を配列の組み込みメソッド`join()`を通して連結する。結果が`1,2,3`というのはまず一旦`str`を配列（`[1, 2, 3]`）にして区切り文字指定なしで返され`1,2,3`になったわけです。

なので`arguments`か文字列のようにイテレータできるのが前提条件ですが、
```jsx
// iterable object
function arrCallMap() {
  let map = new Map([[0, '1'], [1, '2']]);
  // console.log(map); // Map(2) { 0 => '1', 1 => '2' }
  return Array.prototype.join.call(map);
}
console.log(arrCallMap()); // print nothing

function arrCallSet() {
  let set = new Set([1, 2, 3]);
  return Array.prototype.join.call(set);
}
console.log(arrCallSet()); // print nothing

// Number is not iterable
function arrCallNum() {
  let num = 123;
  return Array.prototype.join.call(num);
}
console.log(arrCallNum()); // print nothing
```
イテレータできるオブジェクトなら`call()`では何も出てきません。

## Task
```jsx
// task
// Spy decorator
function work(a, b) {
  console.log(a + b);
}
// Sinon.JS
function spy(fn) {
  function wrapper(...args) {
    wrapper.calls.push(args);
    return fn.apply(this, args);
  }
  wrapper.calls = [];
  return wrapper;
}
work = spy(work);

work(1, 2); // 3
work(4, 5); // 9
for (let args of work.calls) {
  console.log(`call: ${args.join()}`)
}
// call: 1, 2
// call: 4, 5
console.log(work.calls); // [ [ 1, 2 ], [ 4, 5 ] ]

// Delaying decorator
function foo(x) {
  console.log(x);
}
function delay(fn, ms) {
  return function () {
    setTimeout(() => fn.apply(this, arguments), ms);
  };
}

let f1000 = delay(console.log, 1000);
f1000('test'); // test
```

### Debounce decorator
```jsx
// Debounce decorator
function debounce(fn, ms) {
  let timeout = null;
  return function () {
    clearTimeout(timeout);
    timeout = setTimeout(() => fn.apply(this, arguments), ms);
  }
}

let eventHandlers = {
  wait(x) {
    console.log(x);
  }
}

debounce(eventHandlers.wait, 1000)('wait for one second'); // wait for one second
debounce(eventHandlers.wait, 3000)('wait for two seconds'); // wait for two seconds
```

### Throttle decorator
```jsx
// Throttle decorator
function throttle(fn, ms) {
  let isThrottled = false
  let savedArgs = null;
  let savedThis = null;

  function wrapper() {
    if (isThrottled) {
      savedArgs = arguments;
      savedThis = this;
      return;
    }
    isThrottled = true;

    fn.apply(this, arguments);

    setTimeout(function () {
      isThrottled = false;
      if (savedArgs) {
        wrapper.apply(savedThis, savedArgs);
        savedArgs = null;
        savedThis = null;
      }
    }, ms);
  }
  return wrapper;
}
```

# Function binding
## Losing “this”
なぜ`this`を失うでしょうか。これは`setTimeout()`などの`WebAPI`だけに起こる問題ではありませんが。少し別の例を見ていきたいと思います。
```jsx
let user = {
  firstName: 'John',
  sayHi() {
    console.log(`Hello, ${this.firstName}!`);
  }
}
setTimeout(user.sayHi, 1000); // Hello, undefined!

function test1(fn) {
  console.log('do something for one second');
  // fn is function parameter which belongs to function "test1"'s scope
  return fn();
}
test1(user.sayHi);
// TypeError: Cannot read properties of undefined (reading 'firstName')
// note: when we put argument into function, actually it means an action on "reassign", so the scope of argument will be changed
```
`this`を失ったのは、引数のスコープが変更されたのが原因です。引数が関数に入れるというのは関数のパラメータに代入するということで、引数は関数スコープに所属していて`this`が現在の関数に参照しています。

なので引数にせず、そのまま呼び出したら関数に所属しないプロパティ（`user`）が外部への参照を探し、最後は外部の`user`にたどり着いて`sayHi()`を正しく実行した。（もし関数内変数`user`が同じく存在したら`sayHi()`が見つからずにエラーになる。）
```jsx
function test2() {
  console.log('do something for one second');
  // not as argument, its scope won't be changed
  return function () {
    user.sayHi();
  }
}
test2()();
// do something for one second
// Hello, John!
// or
function test3() {
  console.log('do something for one second');
  // not as argument, its scope won't be changed
  return () => user.sayHi();
}
test3()();
// do something for one second
// Hello, John!

setTimeout(() => user.sayHi(), 1000); // Hello, John!
```
これが`Solution 1: a wrapper`が成立したわけです。

`setTimeout()`にはもう一つの特性があります。`setTimeout()`のようなタイマーが非同期を実現するために、`Event Loop`ではタイマーが別のタスク`Macrotask`に移し処理を行い、下のコードを継続していくのです。

```jsx
let user = {
  firstName: 'John',
  sayHi() {
    console.log(`Hello, ${this.firstName}!`);
  }
}
setTimeout(() => user.sayHi(), 1000);
user = {
  sayHi() {
    console.log(`Reference has been changed`)
  }
}
// Reference has been changed
```

https://ja.javascript.info/event-loop

なので`Macrotask`でタイマーが終わり、コールバックを実行する時に変数`user`はすでに入れ替えられ、別の`sayHi()`を実行してしまいました。

これを解決するには`bind()`で外側から強制的にコンテキストの参照先を結合させます。
```jsx
// bind()
// only property
let user = {
  firstName: 'John'
};
// method
function sayBye() {
  console.log(`Bye, ${this.firstName}`);
}
let bye = sayBye.bind(user);
bye(); // Bye, John

function greet(phrase) {
  console.log(`${phrase}, ${this.firstName}`);
}
let greeting = greet.bind(user);
greeting('Hello'); // Hello, John
// note: we don't have to change scope, we can use "bind()" to combine with "user" which "this" always refers to
```
（`bind()`を利用することでプロパティとメソッド、機能を分離する（`decoupling`）こともできます。）
```jsx
let user = {
  firstName: 'John',
  sayHi() {
    console.log(`Hello, ${this.firstName}`);
  }
}
let sayHi = user.sayHi.bind(user);
setTimeout(sayHi, 1000);

user = {
  sayHi() {
    console.log(`Reference has been changed`);
  }
}
// Hello, John
```
多くのメソッドが存在し、それらをバインドする必要があるなら、
```jsx
let user = {
  firstName: 'John',
  methods: [
    function sayHi() {
      console.log(`Hello, ${this.firstName}`);
    },
    function sayBye() {
      console.log(`bye, ${this.firstName}`);
    }
  ]
}
// bindAll
for (let method of user.methods) {
  if (typeof method === 'function') {
    // bind() in for...of
    method = method.bind(user);
    setTimeout(method, 1000);
  }
}
// Hello, John
// bye, John
console.log(user.methods[0]);
// [Function: sayHi]

// or
user.methods[0] = user.methods[0].bind(user);
setTimeout(user.methods[0], 1000);
// Hello, John
console.log(user.methods[0]);
// [Function: bound sayHi]
```
`for...of`では`user.methods`配列内のメソッドたちが、浅いコピーで`method`に代入され`for...of`のスコープ内だけバインドを使用するので、外側の`user.methods`には影響を及ぼしません。
逆に、`user.methods[0]`に再代入するならバインド済みのメソッドで上書きします。

## Partial functions
部分関数は、カリー化と似ていますが少し違う概念です。
カリー化は一部の固定引数を関数内スコープで保存し、別の内部関数に利用させるという、結合度が高い方法です。部分関数は結合度を下げるために、外側から`bind()`で固定引数を与えます。
```jsx
// Partial functions
function multiple(a, b) {
  return a * b;
}
let double = multiple.bind(null, 2);
console.log(double(3)); // 6
console.log(double(4)); // 8
```
```jsx
function getBindMultiple(base, multiplier) {
  return multiple.bind(null, base)(multiplier);
}
console.log(getBindMultiple(2, 3)); // 6
console.log(getBindMultiple(3, 4)); // 12
```

利用したいプロパティとメソッドが同じオブジェクトに存在するという前提で、`bind()`を使ったら前の例のような、いちいち固定したコンテキスト（`bindAll`）か、コンテキストを`null`することで固定引数を与え、ほかのプロパティを取り入れる部分関数にしかならない。
あるメソッドをベースにして、各々のコンテキストから引数を自由に組み合わせる部分関数を創るためには`call()`を使うほうがもっと適切です。
（`call`は強制的に結合するではなく参照先や引数を提供するのです。）
```jsx
// Going partial without context
let user = {
  firstName: 'John',
  say(time, phrase) {
    console.log(`[${time}] ${this.firstName}:${phrase}`);
  }
};
function partial(fn, ...argsBound) {
  return function (...args) {
    return fn.call(this, ...argsBound, ...args);
  }
}
// fn, argsBound(time)
user.sayNow = partial(user.say, `${new Date().getHours()}:${new Date().getMinutes()}`)

// args(phrase)
user.sayNow('Hello'); // [11:15] John: Hello
```
`call()`、`apply()`、`bind()`、使い方としてはとても似ているかもしれないが本質が違うと思います。
**`call()`と`apply()`は関数の正しい参照先を見つけるためのメソッド、
`bind()`は指定するコンテキストを強制的に結合するメソッドです。**

何か月前も`call()`と`apply()`、そして`bind()`についての勉強メモを書いてみたけど、今回の振り返りではやはりその時完全に理解していなかったと分かった。`this`のことを語りたかったので`this`関連メソッドからコンテキストがどう結合するのを理解しようとしたけれど、今思い返せば無理やり解釈していたかもしれません。

でも今回の振り返りを通して今の自分が前よりコードや書き手の意図を読めるようになって、そして解釈もできるようになりました。何か月も勉強メモを書き続けてよかったと思います。とてもいい勉強になりました。