Closed117

ES2015以降のJavaScriptにキャッチアップする

蔀

変数について

蔀

const/letを使う。
varは使わない。

constは再代入不可能な変数で、正確には定数ではないらしい。
たとえばオブジェクトをconstで定義すると、参照先のアドレスは一緒だが、中身が違うということが起こる。

// `const`でオブジェクトを定義している
const object = {
    key: "値"
};
// オブジェクトそのものは変更できてしまう
object.key = "新しい値";
蔀

エラーの見方。
雰囲気で今まで見ていたけど、改めてちゃんと説明されると、規則性がちゃんとしている

SyntaxError: missing ) after argument list[詳細] index.js:1:13
^^^^^^^^^^^  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^      ^^^^^^^^ ^^^^
エラーの種類                |                        |  行番号:列番号
                  エラー内容の説明                 ファイル名
蔀
蔀

JavaScriptは動的型付け言語に分類される言語であるため、静的型付け言語のような変数の型はありません。 しかし、文字列、数値、真偽値といった値の型は存在します。 これらの値の型のことをデータ型と呼びます。

蔀

データ型はプリミティブ型とオブジェクトに大別される。
プリミティブ型は数値や文字列、真偽値などの基本となるデータ型。
オブジェクトはデータの集合体で、参照を経由して操作する。

蔀
  • プリミティブ型(基本型)
    • 真偽値(Boolean): trueまたはfalseのデータ型
    • 数値(Number): 42 や 3.14159 などの数値のデータ型
    • 巨大な整数(BigInt): ES2020から追加された9007199254740992nなどの任意精度の整数のデータ型
    • 文字列(String): "JavaScript" などの文字列のデータ型
    • undefined: 値が未定義であることを意味するデータ型
    • null: 値が存在しないことを意味するデータ型
    • シンボル(Symbol): ES2015から追加された一意で不変な値のデータ型
  • オブジェクト(複合型)
    • プリミティブ型以外のデータ
    • オブジェクト、配列、関数、正規表現、Dateなど
蔀

なおnullにはtypeofがobjectになるバグがある

console.log(typeof null); // => "object"
蔀

テンプレートリテラルを使うと、文字列の中に変数を${}を使った挿入ができる

const str = "文字列";
console.log(`これは${str}です`); // => "これは文字列です"
蔀

テンプレートリテラルはあと改行がそのまま入れられる

蔀
蔀

厳密等価演算子(===)と等価演算子(==)は常に厳密等価演算子を使えばよさそう。
等価演算子は両辺の型が同じときはいいけども、暗黙の型変換で右辺をキャストするので、意図しない結果が生じる。

// 文字列を数値に変換してから比較
console.log(1 == "1"); // => true
// "01"を数値にすると`1`となる
console.log(1 == "01"); // => true
// 真偽値を数値に変換してから比較
console.log(0 == false); // => true
// nullの比較はfalseを返す
console.log(0 == null); // => false
// nullとundefinedの比較は常にtrueを返す
console.log(null == undefined); // => true
蔀

文字列に対して、2回NOT演算子を重ねると、空文字だった場合falseになるので、空文字判定に使うことができる。
自分で使うことはないだろうけど、昔何かのコードで見たことがある気がする

const str = "";
// 空文字列はfalsyであるため、true -> falseへと変換される
console.log(!!str); // => false

https://jsprimer.net/basic/operator/#not-operator

蔀

JSは動的型付け言語なので、色々勝手に型変換するのだが、その際に矛盾が出ても、undefinedがあるので、カバーできている(できている?)。
undefined自体はリテラルでもなんでもなく、JS内部で定義されているグローバル変数。
nullはリテラル)

https://jsprimer.net/basic/data-type/#undefined-is-not-literal

蔀
蔀

基本

// 関数宣言
function 関数名(仮引数1, 仮引数2) {
    // 関数が呼び出されたときの処理
    // ...
    return 関数の返り値;
}
// 関数呼び出し
const 関数の結果 = 関数名(引数1, 引数2);
console.log(関数の結果); // => 関数の返り値
蔀

戻り値のない(void)関数でも、returnするとundefinedを返す

function fn() {
    // 何も返り値を指定してない場合は`undefined`を返す
    return;
    // すでにreturnされているため、この行は実行されません
}
console.log(fn()); // => undefined
蔀

returnを省略してもundefinedは返す

function fn() {
}

console.log(fn()); // => undefined
蔀

呼び出し時の引数が少ないとき、指定されなかった仮引数はundefinedとして渡される。
逆に多いと、多かった引数を無視して実行される。エラーにはならない。

蔀

またES6から Rest parametersで可変長引数が扱える

function fn(...args) {
    // argsは引数の値が順番に入った配列
    console.log(args); // => ["a", "b", "c"]
}
fn("a", "b", "c");
蔀

元々JSにはargumentsという可変長引数が扱える仕組みがあったが、仕様的にガバい点が多かったので、ES6以降は非推奨となった

蔀

JSにおいて関数はオブジェクトなので、関数式というものが使える。
が、ES6以降ではアロー関数が登場したので、そっちを使った方が良さそう。

// 関数式
const 関数名 = function() {
    // 関数を呼び出したときの処理
    // ...
    return 関数の返り値;
};
蔀

アロー関数

// Arrow Functionを使った関数定義
const 関数名 = () => {
    // 関数を呼び出したときの処理
    // ...
    return 関数の返す値;
};
蔀

色々省略できる

// 仮引数の数と定義
const fnA = () => { /* 仮引数がないとき */ };
const fnB = (x) => { /* 仮引数が1つのみのとき */ };
const fnC = x => { /* 仮引数が1つのみのときは()を省略可能 */ };
const fnD = (x, y) => { /* 仮引数が複数のとき */ };
// 値の返し方
// 次の2つの定義は同じ意味となる
const mulA = x => { return x * x; }; // ブロックの中でreturn
const mulB = x => x * x;            // 1行のみの場合はreturnとブロックを省略できる
蔀

アロー関数には次の特徴がある。

  • 名前をつけることができない(常に匿名関数)
  • thisが静的に決定できる(詳細は「関数とスコープ」の章で解説します)
  • functionキーワードに比べて短く書くことができる
  • newできない(コンストラクタ関数ではない)
  • arguments変数を参照できない

基本的にはアロー関数で書けるときはアロー関数で書く、がいいよう

蔀
蔀

式(Expression)を簡潔に述べると、値を生成し、変数に代入できるものを言います。

// 1という式の評価値を表示
console.log(1); // => 1
// 1 + 1という式の評価値を表示
console.log(1 + 1); // => 2
// 式の評価値を変数に代入
const total = 1 + 1;
// 関数式の評価値(関数オブジェクト)を変数に代入
const fn = function() {
    return 1;
};
// fn() という式の評価値を表示
console.log(fn()); // => 1
蔀

文(Statement)を簡潔に述べると、処理する1ステップが1つの文と言えます。JavaScriptでは、文の末尾にセミコロン(;)を置くことで文と文に区切りをつけます。

const isTrue = true;
// isTrueという式がif文の中に出てくる
if (isTrue) {
}
蔀

一方で、式(Expression)は文(Statement)になれます。文となった式のことを式文と呼びます。 基本的に文が書ける場所には式を書けます。
その際に、式文(Expression statement)は文の一種であるため、セミコロンで文を区切っています。
式は文になれますが、先ほどのif文のように文は式になれません。

蔀

次のような、文を{と}で囲んだ部分をブロックと言います。 ブロックには、複数の文が書けます。

なるほどね

{;;
}
蔀

条件分岐について

https://jsprimer.net/basic/condition/

蔀

falsyな値、という概念があって、下記の値は真偽値に変換するとfalseになる

  • false
  • undefined
  • null
  • 0
  • 0n
  • NaN
  • ""(空文字列)
蔀

JSのswitch文はbreakが必須。
breakを忘れると、条件に合致したcase文の次以降のcase文が無条件で実行される
(この仕様はかなりイマイチだと思う)

蔀
蔀

ループに関しては特に変わったところがない気がするけど、一点だけ。
for...of文というのがES6から使える

for (variable of iterable) {
    実行する文;
}
蔀

for...in文の挙動がイマイチなので、誕生したっぽい

蔀
蔀

オブジェクトがどうしても辞書に見えるときがあったんだけど、どうもJSには連想配列という概念がないようで、同じことをしたければオブジェクトを使う、ということのよう。

蔀

オブジェクトリテラルのプロパティ名はクォート("や')を省略できる。
今までここで結構混乱していた。

蔀

あと分割代入と同じノリで、こんな書き方もできる

const name = "名前";
// `name`というプロパティ名で`name`の変数を値に設定したオブジェクト
const obj = {
    name
};
console.log(obj); // => { name: "名前" }
蔀

オブジェクトのプロパティへのアクセスはドット記法(.)を使う方法とブラケット記法([])の二通りある。

const obj = {
    key: "value"
};
// ドット記法で参照
console.log(obj.key); // => "value"
// ブラケット記法で参照
console.log(obj["key"]); // => "value"

どっちでもいいっちゃどっちでもいいが、ブラケット記法の方がキー名の制約の縛りが緩い。
基本ドット記法でいいみたい。

蔀

上述の通り、constでオブジェクト定義しても、オブジェクトの参照は変更不可能だけども、プロパティは変更できる。
変更を封じるにはObject.freezeを使う。

"use strict";
const object = Object.freeze({ key: "value" });
// freezeしたオブジェクトにプロパティを追加や変更できない
object.key = "value"; // => TypeError: "key" is read-only
蔀

存在しないプロパティにアクセスすると、JSはundefinedを返す。
しかしネストしてるプロパティ、例えばkey.child.grandchildみたいなプロパティにアクセスするときは、
key.childがundefinedだと、undefinedに対して操作を行うことになるので、TypeErrorの例外が返ってくる。

蔀

ES2020からの新機能だけども、オプショナルチェーンが使える

const obj = {
    a: {
        b: "objのaプロパティのbプロパティ"
    }
};
// obj.a.b は存在するので、その評価結果を返す
console.log(obj?.a?.b); // => "objのaプロパティのbプロパティ"
// 存在しないプロパティのネストも`undefined`を返す
// ドット記法の場合は例外が発生してしまう
console.log(obj?.notFound?.notFound); // => undefined
// undefinedやnullはnullishなので、`undefined`を返す
console.log(undefined?.notFound?.notFound); // => undefined
console.log(null?.notFound?.notFound); // => undefined
蔀

マージする方法はObject.assignを使う方法と、スプレッド構文を使う方法とがある模様。

const objectA = { a: "a" };
const objectB = { b: "b" };
const merged = {
    ...objectA,
    ...objectB
};
console.log(merged); // => { a: "a", b: "b" }

この辺はオブジェクトを連想配列として使うための機能なのかな?

蔀

オブジェクトは普通に代入すると、参照を渡すだけになってしまう
Object.assignを使うと、オブジェクトのコピーができる

// 引数の`obj`を浅く複製したオブジェクトを返す
const shallowClone = (obj) => {
    return Object.assign({}, obj);
};
const obj = { a: "a" };
const cloneObj = shallowClone(obj);
console.log(cloneObj); // => { a: "a" }
// オブジェクトを複製しているので、異なるオブジェクトとなる
console.log(obj === cloneObj); // => false

が、これは浅いコピーなので、オブジェクトをネストしたプロパティまではコピーされない。
もし深いコピーがしたければ、開発者側でそういうコードを書く必要がある。

蔀

toString()hasOwnProperty()などがどう実現しているかというと、プロトタイプオブジェクトという仕組みがある。
JSのオブジェクトは全てObjectを継承しており、(※継承しない方法もあるが特殊)
ObjectObject.prototypeという特殊なプロパティを持っている。

https://jsprimer.net/basic/prototype-object/

蔀
蔀

任意のオブジェクトが配列かどうかを判断するにはArray.isArrayを使う。typeofはobjectまでしかわからない。

蔀

配列の結合はconcatを使う。
ちなみに演算子を使うと悲しいことになった

const array = ["A", "B", "C"];
const array2 = ["D", "E"];
console.log(array + array2); // A,B,CD,E
蔀

ES2015から、スプレッド構文を使うと多少スタイリッシュに書ける

const array = ["A", "B", "C"];
const array2 = ["D", "E"];
console.log([...array, ...array2]); // [ 'A', 'B', 'C', 'D', 'E' ]
蔀

インデックス指定での要素の削除はArray#spliceを使う。lengthを使ったやり方もある

const array = [1, 2, 3];
array.length = 0; // 配列を空にする
console.log(array); // => []
蔀

引数なしのslice()concat()で配列のコピーがつくれる

const copiedArray = myArray.slice();
蔀

JSの高階関数、コールバックに要素・インデックス・配列そのものの3つの引数をとるとのこと。
思ってたのとちょっと違った

const array = [1, 2, 3];
array.forEach((currentValue, index, array) => {
    console.log(currentValue, index, array);
});
// コンソールの出力
// 1, 0, [1, 2, 3]
// 2, 1, [1, 2, 3]
// 3, 2, [1, 2, 3]
蔀

(まあでもコールバックの第二引数以降を無視したらSwiftと一緒か)

蔀
蔀

JavaScript(ECMAScript)は文字コードとしてUnicodeを採用し、文字をエンコードする方式としてUTF-16を採用しています。

蔀

JSの文字列も、文字の配列として操作するインターフェイスがある

蔀

正規表現を使うため、二通りの方法がある

// 正規表現リテラルで正規表現オブジェクトを作成
const patternA = /パターン/フラグ;
// `RegExp`コンストラクタで正規表現オブジェクトを作成
const patternB = new RegExp("パターン文字列", "フラグ");
蔀

使い分けはまずリテラルで、変数を使いたいなど、ダメな理由があるならRegExp、で良さそう

このように、RegExpコンストラクタは文字列から正規表現オブジェクトを作成できますが、特殊文字のエスケープが必要となります。 そのため、正規表現リテラルで表現できる場合は、リテラルを利用したほうが簡潔でパフォーマンスもよいです。 正規表現のパターンに変数を利用する場合などは、RegExpコンストラクタを利用します。

蔀

正規表現、どうしても苦手意識がある

const str = "/正規表現のような文字列/";
// 正規表現で`/`からはじまり`/`で終わる文字列のパターン
const regExpLikePattern = /^\/.*\/$/;
// RegExp#testメソッドでパターンにマッチするかを判定
console.log(regExpLikePattern.test(str)); // => true
// Stringメソッドで、`/`からはじまり`/`で終わる文字列かを判定する関数
const isRegExpLikeString = (str) => {
    return str.startsWith("/") && str.endsWith("/");
};
console.log(isRegExpLikeString(str)); // => true
蔀

文字列はimmutableなので、let指定でも変更ができない。つまり削除もできない
String#replaceを使って、新しい文字列をつくることはできる。
空文字にreplaceすることで削除を表現できる……

const str = "文字列";
// "文字"を""(空文字列)へ置換することで"削除"を表現
const newStr = str.replace("文字", "");
console.log(newStr); // => "列"

ただこれ意味あるのかな? よくわからない

蔀

イマイチメリットが飲み込めてないんだけど、タグつきテンプレート関数というのがES2015から使えるみたい。

// 変数をURLエスケープするタグ関数
function escapeURL(strings, ...values) {
    return strings.reduce((result, str, i) => {
        return result + encodeURIComponent(values[i - 1]) + str;
    });
}

const input = "A&B";
// escapeURLタグ関数を使ったタグつきテンプレート
const escapedURL = escapeURL`https://example.com/search?q=${input}&sort=desc`;
console.log(escapedURL); // => "https://example.com/search?q=A%26B&sort=desc"

これについては、そういうものがある、って理解で今はいいか

蔀
蔀

ビルトインオブジェクトには、大きく分けて2種類のものがあります。 1つ目はECMAScript仕様が定義するundefinedのような変数(「undefinedはリテラルではない」を参照)やisNaNのような関数、ArrayやRegExpなどのコンストラクタ関数です。2つ目は実行環境(ブラウザやNode.jsなど)が定義するオブジェクトでdocumentやmoduleなどがあります。 どちらもグローバルスコープに自動的に定義されているという点で大きな使い分けはないため、この章ではどちらもビルトインオブジェクトと呼ぶことにします。

蔀

グローバル変数とビルトインオブジェクトで同じ変数名を定義したら、グローバル変数が優先して参照される

蔀

意図せずにビルトインオブジェクトの変数を隠蔽してしまうリスクを犯さないためにはどうしたらいい?←グローバル変数を極力使わない

蔀

かつて(varしかなかった時代)は即時実行関数なんてものもあったらしいが、今やconst/letを使え、で歴史的遺物以外の意味はない模様

// 匿名関数を宣言 + 実行を同時に行っている
(function() {
    // 関数のスコープ内でfoo変数を宣言している
    var foo = "foo";
    console.log(foo); // => "foo"
})();
// foo変数のスコープ外
console.log(typeof foo === "undefined"); // => true
蔀

JSは基本的に静的スコープで名前解決をする。

const x = 10; // *1

function printX() {
    // この識別子`x`は常に *1 の変数`x`を参照する
    console.log(x); // => 10
}

function run() {
    const x = 20; // *2
    printX(); // 常に10が出力される
}

run();

ただしthisは例外で、動的スコープらしい。
Reactチュートリアルでよくわからなかったところだ

https://jsprimer.net/basic/function-scope/#static-scope

蔀

カプセル化とかクロージャーとかその辺のプログラミング思想の話ってイマイチ体系的に学べてない気がする

蔀
蔀

トップレベルに記述されたthis

  • 実行コンテキストが"Script": グローバルオブジェクト(windowとかglobalとか)
  • 実行コンテキストが"Module": undefined
蔀

関数のthisはアロー関数とそれ以外で異なる

蔀

アロー関数以外の関数

  • メソッドじゃない(オブジェクト配下じゃない)関数は、strictモードだとundefined。strictモードじゃないと、グローバルオブジェクトを参照するように変換される親切(?)仕様

https://jsprimer.net/basic/function-this/#function-declaration-expression-this

  • メソッドのthisはベースオブジェクトを参照する(これはシンプル)。自分が定義されたオブジェクトを返す。ネストされていたとしても、自分の一個上のオブジェクトを返す
蔀

対策

1つはメソッドとして定義されている関数はメソッドとして呼ぶということです。 メソッドをわざわざただの関数として呼ばなければそもそもこの問題は発生しません。

もう1つは、thisの値を指定して関数を呼べるメソッドで関数を実行する方法です。

蔀

call、apply、bindメソッドの3つがある

https://jsprimer.net/basic/function-this/#call-apply-bind

call/applyの違いは関数の引数への値の渡し方が違うだけ。
また、call/applyは元の関数定義を変更する。

bindはthisを束縛した関数を返すので、非破壊的。
Reactでbind使ったのはたぶんそういうこと。

蔀

JSでは、ES2015でアロー関数が導入されて、アロー関数は自分の外のスコープのthisを探索するので、アロー関数を使えば解決する

蔀

クラス(時間なくなってきた)

https://jsprimer.net/basic/class/

蔀

ES2015からclassが登場した

class Point {
    // コンストラクタ関数の仮引数として`x`と`y`を定義
    constructor(x, y) {
        // コンストラクタ関数における`this`はインスタンスを示すオブジェクト
        // インスタンスの`x`と`y`プロパティにそれぞれ値を設定する
        this.x = x;
        this.y = y;
    }
}
蔀

関数とメソッドに大きな違いはない、というけれど、この省略は結構大きい気がする

蔀

プロパティにGet/Setが書ける

class クラス {
    // getter
    get プロパティ名() {
        return;
    }
    // setter
    set プロパティ名(仮引数) {
        // setterの処理
    }
}
const インスタンス = new クラス();
インスタンス.プロパティ名; // getterが呼び出される
インスタンス.プロパティ名 =; // setterが呼び出される
蔀

クラスをnewしてインスタンスオブジェクトをつくるが、その際にインスタンスは元になったクラスのプロトタイプオブジェクトの参照を[[Prototype]]に持つ。
インスタンスはプロパティ・メソッドを参照する際に[[Prototype]]まで探索する。

この仕組みをプロトタイプチェーンと呼ぶ。

蔀

普段は、プロトタイプオブジェクトやプロトタイプチェーンといった仕組みを意識する必要はありません。 class構文はこのようなプロトタイプを意識せずにクラスを利用できるように導入された構文です。 しかし、プロトタイプベースである言語のJavaScriptではクラスをこのようなプロトタイプを使って表現していることは知っておくとよいでしょう。

https://jsprimer.net/basic/class/#read-prototype-chain

蔀

JSには言語使用上クラスが存在しないが、実用上クラスの書き方をしていたので、
なんとかしてクラスをJSにも導入できないかということで、プロトタイプベースのやや特殊なクラスの実装になったという経緯があるみたい。
ES2015でclassが導入されたが、内部的には関数オブジェクトなので、単なるシンタックスシュガーであるとも言える

蔀

プロトタイプベースで、継承の仕組みも実現している。
次の順で名前解決を図る。

  1. instanceオブジェクト自身
  2. Child.prototype(instanceオブジェクトの[[Prototype]]の参照先)
  3. Parent.prototype(Child.prototypeオブジェクトの[[Prototype]]の参照先)

https://jsprimer.net/basic/class/#prototype-inheritance

蔀
蔀

ちょっと重い内容なので、まずは読む。

蔀

https://jsprimer.net/basic/async/#promise-chain

catchメソッドはFulfilled状態(成功)のPromiseインスタンスを作成して返す。
これはなんか混乱する……

Promise.reject(new Error("エラー")).catch(error => {
    console.log(error); // Error: エラー
}).then(() => {
    console.log("thenのコールバック関数が呼び出される");
});
蔀

もしRejectedで返したいなら、return Promise.reject(error);みたいにするのが必要とのこと

蔀

非同期処理の実装として、3つあり、古い順に並べると、下記。

  • エラーファーストコールバック
  • Promise
  • Async Function
蔀

ES2015まではコールバックによる方法しかなかった。
エラーファーストコールバックはその名のとおり、コールバックの第一引数にエラーを入れて、呼び出し側はまずnullチェックする、というもの。
悪名高いコールバック地獄を生むことにもなった。

蔀

使用例。Promiseは

  • Pending: 初期状態
  • Fulfilled: 成功
  • Rejected: 失敗

の3つの状態を持っている。

/**
 * 1000ミリ秒未満のランダムなタイミングでレスポンスを疑似的にデータ取得する関数
 * 指定した`path`にデータがある場合、成功として**Resolved**状態のPromiseオブジェクトを返す
 * 指定した`path`にデータがない場合、失敗として**Rejected**状態のPromiseオブジェクトを返す
 */
function dummyFetch(path) {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            if (path.startsWith("/success")) {
                resolve({ body: `Response body of ${path}` });
            } else {
                reject(new Error("NOT FOUND"));
            }
        }, 1000 * Math.random());
    });
}
// `then`メソッドで成功時と失敗時に呼ばれるコールバック関数を登録
// /success/data のリソースは存在するので成功しonFulfilledが呼ばれる
dummyFetch("/success/data").then(function onFulfilled(response) {
    console.log(response); // => { body: "Response body of /success/data" }
}, function onRejected(error) {
    // この行は実行されません
});
// /failure/data のリソースは存在しないのでonRejectedが呼ばれる
dummyFetch("/failure/data").then(function onFulfilled(response) {
    // この行は実行されません
}, function onRejected(error) {
    console.log(error); // Error: "NOT FOUND"
});

resolve(任意のオブジェクト)で成功したときに渡すデータが指定できて、受ける側は.then(関数)で成功時の処理を記述する。
then()は引数にonFulfilled/onRejectedを持っていて、onRejectedを指定すると失敗時/エラー時も記載できる。
エラーだけ書くための構文として、catch()も用意されている。

蔀

Promiseの書き方だけで一冊本書けそうなレベルで色々あるけど、

  • チェーンできる
  • Promise.allで複数のPromiseをまとめられる
  • Promise.raceで複数のPromiseをまとめて、その中でもっとも応答が早いものを採用できる

あたりが大事

蔀

Promiseも構文が複雑で、ごちゃごちゃする。
その解決を目指して、ES2017からasync/awaitが導入された。
といってもasyncもPromiseを使っているので、Promiseの挙動を理解する必要はある。

asyncをつけた関数は、下記の挙動となる

  • Async Functionが値をreturnした場合、その返り値を持つFulfilledなPromiseを返す
  • Async FunctionがPromiseをreturnした場合、その返り値のPromiseをそのまま返す
  • Async Function内で例外が発生した場合は、そのエラーを持つRejectedなPromiseを返す
蔀

awaitはasync関数の中でだけ使えて、処理の完了まで後続の実行を待つ。

async function asyncMain() {
    // PromiseがFulfilledまたはRejectedとなるまで待つ
    await Promiseインスタンス;
    // Promiseインスタンスの状態が変わったら処理を再開する
}
蔀

上手いこと使えば、Promiseチェーンのコールバック地獄を、同期処理のようなスタイルで記述できて気持ちがいい……らしい。

蔀
蔀

Mapはマップ型のコレクションを扱うためのビルトインオブジェクトです。 マップとは、キーと値の組み合わせからなる抽象データ型です。 他のプログラミング言語の文脈では辞書やハッシュマップ、連想配列などと呼ばれることもあります。

JSにも連想配列ちゃんとできたんだ

蔀

オブジェクトを連想配列代わりに使わなくていいのか〜と思ったら、そういうわけでもなさそう。
JSONのパースなんかはオブジェクトでやる想定のライブラリが多いので、オブジェクトでやるしかなさげ

https://jsprimer.net/basic/map-and-set/#object-and-map

蔀

第一部終わり。
第二部まで手動かしながらやると力つくんだろうけど、あとは実戦でやってくつもりなので、スキップで……

蔀

個人的にキツかった(時間かかった)ポイント

普通の関数の説明もまあまあ長いのに、そこにアロー関数も入ってくるから結構大変。

プロトタイプチェーン周りの説明が難しい

thisはなんか純粋に難しかった。
アロー関数とそれ以外の関数でスコープの概念違うのがホントに実用上混乱しそう。

これもプロトタイプチェーン周りがむずい

Promiseがひたすら難しい

蔀

こう見ると、

  • 関数
  • オブジェクト
  • クラス
  • thisの扱い
  • Promise

あたりがJavaScriptの難解なポイントみたいだ

このスクラップは2021/06/07にクローズされました