SALESFORCE 認定 JAVASCRIPT デベロッパー 試験の勉強内容まとめ

SALESFORCE 認定 JAVASCRIPT デベロッパー試験について自分の勉強したことをアウトプットを兼ねてまとめます。

##試験の感想
試験には無事合格しました。
受けてきた感想としては静的メソッドなどはそこまで出ませんでした。
覚えてる範囲だとconcatとapplyの絡め技とsplice,padStartくらいでmap,filterなんかも出てこず拍子抜けです。
どちらかというとClass定義を用いた問題が多めに出てた印象で、オブジェクト作成時の動作や
プロトタイプの理解などをしっかりしておけばよいかなと思います。

個人的には非同期処理とNode.jsが結構きつかったです。
非同期処理はPromiseとsetTimeout使ったときの処理の順番とかあやふやで悩みました。
また、Node.jsは範囲が広すぎて捨ててたのですが、https.createServerやserver.onでのエラー処理、コアモジュールはどれか？、次のパッチバージョンは何になるかなどが出ました。
一通りNode.jsでサーバーを立てたことがあったので何とかなりましたが触ったことない人は厳しいと思うので
勉強しておくといいと思います。

あとは使い方覚えてるよっていうメソッドほどMDNのサイトで注意事項とか見直しておくとよいと思います。
記憶に残るなんじゃこりゃと思った問題は以下の3つ。
問題)
1) `JSON.parse('"foo"')`;と`JSON.parse("'foo'");`のどっちが動作するか(ほかにも選択肢ありました)
2) `JSON.stringify([new String("false"), new Boolean("false"), undefined])`の出力結果は？
3) 以下のコードを実行したときのログの順番は？

```Javascript
setTimeout(() => {console.log("log1");},0);
console.log("log2");
Promise.resolve(1)
.then(() => {console.log("log3");})
.finally(() => {console.log("log4");});
console.log("log5");
```

答え)
1) `JSON.parse('"foo"');` --> JSON.parse()はシングルコーテーションを許容しないため
2) `"["false",false,null]"` --> undefinedはnullに変換されるため
3) 2 > 5 > 3 > 4 > 1の順

上記の回答を調べるためにMDNの該当ページに行ったら似たような実行例が載ってました。
ここを見て理解するとよさそうですね。

これ以降は試験のちょっとした内容と勉強した内容を記載していきます。
個人的な理解を基に記載しているのであまり鵜呑みにせず自分でも調べてください。
ざーっと書いているので誤字脱字・これは違うとかあってもご容赦頂ければ・・・

##試験情報
公式ガイド：https://tandc.salesforce.com/examguide_cert_javascript_developer.pdf

以下、公式ガイドから抜粋していきます

> 内容: 多肢選択/複数選択方式の 60 問
> 試験の所要時間: 105 分
> 合格点: 65%
> 試験範囲
> ・変数、データ型、コレクション: 23%
> ・オブジェクト、関数、クラス: 25%
> ・ブラウザとイベント: 17%
> ・デバッグとエラーハンドリング: 7%
> ・非同期プログラミング: 13%
> ・サーバーサイド JAVASCRIPT: 8%
> ・テスト: 7%

60問の65%なので39問以上正解が必要ですね。


##文法いろいろ

JavascriptはUnicode 文字セットを使用している

```Javascript:宣言
let Früh = "foobar"
```

文が単独の行で書かれている場合、文の後にセミコロンは必要ない。
しかし、行の中に複数の文が必要な場合は、セミコロンで区切る必要がある。

* var
    * 関数スコープまたはグローバルスコープ.
    * 巻き上げといって現れる場所に関係なくコード実行前に処理される
    * 値は、実際には代入文に到達したときに代入され、初期値はundefined
* let
    * ブロックスコープのローカル変数を宣言
    * let宣言前に変数を使用するとReferenceErrorとなる
* const
    * letと同じくブロックスコープ
    * 初期化が必要
    * オブジェクトや配列の上書きはエラーになるがプロパティや要素の変更はエラーにならない
    * Object.freeze()を使えばプロパティの変更も阻止できる

変数はアンダースコア、ドル記号、文字から始まる必要がある

###分割代入
オブジェクトや配列は各変数に分割して代入することができる

```Javascript:分割代入
const foo = {bar:10,baz:12};
let {bar} = foo;
console.log(bar); //10

let a, b;
({a, b} = {a: 1, b: 2});
//ここはかっこでくくらないとブロックスコープとして解釈されてしまう

const o = {p: 42, q: true};
const {p: foo, q: bar} = o;
 
console.log(foo); // 42 
console.log(bar); // true

const x = [1, 2, 3, 4, 5];
let [a,b,c,d,e] = x;
console.log(a); //1
console.log(b); //2
console.log(c); //3

//配列から取り出した値が undefined だった場合に使用される既定値を指定できる
let a, b;
[a=5, b=7] = [1];
console.log(a); // 1
console.log(b); // 7
```

##データ型
Javascriptの型は8種類
※DateはObjectに分類される

+ String
+ Number
+ BigInt
+ Boolean
+ null
+ undefined
+ object
+ Symbol

###String

文字列の検索方法
indexOf,search,includesなどを使う
searchは正規表現を使用する

**str.indexOf(searchValue[, fromIndex])**
searchValueがstrに存在するか検索し、最初にヒットしたインデックスを返す。
存在しない場合-1を返す。

```JavaScript:indexOf
const phrase = "cyan magenta yellow";
console.log(phrase.indexOf("magenta")); // 5
console.log(phrase.indexOf("blue")); // -1
```

**str.search(regexp)**
正規表現regexpとマッチする文字列があるか検索し、最初にヒットしたインデックスを返す。
存在しない場合-1を返す。

```JavaScript:search
const phrase = "cyan magenta yellow";
console.log(phrase.search(/m/)); // 5
console.log(phrase.search(/\*z/)); // -1
```

**str.includes(searchString[, position])**
strにsearchStringが含まれているか検索し、含まれている場合はtrueを返す。
含まれていない場合はfalseを返す。

```JavaScript:includes
const phrase = "cyan magenta yellow";
console.log(phrase.includes("cyan")); // true
console.log(phrase.includes("blue")); // false
```

**str.split([separator[, limit]])**
strをseparatorで指定した区切り文字列で分割し、分割した結果を配列として返す。
separatorには文字列のほか正規表現も指定できる。

```JavaScript:split
const phrase = "cyan magenta yellow";
console.log(phrase.split(" ")); // ["cyan", "magenta", "yellow"]
console.log(phrase.split(" ", 1)); //  ["cyan"]
```

パディングには以下のメソッドを使う
**str.padStart(targetLength [, padString])**
**str.padEnd(targetLength [, padString])**

padStartはtargetLengthの文字列サイズになるまで左からpadStringを詰めていく。
padEndはtargetLengthの文字列サイズになるまで右からpadStringを詰めていく。
padStringを指定しなかった場合" "が詰められる。

```JavaScript:padding
const str = "01234";
console.log(str.padStart(10,"*")); // *****01234
console.log(str.padEnd(10,"*")); // 01234*****
```
<br/>
substring,slice,substrで文字の切り抜きができる
ただし、substrは非推奨。
**str.substring(indexStart[, indexEnd])**
indexStartがindexEndより大きい場合、二つの変数は交換されて扱われる
負の数やNaNの値を指定すると0として扱う

**str.slice(beginIndex[, endIndex])**
基本はsubstringと一緒
sliceの場合負の数を使用できる

**str.substr(start[, length])**
substrの場合、二つ目のオプションは文字数を表すことに注意(インデックスではない)

```JavaScript:substring
const phrase = "cyan magenta yellow";
console.log(phrase.substring(5,12)); // magenta
console.log(phrase.slice(-14,12)); // magenta
console.log(phrase.slice(5,7)); // magenta
```
###Number
倍精度64ビット浮動小数点形式の数値データ型
+Infinity, -Infinity, NaNの値もあわせて持つ
+Infinity, -Infinityはtruthyな値、NaNはfalsyな値

```Javascript:Number
console.log(Number.MAX_SAFE_INTEGER); // 2^53-1
console.log(Number.MIN_SAFE_INTEGER); // -2^53-1

//上記とは別に最大値としてはNumber.MAX_VALUE,Number.MIN_VALUEが存在する
```

###BigInt
任意の精度で整数を表現できる JavaScript の数値プリミティブ。
BigIntは、整数の末尾にnを追加するか、コンストラクターを呼び出すことで作成する。
BigIntはNumberと一緒に使用できない。

```Javascript:Number
> const x = 2n ** 53n;
9007199254740992n
> const y = x + 1n;
9007199254740993n
```
###null
typeof null はobjectが返る。
falsyな値。

###undefined
宣言のみが行われた変数の値。
undefinedは数値のコンテキストで使用されるとNaNとなる(nullは0として評価される)。
falsyな値。

###Symbol
Symbolは文字列に変換されない,Sym.toString()を使う


プリミティブな型ではないがそのほかのデータ型としてDateとJSONについて以下に記載する。

###Date
協定世界時(UTC)の1970年1月1日からの経過ミリ秒数を持つ**オブジェクト**。


初期化・宣言方法は以下の4種類
なお、生成したDateオブジェクトは実行した環境のタイムゾーンとして扱われる。
**new Date()**
現在の時刻のオブジェクトを返す。
※`let d = Date()`だと現在の時刻を文字列形式で返す。

**new Date(value)**
valueにミリ秒を渡すとその時刻のオブジェクトを返す。

**new Date(dateString)**
dateStringにISO8601や拡張ISO8601で定められたフォーマットの時刻を表す文字列を渡すとその時刻のオブジェクトを返す。
ISO8601形式：2014-10-10T13:50:40+09:00
date.parseなど上記以外のフォーマットで渡すと環境依存となる。
実際iOS13と14では動作が変わるようである。

**new Date(year, monthIndex[, day[, hours[, minutes[, seconds[, milliseconds]]]]])**
年、日付を指定することでその時刻のオブジェクトを返す。
monthindexについては1月は0なので注意


```Javascript:Date
const dt1 = new Date(); // 現在の時刻 ex) Fri Jan 01 2021 23:46:28 GMT+0900 (日本標準時)
const dt1_string = Date(); // "Fri Jan 01 2021 23:46:28 GMT+0900 (日本標準時)"
const value = dt1.getTime(); // 1609512388374
const dt2 = new Date(value); // Fri Jan 01 2021 23:46:28 GMT+0900 (日本標準時)
const dateString = dt1.toISOString(); // "2021-01-01T14:46:28.374Z" これは拡張ISO8601フォーマット
const dt3 = new Date(dateString); // Fri Jan 01 2021 23:46:28 GMT+0900 (日本標準時) 
const dt4 = new Date(2021, 0, 1, 23, 46, 28); // Fri Jan 01 2021 23:46:28 GMT+0900 (日本標準時)

```

###Dateオブジェクトのメソッドいろいろ

**Date.now**
現在の時刻のUNIX元期からの経過ミリ秒を返す。

**dateObj.getTime()**
dateObjのUNIX元期からの経過ミリ秒を返す。

**dateObj.toString()**
dateObjの日付・時刻を以下のようなフォーマットの文字列で返す。
"Fri Jan 01 2021 23:46:28 GMT+0900 (日本標準時)"

**dateObj.toDateString()**
dateObjの日付を以下のようなフォーマットの文字列で返す。
"Fri Jan 01 2021"

**dateObj.toISOString()**
dateObjの日付を拡張ISO8601フォーマットの文字列で返す。
"2021-01-01T14:46:28.374Z"

**dateObj.getFullYear()**
**dateObj.getDate()**
dateObjの年を取得する場合はgetFullYearを使用する。
それ以外の月や日付を取得する場合はgetDateやgetMonthなどget*を使用する。

**dateObj.setFullYear()**
**dateObj.setDate()**
dateObjの年を設定する場合はgetFullYearを使用する。
それ以外の月や日付を設定する場合はgetDateやgetMonthなどget*を使用する。

###日付の計算
getDateなどで日付を取得し、そこから数値演算したものをsetDateなどで設定する。

```Javascript:CalcDate
const dt = new Date(2021, 0, 1, 23, 46, 28); // Fri Jan 01 2021 23:46:28 GMT+0900 (日本標準時)
dt.setDate(dt.getDate() + 3); // 3日後を計算
console.log(dt.toString()); // Mon Jan 04 2021 23:46:28 GMT+0900 (日本標準時)
dt.setDate(dt.getDate() - 7); // 7日前を計算
console.log(dt.toString()); // Mon Dec 28 2020 23:46:28 GMT+0900 (日本標準時)
```

###JSON

**J**ava<Strong>S</Strong>cript **O**bject **N**otationの略
記載方法は省略して変換方法だけ以下に記載する。

**JSON.parse(text[, reviver])**
textにJSONとして解析する文字列を指定してJSON型へ変換する。
reviverには関数を設定でき、変換したJSONのキーと値に対して何らかの処理を行って返すことが可能。
変換したいテキストの最後にカンマがあるとエラーが発生する。

**JSON.stringify(value[, replacer[, space]])**
オブジェクトやJSON型のデータを文字列に変換できる。
replacerに関数を指定すると変換前のデータを処理できる。
spaceを指定すると文字列のインデント用のスペースなどを追加できる。
オブジェクトの関数は無視されて文字列化される。

```JavaScript:JSON
const JSONStr = '{"a":1,"b":"2"}';
console.log(JSON.parse(JSONStr)); // {a: 1, b: "2"}
const obj = {a:1,b:"2",c: function() {console.log("c")}};
console.log(JSON,stringify(obj)); // {"a":1,"b":"2"}
```

##コレクション

###Map
オブジェクトの一種。
オブジェクトとの違う点は以下（他にもある）

+ キーの型はStringやSymbolに限らない
+ キーの順序は挿入順で固定されている
+ sizeプロパティで項目数を取得できる
+ iterableなのでfor...ofループが使える

**初期化**
`new Map()`で初期化する
初期化の際にはキーと値のペアを配列形式でコンストラクタの引数に追加できる。

**各種プロパティ・メソッド**

**myMap.size**
マップの項目数を取得できる。メソッドではないことに注意。

**myMap.get(key)**
keyに対応する値を返す。

**myMap.has(key)**
キーに対する要素がマップに存在していればtrue、なければfalseを返す。

**myMap.set(key, value)**
keyに対応するvalueをマップに追加する

**myMap.delete(key)**
keyに対応する要素があれば削除しtrueを返す。
なければfalseを返す。

```Javascript:Map
let map = new Map([['London', 'UK']]);
map.has('London'); // true
map.get('London'); // 'UK'
map.set('Paris', 'FR');
map.size; // 2
map.delete('Paris'); // true
console.log(map); // Map(1) {"London" => "US"}
```

###Set
値の重複を許さないコレクションのこと。

**初期化**
`new Set()`で初期化する
初期化の際には値を配列形式でコンストラクタの引数に追加できる。

**各種プロパティ・メソッド**

**mySet.size**
セットの項目数を取得できる。メソッドではないことに注意。

**mySet.add(value)**
値を追加する

**mySet.has(key)**
要素が存在していればtrue、なければfalseを返す。

**mySet.delete(key)**
keyに対応する要素があれば削除しtrueを返す。
なければfalseを返す。

```Javascript:Set
let set = new Set([1,2,3]);
set.add(4); // Set(4) {1, 2, 3, 4}
set.add(4); // 重複した値を登録しようとしているので無視される
set.has(1); // true
set.size; // 4
set.delete(4); // true
```


##データ型の変換

数値と文字列を+演算子で結合すると数値を文字列に変換する。
それ以外の演算子では数値を文字列に変換せず、文字列を数値に変換する。

```Javascript:変換1
x = '答えは ' + 42 // "答えは 42"
y = 42 + ' が答え' // "42 が答え"

x = '37' - 7 // 30
y = '37' + 7 // 377
```

文字列を数値に変換するにはparseInt,parseFloatを使用する
※parseIntは小数を切り捨てる
もしくは+演算子を使う

```Javascript:parseInt
let num = '10';
parseInt(num,10); //数値型の10が返る

+num; //数値型の10が返る

```

##リテラル

###配列リテラル
配列リテラル中ではカンマを二つつなげることでundefinedの値で要素を埋めることができる
ただし、要素のリストの最後にカンマを付けた場合、そのカンマは無視される

```Javascript:array
let fish = ['Lion', , 'Angel'];
console.log(fish[1]); //undefined

let myList = ['home', , 'school', , ];
console.log(myList); // ["home", empty, "school", empty]
```

###数値リテラル

Number および BigInt 型は、10進数、16進数、8進数、2進数で書くことができる。

* 10進数は先頭が0でない一連の数字
* 8進数は先頭に0もしくは0o
* 16進数は先頭に0x
* 2進数は先頭に0b


###オブジェクトリテラル
オブジェクトのプロパティには空の文字列を含むあらゆる文字列が使える。
プロパティ名が JavaScript で有効な識別子か数値でなければ、引用符で囲む必要がある。

```Javascript:Object
var unusualPropertyNames = {
  '': '空文字列',
  '!': 'バン！'
}

console.log(unusualPropertyNames.'');   // SyntaxError: Unexpected string が発生
console.log(unusualPropertyNames['']);  // 空文字列
console.log(unusualPropertyNames.!);    // SyntaxError: Unexpected token ! が発生
console.log(unusualPropertyNames['!']); // バン！
```

###正規表現リテラル

/で囲むと正規表現を表す


##ループと反復処理

###ラベル
`ラベル:`の形で使用することでループ処理などにラベルを付けることができる。
そのラベルを使用して`break labelName;`などでlabelNameのループを抜けることが可能。
また、`contunue labelName;`で次のlabelNameの処理に移ることができる

```Javascript:Label
markLoop:
while (themark === true) {
  if (flag === true) {
    break markLoop;
  }
}
```

### for...in ループ

キーが文字列であるオブジェクトの列挙可能プロパティに対して反復処理を行う(Symbolキーは無視する)。
任意の順序でオブジェクトのプロパティに対して反復する
継承したプロパティもfor...inでは列挙するのでhasOwnpropertyメソッドで自身のインスタンスにあるプロパティかチェックしたほうがよい。
Object.keys()などでキーを取得してループするか、forEachを使ったほうが良い
配列にも使用しないほうが良い
反復処理するpropertyはStringなので実際の値をとるにはそれをキーとして使う

```Javascript:for...in
const SYM =Symbol();
const o = {a: 1, b: 2, [SYM]: 3};
for (let property in o) {
  if (!o.hasOwnProperty(property)) continue;
  console.log(`${property}: ${o[property]}`);
}
//a: 1
//b: 2
```

### for...of ループ
反復可能オブジェクト(String,Array,Map,Setなど)に対して反復処理するループを作成する
Mapなどに使用した場合はキーと値の要素を持つ配列を返す

```Javascript:for...of
const str = "abc";
const arr = [1,2,3];
const mp = new Map([["X","x"], ["Y","y"], ["Z","z"]]);

for (const value of str) {
  console.log(value); // "a" "b" "c"
}

for (const value of arr) {
  console.log(value); // 1 2 3
}

for (const value of mp) {
  console.log(value); // ["X","x"]  ["Y","y"]  ["Z","z"]
}
```

##オブジェクト

###オブジェクトのいろいろ

**オブジェクトの初期化**
オブジェクトは`new Object()`もしくは`Object.create()`、リテラルを使用して初期化される。

```Javascript:初期化
const a = 'foo', b = 42, c = {};
const o = {a, b, c}; // a: 'foo', b:42, c: {}と同義
const obj1 = Object.create(o) // prototypeがoオブジェクトとなるオブジェクトを作成する
console.log(obj1); // {} ->prototypeがoなだけなので何もプロパティは設定されていない状態

```

**getter, setter**
オブジェクトのプロパティはgetter,setterを定義することもできる

```Javascript:getter,setter
var o = {
  prop: 'value',
  get propValue() {
    return this.prop;
  },
  set propValue(param) {
    this.prop = param;
  }
}
o.propValue = 'test';
console.log(o.propValue); // 'test'
```

**スプレッドプロパティ**
渡されたオブジェクトから新しいオブジェクトに独自の列挙可能なプロパティをコピーする。

```Javascript:スプレッド演算子
const obj1 = { foo: 'bar', x: 42 };
const obj2 = { foo: 'baz', y: 13 };

var clonedObj1 = {obj1} //  Object {obj1 : { foo: "bar", x: 42 }} obj1をプロパティに持つ
var clonedObj2 = { ...obj1 }; // Object { foo: "bar", x: 42 } obj1のプロパティを持つ
var mergedObj = { ...obj1, ...obj2 }; // Object { foo: "baz", x: 42, y: 13 }
```

**プロパティの削除**
オブジェクトのプロパティを削除するメソッドはない、削除したい場合はdelete演算子を使う

```Javascript:delete
const Employee = {
  firstname: 'John',
  lastname: 'Doe'
};
delete Employee.firstname;
console.log(Employee.firstname); // undefined
```

**オブジェクトの代入**
オブジェクトの代入は参照渡しであり、===で比較するとtrueになる。
オブジェクトの比較にはObject.is()を使う（のちほど）。
代入元のオブジェクトを変更すると代入先も変更される。

```Javascript:代入
const obj1 = {a:1,b:2};
const obj2 = obj1;
console.log(obj1 === obj2) // true

obj1.a = 3;
console.log(obj2); // {a: 3, b: 2}
```


###静的メソッド


**Object.assign(target, ...source)**
コピー元オブジェクトから列挙可能かつ直接所有のプロパティだけをコピー先オブジェクトにコピー
String と Symbol の両方のプロパティがコピーされる
代入なのでgetter, setterが呼び出される
プロパティの値をコピーするためオブジェクトの参照値がコピーされる(DeepCloneは別の方法が必要)

```Javascript:assign
const target = { a: 1, b: 2 };
const SYM =Symbol();
const source = { b: 4, c: 5, [SYM]: 6 };

const returnedTarget = Object.assign(target, source); 
console.log(returnedtarget); // {a: 1, b: 4, c: 5, Symbol(): 6}
console.log(returnedtarget === target); // true
const copy = Object.assign({},target);
console.log(copy); // { a: 1, b: 2 }
```

**Object.defineProperty(obj, prop, descriptor)**
obj: 対象のオブジェクト
prop: プロパティ名またはシンボル
descriptor: 定義または変更されるプロパティの記述子
writeable, enumerable, configurableといった属性値がある
あるオブジェクトにプロパティを直接定義したり、既存のプロパティを変更できる
デフォルトではこのメソッドで追加された値は不変となり列挙可能ではない

```Javascript:defineProperty
const obj = {foo: 'bar'};
Object.defineProperty(obj, 'color', {
  get: () => this.color,
  set: value => this.color = value
} ); // colorというプロパティにgetter,setterを追加
Object.defineProperty(obj, 'prop', {
  value: 42,
  writable: false,
　enumerable: true
}); // 書き込み不可で列挙可能なpropプロパティを定義
```

**Object.getOwnPropertyDescriptor(obj, prop)**
obj: 対象のオブジェクト
prop: 対象のプロパティ名
プロパティの構成を記述するオブジェクトを返す

```Javascript:getOwnPropertyDescriptor
const object1 = {
  property1: 42
};
console.log(Object.getOwnPropertyDescriptor(object1, 'property1')); 
// {value: 42, writable: true, enumerable: true, configurable: true}
```

**Object.entries(obj)**
obj: 対象のオブジェクト
引数に与えられたオブジェクトの列挙可能な所有プロパティのキーと値を配列として返す。
返される順序は不定なので必要に応じてsortする必要がある
ObjectからMapへの変換が簡単にできる
配列が返ってくるのでforEachが使える

```Javascript:entries

const obj = { foo: 'bar', baz: 42 };
console.log(Object.entries(obj)); // [ ['foo', 'bar'], ['baz', 42] ]

for ( let [key, value] of Object.entries(obj)) {
  console.log(`${key} ${value}`);
}

const map = new Map(Object.entries(obj)); // Map { foo: "bar", baz: 42 }
Object.entries(obj).forEach(([key, value]) => console.log(`${key}: ${value}`)); // "foo: bar", "baz: 42"
```

**Object.keys(obj)**
obj: 対象のオブジェクト
引数のオブジェクトの列挙可能な自身のプロパティ名を配列で返す。

```JavaScript:keys
const obj = { foo: 'bar', baz: 42 };
console.log(Object.keys(obj)); // ['foo', 'baz']
```

**Object.values(obj)**
obj: 対象のオブジェクト
引数のオブジェクトの列挙可能な自身のプロパティに対応する値を配列で返す。

```JavaScript:values
const obj = { foo: 'bar', baz: 42 };
console.log(Object.values(obj)); // ['bar', 42]
```

**Object.is(value1, value2)**
二つの引数が同じ値であるかどうかを比較する。
===との違いはNanとNanを比較したときにObject.isはtrueになるが===はfalseとなることと、
+0と-0を比較したときにObject.isはfalseとなるが===はtrueになること

```JavaScript:is
const obj1 = { foo: 'bar', baz: 42 };
const obj2 = { foo: 'bar', baz: 42 };
const obj3 = obj1;
console.log(Object.is(obj1, obj2)); // false
console.log(Object.is(obj1, obj3)); // true
```

###オブジェクトの編集を制限するメソッド

**Object.freeze(obj)**
obj: 凍結対象のオブジェクト
オブジェクトを凍結した状態に変更する。
配列も凍結できる
この状態ではプロパティ値の変更、setterの呼び出し、プロパティの追加、ディスクリプタの変更ができなくなる

```JavaScript:freeze
const obj = { foo: 'bar', baz: 42 };
Object.freeze(obj);
obj.foo = 'fizz'; // No Change or error in strict mode
```

**Object.seal(obj)**
obj: 対象のオブジェクト
対象オブジェクトに対して新たなプロパティの追加をできなくさせるほか、
既存プロパティの記述子(writeableなど)の変更もできなくなる。
既存のプロパティの値に対する変更は可能

```JavaScript:seal
const obj = { foo: 'bar', baz: 42 };
Object.seal(obj);
Object.defineProperty(obj, 'foo', {
  writable : false
});// No Change or error in strict mode
```

**Object.preventExtensions(obj)**
obj: 対象のオブジェクト
objに対して新たなプロパティの追加をできなくさせる。
既存のプロパティに対する削除・値の変更・記述子の変更は可能

```JavaScript:preventExtensions
const obj = { foo: 'bar', baz: 42 };
Object.preventExtensions(obj);
obj.fizz = 'fizz'; // No Change or error in strict mode
```

freeze > seal > preventExtentionsの順にできることが増えていく

##配列

###配列へのアクセス
不正なインデックス番号を使った場合は undefinedを返す。


###静的メソッド

**Array.isArray(value)**
value:判定対象
valueが配列かどうかチェックし配列であればtrueを返す。

**Array.of(element0[, element1[, ...[, elementN]]])**
elemenN: 作成する配列の要素
可変長の引数からそれらを要素とする配列を生成する。
コンストラクタArray()との違いは単一の数字要素を与えたときにそれを要素として配列を作るか、要素数として配列を作るかの違い

```JavaScript:of
console.log(Array.of(1,2,3,"4")); // [1,2,3,"4"]
console.log(Array.of(7)); // [7]
console.log(Array(7)); // [empty x 7] 要素数7で要素がすべてundefinedの配列
```

**Array.from(arrayLike[, mapFn[, thisArg]])**
arrayLike: 配列に変換する配列のようなオブジェクトもしくは反復可能オブジェクト(map, setなど)
mapFN: 各要素に適用されるmap関数
thisArg: 任意で指定するmap関数内でthisとして使うオブジェクト
指定したオブジェクトを配列に変換する

```JavaScript:from
console.log(Array.from('foo')); // [ "f", "o", "o" ]
let map = new Map([['London', 'UK'], ['Paris', 'FR']]);
console.log(Array.from(map)); // [['London', 'UK'], ['Paris', 'FR']]
```

###配列の操作

**arr.push([element1[, ...[, elementN]]])**
elementN: 追加する要素
配列の最後に要素を追加し、追加後のarray.lengthを返す。
実行した配列そのものを変更する。

```Javascript:push
let arr = ['a','b','c'];
console.log(arr.push('d')); //4
console.log(arr) // ['a','b','c','d']
```

**arr.pop()**
配列の最後の要素を取り除き、取り除いた値を返す。
空の配列に対してpop()を実行した場合は、undefinedを返す。
実行した配列そのものを変更する。

```Javascript:pop
let arr = ['a','b','c'];
console.log(arr.pop()); // c
console.log(arr); // ['a','b']
```

**arr.shift()**
配列から先頭の要素を削除し、その値を返す。
空の配列に対してshift()を実行した場合は、undefinedを返す。
実行した配列そのものを変更する

```Javascript:shift
let arr = ['a','b','c'];
console.log(arr.shift()); // a
console.log(arr); // ['b','c']
```

**arr.unshift(element1[, ...[, elementN]])**
elementN: 追加する要素
配列の先頭に要素を追加し、追加後のarray.lengthを返す。
実行した配列そのものを変更する。

```Javascript:unshift
let arr = ['a','b','c'];
console.log(arr.unshift('d')); // 4
console.log(arr); // ['d','a','b','c']
```

**const new_array = old_array.concat([value1[, value2[, ...[, valueN]]]])**
valueN: 新しい配列に連結する配列や値
呼び出した配列に引数の要素を追加した配列を返す。
呼び出した配列自体は変更されない。
引数には要素のほか配列を渡すことができる、配列を渡した場合は要素に分解されて結合される。
ネストされた配列は配列として追加される。

```Javascript:concat
let arr1 = ['a','b','c'];
let arr2 = arr1.concat('d', ['e','f'], [['g']]);
console.log(arr1); // ['a','b','c']
console.log(arr2); // ['a','b','c','d','e','f',["g"]]
```

**arr.slice([start[, end]])**
start: 取り出し開始位置を示すインデックス
end: 取り出し終了位置を示すインデックス(取り出されるのは指定したインデックスの一つ前の要素まで)
呼び出し元の配列から特定の範囲内を切り取った部分配列を返す。
呼び出し元の配列は変更されない。
startを指定しない場合は0スタート、endを指定しない場合は最後の要素まで。
endに負の数を指定した場合は最後の要素から数えた値までを取り出す。

```Javascript:slice
let arr = ['a','b','c'];
console.log(arr.slice(1,2)); // ['b']
console.log(arr.slice(0,-1)); // ['a','b']
```

**let arrDeletedItems = array.splice(start[, deleteCount[, item1[, item2[, ...]]]])**
start: 配列を変更する開始位置を示すインデックス
deleteCount: 削除する要素の数
itemN: 配列に追加する要素
呼び出し元の配列から削除された要素からなる配列を返す。
呼び出し元の配列が変更される。
startが負の数の場合最後の要素から戻った場所を示す。
deleteCountが省略された場合start以降の要素を削除する。
deleteCountが0以下の場合削除はされない。


```Javascript:splice
let arr = ['a','b','c'];
console.log(arr.splice(1,1)); // ['b'] arr[1]から1個の要素が削除された
console.log(arr); //  ['a','c']　削除された残りの配列
console.log(arr.splice(1,0,'b1','b2')); //[] 削除された要素はない
console.log(arr); // ['a','b1','b2','c'] 二つ要素がarr[1]の位置に追加された
```

**arr.copyWithin(target[, start[, end]])**
target: 値のコピー先のインデックス
start: コピー元の開始インデックス
end: コピー元の終了インデックス
配列の一部をサイズ変更せずにコピーする。
変更されたオブジェクトそのものを返す、呼び出し元のオブジェクトを変更する
endが省略された場合最後の要素までをコピーする


```JavaScript:copyWithin
let arr = ['a','b','c'];
console.log(arr.copyWithin(1,2)); // ['a','c','c'] arr[1]の要素がarr[2]から最後までの値にコピーされた
console.log(arr); // ['a','c','c']
console.log(arr.copyWithin(2,0,1)); //['a','c','a'] arr[2]の要素がarr[0]からarr[1]の手前までの値でコピーされた
```

**arr.fill(value[, start[, end]])**
value: 配列に設定する値
start: 値の設定開始インデックス
end: 値の設定終了インデックス
配列内の特定の要素をvalueに変更する。
startやendが省略された場合すべての要素を変更する。
呼び出し元が変更され、変更された配列そのものを返す。

```JavaScript:fill
let arr = ['a','b','c'];
console.log(arr.fill('d')); // ["d", "d", "d"]
console.log(arr); // ["d", "d", "d"]
```

###配列の並べ替え

**arr.sort([compareFunction])**
compareFunction: ソート順を定義する関数
配列の要素を並び替える。
関数を指定しなかった場合は要素はすべて文字列に変換されて昇順にソートされる。
関数を指定する場合、compareFunction(a, b)について負の数を返したときはaが先になるように並び替える
正の数を返したときはbが先になるように並び替える。
0が返るときは何もしない。
呼び出し元の配列を変更する。

```Javascript:sort
let arr = [1, 3, 11];
arr.sort(); // 文字列として昇順ソート
console.log(arr); // [1, 11, 3] 11が3よりも先に来る
arr.sort((a,b) => a - b); //数値として昇順ソート
console.log(arr); // [1, 3, 11]
```
**arr.reverse()**
配列の要素を反転させる。
呼び出し元の配列を変更する。

```Javascript:reverse
let arr = [1, 3, 11];
arr.reverse(); 
console.log(arr); // [11, 3, 1] 
```

###配列の検索

**arr.indexOf(searchElement[, fromIndex])**
配列内の要素からsearchElementを検索し、最初にヒットしたインデックス、なければ-1を返す。
fromIndexを指定すると検索するインデックスの範囲を指定できる。
比較自体は===で実行される。

**arr.lastIndexOf(searchElement[, fromIndex])**
配列内の最後の要素からsearchElementを検索し、最初にヒットしたインデックス、なければ-1を返す。
fromIndexを指定すると検索するインデックスの範囲を指定できる。
比較自体は===で実行される。

```JavaScript:index/lastIndexOf
let arr = [1, 3, 11, 3, 4];
console.log(arr.indexOf(3)); // 1
console.log(arr.lastIndexOf(3)); // 3 後ろから検索して最初にヒットしたインデックス
```


**arr.findIndex(callback( element[, index[, array]] )[, thisArg])**
element: 配列内で現在処理されている要素
index: 配列内で現在処理されている要素のインデックス
array: 呼び出し元の配列
thisArg: 任意で指定するコールバック関数内でthisとして使うオブジェクト
配列内の要素からコールバック関数で指定した条件にヒットする要素を検索し、最初にヒットしたインデックス、なければ-1を返す。
検索の際に使用する関数をコールバック関数に指定する。
コールバック関数ではtrueを返した際に検索にヒットしたと判定する。

**arr.find(callback(element[, index[, array]])[, thisArg])**
element: 配列内で現在処理されている要素
index: 配列内で現在処理されている要素のインデックス
array: 呼び出し元の配列
thisArg: 任意で指定するコールバック関数内でthisとして使うオブジェクト
配列内の要素からコールバック関数で指定した条件にヒットする要素を検索し、最初にヒットした要素、なければ-1を返す。
検索の際に使用する関数をコールバック関数に指定する。
コールバック関数ではtrueを返した際に検索にヒットしたと判定する。
検索した結果、要素がほしい場合にはfindを使用する。

```JavaScript:findIndex/find
let arr = [{id:1, value:"foo"}, {id:2, value:"foo"}];
console.log(arr.findIndex(element => element.id === 2)); // 1
console.log(arr.find(element => element.id === 2)); // {id:2, value:"bar"}
```

**arr.some(callback(element[, index[, array]])[, thisArg])**
element: 配列内で現在処理されている要素
index: 配列内で現在処理されている要素のインデックス
array: 呼び出し元の配列
thisArg: 任意で指定するコールバック関数内でthisとして使うオブジェクト
呼び出し元の配列内で一つでもコールバック関数で指定した結果に一致する要素があればtrue,なければfalseを返す。

```JavaScript:some
let arr = [{id:1, value:"foo"}, {id:2, value:"foo"}];
console.log(arr.some(element => element.id === 2)); // true
console.log(arr.some(element => element.id === 3)); // false
```

**arr.every(callback(element[, index[, array]])[, thisArg])**
element: 配列内で現在処理されている要素
index: 配列内で現在処理されている要素のインデックス
array: 呼び出し元の配列
thisArg: 任意で指定するコールバック関数内でthisとして使うオブジェクト
呼び出し元の配列の要素すべてがコールバック関数で指定した結果に一致する場合true,なければfalseを返す。

```JavaScript:every
let arr = [{id:1, value:"foo"}, {id:2, value:"foo"}];
console.log(arr.every(element => element.value === "foo")); // true
console.log(arr.every(element => element.id === 1)); // false
```

###配列の要素の変換

**let new_array = arr.map(callback(element[, index[, array]])[, thisArg])**
element: 配列内で現在処理されている要素
index: 配列内で現在処理されている要素のインデックス
array: 呼び出し元の配列
thisArg: 任意で指定するコールバック関数内でthisとして使うオブジェクト
配列の要素をひとつづつ処理して別の配列に変換して返す。
呼び出し元の配列は変更しない。

```JavaScript:Map
const cart = [{name: "iPhone", price: 10000}, {name: "Android", price: 5000}];
console.log(cart.map(x => x.name)); // ["iPhone", "Android"]
console.log(cart.map(x => x.price)); // [10000, 5000]
console.log(cart.map(x => x.price * 0.8)); // [8000, 4000]
```

**let newArray = arr.filter(callback(element[, index, [array]])[, thisArg])**
element: 配列内で現在処理されている要素
index: 配列内で現在処理されている要素のインデックス
array: 呼び出し元の配列
thisArg: 任意で指定するコールバック関数内でthisとして使うオブジェクト
配列の要素をひとつずつテストしてtrueを返した要素のみで構成される別の配列に変換して返す。
呼び出し元の配列は変更しない。

```JavaScript:filter
const cart = [{name: "iPhone", price: 10000}, {name: "Android", price: 5000}];
console.log(cart.filter(x => x.price > 5000)); // [{name: "iPhone", price: 10000}]
```

**arr.reduce(callback( accumulator, currentValue[, index[, array]])[, initialValue])**
accumulator: 最終的に配列が変換される先
currentValue: 配列内で現在処理されている要素
index: 配列内で現在処理されている要素のインデックス
array: 呼び出し元の配列
initialValue: accumulatorの初期値
配列内の要素を一つずつ処理して変換し、accumulatorに結果を格納する
呼び出し元の配列は変更されない。

```JavaScript:reduce
const arr = [1,2,3,4,5];
console.log(arr.reduce((a,x) => a += x)); // 15 配列内の要素をすべて足し合わせた結果
```


**arr.join([separator])**
配列内の要素をsepartatorで連結した文字列で返す。
separatorはデフォルトでは","であり、""を指定すると何も区切らずに連結する。
null, undefinedは空文字として連結される

```JavaScript:join
const elements = ["hello", null, 'world'];
console.log(elements.join()); // "hello,,world"
console.log(elements.join("")); // helloworld
console.log(elements.join("-")); // "hello--world"
```

###配列が元の配列を変更するか

| 変更する        | 変更しない | 
|:---------------|:-----------|
| push, pop      | concat     | 
| shift, unshift | slice      | 
| splice         | map        |
| copyWithin     | filter     |
| fill           | reduce     | 
| reverse        | join       | 
| sort           |            | 


##thisの扱いについて

###this
1.すべての関数の外、最上位で呼ばれる関数内ではthisはグローバルオブジェクトを参照する。
2.関数がオブジェクトから呼び出される場合はthisはその呼び出し元のオブジェクトを参照する。
3.関数がコンストラクタとして使用される場合、thisは新しく作成されるオブジェクトを参照する

```Javascript:this
//1のパターン
function displayThis() {
  console.log(this);
};
displayThis(); // windowオブジェクトが表示される

//2のパターン
let obj = { prop: "val", objFunc: displayThis};
obj.objFunc(); // {prop: "val", objFunc: ƒ}  呼び出し元のオブジェクトが表示される

//3のパターン
function Product(name) {
  this.name = name;
  console.log(this);
}
let p = new Product('prod'); // Product {name: "prod"} 作成されたインスタンスが表示される
console.log(p.name); // prod
```

関数の中の関数ではthisはグローバルオブジェクトかundefinedになってしまう。
下の例だとfunc2はXXX.func2()の形で呼ばれていないため、window.func2()として解釈されてしまう。

```Javascript:this_in_function
const o = {name:'TEST',
           func1: function () {
               function func2() {
                   console.log('func2',this);
               }
               console.log('func1',this);
               func2();
           }
          };
o.func1();
// func1 {name: "TEST", func1: ƒ}
// func2 Window {0: global, window: Window, self: Window, document: document, name: "", location: Location, …}
```

アロー関数を使うとthisを語彙的に束縛できるため上記事象を回避できる。
すなわち、アロー関数では通常の変数と同様に内側のスコープから順にthisを探索していくこととなる。
下記の例だとfunc2はXXX.func2()の形で呼ばれていないため次にスコープであるfunc1のthisを探す。
見つかったのでthisは`{name: "TEST", func1: ƒ}`となる

```Javascript:this_in_allowfunction
const o = {name:'TEST',
           func1: function () {
               let func2 = () => {
                   console.log('func2',this);
               }
               console.log('func1',this);
               func2();
           }
          };
o.func1();
// func1 {name: "TEST", func1: ƒ}
// func2 {name: "TEST", func1: ƒ}
```

###thisを明示的に指定するメソッド

**func.call([thisArg[, arg1, arg2, ...argN]])**
thisArg: func関数で使用するthisの値
argN: func関数の引数
func関数を呼び出す際にcallをチェーンすることでその中で使用されるthisの値を指定できる。
argNはfunc関数の引数となる。

```JavaScript:call
let obj = {a:1,b:2};
function func1(value) {
  this.a = value;
}
func1.call(obj,3);
console.log(obj); //{a: 3, b: 2}
```

**func.apply(thisArg, [ argsArray])**
thisArg: func関数で使用するthisの値
arsArray: 配列風のオブジェクト
func関数を呼び出す際にapplyをチェーンすることでその中で使用されるthisの値を指定できる。
callとの違いは引数が配列風オブジェクトなこと、配列を使いたい場合はこっちが便利。

```JavaScript:apply
let arr = [1,2,3,4,5];
console.log(Math.min.apply(null, arr)); // 1
//これはMath.min(arr[0],arr[1],...)と一緒
//すなわちMath.min(...arr)

func1.call(obj,3);
console.log(obj); //{a: 3, b: 2}
```

**let boundFunc = func.bind(thisArg[, arg1[, arg2[, ...argN]]])**
thisArg: func関数で使用するthisの値
argN: func関数の引数
func関数を呼び出す際に使用するthisの値を明示的に固定し、指定したthisを使う関数を返す。
callと違って関数を返すためその場でfunc関数は実行されない。
一度bindされるとほかのcallなどでthisを指定しても反映されない。

```JavaScript:bind
let obj = {a:1,b:2};
function func1(value) {
  this.a = value;
}
let bindfunc = func1.bind(obj);
console.log(obj); //{a: 1, b: 2}
bindfunc(3);
console.log(obj); //{a: 3, b: 2}

let obj2 = {a:100,b:101};
bindfunc.call(obj2,200); //obj2をthisに指定したが無視されてobj1をthisとして実行される
console.log(obj,obj2); // {a: 200, b: 2} {a: 100, b: 101}
```

##クラス

###クラスの定義

以下のように記載することでClassを定義できる。
クラス定義の実態は関数となる。
クラスの定義は巻き上げされない。

```Javascript:class
//パターン1: prototypeの糖衣構文
class Car {
  constructor() {
  }
}
let c = new Car();

//パターン2　関数による定義
function Car() {
}
let c = new Car();
```

###静的メソッド

クラス定義中に静的メソッドを定義できる。
静的メソッドはインスタンスからは実行できない。

```Javascript:static
class Car {
  static func1() {
    console.log('static');
  }

  constructor() {
  }
}

let c = new Car();
Car.func1(); // static
c.func1(); // TypeError

```

###継承

extends構文を使うとオブジェクトを継承できる。
継承先のクラスのコンストラクタではsuper()を起動しないと失敗する。
継承元と継承先に同じメソッド名がある場合は継承先のメソッドが使用される。
継承元のメソッドもプロトタイプチェーンを利用して使用できる。

```Javascript:extends
class Viecle {
  static staticride() {
    console.log('static ride');
  }
  ride() {
    console.log('viecle ride');
  }
  ride2() {
    console.log('viecle ride2');
  }
  constructor() {
    console.log('Viecle Constructor');
  }
}

class Car extends Viecle {
  constructor() {
    super(); // 継承時は必須
    console.log('Car Constructor');
  }
  ride() {
    console.log('car ride');
  }
}

let c = new Car();
// Viecle Constructor
// Car Constructor

c.ride(); // Car ride
c.ride2(); // viecle ride2
Car.staticride(); // static ride
```

##モジュール

`<script type="module" src="...">`の記載でHTMLファイルにjavascriptモジュールをインポートできる
module自体はstrictモードで実行される

エクスポートには名前付きエクスポートとデフォルトエクスポートの2種類がある

###名前付きエクスポート

```Javascript:namedexport.js
export let myVariable = Math.sqrt(2);
export const CONST_VALUE = 'VALUE';
export function myFunction() { ... };

//もしくは・・・

let myVariable = Math.sqrt(2);
const CONST_VALUE = 'VALUE';
function myFunction() { ... };

export {myVariable, CONST_VALUE, myFunction}
```

名前付きエクスポートをインポートするときは同じ名前を{}で囲んでインポートする。

```Javascript:namedimport.js
import {myVariable, CONST_VALUE, myFunction} from 'namedexport.js';
```

###デフォルトエクスポート
1ファイルにつき1つのみエクスポートできる。


```Javascript:defaultexport.js
export default function () { ... }

//もしくは・・・

function myFunction() { ... };
export { myFunction as default };
```

デフォルトエクスポートをインポートするときは任意の名前を設定してインポートする。
その際は{}で記載しない。

```Javascript:defaultimport.js
import defaultFunction from 'defaultexport.js';
```

いずれも`import {default as func1} や import { myFunction as func1}`の形で別名を付けることができる。
`import * as myModule from 'sample.js'`でモジュールをインポートした場合、
sample.jsからエクスポートされたすべての変数や関数などをmyModule名前空間で参照できる。
`myModule.func1();`みたいな形。


##非同期処理

###よく使うメソッド

**var timeoutID = scope.setTimeout(function[, delay])**
function: 指定した時間経過した後に実行したい関数
delay: タイマーの秒数(ミリ秒単位)
タイマーを設定し、タイマーの時間が来た後にfunctionを実行する。
実行後はタイマーのIDを返し、これを使用してタイマーを中止できる(clearTimeout)。
setTimeoutにて指定された関数はコールスタックからWeb APIを経由して最終的にキューに追加される。
その後コールスタックの処理が空になったタイミングでキューからコールスタックに追加され実行される。
以下のサイトがわかりやすかった。

> JavaScript イベントループの仕組みをGIFアニメで分かりやすく解説
> https://coliss.com/articles/build-websites/operation/javascript/javascript-visualized-event-loop.html


**scope.clearTimeout(timeoutID)**
timeoutID: setTimeoutを実行して帰ってきたId
指定されたIdのタイマーの処理を中止させる。

```JavaScript:setTimeout
//カウントダウン処理
for(let i = 5; i >= 0; i--) {
  setTimeout(() => {
    console.log(i);
  },(5-i)*1000);
} 

let timerId = setTimeout(() => {console.log('TEST');},1*1000);
clearTimeout(timerId);
//console.log('TEST')は実行されない

```

**var intervalID = scope.setInterval(func, delay[, param1, param2, ...]);**
func: 指定した時間経過した後に実行したい関数
delay: タイマーの秒数(ミリ秒単位)
paramN: funcに追加で渡す引数
delayに指定した間隔で引数に渡した関数を実行する。
実行後はタイマーのIDを返し、これを使用してタイマーを中止できる(clearInterval)。
clearIntervalを使用して処理を中止させない限り処理し続ける。

**scope.clearInterval(timeoutID)**
timeoutID: setTimeoutを実行して帰ってきたId
指定されたIdのタイマーの処理を中止させる。

```JavaScript:setInterval
//1秒ごとに時刻を表示する処理を5回まで実施する。
let i = 0;
const timerId = setInterval(() => {
  let now = new Date();
  if (i < 5) {
    console.log(now.toString());
    i++
  } else {
    clearInterval(timerId);
  }
}, 1*1000);
```

###Promise

非同期処理を表すオブジェクト、これを使うことでコールバック地獄から解消される。
promiseの処理結果に対してthenやcatchをチェインできる。
チェイン内でエラーが起きた場合はどこの処理であってもcatchが実行される。

**使い方**

> + 非同期処理を記述し、実行するための関数func1を用意する。
> + func1内では return new Promiseを使用してPromiseを返すようにする(返したPromiseをthenハンドラなどで処理する)。
> + 上記で返すPromiseの引数には実際に行いたい処理を記載する関数func2を設定する。
> + func2の引数にはresolve,rejectの二つの引数を設定する。
> + func2内の処理で処理に成功した場合はresolve(value)を実行するようにする。
> + 処理が実際に成功すると、呼び出し元の処理でthenを使用している場合、そこへresolveへ渡した引数を渡すことができる。
> + func2内の処理で処理に失敗した場合はreject(value)を実行するようにする。
> + 処理が実際に成功すると、呼び出し元の処理でthenやcatchを使用している場合、そこへrejectへ渡した引数を渡すことができる。
> + Promiseを受け取った側ではthenメソッドに処理が成功した場合に実行する関数、処理が失敗した場合に実行する関数を引数として設定して実行する。

コードにするとこんな感じ。

```JavaScript:Promise_image
//Promiseを返すfunc1を定義
function func1() {
  return new Promise((resolve,reject) => {//ここがfunc2の部分
    //実際に実行したい非同期処理処理...
    if (処理に成功した) {
      resolve('成功'); // '成功'の文字列がthenメソッドへ渡される
    } else {
      reject('失敗');
    }
  });
}

func1()
.then((msg) => { console.log(msg) }, (errmsg) => { console.log(errmsg) });
//成功時は'成功',失敗時は'失敗'の文字列が出力される
}

```

**例**
呼び出し元から文字列を受け取ってそれを基にオブジェクトを作成して返す処理を非同期で実装する。
その際に文字列が'ERR'だと作成に失敗するようにする。

```JavaScript:Promise_image
function createObject(str) {
  return new Promise((resolve, reject) => {
    if (str === 'ERR') {
      reject(new Error('引数が不正です。'));
    } else {
      resolve({param: str});
    }
  });
}

createObject('TEST')
.then(obj => { console.log(obj); }, err => { console.error(err); });
// {param: "TEST"}

createObject('ERR')
.then(obj => { console.log(obj); }, err => { console.error(err); });
// Error: 引数が不正です。

//catchを使う場合
createObject('ERR')
.then(obj => { console.log(obj); })
.catch(err => { console.error(err); });
```

**Promise.all(iterable)**
iterable: 反復可能なオブジェクト
複数のPromiseを配列にして渡すことで渡したPromiseを並列に処理し、
すべてのPromiseがresolveになればthenを、一つでもrejectになればその時点でPromiseを返すような処理が実現できる。
処理結果は配列として返される。
配列の添え字と渡したPromiseの順番は一致する。

```JavaScript:Promise.all
let p1 = new Promise((resolve,reject) => {resolve('p1');});
let p2 = new Promise((resolve,reject) => {resolve('p2');});
let p3 = new Promise((resolve,reject) => {reject('p3');});

Promise.all([p1, p2])
.then(results => {
  console.log(results[0], results[1]); // p1 p2
});

Promise.all([p1, p2, p3])
.then(results => {
  console.log(results[0], results[1]); // p1 p2
})
.catch(results => {
  console.error(results); // p3
});
```

**Promise.allSettled(iterable);**
iterable: 反復可能なオブジェクト
複数のPromiseを配列にして渡すことで渡したPromiseを並列に処理し、
すべてのPromiseがresolveもしくはrejectedの状態になった時点でPromiseを返すような処理を実現できる。
Promise.allとの違いはrejectの状態のPromiseが発生してもすぐにCatchが実行されないこと。
実行結果は配列で返されるが、配列の要素には関数から渡された値のほかにstatusを持つ。
Statusの値が"fulfilled"なら処理成功、"rejected"なら処理失敗を示す。

```JavaScript:Promise.all
let p1 = new Promise((resolve,reject) => {resolve('p1');});
let p2 = new Promise((resolve,reject) => {resolve('p2');});
let p3 = new Promise((resolve,reject) => {reject('p3');});

Promise.allSettled([p1, p2, p3])
.then(results => {
  results.forEach(x => console.log(x)); 
});
// {status: "fulfilled", value: "p1"}
// {status: "fulfilled", value: "p2"}
// {status: "rejected", reason: "p3"}
```

**Promise.race(iterable)**
iterable: 反復可能なオブジェクト
複数のPromiseを配列にして渡すことで渡したPromiseを並列に処理し、
一つでもPromiseがresolveもしくはrejectになった時点でその結果をthenやcatchに渡す処理が実現できる。

```JavaScript:Promise.race
let p1 = new Promise((resolve,reject) => {resolve('p1');});
let p2 = new Promise((resolve,reject) => {resolve('p2');});
let p3 = new Promise((resolve,reject) => {reject('p3');});

Promise.race([p1, p2, p3])
.then(result => {
  console.log(result); // p1 or p2
})
.catch(result => {
  console.error(result); // p3
});
```

###async / await

async/awaitキーワードを使うことでPromiseを使った処理を同期処理のように記載することができる。

**async function name() {}**
awaitを使用する関数で使用する。

**[rv] = await expression**
expression: 解決を待つPromise
Promise内でresolve/reject関数に渡された引数をそのまま使用できるようになる。
通常はthen/catchなどでコールバック関数を記載しないと引数を取り出せないため処理が簡略化できる。
await キーワードを使用したpromiseがrejectされた場合はtry-catchでエラーハンドリングを行う。

```Javascript:async/await
let p1 = new Promise((resolve,reject) => {resolve('p1');});
let p3 = new Promise((resolve,reject) => {reject('p3');});

//Promiseを使う場合
p1
.then(result => console.log(result)) //p1
.catch(err => console.log(err));

p3
.then(result => console.log(result)) 
.catch(err => console.log(err));  // p3

//async/awaitを使う場合
try {
  console.log(await p1); // p1
} catch (e) {
  console.log(e);
}

try {
  console.log(await p3);
} catch (e) {
  console.log(e); // p3
}
```

###Promiseを返すメソッド例

**const fetchResponsePromise = fetch(resource [, init])**
resource: 取得したいリソースのURL
init: リクエストに設定したい内容を含むオブジェクト
resouceで設定したURLに対してHTTPリクエストを実行する。
何もinitに指定がなければGETで実行する。
実行するとPromiseを返す。
※下記の例はchromeのブラウザから実行しているのでCORSのエラーが出る


```Javascript:fetch
fetch('https://www.google.co.jp')
.then(res=> console.log(res))
.catch(err => console.log(err));
// TypeError: Failed to fetch

//もしくは
try {
    console.log(await fetch('https://www.google.co.jp'));
} catch (e) { 
    console.log(e); // TypeError: Failed to fetch
}
```

##ブラウザとイベント

不安なところだけ抜粋

###DOM

**Windowオブジェクト**
ユーザーの現在のウィンドウに関する情報を保持しているほか、ページ遷移などのメソッドも持つ。

+ `window.location.href = "https://www.google.com"`で指定したURLへ遷移できる。
+ `window.open("https://www.google.com");`で指定したURLへ遷移できる。
+ `window.history.back/window.history.forward`でブラウザでいう前に戻る/進むボタンと同じ動作を行う


**documentオブジェクト**
HTMLの操作に必要なメソッドなどを持つ。

基本的な`querySelector()`のセレクタ指定方法。
これらを組み合わせれば大体のものは検索できる

+ `querySelector("h2")`: h2タグの要素
+ `querySelector("#id")`: id="id"を持つ要素
+ `querySelector(".cls")`: class="cls"を持つ要素
+ `querySelector('[name="name1"]')`: [name="name1"]を持つ要素
+ `querySelector('h2[name="name1"]')`: [name="name1"]を持つh2要素
+ `querySelector('div.div1 h2[name="name1"]')`: class="div1"を持つdivタグの子供である[name="name1"]を持つh2要素

**HTMLElementオブジェクトのプロパティ**

+ innerHTML: 指定した要素の中身を示す。HTMLで記載するとそれが反映される
+ innerTEXT: 指定した要素の中身を示す。HTMLタグは解析されずそのまま表示される
+ dataset: data-接頭辞を付けた属性を取得するのに使う。`data-foo="bar"`と設定していた場合`dataset.foo`で"bar"を取得可能
+ tagName: tagの名前を示す


###javascriptでの要素の変更

**var element = document.createElement(tagName[, options]);**
tagNameに作成したいタグ名を文字列で指定するとそのタグの要素を作ることができる。

**let insertedNode = parentNode.insertBefore(newNode, referenceNode)**
parentNode配下にある、referenceNodeの直前にnewNodeを追加する。
referenceNodeにnullを指定すると最後に追加される。

```Javascript:insertBefore
let parent = document.querySelector('div');
let reference = document.getElementById('p2');
let newNode1 = document.createElement('input');
let newNode2 = document.createElement('button');
parent.insertBefore(newNode1, reference);
parent.insertBefore(newNode2, null);
```

```HTML
<div>
  <p id="p1">p1</p>
  <p id="p2">p2</p>
</div>
<!--　上記HTMLが以下のようになる -->
<div>
  <p id="p1">p1</p>
  <input>
  <p id="p2">p2</p>
  <button></button>
</div>
```
<br/>

**var aChild = element.appendChild(aChild)**
elementの直下(一番後ろ)に要素を追加する。

```Javascript:
let div = document.querySelector('div');
div.appendChild(document.createElement('input'));
```

```HTML
<div>
  Content
<div>
<!--　上記HTMLが以下のようになる -->
<div>
  Content
  <input></input>
<div>
```

**node.remove()**
nodeを削除する。

###DOMイベント

**eventオブジェクト**
**イベントの伝搬**
**カスタムイベントの作成**
-->時間がなく、それなりに何とかなる自信があったので省略。


###データ保持
ブラウザ側からデータを保存する際には以下3つを使用できる。

**cookie**

+ 容量は4KB
+ document.cookieで取得できる
+ HTTPリクエストを送ると一緒に送られる
+ 有効期限を任意に設定できる
+ クライアントだけでなくサーバ側でも設定可能
+ key=valueの組み合わせとなり、複数のcookieをセットする場合はセミコロンで区切る
+ cookieを削除するにはexpires=過去の日付もしくはmax-age=0を付与する


```Javascript:cookie
document.cookie = "name=test";
console.log(document.cookie); // name=test; ... すべてのクッキーが表示される

//特定のcookieを取得する
document.cookie.split(";").find(row => row.startsWith("name")).split("=")[1];
// test
document.cookie = "name=test; max-age=0";
console.log(document.cookie); // null
```

**sessionStorage**

+ 容量は5MB
+ window.sessionStorageで取得できる
+ セッションが終了するとデータは破棄される
+ `sessionStorage.setItem("key", "value");`で値を設定できる
+ `sessionStorage.getItem("key");`で値を取得できる
+ 削除するには`sessionStorage.removeItem("key");`を使用する。

```Javascript:sessionStorage
console.log(sessionStorage); // Storage {length: 0}
sessionStorage.setItem("name", "test");
console.log(sessionStorage); // Storage {name: "test", length: 1}
console.log(sessionStorage.getItem("name")); // test
sessionStorage.removeItem("name");
console.log(sessionStorage); Storage {length: 0}
```

**localStorage**

+ 容量は10MB
+ window.localStorageで取得できる
+ セッションが終了してもデータは破棄されず、明示的に削除しない限り残り続ける
+ 使い方はsessionStorageと一緒