From 1f425f1dd22a6af47e27a80366cd680e8436a4f9 Mon Sep 17 00:00:00 2001 From: Takuya Shizukuishi Date: Mon, 14 Apr 2025 09:59:46 +0900 Subject: [PATCH 1/8] Merge pull request #9 from recruit-tech/feature-closure MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit feat: クロージャの課題を追加 --- i18n/ja.json | 1 + menu.json | 1 + problems/closure/problem_ja.md | 56 +++++++++++++++++++++++++++++++++ problems/closure/solution_ja.md | 7 +++++ solutions/closure/index.js | 23 ++++++++++++++ 5 files changed, 88 insertions(+) create mode 100644 problems/closure/problem_ja.md create mode 100644 problems/closure/solution_ja.md create mode 100644 solutions/closure/index.js diff --git a/i18n/ja.json b/i18n/ja.json index 92c4df18..25f50f9f 100644 --- a/i18n/ja.json +++ b/i18n/ja.json @@ -22,6 +22,7 @@ "FUNCTIONS": "関数", "FUNCTION ARGUMENTS": "関数の引数", "ARROW FUNCTIONS": "アロー関数", + "CLOSURE": "クロージャ", "DESTRUCTURING": "分割代入", "CALLBACK": "コールバック関数", "PROMISE": "Promiseオブジェクト", diff --git a/menu.json b/menu.json index 6e4c690a..eb53b909 100644 --- a/menu.json +++ b/menu.json @@ -31,5 +31,6 @@ "FIZZBUZZ", "USE REGEX LUKE", "SCOPE", + "CLOSURE", "PAGINATION" ] diff --git a/problems/closure/problem_ja.md b/problems/closure/problem_ja.md new file mode 100644 index 00000000..ca514341 --- /dev/null +++ b/problems/closure/problem_ja.md @@ -0,0 +1,56 @@ +JavaScriptでは、関数定義の内側から外側の変数を参照できます。 + +```js +const makeCounter = () => { + // makeCounterを実行すると、count変数が宣言される + let count = 0; + // count変数は、incrementの関数定義の外側にある + + // incrementの関数定義 + const increment = () => { + // incrementの関数定義の内部にcount変数はない + + count++; // 外側のcount変数を参照して更新できる + return count; // 外側のcount変数を参照してreturnできる + }; + + return [increment]; +}; + +const [increment] = makeCounter(); +console.log(increment()); // 1 +console.log(increment()); // 2 +``` + +このような動作は、クロージャ(定義時の外側の環境を知っている関数)によって実現されますが、詳細な説明は割愛します。ここでは「JavaScriptの関数では、定義の内側から外側の変数を参照できる」ということがわかっていれば大丈夫です。 + +また、`increment`実行時に「`makeCounter`の実行が終了しているにもかかわらず`count`変数を利用できる」ことに驚くかもしれません。 + +一部の言語(C言語など)では、関数内で定義された変数は、その関数の実行が終了すると同時に消えてしまって利用不可能になります。しかしJavaScriptでは、関数によって参照される可能性のある変数は消えません。 + +## やってみよう + +`increment`(プラス1)だけではなく、`decrement`(マイナス1)もできるカウンターを作りましょう。 + +`closure.js`ファイルを作成し、以下のテンプレートをコピペしてコードを書いてみてください。 + +```js +// テンプレート +const makeCounter = () => { + // ここにコードを書く +}; + +// 以下は変更しない +const [increment, decrement] = makeCounter(); +console.log(increment()); // 1 +console.log(increment()); // 2 +console.log(decrement()); // 1 +console.log(decrement()); // 0 +console.log(increment()); // 1 +``` + +コードが書けたら次のコマンドを実行し、あなたのプログラムが正しく動くか確認しましょう。 + +```bash +javascripting verify closure.js +``` diff --git a/problems/closure/solution_ja.md b/problems/closure/solution_ja.md new file mode 100644 index 00000000..a56110ee --- /dev/null +++ b/problems/closure/solution_ja.md @@ -0,0 +1,7 @@ +--- +# 検証成功です! + +カウンターが動作しました。 + +コンソールで `javascripting` コマンドを実行します。次の課題を選択しましょう。 +--- diff --git a/solutions/closure/index.js b/solutions/closure/index.js new file mode 100644 index 00000000..f3195f3e --- /dev/null +++ b/solutions/closure/index.js @@ -0,0 +1,23 @@ +const makeCounter = () => { + let count = 0; + + const increment = () => { + count++; + return count; + }; + + const decrement = () => { + count--; + return count; + }; + + return [increment, decrement]; +}; + +// 以下は変更しない +const [increment, decrement] = makeCounter(); +console.log(increment()); // 1 +console.log(increment()); // 2 +console.log(decrement()); // 1 +console.log(decrement()); // 0 +console.log(increment()); // 1 From 4a5f879fd10bef2e163a85452ab7ac528f48bc32 Mon Sep 17 00:00:00 2001 From: TxxM6 Date: Mon, 14 Apr 2025 19:42:29 +0900 Subject: [PATCH 2/8] =?UTF-8?q?feat:=20fetch=E3=81=AE=E8=AA=B2=E9=A1=8C?= =?UTF-8?q?=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- i18n/ja.json | 3 ++- lib/problem.js | 30 +++++++++++++++++++++- menu.json | 1 + problems/fetch/problem_ja.md | 47 +++++++++++++++++++++++++++++++++++ problems/fetch/solution_ja.md | 7 ++++++ solutions/fetch/index.mjs | 13 ++++++++++ 6 files changed, 99 insertions(+), 2 deletions(-) create mode 100644 problems/fetch/problem_ja.md create mode 100644 problems/fetch/solution_ja.md create mode 100644 solutions/fetch/index.mjs diff --git a/i18n/ja.json b/i18n/ja.json index 92c4df18..95e33445 100644 --- a/i18n/ja.json +++ b/i18n/ja.json @@ -28,6 +28,7 @@ "PROMISE ALL": "Promise.all", "ASYNC AWAIT": "async/await", "SCOPE": "スコープ", - "USE REGEX LUKE": "正規表現を使え、ルーク" + "USE REGEX LUKE": "正規表現を使え、ルーク", + "FETCH": "Fetch API" } } diff --git a/lib/problem.js b/lib/problem.js index 8babe453..e0897323 100644 --- a/lib/problem.js +++ b/lib/problem.js @@ -1,4 +1,5 @@ const path = require("path"); +const fs = require("fs"); const getFile = require("./get-file"); const compare = require("./compare-solution"); @@ -16,13 +17,40 @@ module.exports = function createProblem(dirname) { workshopper.i18n.lang() === "en" ? "" : "_" + workshopper.i18n.lang(); this.problem = { file: path.join(dirname, "problem" + postfix + ".md") }; this.solution = { file: path.join(dirname, "solution" + postfix + ".md") }; - this.solutionPath = path.resolve( + + // Check for the existence of index.[js, mjs, cjs] + const jsSolutionPath = path.resolve( __dirname, "..", "solutions", problemName, "index.js", ); + const mjsSolutionPath = path.resolve( + __dirname, + "..", + "solutions", + problemName, + "index.mjs", + ); + const cjsSolutionPath = path.resolve( + __dirname, + "..", + "solutions", + problemName, + "index.cjs", + ); + // Set solutionPath to the first existing file + if (fs.existsSync(jsSolutionPath)) { + this.solutionPath = jsSolutionPath; + } else if (fs.existsSync(mjsSolutionPath)) { + this.solutionPath = mjsSolutionPath; + } else if (fs.existsSync(cjsSolutionPath)) { + this.solutionPath = cjsSolutionPath; + } else { + throw new Error(`No solution file found for problem: ${problemName}`); + } + this.troubleshootingPath = path.join( __dirname, "..", diff --git a/menu.json b/menu.json index 6e4c690a..b331770b 100644 --- a/menu.json +++ b/menu.json @@ -28,6 +28,7 @@ "ASYNC AWAIT", "ARRAY FILTERING", "ARRAYS MORE", + "FETCH", "FIZZBUZZ", "USE REGEX LUKE", "SCOPE", diff --git a/problems/fetch/problem_ja.md b/problems/fetch/problem_ja.md new file mode 100644 index 00000000..0f44e851 --- /dev/null +++ b/problems/fetch/problem_ja.md @@ -0,0 +1,47 @@ +Fetch API は JavaScript で HTTPリクエストを実行するためのAPIです。 +`fetch` 関数の戻り値は `Promise` です。第一引数に URL、第二引数には任意でリクエストのメソッド(デフォルトは `GET`)、ヘッダー、リクエストボディなどを設定できます。 +以下のコードは使用例です。 + +```js +// async awaitを利用する場合 +async function fetchData() { + const response = await fetch("https://example.com"); + // 200 – 299 の範囲外のステータスの時 + if (!response.ok) { + throw new Error(`Error! Status: ${response.status}`); + } + // レスポンスのボディをJSONとして解析 + const data = await response.json(); + return data; +} + +// fetchData関数を呼び出す +const data = await fetchData(); +``` + +`fetch` 関数の戻り値の `Promise` が解決されると、HTTPレスポンスが `Response` オブジェクトとして取得できます。 +この `Response` オブジェクトはいくつかのメソッドを持っています。例えば `.json()` メソッドは、レスポンスボディの中身を JSON として解析し、オブジェクトなどの結果を `Promise` で返します。 + +## やってみよう + +`fetch.mjs` ファイルを作りましょう。 + +今回は、https://dummyjson.com というサイトを利用して、HTTPリクエストを送ってみましょう。 +下記は、https://dummyjson.com へのリクエストとレスポンスの例です。https://dummyjson.com/todos/1 を GET すると、ステータスコードが200で、ボディが「Todoを表すJSON文字列」であるレスポンスが得られます。 + +```js +await fetch("https://dummyjson.com/todos/1"); + +/* response body */ +// {"id":1,"todo":"Do something nice for someone you care about","completed":false,"userId":152} +``` + +`console.log()` を使って、https://dummyjson.com/todos/1 のレスポンスボディの一部である `todo` プロパティの値を表示しましょう。 + +次のコマンドを実行し、あなたのプログラムが正しく動くか確認しましょう。 + +```bash +javascripting verify fetch.mjs +``` + +ここでは詳しくは述べませんが、ファイル拡張子を `.mjs` にすることで Top-level awaitを利用できます。(Top-level await を使用しているファイル全体が1つの大きな `async` 関数のように機能します。) diff --git a/problems/fetch/solution_ja.md b/problems/fetch/solution_ja.md new file mode 100644 index 00000000..95ecb947 --- /dev/null +++ b/problems/fetch/solution_ja.md @@ -0,0 +1,7 @@ +--- +# fetchを使ってデータを取得できましたね! + +これでこの演習は終わりです。 + +コンソールで `javascripting` コマンドを実行します。次の課題を選択しましょう。 +--- diff --git a/solutions/fetch/index.mjs b/solutions/fetch/index.mjs new file mode 100644 index 00000000..becac955 --- /dev/null +++ b/solutions/fetch/index.mjs @@ -0,0 +1,13 @@ +// Top-level await +const todo = await fetchTodo(); +console.log(todo); + +async function fetchTodo() { + const response = await fetch("https://dummyjson.com/todos/1"); + // 200 – 299 の範囲外のステータスの時 + if (!response.ok) { + throw new Error(`Error! Status: ${response.status}`); + } + const data = await response.json(); + return data.todo; +} From d3a1840045a1a71e99fcaca43ef858a0badb3668 Mon Sep 17 00:00:00 2001 From: progfay <19568747+progfay@users.noreply.github.com> Date: Wed, 23 Apr 2025 10:48:43 +0900 Subject: [PATCH 3/8] fix typo of booleans --- problems/booleans/problem_ja.md | 2 +- solutions/booleans/index.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/problems/booleans/problem_ja.md b/problems/booleans/problem_ja.md index 04742e78..9fbfdeb6 100644 --- a/problems/booleans/problem_ja.md +++ b/problems/booleans/problem_ja.md @@ -83,7 +83,7 @@ false || f(); // f is called! `booleans.js` ファイルを作り、以下のコードをコピーしてください。 ```js -console.log((0 === 1) === ""); +console.log((0 == 1) === ""); console.log((0 === 1) === ""); console.log((10 == "10") === ""); diff --git a/solutions/booleans/index.js b/solutions/booleans/index.js index 31e15a35..2a8f42bc 100644 --- a/solutions/booleans/index.js +++ b/solutions/booleans/index.js @@ -1,4 +1,4 @@ -console.log((0 === 1) === false); +console.log((0 == 1) === false); console.log((0 === 1) === false); console.log((10 == "10") === true); From 0a8140ccc2b6a2305f06983a789f40fa0192bc7b Mon Sep 17 00:00:00 2001 From: progfay <19568747+progfay@users.noreply.github.com> Date: Wed, 23 Apr 2025 10:50:05 +0900 Subject: [PATCH 4/8] 3.0.4 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 9da0ed64..c0326bf2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@recruit-tech/javascripting", - "version": "3.0.3", + "version": "3.0.4", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@recruit-tech/javascripting", - "version": "3.0.3", + "version": "3.0.4", "license": "MIT", "dependencies": { "colors": "1.4.0", diff --git a/package.json b/package.json index 713ce764..4f2cec22 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@recruit-tech/javascripting", "description": "Learn JavaScript by adventuring around in the terminal.", - "version": "3.0.3", + "version": "3.0.4", "repository": { "url": "https://github.com/recruit-tech/javascripting.git" }, From 1b1671e1bd7cdd27571dd36aa26ca9eafc1e23b7 Mon Sep 17 00:00:00 2001 From: Shunsuke Mano <19568747+progfay@users.noreply.github.com> Date: Thu, 15 May 2025 12:54:54 +0900 Subject: [PATCH 5/8] fix typo --- problems/booleans/problem_ja.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/problems/booleans/problem_ja.md b/problems/booleans/problem_ja.md index 9fbfdeb6..7b520a4c 100644 --- a/problems/booleans/problem_ja.md +++ b/problems/booleans/problem_ja.md @@ -100,7 +100,7 @@ console.log((0 || 10) === ""); console.log((1 || 10) === ""); ``` -全ての `console.log` による出力が `true` となるように、`''` と書かれた場所を書き換えてください。 (文字列である必要はありません。) +全ての `console.log` による出力が `true` となるように、`''` と書かれた場所を書き換えてください。 (文字列である必要はありません。) 次のコマンドを実行し、あなたのプログラムが正しく動くか確認しましょう。 From b299cad0d955f6d370812b4bfe798a7cd0ebef87 Mon Sep 17 00:00:00 2001 From: Shunsuke Mano <19568747+progfay@users.noreply.github.com> Date: Thu, 15 May 2025 12:55:18 +0900 Subject: [PATCH 6/8] fix typo --- problems/null-undefined/problem_ja.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/problems/null-undefined/problem_ja.md b/problems/null-undefined/problem_ja.md index 16f7f31d..9c8e360f 100644 --- a/problems/null-undefined/problem_ja.md +++ b/problems/null-undefined/problem_ja.md @@ -96,7 +96,7 @@ console.log((false || "default") === ""); console.log((false ?? "default") === ""); ``` -全ての `console.log` による出力が `true` となるように、`''` と書かれた場所を書き換えてください。 (文字列である必要はありません。) +全ての `console.log` による出力が `true` となるように、`''` と書かれた場所を書き換えてください。 (文字列である必要はありません。) 次のコマンドを実行し、あなたのプログラムが正しく動くか確認しましょう。 From 0cf7b47e0c8c1719e873dfd9595a2fe6caccf382 Mon Sep 17 00:00:00 2001 From: ryusuke-yamaji <274366809+ryusuke-yamaji@users.noreply.github.com> Date: Wed, 27 May 2026 14:41:12 +0900 Subject: [PATCH 7/8] =?UTF-8?q?Promise.any=20=E3=81=AE=E8=AA=AC=E6=98=8E?= =?UTF-8?q?=E6=96=87=E3=81=AE=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- problems/promise-all/problem_ja.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/problems/promise-all/problem_ja.md b/problems/promise-all/problem_ja.md index 19db1585..8eb9fc8a 100644 --- a/problems/promise-all/problem_ja.md +++ b/problems/promise-all/problem_ja.md @@ -20,7 +20,7 @@ Promise.all([ `Promise.any` 関数では引数の配列の要素内の全ての `Promise` の解決のを待つのとは逆に、どれかひとつが解決されることで返り値の `Promise` も解決されます。 返り値の `Promise` によって解決される値は、引数の配列の要素の `Promise` の中で一番最初に解決された値になります。 -また、配列内の `Promise` のうちのいづれかが拒否されると返り値の `Promise` も拒否されます。 +また、配列内の `Promise` のすべてが拒否されると返り値の `Promise` も拒否されます。 ```js // resolved after 1000ms From 167fb1f78856932d05d14a51067b51a6a8277253 Mon Sep 17 00:00:00 2001 From: ryusuke-yamaji <274366809+ryusuke-yamaji@users.noreply.github.com> Date: Thu, 28 May 2026 14:56:11 +0900 Subject: [PATCH 8/8] =?UTF-8?q?=E6=97=A5=E6=9C=AC=E8=AA=9E=E8=A1=A8?= =?UTF-8?q?=E7=8F=BE=E3=81=AE=E5=BE=AE=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- problems/promise-all/problem_ja.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/problems/promise-all/problem_ja.md b/problems/promise-all/problem_ja.md index 8eb9fc8a..60ae05f3 100644 --- a/problems/promise-all/problem_ja.md +++ b/problems/promise-all/problem_ja.md @@ -20,7 +20,7 @@ Promise.all([ `Promise.any` 関数では引数の配列の要素内の全ての `Promise` の解決のを待つのとは逆に、どれかひとつが解決されることで返り値の `Promise` も解決されます。 返り値の `Promise` によって解決される値は、引数の配列の要素の `Promise` の中で一番最初に解決された値になります。 -また、配列内の `Promise` のすべてが拒否されると返り値の `Promise` も拒否されます。 +また、配列内のすべての `Promise` が拒否されると返り値の `Promise` も拒否されます。 ```js // resolved after 1000ms