Closed23

#jsprimer 読み直し

bufferingsbufferings

https://jsprimer.net/basic/data-type/#integer-literal

  • 2^53-1 (9007199254740991)
  • 2進数: 0b01010
  • 8進数: 0o644
  • 16進数: 0x1234a

https://jsprimer.net/basic/data-type/#numeric-separators

  • ES2021 から区切り文字としてのアンダースコアが使える 1_000_000_000_000

https://jsprimer.net/basic/data-type/#double-quote-and-single-quote

  • いつも Groovy とかと混ざってしまうけど、'" の文字列は同じ

テンプレートリテラル

  • 変数の展開(${xxx})が使えるのはテンプレートリテラル
  • テンプレートリテラルは3つ重ねなくても( ``` ってしなくても)改行が使える
  • インデントはそのまま反映される(Javaとは違う)
bufferingsbufferings

https://jsprimer.net/basic/operator/#unary-plus-operator

// 自分自身とも一致しない
console.log(NaN === NaN); // => false
// Number型である
console.log(typeof NaN); // => "number"
// Number.isNaNでNaNかどうかを判定
console.log(Number.isNaN(NaN)); // => true

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

falsy values

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

https://jsprimer.net/basic/operator/#nullish-coalescing-operator

nullish とは、評価結果が null または undefined となる値のことです。

falsy じゃないんだな。だから 0false や空文字を渡しても、右辺の値は使われない

console.log(false ?? "右辺の値"); // => false
console.log(0 ?? "右辺の値"); // => 0
console.log(("" ?? "a") === ""); // => true

へー。

あぁ、そうか。|| が falsy だから Nullish coalescing operator ができたんだ(すぐ後に書いてあった

bufferingsbufferings

文字列への変換

  • String コンストラクター関数を使うと文字列に変換できる
  • プリミティブに留めておくと良さそう

文字列→数値への変換

  • Number コンストラクター関数
  • Number.parseInt(文字列, 基数) も利用可。Number.parseFloat もある。
  • 変換できなかった場合 NaN になるので、結果が Number.isNaN かどうかを確認

NaN

  • number型
  • 計算に出てくると結果は NaN になる
  • NaN 同士の比較は false になる
  • チェックは isNaN または Number.isNaN でできる
bufferingsbufferings

https://jsprimer.net/basic/function-declaration/

宣言

  • 同じ名前の関数宣言は上書きされる

返り値

  • return; や、return なしの場合は、返り値は undefined

引数

  • 指定されなかった引数は undefined
  • デフォルト引数(ES2015)は undefined に対してのみ適用される。null を渡した場合はデフォルト値になることはなく null が使用される
    • デフォルト引数登場前は || がよく使われていたがこれには falsy の問題がある。
    • もし似たようなことをやりたいなら ES2020 で登場した ?? を使う方が良い。けど、デフォルト値で済むはず
  • Rest Parameters [ES2015]
  • Destructuring assignment [ES2015]

関数式

  • 通常は匿名関数でいいけど、再帰を使うときは名前をつければ内部からはそれで呼び出せる。外部からは呼び出せないので、変数名の方で呼び出す。
  • Arrow Function は名前を持つことができない

メソッド

  • メソッドは ES2015 で短縮記法が導入された
const obj = {
    method() {
        return "this is method";
    }
};
bufferingsbufferings

文と式、条件分岐、は問題なし。

反復処理のところは、for...in 文を使わない、のと

https://jsprimer.net/basic/loop/#for-of-statement

  • Symbol.iterator という名前のメソッドを実装したオブジェクトは iterable と呼ばれる
  • iterable オブジェクトは for...of 文で反復処理できる

くらいかな。

bufferingsbufferings

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

キーと値をコロンで区切る

const obj = {
    "key": "value"
};

プロパティ名のクォートは省略できる

const obj = {
    key: "value"
};

(変数の識別子として利用できないプロパティ名はクォートが必要)

複数の場合はカンマ区切り

const color = {
    red: "red",
    green: "green",
    blue: "blue"
};

プロパティ名と変数名が同じ場合は省略記法が使える(ES2015)

const name = "名前";
const obj = {
    name
};

プロパティへのアクセス

  • ドット記法とブラケット記法が使える
  • けど、キーが識別子の命名規則を満たさない場合はドット記法は使えなくてブラケット記法のみ
  • ブラケット記法の場合は変数も使える
  • シンボルをキーにする場合もブラケット記法になる

Computed property names (ES2015)

ブラケット記法でキーの部分に変数を使用することができる

const key = "key-string";
const obj = {
    [key]: "value"
};

プロパティの追加

存在しないプロパティに値をいれると作成される。やらないほうがいいけど。

プロパティの削除

削除したい場合は delete 演算子でできる

delete obj.key1;

Object.freeze

  • strict mode のみ
  • Object.freeze したあとにプロパティの追加や変更をすると例外が発生するようになる

プロパティの存在確認

in 演算子を使う

if ("key" in obj) {

hasOwnProperty を使う

if (obj.hasOwnProperty("key")) {

in はプロトタイプオブジェクトまで見に行く。 hasOwnProperty はそのオブジェクト自身が持っているかを判定する。

Optional chaining 演算子

  • ES2020
  • nullish なら undefined を返す
const title = widget?.window?.title ?? "未定義";
console.log(languages?.[langJapanese]?.[messageKey]); // => "こんにちは!"

toString メソッド

実はStringコンストラクタ関数は、引数に渡されたオブジェクトのtoStringメソッドを呼び出しています。 そのため、Stringコンストラクタ関数とtoStringメソッドの結果はどちらも同じになります。

そっか。プリミティブは String コンストラクタ、オブジェクトは toString って感じの使い分けが読みやすそうかな。

プロパティ名は文字列化される

Symbol 以外。へー。

列挙

  • Object.keys
  • Object.values (ES2017)
  • Object.entries (ES2017)
console.log(Object.keys(obj)); // => ["one", "two", "three"]
console.log(Object.values(obj)); // => [1, 2, 3]
console.log(Object.entries(obj)); // => [["one", 1], ["two", 2], ["three", 3]]

オブジェクトのマージ

  • Object.assign はもう使わなさそう。spread 構文を使う
  • spread 構文は、オブジェクトリテラルの中だけで使用可能
const objectA = { a: "a" };
const objectB = { b: "b" };
const merged = {
    ...objectA,
    ...objectB
};
console.log(merged); // => { a: "a", b: "b" }

プロパティ名がかぶった場合は、後ろのオブジェクトが優先される

Shallow Clone も spread で書けば良さそう

bufferingsbufferings

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

  • オブジェクトはObject.prototypeを継承することでtoStringメソッドなどを呼び出せるようになってる
  • プロトタイプメソッドとインスタンスメソッドではインスタンスメソッドが優先される
  • Object.createメソッドを使うことでプロトタイプオブジェクトを継承しないオブジェクトを作成できる
    • でも Map が ES2015 で導入されたから Map が使いやすいところでは Map を使うと良さそう
    • それでもオブジェクトリテラルなどが便利なところは多いのでオブジェクトをマップっぽく使うのは続きそう
bufferingsbufferings

https://jsprimer.net/basic/array/

  • インデックスは 0 以上 2^32 - 1 未満の整数
  • 存在しないインデックスへのアクセスは undefined を返す
  • sparse array (疎な配列)は、未定義の要素が含まれている
    • hasOwnProperty を使えば要素自体の存在判定ができる
  • 配列かどうかは Array.isArray でチェック
    • typeof では判別できない。 "object" になるため。
  • 分割代入できる

検索

インデックスを取得

  • Array#indexOf : === で比較。なければ -1 を返す
  • Array#findIndex (ES2015) : Predicate を渡す。なければ -1 を返す

要素を取得

  • Array#find (ES2015) : Predicate を渡す。なければ undefined を返す

指定範囲の要素を取得

  • slice を使う
    • 新しい配列が作られるが、その要素は Shallow コピーなところに気をつけておく

含まれているかどうか

  • indexOf ではなく includes (ES2016) を使えば、真偽値が返される
  • Predicate を使いたい場合は some を使う

追加と削除

  • push/pop 末尾
  • unshift/shift 先頭

結合

  • Array#concat があるけど、ES2015 からは spread 構文を使うだけで良さそう
const array = ["A", "B", "C"];
const newArray = ["X", ...array, "Z"];
console.log(newArray); // => ["X", "A", "B", "C", "Z"]

フラット化

  • Array#flat (ES2019) で、フラット化ができる
  • 引数なしだと1段階。すべてをフラット化する場合は Infinity を指定する

削除

  • Array#splice で削除できる。同時に追加もできる。
  • 全部削除するなら length0 にすれば良い。その要素数までに配列が切り詰められる
  • もしくは空の配列をアサインすればいい

Array-like オブジェクト

  • length を持つ
  • インデックスアクセスができる
  • isArray は false
  • Array.from (ES2015) を使えば配列に変換できる
const argumentsArray = Array.from(arguments);
bufferingsbufferings

https://jsprimer.net/basic/string/

一部を取得

  • slice, substring それぞれの挙動の違いに注意して使う

検索

  • indexOf, lastIndexOf

真偽値

  • startsWith, endsWith, includes (いずれも ES2015)

正規表現

  • new RegExp だと呼び出した時点で生成、リテラル (e.g. /a+/) だとパースした時点で生成され不正な場合は構文エラーになる
  • new RegExp だと動的に正規表現を決めることができる

→基本はリテラルで、動的にしたい場合だけコンストラクターかな

正規表現で検索

  • search で検索してインデックスを取得できる
  • match はマッチした文字列に関する情報を返す
    • マッチしない場合は null
    • マッチした場合は結果を含む特殊な配列を返す
  • matchAll (ES2020) g フラグ付き用。マッチした結果を Iterator で返す
    • 以前は RexExp#exec でやってたけど、今は matchAll を使えば良い

正規表現で真偽値

  • RegExp#test がある。

文字列の置換

  • String#replace
    • 渡した文字列を置換文字列で置換する。最初に一致したものだけを置換
    • 正規表現を指定することもできる。g フラグを使ったら全部置換
  • String#replaceAll (ES2021)
    • 指定した文字列に一致するものすべてを置換
    • 正規表現も指定できる
  • どちらもコールバック関数を渡すことで、キャプチャに対する置換ができる
const result = "今日は2017-03-01です".replace(
  		/(\d{4})-(\d{2})-(\d{2})/g, (all, year, month, day) => {
        return `${year}${month}${day}`;
    });
console.log(result); // => "今日は2017年03月01日です"

タグつきテンプレート関数

() を使わずに 関数`テンプレート` とすると (strings, ...values) の形で引数が渡される

  • 第一引数にはテンプレートの中身が ${} で区切られた文字列の配列
  • 第二引数以降は ${} の中に書いた式の評価結果

これによって、処理を付け加えることができる。

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

String.raw (ES2015) を使うとそのまま返す

console.log(String.raw`template ${0} literal ${1}`); // => "template 0 literal 1"

Appolo の GraphQL のやつで使われてた気がするー。

bufferingsbufferings

https://jsprimer.net/basic/string-unicode/

  • Code Unit = UTF-16 の単位(16ビット = 2バイト)
  • Code Point = 文字に対応
    • サロゲートペアの場合は2つのCode Unitの組み合わせ(4バイト)

基本的には文字列はCode Unit単位で扱われる

console.log("🍎".length); // => 2

ES2015からCode Pointごとに扱うメソッドや構文が追加されている

  • CodePoint を名前に含むメソッド
  • u フラグが有効化されている正規表現
  • 文字列のIteratorを扱うもの (Destructing, for...of, Array.from など)

u フラグが有効化されている正規表現

const [all, fish] = "𩸽のひらき".match(/(.)のひらき/u);
console.log(all); // => "𩸽のひらき"
console.log(fish); // => "𩸽"

へー。u 常につけてて良さそう。

length

length プロパティには、Code Unit単位の長さが入ってるので、文字列の長さを知りたければ Array.from で一回Code Point単位の配列に変換してあげる:

console.log("リンゴ🍎".length) // => 5
console.log(Array.from("リンゴ🍎").length); // => 4
bufferingsbufferings

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

JavaScriptでは、プリミティブ型の値に対してプロパティアクセスするとき、自動で対応するラッパーオブジェクトに変換されます。

そっかー!

一方、明示的に作成したラッパーオブジェクトからプリミティブ型の値を取り出すこともできます。
ラッパーオブジェクト.valueOf メソッドを呼び出すことで、ラッパーオブジェクトから値を取り出せます。

ふむふむ

この2つを明示的に使い分ける利点はないため、常にリテラルを使うことを推奨します。

すっきりしたー!!

じゃ、ラッパーオブジェクトの new をすることはなくて、コンストラクター関数で変換するのに使うくらいか。

bufferingsbufferings

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

  • var の巻き上げ (hositing) は、一番近い関数またはグローバルスコープ
  • 関数宣言も一番近い関数またはグローバルスコープに巻き上げられる

即時実行関数

jQuery とかでよく見たやつ

(function() {
    // 関数のスコープ内でfoo変数を宣言している
    var foo = "foo";
    console.log(foo); // => "foo"
})();

var だとブロック内で宣言しても巻き上げられちゃうから↑みたいにしないといけなかったけど、ES2015 で letconst が導入されたから、ブロックスコープでやればいい

{
    const foo = "foo";
    console.log(foo); // => "foo"
}
bufferingsbufferings

https://jsprimer.net/basic/function-this/

Arrow Function 以外

  • メソッドとして定義されている関数はメソッドとして呼ぶこと

どうしても this を固定したい場合には

関数.call(thisの値, ...関数の引数);
関数.apply(thisの値, [関数の引数1, 関数の引数2]);

で呼び出すか

関数.bind(thisの値, ...関数の引数); // => thisや引数がbindされた関数

で束縛した関数を作り出す

Arrow Function

  • ES2015 より前は var that=this みたいにしてた
  • けど、ES2015 では Arrow Function が導入されたのでこれを使えば良い
  • Arrow Function自体は this を持たず、外側の this を参照する
  • これは関数の定義時に Static に決まる

自分の結論

  • this はオブジェクトかクラスの中だけで使うのが良さそう
  • コールバック関数には Arrow Function を使えば良さそう
bufferingsbufferings

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

クラス宣言

class MyClass {
    constructor() {
    }
}

クラス式

const MyClass = class MyClass {
    constructor() {}
};

const AnonymousClass = class {
    constructor() {}
};

コンストラクタ関数

  • コンストラクタ関数は省略可能
    • 省略すると空のコンストラクタ関数が定義される
  • コンストラクタ関数は new 演算子でインスタンス化する際に呼び出される。

プロパティ

  • コンストラクタ関数内では this は、これから新しく作るオブジェクトを指す
  • this にプロパティを設定することでクラスにプロパティを設定する

コンストラクタ関数は直接は呼び出せない

MyClass(); // => TypeError: class constructors must be invoked with |new|
  • コンストラクタ関数では return 文で値を返すべきではない
  • 慣習としてクラス名は大文字で始める

ES2015以前はクラス構文がなかったので、関数でクラスを表現していた

  • ただ、それだと関数として呼び出せてしまうので、クラス構文を使ったほうが良い

プロトタイプメソッド

  • オブジェクトリテラルと異なり、key: fun の形式では定義できない
  • また、メソッド同士の間にカンマは入れない
class Counter {
    constructor() {
        this.count = 0;
    }
    increment() {
        this.count++;
    }
}

インスタンスに対するメソッド

  • this に対してメソッドを定義すればよい
  • Arrow Function を使うことができる(ので this を bind できる
  • プロトタイプメソッドよりインスタンスメソッドが優先される
class Counter {
    constructor() {
        this.count = 0;
        this.increment = () => {
            this.count++;
        };
    }
}

アクセッサプロパティ

  • get set でアクセッサプロパティを定義できる
  • それによって、値の取得や設定時に別の処理を差し込むことができる
class NumberWrapper {
    constructor(value) {
        this._value = value;
    }
    get value() {
        console.log("getter");
        return this._value;
    }
    set value(newValue) {
        console.log("setter");
        this._value = newValue;
    }
}

ES2021 時点では、プライベートプロパティを定義できなので、直接読み書きしてほしくないプロパティを _value のように習慣としてアンダーバーで開始する(けどただの習慣なので普通にアクセスはできる

#value みたいにしてプライベートなプロパティやメソッドを定義できるようになりそうではある)

静的メソッド(クラスメソッド)

  • メソッド名の前に static をつければ良い
  • 静的メソッドの this はクラス自身を指す。継承してる場合は子クラスが入るっぽい。それは便利そう。
  • 静的メソッドも子クラス経由で呼び出せる(プロトタイプチェーンによる
  • super 経由で親の静的メソッドも呼び出せるみたいだけど・・・僕は使わなさそう
bufferingsbufferings

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

Map

初期化

const map = new Map([["key1", "value1"], ["key2", "value2"]]);

サイズプロパティ

map.size

追加と取り出し

map.set("key", "value1");
map.get("key");

チェック

map.has("key");

削除

map.delete("key");
map.clear();

反復処理

map.forEach((value, key) => {
    results.push(`${key}:${value}`);
});

for (const key of map.keys()) {
    keys.push(key);
}

for (const [key, value] of map.entries()) {
    entries.push(`${key}:${value}`);
}

// map.entries() と同じ
for (const [key, value] of map) {
    results.push(`${key}:${value}`);
}

Object と Map

Map でだいたいのことはできるけど、Object を渡される設計になっていたり、Object の方がリテラルで書きやすかったりするので、Object を使うことも多い

キーの等価性と NaN

  • Map のキーの判定は基本的には ===
  • ただし NaN だけは違っていて NaN 同士は常に等価とみなされる

Set

重複する値を持たない

// 初期化
const set = new Set(["value1", "value2", "value2"]);

// サイズプロパティ
set.size

// 追加
set.add("a");

// 確認
set.has("a")

// 削除
set.delete("a");
set.clear();

反復処理

set.forEach((value) => {
    results.push(value);
});

for (const value of set) {
    results.push(value);
}
bufferingsbufferings

https://jsprimer.net/basic/json/

JSON オブジェクトの

  • JSON.parse でパース
  • JSON.stringify(obj) で JSON に変換

JSON.stringify

https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify

JSON.stringify(value[, replacer[, space]])

replacer は関数または配列

関数の場合

関数の場合、 key と文字列化される value の2つの引数を取ります。キーをもつオブジェクトが replacer では this 引数として提供されます。
最初、 replacer 関数が、文字列化されるオブジェクト自体を表すキーとして空文字列で呼び出されます。それから、文字列化されるオブジェクトのそれぞれのプロパティや配列に対して呼び出されます。

面白い。使うときにはドキュメント見ながら UT 書かなきゃな。

配列の場合

replacer が配列である場合、その配列の値は結果の JSON 文字列に含めるプロパティの名前を示します。

space 引数

インデントを調整できる。これも使うときによもっと。

toJSON

オブジェクトが toJSON メソッドを持つ場合は JSON 化するときに、それが使われる

bufferingsbufferings

https://jsprimer.net/basic/date/

toISOString() で ISO 8601 形式の文字列に変換できる

console.log(new Date().toISOString()); // 2021-10-06T12:16:48.940Z

任意の時刻のインスタンス化

// エポックタイムからのミリ秒
new Date(1136214245999);

// ISO 8601文字列から
new Date("2006-01-02T15:04:05.999Z");

// 非推奨(ローカルのタイムゾーンに依存してしまうから)
new Date(year, month, day, hour, minutes, seconds, milliseconds);

// 代わりに UTC を使ってミリ秒を取得して生成する
const ms = Date.UTC(2006, 0, 2, 15, 4, 5, 999);
const date2 = new Date(ms);

ビルトインの Date は機能が不十分なので、ライブラリを使ったほうが良いみたい。

けど Moment.js もメンテナンスモードだし、何がいいんだろう?

https://momentjs.com/docs/#/-project-status/recommendations

Temporal が Stage3 か

https://tc39.es/proposal-temporal/docs/ja/index.html

bufferingsbufferings

https://jsprimer.net/basic/math/

使うときに MDN 見たら良いかなとは思うけど

// 三角関数
Math.sin(rad90);

// 乱数
Math.random();

// 最大最小
Math.max(...numbers);
Math.min(...numbers);

// 切り捨て・切り上げ・四捨五入
Math.floor(1.3);
Math.ceil(1.3);
Math.round(1.3);

// 小数点以下を切り落とす(ES2015)
// 負の値のときに↑の floor とは動きが異なる
Math.trunc(1.3);
bufferingsbufferings

第一部読み終わった。基本の復習はこんなところかな。

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