請啟用 Javascript 查看內容

JS 學徒特訓班 - JS 函式 11 ~ 23 關

 ·  ☕ 12 分鐘

竹白的 JS 學徒特訓班筆記 - 目錄

記錄六角學院所開的 JS 學徒特訓班,為期 60天的特訓。

第十一關:函式參數再練習

題目連結

劇情略。

11. 問題

題目一:老闆想要隨時扣你的薪水

老闆:「今天是發薪日,先發給你薪資 23500 元(遞給小杰」
老闆:「以下三步驟動作請寫成程式」
老闆:「步驟一:昨天你上廁所太久了,我要扣你 1000 元」
老闆:「步驟二:小黑說不喜歡你煮的狗食,我要再扣你 3500 元 (再次取走」
老闆:「步驟三:我今天心情不太好,扣個 500 元意思一下」
小杰:「屁啦!心情不太好關我啥事!」
老闆:「這不是重點,快點算!將之前的寫法換成函式,並增加一個參數,讓我方便何時扣你多少都沒問題」
老闆:「最後你寫的函式,要執行三次,每次都要回報你的總薪水剩下多少。」

1
2
3
4
5
var salary = 23500;

//請依照上面的武功秘笈 Codepen,依序實現兩步驟,算出小杰被扣了多少錢

console.log('小杰目前還剩下' + salary + '元');

題目二: if + 指派運算子

老闆:「好了,現在我要告訴你贈品條件!」

  • 目前小杰手上有 3 個贈品
  • 消費滿 100 元就送對方贈品

下圖為第八關截圖,請依照以下邏輯進行改寫

  1. 現在來了三個客人,並依序有消費,A顧客消費 150、B 顧客消費 99、C 顧客消費 110。
  2. 請設計一個函式,裡面代入一個參數為顧客消費金額,確認該客戶是否符合贈品條件,若符合就讓 giftNum 變數減少數量。
  3. 並依序執行三次函式,每次函式皆會 return 目前贈品數量剩下多少。
1
2
3
4
5
6
7
8
9
var giftNum = 3;     // 贈品數量
var customerA = 150; // 顧客 A 消費金額
var customerB = 99;  // 顧客 B 消費金額
var customerC = 99;  // 顧客 C 消費金額

if (客戶A條件) {}
if (客戶B條件) {}

console.log('目前贈品剩下' + giftNum + '個');

題目三:if + 指派運算子

老闆:「我現在補齊給你總計 200 個贈品!」
老闆:「然後我覺得現在贈品門檻太高了,我決定大放送,只要滿 50 元就送一個!以此類推,他買 500 元就送 10 個贈品!
小杰:「老闆你竟然利用我的特休,我跟你沒完啦。」
老闆:「別廢話,客人來了快點!他買完後告訴我贈品還夠不夠!」

下圖為第八關截圖,giftNum 已改為 200 份,請依照以下邏輯進行改寫

  1. 請用函式改寫,同時來了三組客人,請用你寫的函式連續執行三次,來幫助顧客是否有符合贈品條件。
  2. 另外每次執行函式時,都必須告訴老闆一次目前贈品數量。

第一組客人:Mary 買了 10 份薯條,10 份漢堡
第二組客人:Bob 買了 1 份薯條
第三組客人:Tim 買了 20 份薯條,15 份漢堡

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
var giftNum = 200; // 贈品數量
var friesPrice = 30; // 薯條單價
var hamburgerPrice = 50; // 漢堡單價

// 以下是題目
// mary 買了 10 份薯條,10 份漢堡
// 請計算完贈品規則後,善用指派運算子去計算目前剩下的贈品有幾個
// 並用下面的 if 回報給老闆

if (條件式) {
  console.log('老闆!贈品還夠!剩下' + giftNum + '個~');
} else {
  console.log('老闆贈品賣光啦~');
}

11. 參考解答

題目一:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
function calculate(num) {
  salary -= num;
  return salary;
}

calculate(1000);
console.log('小杰目前還剩下' + salary + '元');
// '小杰目前還剩下22500元 '

calculate(3500);
console.log('小杰目前還剩下' + salary + '元');
// '小杰目前還剩下19000元'

calculate(500);
console.log('小杰目前還剩下' + salary + '元');
// '小杰目前還剩下18500元'

題目二:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
function calculate(num) {
  if (num >= 100) {
    giftNum -= 1;
  }
  return giftNum;
}

calculate(customerA);
console.log('目前贈品剩下' + giftNum + '個');
// 目前贈品剩下2個

calculate(customerB);
console.log('目前贈品剩下' + giftNum + '個');
// 目前贈品剩下2個

calculate(customerC);
console.log('目前贈品剩下' + giftNum + '個');
// 目前贈品剩下1個

題目沒說明,因此這裡不考慮贈品不足(giftNum 變負數)的情況。

題目三:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
function calculate(friesNum = 0, hamburgerNum = 0) {
  let total = friesPrice * friesNum + hamburgerPrice * hamburgerNum;
  giftNum -= Math.floor(total / 50);
  if (giftNum > 0) {
    console.log('老闆!贈品還夠!剩下' + giftNum + '個~');
  } else {
    console.log('老闆贈品賣光啦~');
  }
}

calculate(10, 10); // 老闆!贈品還夠!剩下184個
calculate(1);      // 老闆!贈品還夠!剩下184個
calculate(20, 15); // 老闆!贈品還夠!剩下157個

第十二關:DOM 操作

題目連結

劇情略。

12. 題目

  1. 請觀看此 Codepen 的線索,試圖找到密碼,並在 Slack 上回傳密碼
  2. 以下 Codepen 的 HTML 與 CSS 面板都壞掉了,你只能編輯 JS 面板,來去尋找線索

12. 參考解答

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
let url = 'https://hexschool.github.io/JSTraining/stage12/';

// 區塊一,路徑放錯了,得用 setAttribute 換 src 屬性,換成 1.png
document.querySelector('.keyItem1 img').setAttribute('src', `${url}1.png`);

// 區塊二,唯一有放對,不需更改

// 區塊三,請用 innerHTML,指定 keyItem3,讓他插入 3.png 圖片
document.querySelector('.keyItem3').innerHTML = `
  <div class="keyItem keyItem3">
    <img src="${url}3.png" alt="">
  </div>`;

// 區塊四,只能用 style 插入 background 4.png
document.querySelector('.keyItem4').style.background = `url("${url}4.png")`;

解答 Codepen

教學、延伸知識連結

第十三關:函式+if 判斷

題目連結

劇情略。

13. 問題

以下為 BMI 計算方式:

  1. BMI 計算為:體重(公斤) / 身高的平方(單位為公尺)
    • 例如 150 公分 50 kg = 50/(1.5*1.5) = 22.2 BMI 指數
  2. BMI 數值狀態如下
    • 體重過輕:BMI < 18.5
    • 正常:18.5 ≦ BMI < 24
    • 過重:24 ≦ BMI < 27
    • 輕度肥胖:27 ≦ BMI < 30
    • 中度肥胖:30 ≦ BMI < 35
    • 重度肥胖:BMI ≧3 5 

小杰打開 Code 才發現,裡面的 if 判斷根本都在亂寫,請依照上面的 BMI 狀態來改寫,並還小杰一個清白。

請修改此 Codepen,並將修改結果回傳到 Slack。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
function calculationBMI(height, kg) {
  var bmi = kg / (height / 100 * height / 100);
  if (bmi > 20) {
    return '過胖!但能吃就是福,維持現況也沒什麼不好的~';
  } else {
    return '你很瘦~ㄕㄡˋ~~';
  }
}

console.log(calculationBMI(178, 70));

13. 參考解答

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
if (bmi < 18.5) {
  return '體重過輕';
} 
if (bmi <= 24) {
  return '正常';
} 
if (bmi <= 27) {
  return '過重';
} 
if (bmi <= 30) {
  return '輕度肥胖';
} 
if (bmi <= 35) {
  return '中度肥胖';
}
return '重度肥胖';

if 裡面已經使用 return,就不建議使用多餘的 else if 了。

教學、延伸知識連結

第十四關:監聽與函式設計

題目連結

劇情略。

14. 題目

小杰打開網頁後,發現工讀生只把 HTML 標籤寫好,JS 完全沒寫,
因為 HTML 面板已經壞掉了,請直接修改 JS Code,
讓 .total 的顯示數值,可以正常運作。

並在 Slack 上回傳 carbon JS Code 與 Codepen。

BMI 2.0版 Codepen 網址

1
2
3
4
5
6
7
<input type="text" class="height" placeholder="請輸入您的身高(公分)">
<input type="text" class="kg" placeholder="請輸入您的體重(公斤)">
<input type="button" class="send" value="計算">
<p class="total">您的 BMI 指數為 
  <span class="BMI">22</span>,
  狀態是 <span class="status">正常</span>
</p>

14. 參考解答

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
document.querySelector('.send').addEventListener('click', function () {
  let heightDom = document.querySelector('.height');
  let kgDom = document.querySelector('.kg');
  let height = Number(heightDom.value);
  let kg = Number(kgDom.value);
  let totalDom = document.querySelector('.total');

  // 若輸入不合法的值
  if (isNaN(height) || isNaN(kg) || height < 1 || kg < 1) {
    totalDom.textContent = '請輸入大於 1 的數字';
    return;
  }

  let bmi = Math.floor(kg / (((height / 100) * height) / 100));
  let result = bmiResult(bmi);
  totalDom.innerHTML = `
    您的 BMI 指數為 <span class="BMI">${bmi}</span>, 
    狀態是 <span class="status">${result}</span>`;
});

function bmiResult(bmi) {
  if (bmi < 18.5) {
    return '體重過輕';
  }
  if (bmi <= 24) {
    return '正常';
  }
  if (bmi <= 27) {
    return '過重';
  }
  if (bmi <= 30) {
    return '輕度肥胖';
  }
  if (bmi <= 35) {
    return '中度肥胖';
  }
  return '重度肥胖';
}

教學、延伸知識連結

第十五關:物件運用 - 資料與渲染分離

題目連結

劇情略。

15. 問題

以下是護理師改寫小杰的邏輯,程式碼連結

護理師拿了之前工讀生的 Code 改寫,根本沒用小杰的。但可以看得出來,這個 Code 有刻意遺留了些不完整的地方,像是 bmiData 的物件沒有將條件寫完整,頗有想要再次考驗小杰的味道。

請各位將你們的 Code 跟護理師的進行比較,看看差異在哪裡,並透過 Slack 分享自己吸收的結果,並試著依照自己的邏輯寫一遍。

這次 HTML 面板壞掉了,你唯一可以改的面板只有 CSS 與 JS 面板。

15. 參考解答

這裡就不優化了,直接補齊缺的部分。

補齊所有狀態顏色:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
.blue {
  color: blue;
}
.green {
  color: green;
}
.pink {
  color: pink;
}
.orange {
  color: orange;
}
.purple {
  color: purple;
}
.red {
  color: red;
}

建議語意化命名 class 名稱,但這裡就依照題目吧。

補齊 BMIData 物件狀態:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
var BMIData = {
  overThin: {
    statusText: '體重過輕',
    class: 'blue',
  },
  normal: {
    statusText: '正常',
    class: 'green',
  },
  overweight: {
    statusText: '過重',
    class: 'pink',
  },
  mildObesity: {
    statusText: '輕度肥胖',
    class: 'orange',
  },
  moderateObesity: {
    statusText: '中度肥胖',
    class: 'purple',
  },
  severeObesity: {
    statusText: '重度肥胖',
    class: 'red',
  },
};

補齊 calculationBMI 函式:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
if (bmi < 18.5) {
  render('overThin', bmi);
} else if (bmi <= 24) {
  render('normal', bmi);
} else if (bmi <= 27) {
  render('overweight', bmi);
} else if (bmi <= 30) {
  render('mildObesity', bmi);
} else if (bmi <= 35) {
  render('moderateObesity', bmi);
} else {
  render('severeObesity', bmi);
}

render 函式加入其他 class 前要先清除所有 class 樣式:

1
statusDOM.classList.remove(...statusDOM.classList);

解答 CodePen

教學、延伸知識連結

第十六關:forEach 與 HTML 字串累加

題目連結

劇情略。

16. 問題

小杰一時間還不知道怎麼寫,所以先寫了陣列 + forEach 的方式來累加 HTML 字串,再來顯示介面出來。範例程式碼

請你依照這邏輯,試著改寫物件格式,呈現的格式如下程式碼。

設計好物件後,依序 push 五筆資料到陣列裡,最後整理完字串資料,再 innerHTML 到 ul 裡。

這次 HTML 與 CSS 面板都壞了,請改寫 JS 面板。

PS:請拿上面的 Codepen 改寫即可,不需拿前幾關的 code 來整合

<ul>
    <li>小杰的身高為 178 公分,體重是 70 公斤,BMI 數據為 20,狀態為正常</li>
    <li>小美的身高為 150 公分,體重是 200 公斤,BMI 數據為 38,狀態為超重</li>
</ul>

16. 參考答案:

物件格式改寫,我改一個就好,剩下雷同。

1
2
3
4
5
6
7
{
  name: '廖洧杰',
  height: 178,
  kg: 70,
  bmi: 22,
  status: '正常',
}

str 拿出來,避免每次迭代都被重置,並使用字串樣板加入變數。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
var str = '';
data.forEach(function (item) {
  var content = `
    <li>${item.name}的身高為 ${item.height} 公分,
    體重是 ${item.kg} 公斤,
    BMI 數據為 ${item.bmi}    狀態為${item.status}<li>`;
  str += content;
  list.innerHTML = str;
});

教學、延伸知識連結

第十七關:innerHTML+forEach

題目連結

劇情略。

17. 問題

明明陣列裡面有五筆資料,但寫出來的程式碼,列表永遠只顯示一筆資料,究竟小杰哪裡觀念有問題呢?一起來幫助小杰吧~

小杰程式碼網址

請思考並在 Slack 回覆:

  1. str 放 forEach 外頭跟裡面的差異
  2. innerHTML 放 forEach 外頭跟裡面的差異
  3. innerHTML 當要寫內容進去時,會不會將裡面內容給覆蓋掉?

17. 參考解答

  1. 作用域不同,一個是全域,另一個只作用在 forEachcallback 函式內。放在外面才會累加結果。
  2. innerHTML 放在外面就不會迭代,只會跑一次。
  3. 會覆蓋。

教學、延伸知識連結

第十八關:字串相加 innerHTML+ 樣式搭配

題目連結

劇情略。

18. 問題

挑戰者們不要忘了,JS 始終要搭配妳寫好的 CSS 來進行渲染,護理師好像已經看膩了小杰爛到不行的陽春版本,寫了一個 .list2 版本,請依照他的 CSS 樣式邏輯,將 data 陣列裡面的資料,依序渲染到 .list2 裡面的 li 來顯示。

護理師的 Codepen 網址

18. 參考解答

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
var list2 = document.querySelector('.list2');
var str = '';
data.forEach(function (item) {
  var content = `
    <li class="list-card">
    <h2>${item.name}</h2>
    <p>你的身高是${item.height}</p>
    </li>`;
  str += content;
});
list.innerHTML = str;

第十九關:驗收前夕

題目連結

劇情略。

19. 問題

小杰打開師傅的程式碼,才發現這程式碼其實是來自於 Vic 這位工程師的範例程式碼。

雖然跟設計稿有非常大的差異,但從程式邏輯中,慢慢掌握到了一些概念。

請解讀 Vic 的(程式碼),並在 Slack 上回報他以下邏輯是做了什麼事情:

  1. arrayBMIrecord 的陣列用途是什麼?
  2. 請描述 calculateBMI 函式做了什麼事情
  3. 請描述 render 函式做了什麼事情

19. 參考解答

  1. 儲存處理過的資料。
  2. 處理資料、將資料丟到 arrayBMIrecord、並執行 render 函式。
  3. 渲染 arrayBMIrecord 資料到畫面上。

第二十關:BMI 計算機團戰關卡

題目連結

劇情略。

20. 題目

完成上方 UI 設計稿。

20. 參考解答

教學、延伸知識連結

第二十一關:BMI 追加功能

題目連結

劇情略。

22. 問題

可憐的小杰又被威脅了,原來院長想要統計每次 BMI 測量的總平均值,一起做完這最後的需求,讓小杰順利離開醫院吧!

22. 參考解答

1
<span class="js-bmiAverage">0</span>
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
const bmiAveragea = document.querySelector('.js-bmiAverage');

function render() {
  let sum = 0;
    arrayBMIrecord.forEach((item) => {
      sum += item.bmi;
    });
  let avg = Math.floor(sum / arrayBMIrecord.length) || 0;
  bmiAveragea.textContent = avg;
}

CodePen 解答

第二十二關:陣列調整

題目連結

劇情略。

22. 問題

最新的資料放在第一筆,讓最新資料都能排序在最前面,將資料進行反轉。

22. 參考解答

有好幾種做法,渲染時,從後面迭代資料:

1
2
3
4
for (let i = arrayBMIrecord.length - 1; i > 0; i -= 1) {
  const item = arrayBMIrecord[i];
  // ...
}

或是先淺拷貝一份資料,並使用 reverse() 反轉陣列,再迭代資料:

1
2
3
4
5
let temp = [...arrayBMIrecord].reverse();

temp.forEach(
  // ...
);

或是添加資料時,使用 unshift() 將最新資料加在最前面:

1
arrayBMIrecord.unshift(userRecord);

但使用 unshift 效率不佳,因為背後的運作原理會將每個元素往後移動。

CodePen 解答

教學、延伸知識連結

第二十三關:資料格式處理,陣列 unshift

題目連結

劇情略。

23. 問題

小杰一看到題目整個傻眼,裡面用了他從來沒看過的圖表資料,好在老闆在程式碼裡面寫了些註解,讓小杰知道自己不需要懂圖表框架,好好的把資料處理好後,圖表自然就會顯示正常,一起來幫幫小杰吧!

以下是預期希望出現正常的圖表,請改寫此 Codepen,並在 Slack 回傳你的解法:
PS:HTML 與 CSS 面板壞掉了,只能變更 JS 面板

23. 參考解答

1
monthMoney.unshift(title);

竹白
作者
竹白
前端筆記

文章目錄