JavaScriptの概念


## はじめに
https://qiita.com/tsin1rou/items/90576b6c00b895478610

Twitterでこんな記事を見かけました。知らなくても形にして動かすことはできますが、裏での処理がわからなくてうまく説明できなかったり、何より当然抑えておくべきだと思いましたので書くことにしました。

内容は参考元と同じなので、体に染みたら削除しようと思います。

参考元の著者： Twitter: [@tsin1rou](https://twitter.com/tsin1rou)　Qiita: [@tsin1rou](https://qiita.com/tsin1rou)

## 0. 関数の宣言
```js
const bar = (arg) => {
  return arg * 2;
};
``` 
まずは関数の宣言です。returnの行しかない関数は省略して`(引数)=>(返り値)`と書けます。
```js
const bar = (arg) => (arg * 2);
const bar = arg => arg * 2; // 更に()の省略も可
``` 
よく見かける謎の記法です。
```js
const bar = x => x + y;
const foo = x => (y => x + y); // 上記と同じ
const piyo = foo(4); // piyo = y => 4 + y;

console.log(piyo(3)); // -> 7
console.log(foo(4)(3)); // -> 7
```

## 1. コールスタック
JavaScriptでは関数を呼び出すとコールスタックと呼ばれる部分に呼び出された関数が積み上がっていきます。常に一番上の関数が実行されており、関数の実行が終わって値を返すとスタックから取り除かれ、上から２番目に載っていた関数へと戻っていきます。

```js
    const bar = x => x+6;
    const foo = x => bar(x * 2) * 3;
->  console.log(foo(5));
```
上記は例です。上から順に実行され、`console.log(foo(5));`の行まで来ました。

| コールスタック | 
|:-----------|
| console.log(foo(5))|
| main()    | 

常にコールスタックの一番上を実行します。今回は`foo(5)`という関数を実行する命令が再び登場するので更に積み上げます。

| コールスタック | 
|:-----------|
| foo(5)   | 
| console.log(foo(5))|
| main()    | 

`foo`を実行すると今度は`bar`が登場しているので積み上げます。
```js
    const bar = x => x+6;
->  const foo = x => bar(x * 2) * 3;
    console.log(foo(5));
```

| コールスタック | 
|:-----------|
| bar(10)   | 
| foo(5)   | 
| console.log(foo(5))|
| main()    | 

一番上にある`bar(10)`を実行します。
```js
->  const bar = x => x+6;
    const foo = x => bar(x * 2) * 3;
    console.log(foo(5));
```
`16`という返り値を得られたので`bar(10)`をコールスタックから取り除きます。

| コールスタック | 
|:-----------|
| foo(5)   | 
| console.log(foo(5))|
| main()    | 


一番上にある`foo(5)`の処理に戻ります。`bar(10)`=`16`ということがわかったので、`48`という返り値が求まります。そしてこの`foo(5)`をコールスタックから取り除き、一番上に現れた`console.log(foo(5))`を実行して、コンソールに`48`が表示される流れです。その後は`main()`に従って次の処理へと進んでいきます。


## 2. プリミティブ型
基本的なデータ型です。全部で6種類あります。

**`boolean`**:　true, false
**`string`**:　"abcde"文字列
**`number`**:　普通の数字。100

**`null`** **`undefined`**:　nullは「無い」状態で、undefinedは「何も定義されていない」状態を表します。

**`symbol`**:　ES2015で導入された新しい型。ユニークなIDが生成出来ます。


## 3. 値型、参照型
先程のプリミティブ型は全て値型です。値型は **「変数をコピーした時に中身もコピーされる」** ということです。

```js:値型
let x = 20;
let y = 'abc';

const a = x;
const b = y;

x = 7;
y = 'def';

console.log(x, y, a, b); // -> 7 "def" 20 "abc"
```

| 変数 | 値 |
|:---:|:---:|
| x   | 7     |
| y   | "def" |
| a   | 20    |
| b   | "abc" | 


### 参照型
一方、オブジェクトや配列などは参照型です。
```js:参照型
const a = [0, 1, 2];
```
`a`にはアドレスがセットされます。
| 変数 | 値 |
|:---:|:---:|
| a   | <#001> |

| アドレス | データ |
|:---:|:---:|
| #001   | [0,1,2] |

ここで変数をコピーしてみると...

```js:参照型
const b = a;
```
**参照だけがコピーされます。**
| 変数 | 値 |
|:---:|:---:|
| a   | <#001> |
| b   | <#001> |

| アドレス | データ |
|:---:|:---:|
| #001   | [0,1,2] |


つまり、**`a`の中身を変えると`b`の中身まで変わります。**

## 4.型変換
JavaScriptはプリミティブ型に併せて
1. Stringへの変換
2. Booleanへの変換
3. Numberへの変換

の3種類が存在します。

### Stringへの変換
```js
String(123)           // '123'
String(-12.3)         // '-12.3'
String(null)          // 'null'
String(undefined)     // 'undefined'
String(true)          // 'true'
String(false)         // 'false'

// 暗黙の型変換。　+の片方がStringならもう一方もStringに変換される
String(''+0+1)        // '01'
String(0+''+1)        // '01'
String(0+1+'')        // '1' <- 0+1を先に計算
```

### Boolean
`''` `0` `-0` `NaN` `null` `undefind` `false` のみが`false` に変換されます。他はすべて`true`です。
```js
Boolean('')           // false
Boolean(0)            // false     
Boolean(-0)           // false
Boolean(NaN)          // false
Boolean(null)         // false
Boolean(undefined)    // false
Boolean(false)        // false

Boolean({})           // true
Boolean([])           // true
```

`||` `&&` `!` 3つの倫理演算子は暗黙のBoolean型変換を行います。
※ `||` `&&` は型変換を内部でのみ行い、返り値は元の値・型がそのまま返ってきます。
```js
// player.nameが設定されていなければ'NoName'
const name = player.name || 'NoName';

// if文で書くとこうなる
let name;
if(player.name){
  name = player.name;
} else {
  name = 'NoName';
}
```

### Number
```js
Number(null)           // 0
Number(undefined)      // NaN
Number(true)           // 1
Number(false)          // 0
Number(" 12 ")         // 12
Number("-12.34")       // -12.34
Number("\n")           // 0
Number(" 12s ")        // NaN
Number(123)            // 123
```
以下の演算子はNumberへの暗黙の型変換を行います。

- 比較演算子 `<` `>` `<=` `=>`
- ビット演算子 `|` `$` `^` `~`
- 算術演算子 `-` `+` `*` `/` `%`　※`+`の片方がStringならString。
- 比較演算子 `==` `!`　※両方とも、比較対象がStringならString。

また、==はnullとundefinedの型変換を行わないという特別ルールがあります。
```js
null == 0  // false
```

## 5. ==と===の違い
結論から言うと可能な限り`===`推奨とのことです。
`==`は暗黙の型変換を行うので意図せぬ挙動の原因となるからです。
```js
false == 0              // true
false === 0             // false
0 == ""                 // true
0 === ""                // false
```

`null`と`undefined`は`==`においてそれぞれにのみ一致します。
```js
null == undefined       // true
null === undefined      // false
```
`NaN`は自分自身を含めて何とも一致しないというルールがあります。`NaN`であるかどうか確認したい場合は`Number.isNaN()`を使用します。
```js
const a = NaN;
console.log(a === a);     // false
console.log(a === NaN);   // false
console.log(Number.isNaN(a));  // true
```

### typeof
どの型なのか調べる場合に使用します。
```js
typeof 6                 // "number"
typeof 'ker'             // "string"
typeof {}                // "object"
typeof true              // "boolean"
typeof undefined         // "undefined"
typeof (()=>{})          // "function"
```

## 6. スコープ
ifでもforでも関数でも、{}で囲まれてさえいればスコープが発生すると考えればOKのようです。
```js
const bar = 'foobar';
{
  console.log(bar); // Uncaught ReferenceError: bar is not defined
  const bar = 'foobar';
  console.log(bar); // -> "foobar"
}
console.log(bar); // -> "foobar"
```
JavaScriptでも基本的には上のスコープを見にいくのですが、**同名の変数が同じスコープで定義されていると代わりにエラーが出ます**。
```js
const bar = 'foobar';
{
  console.log(bar); // -> "foobar"
}

{
  console.log(bar); // ReferenceError
  const bar = 'foobar';
}
```
これは **「宣言の巻き上げ」** というJavaScriptの仕様で、全ての変数はスコープの一番上で宣言されたのと同じ扱いになります。ただし、明示的に宣言した場合にはエラーが出ません。
```js
{
  let bar;
  console.log(bar); // -> undefined
  bar = 'foobar';
}

{
  console.log(bar); // ReferenceError
  let bar = 'foobar';
}
```
## 7. 文Statementと式Expression
JavaScriptのソースコードは**Statement**と**Expression**という2つの構文から成り立っています。

StatementはJavaScriptにおける文の単位のことで、例えばif文だとif (式) 別の文という形ですね。**基本的に「戻り値」がないものがStatement**です。**Expressionは評価できて値が返る**ものです。

```js:Statementの例
if (a > b) {
  console.log("a is greater than b");
}
```
```js:Expressionの例
const sum = a + b;
```

- `const` `let`
- `if(else)` `switch`
- `for` `(do)while` `break` `continue`
- `throw` `try` `catch(finally)`

StatementとStatementの間には`;`セミコロンを使います。ただし、`{}`で囲まれたブロック文でStatementが終わる際には`;`を付けてはいけません。これが`if(){}`とか`while(){}`の後ろに`;`が付いていない理由です。

逆に`if()bar();`みたいな`{}`を使わないifの後ろには通常通り`;`が必要です。

## 8. ブラウザで使用できるタイマー
setTimeoutとsetIntervalは共にブラウザで使用できるタイマーです。

- `setTimeout(callback, delay)`は delayミリ秒以降にcallbackが実行されます。
- `setInterval(callback, interval)`は intervalミリ秒ごとにcallbackが実行されます。

### setTimeout, setInterval
```js:index.js
console.log('startTime:', new Date());

setTimeout(()=>{
  console.log('timeout:', new Date());
}, 1000);
const timeout = setInterval(()=>{
  console.log('interval:', new Date());
}, 1000);

setTimeout(()=>{
  clearInterval(timeout);
}, 5000);
```
callbackに引数を渡したい時は、
`setTimeout(callback, delay, 引数1, 引数2, ...)`
or
`setTimeout(()=>{callback(引数1, 引数2, ...)}, delay)`

### requestAnimationFrame
JavaScriptでアニメーションを作る際に使用します。
`requestAnimationFrame(callback)`とすると次の描画の直前にcallbackが実行されます。
これを再帰させて下のコードの様にすると描画ごとに実行される関数の完成です。
```js
const animation = ()=>{
  requestAnimationFrame(animation);
  // 描画処理
};
requestAnimationFrame(animation);
```

## 9. DOMとレイアウトツリー
DOM(ドキュメント・オブジェクト・モデル)。要素のツリー。
```
DOCUMENT (always the root)
 └─HTML
   ├─HEAD
   │  └─...
   └─BODY
      ├─DIV
      │  └─...
      ├─SCRIPT
      └─SCRIPT
```
ブラウザはhtml文章をパースしてDOMを生成します。画面に描画されているのはDOMなので、これを適当に編集すれば画面の表示も変わります。ReactやVueの使用が多いので、直接編集することはあまりないと思います。

### 要素を取得する
よく使用されるのは`getElementById()`や`getElementsByClassName()`、`querySelectorAll()`などです。
```js
// idが"bar"の要素を取得
const element1 = document.getElementById('bar');

// <... class="foo bar baz">のようにクラスに含まれていればまとめて取得。返り値はリスト。
const element2 = document.getElementsByClassName('foo');

// cssのセレクター形式で要素を取得。返り値はリスト。
const element3 = document.querySelectorAll('#bar'); // id="bar"の要素を取得
const element4 = document.querySelectorAll('div.highlighted > p'); // p要素のうち直接の親が'highlighted'クラスを持つものを取得
```

### 要素の編集
```js
const element = document.getElementById('...');

// 要素を作成
const foobar = document.createElement('div');

// 要素を追加
element.appendChild(foobar);

// 要素を削除
element.removeChild(foobar);

// 子要素を全削除
while(element.firstChild){
  element.removeChild(element.firstChild);
}

// クラスを取得
const classList = element.classList;
if(classList.contains('foobar'){
  // 要素にfoobarクラスが設定されていれば走る
}

// クラスを追加・削除・切り替え
classList.add('foobar');
classList.remove('foobar');
classList.toggle('foobar');
```

### イベント関連
`addEventListener()`と`removeEventListener()`を使います。
`click`イベントの例です。
```js
element.addEventListener('click', (event)=>{
  // preventDefault()を呼ぶと通常の動作が行われなくなる。
  // 例えばaタグをクリックしても移動しなくなる。
  event.preventDefault();
  console.log('要素がクリックされました');
});

// removeの方は両方の引数を同じにする必要がある
const clickHandler = (event)=>{...};
element.addEventListener('click', clickHandler);
element.removeEventListener('click', clickHandler);
```

## 1０. thisとcallとapplyとbind
`function`と`()=>{}`で何が`this`になるかが異なります。

#### functionにおいてのthis
`this`は呼び出し方に応じて変化します。
呼び出されるまで何が`this`になるか分かりません。
```js
function bar(){
  console.log(this);
}

bar();         // -> global

const obj = {
  bar
};
obj.bar();     // -> obj

const obj2 = {
  foo: function(){
    bar();
  }
};
obj2.foo();    // -> global
```

#### アロー関数においてのthis
コードを見れば何が`this`になるかが分かります **(レキシカルなthis)**
アロー関数はどこからどうやって呼び出しても`this`は常に同じものをさします。
```js
const bar = () => {
  //この段階でthisがglobalなのでどこで呼び出してもglobalになる
  console.log(this);
};

bar();         // -> global

const obj = {
  bar
};
obj.bar();     // -> global

const obj2 = {
  foo: function(){
    bar();
  }
};
obj2.foo();    // -> global
```

### callとapply
`call`も`apply`も呼び出す際に何が`this`になるのかを明示的に決定するための関数です。共に**第一引数に`this`にしたいものを入れます**。

`call`は第二引数以降に直接引数にしたいものを入れていきますが、`apply`は引数をまとめて配列にしたものを第二引数にするという違いがあります。ただし共にアロー関数の`this`を変えることはできません。

```js
const bar = (arg) => {
  console.log(arg);
  console.log(this);
};
function foo(arg){
  console.log(arg);
  console.log(this);
}

const obj = {};
const args = ['arg1', 'arg2'];
bar.call(obj, args);
// -> ["arg1", "arg2"]
// -> global

foo.call(obj, args);
// -> ["arg1", "arg2"]
// -> Object {}

bar.apply(obj, args);
// -> "arg1"  (配列が展開されて渡される)
// -> global

foo.apply(obj, args);
// -> "arg1"
// -> Object {}
```

### bind
同じように何を`this`にするかを決められます。一度決めれば永続的に効果を発揮しますが、アロー関数には使えません。
```js
const bar = () => {
  console.log(this);
};
function foo(){
  console.log(this);
}

const obj = {};

// bindは新しい関数を返す
const bar2 = bar.bind(obj);
const foo2 = foo.bind(obj);

bar();     // -> global (元の関数を変化させない)
foo();     // -> global
bar2();    // -> global (アロー関数には効かない)
foo2();    // -> Object {}
```

## 1１. newとインスタンスについて
`new`はインスタンスを作成するのに使用します。
```js
class Bar {
  constructor(args){
    console.log(this);
    const { name } = args || {};
    this.name = name || 'NoName';
  }
}
const Foo1 = (args) => {
  console.log(this);
  const { name } = args || {};
  this.name = name || 'NoName';
}
function Foo2(args) {
  console.log(this);
  const { name } = args || {};
  this.name = name || 'NoName';
}
                              // thisの中身↓
const bar1 = Bar();           // -> TypeError: Class constructor Bar cannot be invoked without 'new'
const bar2 = new Bar();       // -> Object { name:"NoName" }
const foo1_1 = Foo1();        // -> global
const foo1_2 = new Foo1();    // -> TypeError: Foo1 is not a constructor
const foo2_1 = Foo2();        // -> global
const foo2_2 = new Foo2();    // -> Object { name:"NoName" }
```
`new`と`class`は常にセットで使用し、それ以外の場合は使用しないという取り決めが良いようです。

### instanceof
インスタンスがどんなクラスを継承してきたのかを確かめるには`instanceof`を使用します。たくさん継承を重ねていても先祖のどこかに存在すれば`true`になります。
```js
class bar {
  constructor(args){
    ...
  }
}
class Foo extends Bar {
  constructor(args){
    super(args);
    ...
  }
}
const bar = new Bar();
const foo = new Foo();

console.log(bar instanceof Bar);  // -> true
console.log(bar instanceof Foo);  // -> false
console.log(foo instanceof Bar);  // -> true
console.log(foo instanceof Foo);  // -> true
```