JavaScript基礎知識の拾い集め

# 初めに
何か月もかけてやっと一通り基礎を勉強してきました。残りの断片的な知識は一つの文章にまとめたいと思います。

# Precedence & Associativity
優先度と結合性、[Operator precedence](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Operator_Precedence)。
（日本語版と英語版の順位は違う。ここでは英語版に準ずる）
**優先度：優先順位の大きいほうから実行する。
結合性：優先順位が同じである場合は実行の方向性を決める。**

```jsx
let a = 5 * 2 + 6 / 3 - 2
console.log(a) // 10
// Addition (+): 11, left-to-right
// Subtraction (-): 11, left-to-right
// Multiplication (*): 12, left-to-right
// Division (/): 12, left-to-right

```
人間である私たちにとって、乗算または除算してから加算や減算をするという物事の順序があるように、プログラムの世界もプロセスの優先順位が決められている。

下の例を見ていきたいと思います。
```jsx
let b = 1
let c = 2
let d = 3
b = c = d
console.log(b) // 3
console.log(c) // 3
console.log(d) // 3
// Assignment (=): 2, right-to-left
// d => c => b
```
アサインは右手（Right-hand side）の値を左手の変数に入れるということで、`b = c = d`もそのルールに沿って`d => c => b`でリアサインしていきます。結果としては変数`d`の値`3`になった。

`1 + '2'`のような数値と文字列の組み合わせが要注意です。
```jsx
let e = 1 + '2'
console.log(e) // 12 // string
e = 1 + '2' + 3
console.log(e) // 123 // string
e = '2' + 3
console.log(e) // 23 // string
```
（`+`で型変換が起こってしまうが、結合性の方向とは関係ありません。）
正しく計算してくれることはまずありません。おまけに型変換（coercion）で文字列になってしまう。実際に型変換の影響がこれだけではなく、時に深刻な問題を起こしてしまうので気を付けましょう。

# Default value by logical OR (||)
論理和、[Logical OR (||)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Logical_OR)
基本構文：
```jsx
value1 || value2
```
結合性がleft-to-right、`value1`が`true`に変換できるtruthy valueの場合は先に返す。これはShort-circuit（短絡評価）と言います。
```jsx
function sayHi(name) {
  name = name || 'Anonymous'
  console.log(`Hi, ${name}`)
}
sayHi('Taro') // Hi, Taro
sayHi() // Hi, Anonymous
```
この特性を利用してデフォルト値`'Anonymous'`を設置し、`name`がfalsy valueだったら自動的に代入します。
（下のようにデフォルト引数でも同じことできる。）
```jsx
function sayHi(name = 'Anonymous') {
  console.log(`Hi, ${name}`)
}
sayHi() // Hi, Anonymous
```
`||`は必ず値を返すので、`value1`がfalseに変換できるfalsy valueの場合は`value2`が何でも返します。
```jsx
let f = false || []
console.log(f) // [] // falsy value
```
`false`はもちろん、`[]`もfalseに変換できる。このとき`||`は`value2`がfalsy valueでも返します。

ほかの演算子と一緒に使うとどうなるでしょうか。
```jsx
f = true || false && false
console.log(f) // true
// Logical OR (||): 3, left-to-right
// Logical AND (&&): 4, left-to-right
// false && false => false, true || false => true
```
優先順で処理していきますね。

部分の順位を変えたいなら数学計算のように`()`を付けましょう。
```jsx
f = (true || false) && false
console.log(f) // false
// Grouping (()): 18, n/a
// (true || false) => (true), true && false => false => return value2
f = true && false // false => return value2
console.log(f) // false
```
`&&`は計算結果が`true`の場合は`value1`を返す。`false`の場合は`value2`を。
```jsx
let g = 1 && 0 // truthy && falsy => false => return value2
console.log(g) // 0
g = 0 && [] // falsy && falsy => true => return value1
console.log(g) // 0
```
どちらもtruthy valueの場合は集合（set）の概念で判断する。
```jsx
g = 1 && 2 // 1 && 2 => Disjoint sets(false) => return value2
console.log(g) // 2
g = [123] && 123 // Disjoint sets(false) => return value2
console.log(g) // 123 
g = 1 && '1' // Disjoint sets(false) => return value2
console.log(g) // '1'

g = (1 > 0) && (-1 < 0) // true && true => return value1
console.log(g) // true
```
[Logical AND (&&)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Logical_AND)
[Falsy](https://developer.mozilla.org/en-US/docs/Glossary/Falsy)
[Truthy](https://developer.mozilla.org/en-US/docs/Glossary/Truthy)

# Function: First class object
[Discover the power of first class functions](https://www.freecodecamp.org/news/discover-the-power-of-first-class-functions-fd0d7b599b69/#:~:text=In%20JavaScript%2C%20functions%20are%20first,returned%20from%20a%20function)
- **関数はオブジェクトの一種。**
- **関数を値として、変数、オブジェクトや配列に格納できる。**
```jsx
// first class
let firstClass = function () {
  console.log('firstClass')
}
firstClass() // firstClass

let functions = {
  sayHey: function (name) {
    console.log(`Hey, ${name}`)
  },
  sayBey: function (name) {
    console.log(`Bey , ${name}`)
  }
}
functions.sayHey('Taro') // Hey, Taro
functions.sayBey('Taro') // Bey, Taro
let item = 'sayHey'
functions[item]('Jiro') // Hey, Jiro

let arr = [functions, function () { console.log('fn in arr') }]
arr[0][item]('Saburo') // Hey, Saburo
arr[1]() // fn in arr
```
（値の保管場所によって呼び出す形式が少し違いますが。）
関数は`()`で実行するので`()`がないと関数自体が値として扱う。
`()`つけると実行することによって`return`した値を扱う、という意味になります。（`return`の値を指定しない場合は`undefined`になる。）


- **関数はほかの関数の引数として入れることができる。**
- **関数の返り値として、ほかの関数を返すことができる。**

```jsx
function add(a, b) {
  return a + b
}
function doSomething(fn, num1, num2) {
  return fn(num1, num2) // add(1, 2)
}
console.log(doSomething(add, 1, 2)) // 3
```

- **関数はオブジェクトのようにpropertyを有する。**

オブジェクトのプロパティを見つけるために`hasOwnProperty()`や`Object.getOwnPropertyNames()`を使いましょう。
```jsx
let obj = {
  myId: 1,
  add: add,
  objOwnMethod: function () {
    console.log('objOwnMethod')
  }
}
console.log(obj.hasOwnProperty('myId')) // true
console.log(obj.hasOwnProperty('add')) // true
console.log(obj.hasOwnProperty('objOwnMethod')) // true
console.log(Object.getOwnPropertyNames(obj)) // [ 'myId', 'add', 'objOwnMethod' ]
```
しかし関数のオブジェクトというのは、オブジェクトといくつの違いがあります。

```jsx
function demoForProperties(age) {
  this.age = age
  console.log(this)
}
console.log(demoForProperties.hasOwnProperty('age')) // false
console.log(Object.getOwnPropertyNames(demoForProperties))
// [ 'length', 'name', 'arguments', 'caller', 'prototype' ]
demoForProperties() // Object [global]
```
関数自体は何が入っても（入らなくても）必ず`[ 'length', 'name', 'arguments', 'caller', 'prototype' ]`などのプロパティを有している。

例のように`this`でプロパティ`age`を提示し`age`が生成しましたが、ここの`this`はグローバルなので、`age`はグローバルに所属するプロパティです。

```jsx
let demo = new demoForProperties(18)
console.log(demo.hasOwnProperty('age')) // true
console.log(Object.getOwnPropertyNames(demo)) // [ 'age' ]

console.log(demo) // demoForProperties { age: 18 }
```
関数をコンストラクタとしてインスタンスを創ると`this`はインスタンスに参照し、インスタンスが`age`プロパティが生成して所有する。（インスタンスはオブジェクト。）

**補足：Callback function**
```jsx
function print(fn) {
  fn()
}
print(function () {
  console.log('Hello!')
})
// Hello!
```
関数の中にほかの関数を入れて呼び出すのがコールバックって言います。

# about `this`
JavaScriptでは`this`は動的で環境やメソッドによって違う参照先になります。
下のように、オブジェクト`obj`に、三つのところで`this`をコンソールしてみたら、
```jsx
const obj = {
  name: 'It is obj',
  print: function () {
    this.name = 'It is print fn'
    console.log(this)

    function setNewName(newName) {
      this.name = newName
      console.log(this) // Object [global]
    }

    setNewName('It is newName fn')
    console.log(this)
  }
}

obj.print()
// { name: 'It is print fn', print: [Function: print] }
// Object [global] // create new property 'name' in global
// { name: 'It is print fn', print: [Function: print] }
```

`this`がImplicit bindingの状態では、自分のいる環境に所属しています。
`obj.print()`を呼ぶとき、一番目の`this`はImplicit bindingで`obj`に所属している。

二番目は`setNewName()`関数での`this`をコンソールした結果です。`setNewName()`の`this`は`print()`からスコープを継承してないため、globalに所属している。（実行環境によって名前が違う。Node.jsではglobal object、ブラウザではwindow objectです。）
なので`setNewName()`のプロパティ`name`は`obj`の`name`を上書きしたのではなく`global`に新しいプロパティ`name`を創り、値をアサインしました。

三番目の`this`は一番目と変わらず`print()`に所属するので`obj`をコンソールしました。

もし`setNewNane()`はアロー関数に変えたら、
```jsx
...
    const setNewName = (newName) => {
      this.name = newName
      console.log(this)
    }
...
obj.print()
// { name: 'It is print fn', print: [Function: print] }
// { name: 'It is newName fn', print: [Function: print] }
// { name: 'It is newName fn', print: [Function: print] }
```
アロー関数は自分のいる環境のスコープを継承するので、`this`の参照先が`obj`に統一され、プロパティ`name`の上書きがうまく行きました。

もう一つの方法があります。
```jsx
const obj = {
  name: 'It is obj',
  print: function () {
    const saveThis = this // *

    this.name = 'It is print fn'
    console.log(this)

    function setNewName(newName) {
      saveThis.name = newName  // *
      console.log(this) // Object [global]
    }

    setNewName('It is newName fn')
    console.log(this)
  }
}

obj.print()
// { name: 'It is print fn', print: [Function: print] }
// Object [global]
// { name: 'It is newName fn', print: [Function: print] }
```
`this`の値をほかの変数に格納したら、別の環境でもプロパティの操作をすることができます。
ほかにも`call()`や`apply()`でExplicit binding、`bind()`でHard bindingなどの`this`の参照を変えるバインドメソッドが利用できます。

# Object.prototype.toString()
[Object.prototype.toString()](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Object/toString)
型チェックするときによく`typeof`使いますが、`Object.prototype.toString()`ならより詳細な情報が返してくれます。
```jsx
let a = []
console.log(typeof a) // object
console.log(Object.prototype.toString.call(a)) // [object Array]

a = new Promise(resolve => { })
console.log(typeof a) //object
console.log(Object.prototype.toString.call(a)) // [object Promise]

a = undefined
console.log(typeof a) //undefined
console.log(Object.prototype.toString.call(a)) // [object Undefined]

a = null
console.log(typeof a) //object
console.log(Object.prototype.toString.call(a)) // [object Null]
```

# functional programming
これは自分がプログラミングの勉強をしてから触れた、一番核心になる概念と言っていいくらいです。

以下は配列の走査のデモコードです。
```jsx
let arr1 = [1, 2, 3]

function mapItem(arr, fn) {
  let newArr = []
  for (let i = 0; i < arr.length; i++) {
    newArr.push(fn(arr[i]))
  }
  return newArr
}

let arr2 = mapItem(arr1, function (item) {
  return item * 2
})
console.log(arr2) // [ 2, 4, 6 ]

let arr3 = mapItem(arr2, (item) => item > 2)
console.log(arr3) // [ false, true, true ]
```
`mapItem()`の引数が配列とコールバックで、コールバックの走査が終わったら新しい配列を返す。

コールバックの引数が一つならまたいいけど、複数以上になったら`mapItem()`を書き直すしかないかな？
```jsx
let checkLimiter = function (limiter, item) { // two parameters
  return item < limiter
}

// let arr4 = mapItem(arr1, checkLimiter)
// console.log(arr4) // [ false, false, false ]
// the second parameter 'item' is undefined
```
バインドメソッドを使ったら実行環境を変えてさきに引数を埋め込みたり、
```jsx
// by bind()
let arr4 = mapItem(arr1, checkLimiter.bind(this, 3))
console.log(arr4) // [ true, true, false ]

let checkLimiterSimplified = (limiter) => {

  return function (limiter, item) {
    return item < limiter
  }.bind(this, limiter)

}

let arr5 = mapItem(arr1, checkLimiterSimplified(3))
console.log(arr5) // [ true, true, false ]
```
あるいはバインドした関数をラッパーして値として返したりする。もしラッパー関数の引数が複数である場合は、外側で配列にして入れたら`...`で展開したりするのもできます。

この例では、ほかにクロージャを用いてもいいです。
```jsx
// by closure
let checkLimiterSimplified = (limiter) => {
  return function (item) {
    return item < limiter
  }
}

let arr6 = mapItem(arr1, checkLimiterSimplified(3))
console.log(arr6) // [ true, true, false ]
```
（引数が多くなったら`bind()`を使った方がいいかもしれません。）

# which is faster: for/for...of/forEach
[Which is faster: for, for…of, or forEach loops in JavaScript](https://levelup.gitconnected.com/which-is-faster-for-for-of-foreach-loops-in-javascript-18dbd9ffbca9)
最近パフォーマンスのこと調べててこの文章のテーマに興味を惹かれて、**Chrome DevTools**で`for`ループ系列のほかに、`map()`、`filter()`、`reduce()`もテストしてみました。

```jsx
const arr = [...Array(5000).keys()];

// for
console.time('for');
for (let i = 0; i < arr.length; i++) {
}
console.timeEnd('for');

// for...of
console.time('for...of');
for (let value of arr) {
}
console.timeEnd('for...of');

// forEach loop
console.time('forEach');
arr.forEach((value) => {
});
console.timeEnd('forEach');

// for: 0.212ms
// for...of: 0.178ms
// forEach: 0.078ms

// forEach > for...of > for
```
結論から言うと、参考文章とほぼ同じ結果なんですが。
驚いたのはES6の高階関数たちの結果↓です。
```jsx
// map
console.time('map');
arr.map((x) => x);
console.timeEnd('map');

// filter
console.time('filter');
arr.filter((x) => x);
console.timeEnd('filter');

// reduce
console.time('reduce');
arr.reduce((x, y) => x + y);
console.timeEnd('reduce');

// map: 0.154ms
// filter: 0.141ms
// reduce: 0.101ms
```
どれも早かった...！
ちなみにそれぞれ`for`や`forEach`と比べたら、
```jsx
const arr = [...Array(5000).keys()];
// map
console.time('map');
arr.map((x) => String(x));
console.timeEnd('map');

console.time('map vs. for');
let newArr1 = [];
for (let i = 0; i < arr.length; i++) {
  newArr1.push(String(arr[i]));
}
console.timeEnd('map vs. for');

console.time('map vs. forEach');
arr.forEach((x) => String(x));
console.timeEnd('map vs. forEach');

// map: 0.175048828125 ms
// map vs. for: 0.406982421875 ms
// map vs. forEach: 0.137939453125 ms
```
```jsx
// filter
console.time('filter');
arr.filter((x) => (x >= 0));
console.timeEnd('filter');

console.time('filter vs. for');
let newArr2 = [];
for (let i = 0; i < arr.length; i++) {
  if (arr[i] >= 0) {
    newArr2.push(arr[i]);
  }
}
console.timeEnd('filter vs. for');

console.time('filter vs. forEach');
arr.forEach((x) => (filter >= 0));
console.timeEnd('map vs. forEach');

// filter: 0.1337890625 ms
// filter vs. for: 0.42578125 ms
// filter vs. forEach: 0.08984375 ms
```
```jsx
// reduce
let total = 0;
console.time('reduce');
arr.reduce((x, y) => (x + y));
console.timeEnd('reduce');

console.time('reduce vs. for');
for (let i = 0; i < arr.length; i++) {
  total += arr[i];
}
console.timeEnd('reduce vs. for');

console.time('reduce vs. forEach');
arr.forEach((x) => (total += x));
console.timeEnd('reduce vs. forEach');

// reduce: 0.0908203125 ms
// reduce vs. for: 0.21484375 ms
// reduce vs. forEach: 0.158935546875 ms
```
`forEach`のほうが早かったんですが。けれど高階関数のパフォーマンスも優れていると思います。

（`for...of`は副作用や予想外のエラーになりやすいのでテストから外しました。）

# for...in vs. for...of
[for...in](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Statements/for...in)
[for...of](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Statements/for...of)
`for...in`：列挙可能なプロパティ。（オブジェクト、配列）
`for...of`：反復処理可能なオブジェクト。（配列、配列風オブジェクト（e.g.Nodelist）、文字列、ジェネレータ...）

```jsx
// for...in
let obj = {
  a: 1,
  b: 2,
  c: 3,
};
let arr = [4, 5, 6];

for (let key in obj) {
  console.log(key);
};
// a
// b
// c
for (let index in arr) {
  console.log(index);
};
// 0
// 1
// 2
```
```jsx
// for...of
for (let value of arr) {
  console.log(value);
};
// 4
// 5
// 6

for (let value of obj) {
  console.log(value);
};
// TypeError: obj is not iterable

// iterable object
let obj1 = {
  from: 1,
  to: 5,
  *[Symbol.iterator]() {
    for (let i = this.from; i <= this.to; i++) {
      yield i;
    };
  },
};

for (let value of obj1) {
  console.log(value);
};
// 1
// 2
// 3 ...
```
`for...in`はプロパティを呼び出す、`for...of`は反復できるオブジェクトの値を呼び出す。
使い分けもとても便利そうに見えますが、調べればほとんどこの二つのメソッドをおすすめしないのはなぜでしょうか。
```jsx
let arr = [];
arr[2] = 2;
for (let prop in arr) {
  console.log(prop)
};
// 2
for (let value of arr) {
  console.log(value)
};
// undefined
// undefined
// 2
```
`for...in`はempty slotだからプロパティ（インデックス）が見つけられない、`for...of`はempty slotなのに`undefined`として出力。
[empty solt](https://qiita.com/hu-yu/items/8bfc4a528b856e782986#empty-slot)
```jsx
Array.prototype.foo = 1;
let arr = [1, 2];
for (let prop in arr) {
  console.log(prop)
};
// 0
// 1
// foo

let obj = {
  a: 1,
  b: 2,
  c: 3,
};
Object.prototype.newProp = 'I am new property.';
for (let prop in arr) {
  console.log(prop)
};
// 0
// 1
// foo
// newProp

function getName(name) {
  this.name = name;
}
let test = new getName('Mick');
for (let prop in test) {
  console.log(prop)
}
// name
// newProp
```
（組み込み関数の`.prototype`を弄ることおすすめしません。）
上のようにプロトタイプチェーンに新しいプロパティ作ると、`for...in`が所有していないプロパティも出力してしまいました。

オブジェクトならもちろん`Object.prototype.hasOwnProperty()`メソッドで所有プロパティのチェックができますが、配列に対してはどうしようもないです。
```jsx
for (let prop in test) {
  if (Object.prototype.hasOwnProperty.call(test, prop)) {
    console.log(prop);
  };
};
// name
```

https://stackoverflow.com/questions/500504/why-is-using-for-in-for-array-iteration-a-bad-idea


# new.target
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/new.target

https://javascript.info/constructor-new#constructor-mode-test-new-target

関数やクラスが`new`で呼び出されたかを検出するメソッドです。
```jsx
function Person(name) {
  if (!new.target) {
    return new Person(name)
  }

  this.name = name;
}
let mick = Person('Mick');
console.log(mick); // Person { name: 'Mick' }
```
# Singleton
関数やクラスのインスタンスが一つしかないという意味です。
この用語に対しては認識はまだ浅いが、巨大なるインスタンスが一つしか存在しない、またはインスタンスのすべてが同じオブジェクトに参照するという。

```jsx
const Singleton = function () {
  // when we create instance return the value
  if (typeof Singleton.cache === 'object') return Singleton.cache;

  // set property 'cache' in first invocation
  this.p = 'public';
  Singleton.cache = this;
};

let obj1 = new Singleton();
let obj2 = new Singleton();
console.log(obj1 === obj2); // true

Singleton.cache = 'Reference changed';
console.log(Singleton.cache);
```
これでインスタンスが同じオブジェクトに参照しますが。`cache`は外部のアクセスによりいつでも上書きされることができるので、`cache`を隠す必要があります。

```jsx
const Singleton = (function () {
  let cache;
  return function () {
    if (typeof cache === 'object') return cache;

    this.p = 'public';
    cache = this;
  };
}())
let obj1 = new Singleton();
let obj2 = new Singleton();
console.log(obj1 === obj2); // true
console.log(Singleton.cache); // undefined
```
クロージャの特性で関数の内部関数にアクセスできない、`IIFE`の即時実行で静的スコープで包んで、グローバルスコープから同じ変数へのアクセスを防ぐ。
```jsx
class Singleton {
  constructor() {
    // ...
  }

  method() {
    // ...
  }
}

let objSingleton = new Singleton();
Object.freeze(objSingleton);

export default objSingleton;
```
ちょっと違うやり方でモジュールからクラスを直接利用するのではなく、`Object.freeze()`で凍結されたインスタンスを使用します。

# optional chaining `?.`

https://javascript.info/optional-chaining

ネストされたオブジェクトにアクセスするとき、存在しないプロパティに遭遇してしまってもエラーをなげずにコードを進める安全な方法です。
```jsx
let user = {};
console.log(user.address.street);
// TypeError: Cannot read properties of undefined (reading 'street')
```
存在しないプロパティにアクセスしてしまったら普通はエラーが出てそのあとの実行が中断されてしまいます。
```jsx
let user = {};
console.log(user.address ? user.address.street : undefined);
// undefined
console.log(user.address && user.address.street && user.address.name);
// undefined
```
条件演算子`?`か論理結合`&&`で解決できるが、ネストの深さに連れてコードも長くなってしまいます。
```jsx
console.log(user?.address?.street);
// undefined
```
オプショナルチェイニング`?.`なら簡潔に済みます。
（`?.`の前の変数は定義されてなければなりません。）
```jsx
let userAdmin = {
  admin() {
    console.log('Admin');
  }
};
let userGuest = {};

userAdmin.admin?.(); // Admin
userGuest.admin?.(); // nothing happens
```
関数の存在をチェックして実行するのもできます。
（以下はほかの使い方。）
```jsx
let key = 'firstName';

let user1 = {
  firstName: 'Mick',
};
let user2 = null;

console.log(user1?.[key]); // Mick
console.log(user2?.[key]); // undefined

// if user1 exists, delete property 'firstName'
delete user1?.firstName;
console.log(user1) // {}
```

まずはここまで、書きたいものがあればまた更新します！