From 8da3603dda0a87985f53bdbfbe45a98eab23f164 Mon Sep 17 00:00:00 2001 From: 5hun Yanaura Date: Thu, 3 Jun 2021 18:09:53 +0900 Subject: [PATCH 001/208] Fix typo --- 1-js/04-object-basics/08-symbol/article.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/1-js/04-object-basics/08-symbol/article.md b/1-js/04-object-basics/08-symbol/article.md index 394b773385..ba58478248 100644 --- a/1-js/04-object-basics/08-symbol/article.md +++ b/1-js/04-object-basics/08-symbol/article.md @@ -76,7 +76,7 @@ alert(id.description); // id 例えば、サードパーティのコードに属する `user` オブジェクトを使用している場合です。 それらに "識別子" を追加したいとします。 -シンボルの気0を使用してみましょう: +シンボルのキーを使用してみましょう: ```js run let user = { // 別のコードに属しているオブジェクト @@ -90,7 +90,7 @@ user[id] = 1; alert( user[id] ); // キーとして symbol を使ってデータにアクセスできます ``` -文字列 `"id"`に対して `Symbol("id")` を使うことの利点は何でしょうか? +文字列 `"id"` に対して `Symbol("id")` を使うことの利点は何でしょうか? `user` オブジェクトは別のコードに属しており、コードはそこでも動作するので、そこに単純に任意のフィールドを追加するべきではありません。それは安全ではありません。しかし、シンボルは誤ってアクセスすることはできず、サードパーティのコードは恐らく見ることすらできないため、恐らく問題になりません。 From 954677f53eac2688814af2c6d28737009ab0056b Mon Sep 17 00:00:00 2001 From: ISHII 2bit <2bitism@gmail.com> Date: Sat, 5 Jun 2021 14:13:58 +0900 Subject: [PATCH 002/208] fixed little mistake --- 5-network/06-fetch-api/article.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/5-network/06-fetch-api/article.md b/5-network/06-fetch-api/article.md index cb4f72309f..36564e7c90 100644 --- a/5-network/06-fetch-api/article.md +++ b/5-network/06-fetch-api/article.md @@ -149,7 +149,7 @@ fetch('https://another.com/page', { ```js fetch('http://site.com/file', { - integrity: 'sha256-abd' + integrity: 'sha256-abc' }); ``` From aaa7ad7f50ac9f34b80d5940b077c728b14845f9 Mon Sep 17 00:00:00 2001 From: Kenji Ishii Date: Sun, 13 Jun 2021 17:11:57 +0900 Subject: [PATCH 003/208] Update 1-js/05-data-types/04-array/article.md --- 1-js/05-data-types/04-array/array-pop.svg | 70 +------------- 1-js/05-data-types/04-array/array-shift.svg | 102 +------------------- 1-js/05-data-types/04-array/array-speed.svg | 49 +--------- 1-js/05-data-types/04-array/article.md | 60 ++++++++++-- 1-js/05-data-types/04-array/queue.svg | 23 +---- 1-js/05-data-types/04-array/stack.svg | 23 +---- 6 files changed, 59 insertions(+), 268 deletions(-) diff --git a/1-js/05-data-types/04-array/array-pop.svg b/1-js/05-data-types/04-array/array-pop.svg index a6e8993b73..351916051a 100644 --- a/1-js/05-data-types/04-array/array-pop.svg +++ b/1-js/05-data-types/04-array/array-pop.svg @@ -1,69 +1 @@ - - - - array-pop.svg - Created with sketchtool. - - - - - - 0 - - - 1 - - - - 2 - - - - 3 - - - "Apple" - - - "Orange" - - - "Pear" - - - "Lemon" - - - length = 4 - - - - clear - - - - - 0 - - - 1 - - - - 2 - - - "Apple" - - - "Orange" - - - "Pear" - - - length = 3 - - - - \ No newline at end of file +0123"Apple""Orange""Pear""Lemon"length = 4clear012"Apple""Orange""Pear"length = 3 \ No newline at end of file diff --git a/1-js/05-data-types/04-array/array-shift.svg b/1-js/05-data-types/04-array/array-shift.svg index 5e9705bd53..09236b9d19 100644 --- a/1-js/05-data-types/04-array/array-shift.svg +++ b/1-js/05-data-types/04-array/array-shift.svg @@ -1,101 +1 @@ - - - - array-shift.svg - Created with sketchtool. - - - - 1 - - - - 2 - - - - 3 - - - "Orange" - - - "Pear" - - - "Lemon" - - - length = 4 - - - - - - 2 - - - - 3 - - - "Orange" - - - "Pear" - - - "Lemon" - - - length = 3 - - - - clear - - - move - - elements - to the left - - - - - 0 - - - "Apple" - - - - - - 0 - - - 1 - - - - 2 - - - "Orange" - - - "Pear" - - - "Lemon" - - - 1 - - - 1 - - - - \ No newline at end of file +123"Orange""Pear""Lemon"length = 423"Orange""Pear""Lemon"length = 3clearmove elements to the left0"Apple"012"Orange""Pear""Lemon"11 \ No newline at end of file diff --git a/1-js/05-data-types/04-array/array-speed.svg b/1-js/05-data-types/04-array/array-speed.svg index 091f0fa52b..5660cd5ec7 100644 --- a/1-js/05-data-types/04-array/array-speed.svg +++ b/1-js/05-data-types/04-array/array-speed.svg @@ -1,48 +1 @@ - - - - array-speed.svg - Created with sketchtool. - - - - - - 0 - - - 1 - - - - 2 - - - - 3 - - - - - pop - - - - - unshift - - - - - push - - - - - shift - - - - - - \ No newline at end of file +0123popunshiftpushshift \ No newline at end of file diff --git a/1-js/05-data-types/04-array/article.md b/1-js/05-data-types/04-array/article.md index ac68697517..8817f420d2 100644 --- a/1-js/05-data-types/04-array/article.md +++ b/1-js/05-data-types/04-array/article.md @@ -2,14 +2,12 @@ オブジェクトを使用すると、キー付きの値のコレクションを格納することができます。 -しかし、実査には頻繁に *順序付されたコレクション* が必要であることがわかります。それは、1つ目、2つ目、3つ目... と言った要素であり、例えばユーザ、商品、HTML要素など何かのリストを格納します。 +しかし、実際には多くの頻度で *順序付されたコレクション* が必要であることがわかります。それは、1つ目、2つ目、3つ目... と言った要素であり、例えばユーザ、商品、HTML要素など何かのリストを格納します。 ここでオブジェクトを使うのは便利ではありません。なぜなら、オブジェクトには要素の順序を管理するためのメソッドは提供されていないからです。既存のリストの "間に" 新しいプロパティを挿入することはできません。オブジェクトはこのように使うものではありません。 順序付けされたコレクションを格納するために、`Array` と呼ばれる特別なデータ構造があります。 -[cut] - ## 宣言 空の配列を作る2つの構文があります: @@ -327,7 +325,7 @@ for (let key in arr) { 一般的に、配列に対しては `for..in` は使うべきではありません。 -## "length" +## "length" について 配列を変更したとき、`length` プロパティは自動的に更新されます。正確には、それは配列の実際の値の数ではなく、最大の数値インデックスに1を加えたものです。 @@ -381,8 +379,6 @@ alert( arr[0] ); // undefined! 要素がありません. alert( arr.length ); // length は 2 です ``` -上のコードでは、`new Array(number)` はすべて要素 `undefined` を持っています。 - このような驚きを避けるため、何をしているのか本当に分かっていない限り、通常は角括弧を使います。 ## 多次元配列 @@ -405,6 +401,7 @@ alert( matrix[1][1] ); // 中央の要素 例: + ```js run let arr = [1, 2, 3]; @@ -430,6 +427,53 @@ alert( "1" + 1 ); // "11" alert( "1,2" + 1 ); // "1,21" ``` +## 配列を == で比較しないでください + +JavaScript の配列は他のプログラミング言語とは異なり、`==` 演算子で比較すべきではありません。 + +この演算子は配列に対して特別な扱いせず、他のオブジェクトと同様に動作します。 + +ルールを思い出してみましょう: + +- 2つのオブジェクトは、同じオブジェクトを参照しているときにだけ、等価 `==` です。 +- `==` の引数の一方がオブジェクトで、もう一方がプリミティブの場合、オブジェクトはチャプター で説明したように、プリミティブに変換されます。 +- ...互いに `==` で等価である `null` と `undefined` を除いては、他には何もありません。 + +厳密比較 `===` は、型変換をしないためよりシンプルです。 + +なので、`==` で配列を比較する場合、全く同じ配列を参照している2つの変数を比較しない限り、決して等価にはなりません。 + +例: +```js run +alert( [] == [] ); // false +alert( [0] == [0] ); // false +``` + +これらの配列は技術的には異なるオブジェクトです。したがって、等しくはなりません。`==` 演算子は要素毎の比較は行いません。 + +プリミティブとの比較では、以下のように、一見すると奇妙な結果がでることがあります: + +```js run +alert( 0 == [] ); // true + +alert('0' == [] ); // false +``` + +ここでは、両方のケースで配列オブジェクトとプリミティブを比較しています。なので、配列 `[]` は比較のためにプリミティブに変換され、空文字 `''` になります。 + +次に、チャプター で説明されているように、比較のプロセスがプリミティブで続行されます。 + +```js run +// [] の変換後は '' です +alert( 0 == '' ); // true, '' は数値 0 に変換されるため + +alert('0' == '' ); // false, 型変換はされません、異なる文字列です +``` + +では、どうやって配列を比較しましょう? + +簡単です: `==` 演算子を使いません。代わりにループや次のチャプターで説明するイテレーションメソッドを使用して比較します。 + ## サマリ 配列はオブジェクトの特別な種類であり、順序付けされたデータ項目を格納するのに適しています。 @@ -461,4 +505,8 @@ alert( "1,2" + 1 ); // "1,21" - `for (let item of arr)` -- アイテムだけのための、現代の構文です。 - `for (let i in arr)` -- 決して使いません。 +配列を比較するには、`==` 演算子(`>`, `<` なども同様)は使用しません。これらは配列に対して特別な処理はしません。単にオブジェクトとして扱い、それは通常期待することではありません。 + +代わりに、配列を要素毎に比較するために `for..of` ループが使用できます。 + 私たちは、チャプター で配列に戻り、追加、削除、要素の抽出や配列のソートと言ったより多くのメソッドを学びます。 diff --git a/1-js/05-data-types/04-array/queue.svg b/1-js/05-data-types/04-array/queue.svg index 12c23b9364..0ed2f1cd70 100644 --- a/1-js/05-data-types/04-array/queue.svg +++ b/1-js/05-data-types/04-array/queue.svg @@ -1,22 +1 @@ - - - - queue.svg - Created with sketchtool. - - - - - - - - - - push - - - shift - - - - \ No newline at end of file +pushshift \ No newline at end of file diff --git a/1-js/05-data-types/04-array/stack.svg b/1-js/05-data-types/04-array/stack.svg index 6719153f71..dcc600e714 100644 --- a/1-js/05-data-types/04-array/stack.svg +++ b/1-js/05-data-types/04-array/stack.svg @@ -1,22 +1 @@ - - - - stack.svg - Created with sketchtool. - - - - - - - - - - push - - - pop - - - - \ No newline at end of file +pushpop \ No newline at end of file From 21ce8181c4329e39681563e23fb51e9be170bb7b Mon Sep 17 00:00:00 2001 From: Kenji Ishii Date: Sat, 26 Jun 2021 10:46:14 +0900 Subject: [PATCH 004/208] Update 1-js/05-data-types/05-array-methods/article.md --- .../05-data-types/05-array-methods/article.md | 71 ++++++++++++------- 1 file changed, 47 insertions(+), 24 deletions(-) diff --git a/1-js/05-data-types/05-array-methods/article.md b/1-js/05-data-types/05-array-methods/article.md index 5c9c411056..e177da33af 100644 --- a/1-js/05-data-types/05-array-methods/article.md +++ b/1-js/05-data-types/05-array-methods/article.md @@ -2,8 +2,6 @@ 配列は多くのメソッドを提供します。分かりやすくするために、このチャプターではグループに分けて説明します。 -[cut] - ## アイテムの追加/削除 私たちは既に先頭または末尾にアイテムを追加/削除するメソッドを知っています: @@ -131,16 +129,15 @@ arr.slice(start, end) 例: ```js run -let str = "test"; let arr = ["t", "e", "s", "t"]; -alert( str.slice(1, 3) ); // es -alert( arr.slice(1, 3) ); // e,s +alert( arr.slice(1, 3) ); // e,s (1 から 3 をコピー) -alert( str.slice(-2) ); // st -alert( arr.slice(-2) ); // s,t +alert( arr.slice(-2) ); // s,t (-2 から末尾まで) ``` +引数なし `arr.slice()` でも呼び出すことができ、これは `arr` のコピーを生成します。これは、オリジナルの配列に影響を与えない形でさらに変換するためのためのコピーを取得するのによく使用されます。 + ### concat メソッド [arr.concat](mdn:js/Array/concat) は配列を他の配列またはアイテムと結合します。 @@ -183,10 +180,9 @@ let arrayLike = { }; alert( arr.concat(arrayLike) ); // 1,2,[object Object] -//[1, 2, arrayLike] ``` -...しかし、もし配列のようなオブジェクトが `Symbol.isConcatSpreadable` プロパティを持つ場合、代わりにその要素が追加されます: +...しかし、もし配列のようなオブジェクトが `Symbol.isConcatSpreadable` プロパティを持つ場合、`concat` は配列として扱います。つまり、代わりにその要素が追加されます: ```js run let arr = [1, 2]; @@ -208,7 +204,6 @@ alert( arr.concat(arrayLike) ); // 1,2,something,else [arr.forEach](mdn:js/Array/forEach) メソッドは配列の全要素に対して関数を実行することができます。 構文: - ```js arr.forEach(function(item, index, array) { // ... item に対して何か処理をする @@ -232,6 +227,7 @@ arr.forEach(function(item, index, array) { 関数の結果(もし何かを返す場合)は捨てられ、無視されます。 + ## 配列での検索 これらは、配列で何かを探すためのメソッドです。 @@ -277,7 +273,8 @@ alert( arr.includes(NaN) );// true (正しい) 構文はこうです: ```js let result = arr.find(function(item, index, array) { - // item が探しているものであれば true を返すようにします + // true が返却されると、item が返却され、イテレーションは停止します + // 偽の場合は undefined です }); ``` @@ -312,13 +309,15 @@ alert(user.name); // John ### filter `find` メソッドは、関数が `true` を返すようにする単一の(最初の)要素を探します。 -もしそれが多い場合、[arr.filter(fn)](mdn:js/Array/filter) を使います。 -構文は大体 `find` と同じですが、マッチした要素の配列を返します: +もし複数になる可能性がある場合、[arr.filter(fn)](mdn:js/Array/filter) を使います。 + +構文は大体 `find` と同じですが、`filter` はマッチしたすべて要素の配列を返します: ```js let results = arr.filter(function(item, index, array) { - // item がフィルタを通過する場合はtrueを返します + // true の場合、item は results にプッシュされ、イテレーションは継続します + // 何も見つからない場合は、空配列を返します }); ``` @@ -345,7 +344,9 @@ alert(someUsers.length); // 2 [arr.map](mdn:js/Array/map) メソッドは最も便利なものの1つで、よく使われます。 -構文: +これは、配列の各要素に対して関数を呼び出し、結果の配列を返します。 + +構文は次の通りです: ```js let result = arr.map(function(item, index, array) { @@ -353,8 +354,6 @@ let result = arr.map(function(item, index, array) { }) ``` -これは、配列の各要素で関数を呼び出し、結果の配列を返します。 - 例えば、ここでは各要素をその長さに変換します: ```js run @@ -364,7 +363,9 @@ alert(lengths); // 5,7,6 ### sort(fn) -メソッド [arr.sort](mdn:js/Array/sort) は配列を *決まった位置に* ソートします。 +メソッド [arr.sort](mdn:js/Array/sort) は配列を *決まった位置に* ソートし、要素の順番を変更します。 + +これもソートされた配列を返しますが、`arr` 自身が変更されるので、返却値は通常無視されます。 例: @@ -425,6 +426,7 @@ alert(arr); // *!*1, 2, 15*/!* ```js run [1, -2, 15, 2, 0, 8].sort(function(a, b) { alert( a + " <> " + b ); + return a - b; }); ``` @@ -454,6 +456,22 @@ arr.sort( (a, b) => a - b ); これは、他の上で書いているより長いバージョンとまったく同じように動作します。 ```` +````smart header="文字列には `localeCompare` を使用します" +[strings](info:string#correct-comparisons)の比較アルゴリズムを思い出してください。デフォルトではコードで文字比較を行います。 + +多くのアルファベットでは、`Ö` などの文字のソートを正しく行うためのメソッド `str.localeCompare` を使用するのがよいです。 + +例えば、ドイツ語でいくつかの国をソートしてみましょう: + +```js run +let countries = ['Österreich', 'Andorra', 'Vietnam']; + +alert( countries.sort( (a, b) => a > b ? 1 : -1) ); // Andorra, Vietnam, Österreich (間違い) + +alert( countries.sort( (a, b) => a.localeCompare(b) ) ); // Andorra,Österreich,Vietnam (正しい!) +``` +```` + ### reverse メソッド [arr.reverse](mdn:js/Array/reverse) は `arr` 内の要素の順序を逆転させます。 @@ -519,7 +537,7 @@ alert( str ); // Bilbo;Gandalf;Nazgul ### reduce/reduceRight -配列に対して繰り返し処理が必要なときは、`forEach` を使うことができます。 +配列に対して繰り返し処理が必要なときは、`forEach`, `for` あるいは `for..if` を使うことができます。 各要素のデータを反復して返す必要があるときには、`map`を使うことができます。 @@ -528,20 +546,25 @@ alert( str ); // Bilbo;Gandalf;Nazgul 構文: ```js -let value = arr.reduce(function(previousValue, item, index, arr) { +let value = arr.reduce(function(accumulator, item, index, arr) { // ... }, initial); ``` -関数は各要素に適用されます。あなたはよく知られている引数に気づくかもしれません。2つ目から始まる引数は次の通りです: +関数は各要素に順番に適用され、その結果を次の呼び出しに "引き継ぎ" ます: +引数: + +- `accumulator` - 前の関数呼び出しの結果で、初回は `initial` と等価です(`initial` が指定されている場合) - `item` -- 現在の配列の項目です。 -- `index` -- その位置です。 +- `index` -- 位置です。 - `arr` -- 配列です。 -これまでのところ、`forEach/map` のようです。しかし、もう1つ引数があります: +関数が適用されると、前の関数呼び出しの結果が、次の関数呼び出しの最初の引数として渡されます。 + +したがって、最初の引数は基本的に、以前のすべての実行の結合結果を格納するアキュムレータです。そして、最後にそれは `reduce` の結果になります。 -- `previousValue` -- 前の関数の呼び出し結果です。最初の呼び出しは `initial` です。 +複雑に見えますか? これを掴むための最も簡単な方法は、例を見る、です。 From 062f9175599e5c22deb26f7a6092fbc61f313394 Mon Sep 17 00:00:00 2001 From: Kenji Ishii Date: Sun, 4 Jul 2021 12:58:35 +0900 Subject: [PATCH 005/208] Update 1-js/05-data-types/05-array-methods --- .../05-data-types/05-array-methods/article.md | 55 +++++++++++++------ 1 file changed, 37 insertions(+), 18 deletions(-) diff --git a/1-js/05-data-types/05-array-methods/article.md b/1-js/05-data-types/05-array-methods/article.md index e177da33af..5ba1ebb98a 100644 --- a/1-js/05-data-types/05-array-methods/article.md +++ b/1-js/05-data-types/05-array-methods/article.md @@ -578,12 +578,11 @@ let result = arr.reduce((sum, current) => sum + current, 0); alert(result); // 15 ``` -ここでは、2つの引数だけを使用する `reduce` の最も一般的なバリアントを使用しました。 - +ここでは、2つの引数だけを使用する `reduce` の最も一般的なパターンを使用しました。 何が起きているか、詳細を見てみましょう。 -1. 最初の実行で `sum` は initial 値(`reduce` の最後の引数)であり、 `0` と等価です。そして、 `current` は最初の配列要素で `1` になります。従って、結果は `1` です。 +1. 最初の実行で `sum` は `initial` 値(`reduce` の最後の引数)であり、 `0` です。そして、`current` は最初の配列要素で `1` になります。従って、結果は `1` です。 2. 2回目の実行では、`sum = 1` で、2つ目の配列要素(`2`)をそれに足して返します。 3. 3回目の実行では、`sum = 3` で、それに1つ要素を足します。それが続きます。 @@ -634,6 +633,7 @@ arr.reduce((sum, current) => sum + current); [arr.reduceRight](mdn:js/Array/reduceRight) メソッドも同じをことを行いますが、右から左に実行します。 + ## Array.isArray 配列は別の言語の型を形成しません。 それらはオブジェクトに基づいています。 @@ -671,31 +671,37 @@ arr.map(func, thisArg); `thisArg` パラメータの値は `func` での `this` になります。 -例えば、ここではオブジェクトメソッドをフィルタとして使用し、`thisArg` が役立ちます。: +例えば、ここでは filter で `army` オブジェクトのメソッドを使用し、`thisArg` はそのコンテキストを渡します。: ```js run -let user = { - age: 18, - younger(otherUser) { - return otherUser.age < this.age; +let army = { + minAge: 18, + maxAge: 27, + canJoin(user) { + return user.age >= this.minAge && user.age < this.maxAge; } }; let users = [ - {age: 12}, {age: 16}, - {age: 32} + {age: 20}, + {age: 23}, + {age: 30} ]; *!* -// user より若いすべてのユーザを見つけます -let youngerUsers = users.filter(user.younger, user); +// army.canJoin が true となるユーザを見つけます +let soldiers = users.filter(army.canJoin, army); */!* -alert(youngerUsers.length); // 2 +alert(soldiers.length); // 2 +alert(soldiers[0].age); // 20 +alert(soldiers[1].age); // 23 ``` -上の呼び出しでは、フィルタとして `user.younger` を使い、そのコンテキストとして `user` を提供しています。もしもコンテキストを提供しなかった場合、`users.filter(user.younger)` はスタンドアロン関数として `this=undefined` で `user.younger` を呼び出します。それは即時エラーを意味します。 +もし上の例で `users.filter(army.canJoin)` としていた場合、`army.canJoin` はスタンドアローンの関数として呼び出されるため、`this=undefined` であり、即時エラーになります。 + +呼び出し `users.filter(army.canJoin, army)` は `users.filter(user => army.canJoin(user))` に置き換え可能であり、同じことをします。多くの人にとってより理解しやすいので、後者の方がよく利用されます。 ## サマリ @@ -716,15 +722,15 @@ alert(youngerUsers.length); // 2 - `find/filter(func)` -- 関数を介して要素をフィルタリングし、`true` を返す最初の/すべての値を返します。 - `findIndex` は `find` のようですが、値の代わりにインデックスを返します。 +- 要素を反復処理するには: + - `forEach(func)` -- すべての要素に対して `func`を呼び出し、何も返しません。 + - 配列を変換するには: - `map(func)` -- すべての要素に対して `func` を呼び出した結果から新しい配列を作成します。 - `sort(func)` -- 配列を適切な位置でソートし、それを返します。 - `reverse()` -- 配列を反転してそれを返します。 - `split/join` -- 文字列を配列に変換したり、戻します。 - - `reduce(func, initial)` -- 各要素に対して `func`を呼び出し、呼び出しの間に中間結果を渡すことで配列全体の単一の値を計算します。 - -- 要素を反復処理するには: - - `forEach(func)` -- すべての要素に対して `func`を呼び出し、何も返しません。 + - `reduce/reduceRight(func, initial)` -- 各要素に対して `func`を呼び出し、呼び出しの間に中間結果を渡すことで配列全体の単一の値を計算します。 - さらに: - `Array.isArray(arr)` は `arr` が配列かどうかをチェックします。 @@ -737,10 +743,23 @@ alert(youngerUsers.length); // 2 関数 `fn` は `map` と同じように配列の各要素で呼ばれます。もし どれか/すべて の結果が `true` であれば `true`, それ以外は `false` になります。 + これらのメソッドは `||` や `&&` 演算子のように振る舞います。もし `fn` が真の値を返す場合、`arr.some()` はすぐに `true` を返し、残りの項目に対するイテレーションを停止します。`fn` が偽の値を返す場合は、`arr.every()` はすぐに `false` を返し、同様に残りの項目のイテレーションは停止します。 + + `every` は配列を比較するのに使えます: + ```js run + function arraysEqual(arr1, arr2) { + return arr1.length === arr2.length && arr1.every((value, index) => value === arr2[index]); + } + + alert( arraysEqual([1, 2], [1, 2])); // true + ``` + - [arr.fill(value, start, end)](mdn:js/Array/fill) -- インデックス `start` から `end` まで `value` で配列を埋めます。 - [arr.copyWithin(target, start, end)](mdn:js/Array/copyWithin) -- 位置 `start` から `end` までの要素を、*自身* の `target` の位置にコピーします (既存のものを上書きします)。 +- [arr.flat(depth)](mdn:js/Array/flat)/[arr.flatMap(fn)](mdn:js/Array/flatMap) は多次元配列からフラットな配列を生成します。 + 完全なリストは [manual](mdn:js/Array) を見てください。 初めてみたとき、多くのメソッドがあり覚えるのがとても難しいように見えるかもしれません。しかし、実際にはそう見えるよりもはるかに簡単です。 From e871059109a5c109645a457bb22931f0828ed18d Mon Sep 17 00:00:00 2001 From: Kenji Ishii Date: Sun, 11 Jul 2021 10:47:28 +0900 Subject: [PATCH 006/208] Update 1-js/05-data-types/06-iterable/article.md --- 1-js/05-data-types/06-iterable/article.md | 24 ++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/1-js/05-data-types/06-iterable/article.md b/1-js/05-data-types/06-iterable/article.md index b960719b4f..97876d4c49 100644 --- a/1-js/05-data-types/06-iterable/article.md +++ b/1-js/05-data-types/06-iterable/article.md @@ -7,15 +7,14 @@ もしオブジェクトが何かの集合(リスト、セット)を表す場合、`for..of` はそれをループ処理するのに最適な構文です。それでは、それを動作させる方法を見てみましょう。 -[cut] ## Symbol.iterator -私たち自身で実際に作ってみると、反復可能(iterables) の概念を簡単に掴む事ができます。 +自分自身で実際に作ってみると、反復可能(iterables) の概念を簡単に掴む事ができます。 例えば、配列ではありませんが、`for..of` に適したオブジェクトを持っています。 -数値の間隔を表す `range` オブジェクトのように: +以下は数値の間隔を表す `range` オブジェクト: ```js let range = { @@ -45,7 +44,8 @@ let range = { // 1. for..of の呼び出しは、最初にこれを呼び出します range[Symbol.iterator] = function() { - // 2. ...これは iterator を返します: + // ...これは iterator オブジェクトを返します: + // 2. 以降、for..of はこのイテレータでのみ機能し、次の値を要求します return { current: this.from, last: this.to, @@ -64,11 +64,11 @@ range[Symbol.iterator] = function() { // これで動作します! for (let num of range) { - alert(num); // 1, then 2, 3, 4, 5 + alert(num); // 1, 2, 3, 4, 5 } ``` -反復可能(iterables)の中心的な機能に注意してください。: 重要な関心の分離があります。 +反復可能(iterables)の中心的な機能に注目してください。関心の分離です。 - `range` 自身は `next()` メソッドを持っていません。 - 代わりに、別のオブジェクト、いわゆる "イテレータ" は `range[Symbol.iterator]()` の呼び出しで生成され、反復を処理します。 @@ -103,7 +103,9 @@ for (let num of range) { } ``` -今、`range[Symbol.iterator]()` は `range` オブジェクト自身を返します: それは必要な `next()` メソッドを持ち、`this.current` で現在の反復の状況を覚えています。場合によってはそれでも問題ありません。欠点は、オブジェクトに対して同時に2つの `for..of` ループを実行することは不可能だということです。: イテレータ が1つしかないので、オブジェクトは繰り返し状態を共有します。 +今、`range[Symbol.iterator]()` は `range` オブジェクト自身を返します: `next()` メソッドを持ち、`this.current` で現在の反復の状況を覚えています。 + +欠点は、オブジェクトに対して同時に2つの `for..of` ループを実行することは不可能だということです。: イテレータ が1つしかないので、オブジェクトは繰り返し状態を共有します。ですが、2つの並列 for-of はたとえ非同期のりナリオにおいてもまれです。 ```smart header="無限のイテレータ" 無限の イテレータ もまた実行可能です。例えば、 `range.to = Infinity` で、`range` が無限大になります。または、擬似乱数の無限のシーケンスを生成する反復可能なオブジェクトを作ることができます。これもまた役立つことがあります。 @@ -122,6 +124,7 @@ for (let num of range) { ```js run for (let char of "test") { + // 文字ごとに1回、計4回実行します。 alert( char ); // t, 次に e, 次に s, そして t } ``` @@ -160,14 +163,16 @@ while (true) { これはほとんど必要とされませんが、`for..of` よりも処理をよりコントロールできます。例えば、繰り返し処理を分割したい場合: 少し反復してから停止し、別のことをしてから後で再開するような場合に役立ちます。 -## 反復可能(Iterables) と 配列ライク(array-like)s +## 反復可能(Iterables) と 配列ライク(array-like) 同じように見える2つの正式な用語がありますが、大きく異なるものです。 混乱を避けるためにそれらをよく理解してください。 - *反復可能(Iterables)* は上で説明したように、`Symbol.iterator` メソッドを実装したオブジェクトです。 - *配列ライク(Array-likes)* は、インデックスと `length` を持ったオブジェクトです。なので、これらは配列のように見えます。 -もちろん、それらの特性は組み合わせることができます。例えば、文字列は 反復可能(iterable) (`for..of` が動作する) であり、かつ 配列ライク(array-like) (数値インデックスと `length` を持っています) です。 +ブラウザや他の環境で、実際のタスクを処理するのに JavaScript を使用するとき、反復可能あるいは配列ライク、もしくはその両方のオブジェクトに出くわすことがあります。 + +例えば、文字列は 反復可能(iterable) (`for..of` が動作する) であり、かつ 配列ライク(array-like) (数値インデックスと `length` を持っています) です。 しかし、反復可能(iterable) は 配列ライク(array-like) でない可能性があります。そして、同じように 配列ライク(array-like) も 反復可能(iterable) でない場合があります。 @@ -294,6 +299,7 @@ alert( str.slice(1, 3) ); // ゴミ (異なるサロゲートペアの片割れ - 文字列や配列のような組み込みの iterables もまた、`Symbol.iterator` を実装しています。 - 文字列の イテレータ はサロゲートペアが考慮できます。 + インデックス付きのプロパティと `length` をもつオブジェクトは *配列ライク(array-like)* と呼ばれます。このようなオブジェクトは他のプロパティやメソッドを持つことができますが、配列の組み込みメソッドは持っていません。 もしも仕様の内側を見ていくと、多くの組み込みメソッドは、"本当の" 配列の代わりに 反復可能(iterable) または 配列ライク(array-like) で動作することを想定していることがわかるでしょう。なぜなら、それらはより抽象的なためです。 From fa2e866a9df8420295bf71883216ba858ab1ecaa Mon Sep 17 00:00:00 2001 From: Kenji Ishii Date: Sun, 11 Jul 2021 11:22:26 +0900 Subject: [PATCH 007/208] Update 1-js/05-data-types/07-map-set/article.md --- 1-js/05-data-types/07-map-set/article.md | 34 ++++++++++++++++-------- 1 file changed, 23 insertions(+), 11 deletions(-) diff --git a/1-js/05-data-types/07-map-set/article.md b/1-js/05-data-types/07-map-set/article.md index d0a65116da..4e651cfd06 100644 --- a/1-js/05-data-types/07-map-set/article.md +++ b/1-js/05-data-types/07-map-set/article.md @@ -41,6 +41,12 @@ alert( map.size ); // 3 上の通り、オブジェクトとは違い、キーは文字列には変換されません。任意の型のキーが利用可能です。 +```smart header="`map[key]` は `Map` を使用する正しい方法ではありません" +例えば、`map[key] = 2` のように、`map[key]` でも動作しますが、これは `map` を通常の JavaScript オブジェクトとして扱っているので、対応するすべての制限(文字列/シンボルキーのみなど)があることを意味します。 + +なので、`map` メソッドを使用するべきです: `set`, `get` など. +``` + **Map はキーとしてオブジェクトを使うこともできます。** 例: @@ -57,36 +63,35 @@ visitsCountMap.set(john, 123); alert( visitsCountMap.get(john) ); // 123 ``` -オブジェクトをキーとして使用することは、最も注目に値する重要な `Map` の機能の1つです。文字列キーの場合、`Object` で問題ありませんが、オブジェクトキーの場合はそうではありません。 +オブジェクトをキーとして使用することは、最も注目に値する重要な `Map` の機能の1つです。同じことは `Object` ではカウントされません。`Object` ではキーとして文字列使用は問題ありませんが、キーとして別のオブジェクトを使用することはできません。 やってみましょう: ```js run let john = { name: "John" }; +let ben = { name: "Ben" }; -let visitsCountObj = {}; // オブジェクトを用意 +let visitsCountObj = {}; // オブジェクトを使用 -visitsCountObj[john] = 123; // john オブジェクトをキーとして使用 +visitsCountObj[ben] = 234; // ben オブジェクトをキーに使用 +visitsCountObj[john] = 123; // john オブジェクトをキーに使用。ben オブジェクトは置換されます *!* // That's what got written! -alert( visitsCountObj["[object Object]"] ); // 123 +alert( visitsCountObj["[object Object]"] ); // 123 */!* ``` `visitsCountObj` はオブジェクトなので、`john` などのすべてのキーを文字列に変換します。そのため、文字列キー `"[object Object]"` となります。間違いなくこれは望むものではありません。 - ```smart header="`Map` はどのようにキーを比較するか" 等価のテストをするために、`Map` は [SameValueZero](https://tc39.github.io/ecma262/#sec-samevaluezero) アルゴリズムを使います。大雑把には厳密等価 `===` と同じですが、違いは `NaN` は `NaN` と等しいとみなされる点です。なので、`NaN` も同様にキーとして使うことができます。 このアルゴリズムは変更したりカスタマイズすることはできません。 ``` - ````smart header="チェーン" - -`map.set` 呼び出しは map 自身を返すので、呼び出しを "チェーン" することができます: +すべての `map.set` 呼び出しは map 自身を返すので、呼び出しを "チェーン" することができます: ```js map.set('1', 'str1') @@ -95,6 +100,7 @@ map.set('1', 'str1') ``` ```` + ## Map での繰り返し/ループ `map` でループするためには3つのメソッドがあります: @@ -135,6 +141,7 @@ for (let entry of recipeMap) { // recipeMap.entries() と同じ それに加えて、`Map` は `Array` と同じように、組み込みの `forEach` メソッドを持っています。 ```js +// 各 (key, value) ペアに対して関数を実行 recipeMap.forEach( (value, key, map) => { alert(`${key}: ${value}`); // cucumber: 500 etc }); @@ -155,7 +162,7 @@ let map = new Map([ alert( map.get('1') ); // str1 ``` -オブジェクトのキー/値のペアの配列を、その形式で返す組み込みのメソッド [Object.entries(obj)](mdn:js/Object/entries) があります。 +通常のオブジェクトがあり、そこから `Map` を生成したい場合は、オブジェクトのキー/値のペアの配列を、その形式で返す組み込みのメソッド [Object.entries(obj)](mdn:js/Object/entries) を使用できます。 なので、次のようにオブジェクトから map の初期化をすることができます: @@ -174,6 +181,7 @@ alert( map.get('name') ); // John ここで、`Object.entries` はキー/値のペアの配列を返します: `[ ["name","John"], ["age", 30] ]`。これは `Map` が必要とするものです。 + ## Object.fromEntries: Map から オブジェクト つい先程、通常のオブジェクトから `Object.entries(obj)` を使用して `Map` を作成する方法を見ました。 @@ -225,7 +233,7 @@ let obj = Object.fromEntries(map); // omit .entries() ## Set -`Set` -- は値の集まりで、それぞれの値は一度しか現れません。 +`Set` は、特別タイプのコレクションで、"値の集合" (キーなし)です。それぞれの値は一度しか現れません。 主なメソッドは次の通りです: @@ -236,6 +244,8 @@ let obj = Object.fromEntries(map); // omit .entries() - `set.clear()` -- set から全てを削除します。 - `set.size` -- set の要素数です。 +主な特徴は同じ値での `set.add(value)` の繰り返しの呼び出しでは何もしないことです。`Set` では各値は一度しか現れないためです。 + 例えば、訪問者全員を覚えておきたいです。が、繰り返し訪問しても重複しないようにしたいです。訪問者は一度だけ "カウント" される必要があります。 `Set` はそれに相応しいものです: @@ -281,7 +291,9 @@ set.forEach((value, valueAgain, set) => { 面白い点に注意してください。`Set` の中の `forEach` 関数は3つの引数を持っています: 値(value), 次に *再び値(valueAgain)*, 次にターゲットのオブジェクトです。実際、引数には同じ値が2回出現します。 -これは `forEach` が3つの引数をもつ `Map` との互換性のために作られています。 +これは `forEach` が3つの引数をもつ `Map` との互換性のために作られています。少しおかしく見えるかもしれません。が、特定ケースにおいて簡単に `Map` を `Set` に、またその逆を行うのに役立ちます。 + +`Map` がイテレーションのために持っているメソッドと同じメソッドもサポートしています: - `set.keys()` -- 値に対する iterable なオブジェクトを返します。 - `set.values()` -- `set.keys` と同じで、`Map` との互換性のためです。 From 1c9ca3690262c5eb0c60f605513483052111ad61 Mon Sep 17 00:00:00 2001 From: t-yng Date: Sat, 17 Jul 2021 01:41:15 +0900 Subject: [PATCH 008/208] =?UTF-8?q?=E3=82=BF=E3=82=A4=E3=83=9D=E3=82=92?= =?UTF-8?q?=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 1-js/06-advanced-functions/04-var/article.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/1-js/06-advanced-functions/04-var/article.md b/1-js/06-advanced-functions/04-var/article.md index 88ca984a03..a11bcc9239 100644 --- a/1-js/06-advanced-functions/04-var/article.md +++ b/1-js/06-advanced-functions/04-var/article.md @@ -33,7 +33,7 @@ alert(phrase); // Error, phrase は未定義です ## "var" はブロックスコープを持ちません -`var` 変数はのスコープは "関数全体" か "グローバル" のいずれかであり、ブロックを通って見ることができます。 +`var` 変数のスコープは "関数全体" か "グローバル" のいずれかであり、ブロックを通って見ることができます。 例: From 1b9f0df2464479f6777daa356b75b95a1b69ece9 Mon Sep 17 00:00:00 2001 From: Kenji Ishii Date: Sun, 18 Jul 2021 15:17:18 +0900 Subject: [PATCH 009/208] Translate 1-js/05-data-types/08-weakmap-weakset/article.md --- .../08-weakmap-weakset/article.md | 122 +++++++++--------- 1 file changed, 63 insertions(+), 59 deletions(-) diff --git a/1-js/05-data-types/08-weakmap-weakset/article.md b/1-js/05-data-types/08-weakmap-weakset/article.md index a6ac4a8945..bb240fba09 100644 --- a/1-js/05-data-types/08-weakmap-weakset/article.md +++ b/1-js/05-data-types/08-weakmap-weakset/article.md @@ -30,14 +30,16 @@ let array = [ john ]; john = null; // 参照を上書きします *!* -// john は配列内に格納されているので、ガベージコレクションされません。 -// array[0] としてそれを取得することが可能です +// 以前 john で参照されていたオブジェクトは配列内に格納されています +// そのため、ガベージコレクションされません。 +// array[0] で取得することが可能です */!* ``` -また、通常の Map のキーとしてオブジェクトを使うと、Map が存在している間はそのオブジェクトも存在します。これはメモリを占め、ガベージコレクションされないかもしれません。 +また、通常の `Map` のキーとしてオブジェクトを使うと、`Map` が存在している間はそのオブジェクトも存在します。これはメモリを占め、ガベージコレクションされないかもしれません。 例: + ```js let john = { name: "John" }; @@ -68,7 +70,8 @@ let obj = {}; weakMap.set(obj, "ok"); // 正常に動作します (オブジェクトのキー) *!* -weakMap.set("test", "Whoops"); // エラー, "test" はプリミティブだからです +// キーに文字列は使えません +weakMap.set("test", "Whoops"); // エラー, "test" はオブジェクトではないため */!* ``` @@ -104,80 +107,79 @@ john = null; // 参照を上書きします ## ユースケース: additional data -The main area of application for `WeakMap` is an *additional data storage*. +`WeakMap` のアプリケーションの主な領域は、 *追加のデータ格納* です。 -If we're working with an object that "belongs" to another code, maybe even a third-party library, and would like to store some data associated with it, that should only exist while the object is alive - then `WeakMap` is exactly what's needed. +別のコード、おそらくサードパーティライブラリに "属する" オブジェクトを操作していて、それに関連付けられたデータをいくつか保存したい場合、それは元のオブジェクトが生きている間だけ存在している必要があります。このとき、`WeakMap` はまたさに必要とされるものです。 -We put the data to a `WeakMap`, using the object as the key, and when the object is garbage collected, that data will automatically disappear as well. +キーとしてオブジェクトを使用して、`WeakMap` にデータを格納し、オブジェクトがガベージコレクションされたとき、データも同様自動的に消えます。 ```js weakMap.put(john, "secret documents"); // もし john がなくなった場合、秘密のドキュメントは破壊されるでしょう ``` -Let's look at an example. +例を見てみましょう。 -For instance, we have code that keeps a visit count for users. The information is stored in a map: a user object is the key and the visit count is the value. When a user leaves (its object gets garbage collected), we don't want to store their visit count anymore. +例えば、ユーザの訪問カウントを保持するコードがあるとします。情報は map に保持されています。ユーザオブジェクトがキーであり、訪問カウントがその値です。ユーザが離れたとき(そのオブジェクトがガベージコレクションされる)、もうそのユーザの訪問カウントは保持する必要はありません。 -Here's an example of a counting function with `Map`: +これは、 `Map` を使用したカウント関数の例です: ```js // 📁 visitsCount.js let visitsCountMap = new Map(); // map: user => visits count -// increase the visits count +// 訪問カウントを増やす function countUser(user) { let count = visitsCountMap.get(user) || 0; visitsCountMap.set(user, count + 1); } ``` -And here's another part of the code, maybe another file using it: +また、これはコードの別の部分で、おそらく上の関数を使用する別のファイルです: ```js // 📁 main.js let john = { name: "John" }; -countUser(john); // count his visits -countUser(john); +countUser(john); // 訪問をカウント -// later john leaves us +// あとで john が離脱したとき john = null; ``` -Now `john` object should be garbage collected, but remains in memory, as it's a key in `visitsCountMap`. +このとき、`john` オブジェクトはガベージコレクションされるべきですが、`visitsCountMap` のキーなので、メモリに残ったままです。 -We need to clean `visitsCountMap` when we remove users, otherwise it will grow in memory indefinitely. Such cleaning can become a tedious task in complex architectures. +ユーザが削除されたとき、`visitsCountMap` をクリーンアップする必要があります。そうしないと、メモリ内で無限に大きくなります。このようなクリーニングは複雑なアーキテクチャでは面倒な作業になりえます。 -We can avoid it by switching to `WeakMap` instead: +代わりに `WeakMap` に切り替えることで回避できます: ```js // 📁 visitsCount.js let visitsCountMap = new WeakMap(); // weakmap: user => visits count -// increase the visits count +// 訪問数を増加 function countUser(user) { let count = visitsCountMap.get(user) || 0; visitsCountMap.set(user, count + 1); } ``` -Now we don't have to clean `visitsCountMap`. After `john` object becomes unreachable by all means except as a key of `WeakMap`, it gets removed from memory, along with the information by that key from `WeakMap`. +これで、`visitsCountMap` をクリーンアップする必要はありません。`WeakMap` のキーを除いたすべての手段で `john` オブジェクトが到達不可能になった後、`WeakMap` からそのキーによる情報とともに、メモリからは削除されます。 -## Use case: caching +## ユースケース: キャッシュ -Another common example is caching: when a function result should be remembered ("cached"), so that future calls on the same object reuse it. +もう一つの一般的な例はキャッシュです。関数からの結果を保持("キャッシュ")できるので、同じオブジェクトに対する将来の呼び出しで再利用することができます。 -We can use `Map` to store results, like this: +これを実現するために、`Map`(最適ではないシナリオ)が利用できます: ```js run // 📁 cache.js let cache = new Map(); -// calculate and remember the result +// 計算し結果を覚える function process(obj) { if (!cache.has(obj)) { - let result = /* calculations of the result for */ obj; + let result = obj /* に対する計算結果 */; cache.set(obj, result); } @@ -186,26 +188,26 @@ function process(obj) { } *!* -// Now we use process() in another file: +// ここで、別のファイルで process() を使用します。 */!* // 📁 main.js -let obj = {/* let's say we have an object */}; +let obj = {/* オブジェクトがあるとします */}; -let result1 = process(obj); // calculated +let result1 = process(obj); // 計算します -// ...later, from another place of the code... -let result2 = process(obj); // remembered result taken from cache +// ...その後、別の場所で呼ばれるとします... +let result2 = process(obj); // キャッシュから取得した、記憶された結果が使われます -// ...later, when the object is not needed any more: +// ...後ほど、オブジェクトがこれ以上は不要になったとき obj = null; -alert(cache.size); // 1 (Ouch! The object is still in cache, taking memory!) +alert(cache.size); // 1 (なんと! オブジェクトは依然としてキャッシュされており、メモリを食っています!) ``` -For multiple calls of `process(obj)` with the same object, it only calculates the result the first time, and then just takes it from `cache`. The downside is that we need to clean `cache` when the object is not needed any more. +同じオブジェクトので `process(obj)` の複数回の呼び出しに対して、初回だけ結果の計算を行い、その後は `cache` から値を取ります。デメリットは、オブジェクトがこれ以上不要になったとき、`cache` のクリーンアップが必要なことです。 -If we replace `Map` with `WeakMap`, then this problem disappears: the cached result will be removed from memory automatically after the object gets garbage collected. +`Map` を `WeakMap` に置き換えた場合、この問題は消えます。キャッシュされた結果はオブジェクトのガベージコレクト後、自動的にメモリから削除されます。 ```js run // 📁 cache.js @@ -213,10 +215,10 @@ If we replace `Map` with `WeakMap`, then this problem disappears: the cached res let cache = new WeakMap(); */!* -// calculate and remember the result +// 計算し結果を覚える function process(obj) { if (!cache.has(obj)) { - let result = /* calculate the result for */ obj; + let result = obj /* に対する計算結果 */; cache.set(obj, result); } @@ -230,25 +232,25 @@ let obj = {/* some object */}; let result1 = process(obj); let result2 = process(obj); -// ...later, when the object is not needed any more: +// ...後ほど、オブジェクトがこれ以上は不要になったとき obj = null; -// Can't get cache.size, as it's a WeakMap, -// but it's 0 or soon be 0 -// When obj gets garbage collected, cached data will be removed as well +// WeakMap なので cache.size は取得できません +// が、0 あるいはすぐに 0 になります +// オブジェクトがガベージコレクトされると、キャッシュされたデータも同様に削除されます。 ``` ## WeakSet -`WeakSet` behaves similarly: +`WeakSet` も同様に動作します: -- It is analogous to `Set`, but we may only add objects to `WeakSet` (not primitives). -- An object exists in the set while it is reachable from somewhere else. -- Like `Set`, it supports `add`, `has` and `delete`, but not `size`, `keys()` and no iterations. +- `Set` に似ていますが、`WeakSet` へはオブジェクトのみ追加できます(プリミティブではありません) +- オブジェクトは、別の場所から到達可能である間、`Set` に存在します。 +- `Set` 同様、`add`, `has`, `delete` をサポートしますが、`size`, `keys()` とイテレーションはサポートしません。 -Being "weak", it also serves as an additional storage. But not for an arbitrary data, but rather for "yes/no" facts. A membership in `WeakSet` may mean something about the object. +"弱い" ので、追加の格納場所としても使えます。ですが、任意のデータではなく、むしろ "はい/いいえ" の事実のためです。`WeakSet` のメンバーはオブジェクトについてなにかを意味する場合があります。 -For instance, we can add users to `WeakSet` to keep track of those who visited our site: +例えば、ユーザを `WeakSet` に追加して、サイトにアクセスしたユーザを追跡できます。: ```js run let visitedSet = new WeakSet(); @@ -257,31 +259,33 @@ let john = { name: "John" }; let pete = { name: "Pete" }; let mary = { name: "Mary" }; -visitedSet.add(john); // John visited us -visitedSet.add(pete); // Then Pete -visitedSet.add(john); // John again +visitedSet.add(john); // John が訪問 +visitedSet.add(pete); // 次に Pete +visitedSet.add(john); // John 再び -// visitedSet has 2 users now +// visitedSet は 2 ユーザいます -// check if John visited? +// John が訪問したかどうかをチェック alert(visitedSet.has(john)); // true -// check if Mary visited? +// Mary が訪問したかをチェック alert(visitedSet.has(mary)); // false john = null; -// visitedSet will be cleaned automatically +// visitedSet は自動的にクリーンアップされます。 ``` -The most notable limitation of `WeakMap` and `WeakSet` is the absence of iterations, and inability to get all current content. That may appear inconvenient, but does not prevent `WeakMap/WeakSet` from doing their main job -- be an "additional" storage of data for objects which are stored/managed at another place. +最も注目すべき `WeakMap` と `WeakSet` の制限は、イテレーションの欠如と現在のすべてのコンテンツを取得することができないことです。これは不便に見えるかもしれませんが、`WeakMap/WeakSet` がこれらの主要なジョブ -- 別の場所に保存/管理されているオブジェクトのデータの "追加の" 保管場所になること -- をするのを妨げることはありません。 + +## サマリ -## Summary +`WeakMap` は `Map` ライクなコレクションであり、オブジェクトのみがキーとして許可され、他の手段でそのオブジェクトが到達不可能になると、関連付けされた値と一緒に削除されます。 -`WeakMap` is `Map`-like collection that allows only objects as keys and removes them together with associated value once they become inaccessible by other means. +`WeakSet` は `Set` ライクなコレクションであり、オブジェクトのみが保管でき、他の手段でそのオブジェクトが到達不可能になると、それらも削除されます。 -`WeakSet` is `Set`-like collection that stores only objects and removes them once they become inaccessible by other means. +これらの主なアドバンテージは、オブジェクトに対して弱い参照を持っていることです。なので、ガベージコレクションで容易に削除できます。 -Both of them do not support methods and properties that refer to all keys or their count. Only individual operations are allowed. +なお、これには `clear`, `size`, `keys`, `values` などのサポートがないという代償が伴います。 -`WeakMap` and `WeakSet` are used as "secondary" data structures in addition to the "main" object storage. Once the object is removed from the main storage, if it is only found as the key of `WeakMap` or in a `WeakSet`, it will be cleaned up automatically. +`WeakMap` と `WeakSet` は "主要な" オブジェクト保管場所に加え、"2つ目の" データ構造として使用されます。一旦オブジェクトが主要な保管場所から削除されると、それが `WeakMap` のキーまたは `WeakSet` でのみ見つかった場合、オブジェクトは自動的にクリーンアップされます。 From 64fb0031ee24bdacaeda46a27c2bc2317642d9b6 Mon Sep 17 00:00:00 2001 From: Kenji Ishii Date: Sun, 18 Jul 2021 15:28:51 +0900 Subject: [PATCH 010/208] Update 1-js/05-data-types/09-keys-values-entries/article.md --- .../09-keys-values-entries/article.md | 38 +++++++++++++++++-- 1 file changed, 35 insertions(+), 3 deletions(-) diff --git a/1-js/05-data-types/09-keys-values-entries/article.md b/1-js/05-data-types/09-keys-values-entries/article.md index e331453b36..0eed474666 100644 --- a/1-js/05-data-types/09-keys-values-entries/article.md +++ b/1-js/05-data-types/09-keys-values-entries/article.md @@ -11,7 +11,7 @@ - `Map` - `Set` -- `Array` (`arr.values()` を除く) +- `Array` 通常のオブジェクトも同様のメソッドをサポートしますが、構文は少し異なります。 @@ -63,8 +63,40 @@ for (let value of Object.values(user)) { } ``` -## Object.keys/values/entries は Symbol を使っているプロパティを無視します - +```warn header="Object.keys/values/entries は Symbol プロパティを無視します" `for..in` ループのように、これらのメソッドはキーとして `Symbol(...)` を使っているプロパティを無視します。 通常それは便利です。しかし、もしもこのようなキーも同様に扱いたい場合は、別のメソッド [Object.getOwnPropertySymbols](mdn:js/Object/getOwnPropertySymbols) があります。これは Symbol を使っているキーのみの配列を返します。また、メソッド [Reflect.ownKeys(obj)](mdn:js/Reflect/ownKeys) は *すべての* キーを返します。 +``` + + +## オブジェクトの変換 + +オブジェクトには、配列に存在する多くのメソッドがありません。例えば `map`, `filter` など。 + +それらを適用したい場合は、`Object.fromEntries` に続いて、`Object.entries` が使用できます。: + +1. `Object.entries(obj)` を使用して `obj` からキー/値ペアの配列を取得します。 +2. その配列で、配列のメソッドを使用します。例えば `map` +3. 結果の配列で `Object.fromEntries(array)` を使用して、配列をオブジェクトに戻します。 + +例えば、価格をもつオブジェクトがあり、それらを2倍したいとします。: + +```js run +let prices = { + banana: 1, + orange: 2, + meat: 4, +}; + +*!* +let doublePrices = Object.fromEntries( + // 配列に変換して map を実行、その後 fromEntries でオブジェクトに戻します + Object.entries(prices).map(([key, value]) => [key, value * 2]) +); +*/!* + +alert(doublePrices.meat); // 8 +``` + +一見難しく見えますが、1,2回使うと簡単に理解できます。このようにして協力な変換のチェーンを作ることができます。 From 08336c96730352d99f22f7f9bede41bcbd4c6b31 Mon Sep 17 00:00:00 2001 From: Kenji Ishii Date: Sat, 31 Jul 2021 17:47:07 +0900 Subject: [PATCH 011/208] [WIP] Update 1-js/05-data-types/10-destructuring-assignment/article.md --- .../10-destructuring-assignment/article.md | 121 ++++++++++++++---- 1 file changed, 93 insertions(+), 28 deletions(-) diff --git a/1-js/05-data-types/10-destructuring-assignment/article.md b/1-js/05-data-types/10-destructuring-assignment/article.md index 3524124cd6..f0d958d669 100644 --- a/1-js/05-data-types/10-destructuring-assignment/article.md +++ b/1-js/05-data-types/10-destructuring-assignment/article.md @@ -2,11 +2,14 @@ JavaScriptで最も使われる2つのデータ構造は `Object` と `Array` です。 -オブジェクトは多くの情報を1つのエンティティにまとめることができ、配列は順序付けされたコレクションを格納することができます。従って、私たちはオブジェクトまたは配列を作り、それを1つのエンティティとして扱うことができます。また、それを関数呼び出しの際に渡すこともできます。 +- オブジェクトを使用すると、データ項目をキーごとに格納する単一のエンティティを作成できます。 +- 配列は順序付けされたリストにデータ項目を集めることができます。 -*分割代入(Destructuring assignment)* は、配列またはオブジェクトの中身を複数の変数に代入できる特別な構文です。デストラクタリング(非構造化/構造の分解)は、多くのパラメータとデフォルト値を持つ複雑な関数でもうまく機能します。このチャプターでは、すぐにこれらがどのように処理されているかわかるでしょう。 +ですが、これらを関数にわたすとき、オブジェクト/配列全体は必要としない場合があります。個々の部分が必要な場合です。 -[cut] +*分割代入(Destructuring assignment)* は、配列またはオブジェクトの中身を複数の変数に *アンパック* できるようにする特別な構文であり、非常に便利な場合があります。 + +分割代入(非構造化/構造の分解)は、多くのパラメータとデフォルト値を持つ複雑な関数でもうまく機能します。この後すぐにそれらを見ていきます。 ## Array の非構造化 @@ -14,25 +17,31 @@ JavaScriptで最も使われる2つのデータ構造は `Object` と `Array` ```js // 姓名の配列があります -let arr = ["Ilya", "Kantor"] +let arr = ["John", "Smith"] *!* // 分割代入 +// sets firstName = arr[0] +// and surname = arr[1] let [firstName, surname] = arr; */!* -alert(firstName); // Ilya -alert(surname); // Kantor +alert(firstName); // John +alert(surname); // Smith ``` これで、配列要素の代わりに変数を扱うことができます。 `split` やその他配列を返すメソッドと組み合わせると便利です: -```js -let [firstName, surname] = "Ilya Kantor".split(' '); +```js run +let [firstName, surname] = "John Smith".split(' '); +alert(firstName); // John +alert(surname); // Smith ``` +ご覧の通り、構文はシンプルです。ですがいくつかの独特な部分があります。より理解するために他の例も見ていきましょう。 + ````smart header="\"分割\" は \"破壊的\" を意味しません" これは、項目を変数にコピーすることによって "非構造化(destructurizes)" するため、"分割代入(destructuring assignment)" と呼ばれています。 配列自体は変更されません。 @@ -61,32 +70,31 @@ alert( title ); // Consul ````smart header="右辺は任意の反復可能(iterable)に対して動作します" -実際には配列だけでなく、任意の反復可能(iterable)に対して使うことができます: +...実際には配列だけでなく、任意の反復可能(iterable)に対して使うことができます: ```js let [a, b, c] = "abc"; // ["a", "b", "c"] let [one, two, three] = new Set([1, 2, 3]); ``` - +内部的には分割代入は右辺の値に対してイテレーションすることで動作するため、これも動作します。これは `=` の右側の値に対して `for..of` を呼び出し、値を代入するためのシンタックスシュガーの一種です。 ```` ````smart header="左辺では任意のものに代入することが可能です" - 左辺には任意の "割り当て可能なもの" を指定することができます。 例えば、オブジェクトのプロパティも指定できます: ```js run let user = {}; -[user.name, user.surname] = "Ilya Kantor".split(' '); +[user.name, user.surname] = "John Smith".split(' '); -alert(user.name); // Ilya +alert(user.name); // John +alert(user.surname); // Smith ``` ```` ````smart header=".entries() を使ったループ" - 以前のチャプターで、[Object.entries(obj)](mdn:js/Object/entries) メソッドを見ました。 オブジェクトの key-value をループするのに、分割代入を一緒に使うこともできます: @@ -105,7 +113,7 @@ for (let [key, value] of Object.entries(user)) { } ``` -map も同様です: +`Map` を使用した同様のコードは、反復可能なのでよりシンプルです: ```js run let user = new Map(); @@ -113,32 +121,69 @@ user.set("name", "John"); user.set("age", "30"); *!* -for (let [key, value] of user.entries()) { +// Map は [key, value] ペアで反復します +for (let [key, value] of user) { */!* alert(`${key}:${value}`); // name:John, then age:30 } ``` ```` +````smart header="変数を入れ替えるトリック" +分割代入を使用して2つの変数の値を入れ替える、広く知られたトリックがあります: + +```js run +let guest = "Jane"; +let admin = "Pete"; + +// 値を入れかえましょう: guest=Pete, admin=Jane +*!* +[guest, admin] = [admin, guest]; +*/!* + +alert(`${guest} ${admin}`); // Pete Jane (successfully swapped!) +``` + +ここでは、2つの変数の一時的な配列を作り、その直後、入れ替えた順番で分割しました。 + +この方法で2つ以上の変数を入れ替えることも可能です。 +```` + ### 残り '...' -最初の値を取得するだけでなく、それに続くすべての値も集めたい場合、"残りの部分" の取得を意味する 3つのドッド `"..."` をパラメータに追加することで実現できます: +通常、代入する変数の数よりも配列の要素数のほうが多い場合、"余分な" 項目は省略されます。 +例えば、ここでは2つの項目が取得され、残りは無視されています: ```js run -let [name1, name2, *!*...rest*/!*] = ["Julius", "Caesar", *!*"Consul", "of the Roman Republic"*/!*]; +let [name1, name2] = ["Julius", "Caesar", "Consul", "of the Roman Republic"]; alert(name1); // Julius alert(name2); // Caesar +// その以降の項目はどこにも代入されていません +``` + +続く項目もすべて取得したい場合は、3つのドット `"..."` を使用して "残り" を取得するパラメータを1つ追加します。: + +```js run +let [name1, name2, *!*...rest*/!*] = ["Julius", "Caesar", *!*"Consul", "of the Roman Republic"*/!*]; *!* +// rest は3つ目の項目からの配列です alert(rest[0]); // Consul alert(rest[1]); // of the Roman Republic alert(rest.length); // 2 */!* ``` -`rest` は残りの値が要素として格納されている配列です。 `rest` の代わりに他の変数名を使うこともできます。変数名の前には3つのドットがあり、利用時には分割代入の最後にくるようにしてください。 +`rest `の値は、残りの配列要素の配列です。 + +`rest` の代わりに他の変数名を使用できます。その前に3つのドットがあり、分割代入の最後にくるようにしてください。 + +```js run +let [name1, name2, *!*...titles*/!*] = ["Julius", "Caesar", "Consul", "of the Roman Republic"]; +// now titles = ["Consul", "of the Roman Republic"] +``` ### デフォルト値 @@ -150,6 +195,7 @@ let [firstName, surname] = []; */!* alert(firstName); // undefined +alert(surname); // undefined ``` 値がなかった場合に "デフォルト" 値を使いたければ、`=` を使ってデフォルト値を指定することができます: @@ -176,6 +222,7 @@ alert(name); // Julius (配列から) alert(surname); // プロンプトが得たもの ``` +注意: `prompt` は値がない場合(`surname`)にのみ実行されます。 ## オブジェクトの非構造化 @@ -207,7 +254,9 @@ alert(width); // 100 alert(height); // 200 ``` -プロパティ `options.title`, `options.width` と `options.height` は、該当する変数に代入されます。順序は関係ありません。これも動作します。: +プロパティ `options.title`, `options.width` と `options.height` は、該当する変数に代入されます。順序は関係ありません。 + +順番は関係ありません。これも動作します。: ```js // let {...} 内のプロパティ順を変えた場合 @@ -290,11 +339,26 @@ alert(w); // 100 alert(h); // 200 ``` -### 残りの演算子(Rest operator) +多くのプロパティをもつ複雑なオブジェクトがあったとしても、必要なものだけを抽出することができます: + +```js run +let options = { + title: "Menu", + width: 100, + height: 200 +}; + +// title だけ変数として抽出 +let { title } = options; + +alert(title); // Menu +``` + +### 残りのパターン "..." 仮に、指定した変数よりも多くのプロパティをオブジェクトがもっていたらどうなるでしょうか。いくつか設定した後、"残り" をどこかにまとめて代入することはできるでしょうか? -ここで、残りの演算子(3つのドット)を使用するという仕様はほぼ標準ですが、ほとんどのブラウザではまだサポートされていません。 +配列でしたのと同じように、残りのパターンを使用することができます。いくつかの古いブラウザ(IE、polyfill するために Babel を使用)ではサポートされていませんが、モダンブラウザでは動作します。 このようになります: @@ -306,6 +370,8 @@ let options = { }; *!* +// title = title と名前付けられたプロパティ +// rest = オブジェクトのプロパティの残り let {title, ...rest} = options; */!* @@ -314,10 +380,8 @@ alert(rest.height); // 200 alert(rest.width); // 100 ``` - - ````smart header="Gotcha without `let`" -上の例で、変数は代入の直前に宣言されています: `let {…} = {…}`。もちろん既存の変数を使うこともできますが、罠もあります。 +上の例で、変数は代入の直前に宣言されています: `let {…} = {…}`。もちろん `let` なしで既存の変数を使うこともできますが、罠もあります。 これは動作しません: ```js run @@ -327,7 +391,7 @@ let title, width, height; {title, width, height} = {title: "Menu", width: 200, height: 100}; ``` -問題は、JavaScriptがメインコードフローの `{...}' をコードブロックとして扱うことです。このようなコードブロックは、次のように文をグループ化するために使われます。 +問題は、JavaScriptがメインコードフローの `{...}` をコードブロックとして扱うことです。このようなコードブロックは、次のように文をグループ化するために使われます。 ```js run { @@ -338,6 +402,8 @@ let title, width, height; } ``` +そのため、ここでは JavaScript はコードブロックがあることを前提としています。したがってエラーになりますが、本当は分割代入がしたいです。 + コードブロックではないと JavaScript に示すためには、代入全体を括弧 `(...)` で囲む必要があります: ```js run @@ -348,7 +414,6 @@ let title, width, height; alert( title ); // Menu ``` - ```` ## 入れ子構造の非構造化 @@ -364,7 +429,7 @@ let options = { height: 200 }, items: ["Cake", "Donut"], - extra: true // 分割されない何か追加のデータ + extra: true }; // わかりやすくするために、複数の行での分割代入 From aa96450fb005ae2815adc794fcf5b79122682aeb Mon Sep 17 00:00:00 2001 From: Kenji Ishii Date: Sun, 8 Aug 2021 14:55:53 +0900 Subject: [PATCH 012/208] Update 1-js/05-data-types/10-destructuring-assignment --- .../10-destructuring-assignment/article.md | 29 ++++++++----------- 1 file changed, 12 insertions(+), 17 deletions(-) diff --git a/1-js/05-data-types/10-destructuring-assignment/article.md b/1-js/05-data-types/10-destructuring-assignment/article.md index f0d958d669..77cf784b9f 100644 --- a/1-js/05-data-types/10-destructuring-assignment/article.md +++ b/1-js/05-data-types/10-destructuring-assignment/article.md @@ -455,13 +455,7 @@ alert(item2); // Donut 最終的には、`width`, `height`, `item1`, `item2` と、デフォルト値から `title` を得ます。 -これは分割代入ではよく起こります。私たちは、多くのプロパティを持つ複雑なオブジェクトを持っており、必要なものだけを抽出したいからです。 - -このようなことも頻繁にあります。: -```js -// 変数全体から size を取り、残りは無視します -let { size } = options; -``` +代わりに中身を取得しているので、`size` と `items` の変数はないことに注意してください。 ## スマートな関数パラメータ @@ -480,6 +474,7 @@ function showMenu(title = "Untitled", width = 200, height = 100, items = []) { こうなりますか? ```js +// デフォルト値で良い場合は undefined にする showMenu("My Menu", undefined, undefined, ["Item1", "Item2"]) ``` @@ -534,27 +529,25 @@ showMenu(options); 構文は分割代入と同じです: ```js function({ - // 渡されるプロパティ: 内部で利用するパラメータ名 = デフォルト値 - incomingProperty: parameterName = defaultValue + incomingProperty: varName = defaultValue ... }) ``` +パラメータのオブジェクトに対して、プロパティ `incomingProperty` に対応する変数 `varName` があり、デフォルトでは `defaultValue` になります。 + なお、このような分割代入は `showMenu()` に引数があることを前提にしている点に注意してください。もしすべての値をデフォルトにしたい場合には、空のオブジェクトを指定する必要があります: ```js -showMenu({}); +showMenu({}); // OK, すべての値はデフォルト値になります -// これはエラーになります -showMenu(); +showMenu(); // これはエラーになります ``` これについては、非構造化対象全体のデフォルト値に `{}` を指定することで対応することができます: - ```js run -// 明快にするためのちょっとしたパラメータの簡略化 -function showMenu(*!*{ title = "Menu", width = 100, height = 200 } = {}*/!*) { +function showMenu({ title = "Menu", width = 100, height = 200 }*!* = {}*/!*) { alert( `${title} ${width} ${height}` ); } @@ -568,11 +561,13 @@ showMenu(); // Menu 100 200 - 分割代入はオブジェクトや配列を多数の変数に即座にマッピングすることができます。 - オブジェクト構文: ```js - let {prop : varName = default, ...} = object + let {prop : varName = default, ...rest} = object ``` これはプロパティ `prop` が変数 `varName` に代入され、もしこのようなプロパティが存在しない場合には `default` が使われることを意味します。 + マッピングがないオブジェクトプロパティは、`rest` オブジェクトへコピーされます。 + - 配列構文: ```js @@ -581,4 +576,4 @@ showMenu(); // Menu 100 200 最初のアイテムは `item1` に行き、2つ目は `item2` に行きます。残りのすべてのアイテムは配列 `rest` になります。 -- より複雑なケースでは、左辺は右辺と同じ構造を指定します。 +- ネストされた配列/オブジェクトからデータを抽出することも可能で、その場合、左辺は右辺と同じ構造を指定する必要があります。 From ab0a8810e1ccca82a900f1d84325e613f4bbfd0c Mon Sep 17 00:00:00 2001 From: Kenji Ishii Date: Sun, 8 Aug 2021 15:06:06 +0900 Subject: [PATCH 013/208] Update 1-js/05-data-types/11-date/article.md --- 1-js/05-data-types/11-date/article.md | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/1-js/05-data-types/11-date/article.md b/1-js/05-data-types/11-date/article.md index 459f30df92..8c481919bd 100644 --- a/1-js/05-data-types/11-date/article.md +++ b/1-js/05-data-types/11-date/article.md @@ -4,8 +4,6 @@ 例えば、作成/修正時刻を保存したり、時間を測定したり、単に現在の時刻を表示するために使うことができます。 -[cut] - ## 作成 新しい `Date` オブジェクトを作るには、 次のいずれかの引数で `new Date()` を呼びます: @@ -35,19 +33,30 @@ これは日付の軽量な数値表現です。常に `new Date(timestamp)` を使ってタイムスタンプから日付を作成し、存在する `Date` オブジェクトを `date.getTime()` メソッド(後述) を使ってタイムスタンプに変換します。 + 1970.01.01 以前の日付は負のタイムスタンプになります。例: + ```js run + // 31 Dec 1969 + let Dec31_1969 = new Date(-24 * 3600 * 1000); + alert( Dec31_1969 ); + ``` + `new Date(datestring)` -: 1つの引数でそれが文字列の場合、`Date.parse` アルゴリズム(後述)でパースされます。 +: 1つの引数でそれが文字列の場合、自動でパースされます。`Date.parse` アルゴリズム(後述)でパースされます。 ```js run let date = new Date("2017-01-26"); - alert(date); // Thu Jan 26 2017 ... + alert(date); + // 時刻が設定されていないので、GMT の深夜0時とみなされ、 + // コーザルが実行されるタイムゾーンに応じて調整されます。 + // したがって、結果は以下になります + // Thu Jan 26 2017 11:00:00 GMT+1100 (Australian Eastern Daylight Time) + // または + // Wed Jan 25 2017 16:00:00 GMT-0800 (Pacific Standard Time) ``` `new Date(year, month, date, hours, minutes, seconds, ms)` : ローカルタイムゾーンで、与えられた要素で日付を作成します。最初の2つの引数は必須です。 - 補足: - - `year` は4桁でなければいけません。`2013` はOKですが、`98` はダメです。 - `month` `0` (1月) から数え、`11` (12月)までです。 - `date` パラメータは実際の月の日です。もし指定がなければ `1` になります。 @@ -259,6 +268,8 @@ CPUを必要とする機能について、信頼できるベンチマークが 例えば、2つの日付の差を計算する2つの関数を測定してみましょう。どちらがより速いでしょうか? +このようなパフォーマンス測定はよく "ベンチマーク" と呼ばれます。 + ```js // date1 と date2 を持っており、これらの差をmsで返すのはどちらの関数が速いでしょう? function diffSubtract(date1, date2) { From 3272102d8d23879eed14962fda3f1a8e26a86663 Mon Sep 17 00:00:00 2001 From: Kenji Ishii Date: Sun, 8 Aug 2021 15:17:43 +0900 Subject: [PATCH 014/208] Update 1-js/05-data-types/12-json/article.md --- 1-js/05-data-types/12-json/article.md | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/1-js/05-data-types/12-json/article.md b/1-js/05-data-types/12-json/article.md index 13ca5b9064..a783992595 100644 --- a/1-js/05-data-types/12-json/article.md +++ b/1-js/05-data-types/12-json/article.md @@ -25,8 +25,6 @@ alert(user); // {name: "John", age: 30} 幸いにも、これらの処理を行うためにコードを書く必要はありません。この課題は既に解決されています。 -[cut] - ## JSON.stringify [JSON](http://en.wikipedia.org/wiki/JSON) (JavaScript Object Notation) は値とオブジェクトを表現する一般的な形式です。[RFC 4627](http://tools.ietf.org/html/rfc4627) で標準として記述されています。当初はJavaScriptのために作られたものでしたが、多くの他の言語も同様に JSON を処理するライブラリを持っています。従って、クライアントが JavaScript を使い、サーバが Ruby/PHP/Java/その他 で書かれている場合に、データ交換としてJSONを使うのは簡単です。 @@ -104,10 +102,10 @@ alert( JSON.stringify([1, 2, 3]) ); // [1,2,3] JSONはデータのみのマルチ言語仕様なので、JavaScript固有のオブジェクトプロパティの一部は `JSON.stringify` ではスキップされます。 -つまり、次のようなプロパティは無視されます: +つまり: - 関数プロパティ(メソッド) -- シンボルプロパティ +- シンボルキーと値 - `undefined` を格納しているプロパティ ```js run @@ -258,17 +256,17 @@ let room = { let meetup = { title: "Conference", participants: [{name: "John"}, {name: "Alice"}], - place: room // meetup が room を参照する + place: room // meetup は room を参照 }; -room.occupiedBy = meetup; // room が meetup を参照する +room.occupiedBy = meetup; // room は meetup を参照 alert( JSON.stringify(meetup, function replacer(key, value) { - alert(`${key}: ${value}`); // replacer が取得しているものを見るために + alert(`${key}: ${value}`); return (key == 'occupiedBy') ? undefined : value; })); -/* replacer に来た key:value ペア: +/* key:value pairs that come to replacer: : [object Object] title: Conference participants: [object Object],[object Object] @@ -278,6 +276,7 @@ name: John name: Alice place: [object Object] number: 23 +occupiedBy: [object Object] */ ``` @@ -330,6 +329,8 @@ alert(JSON.stringify(user, null, 2)); */ ``` +3番目の引数も文字列にすることができます。 この場合、文字列はスペースの数の代わりにインデントに使用されます。 + `spaces` パラメータは単にロギングや見やすい出力のためだけに使われます。 ## カスタムの "toJSON" @@ -397,6 +398,7 @@ alert( JSON.stringify(meetup) ); 上の通り、`toJSON` は `JSON.stringify(room)` の直接呼び出しとネストされたオブジェクト両方で使われます。 + ## JSON.parse JSON文字列をデコードするには、[JSON.parse](mdn:js/JSON/parse) と言うメソッドが必要です。 @@ -518,6 +520,7 @@ alert( schedule.meetups[1].date.getDate() ); // これも動作します! ``` + ## サマリ - JSON はほとんどのプログラミング言語に対して、自身の独立した標準とライブラリを持つデータ形式です。 From cc1f2bdfbfafc540f343477a2baa7b86f68b64fc Mon Sep 17 00:00:00 2001 From: Kenji Ishii Date: Sun, 8 Aug 2021 15:41:19 +0900 Subject: [PATCH 015/208] Update 1-js/06-advanced-functions/01-recursion/article.md --- .../01-recursion/article.md | 30 ++++++++++++------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/1-js/06-advanced-functions/01-recursion/article.md b/1-js/06-advanced-functions/01-recursion/article.md index 3106b0c16d..d7ec4b1ed1 100644 --- a/1-js/06-advanced-functions/01-recursion/article.md +++ b/1-js/06-advanced-functions/01-recursion/article.md @@ -4,14 +4,12 @@ 最初のトピックは *再帰* です。 -あなたが初心者のプログラマーでない場合、おそらく知っている内容なのでこのチャプターをスキップしても問題ありません。 +あなたがプログラミング初心者でないなら、おそらく馴染みのある内容なのでこのチャプターをスキップしても問題ありません。 再帰は、タスクを同じ種類の複数のタスクに分割することができる状況で役立つプログラミングパターンで、よりシンプルに実現することができます。あるいは、タスクを簡単なアクションと同じタスクのよりシンプルなパターンに単純化できる場合や、この後すぐに見ていきますが特定のデータ構造を扱う場合にも役立ちます。 関数がタスクを解決するとき、処理の過程で多くの他の関数を呼ぶことができます。この部分的なケースとして、関数が *自分自身* を呼ぶときです。それは *再帰* と呼ばれます。 -[cut] - ## 2つの考え方 初めのシンプルな例として、`x` の `n` 乗をする関数 `pow(x, n)` を書いてみましょう。つまり、`x` 自身を `n` 回乗算します。 @@ -59,7 +57,6 @@ pow(2, 4) = 16 `pow(x, n)` が呼ばれたとき、その実行は2つの分岐に分かれます。: - ```js if n==1 = x / @@ -76,7 +73,7 @@ pow(x, n) = ![recursive diagram of pow](recursion-pow.svg) -例えば、`pow(2, 4)` を計算するために、再帰的なバリアントは次のようなステップを踏みます: +例えば、`pow(2, 4)` を計算するために、再帰的なパターンでは次のようなステップを踏みます: 1. `pow(2, 4) = 2 * pow(2, 3)` 2. `pow(2, 3) = 2 * pow(2, 2)` @@ -103,7 +100,7 @@ function pow(x, n) { それは再帰の使用を制限しますが、依然として非常に広範囲に使われています。再帰的な考え方でコードがシンプルになり、保守が容易になるタスクはたくさんあります。 -## 実行スタック +## 実行コンテキストとスタック さて、どのように再帰呼び出しが動作するか検証してみましょう。そのためには関数の内部を見ていきます。 @@ -151,6 +148,7 @@ function pow(x, n) { alert( pow(2, 3) ); ``` + 変数は同じですが、行が変わっています。なので、コンテキストは次のようになります: