[{"data":1,"prerenderedAt":5808},["ShallowReactive",2],{"book:\u002Fbooks\u002F2025\u002Forganize-learned-from-docker":3,"search:articles":706,"search:books":5314},{"id":4,"title":5,"author":6,"body":7,"description":695,"extension":696,"meta":697,"navigation":286,"path":698,"publishDate":699,"published":286,"publisher":700,"seo":701,"stem":702,"tags":703,"__hash__":705},"books\u002Fbooks\u002F2025\u002Forganize-learned-from-docker.md","Docker&仮想サーバー完全入門","リブロワークス",{"type":8,"value":9,"toc":682},"minimark",[10,21,24,29,32,35,43,47,50,113,129,132,153,156,160,167,191,201,209,215,223,237,243,251,257,313,319,344,348,355,390,396,399,402,461,464,471,485,488,505,515,540,543,550,564,575,595,608,612,615,622,628,631,634,637,640,643,675,678],[11,12,13,14,20],"p",{},"『",[15,16,5],"a",{"href":17,"rel":18},"https:\u002F\u002Fbook.impress.co.jp\u002Fbooks\u002F1121101138",[19],"nofollow","』を読了したため、本書を通して学んだことをまとめます。",[11,22,23],{},"まだ1周目で理解が浅い部分もあるため、Dockerを実際に使いながら必要に応じて追記していきます。",[25,26,28],"h2",{"id":27},"dockerでホスト環境への影響を抑える","Dockerでホスト環境への影響を抑える",[11,30,31],{},"Dockerを利用すると、アプリケーションに必要なランタイムやライブラリをコンテナ内にまとめられます。",[11,33,34],{},"ホストOSへ依存関係を直接インストールする場面を減らせるため、開発環境を整理しやすくなります。また、DockerfileやComposeファイルを共有することで、チーム内で環境を再現しやすくなります。",[36,37,40],"article-callout",{"title":38,"type":39},"ホストOSから完全に独立するわけではない","note",[11,41,42],{},"コンテナはホストのカーネルやDocker Engineを利用します。ボリューム、ポート、CPUアーキテクチャなど、ホスト環境との接点がなくなるわけではありません。",[25,44,46],{"id":45},"dockerコマンドで操作対象を明示する","Dockerコマンドで操作対象を明示する",[11,48,49],{},"Docker CLIには、操作対象を含めて記述するコマンドがあります。",[51,52,57],"pre",{"className":53,"code":54,"language":55,"meta":56,"style":56},"language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","docker container run nginx\ndocker image pull nginx\ndocker volume ls\ndocker network ls\n","bash","",[58,59,60,79,92,103],"code",{"__ignoreMap":56},[61,62,65,69,73,76],"span",{"class":63,"line":64},"line",1,[61,66,68],{"class":67},"sBMFI","docker",[61,70,72],{"class":71},"sfazB"," container",[61,74,75],{"class":71}," run",[61,77,78],{"class":71}," nginx\n",[61,80,82,84,87,90],{"class":63,"line":81},2,[61,83,68],{"class":67},[61,85,86],{"class":71}," image",[61,88,89],{"class":71}," pull",[61,91,78],{"class":71},[61,93,95,97,100],{"class":63,"line":94},3,[61,96,68],{"class":67},[61,98,99],{"class":71}," volume",[61,101,102],{"class":71}," ls\n",[61,104,106,108,111],{"class":63,"line":105},4,[61,107,68],{"class":67},[61,109,110],{"class":71}," network",[61,112,102],{"class":71},[11,114,115,118,119,118,122,118,125,128],{},[58,116,117],{},"container","、",[58,120,121],{},"image",[58,123,124],{},"volume",[58,126,127],{},"network","のように対象が明示されるため、何を操作するコマンドなのか把握しやすくなります。",[11,130,131],{},"一方で、次の短い形式も現在利用できます。",[51,133,135],{"className":53,"code":134,"language":55,"meta":56,"style":56},"docker run nginx\ndocker pull nginx\n",[58,136,137,145],{"__ignoreMap":56},[61,138,139,141,143],{"class":63,"line":64},[61,140,68],{"class":67},[61,142,75],{"class":71},[61,144,78],{"class":71},[61,146,147,149,151],{"class":63,"line":81},[61,148,68],{"class":67},[61,150,89],{"class":71},[61,152,78],{"class":71},[11,154,155],{},"短い形式を古いという理由だけで避ける必要はありません。学習時には対象を含む形式も知っておくと、CLIの構造を理解しやすいと感じました。",[25,157,159],{"id":158},"docker-compose-v2を利用する","Docker Compose V2を利用する",[11,161,162,163,166],{},"現在のCompose CLIでは、ハイフンのない",[58,164,165],{},"docker compose","を使用します。",[51,168,170],{"className":53,"code":169,"language":55,"meta":56,"style":56},"docker compose up\ndocker compose down\n",[58,171,172,182],{"__ignoreMap":56},[61,173,174,176,179],{"class":63,"line":64},[61,175,68],{"class":67},[61,177,178],{"class":71}," compose",[61,180,181],{"class":71}," up\n",[61,183,184,186,188],{"class":63,"line":81},[61,185,68],{"class":67},[61,187,178],{"class":71},[61,189,190],{"class":71}," down\n",[11,192,193,194,197,198,200],{},"旧Compose V1で使われていた",[58,195,196],{},"docker-compose","とはコマンド形式が異なります。Docker DesktopにはCompose CLIが含まれているため、基本的には",[58,199,165],{},"を利用します。",[25,202,204,205,208],{"id":203},"composeファイルはcomposeyamlを使う","Composeファイルは",[58,206,207],{},"compose.yaml","を使う",[11,210,211,212,214],{},"Composeファイルの推奨ファイル名は",[58,213,207],{},"です。",[51,216,221],{"className":217,"code":219,"language":220,"meta":56},[218],"language-text","compose.yaml\n","text",[58,222,219],{"__ignoreMap":56},[11,224,225,228,229,232,233,236],{},[58,226,227],{},"compose.yml","も利用できます。また、",[58,230,231],{},"docker-compose.yaml","と",[58,234,235],{},"docker-compose.yml","も後方互換性のためサポートされています。",[11,238,239,240,242],{},"複数の候補が同じ場所にある場合は",[58,241,207],{},"が優先されるため、新しく作る場合はこの名前へ揃えると分かりやすくなります。",[25,244,246,247,250],{"id":245},"トップレベルのversionは不要","トップレベルの",[58,248,249],{},"version","は不要",[11,252,253,254,256],{},"以前のComposeファイルでは、先頭に次のような",[58,255,249],{},"を書く例がありました。",[51,258,262],{"className":259,"code":260,"language":261,"meta":56,"style":56},"language-yaml shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","version: '3'\n\nservices:\n  web:\n    image: nginx\n","yaml",[58,263,264,282,288,296,303],{"__ignoreMap":56},[61,265,266,269,273,276,279],{"class":63,"line":64},[61,267,249],{"class":268},"swJcz",[61,270,272],{"class":271},"sMK4o",":",[61,274,275],{"class":271}," '",[61,277,278],{"class":71},"3",[61,280,281],{"class":271},"'\n",[61,283,284],{"class":63,"line":81},[61,285,287],{"emptyLinePlaceholder":286},true,"\n",[61,289,290,293],{"class":63,"line":94},[61,291,292],{"class":268},"services",[61,294,295],{"class":271},":\n",[61,297,298,301],{"class":63,"line":105},[61,299,300],{"class":268},"  web",[61,302,295],{"class":271},[61,304,306,309,311],{"class":63,"line":305},5,[61,307,308],{"class":268},"    image",[61,310,272],{"class":271},[61,312,78],{"class":71},[11,314,315,316,318],{},"現在、トップレベルの",[58,317,249],{},"は後方互換性のために残されたobsoleteな項目です。指定してもComposeは最新のスキーマでファイルを検証するため、新しいComposeファイルでは省略します。",[51,320,322],{"className":259,"code":321,"language":261,"meta":56,"style":56},"services:\n  web:\n    image: nginx\n",[58,323,324,330,336],{"__ignoreMap":56},[61,325,326,328],{"class":63,"line":64},[61,327,292],{"class":268},[61,329,295],{"class":271},[61,331,332,334],{"class":63,"line":81},[61,333,300],{"class":268},[61,335,295],{"class":271},[61,337,338,340,342],{"class":63,"line":94},[61,339,308],{"class":268},[61,341,272],{"class":271},[61,343,78],{"class":71},[25,345,347],{"id":346},"restart-policyは用途に合わせて設定する","restart policyは用途に合わせて設定する",[11,349,350,351,354],{},"Composeのサービスには、コンテナを再起動する条件を",[58,352,353],{},"restart","で設定できます。",[51,356,358],{"className":259,"code":357,"language":261,"meta":56,"style":56},"services:\n  web:\n    image: nginx\n    restart: always\n",[58,359,360,366,372,380],{"__ignoreMap":56},[61,361,362,364],{"class":63,"line":64},[61,363,292],{"class":268},[61,365,295],{"class":271},[61,367,368,370],{"class":63,"line":81},[61,369,300],{"class":268},[61,371,295],{"class":271},[61,373,374,376,378],{"class":63,"line":94},[61,375,308],{"class":268},[61,377,272],{"class":271},[61,379,78],{"class":71},[61,381,382,385,387],{"class":63,"line":105},[61,383,384],{"class":268},"    restart",[61,386,272],{"class":271},[61,388,389],{"class":71}," always\n",[11,391,392,395],{},[58,393,394],{},"always","を指定したコンテナは、停止した場合やDockerデーモンが再起動した場合に再起動されます。",[11,397,398],{},"常時稼働が必要なサービスでは便利ですが、学習用コンテナへ一律に指定すると、Docker起動時に意図しないコンテナまで立ち上がることがあります。必要性を考えて設定することが大切です。",[11,400,401],{},"主なrestart policyには次があります。",[403,404,405,418],"table",{},[406,407,408],"thead",{},[409,410,411,415],"tr",{},[412,413,414],"th",{},"値",[412,416,417],{},"動作",[419,420,421,432,442,451],"tbody",{},[409,422,423,429],{},[424,425,426],"td",{},[58,427,428],{},"no",[424,430,431],{},"自動的に再起動しない",[409,433,434,439],{},[424,435,436],{},[58,437,438],{},"on-failure",[424,440,441],{},"エラー終了した場合に再起動する",[409,443,444,448],{},[424,445,446],{},[58,447,394],{},[424,449,450],{},"停止した場合に常に再起動する",[409,452,453,458],{},[424,454,455],{},[58,456,457],{},"unless-stopped",[424,459,460],{},"明示的に停止されていない限り再起動する",[25,462,463],{"id":463},"コンテナのログを確認する",[11,465,466,467,470],{},"Composeで起動したサービスのログは",[58,468,469],{},"docker compose logs","で確認できます。",[51,472,474],{"className":53,"code":473,"language":55,"meta":56,"style":56},"docker compose logs\n",[58,475,476],{"__ignoreMap":56},[61,477,478,480,482],{"class":63,"line":64},[61,479,68],{"class":67},[61,481,178],{"class":71},[61,483,484],{"class":71}," logs\n",[11,486,487],{},"サービスを指定すると、対象のログだけを表示できます。",[51,489,491],{"className":53,"code":490,"language":55,"meta":56,"style":56},"docker compose logs web\n",[58,492,493],{"__ignoreMap":56},[61,494,495,497,499,502],{"class":63,"line":64},[61,496,68],{"class":67},[61,498,178],{"class":71},[61,500,501],{"class":71}," logs",[61,503,504],{"class":71}," web\n",[11,506,507,508,511,512,166],{},"ログを継続して追いかける場合は",[58,509,510],{},"--follow","、末尾の行数を限定する場合は",[58,513,514],{},"--tail",[51,516,518],{"className":53,"code":517,"language":55,"meta":56,"style":56},"docker compose logs --follow --tail 100 web\n",[58,519,520],{"__ignoreMap":56},[61,521,522,524,526,528,531,534,538],{"class":63,"line":64},[61,523,68],{"class":67},[61,525,178],{"class":71},[61,527,501],{"class":71},[61,529,530],{"class":71}," --follow",[61,532,533],{"class":71}," --tail",[61,535,537],{"class":536},"sbssI"," 100",[61,539,504],{"class":71},[25,541,542],{"id":542},"イメージを再ビルドする",[11,544,545,546,549],{},"Composeでイメージをビルドするには",[58,547,548],{},"docker compose build","を使います。",[51,551,553],{"className":53,"code":552,"language":55,"meta":56,"style":56},"docker compose build\n",[58,554,555],{"__ignoreMap":56},[61,556,557,559,561],{"class":63,"line":64},[61,558,68],{"class":67},[61,560,178],{"class":71},[61,562,563],{"class":71}," build\n",[11,565,566,567,570,571,574],{},"コンテナの起動前にビルドも実行したい場合は、",[58,568,569],{},"docker compose up","へ",[58,572,573],{},"--build","を付けられます。",[51,576,578],{"className":53,"code":577,"language":55,"meta":56,"style":56},"docker compose up --detach --build\n",[58,579,580],{"__ignoreMap":56},[61,581,582,584,586,589,592],{"class":63,"line":64},[61,583,68],{"class":67},[61,585,178],{"class":71},[61,587,588],{"class":71}," up",[61,590,591],{"class":71}," --detach",[61,593,594],{"class":71}," --build\n",[11,596,597,599,600,603,604,607],{},[58,598,569],{},"は、サービスの設定やイメージが変更されている場合にコンテナを再作成します。ただし、Dockerfileやビルド対象を確実に再ビルドしてから起動したい場合は、",[58,601,602],{},"build","または",[58,605,606],{},"up --build","を明示すると意図が分かりやすくなります。",[25,609,611],{"id":610},"dev-containerで開発環境をコンテナ化する","Dev Containerで開発環境をコンテナ化する",[11,613,614],{},"VS CodeのDev Containers拡張機能を使うと、コンテナを開発環境として利用できます。",[11,616,617,618,621],{},"プロジェクト内の",[58,619,620],{},".devcontainer\u002Fdevcontainer.json","へ設定を記述し、コンテナ内のランタイム、ツール、VS Code拡張機能などを揃えられます。",[51,623,626],{"className":624,"code":625,"language":220,"meta":56},[218],".devcontainer\u002F\n  devcontainer.json\n",[58,627,625],{"__ignoreMap":56},[11,629,630],{},"ソースコードをコンテナへマウントし、ターミナル、補完、コードナビゲーション、デバッグなどを普段のVS Codeと近い操作感で利用できます。",[11,632,633],{},"環境構築手順を文章だけで共有するよりも、実行環境そのものを設定として共有できる点が便利だと感じました。",[25,635,636],{"id":636},"まとめ",[11,638,639],{},"本書を通して、Dockerの基本操作だけでなく、Composeによる複数コンテナの管理や、開発環境をコンテナ化する考え方を学べました。",[11,641,642],{},"特に、次の点を今後の開発でも意識したいです。",[644,645,646,650,655,660,666,669,672],"ul",{},[647,648,649],"li",{},"Docker CLIで何を操作しているか理解する",[647,651,652,653,208],{},"Compose V2の",[58,654,165],{},[647,656,204,657,659],{},[58,658,207],{},"へ揃える",[647,661,662,663,665],{},"不要な",[58,664,249],{},"を記述しない",[647,667,668],{},"restart policyを用途に合わせて設定する",[647,670,671],{},"ログ確認と再ビルドのコマンドを使い分ける",[647,673,674],{},"Dev Containerで再現可能な開発環境を試す",[11,676,677],{},"Dockerはコマンドを覚えるだけでなく、イメージ、コンテナ、ボリューム、ネットワークの関係を実際に操作しながら理解していきたいです。",[679,680,681],"style",{},"html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}",{"title":56,"searchDepth":81,"depth":81,"links":683},[684,685,686,687,689,690,691,692,693,694],{"id":27,"depth":81,"text":28},{"id":45,"depth":81,"text":46},{"id":158,"depth":81,"text":159},{"id":203,"depth":81,"text":688},"Composeファイルはcompose.yamlを使う",{"id":245,"depth":81,"text":245},{"id":346,"depth":81,"text":347},{"id":463,"depth":81,"text":463},{"id":542,"depth":81,"text":542},{"id":610,"depth":81,"text":611},{"id":636,"depth":81,"text":636},"『Docker&仮想サーバー完全入門』を読了したため、本書を通して学んだことをまとめます。","md",{},"\u002Fbooks\u002F2025\u002Forganize-learned-from-docker","2025-02-22","インプレス",{"title":5,"description":695},"books\u002F2025\u002Forganize-learned-from-docker",[704],"Docker","tZTXcHlrKtyN5T4H4BpRH6Dc272-Srw05uyFSfG7w5Q",[707,1423,3088,3578,4067,4495,4887],{"id":708,"title":709,"body":710,"description":1413,"extension":696,"meta":1414,"navigation":286,"path":1415,"publishDate":1416,"published":286,"seo":1417,"stem":1418,"tags":1419,"thumbnail":1421,"updated":1416,"__hash__":1422},"articles\u002Farticles\u002F2024\u002Flately-learned-js-tip.md","最近学んだJavaScriptのTipsをアウトプット",{"type":8,"value":711,"toc":1396},[712,715,718,721,724,778,782,785,858,874,889,903,918,921,924,960,970,978,991,1017,1033,1036,1051,1054,1061,1118,1121,1127,1160,1170,1174,1183,1202,1205,1213,1251,1254,1278,1285,1291,1302,1330,1351,1354,1360,1363,1379,1381,1384,1393],[11,713,714],{},"こんにちは、Yuriです。",[11,716,717],{},"今回は、最近学んだJavaScriptの小さなTipsとして、オブジェクトの分割代入とオプショナルチェーンを整理します。",[25,719,720],{"id":720},"オブジェクトの分割代入",[11,722,723],{},"オブジェクトの分割代入を使うと、プロパティの値を取り出して変数へ代入できます。",[51,725,729],{"className":726,"code":727,"language":728,"meta":56,"style":56},"language-javascript shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","const user = {\n  greeting: 'Hello',\n  farewell: 'Bye',\n}\n\nconst { greeting, farewell } = user\n\nconsole.log(greeting) \u002F\u002F 'Hello'\nconsole.log(farewell) \u002F\u002F 'Bye'\n","javascript",[58,730,731,736,741,746,751,755,761,766,772],{"__ignoreMap":56},[61,732,733],{"class":63,"line":64},[61,734,735],{},"const user = {\n",[61,737,738],{"class":63,"line":81},[61,739,740],{},"  greeting: 'Hello',\n",[61,742,743],{"class":63,"line":94},[61,744,745],{},"  farewell: 'Bye',\n",[61,747,748],{"class":63,"line":105},[61,749,750],{},"}\n",[61,752,753],{"class":63,"line":305},[61,754,287],{"emptyLinePlaceholder":286},[61,756,758],{"class":63,"line":757},6,[61,759,760],{},"const { greeting, farewell } = user\n",[61,762,764],{"class":63,"line":763},7,[61,765,287],{"emptyLinePlaceholder":286},[61,767,769],{"class":63,"line":768},8,[61,770,771],{},"console.log(greeting) \u002F\u002F 'Hello'\n",[61,773,775],{"class":63,"line":774},9,[61,776,777],{},"console.log(farewell) \u002F\u002F 'Bye'\n",[779,780,781],"h3",{"id":781},"ネストしたプロパティを取り出す",[11,783,784],{},"オブジェクトの中にオブジェクトがある場合は、データの構造に合わせて分割代入のパターンもネストできます。",[51,786,788],{"className":726,"code":787,"language":728,"meta":56,"style":56},"const user = {\n  greeting: 'Hello',\n  message: {\n    farewell: 'Bye',\n  },\n}\n\nconst {\n  greeting,\n  message: { farewell },\n} = user\n\nconsole.log(greeting) \u002F\u002F 'Hello'\nconsole.log(farewell) \u002F\u002F 'Bye'\n",[58,789,790,794,798,803,808,813,817,821,826,831,837,843,848,853],{"__ignoreMap":56},[61,791,792],{"class":63,"line":64},[61,793,735],{},[61,795,796],{"class":63,"line":81},[61,797,740],{},[61,799,800],{"class":63,"line":94},[61,801,802],{},"  message: {\n",[61,804,805],{"class":63,"line":105},[61,806,807],{},"    farewell: 'Bye',\n",[61,809,810],{"class":63,"line":305},[61,811,812],{},"  },\n",[61,814,815],{"class":63,"line":757},[61,816,750],{},[61,818,819],{"class":63,"line":763},[61,820,287],{"emptyLinePlaceholder":286},[61,822,823],{"class":63,"line":768},[61,824,825],{},"const {\n",[61,827,828],{"class":63,"line":774},[61,829,830],{},"  greeting,\n",[61,832,834],{"class":63,"line":833},10,[61,835,836],{},"  message: { farewell },\n",[61,838,840],{"class":63,"line":839},11,[61,841,842],{},"} = user\n",[61,844,846],{"class":63,"line":845},12,[61,847,287],{"emptyLinePlaceholder":286},[61,849,851],{"class":63,"line":850},13,[61,852,771],{},[61,854,856],{"class":63,"line":855},14,[61,857,777],{},[11,859,860,861,232,864,214,867,870,871,873],{},"この例で作られる変数は",[58,862,863],{},"greeting",[58,865,866],{},"farewell",[58,868,869],{},"message","は、内側の",[58,872,866],{},"へたどるためのパターンとして使われているだけなので、変数として宣言されません。",[51,875,877],{"className":726,"code":876,"language":728,"meta":56,"style":56},"console.log(message)\n\u002F\u002F ReferenceError: message is not defined\n",[58,878,879,884],{"__ignoreMap":56},[61,880,881],{"class":63,"line":64},[61,882,883],{},"console.log(message)\n",[61,885,886],{"class":63,"line":81},[61,887,888],{},"\u002F\u002F ReferenceError: message is not defined\n",[36,890,892],{"title":891,"type":39},"親オブジェクトも使いたい場合",[11,893,894,896,897,899,900,902],{},[58,895,869],{},"自体と、その中の",[58,898,866],{},"を両方使いたい場合は、分割代入を分けるか、先に",[58,901,869],{},"を取り出してから再度分割代入します。",[51,904,906],{"className":726,"code":905,"language":728,"meta":56,"style":56},"const { message } = user\nconst { farewell } = message\n",[58,907,908,913],{"__ignoreMap":56},[61,909,910],{"class":63,"line":64},[61,911,912],{},"const { message } = user\n",[61,914,915],{"class":63,"line":81},[61,916,917],{},"const { farewell } = message\n",[779,919,920],{"id":920},"プロパティ名とは異なる変数名で受け取る",[11,922,923],{},"コロンの右側へ名前を書くと、プロパティ名とは異なる変数名で値を受け取れます。",[51,925,927],{"className":726,"code":926,"language":728,"meta":56,"style":56},"const user = {\n  name: 'Yuri',\n}\n\nconst { name: userName } = user\n\nconsole.log(userName) \u002F\u002F 'Yuri'\n",[58,928,929,933,938,942,946,951,955],{"__ignoreMap":56},[61,930,931],{"class":63,"line":64},[61,932,735],{},[61,934,935],{"class":63,"line":81},[61,936,937],{},"  name: 'Yuri',\n",[61,939,940],{"class":63,"line":94},[61,941,750],{},[61,943,944],{"class":63,"line":105},[61,945,287],{"emptyLinePlaceholder":286},[61,947,948],{"class":63,"line":305},[61,949,950],{},"const { name: userName } = user\n",[61,952,953],{"class":63,"line":757},[61,954,287],{"emptyLinePlaceholder":286},[61,956,957],{"class":63,"line":763},[61,958,959],{},"console.log(userName) \u002F\u002F 'Yuri'\n",[11,961,962,963,966,967,214],{},"この場合も",[58,964,965],{},"name","という変数は作られず、作られるのは",[58,968,969],{},"userName",[25,971,973,974,977],{"id":972},"オプショナルチェーン","オプショナルチェーン（",[58,975,976],{},"?.","）",[11,979,980,981,603,984,987,988,990],{},"オプショナルチェーンは、参照の途中にある値が",[58,982,983],{},"null",[58,985,986],{},"undefined","だった場合、エラーを発生させずに",[58,989,986],{},"を返す演算子です。",[51,992,994],{"className":726,"code":993,"language":728,"meta":56,"style":56},"const user = {\n  name: 'Yuri',\n}\n\nconsole.log(user.profile?.hobby) \u002F\u002F undefined\n",[58,995,996,1000,1004,1008,1012],{"__ignoreMap":56},[61,997,998],{"class":63,"line":64},[61,999,735],{},[61,1001,1002],{"class":63,"line":81},[61,1003,937],{},[61,1005,1006],{"class":63,"line":94},[61,1007,750],{},[61,1009,1010],{"class":63,"line":105},[61,1011,287],{"emptyLinePlaceholder":286},[61,1013,1014],{"class":63,"line":305},[61,1015,1016],{},"console.log(user.profile?.hobby) \u002F\u002F undefined\n",[11,1018,1019,1022,1023,1025,1026,1028,1029,1032],{},[58,1020,1021],{},"user.profile","は",[58,1024,986],{},"ですが、",[58,1027,976],{},"を使っているため、その先の",[58,1030,1031],{},"hobby","へアクセスせず評価を終了します。",[11,1034,1035],{},"通常のプロパティアクセスではエラーになります。",[51,1037,1039],{"className":726,"code":1038,"language":728,"meta":56,"style":56},"console.log(user.profile.hobby)\n\u002F\u002F TypeError: Cannot read properties of undefined\n",[58,1040,1041,1046],{"__ignoreMap":56},[61,1042,1043],{"class":63,"line":64},[61,1044,1045],{},"console.log(user.profile.hobby)\n",[61,1047,1048],{"class":63,"line":81},[61,1049,1050],{},"\u002F\u002F TypeError: Cannot read properties of undefined\n",[779,1052,1053],{"id":1053},"配列の要素へアクセスする",[11,1055,1056,1057,1060],{},"角括弧によるアクセスでは、",[58,1058,1059],{},"?.[]","の形で使用します。",[51,1062,1064],{"className":726,"code":1063,"language":728,"meta":56,"style":56},"const items = [\n  { name: 'Item 1' },\n  { name: 'Item 2' },\n]\n\nconsole.log(items[1]?.name) \u002F\u002F 'Item 2'\nconsole.log(items[3]?.name) \u002F\u002F undefined\n\nconst emptyItems = undefined\n\nconsole.log(emptyItems?.[0]) \u002F\u002F undefined\n",[58,1065,1066,1071,1076,1081,1086,1090,1095,1100,1104,1109,1113],{"__ignoreMap":56},[61,1067,1068],{"class":63,"line":64},[61,1069,1070],{},"const items = [\n",[61,1072,1073],{"class":63,"line":81},[61,1074,1075],{},"  { name: 'Item 1' },\n",[61,1077,1078],{"class":63,"line":94},[61,1079,1080],{},"  { name: 'Item 2' },\n",[61,1082,1083],{"class":63,"line":105},[61,1084,1085],{},"]\n",[61,1087,1088],{"class":63,"line":305},[61,1089,287],{"emptyLinePlaceholder":286},[61,1091,1092],{"class":63,"line":757},[61,1093,1094],{},"console.log(items[1]?.name) \u002F\u002F 'Item 2'\n",[61,1096,1097],{"class":63,"line":763},[61,1098,1099],{},"console.log(items[3]?.name) \u002F\u002F undefined\n",[61,1101,1102],{"class":63,"line":768},[61,1103,287],{"emptyLinePlaceholder":286},[61,1105,1106],{"class":63,"line":774},[61,1107,1108],{},"const emptyItems = undefined\n",[61,1110,1111],{"class":63,"line":833},[61,1112,287],{"emptyLinePlaceholder":286},[61,1114,1115],{"class":63,"line":839},[61,1116,1117],{},"console.log(emptyItems?.[0]) \u002F\u002F undefined\n",[779,1119,1120],{"id":1120},"関数やメソッドを呼び出す",[11,1122,1123,1124,166],{},"関数が存在するときだけ呼び出したい場合は、",[58,1125,1126],{},"?.()",[51,1128,1130],{"className":726,"code":1129,"language":728,"meta":56,"style":56},"const actions = {\n  greet: () => 'Hello',\n}\n\nconsole.log(actions.greet?.()) \u002F\u002F 'Hello'\nconsole.log(actions.sayGoodbye?.()) \u002F\u002F undefined\n",[58,1131,1132,1137,1142,1146,1150,1155],{"__ignoreMap":56},[61,1133,1134],{"class":63,"line":64},[61,1135,1136],{},"const actions = {\n",[61,1138,1139],{"class":63,"line":81},[61,1140,1141],{},"  greet: () => 'Hello',\n",[61,1143,1144],{"class":63,"line":94},[61,1145,750],{},[61,1147,1148],{"class":63,"line":105},[61,1149,287],{"emptyLinePlaceholder":286},[61,1151,1152],{"class":63,"line":305},[61,1153,1154],{},"console.log(actions.greet?.()) \u002F\u002F 'Hello'\n",[61,1156,1157],{"class":63,"line":757},[61,1158,1159],{},"console.log(actions.sayGoodbye?.()) \u002F\u002F undefined\n",[11,1161,1162,1163,1166,1167,1169],{},"プロパティが存在していても、その値が関数でなければ",[58,1164,1165],{},"TypeError","になります。",[58,1168,1126],{},"は「呼び出し可能かどうか」まで確認するものではありません。",[779,1171,1173],{"id":1172},"dom要素へアクセスする","DOM要素へアクセスする",[11,1175,1176,1179,1180,1182],{},[58,1177,1178],{},"querySelector","は対象が見つからなかった場合に",[58,1181,983],{},"を返します。要素が存在するときだけプロパティを参照したい場合にも利用できます。",[51,1184,1186],{"className":726,"code":1185,"language":728,"meta":56,"style":56},"const button = document.querySelector('.sample-button')\n\nconsole.log(button?.textContent) \u002F\u002F 要素がなければundefined\n",[58,1187,1188,1193,1197],{"__ignoreMap":56},[61,1189,1190],{"class":63,"line":64},[61,1191,1192],{},"const button = document.querySelector('.sample-button')\n",[61,1194,1195],{"class":63,"line":81},[61,1196,287],{"emptyLinePlaceholder":286},[61,1198,1199],{"class":63,"line":94},[61,1200,1201],{},"console.log(button?.textContent) \u002F\u002F 要素がなければundefined\n",[779,1203,1204],{"id":1204},"短絡評価される範囲",[11,1206,1207,1208,603,1210,1212],{},"オプショナルチェーンでは、左側が",[58,1209,983],{},[58,1211,986],{},"の場合、そのチェーンの残りは評価されません。",[51,1214,1216],{"className":726,"code":1215,"language":728,"meta":56,"style":56},"let index = 0\nconst items = null\n\nconst item = items?.[index++]\n\nconsole.log(item) \u002F\u002F undefined\nconsole.log(index) \u002F\u002F 0\n",[58,1217,1218,1223,1228,1232,1237,1241,1246],{"__ignoreMap":56},[61,1219,1220],{"class":63,"line":64},[61,1221,1222],{},"let index = 0\n",[61,1224,1225],{"class":63,"line":81},[61,1226,1227],{},"const items = null\n",[61,1229,1230],{"class":63,"line":94},[61,1231,287],{"emptyLinePlaceholder":286},[61,1233,1234],{"class":63,"line":105},[61,1235,1236],{},"const item = items?.[index++]\n",[61,1238,1239],{"class":63,"line":305},[61,1240,287],{"emptyLinePlaceholder":286},[61,1242,1243],{"class":63,"line":757},[61,1244,1245],{},"console.log(item) \u002F\u002F undefined\n",[61,1247,1248],{"class":63,"line":763},[61,1249,1250],{},"console.log(index) \u002F\u002F 0\n",[11,1252,1253],{},"ただし、オプショナルチェーンが途切れた後のアクセスは保護されません。",[51,1255,1257],{"className":726,"code":1256,"language":728,"meta":56,"style":56},"const user = null\n\nuser?.profile.name \u002F\u002F undefined\n(user?.profile).name \u002F\u002F TypeError\n",[58,1258,1259,1264,1268,1273],{"__ignoreMap":56},[61,1260,1261],{"class":63,"line":64},[61,1262,1263],{},"const user = null\n",[61,1265,1266],{"class":63,"line":81},[61,1267,287],{"emptyLinePlaceholder":286},[61,1269,1270],{"class":63,"line":94},[61,1271,1272],{},"user?.profile.name \u002F\u002F undefined\n",[61,1274,1275],{"class":63,"line":105},[61,1276,1277],{},"(user?.profile).name \u002F\u002F TypeError\n",[11,1279,1280,1281,1284],{},"括弧で区切ると、その後の",[58,1282,1283],{},".name","は別のプロパティアクセスとして評価されます。",[25,1286,1288,1287],{"id":1287},"と組み合わせて既定値を設定する",[58,1289,1290],{},"??",[11,1292,1293,1294,603,1296,1298,1299,1301],{},"オプショナルチェーンで取得した結果が",[58,1295,983],{},[58,1297,986],{},"だった場合、ヌル値合体演算子（",[58,1300,1290],{},"）で既定値を設定できます。",[51,1303,1305],{"className":726,"code":1304,"language":728,"meta":56,"style":56},"const user = {}\n\nconst hobby = user.profile?.hobby ?? '未設定'\n\nconsole.log(hobby) \u002F\u002F '未設定'\n",[58,1306,1307,1312,1316,1321,1325],{"__ignoreMap":56},[61,1308,1309],{"class":63,"line":64},[61,1310,1311],{},"const user = {}\n",[61,1313,1314],{"class":63,"line":81},[61,1315,287],{"emptyLinePlaceholder":286},[61,1317,1318],{"class":63,"line":94},[61,1319,1320],{},"const hobby = user.profile?.hobby ?? '未設定'\n",[61,1322,1323],{"class":63,"line":105},[61,1324,287],{"emptyLinePlaceholder":286},[61,1326,1327],{"class":63,"line":305},[61,1328,1329],{},"console.log(hobby) \u002F\u002F '未設定'\n",[11,1331,1332,1022,1335,1338,1339,1342,1343,1345,1346,603,1348,1350],{},[58,1333,1334],{},"||",[58,1336,1337],{},"0","、空文字列、",[58,1340,1341],{},"false","も右側の値へ置き換えますが、",[58,1344,1290],{},"が右側を返すのは",[58,1347,983],{},[58,1349,986],{},"の場合だけです。",[25,1352,1353],{"id":1353},"オプショナルチェーンを使うときの注意点",[11,1355,1356,1357,1359],{},"オプショナルチェーンは条件分岐を簡潔にできますが、必須であるはずの値にまで使用すると、不具合を",[58,1358,986],{},"として見逃す可能性があります。",[11,1361,1362],{},"次のように使い分けることが大切です。",[644,1364,1365,1370,1373],{},[647,1366,1367,1368,208],{},"値が存在しないことを仕様として許容する場合は",[58,1369,976],{},[647,1371,1372],{},"値が必須の場合は明示的に確認し、存在しなければエラーとして扱う",[647,1374,1375,1376,1378],{},"取得結果に既定値が必要な場合は",[58,1377,1290],{},"を組み合わせる",[25,1380,636],{"id":636},[11,1382,1383],{},"ネストした分割代入を使うと、深い階層の値を簡潔に変数へ取り出せます。ただし、途中のプロパティ名がそのまま変数になるわけではありません。",[11,1385,1386,1387,1389,1390,1392],{},"オプショナルチェーンを使うと、",[58,1388,983],{},"や",[58,1391,986],{},"の可能性があるプロパティ、配列、関数へ安全にアクセスできます。便利さだけで多用せず、その値が本当に省略可能なのかを考えて使っていきたいです。",[679,1394,1395],{},"html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":56,"searchDepth":81,"depth":81,"links":1397},[1398,1402,1409,1411,1412],{"id":720,"depth":81,"text":720,"children":1399},[1400,1401],{"id":781,"depth":94,"text":781},{"id":920,"depth":94,"text":920},{"id":972,"depth":81,"text":1403,"children":1404},"オプショナルチェーン（?.）",[1405,1406,1407,1408],{"id":1053,"depth":94,"text":1053},{"id":1120,"depth":94,"text":1120},{"id":1172,"depth":94,"text":1173},{"id":1204,"depth":94,"text":1204},{"id":1287,"depth":81,"text":1410},"??と組み合わせて既定値を設定する",{"id":1353,"depth":81,"text":1353},{"id":636,"depth":81,"text":636},"JavaScriptのネストしたオブジェクトの分割代入とオプショナルチェーンについて、コード例と注意点を交えて整理します。",{},"\u002Farticles\u002F2024\u002Flately-learned-js-tip","2024-12-28",{"title":709,"description":1413},"articles\u002F2024\u002Flately-learned-js-tip",[1420],"JavaScript","\u002Fimages\u002Farticles\u002Flately-learned-js-tip.png","Xi5F4LrgdP-cQxce9jEEPhpLiC086mIJfek4NR0cS3g",{"id":1424,"title":1425,"body":1426,"description":3079,"extension":696,"meta":3080,"navigation":286,"path":3081,"publishDate":3082,"published":286,"seo":3083,"stem":3084,"tags":3085,"thumbnail":3086,"updated":3082,"__hash__":3087},"articles\u002Farticles\u002F2024\u002Forganize-learned-from-todo.md","Todoアプリのハンズオンで学んだことを整理する",{"type":8,"value":1427,"toc":3050},[1428,1430,1439,1452,1455,1462,1471,1505,1508,1515,1517,1520,1523,1565,1568,1572,1575,1585,1588,1591,1597,1606,1813,1824,1827,1830,1931,1934,1937,2024,2027,2030,2059,2062,2065,2076,2225,2228,2231,2234,2237,2240,2248,2260,2328,2334,2339,2345,2388,2393,2396,2399,2465,2472,2475,2482,2595,2604,2611,2616,2676,2679,2682,2685,2779,2782,2788,2807,2817,2820,2823,2902,2909,2912,2915,3036,3039,3041,3044,3047],[11,1429,714],{},[11,1431,1432,1433,1438],{},"今回は「",[15,1434,1437],{"href":1435,"rel":1436},"https:\u002F\u002Fzenn.dev\u002Fsprout2000\u002Fbooks\u002F76a279bb90c3f3",[19],"React Hooks と TypeScript でつくる Todo PWA ～入門 React ハンズオン～","」を進める中で学んだことを、次の4つに分けて整理します。",[644,1440,1441,1444,1446,1449],{},[647,1442,1443],{},"HTML",[647,1445,1420],{},[647,1447,1448],{},"React",[647,1450,1451],{},"TypeScript",[25,1453,1443],{"id":1454},"html",[779,1456,1458,1461],{"id":1457},"autofocus属性",[58,1459,1460],{},"autofocus","属性",[11,1463,1464,1466,1467,1470],{},[58,1465,1460],{},"は、ページの読み込み時や",[58,1468,1469],{},"dialog","要素の表示時に、指定した要素へ自動的にフォーカスするための属性です。",[51,1472,1475],{"className":1473,"code":1474,"language":1454,"meta":56,"style":56},"language-html shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","\u003Cinput type=\"text\" autofocus>\n",[58,1476,1477],{"__ignoreMap":56},[61,1478,1479,1482,1485,1489,1492,1495,1497,1499,1502],{"class":63,"line":64},[61,1480,1481],{"class":271},"\u003C",[61,1483,1484],{"class":268},"input",[61,1486,1488],{"class":1487},"spNyl"," type",[61,1490,1491],{"class":271},"=",[61,1493,1494],{"class":271},"\"",[61,1496,220],{"class":71},[61,1498,1494],{"class":271},[61,1500,1501],{"class":1487}," autofocus",[61,1503,1504],{"class":271},">\n",[11,1506,1507],{},"1つの文書やダイアログ内では、複数の要素へ指定しないようにします。複数指定した場合、最初に見つかった要素がフォーカスされます。",[36,1509,1512],{"title":1510,"type":1511},"アクセシビリティへの配慮","caution",[11,1513,1514],{},"ページを開いた直後にフォーカスが移動すると、スクリーンリーダー利用者やモバイル利用者を混乱させる場合があります。本当に自動フォーカスが必要かを検討して使用しましょう。",[25,1516,1420],{"id":728},[779,1518,1519],{"id":1519},"オブジェクトのプロパティ名を動的にする",[11,1521,1522],{},"オブジェクトリテラルのプロパティ名を角括弧で囲むと、式の評価結果をプロパティ名として使用できます。これは計算プロパティ名と呼ばれます。",[51,1524,1526],{"className":726,"code":1525,"language":728,"meta":56,"style":56},"const key = 'sampleKey'\n\nconst sample = {\n  [key]: 'sampleValue',\n}\n\nconsole.log(sample) \u002F\u002F { sampleKey: 'sampleValue' }\nconsole.log(sample[key]) \u002F\u002F 'sampleValue'\n",[58,1527,1528,1533,1537,1542,1547,1551,1555,1560],{"__ignoreMap":56},[61,1529,1530],{"class":63,"line":64},[61,1531,1532],{},"const key = 'sampleKey'\n",[61,1534,1535],{"class":63,"line":81},[61,1536,287],{"emptyLinePlaceholder":286},[61,1538,1539],{"class":63,"line":94},[61,1540,1541],{},"const sample = {\n",[61,1543,1544],{"class":63,"line":105},[61,1545,1546],{},"  [key]: 'sampleValue',\n",[61,1548,1549],{"class":63,"line":305},[61,1550,750],{},[61,1552,1553],{"class":63,"line":757},[61,1554,287],{"emptyLinePlaceholder":286},[61,1556,1557],{"class":63,"line":763},[61,1558,1559],{},"console.log(sample) \u002F\u002F { sampleKey: 'sampleValue' }\n",[61,1561,1562],{"class":63,"line":768},[61,1563,1564],{},"console.log(sample[key]) \u002F\u002F 'sampleValue'\n",[11,1566,1567],{},"入力項目の名前をキーとしてオブジェクトを更新する場合などに利用できます。",[779,1569,1571],{"id":1570},"tree-shaking","Tree Shaking",[11,1573,1574],{},"Tree Shakingは、利用されていないコードをビルド結果から取り除く最適化です。",[11,1576,1577,1578,232,1581,1584],{},"主にバンドラーがES Modulesの",[58,1579,1580],{},"import",[58,1582,1583],{},"export","を静的に解析し、参照されていないエクスポートを出力対象から除外します。",[11,1586,1587],{},"実際に削除できる範囲は、バンドラーの設定やコードに副作用があるかどうかによって変わります。",[25,1589,1448],{"id":1590},"react",[779,1592,1594],{"id":1593},"createroot",[58,1595,1596],{},"createRoot",[11,1598,1599,1601,1602,1605],{},[58,1600,1596],{},"は、Reactが管理するルートをブラウザのDOM要素へ作成するAPIです。作成したルートの",[58,1603,1604],{},"render","メソッドへReact要素を渡して表示します。",[51,1607,1611],{"className":1608,"code":1609,"language":1610,"meta":56,"style":56},"language-tsx shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","import { StrictMode } from 'react'\nimport { createRoot } from 'react-dom\u002Fclient'\nimport App from '.\u002FApp'\n\nconst container = document.getElementById('root')\n\nif (!container) {\n  throw new Error('root要素が見つかりません')\n}\n\ncreateRoot(container).render(\n  \u003CStrictMode>\n    \u003CApp \u002F>\n  \u003C\u002FStrictMode>,\n)\n","tsx",[58,1612,1613,1637,1657,1674,1678,1712,1716,1733,1755,1759,1763,1777,1787,1798,1808],{"__ignoreMap":56},[61,1614,1615,1618,1621,1625,1628,1631,1633,1635],{"class":63,"line":64},[61,1616,1580],{"class":1617},"s7zQu",[61,1619,1620],{"class":271}," {",[61,1622,1624],{"class":1623},"sTEyZ"," StrictMode",[61,1626,1627],{"class":271}," }",[61,1629,1630],{"class":1617}," from",[61,1632,275],{"class":271},[61,1634,1590],{"class":71},[61,1636,281],{"class":271},[61,1638,1639,1641,1643,1646,1648,1650,1652,1655],{"class":63,"line":81},[61,1640,1580],{"class":1617},[61,1642,1620],{"class":271},[61,1644,1645],{"class":1623}," createRoot",[61,1647,1627],{"class":271},[61,1649,1630],{"class":1617},[61,1651,275],{"class":271},[61,1653,1654],{"class":71},"react-dom\u002Fclient",[61,1656,281],{"class":271},[61,1658,1659,1661,1664,1667,1669,1672],{"class":63,"line":94},[61,1660,1580],{"class":1617},[61,1662,1663],{"class":1623}," App ",[61,1665,1666],{"class":1617},"from",[61,1668,275],{"class":271},[61,1670,1671],{"class":71},".\u002FApp",[61,1673,281],{"class":271},[61,1675,1676],{"class":63,"line":105},[61,1677,287],{"emptyLinePlaceholder":286},[61,1679,1680,1683,1686,1688,1691,1694,1698,1701,1704,1707,1709],{"class":63,"line":305},[61,1681,1682],{"class":1487},"const",[61,1684,1685],{"class":1623}," container ",[61,1687,1491],{"class":271},[61,1689,1690],{"class":1623}," document",[61,1692,1693],{"class":271},".",[61,1695,1697],{"class":1696},"s2Zo4","getElementById",[61,1699,1700],{"class":1623},"(",[61,1702,1703],{"class":271},"'",[61,1705,1706],{"class":71},"root",[61,1708,1703],{"class":271},[61,1710,1711],{"class":1623},")\n",[61,1713,1714],{"class":63,"line":757},[61,1715,287],{"emptyLinePlaceholder":286},[61,1717,1718,1721,1724,1727,1730],{"class":63,"line":763},[61,1719,1720],{"class":1617},"if",[61,1722,1723],{"class":1623}," (",[61,1725,1726],{"class":271},"!",[61,1728,1729],{"class":1623},"container) ",[61,1731,1732],{"class":271},"{\n",[61,1734,1735,1738,1741,1744,1746,1748,1751,1753],{"class":63,"line":768},[61,1736,1737],{"class":1617},"  throw",[61,1739,1740],{"class":271}," new",[61,1742,1743],{"class":1696}," Error",[61,1745,1700],{"class":268},[61,1747,1703],{"class":271},[61,1749,1750],{"class":71},"root要素が見つかりません",[61,1752,1703],{"class":271},[61,1754,1711],{"class":268},[61,1756,1757],{"class":63,"line":774},[61,1758,750],{"class":271},[61,1760,1761],{"class":63,"line":833},[61,1762,287],{"emptyLinePlaceholder":286},[61,1764,1765,1767,1770,1772,1774],{"class":63,"line":839},[61,1766,1596],{"class":1696},[61,1768,1769],{"class":1623},"(container)",[61,1771,1693],{"class":271},[61,1773,1604],{"class":1696},[61,1775,1776],{"class":1623},"(\n",[61,1778,1779,1782,1785],{"class":63,"line":845},[61,1780,1781],{"class":271},"  \u003C",[61,1783,1784],{"class":67},"StrictMode",[61,1786,1504],{"class":271},[61,1788,1789,1792,1795],{"class":63,"line":850},[61,1790,1791],{"class":271},"    \u003C",[61,1793,1794],{"class":67},"App",[61,1796,1797],{"class":271}," \u002F>\n",[61,1799,1800,1803,1805],{"class":63,"line":855},[61,1801,1802],{"class":271},"  \u003C\u002F",[61,1804,1784],{"class":67},[61,1806,1807],{"class":271},">,\n",[61,1809,1811],{"class":63,"line":1810},15,[61,1812,1711],{"class":1623},[11,1814,1815,1817,1818,1820,1821,166],{},[58,1816,1596],{},"へ渡したDOM要素の内側はReactが管理します。サーバー側でReactが生成したHTMLを再利用する場合は、",[58,1819,1596],{},"ではなく",[58,1822,1823],{},"hydrateRoot",[779,1825,1826],{"id":1826},"stateを直接変更しない",[11,1828,1829],{},"Reactのstateに保存したオブジェクトや配列は、読み取り専用として扱います。値を直接変更せず、新しい値を作ってset関数へ渡します。",[51,1831,1833],{"className":1608,"code":1832,"language":1610,"meta":56,"style":56},"setTodos(currentTodos =>\n  currentTodos.map(todo =>\n    todo.id === id\n      ? { ...todo, completed: true }\n      : todo,\n  ),\n)\n",[58,1834,1835,1849,1866,1882,1909,1920,1927],{"__ignoreMap":56},[61,1836,1837,1840,1842,1846],{"class":63,"line":64},[61,1838,1839],{"class":1696},"setTodos",[61,1841,1700],{"class":1623},[61,1843,1845],{"class":1844},"sHdIc","currentTodos",[61,1847,1848],{"class":1487}," =>\n",[61,1850,1851,1854,1856,1859,1861,1864],{"class":63,"line":81},[61,1852,1853],{"class":1623},"  currentTodos",[61,1855,1693],{"class":271},[61,1857,1858],{"class":1696},"map",[61,1860,1700],{"class":1623},[61,1862,1863],{"class":1844},"todo",[61,1865,1848],{"class":1487},[61,1867,1868,1871,1873,1876,1879],{"class":63,"line":94},[61,1869,1870],{"class":1623},"    todo",[61,1872,1693],{"class":271},[61,1874,1875],{"class":1623},"id ",[61,1877,1878],{"class":271},"===",[61,1880,1881],{"class":1623}," id\n",[61,1883,1884,1887,1889,1892,1894,1897,1900,1902,1906],{"class":63,"line":105},[61,1885,1886],{"class":271},"      ?",[61,1888,1620],{"class":271},[61,1890,1891],{"class":271}," ...",[61,1893,1863],{"class":1623},[61,1895,1896],{"class":271},",",[61,1898,1899],{"class":268}," completed",[61,1901,272],{"class":271},[61,1903,1905],{"class":1904},"sfNiH"," true",[61,1907,1908],{"class":271}," }\n",[61,1910,1911,1914,1917],{"class":63,"line":305},[61,1912,1913],{"class":271},"      :",[61,1915,1916],{"class":1623}," todo",[61,1918,1919],{"class":271},",\n",[61,1921,1922,1925],{"class":63,"line":757},[61,1923,1924],{"class":1623},"  )",[61,1926,1919],{"class":271},[61,1928,1929],{"class":63,"line":763},[61,1930,1711],{"class":1623},[11,1932,1933],{},"配列を新しく作っても、その中にあるオブジェクトまでは自動的に複製されません。更新対象のオブジェクトもスプレッド構文などで新しく作る必要があります。",[11,1935,1936],{},"配列を更新する際は、次のように破壊的な操作を避けます。",[403,1938,1939,1952],{},[406,1940,1941],{},[409,1942,1943,1946,1949],{},[412,1944,1945],{},"操作",[412,1947,1948],{},"避けたい方法",[412,1950,1951],{},"利用する方法",[419,1953,1954,1973,1994,2008],{},[409,1955,1956,1959,1967],{},[424,1957,1958],{},"追加",[424,1960,1961,118,1964],{},[58,1962,1963],{},"push",[58,1965,1966],{},"unshift",[424,1968,1969,1970],{},"スプレッド構文、",[58,1971,1972],{},"concat",[409,1974,1975,1978,1986],{},[424,1976,1977],{},"削除",[424,1979,1980,118,1983],{},[58,1981,1982],{},"pop",[58,1984,1985],{},"splice",[424,1987,1988,118,1991],{},[58,1989,1990],{},"filter",[58,1992,1993],{},"slice",[409,1995,1996,1999,2004],{},[424,1997,1998],{},"置換",[424,2000,2001,2002],{},"添字への代入、",[58,2003,1985],{},[424,2005,2006],{},[58,2007,1858],{},[409,2009,2010,2013,2019],{},[424,2011,2012],{},"並び替え",[424,2014,2015,2016],{},"元配列への",[58,2017,2018],{},"sort",[424,2020,2021,2022],{},"コピー後に",[58,2023,2018],{},[779,2025,2026],{"id":2026},"直前のstateを基に更新する",[11,2028,2029],{},"更新後のstateが更新前の値に依存する場合は、set関数へ値ではなく更新用の関数を渡します。",[51,2031,2033],{"className":1608,"code":2032,"language":1610,"meta":56,"style":56},"setCount(currentCount => currentCount + 1)\n",[58,2034,2035],{"__ignoreMap":56},[61,2036,2037,2040,2042,2045,2048,2051,2054,2057],{"class":63,"line":64},[61,2038,2039],{"class":1696},"setCount",[61,2041,1700],{"class":1623},[61,2043,2044],{"class":1844},"currentCount",[61,2046,2047],{"class":1487}," =>",[61,2049,2050],{"class":1623}," currentCount ",[61,2052,2053],{"class":271},"+",[61,2055,2056],{"class":536}," 1",[61,2058,1711],{"class":1623},[11,2060,2061],{},"Reactはイベントハンドラーの処理をまとめてからstateを更新することがあります。更新用関数を使うことで、その時点の最新のstateを基に次の値を計算できます。",[779,2063,2064],{"id":2064},"イベントハンドラーの命名",[11,2066,2067,2068,2071,2072,2075],{},"イベントを処理する関数には",[58,2069,2070],{},"handle","、propsとして受け渡すイベントには",[58,2073,2074],{},"on","を付けると役割が伝わりやすくなります。",[51,2077,2079],{"className":1608,"code":2078,"language":1610,"meta":56,"style":56},"type TodoItemProps = {\n  onChange: (id: number) => void\n}\n\nfunction TodoItem({ onChange }: TodoItemProps) {\n  const handleChange = () => {\n    onChange(1)\n  }\n\n  return \u003Cinput type=\"checkbox\" onChange={handleChange} \u002F>\n}\n",[58,2080,2081,2095,2120,2124,2128,2151,2168,2180,2185,2189,2221],{"__ignoreMap":56},[61,2082,2083,2086,2089,2092],{"class":63,"line":64},[61,2084,2085],{"class":1487},"type",[61,2087,2088],{"class":67}," TodoItemProps",[61,2090,2091],{"class":271}," =",[61,2093,2094],{"class":271}," {\n",[61,2096,2097,2100,2102,2104,2107,2109,2112,2115,2117],{"class":63,"line":81},[61,2098,2099],{"class":268},"  onChange",[61,2101,272],{"class":271},[61,2103,1723],{"class":271},[61,2105,2106],{"class":1844},"id",[61,2108,272],{"class":271},[61,2110,2111],{"class":67}," number",[61,2113,2114],{"class":271},")",[61,2116,2047],{"class":1487},[61,2118,2119],{"class":67}," void\n",[61,2121,2122],{"class":63,"line":94},[61,2123,750],{"class":271},[61,2125,2126],{"class":63,"line":105},[61,2127,287],{"emptyLinePlaceholder":286},[61,2129,2130,2133,2136,2139,2142,2145,2147,2149],{"class":63,"line":305},[61,2131,2132],{"class":1487},"function",[61,2134,2135],{"class":1696}," TodoItem",[61,2137,2138],{"class":271},"({",[61,2140,2141],{"class":1844}," onChange",[61,2143,2144],{"class":271}," }:",[61,2146,2088],{"class":67},[61,2148,2114],{"class":271},[61,2150,2094],{"class":271},[61,2152,2153,2156,2159,2161,2164,2166],{"class":63,"line":757},[61,2154,2155],{"class":1487},"  const",[61,2157,2158],{"class":1623}," handleChange",[61,2160,2091],{"class":271},[61,2162,2163],{"class":271}," ()",[61,2165,2047],{"class":1487},[61,2167,2094],{"class":271},[61,2169,2170,2173,2175,2178],{"class":63,"line":763},[61,2171,2172],{"class":1696},"    onChange",[61,2174,1700],{"class":268},[61,2176,2177],{"class":536},"1",[61,2179,1711],{"class":268},[61,2181,2182],{"class":63,"line":768},[61,2183,2184],{"class":271},"  }\n",[61,2186,2187],{"class":63,"line":774},[61,2188,287],{"emptyLinePlaceholder":286},[61,2190,2191,2194,2197,2199,2201,2203,2205,2208,2210,2212,2215,2218],{"class":63,"line":833},[61,2192,2193],{"class":1617},"  return",[61,2195,2196],{"class":271}," \u003C",[61,2198,1484],{"class":268},[61,2200,1488],{"class":1487},[61,2202,1491],{"class":271},[61,2204,1494],{"class":271},[61,2206,2207],{"class":71},"checkbox",[61,2209,1494],{"class":271},[61,2211,2141],{"class":1487},[61,2213,2214],{"class":271},"={",[61,2216,2217],{"class":1623},"handleChange",[61,2219,2220],{"class":271},"} \u002F>\n",[61,2222,2223],{"class":63,"line":839},[61,2224,750],{"class":271},[11,2226,2227],{},"これはReactが強制する規則ではありませんが、一貫した命名はコードを読みやすくします。",[779,2229,2230],{"id":2230},"propsの分割代入",[11,2232,2233],{},"propsを分割代入すると記述は短くなりますが、変数がprops由来なのかコンポーネント内で定義されたものなのか分かりにくくなる場合があります。",[11,2235,2236],{},"コンポーネントが大きくなった場合は、変数名やコンポーネントの責務を見直し、値の出所を追いやすくすることが大切です。",[25,2238,1451],{"id":2239},"typescript",[779,2241,2243,232,2246],{"id":2242},"documentgetelementbyidとnull",[58,2244,2245],{},"document.getElementById",[58,2247,983],{},[11,2249,2250,2252,2253,2256,2257,2259],{},[58,2251,2245],{},"の戻り値は",[58,2254,2255],{},"HTMLElement | null","です。対象の要素が存在しない可能性があるため、利用前に",[58,2258,983],{},"を確認します。",[51,2261,2264],{"className":2262,"code":2263,"language":2239,"meta":56,"style":56},"language-typescript shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","const container = document.getElementById('root')\n\nif (!container) {\n  throw new Error('root要素が見つかりません')\n}\n",[58,2265,2266,2290,2294,2306,2324],{"__ignoreMap":56},[61,2267,2268,2270,2272,2274,2276,2278,2280,2282,2284,2286,2288],{"class":63,"line":64},[61,2269,1682],{"class":1487},[61,2271,1685],{"class":1623},[61,2273,1491],{"class":271},[61,2275,1690],{"class":1623},[61,2277,1693],{"class":271},[61,2279,1697],{"class":1696},[61,2281,1700],{"class":1623},[61,2283,1703],{"class":271},[61,2285,1706],{"class":71},[61,2287,1703],{"class":271},[61,2289,1711],{"class":1623},[61,2291,2292],{"class":63,"line":81},[61,2293,287],{"emptyLinePlaceholder":286},[61,2295,2296,2298,2300,2302,2304],{"class":63,"line":94},[61,2297,1720],{"class":1617},[61,2299,1723],{"class":1623},[61,2301,1726],{"class":271},[61,2303,1729],{"class":1623},[61,2305,1732],{"class":271},[61,2307,2308,2310,2312,2314,2316,2318,2320,2322],{"class":63,"line":105},[61,2309,1737],{"class":1617},[61,2311,1740],{"class":271},[61,2313,1743],{"class":1696},[61,2315,1700],{"class":268},[61,2317,1703],{"class":271},[61,2319,1750],{"class":71},[61,2321,1703],{"class":271},[61,2323,1711],{"class":268},[61,2325,2326],{"class":63,"line":305},[61,2327,750],{"class":271},[11,2329,2330,2331,2333],{},"Non-nullアサーション演算子（",[58,2332,1726],{},"）で確認を省略することもできますが、実行時に要素が必ず存在することをTypeScriptが保証するわけではありません。",[779,2335,2337],{"id":2336},"readonly",[58,2338,2336],{},[11,2340,2341,2342,2344],{},"プロパティへ",[58,2343,2336],{},"を指定すると、そのプロパティへの再代入を型チェックで防げます。",[51,2346,2348],{"className":2262,"code":2347,"language":2239,"meta":56,"style":56},"type Todo = {\n  readonly id: number\n  title: string\n}\n",[58,2349,2350,2361,2374,2384],{"__ignoreMap":56},[61,2351,2352,2354,2357,2359],{"class":63,"line":64},[61,2353,2085],{"class":1487},[61,2355,2356],{"class":67}," Todo",[61,2358,2091],{"class":271},[61,2360,2094],{"class":271},[61,2362,2363,2366,2369,2371],{"class":63,"line":81},[61,2364,2365],{"class":1487},"  readonly",[61,2367,2368],{"class":268}," id",[61,2370,272],{"class":271},[61,2372,2373],{"class":67}," number\n",[61,2375,2376,2379,2381],{"class":63,"line":94},[61,2377,2378],{"class":268},"  title",[61,2380,272],{"class":271},[61,2382,2383],{"class":67}," string\n",[61,2385,2386],{"class":63,"line":105},[61,2387,750],{"class":271},[11,2389,2390,2392],{},[58,2391,2336],{},"はコンパイル時の制約です。また、入れ子になったオブジェクトまで自動的に読み取り専用にするものではありません。",[779,2394,2395],{"id":2395},"イベントごとの型",[11,2397,2398],{},"Reactのイベントハンドラーでは、対象に応じたイベント型を利用できます。",[51,2400,2402],{"className":1608,"code":2401,"language":1610,"meta":56,"style":56},"function handleChange(event: React.ChangeEvent\u003CHTMLInputElement>) {\n  console.log(event.target.checked)\n}\n",[58,2403,2404,2435,2461],{"__ignoreMap":56},[61,2405,2406,2408,2410,2412,2415,2417,2420,2422,2425,2427,2430,2433],{"class":63,"line":64},[61,2407,2132],{"class":1487},[61,2409,2158],{"class":1696},[61,2411,1700],{"class":271},[61,2413,2414],{"class":1844},"event",[61,2416,272],{"class":271},[61,2418,2419],{"class":67}," React",[61,2421,1693],{"class":271},[61,2423,2424],{"class":67},"ChangeEvent",[61,2426,1481],{"class":271},[61,2428,2429],{"class":67},"HTMLInputElement",[61,2431,2432],{"class":271},">)",[61,2434,2094],{"class":271},[61,2436,2437,2440,2442,2445,2447,2449,2451,2454,2456,2459],{"class":63,"line":81},[61,2438,2439],{"class":1623},"  console",[61,2441,1693],{"class":271},[61,2443,2444],{"class":1696},"log",[61,2446,1700],{"class":268},[61,2448,2414],{"class":1623},[61,2450,1693],{"class":271},[61,2452,2453],{"class":1623},"target",[61,2455,1693],{"class":271},[61,2457,2458],{"class":1623},"checked",[61,2460,1711],{"class":268},[61,2462,2463],{"class":63,"line":94},[61,2464,750],{"class":271},[11,2466,2467,2468,2471],{},"イベント型だけでなく、",[58,2469,2470],{},"ChangeEventHandler\u003CHTMLInputElement>","のようなハンドラー全体の型を付ける方法もあります。",[779,2473,2474],{"id":2474},"ジェネリクスの型引数へ制約を付ける",[11,2476,2477,2478,2481],{},"型引数に",[58,2479,2480],{},"extends","を指定すると、ジェネリクスで受け取れる型に必要なプロパティを保証できます。",[51,2483,2485],{"className":2262,"code":2484,"language":2239,"meta":56,"style":56},"type Identifiable = {\n  id: number\n}\n\nfunction findById\u003CT extends Identifiable>(items: T[], id: number) {\n  return items.find(item => item.id === id)\n}\n",[58,2486,2487,2498,2507,2511,2515,2558,2591],{"__ignoreMap":56},[61,2488,2489,2491,2494,2496],{"class":63,"line":64},[61,2490,2085],{"class":1487},[61,2492,2493],{"class":67}," Identifiable",[61,2495,2091],{"class":271},[61,2497,2094],{"class":271},[61,2499,2500,2503,2505],{"class":63,"line":81},[61,2501,2502],{"class":268},"  id",[61,2504,272],{"class":271},[61,2506,2373],{"class":67},[61,2508,2509],{"class":63,"line":94},[61,2510,750],{"class":271},[61,2512,2513],{"class":63,"line":105},[61,2514,287],{"emptyLinePlaceholder":286},[61,2516,2517,2519,2522,2524,2527,2530,2532,2535,2538,2540,2543,2546,2548,2550,2552,2554,2556],{"class":63,"line":305},[61,2518,2132],{"class":1487},[61,2520,2521],{"class":1696}," findById",[61,2523,1481],{"class":271},[61,2525,2526],{"class":67},"T",[61,2528,2529],{"class":1487}," extends",[61,2531,2493],{"class":67},[61,2533,2534],{"class":271},">(",[61,2536,2537],{"class":1844},"items",[61,2539,272],{"class":271},[61,2541,2542],{"class":67}," T",[61,2544,2545],{"class":1623},"[]",[61,2547,1896],{"class":271},[61,2549,2368],{"class":1844},[61,2551,272],{"class":271},[61,2553,2111],{"class":67},[61,2555,2114],{"class":271},[61,2557,2094],{"class":271},[61,2559,2560,2562,2565,2567,2570,2572,2575,2577,2580,2582,2584,2587,2589],{"class":63,"line":757},[61,2561,2193],{"class":1617},[61,2563,2564],{"class":1623}," items",[61,2566,1693],{"class":271},[61,2568,2569],{"class":1696},"find",[61,2571,1700],{"class":268},[61,2573,2574],{"class":1844},"item",[61,2576,2047],{"class":1487},[61,2578,2579],{"class":1623}," item",[61,2581,1693],{"class":271},[61,2583,2106],{"class":1623},[61,2585,2586],{"class":271}," ===",[61,2588,2368],{"class":1623},[61,2590,1711],{"class":268},[61,2592,2593],{"class":63,"line":763},[61,2594,750],{"class":271},[11,2596,2597,2598,2600,2601,2603],{},"制約がなければ",[58,2599,2526],{},"にどのような型が渡されるか分からないため、関数内で",[58,2602,2106],{},"へ安全にアクセスできません。",[779,2605,2607,2610],{"id":2606},"keyof型演算子",[58,2608,2609],{},"keyof","型演算子",[11,2612,2613,2615],{},[58,2614,2609],{},"は、オブジェクト型のプロパティ名からユニオン型を作る型演算子です。",[51,2617,2619],{"className":2262,"code":2618,"language":2239,"meta":56,"style":56},"type Todo = {\n  id: number\n  title: string\n}\n\ntype TodoKey = keyof Todo\n\u002F\u002F 'id' | 'title'\n",[58,2620,2621,2631,2639,2647,2651,2655,2670],{"__ignoreMap":56},[61,2622,2623,2625,2627,2629],{"class":63,"line":64},[61,2624,2085],{"class":1487},[61,2626,2356],{"class":67},[61,2628,2091],{"class":271},[61,2630,2094],{"class":271},[61,2632,2633,2635,2637],{"class":63,"line":81},[61,2634,2502],{"class":268},[61,2636,272],{"class":271},[61,2638,2373],{"class":67},[61,2640,2641,2643,2645],{"class":63,"line":94},[61,2642,2378],{"class":268},[61,2644,272],{"class":271},[61,2646,2383],{"class":67},[61,2648,2649],{"class":63,"line":105},[61,2650,750],{"class":271},[61,2652,2653],{"class":63,"line":305},[61,2654,287],{"emptyLinePlaceholder":286},[61,2656,2657,2659,2662,2664,2667],{"class":63,"line":757},[61,2658,2085],{"class":1487},[61,2660,2661],{"class":67}," TodoKey",[61,2663,2091],{"class":271},[61,2665,2666],{"class":271}," keyof",[61,2668,2669],{"class":67}," Todo\n",[61,2671,2672],{"class":63,"line":763},[61,2673,2675],{"class":2674},"sHwdD","\u002F\u002F 'id' | 'title'\n",[11,2677,2678],{},"プロパティを追加・削除したときにユニオン型を手作業で更新する必要がなくなり、型定義との不整合を防ぎやすくなります。",[779,2680,2681],{"id":2681},"インデックスアクセス型",[11,2683,2684],{},"インデックスアクセス型を使うと、オブジェクト型から特定のプロパティの型を取り出せます。",[51,2686,2688],{"className":2262,"code":2687,"language":2239,"meta":56,"style":56},"type Todo = {\n  id: number\n  title: string\n}\n\ntype TodoId = Todo['id']\n\u002F\u002F number\n\ntype TodoValue = Todo[keyof Todo]\n\u002F\u002F number | string\n",[58,2689,2690,2700,2708,2716,2720,2724,2746,2751,2755,2774],{"__ignoreMap":56},[61,2691,2692,2694,2696,2698],{"class":63,"line":64},[61,2693,2085],{"class":1487},[61,2695,2356],{"class":67},[61,2697,2091],{"class":271},[61,2699,2094],{"class":271},[61,2701,2702,2704,2706],{"class":63,"line":81},[61,2703,2502],{"class":268},[61,2705,272],{"class":271},[61,2707,2373],{"class":67},[61,2709,2710,2712,2714],{"class":63,"line":94},[61,2711,2378],{"class":268},[61,2713,272],{"class":271},[61,2715,2383],{"class":67},[61,2717,2718],{"class":63,"line":105},[61,2719,750],{"class":271},[61,2721,2722],{"class":63,"line":305},[61,2723,287],{"emptyLinePlaceholder":286},[61,2725,2726,2728,2731,2733,2735,2738,2740,2742,2744],{"class":63,"line":757},[61,2727,2085],{"class":1487},[61,2729,2730],{"class":67}," TodoId",[61,2732,2091],{"class":271},[61,2734,2356],{"class":67},[61,2736,2737],{"class":1623},"[",[61,2739,1703],{"class":271},[61,2741,2106],{"class":71},[61,2743,1703],{"class":271},[61,2745,1085],{"class":1623},[61,2747,2748],{"class":63,"line":763},[61,2749,2750],{"class":2674},"\u002F\u002F number\n",[61,2752,2753],{"class":63,"line":768},[61,2754,287],{"emptyLinePlaceholder":286},[61,2756,2757,2759,2762,2764,2766,2768,2770,2772],{"class":63,"line":774},[61,2758,2085],{"class":1487},[61,2760,2761],{"class":67}," TodoValue",[61,2763,2091],{"class":271},[61,2765,2356],{"class":67},[61,2767,2737],{"class":1623},[61,2769,2609],{"class":271},[61,2771,2356],{"class":67},[61,2773,1085],{"class":1623},[61,2775,2776],{"class":63,"line":833},[61,2777,2778],{"class":2674},"\u002F\u002F number | string\n",[779,2780,2781],{"id":2781},"アンビエント宣言",[11,2783,2784,2787],{},[58,2785,2786],{},"declare","を使うと、実装が別の場所に存在する値や型をTypeScriptへ伝えられます。JavaScriptライブラリや実行環境が提供するグローバル変数の型を補う場合などに使用します。",[51,2789,2791],{"className":2262,"code":2790,"language":2239,"meta":56,"style":56},"declare const appVersion: string\n",[58,2792,2793],{"__ignoreMap":56},[61,2794,2795,2797,2800,2803,2805],{"class":63,"line":64},[61,2796,2786],{"class":1487},[61,2798,2799],{"class":1487}," const",[61,2801,2802],{"class":1623}," appVersion",[61,2804,272],{"class":271},[61,2806,2383],{"class":67},[11,2808,2809,2810,2813,2814,2816],{},"一般的には",[58,2811,2812],{},".d.ts","形式の型定義ファイルへ記述します。",[58,2815,2786],{},"は実行時の値を作成しないため、実体が存在しなければ実行時エラーになります。",[779,2818,2819],{"id":2819},"メソッドの型注釈",[11,2821,2822],{},"オブジェクト型のメソッドは、メソッド構文と関数プロパティ構文のどちらでも表現できます。",[51,2824,2826],{"className":2262,"code":2825,"language":2239,"meta":56,"style":56},"type Formatter = {\n  format(value: string): string\n}\n\ntype FormatterProperty = {\n  format: (value: string) => string\n}\n",[58,2827,2828,2839,2859,2863,2867,2878,2898],{"__ignoreMap":56},[61,2829,2830,2832,2835,2837],{"class":63,"line":64},[61,2831,2085],{"class":1487},[61,2833,2834],{"class":67}," Formatter",[61,2836,2091],{"class":271},[61,2838,2094],{"class":271},[61,2840,2841,2844,2846,2849,2851,2854,2857],{"class":63,"line":81},[61,2842,2843],{"class":268},"  format",[61,2845,1700],{"class":271},[61,2847,2848],{"class":1844},"value",[61,2850,272],{"class":271},[61,2852,2853],{"class":67}," string",[61,2855,2856],{"class":271},"):",[61,2858,2383],{"class":67},[61,2860,2861],{"class":63,"line":94},[61,2862,750],{"class":271},[61,2864,2865],{"class":63,"line":105},[61,2866,287],{"emptyLinePlaceholder":286},[61,2868,2869,2871,2874,2876],{"class":63,"line":305},[61,2870,2085],{"class":1487},[61,2872,2873],{"class":67}," FormatterProperty",[61,2875,2091],{"class":271},[61,2877,2094],{"class":271},[61,2879,2880,2882,2884,2886,2888,2890,2892,2894,2896],{"class":63,"line":757},[61,2881,2843],{"class":268},[61,2883,272],{"class":271},[61,2885,1723],{"class":271},[61,2887,2848],{"class":1844},[61,2889,272],{"class":271},[61,2891,2853],{"class":67},[61,2893,2114],{"class":271},[61,2895,2047],{"class":1487},[61,2897,2383],{"class":67},[61,2899,2900],{"class":63,"line":763},[61,2901,750],{"class":271},[11,2903,2904,2905,2908],{},"両者には、継承や",[58,2906,2907],{},"strictFunctionTypes","における引数型の扱いなどで違いがあります。プロジェクト内で意図に合った書き方へ揃えることが大切です。",[779,2910,2911],{"id":2911},"型ガード",[11,2913,2914],{},"型ガードは、実行時の確認を通して変数の型を絞り込む仕組みです。ユーザー定義型ガードでは、戻り値の型に型述語を指定します。",[51,2916,2918],{"className":2262,"code":2917,"language":2239,"meta":56,"style":56},"function isNumber(value: unknown): value is number {\n  return typeof value === 'number'\n}\n\nconst value: unknown = 10\n\nif (isNumber(value)) {\n  console.log(value.toFixed(2))\n}\n",[58,2919,2920,2948,2966,2970,2974,2989,2993,3007,3032],{"__ignoreMap":56},[61,2921,2922,2924,2927,2929,2931,2933,2936,2938,2941,2944,2946],{"class":63,"line":64},[61,2923,2132],{"class":1487},[61,2925,2926],{"class":1696}," isNumber",[61,2928,1700],{"class":271},[61,2930,2848],{"class":1844},[61,2932,272],{"class":271},[61,2934,2935],{"class":67}," unknown",[61,2937,2856],{"class":271},[61,2939,2940],{"class":1844}," value",[61,2942,2943],{"class":271}," is",[61,2945,2111],{"class":67},[61,2947,2094],{"class":271},[61,2949,2950,2952,2955,2957,2959,2961,2964],{"class":63,"line":81},[61,2951,2193],{"class":1617},[61,2953,2954],{"class":271}," typeof",[61,2956,2940],{"class":1623},[61,2958,2586],{"class":271},[61,2960,275],{"class":271},[61,2962,2963],{"class":71},"number",[61,2965,281],{"class":271},[61,2967,2968],{"class":63,"line":94},[61,2969,750],{"class":271},[61,2971,2972],{"class":63,"line":105},[61,2973,287],{"emptyLinePlaceholder":286},[61,2975,2976,2978,2980,2982,2984,2986],{"class":63,"line":305},[61,2977,1682],{"class":1487},[61,2979,2940],{"class":1623},[61,2981,272],{"class":271},[61,2983,2935],{"class":67},[61,2985,2091],{"class":271},[61,2987,2988],{"class":536}," 10\n",[61,2990,2991],{"class":63,"line":757},[61,2992,287],{"emptyLinePlaceholder":286},[61,2994,2995,2997,2999,3002,3005],{"class":63,"line":763},[61,2996,1720],{"class":1617},[61,2998,1723],{"class":1623},[61,3000,3001],{"class":1696},"isNumber",[61,3003,3004],{"class":1623},"(value)) ",[61,3006,1732],{"class":271},[61,3008,3009,3011,3013,3015,3017,3019,3021,3024,3026,3029],{"class":63,"line":768},[61,3010,2439],{"class":1623},[61,3012,1693],{"class":271},[61,3014,2444],{"class":1696},[61,3016,1700],{"class":268},[61,3018,2848],{"class":1623},[61,3020,1693],{"class":271},[61,3022,3023],{"class":1696},"toFixed",[61,3025,1700],{"class":268},[61,3027,3028],{"class":536},"2",[61,3030,3031],{"class":268},"))\n",[61,3033,3034],{"class":63,"line":774},[61,3035,750],{"class":271},[11,3037,3038],{},"APIレスポンスなど外部から受け取った値は、型アサーションで決めつけるのではなく、実行時に検証してから利用すると安全です。",[25,3040,636],{"id":636},[11,3042,3043],{},"Todoアプリの実装を通して、HTML属性の使い方からReactのstate更新、TypeScriptの型操作まで、幅広い内容を学べました。",[11,3045,3046],{},"個々の構文を覚えるだけでなく、「なぜその書き方が必要なのか」を実際のアプリ開発と結び付けて理解することが大切だと感じました。",[679,3048,3049],{},"html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sHdIc, html code.shiki .sHdIc{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#EEFFFF;--shiki-default-font-style:italic;--shiki-dark:#BABED8;--shiki-dark-font-style:italic}html pre.shiki code .sfNiH, html code.shiki .sfNiH{--shiki-light:#FF5370;--shiki-default:#FF9CAC;--shiki-dark:#FF9CAC}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}",{"title":56,"searchDepth":81,"depth":81,"links":3051},[3052,3055,3059,3066,3078],{"id":1454,"depth":81,"text":1443,"children":3053},[3054],{"id":1457,"depth":94,"text":1457},{"id":728,"depth":81,"text":1420,"children":3056},[3057,3058],{"id":1519,"depth":94,"text":1519},{"id":1570,"depth":94,"text":1571},{"id":1590,"depth":81,"text":1448,"children":3060},[3061,3062,3063,3064,3065],{"id":1593,"depth":94,"text":1596},{"id":1826,"depth":94,"text":1826},{"id":2026,"depth":94,"text":2026},{"id":2064,"depth":94,"text":2064},{"id":2230,"depth":94,"text":2230},{"id":2239,"depth":81,"text":1451,"children":3067},[3068,3070,3071,3072,3073,3074,3075,3076,3077],{"id":2242,"depth":94,"text":3069},"document.getElementByIdとnull",{"id":2336,"depth":94,"text":2336},{"id":2395,"depth":94,"text":2395},{"id":2474,"depth":94,"text":2474},{"id":2606,"depth":94,"text":2606},{"id":2681,"depth":94,"text":2681},{"id":2781,"depth":94,"text":2781},{"id":2819,"depth":94,"text":2819},{"id":2911,"depth":94,"text":2911},{"id":636,"depth":81,"text":636},"React HooksとTypeScriptでTodoアプリを作るハンズオンを通して学んだ、HTML、JavaScript、React、TypeScriptのポイントを整理します。",{},"\u002Farticles\u002F2024\u002Forganize-learned-from-todo","2024-12-23",{"title":1425,"description":3079},"articles\u002F2024\u002Forganize-learned-from-todo",[1448,1451],"\u002Fimages\u002Farticles\u002Forganize-learned-from-todo.png","ic1Do2Gie9Gb4WmAmZ2IL55ETNgmySMOwEHT_s3q0bk",{"id":3089,"title":3090,"body":3091,"description":3568,"extension":696,"meta":3569,"navigation":286,"path":3570,"publishDate":3571,"published":286,"seo":3572,"stem":3573,"tags":3574,"thumbnail":3576,"updated":3571,"__hash__":3577},"articles\u002Farticles\u002F2024\u002Fdescribe-webstorage-indexeddb.md","Web StorageとIndexedDBについて整理する",{"type":8,"value":3092,"toc":3558},[3093,3095,3098,3108,3111,3114,3117,3120,3204,3207,3212,3215,3243,3254,3305,3308,3311,3319,3338,3341,3344,3347,3350,3353,3455,3458,3472,3475,3478,3481,3505,3511,3514,3517,3524,3527,3530,3545,3548,3550,3553,3556],[11,3094,714],{},[11,3096,3097],{},"Todoアプリを作った際にIndexedDBを利用したのですが、ブラウザにデータを保存する方法としてはWeb Storageもあります。",[11,3099,3100,3101,118,3104,3107],{},"今回は、",[58,3102,3103],{},"localStorage",[58,3105,3106],{},"sessionStorage","、IndexedDBの特徴と使い分けを整理します。",[25,3109,3110],{"id":3110},"ブラウザにデータを保存する仕組み",[11,3112,3113],{},"Web StorageとIndexedDBは、どちらもブラウザ側にデータを保存するためのWeb APIです。",[11,3115,3116],{},"保存したデータが自動的にサーバーへ送信されることはなく、同一オリジンの制約を受けます。ただし、保存容量やデータが削除される条件はブラウザや利用状況によって異なるため、永続性を前提にしすぎないことが大切です。",[11,3118,3119],{},"主な違いは次のとおりです。",[403,3121,3122,3136],{},[406,3123,3124],{},[409,3125,3126,3129,3131,3133],{},[412,3127,3128],{},"項目",[412,3130,3103],{},[412,3132,3106],{},[412,3134,3135],{},"IndexedDB",[419,3137,3138,3151,3164,3177,3190],{},[409,3139,3140,3143,3146,3148],{},[424,3141,3142],{},"保存形式",[424,3144,3145],{},"文字列のキーバリュー",[424,3147,3145],{},[424,3149,3150],{},"オブジェクトやBlobなど",[409,3152,3153,3156,3159,3161],{},[424,3154,3155],{},"API",[424,3157,3158],{},"同期",[424,3160,3158],{},[424,3162,3163],{},"非同期",[409,3165,3166,3169,3172,3175],{},[424,3167,3168],{},"データの範囲",[424,3170,3171],{},"同一オリジン",[424,3173,3174],{},"同一オリジンかつタブ単位",[424,3176,3171],{},[409,3178,3179,3182,3185,3188],{},[424,3180,3181],{},"データの寿命",[424,3183,3184],{},"明示的に削除されるまで",[424,3186,3187],{},"タブを閉じるまで",[424,3189,3184],{},[409,3191,3192,3195,3198,3201],{},[424,3193,3194],{},"得意な用途",[424,3196,3197],{},"小さな設定値",[424,3199,3200],{},"一時的な入力状態",[424,3202,3203],{},"大量・構造化データ",[25,3205,3103],{"id":3206},"localstorage",[11,3208,3209,3211],{},[58,3210,3103],{},"は、文字列をキーバリュー形式で保存する同期APIです。",[11,3213,3214],{},"ブラウザやタブを閉じてもデータが残るため、テーマや表示設定など、小さく単純なデータの保存に向いています。",[51,3216,3218],{"className":726,"code":3217,"language":728,"meta":56,"style":56},"localStorage.setItem('theme', 'dark')\n\nconst theme = localStorage.getItem('theme')\n\nlocalStorage.removeItem('theme')\n",[58,3219,3220,3225,3229,3234,3238],{"__ignoreMap":56},[61,3221,3222],{"class":63,"line":64},[61,3223,3224],{},"localStorage.setItem('theme', 'dark')\n",[61,3226,3227],{"class":63,"line":81},[61,3228,287],{"emptyLinePlaceholder":286},[61,3230,3231],{"class":63,"line":94},[61,3232,3233],{},"const theme = localStorage.getItem('theme')\n",[61,3235,3236],{"class":63,"line":105},[61,3237,287],{"emptyLinePlaceholder":286},[61,3239,3240],{"class":63,"line":305},[61,3241,3242],{},"localStorage.removeItem('theme')\n",[11,3244,3245,3246,3249,3250,3253],{},"値は文字列として保存されます。オブジェクトを扱う場合は、保存時に",[58,3247,3248],{},"JSON.stringify()","、取得時に",[58,3251,3252],{},"JSON.parse()","が必要です。",[51,3255,3257],{"className":726,"code":3256,"language":728,"meta":56,"style":56},"const settings = {\n  theme: 'dark',\n  fontSize: 16,\n}\n\nlocalStorage.setItem('settings', JSON.stringify(settings))\n\nconst storedSettings = JSON.parse(\n  localStorage.getItem('settings') ?? '{}',\n)\n",[58,3258,3259,3264,3269,3274,3278,3282,3287,3291,3296,3301],{"__ignoreMap":56},[61,3260,3261],{"class":63,"line":64},[61,3262,3263],{},"const settings = {\n",[61,3265,3266],{"class":63,"line":81},[61,3267,3268],{},"  theme: 'dark',\n",[61,3270,3271],{"class":63,"line":94},[61,3272,3273],{},"  fontSize: 16,\n",[61,3275,3276],{"class":63,"line":105},[61,3277,750],{},[61,3279,3280],{"class":63,"line":305},[61,3281,287],{"emptyLinePlaceholder":286},[61,3283,3284],{"class":63,"line":757},[61,3285,3286],{},"localStorage.setItem('settings', JSON.stringify(settings))\n",[61,3288,3289],{"class":63,"line":763},[61,3290,287],{"emptyLinePlaceholder":286},[61,3292,3293],{"class":63,"line":768},[61,3294,3295],{},"const storedSettings = JSON.parse(\n",[61,3297,3298],{"class":63,"line":774},[61,3299,3300],{},"  localStorage.getItem('settings') ?? '{}',\n",[61,3302,3303],{"class":63,"line":833},[61,3304,1711],{},[11,3306,3307],{},"同期APIのため、大きなデータを頻繁に読み書きするとメインスレッドを塞ぐ可能性があります。",[25,3309,3106],{"id":3310},"sessionstorage",[11,3312,3313,3315,3316,3318],{},[58,3314,3106],{},"も、文字列をキーバリュー形式で保存する同期APIです。基本的な操作方法は",[58,3317,3103],{},"と同じです。",[51,3320,3322],{"className":726,"code":3321,"language":728,"meta":56,"style":56},"sessionStorage.setItem('draft', '入力途中の内容')\n\nconst draft = sessionStorage.getItem('draft')\n",[58,3323,3324,3329,3333],{"__ignoreMap":56},[61,3325,3326],{"class":63,"line":64},[61,3327,3328],{},"sessionStorage.setItem('draft', '入力途中の内容')\n",[61,3330,3331],{"class":63,"line":81},[61,3332,287],{"emptyLinePlaceholder":286},[61,3334,3335],{"class":63,"line":94},[61,3336,3337],{},"const draft = sessionStorage.getItem('draft')\n",[11,3339,3340],{},"ページを再読み込みしてもデータは残りますが、保存領域はタブごとのページセッションに分かれ、タブを閉じるとデータも削除されます。",[11,3342,3343],{},"そのため、フォームの入力途中の状態や、画面遷移中だけ保持したい一時的な情報に向いています。",[25,3345,3135],{"id":3346},"indexeddb",[11,3348,3349],{},"IndexedDBは、大量の構造化データをブラウザへ保存するための非同期APIです。",[11,3351,3352],{},"JavaScriptのオブジェクトだけでなく、ファイルやBlobなども保存できます。また、オブジェクトストア、インデックス、トランザクションを利用できるため、検索や更新を伴うデータ管理に向いています。",[51,3354,3356],{"className":726,"code":3355,"language":728,"meta":56,"style":56},"const request = indexedDB.open('todo-app', 1)\n\nrequest.onupgradeneeded = () => {\n  const database = request.result\n\n  if (!database.objectStoreNames.contains('todos')) {\n    database.createObjectStore('todos', {\n      keyPath: 'id',\n      autoIncrement: true,\n    })\n  }\n}\n\nrequest.onsuccess = () => {\n  const database = request.result\n  const transaction = database.transaction('todos', 'readwrite')\n  const todos = transaction.objectStore('todos')\n\n  todos.add({ title: '記事を書く', completed: false })\n}\n",[58,3357,3358,3363,3367,3372,3377,3381,3386,3391,3396,3401,3406,3410,3414,3418,3423,3427,3433,3439,3444,3450],{"__ignoreMap":56},[61,3359,3360],{"class":63,"line":64},[61,3361,3362],{},"const request = indexedDB.open('todo-app', 1)\n",[61,3364,3365],{"class":63,"line":81},[61,3366,287],{"emptyLinePlaceholder":286},[61,3368,3369],{"class":63,"line":94},[61,3370,3371],{},"request.onupgradeneeded = () => {\n",[61,3373,3374],{"class":63,"line":105},[61,3375,3376],{},"  const database = request.result\n",[61,3378,3379],{"class":63,"line":305},[61,3380,287],{"emptyLinePlaceholder":286},[61,3382,3383],{"class":63,"line":757},[61,3384,3385],{},"  if (!database.objectStoreNames.contains('todos')) {\n",[61,3387,3388],{"class":63,"line":763},[61,3389,3390],{},"    database.createObjectStore('todos', {\n",[61,3392,3393],{"class":63,"line":768},[61,3394,3395],{},"      keyPath: 'id',\n",[61,3397,3398],{"class":63,"line":774},[61,3399,3400],{},"      autoIncrement: true,\n",[61,3402,3403],{"class":63,"line":833},[61,3404,3405],{},"    })\n",[61,3407,3408],{"class":63,"line":839},[61,3409,2184],{},[61,3411,3412],{"class":63,"line":845},[61,3413,750],{},[61,3415,3416],{"class":63,"line":850},[61,3417,287],{"emptyLinePlaceholder":286},[61,3419,3420],{"class":63,"line":855},[61,3421,3422],{},"request.onsuccess = () => {\n",[61,3424,3425],{"class":63,"line":1810},[61,3426,3376],{},[61,3428,3430],{"class":63,"line":3429},16,[61,3431,3432],{},"  const transaction = database.transaction('todos', 'readwrite')\n",[61,3434,3436],{"class":63,"line":3435},17,[61,3437,3438],{},"  const todos = transaction.objectStore('todos')\n",[61,3440,3442],{"class":63,"line":3441},18,[61,3443,287],{"emptyLinePlaceholder":286},[61,3445,3447],{"class":63,"line":3446},19,[61,3448,3449],{},"  todos.add({ title: '記事を書く', completed: false })\n",[61,3451,3453],{"class":63,"line":3452},20,[61,3454,750],{},[11,3456,3457],{},"Web Storageよりも扱う概念は多いものの、次のような用途で力を発揮します。",[644,3459,3460,3463,3466,3469],{},[647,3461,3462],{},"オフラインでも利用するアプリ",[647,3464,3465],{},"Todoやメモなど、件数が増えるデータ",[647,3467,3468],{},"検索や並び替えにインデックスを使いたいデータ",[647,3470,3471],{},"画像やファイルを含むデータ",[25,3473,3474],{"id":3474},"保存容量とデータの削除",[11,3476,3477],{},"ブラウザストレージの容量は、特定の数値をどの環境でも保証できるものではありません。ブラウザ、端末の空き容量、ユーザー設定などによって変わります。",[11,3479,3480],{},"利用状況はStorage APIから概算できます。",[51,3482,3484],{"className":726,"code":3483,"language":728,"meta":56,"style":56},"const estimate = await navigator.storage.estimate()\n\nconsole.log(estimate.usage)\nconsole.log(estimate.quota)\n",[58,3485,3486,3491,3495,3500],{"__ignoreMap":56},[61,3487,3488],{"class":63,"line":64},[61,3489,3490],{},"const estimate = await navigator.storage.estimate()\n",[61,3492,3493],{"class":63,"line":81},[61,3494,287],{"emptyLinePlaceholder":286},[61,3496,3497],{"class":63,"line":94},[61,3498,3499],{},"console.log(estimate.usage)\n",[61,3501,3502],{"class":63,"line":105},[61,3503,3504],{},"console.log(estimate.quota)\n",[36,3506,3508],{"title":3507,"type":1511},"ブラウザストレージを唯一の保存先にしない",[11,3509,3510],{},"保存したデータは、ユーザーによるサイトデータの削除や、ブラウザのストレージ管理によって失われる場合があります。失われると困るデータはサーバーにも保存しましょう。",[25,3512,3513],{"id":3513},"セキュリティ上の注意",[11,3515,3516],{},"Web StorageとIndexedDBは、認証された同一オリジンのJavaScriptからアクセスできます。",[36,3518,3521],{"title":3519,"type":3520},"機密情報の保存に注意する","warning",[11,3522,3523],{},"クロスサイトスクリプティング（XSS）が発生すると、保存内容を読み取られる可能性があります。アクセストークンや個人情報などの機密情報を、安易に保存しないようにしましょう。",[25,3525,3526],{"id":3526},"どれを選ぶか",[11,3528,3529],{},"選び方を簡単にまとめると、次のようになります。",[644,3531,3532,3537,3542],{},[647,3533,3534,3535],{},"小さな設定をブラウザを閉じた後も残したい場合は",[58,3536,3103],{},[647,3538,3539,3540],{},"タブを開いている間だけ一時的に残したい場合は",[58,3541,3106],{},[647,3543,3544],{},"大量のデータや構造化データを非同期で扱いたい場合はIndexedDB",[11,3546,3547],{},"Web Storageは手軽ですが、同期APIであり文字列しか保存できません。データ量が増えるアプリや、検索・更新を伴うデータにはIndexedDBを検討するとよいでしょう。",[25,3549,636],{"id":636},[11,3551,3552],{},"Web StorageとIndexedDBは、どちらもブラウザ側へデータを保存できますが、得意な用途が異なります。",[11,3554,3555],{},"保存するデータの量や形式だけでなく、同期処理による影響、データを保持する期間、失われた場合の影響まで考えて選ぶことが大切です。",[679,3557,1395],{},{"title":56,"searchDepth":81,"depth":81,"links":3559},[3560,3561,3562,3563,3564,3565,3566,3567],{"id":3110,"depth":81,"text":3110},{"id":3206,"depth":81,"text":3103},{"id":3310,"depth":81,"text":3106},{"id":3346,"depth":81,"text":3135},{"id":3474,"depth":81,"text":3474},{"id":3513,"depth":81,"text":3513},{"id":3526,"depth":81,"text":3526},{"id":636,"depth":81,"text":636},"localStorage、sessionStorage、IndexedDBの違いを、保存形式、同期・非同期、データの寿命、用途から整理します。",{},"\u002Farticles\u002F2024\u002Fdescribe-webstorage-indexeddb","2024-12-22",{"title":3090,"description":3568},"articles\u002F2024\u002Fdescribe-webstorage-indexeddb",[3575,3135],"Web Storage","\u002Fimages\u002Farticles\u002Fdescribe-webstorage-indexeddb.png","3W_8UP01CjjLWIG3qOQhJB7e1YiLPuYONYyx_372v98",{"id":3579,"title":3580,"body":3581,"description":4058,"extension":696,"meta":4059,"navigation":286,"path":4060,"publishDate":4061,"published":286,"seo":4062,"stem":4063,"tags":4064,"thumbnail":4065,"updated":4061,"__hash__":4066},"articles\u002Farticles\u002F2024\u002Flearn-javascript-eventloop-from-runtime.md","JavaScriptのランタイム構成からイベントループを学ぶ",{"type":8,"value":3582,"toc":4048},[3583,3585,3594,3597,3601,3604,3607,3610,3627,3629,3632,3635,3699,3702,3736,3739,3742,3745,3748,3762,3765,3808,3811,3813,3816,3819,3833,3838,3841,3843,3846,3871,3874,3877,3879,3882,3885,3902,3905,3908,3983,3986,3992,4003,4012,4014,4034,4037,4046],[11,3584,714],{},[11,3586,3587,3588,3593],{},"今回は、JavaScriptのイベントループについて、動画「",[15,3589,3592],{"href":3590,"rel":3591},"https:\u002F\u002Fwww.youtube.com\u002Fwatch?v=eiC58R16hb8",[19],"JavaScript Visualized - Event Loop, Web APIs, (Micro)task Queue","」で学んだ内容を整理します。",[11,3595,3596],{},"ブラウザでJavaScriptがどのように同期処理と非同期処理を扱っているのか、ランタイムを構成する要素から確認していきます。",[25,3598,3600],{"id":3599},"javascriptエンジンとホスト環境","JavaScriptエンジンとホスト環境",[11,3602,3603],{},"ブラウザでJavaScriptを実行するためには、JavaScriptエンジンとブラウザというホスト環境が連携します。",[11,3605,3606],{},"JavaScriptエンジンはコードを解析・実行し、ブラウザはタイマー、ネットワーク通信、DOMイベントなどの環境固有の機能を提供します。",[11,3608,3609],{},"イベントループを理解するうえでは、次の要素を押さえておくと全体像が見えやすくなります。",[644,3611,3612,3615,3618,3621,3624],{},[647,3613,3614],{},"コールスタック",[647,3616,3617],{},"Web APIs",[647,3619,3620],{},"タスクキュー",[647,3622,3623],{},"マイクロタスクキュー",[647,3625,3626],{},"イベントループ",[25,3628,3614],{"id":3614},[11,3630,3631],{},"コールスタックは、実行中の関数や実行コンテキストを管理するスタックです。",[11,3633,3634],{},"後から追加されたものが先に取り出される、後入れ先出し（LIFO）の構造になっています。",[51,3636,3638],{"className":726,"code":3637,"language":728,"meta":56,"style":56},"function a() {\n  console.log('a')\n}\n\nfunction b() {\n  a()\n}\n\nfunction c() {\n  b()\n}\n\nc()\n",[58,3639,3640,3645,3650,3654,3658,3663,3668,3672,3676,3681,3686,3690,3694],{"__ignoreMap":56},[61,3641,3642],{"class":63,"line":64},[61,3643,3644],{},"function a() {\n",[61,3646,3647],{"class":63,"line":81},[61,3648,3649],{},"  console.log('a')\n",[61,3651,3652],{"class":63,"line":94},[61,3653,750],{},[61,3655,3656],{"class":63,"line":105},[61,3657,287],{"emptyLinePlaceholder":286},[61,3659,3660],{"class":63,"line":305},[61,3661,3662],{},"function b() {\n",[61,3664,3665],{"class":63,"line":757},[61,3666,3667],{},"  a()\n",[61,3669,3670],{"class":63,"line":763},[61,3671,750],{},[61,3673,3674],{"class":63,"line":768},[61,3675,287],{"emptyLinePlaceholder":286},[61,3677,3678],{"class":63,"line":774},[61,3679,3680],{},"function c() {\n",[61,3682,3683],{"class":63,"line":833},[61,3684,3685],{},"  b()\n",[61,3687,3688],{"class":63,"line":839},[61,3689,750],{},[61,3691,3692],{"class":63,"line":845},[61,3693,287],{"emptyLinePlaceholder":286},[61,3695,3696],{"class":63,"line":850},[61,3697,3698],{},"c()\n",[11,3700,3701],{},"このコードは、次の流れで実行されます。",[3703,3704,3705,3711,3720,3727],"ol",{},[647,3706,3707,3710],{},[58,3708,3709],{},"c()"," がスタックへ追加される",[647,3712,3713,3715,3716,3719],{},[58,3714,3709],{}," から呼ばれた ",[58,3717,3718],{},"b()"," が追加される",[647,3721,3722,3715,3724,3719],{},[58,3723,3718],{},[58,3725,3726],{},"a()",[647,3728,3729,118,3731,118,3733,3735],{},[58,3730,3726],{},[58,3732,3718],{},[58,3734,3709],{}," の順に処理を終えてスタックから取り除かれる",[11,3737,3738],{},"1つの長い同期処理がコールスタックを占有すると、その処理が終わるまでクリックや描画など、同じスレッド上のほかの処理を進められません。",[25,3740,3617],{"id":3741},"web-apis",[11,3743,3744],{},"Web APIsは、ブラウザというホスト環境が提供する機能です。",[11,3746,3747],{},"代表的なものには、次があります。",[644,3749,3750,3753,3756,3759],{},[647,3751,3752],{},"Fetch API",[647,3754,3755],{},"タイマー",[647,3757,3758],{},"Geolocation API",[647,3760,3761],{},"DOMイベント",[11,3763,3764],{},"JavaScriptからこれらの非同期APIを呼び出すと、ブラウザ側でタイマーの計測やネットワーク通信などが進みます。JavaScriptはその完了を待ってコールスタックを占有し続ける必要がないため、別のコードを実行できます。",[51,3766,3768],{"className":726,"code":3767,"language":728,"meta":56,"style":56},"navigator.geolocation.getCurrentPosition(\n  position => console.log(position),\n  error => console.error(error),\n)\n\nfetch('https:\u002F\u002Fexample.com\u002Fapi\u002Fdata')\n  .then(response => response.json())\n  .then(data => console.log(data))\n",[58,3769,3770,3775,3780,3785,3789,3793,3798,3803],{"__ignoreMap":56},[61,3771,3772],{"class":63,"line":64},[61,3773,3774],{},"navigator.geolocation.getCurrentPosition(\n",[61,3776,3777],{"class":63,"line":81},[61,3778,3779],{},"  position => console.log(position),\n",[61,3781,3782],{"class":63,"line":94},[61,3783,3784],{},"  error => console.error(error),\n",[61,3786,3787],{"class":63,"line":105},[61,3788,1711],{},[61,3790,3791],{"class":63,"line":305},[61,3792,287],{"emptyLinePlaceholder":286},[61,3794,3795],{"class":63,"line":757},[61,3796,3797],{},"fetch('https:\u002F\u002Fexample.com\u002Fapi\u002Fdata')\n",[61,3799,3800],{"class":63,"line":763},[61,3801,3802],{},"  .then(response => response.json())\n",[61,3804,3805],{"class":63,"line":768},[61,3806,3807],{},"  .then(data => console.log(data))\n",[11,3809,3810],{},"APIの処理が完了しても、コールバックがその場ですぐに実行されるとは限りません。実行可能になった処理は、タスクやマイクロタスクとしてキューへ登録されます。",[25,3812,3620],{"id":3620},[11,3814,3815],{},"タスクキューには、タイマーやイベントなどをきっかけに実行可能になったタスクが並びます。",[11,3817,3818],{},"代表的な例は次のとおりです。",[644,3820,3821,3827,3830],{},[647,3822,3823,3826],{},[58,3824,3825],{},"setTimeout()"," のコールバック",[647,3828,3829],{},"ユーザー操作のイベントハンドラー",[647,3831,3832],{},"メッセージイベント",[11,3834,3835,3837],{},[58,3836,3825],{}," に指定する時間は、コールバックが必ずその時間後に実行されるという意味ではありません。指定時間を経過したあとにタスクとして実行可能になるまでの最低待機時間です。",[11,3839,3840],{},"コールスタックや先に処理されるタスクが残っていれば、実際の実行はさらに遅くなります。",[25,3842,3623],{"id":3623},[11,3844,3845],{},"マイクロタスクキューには、主に次の処理が登録されます。",[644,3847,3848,3860,3866],{},[647,3849,3850,3851,118,3854,118,3857],{},"Promiseの ",[58,3852,3853],{},"then()",[58,3855,3856],{},"catch()",[58,3858,3859],{},"finally()",[647,3861,3862,3865],{},[58,3863,3864],{},"queueMicrotask()"," に渡したコールバック",[647,3867,3868,3826],{},[58,3869,3870],{},"MutationObserver",[11,3872,3873],{},"現在のタスクと同期処理が終わってコールスタックが空になると、次のタスクへ進む前にマイクロタスクキューが空になるまで処理されます。",[11,3875,3876],{},"マイクロタスクの実行中に新しいマイクロタスクが追加された場合も、同じタイミングで続けて処理されます。そのため、マイクロタスクを追加し続ける処理は、次のタスクや画面描画を遅らせる可能性があります。",[25,3878,3626],{"id":3626},[11,3880,3881],{},"イベントループは、コールスタックと各キューの状態を見ながら、実行する処理を選び続けます。",[11,3883,3884],{},"ブラウザ上の流れを単純化すると、次のようになります。",[3703,3886,3887,3890,3893,3896,3899],{},[647,3888,3889],{},"1つのタスクを実行する",[647,3891,3892],{},"コールスタック上の同期処理を最後まで実行する",[647,3894,3895],{},"マイクロタスクキューを空になるまで処理する",[647,3897,3898],{},"必要に応じて画面を描画する",[647,3900,3901],{},"次のタスクへ進む",[25,3903,3904],{"id":3904},"コードで実行順を確認する",[11,3906,3907],{},"タスクとマイクロタスクの実行順を、コードで確認してみましょう。",[51,3909,3911],{"className":726,"code":3910,"language":728,"meta":56,"style":56},"console.log('start')\n\nsetTimeout(() => {\n  console.log('timeout')\n}, 0)\n\nPromise.resolve().then(() => {\n  console.log('promise')\n})\n\nqueueMicrotask(() => {\n  console.log('microtask')\n})\n\nconsole.log('end')\n",[58,3912,3913,3918,3922,3927,3932,3937,3941,3946,3951,3956,3960,3965,3970,3974,3978],{"__ignoreMap":56},[61,3914,3915],{"class":63,"line":64},[61,3916,3917],{},"console.log('start')\n",[61,3919,3920],{"class":63,"line":81},[61,3921,287],{"emptyLinePlaceholder":286},[61,3923,3924],{"class":63,"line":94},[61,3925,3926],{},"setTimeout(() => {\n",[61,3928,3929],{"class":63,"line":105},[61,3930,3931],{},"  console.log('timeout')\n",[61,3933,3934],{"class":63,"line":305},[61,3935,3936],{},"}, 0)\n",[61,3938,3939],{"class":63,"line":757},[61,3940,287],{"emptyLinePlaceholder":286},[61,3942,3943],{"class":63,"line":763},[61,3944,3945],{},"Promise.resolve().then(() => {\n",[61,3947,3948],{"class":63,"line":768},[61,3949,3950],{},"  console.log('promise')\n",[61,3952,3953],{"class":63,"line":774},[61,3954,3955],{},"})\n",[61,3957,3958],{"class":63,"line":833},[61,3959,287],{"emptyLinePlaceholder":286},[61,3961,3962],{"class":63,"line":839},[61,3963,3964],{},"queueMicrotask(() => {\n",[61,3966,3967],{"class":63,"line":845},[61,3968,3969],{},"  console.log('microtask')\n",[61,3971,3972],{"class":63,"line":850},[61,3973,3955],{},[61,3975,3976],{"class":63,"line":855},[61,3977,287],{"emptyLinePlaceholder":286},[61,3979,3980],{"class":63,"line":1810},[61,3981,3982],{},"console.log('end')\n",[11,3984,3985],{},"出力順は次のようになります。",[51,3987,3990],{"className":3988,"code":3989,"language":220,"meta":56},[218],"start\nend\npromise\nmicrotask\ntimeout\n",[58,3991,3989],{"__ignoreMap":56},[11,3993,3994,3995,3998,3999,4002],{},"まず同期処理の ",[58,3996,3997],{},"start"," と ",[58,4000,4001],{},"end"," が出力されます。",[11,4004,4005,4006,4008,4009,4011],{},"続いて、登録された順にPromiseのコールバックと ",[58,4007,3864],{}," のコールバックがマイクロタスクとして実行されます。マイクロタスクキューが空になったあと、",[58,4010,3825],{}," のコールバックが次のタスクとして実行されます。",[25,4013,636],{"id":636},[644,4015,4016,4019,4022,4025,4028,4031],{},[647,4017,4018],{},"コールスタックは、実行中の関数をLIFOで管理する",[647,4020,4021],{},"ブラウザは、タイマーやネットワーク通信などのWeb APIsを提供する",[647,4023,4024],{},"タイマーやイベントのコールバックなどは、タスクとしてキューへ登録される",[647,4026,4027],{},"Promiseのコールバックなどは、マイクロタスクとして登録される",[647,4029,4030],{},"現在のタスクが完了すると、次のタスクより先にマイクロタスクが処理される",[647,4032,4033],{},"長い同期処理や増え続けるマイクロタスクは、画面の応答を遅らせる可能性がある",[11,4035,4036],{},"非同期処理の実行順で迷ったときは、「現在の同期処理」「マイクロタスク」「次のタスク」の順に分けて考えると整理しやすくなります。",[11,4038,4039,4040,4045],{},"より詳しい実行モデルは、",[15,4041,4044],{"href":4042,"rel":4043},"https:\u002F\u002Fdeveloper.mozilla.org\u002Fja\u002Fdocs\u002FWeb\u002FJavaScript\u002FReference\u002FExecution_model",[19],"MDNのJavaScript実行モデル","も参考になります。",[679,4047,1395],{},{"title":56,"searchDepth":81,"depth":81,"links":4049},[4050,4051,4052,4053,4054,4055,4056,4057],{"id":3599,"depth":81,"text":3600},{"id":3614,"depth":81,"text":3614},{"id":3741,"depth":81,"text":3617},{"id":3620,"depth":81,"text":3620},{"id":3623,"depth":81,"text":3623},{"id":3626,"depth":81,"text":3626},{"id":3904,"depth":81,"text":3904},{"id":636,"depth":81,"text":636},"ブラウザのJavaScriptランタイムを構成するコールスタック、Web APIs、タスクキュー、マイクロタスクキューとイベントループの関係を整理します。",{},"\u002Farticles\u002F2024\u002Flearn-javascript-eventloop-from-runtime","2024-11-25",{"title":3580,"description":4058},"articles\u002F2024\u002Flearn-javascript-eventloop-from-runtime",[1420],"\u002Fimages\u002Farticles\u002Flearn-javascript-eventloop-from-runtime.png","BcnDDE_gLqK4tjYoVA9O89QF7-dNwS-6lyORZ07cH5g",{"id":4068,"title":4069,"body":4070,"description":4486,"extension":696,"meta":4487,"navigation":286,"path":4488,"publishDate":4489,"published":286,"seo":4490,"stem":4491,"tags":4492,"thumbnail":4493,"updated":4489,"__hash__":4494},"articles\u002Farticles\u002F2024\u002Fhow-to-use-javascript-built-in-objects.md","JavaScriptのビルトインオブジェクトのメソッドをどう使うか",{"type":8,"value":4071,"toc":4478},[4072,4074,4077,4080,4084,4087,4099,4108,4112,4122,4149,4166,4174,4177,4184,4224,4235,4243,4256,4280,4284,4291,4329,4335,4340,4361,4364,4373,4411,4421,4439,4441,4473,4476],[11,4073,714],{},[11,4075,4076],{},"今回は、プロジェクトで「なるほど」と思った、JavaScriptのビルトインオブジェクトのメソッドの使い方を紹介します。",[11,4078,4079],{},"具体的には、オブジェクトが自身のプロパティを持っているか、安全に確認する方法です。仕組みを知っておくと、オブジェクトに同名のメソッドが定義されている場合も意図した判定ができます。",[25,4081,4083],{"id":4082},"ビルトインオブジェクトってなに","「ビルトインオブジェクト」ってなに？",[11,4085,4086],{},"ビルトインオブジェクトは「組み込みオブジェクト」とも呼ばれ、JavaScriptの実行環境にあらかじめ用意されているオブジェクトです。",[11,4088,4089,118,4092,118,4095,4098],{},[58,4090,4091],{},"Object",[58,4093,4094],{},"Array",[58,4096,4097],{},"String"," など、さまざまな種類があります。",[11,4100,4101,4102,4107],{},"詳しくは、",[15,4103,4106],{"href":4104,"rel":4105},"https:\u002F\u002Fdeveloper.mozilla.org\u002Fja\u002Fdocs\u002FWeb\u002FJavaScript\u002FReference\u002FGlobal_Objects",[19],"MDNの標準組み込みオブジェクト","を参照してください。",[25,4109,4111],{"id":4110},"hasownpropertyをインスタンスから呼び出す","hasOwnPropertyをインスタンスから呼び出す",[11,4113,4114,4115,4117,4118,4121],{},"まずは、オブジェクト自身が ",[58,4116,965],{}," プロパティを持っているか、",[58,4119,4120],{},"hasOwnProperty()"," で確認してみます。",[51,4123,4125],{"className":726,"code":4124,"language":728,"meta":56,"style":56},"const obj = {\n  name: 'Yuri',\n}\n\nobj.hasOwnProperty('name') \u002F\u002F true\n",[58,4126,4127,4132,4136,4140,4144],{"__ignoreMap":56},[61,4128,4129],{"class":63,"line":64},[61,4130,4131],{},"const obj = {\n",[61,4133,4134],{"class":63,"line":81},[61,4135,937],{},[61,4137,4138],{"class":63,"line":94},[61,4139,750],{},[61,4141,4142],{"class":63,"line":105},[61,4143,287],{"emptyLinePlaceholder":286},[61,4145,4146],{"class":63,"line":305},[61,4147,4148],{},"obj.hasOwnProperty('name') \u002F\u002F true\n",[11,4150,4151,4152,4155,4156,4159,4160,4162,4163,4165],{},"オブジェクトリテラルで作成した ",[58,4153,4154],{},"obj"," は、通常 ",[58,4157,4158],{},"Object.prototype"," を継承しています。そのため、",[58,4161,4158],{}," に定義されている ",[58,4164,4120],{}," を呼び出せます。",[11,4167,4168,4169,4045],{},"プロトタイプ継承については、",[15,4170,4173],{"href":4171,"rel":4172},"https:\u002F\u002Fja.javascript.info\u002Fprototype-inheritance",[19],"JavaScript.infoのプロトタイプ継承",[25,4175,4176],{"id":4176},"インスタンスから呼び出す場合の問題",[11,4178,4179,4180,4183],{},"では、オブジェクト自身が ",[58,4181,4182],{},"hasOwnProperty"," という同名のメソッドを持っていたらどうなるでしょうか。",[51,4185,4187],{"className":726,"code":4186,"language":728,"meta":56,"style":56},"const obj = {\n  name: 'Yuri',\n  hasOwnProperty() {\n    console.log('Hello')\n  },\n}\n\nobj.hasOwnProperty('name') \u002F\u002F 「Hello」と出力される\n",[58,4188,4189,4193,4197,4202,4207,4211,4215,4219],{"__ignoreMap":56},[61,4190,4191],{"class":63,"line":64},[61,4192,4131],{},[61,4194,4195],{"class":63,"line":81},[61,4196,937],{},[61,4198,4199],{"class":63,"line":94},[61,4200,4201],{},"  hasOwnProperty() {\n",[61,4203,4204],{"class":63,"line":105},[61,4205,4206],{},"    console.log('Hello')\n",[61,4208,4209],{"class":63,"line":305},[61,4210,812],{},[61,4212,4213],{"class":63,"line":757},[61,4214,750],{},[61,4216,4217],{"class":63,"line":763},[61,4218,287],{"emptyLinePlaceholder":286},[61,4220,4221],{"class":63,"line":768},[61,4222,4223],{},"obj.hasOwnProperty('name') \u002F\u002F 「Hello」と出力される\n",[11,4225,4226,4227,4230,4231,4234],{},"先ほどは ",[58,4228,4229],{},"true"," を返していた呼び出しが、今度は ",[58,4232,4233],{},"Hello"," を出力しました。",[11,4236,4237,4239,4240,4242],{},[58,4238,4154],{}," 自身の ",[58,4241,4182],{}," が、プロトタイプから継承した同名メソッドよりも優先されたためです。外部から受け取ったオブジェクトなど、自分で構造を完全に管理できない値では、インスタンスから直接呼び出す方法は意図しない結果につながる可能性があります。",[11,4244,4245,4246,4249,4250,4252,4253,4255],{},"また、",[58,4247,4248],{},"Object.create(null)"," で作ったオブジェクトは ",[58,4251,4158],{}," を継承しないため、そもそも ",[58,4254,4120],{}," を持っていません。",[51,4257,4259],{"className":726,"code":4258,"language":728,"meta":56,"style":56},"const obj = Object.create(null)\nobj.name = 'Yuri'\n\nobj.hasOwnProperty('name') \u002F\u002F TypeError\n",[58,4260,4261,4266,4271,4275],{"__ignoreMap":56},[61,4262,4263],{"class":63,"line":64},[61,4264,4265],{},"const obj = Object.create(null)\n",[61,4267,4268],{"class":63,"line":81},[61,4269,4270],{},"obj.name = 'Yuri'\n",[61,4272,4273],{"class":63,"line":94},[61,4274,287],{"emptyLinePlaceholder":286},[61,4276,4277],{"class":63,"line":105},[61,4278,4279],{},"obj.hasOwnProperty('name') \u002F\u002F TypeError\n",[25,4281,4283],{"id":4282},"objecthasownを使う","Object.hasOwnを使う",[11,4285,4286,4287,4290],{},"現在は、オブジェクト自身のプロパティを調べる場合、静的メソッドの ",[58,4288,4289],{},"Object.hasOwn()"," を使うと簡潔です。",[51,4292,4294],{"className":726,"code":4293,"language":728,"meta":56,"style":56},"const obj = {\n  name: 'Yuri',\n  hasOwnProperty() {\n    console.log('Hello')\n  },\n}\n\nObject.hasOwn(obj, 'name') \u002F\u002F true\n",[58,4295,4296,4300,4304,4308,4312,4316,4320,4324],{"__ignoreMap":56},[61,4297,4298],{"class":63,"line":64},[61,4299,4131],{},[61,4301,4302],{"class":63,"line":81},[61,4303,937],{},[61,4305,4306],{"class":63,"line":94},[61,4307,4201],{},[61,4309,4310],{"class":63,"line":105},[61,4311,4206],{},[61,4313,4314],{"class":63,"line":305},[61,4315,812],{},[61,4317,4318],{"class":63,"line":757},[61,4319,750],{},[61,4321,4322],{"class":63,"line":763},[61,4323,287],{"emptyLinePlaceholder":286},[61,4325,4326],{"class":63,"line":768},[61,4327,4328],{},"Object.hasOwn(obj, 'name') \u002F\u002F true\n",[11,4330,4331,4332,4334],{},"第1引数に調べたいオブジェクト、第2引数にプロパティ名を渡します。対象のオブジェクトに同名の ",[58,4333,4182],{}," が定義されていても影響を受けません。",[11,4336,4337,4339],{},[58,4338,4248],{}," で作ったオブジェクトにも使用できます。",[51,4341,4343],{"className":726,"code":4342,"language":728,"meta":56,"style":56},"const obj = Object.create(null)\nobj.name = 'Yuri'\n\nObject.hasOwn(obj, 'name') \u002F\u002F true\n",[58,4344,4345,4349,4353,4357],{"__ignoreMap":56},[61,4346,4347],{"class":63,"line":64},[61,4348,4265],{},[61,4350,4351],{"class":63,"line":81},[61,4352,4270],{},[61,4354,4355],{"class":63,"line":94},[61,4356,287],{"emptyLinePlaceholder":286},[61,4358,4359],{"class":63,"line":105},[61,4360,4328],{},[25,4362,4363],{"id":4363},"古い実行環境も考慮する場合",[11,4365,4366,4368,4369,4372],{},[58,4367,4289],{}," を利用できない古い実行環境まで考慮する場合は、",[58,4370,4371],{},"Object.prototype.hasOwnProperty.call()"," という書き方もできます。",[51,4374,4376],{"className":726,"code":4375,"language":728,"meta":56,"style":56},"const obj = {\n  name: 'Yuri',\n  hasOwnProperty() {\n    console.log('Hello')\n  },\n}\n\nObject.prototype.hasOwnProperty.call(obj, 'name') \u002F\u002F true\n",[58,4377,4378,4382,4386,4390,4394,4398,4402,4406],{"__ignoreMap":56},[61,4379,4380],{"class":63,"line":64},[61,4381,4131],{},[61,4383,4384],{"class":63,"line":81},[61,4385,937],{},[61,4387,4388],{"class":63,"line":94},[61,4389,4201],{},[61,4391,4392],{"class":63,"line":105},[61,4393,4206],{},[61,4395,4396],{"class":63,"line":305},[61,4397,812],{},[61,4399,4400],{"class":63,"line":757},[61,4401,750],{},[61,4403,4404],{"class":63,"line":763},[61,4405,287],{"emptyLinePlaceholder":286},[61,4407,4408],{"class":63,"line":768},[61,4409,4410],{},"Object.prototype.hasOwnProperty.call(obj, 'name') \u002F\u002F true\n",[11,4412,4413,4416,4417,4420],{},[58,4414,4415],{},"call()"," の第1引数に ",[58,4418,4419],{},"this"," として扱うオブジェクトを指定し、それ以降の引数を元のメソッドへ渡しています。",[11,4422,4423,4424,4426,4427,4429,4430,4432,4433,4435,4436,4438],{},"この例では ",[58,4425,4158],{}," が持つ本来の ",[58,4428,4120],{}," を直接取得し、",[58,4431,4154],{}," を ",[58,4434,4419],{}," として呼び出しています。そのため、",[58,4437,4154],{}," 自身に定義された同名メソッドの影響を受けません。",[25,4440,636],{"id":636},[644,4442,4443,4449,4452,4459,4466],{},[647,4444,4445,4446,4448],{},"オブジェクトリテラルは通常、",[58,4447,4158],{}," のメソッドを継承する",[647,4450,4451],{},"オブジェクト自身が同名メソッドを持つと、継承したメソッドは隠される",[647,4453,4454,4249,4456,4458],{},[58,4455,4248],{},[58,4457,4120],{}," を継承しない",[647,4460,4461,4462,4465],{},"自身のプロパティを確認する場合は、基本的に ",[58,4463,4464],{},"Object.hasOwn(obj, property)"," を使う",[647,4467,4468,4469,4472],{},"古い実行環境も考慮する場合は、",[58,4470,4471],{},"Object.prototype.hasOwnProperty.call(obj, property)"," を使える",[11,4474,4475],{},"オブジェクトの出所や構造を完全には把握できない場合も、インスタンスの同名メソッドに依存しない呼び出し方を選ぶと安全です。",[679,4477,1395],{},{"title":56,"searchDepth":81,"depth":81,"links":4479},[4480,4481,4482,4483,4484,4485],{"id":4082,"depth":81,"text":4083},{"id":4110,"depth":81,"text":4111},{"id":4176,"depth":81,"text":4176},{"id":4282,"depth":81,"text":4283},{"id":4363,"depth":81,"text":4363},{"id":636,"depth":81,"text":636},"JavaScriptでオブジェクト自身のプロパティを安全に確認する方法を、hasOwnPropertyとObject.hasOwnの違いから整理します。",{},"\u002Farticles\u002F2024\u002Fhow-to-use-javascript-built-in-objects","2024-11-24",{"title":4069,"description":4486},"articles\u002F2024\u002Fhow-to-use-javascript-built-in-objects",[1420],"\u002Fimages\u002Farticles\u002Fhow-to-use-javascript-built-in-objects.png","nujvvO9Kj5xXhtbuHUxO1l6JeNtgWjuj3S4rljZKkdU",{"id":4496,"title":4497,"body":4498,"description":4878,"extension":696,"meta":4879,"navigation":286,"path":4880,"publishDate":4881,"published":286,"seo":4882,"stem":4883,"tags":4884,"thumbnail":4885,"updated":4881,"__hash__":4886},"articles\u002Farticles\u002F2024\u002Fdescribe-javascript-deepcopy.md","JavaScriptのディープコピーとは？",{"type":8,"value":4499,"toc":4871},[4500,4502,4505,4508,4512,4515,4518,4527,4530,4537,4588,4602,4617,4620,4644,4647,4656,4660,4667,4713,4718,4733,4736,4760,4775,4782,4791,4795,4798,4807,4810,4835,4841,4843,4866,4869],[11,4501,714],{},[11,4503,4504],{},"今回は、JavaScriptにおけるオブジェクトのディープコピーについて紹介します。",[11,4506,4507],{},"シャローコピーとの違いや実装方法を理解しておくと、コピー先の変更が意図せずコピー元へ影響する問題を避けられます。",[25,4509,4511],{"id":4510},"ディープコピーとは","ディープコピーとは？",[11,4513,4514],{},"ディープコピーとは、コピー元とコピー先が、内部にあるオブジェクトの参照を共有しないコピーのことです。",[11,4516,4517],{},"たとえば、入れ子になったオブジェクトをコピーしたあと、コピー先の内側にある値を変更しても、コピー元の値には影響しません。",[11,4519,4520,4521,4526],{},"詳しい定義は、",[15,4522,4525],{"href":4523,"rel":4524},"https:\u002F\u002Fdeveloper.mozilla.org\u002Fja\u002Fdocs\u002FGlossary\u002FDeep_copy",[19],"MDNのディープコピー","も参照してください。",[25,4528,4529],{"id":4529},"シャローコピーでは参照が共有される",[11,4531,4532,4533,4536],{},"まずは、",[58,4534,4535],{},"Object.assign()"," を使ったシャローコピーを確認します。",[51,4538,4540],{"className":726,"code":4539,"language":728,"meta":56,"style":56},"const original = {\n  user: {\n    name: 'Yuri',\n  },\n  settings: {\n    theme: 'light',\n  },\n}\n\nconst shallowCopy = Object.assign({}, original)\n",[58,4541,4542,4547,4552,4557,4561,4566,4571,4575,4579,4583],{"__ignoreMap":56},[61,4543,4544],{"class":63,"line":64},[61,4545,4546],{},"const original = {\n",[61,4548,4549],{"class":63,"line":81},[61,4550,4551],{},"  user: {\n",[61,4553,4554],{"class":63,"line":94},[61,4555,4556],{},"    name: 'Yuri',\n",[61,4558,4559],{"class":63,"line":105},[61,4560,812],{},[61,4562,4563],{"class":63,"line":305},[61,4564,4565],{},"  settings: {\n",[61,4567,4568],{"class":63,"line":757},[61,4569,4570],{},"    theme: 'light',\n",[61,4572,4573],{"class":63,"line":763},[61,4574,812],{},[61,4576,4577],{"class":63,"line":768},[61,4578,750],{},[61,4580,4581],{"class":63,"line":774},[61,4582,287],{"emptyLinePlaceholder":286},[61,4584,4585],{"class":63,"line":833},[61,4586,4587],{},"const shallowCopy = Object.assign({}, original)\n",[11,4589,4590,4593,4594,4597,4598,4601],{},[58,4591,4592],{},"Object.assign({}, original)"," で新しい外側のオブジェクトは作られますが、",[58,4595,4596],{},"user"," や ",[58,4599,4600],{},"settings"," はコピー元と同じオブジェクトを参照しています。",[51,4603,4605],{"className":726,"code":4604,"language":728,"meta":56,"style":56},"console.log(original === shallowCopy) \u002F\u002F false\nconsole.log(original.user === shallowCopy.user) \u002F\u002F true\n",[58,4606,4607,4612],{"__ignoreMap":56},[61,4608,4609],{"class":63,"line":64},[61,4610,4611],{},"console.log(original === shallowCopy) \u002F\u002F false\n",[61,4613,4614],{"class":63,"line":81},[61,4615,4616],{},"console.log(original.user === shallowCopy.user) \u002F\u002F true\n",[11,4618,4619],{},"そのため、コピー先にある入れ子の値を変更すると、コピー元にも影響します。",[51,4621,4623],{"className":726,"code":4622,"language":728,"meta":56,"style":56},"shallowCopy.user.name = 'Kohei'\n\nconsole.log(original.user.name) \u002F\u002F 「Kohei」\nconsole.log(shallowCopy.user.name) \u002F\u002F 「Kohei」\n",[58,4624,4625,4630,4634,4639],{"__ignoreMap":56},[61,4626,4627],{"class":63,"line":64},[61,4628,4629],{},"shallowCopy.user.name = 'Kohei'\n",[61,4631,4632],{"class":63,"line":81},[61,4633,287],{"emptyLinePlaceholder":286},[61,4635,4636],{"class":63,"line":94},[61,4637,4638],{},"console.log(original.user.name) \u002F\u002F 「Kohei」\n",[61,4640,4641],{"class":63,"line":105},[61,4642,4643],{},"console.log(shallowCopy.user.name) \u002F\u002F 「Kohei」\n",[11,4645,4646],{},"オブジェクトスプレッド構文も、同様にシャローコピーです。",[51,4648,4650],{"className":726,"code":4649,"language":728,"meta":56,"style":56},"const shallowCopy = { ...original }\n",[58,4651,4652],{"__ignoreMap":56},[61,4653,4654],{"class":63,"line":64},[61,4655,4649],{},[25,4657,4659],{"id":4658},"structuredcloneでディープコピーする","structuredCloneでディープコピーする",[11,4661,4662,4663,4666],{},"ディープコピーには、",[58,4664,4665],{},"structuredClone()"," を利用できます。",[51,4668,4670],{"className":726,"code":4669,"language":728,"meta":56,"style":56},"const original = {\n  user: {\n    name: 'Yuri',\n  },\n  settings: {\n    theme: 'light',\n  },\n}\n\nconst deepCopy = structuredClone(original)\n",[58,4671,4672,4676,4680,4684,4688,4692,4696,4700,4704,4708],{"__ignoreMap":56},[61,4673,4674],{"class":63,"line":64},[61,4675,4546],{},[61,4677,4678],{"class":63,"line":81},[61,4679,4551],{},[61,4681,4682],{"class":63,"line":94},[61,4683,4556],{},[61,4685,4686],{"class":63,"line":105},[61,4687,812],{},[61,4689,4690],{"class":63,"line":305},[61,4691,4565],{},[61,4693,4694],{"class":63,"line":757},[61,4695,4570],{},[61,4697,4698],{"class":63,"line":763},[61,4699,812],{},[61,4701,4702],{"class":63,"line":768},[61,4703,750],{},[61,4705,4706],{"class":63,"line":774},[61,4707,287],{"emptyLinePlaceholder":286},[61,4709,4710],{"class":63,"line":833},[61,4711,4712],{},"const deepCopy = structuredClone(original)\n",[11,4714,4715,4717],{},[58,4716,4665],{}," で作ったコピーは、入れ子のオブジェクトもコピー元とは異なる参照になります。",[51,4719,4721],{"className":726,"code":4720,"language":728,"meta":56,"style":56},"console.log(original === deepCopy) \u002F\u002F false\nconsole.log(original.user === deepCopy.user) \u002F\u002F false\n",[58,4722,4723,4728],{"__ignoreMap":56},[61,4724,4725],{"class":63,"line":64},[61,4726,4727],{},"console.log(original === deepCopy) \u002F\u002F false\n",[61,4729,4730],{"class":63,"line":81},[61,4731,4732],{},"console.log(original.user === deepCopy.user) \u002F\u002F false\n",[11,4734,4735],{},"コピー先を変更しても、コピー元には影響しません。",[51,4737,4739],{"className":726,"code":4738,"language":728,"meta":56,"style":56},"deepCopy.user.name = 'Kohei'\n\nconsole.log(original.user.name) \u002F\u002F 「Yuri」\nconsole.log(deepCopy.user.name) \u002F\u002F 「Kohei」\n",[58,4740,4741,4746,4750,4755],{"__ignoreMap":56},[61,4742,4743],{"class":63,"line":64},[61,4744,4745],{},"deepCopy.user.name = 'Kohei'\n",[61,4747,4748],{"class":63,"line":81},[61,4749,287],{"emptyLinePlaceholder":286},[61,4751,4752],{"class":63,"line":94},[61,4753,4754],{},"console.log(original.user.name) \u002F\u002F 「Yuri」\n",[61,4756,4757],{"class":63,"line":105},[61,4758,4759],{},"console.log(deepCopy.user.name) \u002F\u002F 「Kohei」\n",[11,4761,4762,4764,4765,118,4768,118,4771,4774],{},[58,4763,4665],{}," は通常のオブジェクトや配列だけでなく、",[58,4766,4767],{},"Date",[58,4769,4770],{},"Map",[58,4772,4773],{},"Set"," など、構造化複製アルゴリズムが対応している型をコピーできます。また、循環参照を持つオブジェクトにも対応しています。",[11,4776,4777,4778,4781],{},"ただし、関数など構造化複製できない値が含まれている場合は ",[58,4779,4780],{},"DataCloneError"," が発生します。すべてのJavaScriptの値を無条件にコピーできるわけではありません。",[11,4783,4784,4785,4790],{},"詳しい対応範囲は、",[15,4786,4789],{"href":4787,"rel":4788},"https:\u002F\u002Fdeveloper.mozilla.org\u002Fja\u002Fdocs\u002FWeb\u002FAPI\u002FWindow\u002FstructuredClone",[19],"MDNのstructuredClone()","を確認してください。",[25,4792,4794],{"id":4793},"jsonを使ったディープコピーの注意点","JSONを使ったディープコピーの注意点",[11,4796,4797],{},"単純なデータだけを扱う場合、JSONへ変換して戻す方法もあります。",[51,4799,4801],{"className":726,"code":4800,"language":728,"meta":56,"style":56},"const deepCopy = JSON.parse(JSON.stringify(original))\n",[58,4802,4803],{"__ignoreMap":56},[61,4804,4805],{"class":63,"line":64},[61,4806,4800],{},[11,4808,4809],{},"ただし、この方法はJSONとして表現できる値に限られます。",[644,4811,4812,4817,4826,4832],{},[647,4813,4814,4816],{},[58,4815,4767],{}," は文字列へ変換される",[647,4818,4819,4821,4822,4825],{},[58,4820,986],{},"、関数、",[58,4823,4824],{},"Symbol"," などは正しく保持できない",[647,4827,4828,4831],{},[58,4829,4830],{},"BigInt"," を含むと例外が発生する",[647,4833,4834],{},"循環参照を持つオブジェクトは変換できない",[11,4836,4837,4838,4840],{},"対象がJSON互換のデータだと分かっている場合には使えますが、一般的なディープコピーとしては ",[58,4839,4665],{}," の方が意図を表しやすく、扱える型も多くなります。",[25,4842,636],{"id":636},[644,4844,4845,4850,4853,4858,4863],{},[647,4846,4847,4849],{},[58,4848,4535],{}," とオブジェクトスプレッド構文はシャローコピーになる",[647,4851,4852],{},"シャローコピーでは、入れ子のオブジェクトの参照がコピー元と共有される",[647,4854,4855,4857],{},[58,4856,4665],{}," を使うと、対応している値をディープコピーできる",[647,4859,4860,4862],{},[58,4861,4665],{}," でも、関数などコピーできない値がある",[647,4864,4865],{},"JSONによるコピーは、JSON互換データだけに限定して使う",[11,4867,4868],{},"コピー方法を選ぶときは、対象のデータ構造と、コピー後にどの階層まで独立して変更したいかを確認することが大切です。",[679,4870,1395],{},{"title":56,"searchDepth":81,"depth":81,"links":4872},[4873,4874,4875,4876,4877],{"id":4510,"depth":81,"text":4511},{"id":4529,"depth":81,"text":4529},{"id":4658,"depth":81,"text":4659},{"id":4793,"depth":81,"text":4794},{"id":636,"depth":81,"text":636},"JavaScriptのシャローコピーとディープコピーの違いを確認し、structuredCloneを使った実装方法と注意点を整理します。",{},"\u002Farticles\u002F2024\u002Fdescribe-javascript-deepcopy","2024-11-23",{"title":4497,"description":4878},"articles\u002F2024\u002Fdescribe-javascript-deepcopy",[1420],"\u002Fimages\u002Farticles\u002Fdescribe-javascript-deepcopy.png","dtSQ5LrS1Kfo1hFYE2MzMKOvrhU_5JGE1X1eQ9eHhIY",{"id":4888,"title":4889,"body":4890,"description":5304,"extension":696,"meta":5305,"navigation":286,"path":5306,"publishDate":5307,"published":286,"seo":5308,"stem":5309,"tags":5310,"thumbnail":5311,"updated":5312,"__hash__":5313},"articles\u002Farticles\u002F2024\u002Fdescribe-behavior-javascript-this.md","JavaScriptのthisの挙動について呼び出し元で整理する",{"type":8,"value":4891,"toc":5296},[4892,4894,4900,4904,4909,4915,4918,4921,4927,4935,4937,4943,4949,4958,4961,5001,5008,5027,5029,5035,5055,5058,5095,5107,5128,5134,5137,5140,5182,5200,5210,5224,5234,5249,5251,5256,5289,5294],[11,4893,714],{},[11,4895,4896,4897,4899],{},"今回は、プロジェクトや個人開発で振り回されることが多かったJavaScriptの ",[58,4898,4419],{}," の挙動について、あらためて振り返ります。自分をはじめ、初学者の方も知っておくと開発が楽になるかと思います。",[25,4901,4903],{"id":4902},"そもそもthisってなんだ","そもそも「this」ってなんだ？",[11,4905,4906,4908],{},[58,4907,4419],{}," は、関数が実行されたときのコンテキストを参照するためのキーワードです。",[11,4910,4911,4912,4914],{},"同じ関数であっても、どのように呼び出されたかによって ",[58,4913,4419],{}," が参照する値は変わります。この特徴が便利なところでもあり、難しいところでもあります。",[11,4916,4917],{},"それでは、呼び出し方によって参照先がどう変わるのか見てみましょう。",[25,4919,4920],{"id":4920},"使用する場所ごとのthis",[11,4922,4923,4924,4926],{},"この記事では、",[58,4925,4419],{}," を使用するケースを次の2つに分けて確認します。",[644,4928,4929,4932],{},[647,4930,4931],{},"オブジェクトのメソッドとして呼び出す場合",[647,4933,4934],{},"通常の関数として呼び出す場合",[25,4936,4931],{"id":4931},[11,4938,4939,4940,4942],{},"オブジェクトのメソッドとして呼び出した場合、",[58,4941,4419],{}," は基本的に呼び出し元のオブジェクトを参照します。",[11,4944,4945,4946,4948],{},"ここでいう呼び出し元のオブジェクトとは、次の例では ",[58,4947,4154],{}," のことです。",[51,4950,4952],{"className":726,"code":4951,"language":728,"meta":56,"style":56},"const obj = {}\n",[58,4953,4954],{"__ignoreMap":56},[61,4955,4956],{"class":63,"line":64},[61,4957,4951],{},[11,4959,4960],{},"実際に挙動を見てみましょう。",[51,4962,4964],{"className":726,"code":4963,"language":728,"meta":56,"style":56},"const obj = {\n  name: 'Yuri',\n  message: function () {\n    console.log('Hello ' + this.name)\n  },\n}\n\nobj.message()\n",[58,4965,4966,4970,4974,4979,4984,4988,4992,4996],{"__ignoreMap":56},[61,4967,4968],{"class":63,"line":64},[61,4969,4131],{},[61,4971,4972],{"class":63,"line":81},[61,4973,937],{},[61,4975,4976],{"class":63,"line":94},[61,4977,4978],{},"  message: function () {\n",[61,4980,4981],{"class":63,"line":105},[61,4982,4983],{},"    console.log('Hello ' + this.name)\n",[61,4985,4986],{"class":63,"line":305},[61,4987,812],{},[61,4989,4990],{"class":63,"line":757},[61,4991,750],{},[61,4993,4994],{"class":63,"line":763},[61,4995,287],{"emptyLinePlaceholder":286},[61,4997,4998],{"class":63,"line":768},[61,4999,5000],{},"obj.message()\n",[11,5002,5003,5004,5007],{},"この例では、",[58,5005,5006],{},"Hello Yuri"," と出力されます。",[11,5009,5010,5013,5014,5016,5017,5019,5020,5016,5023,5026],{},[58,5011,5012],{},"obj.message()"," という形で呼び出しているため、メソッド内の ",[58,5015,4419],{}," は ",[58,5018,4154],{}," を参照します。したがって、",[58,5021,5022],{},"this.name",[58,5024,5025],{},"obj.name"," として評価されます。",[25,5028,4934],{"id":4934},[11,5030,5031,5032,5034],{},"通常の関数として呼び出した場合、",[58,5033,4419],{}," の値は実行モードによって異なります。",[644,5036,5037,5047],{},[647,5038,5039,5040,5042,5043,5046],{},"非厳格モードのブラウザでは、",[58,5041,4419],{}," はグローバルオブジェクトの ",[58,5044,5045],{},"window"," を参照する",[647,5048,5049,5050,5016,5052,5054],{},"厳格モードやES Modulesでは、",[58,5051,4419],{},[58,5053,986],{}," になる",[11,5056,5057],{},"次のコードは、ブラウザの非厳格モードで実行する例です。",[51,5059,5061],{"className":726,"code":5060,"language":728,"meta":56,"style":56},"window.name = 'Yuri'\n\nfunction message() {\n  console.log('Hello ' + this.name)\n}\n\nmessage()\n",[58,5062,5063,5068,5072,5077,5082,5086,5090],{"__ignoreMap":56},[61,5064,5065],{"class":63,"line":64},[61,5066,5067],{},"window.name = 'Yuri'\n",[61,5069,5070],{"class":63,"line":81},[61,5071,287],{"emptyLinePlaceholder":286},[61,5073,5074],{"class":63,"line":94},[61,5075,5076],{},"function message() {\n",[61,5078,5079],{"class":63,"line":105},[61,5080,5081],{},"  console.log('Hello ' + this.name)\n",[61,5083,5084],{"class":63,"line":305},[61,5085,750],{},[61,5087,5088],{"class":63,"line":757},[61,5089,287],{"emptyLinePlaceholder":286},[61,5091,5092],{"class":63,"line":763},[61,5093,5094],{},"message()\n",[11,5096,5097,5098,5100,5101,5104,5105,5007],{},"非厳格モードでは ",[58,5099,5022],{}," が ",[58,5102,5103],{},"window.name"," として評価されるため、",[58,5106,5006],{},[5108,5109,5110],"blockquote",{},[11,5111,5112,4597,5115,5117,5118,5120,5121,5124,5125,5127],{},[58,5113,5114],{},"let",[58,5116,1682],{}," で宣言した変数は、",[58,5119,5045],{}," オブジェクトのプロパティとして追加されません。一方、ブラウザでトップレベルに ",[58,5122,5123],{},"var"," を使って宣言した変数は、",[58,5126,5045],{}," のプロパティになります。",[11,5129,5130,5131,5133],{},"現在のフロントエンド開発ではES Modulesが広く使われています。ES Modulesは自動的に厳格モードとして扱われるため、通常の関数呼び出しで ",[58,5132,4419],{}," に依存する実装は避けた方が安全です。",[25,5135,5136],{"id":5136},"オブジェクトのメソッドを変数へ代入した場合",[11,5138,5139],{},"ここまでを踏まえて、オブジェクトのメソッドを変数へ代入した場合の挙動を見てみましょう。",[51,5141,5143],{"className":726,"code":5142,"language":728,"meta":56,"style":56},"const obj = {\n  name: 'Yuri',\n  message: function () {\n    console.log('Hello ' + this.name)\n  },\n}\n\nconst message = obj.message\nmessage()\n",[58,5144,5145,5149,5153,5157,5161,5165,5169,5173,5178],{"__ignoreMap":56},[61,5146,5147],{"class":63,"line":64},[61,5148,4131],{},[61,5150,5151],{"class":63,"line":81},[61,5152,937],{},[61,5154,5155],{"class":63,"line":94},[61,5156,4978],{},[61,5158,5159],{"class":63,"line":105},[61,5160,4983],{},[61,5162,5163],{"class":63,"line":305},[61,5164,812],{},[61,5166,5167],{"class":63,"line":757},[61,5168,750],{},[61,5170,5171],{"class":63,"line":763},[61,5172,287],{"emptyLinePlaceholder":286},[61,5174,5175],{"class":63,"line":768},[61,5176,5177],{},"const message = obj.message\n",[61,5179,5180],{"class":63,"line":774},[61,5181,5094],{},[11,5183,5184,5187,5188,5190,5191,5194,5195,3998,5197,5199],{},[58,5185,5186],{},"const message = obj.message"," では、関数そのものを変数へ代入しています。その後は ",[58,5189,5012],{}," ではなく ",[58,5192,5193],{},"message()"," として呼び出しているため、",[58,5196,4419],{},[58,5198,4154],{}," の結び付きは失われます。",[11,5201,5097,5202,5100,5204,5206,5207,5209],{},[58,5203,4419],{},[58,5205,5045],{}," を参照します。",[58,5208,5103],{}," が設定されていなければ、実行環境に応じて空文字などが返ります。",[11,5211,5212,5213,5100,5215,5217,5218,5220,5221,5223],{},"厳格モードやES Modulesでは ",[58,5214,4419],{},[58,5216,986],{}," になるため、",[58,5219,5022],{}," を読み取ろうとすると ",[58,5222,1165],{}," が発生します。",[11,5225,5226,5227,5230,5231,5233],{},"このような場合は、",[58,5228,5229],{},"bind()"," を使って ",[58,5232,4419],{}," の参照先を固定できます。",[51,5235,5237],{"className":726,"code":5236,"language":728,"meta":56,"style":56},"const message = obj.message.bind(obj)\nmessage() \u002F\u002F 「Hello Yuri」と出力される\n",[58,5238,5239,5244],{"__ignoreMap":56},[61,5240,5241],{"class":63,"line":64},[61,5242,5243],{},"const message = obj.message.bind(obj)\n",[61,5245,5246],{"class":63,"line":81},[61,5247,5248],{},"message() \u002F\u002F 「Hello Yuri」と出力される\n",[25,5250,636],{"id":636},[11,5252,5253,5255],{},[58,5254,4419],{}," の参照先は、関数を定義した場所だけではなく、主に関数の呼び出し方によって決まります。",[644,5257,5258,5267,5277,5280],{},[647,5259,5260,5262,5263,5016,5265,5046],{},[58,5261,5012],{}," のようなメソッド呼び出しでは、",[58,5264,4419],{},[58,5266,4154],{},[647,5268,5269,5271,5272,5274,5275,5054],{},[58,5270,5193],{}," のような通常の関数呼び出しでは、非厳格モードのブラウザなら ",[58,5273,5045],{},"、厳格モードやES Modulesなら ",[58,5276,986],{},[647,5278,5279],{},"メソッドを変数へ代入すると、元のオブジェクトとの結び付きは失われる",[647,5281,5282,5283,5285,5286,5288],{},"元のオブジェクトを参照させたい場合は、",[58,5284,5229],{}," で ",[58,5287,4419],{}," を固定できる",[11,5290,5291,5293],{},[58,5292,4419],{}," で迷ったときは「この関数はどのような形で呼び出されているか」を確認すると、参照先を判断しやすくなります。",[679,5295,1395],{},{"title":56,"searchDepth":81,"depth":81,"links":5297},[5298,5299,5300,5301,5302,5303],{"id":4902,"depth":81,"text":4903},{"id":4920,"depth":81,"text":4920},{"id":4931,"depth":81,"text":4931},{"id":4934,"depth":81,"text":4934},{"id":5136,"depth":81,"text":5136},{"id":636,"depth":81,"text":636},"JavaScriptのthisが何を参照するのか、メソッド呼び出しと関数呼び出しの違いから整理します。",{},"\u002Farticles\u002F2024\u002Fdescribe-behavior-javascript-this","2024-11-20",{"title":4889,"description":5304},"articles\u002F2024\u002Fdescribe-behavior-javascript-this",[1420],"\u002Fimages\u002Farticles\u002Fdescribe-behavior-javascript-this.png","2025-03-21","Kay_1TeIK8YsouKEwYLRPzbQMnabHxZ_GWvHciKSZBs",[5315],{"id":4,"title":5,"author":6,"body":5316,"description":695,"extension":696,"meta":5805,"navigation":286,"path":698,"publishDate":699,"published":286,"publisher":700,"seo":5806,"stem":702,"tags":5807,"__hash__":705},{"type":8,"value":5317,"toc":5793},[5318,5323,5325,5327,5329,5331,5335,5337,5339,5379,5389,5391,5411,5413,5415,5419,5439,5445,5449,5453,5458,5466,5470,5474,5478,5518,5522,5546,5548,5552,5584,5588,5590,5592,5636,5638,5642,5654,5656,5670,5676,5696,5698,5702,5714,5720,5736,5744,5746,5748,5752,5757,5759,5761,5763,5765,5767,5789,5791],[11,5319,13,5320,20],{},[15,5321,5],{"href":17,"rel":5322},[19],[11,5324,23],{},[25,5326,28],{"id":27},[11,5328,31],{},[11,5330,34],{},[36,5332,5333],{"title":38,"type":39},[11,5334,42],{},[25,5336,46],{"id":45},[11,5338,49],{},[51,5340,5341],{"className":53,"code":54,"language":55,"meta":56,"style":56},[58,5342,5343,5353,5363,5371],{"__ignoreMap":56},[61,5344,5345,5347,5349,5351],{"class":63,"line":64},[61,5346,68],{"class":67},[61,5348,72],{"class":71},[61,5350,75],{"class":71},[61,5352,78],{"class":71},[61,5354,5355,5357,5359,5361],{"class":63,"line":81},[61,5356,68],{"class":67},[61,5358,86],{"class":71},[61,5360,89],{"class":71},[61,5362,78],{"class":71},[61,5364,5365,5367,5369],{"class":63,"line":94},[61,5366,68],{"class":67},[61,5368,99],{"class":71},[61,5370,102],{"class":71},[61,5372,5373,5375,5377],{"class":63,"line":105},[61,5374,68],{"class":67},[61,5376,110],{"class":71},[61,5378,102],{"class":71},[11,5380,5381,118,5383,118,5385,118,5387,128],{},[58,5382,117],{},[58,5384,121],{},[58,5386,124],{},[58,5388,127],{},[11,5390,131],{},[51,5392,5393],{"className":53,"code":134,"language":55,"meta":56,"style":56},[58,5394,5395,5403],{"__ignoreMap":56},[61,5396,5397,5399,5401],{"class":63,"line":64},[61,5398,68],{"class":67},[61,5400,75],{"class":71},[61,5402,78],{"class":71},[61,5404,5405,5407,5409],{"class":63,"line":81},[61,5406,68],{"class":67},[61,5408,89],{"class":71},[61,5410,78],{"class":71},[11,5412,155],{},[25,5414,159],{"id":158},[11,5416,162,5417,166],{},[58,5418,165],{},[51,5420,5421],{"className":53,"code":169,"language":55,"meta":56,"style":56},[58,5422,5423,5431],{"__ignoreMap":56},[61,5424,5425,5427,5429],{"class":63,"line":64},[61,5426,68],{"class":67},[61,5428,178],{"class":71},[61,5430,181],{"class":71},[61,5432,5433,5435,5437],{"class":63,"line":81},[61,5434,68],{"class":67},[61,5436,178],{"class":71},[61,5438,190],{"class":71},[11,5440,193,5441,197,5443,200],{},[58,5442,196],{},[58,5444,165],{},[25,5446,204,5447,208],{"id":203},[58,5448,207],{},[11,5450,211,5451,214],{},[58,5452,207],{},[51,5454,5456],{"className":5455,"code":219,"language":220,"meta":56},[218],[58,5457,219],{"__ignoreMap":56},[11,5459,5460,228,5462,232,5464,236],{},[58,5461,227],{},[58,5463,231],{},[58,5465,235],{},[11,5467,239,5468,242],{},[58,5469,207],{},[25,5471,246,5472,250],{"id":245},[58,5473,249],{},[11,5475,253,5476,256],{},[58,5477,249],{},[51,5479,5480],{"className":259,"code":260,"language":261,"meta":56,"style":56},[58,5481,5482,5494,5498,5504,5510],{"__ignoreMap":56},[61,5483,5484,5486,5488,5490,5492],{"class":63,"line":64},[61,5485,249],{"class":268},[61,5487,272],{"class":271},[61,5489,275],{"class":271},[61,5491,278],{"class":71},[61,5493,281],{"class":271},[61,5495,5496],{"class":63,"line":81},[61,5497,287],{"emptyLinePlaceholder":286},[61,5499,5500,5502],{"class":63,"line":94},[61,5501,292],{"class":268},[61,5503,295],{"class":271},[61,5505,5506,5508],{"class":63,"line":105},[61,5507,300],{"class":268},[61,5509,295],{"class":271},[61,5511,5512,5514,5516],{"class":63,"line":305},[61,5513,308],{"class":268},[61,5515,272],{"class":271},[61,5517,78],{"class":71},[11,5519,315,5520,318],{},[58,5521,249],{},[51,5523,5524],{"className":259,"code":321,"language":261,"meta":56,"style":56},[58,5525,5526,5532,5538],{"__ignoreMap":56},[61,5527,5528,5530],{"class":63,"line":64},[61,5529,292],{"class":268},[61,5531,295],{"class":271},[61,5533,5534,5536],{"class":63,"line":81},[61,5535,300],{"class":268},[61,5537,295],{"class":271},[61,5539,5540,5542,5544],{"class":63,"line":94},[61,5541,308],{"class":268},[61,5543,272],{"class":271},[61,5545,78],{"class":71},[25,5547,347],{"id":346},[11,5549,350,5550,354],{},[58,5551,353],{},[51,5553,5554],{"className":259,"code":357,"language":261,"meta":56,"style":56},[58,5555,5556,5562,5568,5576],{"__ignoreMap":56},[61,5557,5558,5560],{"class":63,"line":64},[61,5559,292],{"class":268},[61,5561,295],{"class":271},[61,5563,5564,5566],{"class":63,"line":81},[61,5565,300],{"class":268},[61,5567,295],{"class":271},[61,5569,5570,5572,5574],{"class":63,"line":94},[61,5571,308],{"class":268},[61,5573,272],{"class":271},[61,5575,78],{"class":71},[61,5577,5578,5580,5582],{"class":63,"line":105},[61,5579,384],{"class":268},[61,5581,272],{"class":271},[61,5583,389],{"class":71},[11,5585,5586,395],{},[58,5587,394],{},[11,5589,398],{},[11,5591,401],{},[403,5593,5594,5602],{},[406,5595,5596],{},[409,5597,5598,5600],{},[412,5599,414],{},[412,5601,417],{},[419,5603,5604,5612,5620,5628],{},[409,5605,5606,5610],{},[424,5607,5608],{},[58,5609,428],{},[424,5611,431],{},[409,5613,5614,5618],{},[424,5615,5616],{},[58,5617,438],{},[424,5619,441],{},[409,5621,5622,5626],{},[424,5623,5624],{},[58,5625,394],{},[424,5627,450],{},[409,5629,5630,5634],{},[424,5631,5632],{},[58,5633,457],{},[424,5635,460],{},[25,5637,463],{"id":463},[11,5639,466,5640,470],{},[58,5641,469],{},[51,5643,5644],{"className":53,"code":473,"language":55,"meta":56,"style":56},[58,5645,5646],{"__ignoreMap":56},[61,5647,5648,5650,5652],{"class":63,"line":64},[61,5649,68],{"class":67},[61,5651,178],{"class":71},[61,5653,484],{"class":71},[11,5655,487],{},[51,5657,5658],{"className":53,"code":490,"language":55,"meta":56,"style":56},[58,5659,5660],{"__ignoreMap":56},[61,5661,5662,5664,5666,5668],{"class":63,"line":64},[61,5663,68],{"class":67},[61,5665,178],{"class":71},[61,5667,501],{"class":71},[61,5669,504],{"class":71},[11,5671,507,5672,511,5674,166],{},[58,5673,510],{},[58,5675,514],{},[51,5677,5678],{"className":53,"code":517,"language":55,"meta":56,"style":56},[58,5679,5680],{"__ignoreMap":56},[61,5681,5682,5684,5686,5688,5690,5692,5694],{"class":63,"line":64},[61,5683,68],{"class":67},[61,5685,178],{"class":71},[61,5687,501],{"class":71},[61,5689,530],{"class":71},[61,5691,533],{"class":71},[61,5693,537],{"class":536},[61,5695,504],{"class":71},[25,5697,542],{"id":542},[11,5699,545,5700,549],{},[58,5701,548],{},[51,5703,5704],{"className":53,"code":552,"language":55,"meta":56,"style":56},[58,5705,5706],{"__ignoreMap":56},[61,5707,5708,5710,5712],{"class":63,"line":64},[61,5709,68],{"class":67},[61,5711,178],{"class":71},[61,5713,563],{"class":71},[11,5715,566,5716,570,5718,574],{},[58,5717,569],{},[58,5719,573],{},[51,5721,5722],{"className":53,"code":577,"language":55,"meta":56,"style":56},[58,5723,5724],{"__ignoreMap":56},[61,5725,5726,5728,5730,5732,5734],{"class":63,"line":64},[61,5727,68],{"class":67},[61,5729,178],{"class":71},[61,5731,588],{"class":71},[61,5733,591],{"class":71},[61,5735,594],{"class":71},[11,5737,5738,599,5740,603,5742,607],{},[58,5739,569],{},[58,5741,602],{},[58,5743,606],{},[25,5745,611],{"id":610},[11,5747,614],{},[11,5749,617,5750,621],{},[58,5751,620],{},[51,5753,5755],{"className":5754,"code":625,"language":220,"meta":56},[218],[58,5756,625],{"__ignoreMap":56},[11,5758,630],{},[11,5760,633],{},[25,5762,636],{"id":636},[11,5764,639],{},[11,5766,642],{},[644,5768,5769,5771,5775,5779,5783,5785,5787],{},[647,5770,649],{},[647,5772,652,5773,208],{},[58,5774,165],{},[647,5776,204,5777,659],{},[58,5778,207],{},[647,5780,662,5781,665],{},[58,5782,249],{},[647,5784,668],{},[647,5786,671],{},[647,5788,674],{},[11,5790,677],{},[679,5792,681],{},{"title":56,"searchDepth":81,"depth":81,"links":5794},[5795,5796,5797,5798,5799,5800,5801,5802,5803,5804],{"id":27,"depth":81,"text":28},{"id":45,"depth":81,"text":46},{"id":158,"depth":81,"text":159},{"id":203,"depth":81,"text":688},{"id":245,"depth":81,"text":245},{"id":346,"depth":81,"text":347},{"id":463,"depth":81,"text":463},{"id":542,"depth":81,"text":542},{"id":610,"depth":81,"text":611},{"id":636,"depth":81,"text":636},{},{"title":5,"description":695},[704],1786277109318]