十二天前我完全沒寫過 JavaScript。今天回頭看,已經有一個能按按鈕、會扣血、會宣布勝負的網頁了。這篇不學新東西,把前十一天整理一遍,順便誠實記錄哪裡最卡。
| Day | 主題 | 關鍵收穫 |
|---|---|---|
| 1 | 環境建置 | VS Code + Live Server,console.log 印在主控台不是網頁上 |
| 2 | 變數與型別 | let/const、SyntaxError 會擋掉整支程式、typeof null 是 "object" |
| 3 | 字串 | 模板字串 `${}` 比 + 拼接好用太多 |
| 4 | 條件判斷 | === 不是 ==(0 == false 竟然是 true)、邊界值要主動測 |
| 5 | 陣列 | 索引從 0 開始是「位移量」、length 本身是第一個不存在的索引 |
| 6 | 物件 | const 鎖的是「指向誰」不是內容、Console 的活參照陷阱 |
| 7 | 迴圈 | for 三段式、無窮迴圈讓我的電腦起飛 |
| 8 | 函式 | 參數是影本、return 才會交出值、Math.max 設傷害下限 |
| 9 | 作用域 | 變數活在哪、var 為什麼被勸退 |
| 10 | DOM | 抓元素、改 textContent,畫面第一次有東西 |
| 11 | 事件 | addEventListener,遊戲第一次會等玩家 |
這是我這十二天最意外的一個發現:花最久的是 Day 11,但最難的是 Day 10。
Day 10 難,是因為 document 那一整套東西完全是新世界。前九天我學的都是「怎麼算」,Day 10 開始學的是「怎麼跟瀏覽器溝通」——document.getElementById 這種寫法,一開始只能照抄,說不上理解。
Day 11 久,反而不是因為觀念難,而是因為整體內容變長了。
Day 11 寫到後面,我發現自己一直在做同一件事:回頭找「我要的這個動作,該加在哪一行」。
而且不只是找位置,還要在腦中模擬:加在這裡的話,執行到這一行的時候,enemy.hp 是多少?訊息文字現在是什麼?按鈕變了沒?——同一個變數在不同位置有不同的值,而我要判斷的是「在哪個位置插入,才會拿到我要的那個狀態」。
需要同時考慮的東西變多了,這才是變慢的原因。
回頭看,前十天的程式都是「從上到下跑一遍就結束」,腦中只要跟著跑一次就好。從 Day 11 開始,程式變成「掛好機關、停下來等、被點擊、跑一段、再等」——它有了狀態,而狀態會隨著玩家的操作改變。我卡住的地方不是語法,是要開始追蹤「現在是第幾回合、小怪死了沒、訊息該顯示哪一種」。
這也解釋了為什麼 Day 11 出現的兩個 bug 都跟狀態有關:「勝利的瞬間顯示了鞭屍訊息」是搞混了「剛剛死」和「早就死」;「畫面凍住」是資料的狀態和畫面的狀態不同步。這兩個都不是打錯字,是沒想清楚狀態。
如果要挑一個「這件事跟我本業最有關」的瞬間,是 Day 8 那個 -5。
當時我測 calcDamage(15, 20)——攻擊力比防禦力低,函式回傳 -5。數學上沒錯,但放進遊戲就變成 敵人 HP 60 - (-5) = 65,攻擊反而幫對方補血。
這個問題最像我平常的工作:不需要理解程式怎麼寫的,只要餵一組極端的輸入,問題就直觀地跳出來。不用推理、不用讀程式碼,看一眼就知道不對。
而且後續的決策也很 QA:發現問題之後,不是隨便挑個修法,而是要問「這裡應該怎樣才對」。我最後選了「最少造成 1 點傷害」,不是因為它最好寫,是因為之後想做流血類的武器,讓攻擊力不足的玩家也能靠擦傷磨贏魔王——一個邊界值的決定,影響了遊戲後期的戰術空間。
十一天下來我發現自己一直在做這件事:Day 4 測 hp 剛好等於 30、Day 5 測 enemies[length]、Day 7 故意跑無窮迴圈、Day 11 一直按到血變負數。我沒有刻意要做測試,只是很自然會想「如果餵一個怪的值進去會怎樣」——這大概是職業病,但在學程式的時候意外很好用,因為它讓我撞到很多正常照抄範例不會撞到的東西。
寫到 Day 11 的時候,我發現一個具體的困擾:經常覺得「前幾天好像學過可以用一樣的方法」,然後開始翻紀錄找。
所以我整理了一份速查表,放在 repo 的 CHEATSHEET.md。它的分類方式是「我想做什麼」而不是「這是什麼語法」——因為卡住的時候我腦中想的是「我想把這個數字顯示在畫面上」,不是「我需要 DOM 操作」。
裡面還有兩個區塊是為自己做的:
第二個清單是這十一天最意外的收穫。我原本以為寫程式的難處是「錯誤看不懂」,實際上真正難查的是不報錯的錯:字串改一個字不生效、兩個同名函式安靜覆蓋、監聽器掛上 undefined 卻不吭聲、資料改了畫面沒動。這些都不會有紅字告訴你,只會讓你懷疑自己。
ironman-js-battle/
├── day01/ ~ day11/ 每天的練習檔
├── CHEATSHEET.md 速查表
└── README.md 進度清單
十一個 commit,每天一個。程式碼從 console.log("鐵人賽 Day 1 開跑") 長到一個能互動的戰鬥畫面。
接下來要把這些零件真正組裝起來:目前小怪只會站著被打,要讓牠會反擊;目前只有一回合,要做成完整的回合制;目前畫面很醜,要加上 CSS 和血條。
而看完自己的卡關紀錄,我對下一階段最需要的準備已經很清楚了:不是學更多語法,是練習「在腦中追蹤狀態」。Day 11 兩個 bug 都是狀態沒想清楚,而接下來的程式只會更長、狀態只會更多。
第一階段結束。從一片空白的網頁,到一個會等我按下攻擊的小怪——雖然牠現在還是打不死的小強。