---
tags: Learn
---
# 前端工程師面試考題
## JS
### closure (閉包)
簡單來說在一個帶有參數 a 的函式中返回另一個使用到參數 a 的函式
此時在函式外可以獲取到參數 a 就稱為閉包
即把參數存到一個新的函數中 保留其記憶體位置 就是閉包
```javascript=
function makeAdder(x) {
return function(y) {
// 函數被 return 後,還是能繼續存取環境中的 x 變數
return x + y;
};
}
// 建立一個新變數指向原本的函數 傳入 x 值,調用後可以得到 x 就是一個閉包
var add5 = makeAdder(5);
console.log(add5(2)) // 所以這裡結果為 7
```
上述可以發現 add5 本身只有傳入 y 值
但還是可以獲取到 makeAdder 的 x 值
這就是所謂的閉包了 在記憶體中保留了 x 的位置 使其可以繼續被抓取使用
### hoisting (提升)
在宣告變數前先引用變數 不會出現 xxx is not defined 報錯,而是出現 JS 中的預設值 undefined ,就是所謂的提升。
函數與 var 宣告的變數都會被提升,且函數被提升的權重會高於變數。
[參考文章 - 這篇我大概也只看懂一半](https://blog.techbridge.cc/2018/11/10/javascript-hoisting/)
### undefined vs null
先上一段有趣的小東西:
```javascript=
console.log(undefined == null)
console.log(undefined === null)
```
上方代碼輸出結果第一行會為真,第二行則為假
原因是這兩者都屬於 falsey 但是兩者的型別是不同的
在 JS 中 null 屬於 object 型別
而 undefined 的型別則還是 undefined
這也導致在第一行程式碼判斷中 falsey = falsey 所以是 true
然後第二行程式碼會判斷兩者型別 發現 object != undefined 所以返回 false
有趣吧?也附上一篇短短的參考[文章](https://www.jstips.co/zh_tw/javascript/differences-between-undefined-and-null/)
### this 的 call 與 apply
call 與 apply 都會把傳入的第一個參數定義為 this 的指向
其他參數則依序作為原本的參數傳入函數裡
兩者的差別是 apply 傳入的其餘參數必需要用陣列方式傳入
[參考文章](https://realdennis.medium.com/javascript-%E8%81%8A%E8%81%8Acall-apply-bind%E7%9A%84%E5%B7%AE%E7%95%B0%E8%88%87%E7%9B%B8%E4%BC%BC%E4%B9%8B%E8%99%95-2f82a4b4dd66)
### session/cookie/localstorage
session 被存在伺服器端,cookie 被存在客戶端
上述兩者在瀏覽器關閉時都會失效,
session 的安全性 > cookie 的安全性。
localstorage 被存在本地端,
除非被手動刪除,否則會永久保存在本地端。
### scope (作用域)
每個執行環境都有一個參照的外部環境
這個外部環境會看你當前執行的程式碼處於哪種詞彙環境
即他看的是你在聲明時存在的位置所決定的,而不是執行或調用的位置外
所以下方程式碼:
```javascript=
function b (){
console.log(x);
}
function a (){
var x = 2;
b();
}
var x = 1;
a();
```
在函數 b 的外部參考會是全域中的 x 而不是執行它的函數 a
所以輸出的 x 會是全域中的 1 ,這就是詞彙環境(看該程式碼實際上在物理上存在的位置決定)。
[參考文章](https://medium.com/itsems-frontend/javascript-scope-and-scope-chain-ca17a1068c96)
### prototype(原型)
要先知道,在 JS 中每個物件都有自己的原型(`.prototype`),
而物件的原型還會有其原型(`.__proro__`),
直到該原型的原型(`.__proro__`)輸出為 null 為止。
這過程就稱為一個原型鏈。
原型主要用於建構函數中的方法。
當創建建構函數後,
在 new 出來的物件中就可以調用到建構函數中的方法與其原型中的方法,
我們可以通過原型去給建構函數建立方法,
而不需要在每個 new 出來的物件中重複創建新的方法,
通過原型建立的方法可以讓所有 new 出來的物件都使用到,
假設你在每個 new 出來的物件中撰寫方法,
會導致佔用非常多記憶體位置,
但如果你是使用原型方式建立方法,
就只需要佔用一個記憶體位置了。
[參考文章 - 內附程式碼說明,較清楚](https://blog.techbridge.cc/2017/04/22/javascript-prototype/)
### == vs ===
雙等於:先轉換型別後比較,其值若相等則返回 true
全等於(三等於):嚴格比較,不會轉換型別,一開始先判斷型別,若不相等會直接返回 false
幾個要注意的判斷:
- NaN == NaN // false
- undefined === null // false
- undefined == null // true
- +0 === -0 // true
要注意的重點:
- object 與 array 判斷是否等於,看的會是其參考地址是否相等。
### by value vs by reference(傳值與傳參)
可以簡單理解為物件與函數是傳參考 其他變數字串陣列等都是傳值
傳值表示每個東西都是獨立存在 只是把值複製一份給其他東西使用
傳參考則表示每個東西所指向的是同一個記憶體位置 修改其值就會導致共享該記憶體的其他東西一起被修改
[參考文章](https://ithelp.ithome.com.tw/articles/10191057)
## JS(ES6)
### ES6 新增了哪些技術
class 物件導向設計,
箭頭函數,
函數參數預設值,
export&import,
模板字串(使用反引號拼接字串),
解構賦值,
promise,
let&const,
Symbol,
...展開/其餘運算符。
[參考文章 - 還有很多其他技術](https://zhuanlan.zhihu.com/p/87699079)
### promise
首先我們知道 JS 是屬於同步的程式語言,當遇到非同步事件時,他會先執行所有的同步事件,等所有同步事件處理完畢後才會執行那些非同步事件, JS 的非同步事件有計時器或 AJAX 事件等,假設需要在確保非同步事件完成後才繼續執行其他程式碼,就需要使用 promise 了。promise 的強大特性是他可以通過鏈接(.then().catch())執行下一個動作,而不需要使用回調地獄。
[參考文章](https://wcc723.github.io/development/2020/02/16/all-new-promise/)
### 解構賦值
可以簡單理解為等號右方的資料會對應到左方同位置的資料。
細節部分比較繁雜可以直接閱讀參考文章。
[參考文章](https://eyesofkids.gitbooks.io/javascript-start-from-es6/content/part4/destructuring.html)
### var let const
var 的作用域是全域
let 的作用域是 block 即每個 {} 中獨立存在
const 是常數 聲明後即不能被修改 且在聲明時若沒有賦值會報錯
[參考文章](https://wcc723.github.io/javascript/2017/12/20/javascript-es6-let-const/)
### 為什麼要用箭頭函數
除了他可以有效的簡化寫法外,其 this 是完全綁定在語彙上的位置,也就是說在箭頭函數裡的 this 永遠都是語意上的 this ,不管是誰呼叫他,或是被如何 bind 、 call 、 apply ,他永遠都是拿到原先作用域的 this 。
這樣的做法可以讓你在物件上使用回調時,拿到正確的 this ,而非一言不合就 undefined
[參考文章 - 箭頭函式與傳統函式之差異](https://hsuchihting.github.io/javascript/20200813/289345854/)
## Vue.js
### 為什麼要用 Vuex
在大型專案或專案結構較複雜時段於管理資料會方便很多
如兩個兄弟組件之間的溝通 在沒有使用 Vuex 的情況下會變得很麻煩
但使用 Vuex 就可以統一管理資料結構了
### components 是否可以擁有自己的 style
可以,只需要在 style 標籤加上 scoped 屬性
即可把樣式限制在該元件中做使用
## ajax
### API 串接方法有哪些
GET: 讀取(獲取)資源
PUT: 更新資源
DELETE: 刪除資源
POST: 創建資源
PATCH: 更新部分資源
### SSR 是什麼
他的中文翻作伺服器渲染,執行過程如下:
`輸入網址 => 發送請求 => 接收響應(響應回來的直接是一個頁面) => 瀏覽器解析渲染畫面`
另外有一個 CSR 中文翻作客戶端渲染,執行過程如下:
`輸入網址 => 發送請求 => 接收響應(響應回來的是一個模板頁面 需通過 js 解析後才能渲染) => 在響應回來的模板中透過 js 逐行解析,當遇到 ajax 時再發送新的請求 => 最後渲染畫面`
[參考文章 - 這篇跪著讀](https://hsiangfeng.github.io/other/20210529/2519649612/?fbclid=IwAR0OvTvBZln1dR8wWl6Gk1sKh4pd4wepTb1RI4DOXCFgni1gQ4UTNdtl8Jg)
### 從輸入 url 到畫面渲染發生了什麼事
瀏覽器輸入網址後會先查找 IP 地址,
接著通過 TCP 三次握手確認雙方已建立好連結,
然後瀏覽器就可以針對 IP 地址發送請求並等待響應,
此時響應會被拆成很多個封包,最後封包合併後傳送 200 ok
表示響應接收成功,瀏覽器再解析響應數據並渲染畫面,
最後會進行四次揮手道別中斷雙方連結。
[參考文章 - 這篇也很強大繼續跪著看](https://hulitw.medium.com/learning-tcp-ip-http-via-sending-letter-5d3299203660)
### http vs https
https 在應用層與傳輸層之間多了一道 SSL 加密憑證
這個 SSL 可以防止在發送 HTTP 的 GET 或 POST 請求的時候被攔截獲取到資訊
### 何謂 AJAX 非同步 通常都如何實踐
在客戶端向伺服器發送請求時不需等待響應,可以繼續執行其他動作,且接收到響應後不會刷新整個頁面,而是利用 JS 與 DOM 進行局部內容替換,這就是所謂的 AJAX 非同步。
最原生的實現方式是使用 XMLHttpRequest 物件 但因為寫法過於繁雜
後來 jQuery 又推出了 `$.ajax()` 方法
接著是結合 ES6 promise 的 `fetch()` 方法
最後出現了依賴於 ES6 promise 的 axios 輕量級套件
[參考文章](https://tw.alphacamp.co/blog/ajax-asynchronous-request)
## CSS
### box-model 包含 font-size padding border 嗎
沒有包含文字大小。
盒模型指的是:寬度、高度、邊框、內距與外距這五者。
### z-index 使用時機?
有設置 position(非預設值 static)的元素才能使用 z-index 屬性
且 z-index 的值須為整數
### box-sizing: border-box; 用在哪?你平常會用嗎?
幾乎都會使用到,可以方便的計算元素大小,省去很多煩雜的計算。
他讓尺寸不再只是寬度與高度,而是寬度與高度外,又包含了 padding & border
[參考文章](https://titangene.github.io/article/css-box-sizing.html)
### CSS 權重順序?
HTML tag 1分
class 10分
id 100分
行內 style 1000分
!imporant 10000分
[參考文章](https://ithelp.ithome.com.tw/articles/10196454)
### 會如何管理 CSS 結構 會用哪些設計模式、技術優化它?
大型專案上較常使用 SCSS 撰寫
使用語意化方式命名 多數使用小駝峰命名法
了解 OOCSS 的結構與樣式分離、容器與內容分離概念
了解 BEM SMACSS 等概念
[參考文章 - CSS 模組化有哪些方法](https://cythilya.github.io/2018/06/05/css-methodologies/)
### class 與 ID 的差異?
通常 id 都是跟 js 配合或作為錨點用的
且在 CSS 上會盡量避免使用 id ,因為兩者權重差很多
另外就是相同 class 可以有多個,但相同 id 只可以有一個
[參考文章](https://medium.com/@small2883/html%E7%9A%84id-class%E5%B1%AC%E6%80%A7%E4%BB%8B%E7%B4%B9-css-%E7%9A%84-class-%E5%92%8C-id-%E5%85%A9%E8%80%85%E6%9C%89%E4%BD%95%E5%B7%AE%E7%95%B0-25ce5315ece)
ps 文章參考就好,親測 id 可以用數字,只是在樣式中用#數字會報錯,但JS不會出錯
### 是否習慣用格線系統?
我個人通常是有使用 Bootstrap 就會用 但基本上會看設計稿需求
平常手刻不會刻意製作 但不排斥 也有嘗試手寫過格線系統
[參考文章 - 格線佈局的概念](https://developer.mozilla.org/zh-TW/docs/Web/CSS/CSS_Grid_Layout/Basic_Concepts_of_Grid_Layout)
### 通常如何解決跨瀏覽器問題?
使用 Sass 編譯 + CSS Reset
[參考文章 - 我其實沒研究過這問題](https://www.gushiciku.cn/pl/ptVs/zh-tw)
### display 有哪些值
none、inline、inline-block、block、inline-flex、flex、table、inline-grid、grid、unset
[參考文章 - 其他我不知道,但還有好多](https://developer.mozilla.org/en-US/docs/Web/CSS/display)
### 兩種 CSS Reset 差異
Normalize 保留一些常用的預設樣式 如列表 標題等
Reset 把所有瀏覽器預設樣式都清空了
[參考文章](https://www.itread01.com/content/1541043546.html)
### 描述 svg gif png 差異與使用時機:
PNG:有效減少圖片檔案大小以及保留照片透明元素,圓角圖片就很適合使用。
SVG:向量檔,縮放不失真,用在 logo 很方便。
GIF:可以製作小型動態圖片檔案,適用於色彩簡單的檔案,也有支援透明背景。
[參考文章](https://www.cool3c.com/article/146971)
### rem 是什麼
rem 是指 root 層級的文字大小,即 html 標籤的大小,通常是 16px ,可以通過設定 html 的 font-size 影響到所有使用 rem 單位的尺寸。
[參考文章](https://www.hexschool.com/2016/01/02/2016-08-08-em-vs-rem/)
### !important 使用時機
用於強制性覆蓋樣式,因為 important 的權重在 CSS 中是最高的,可以通過這種方式強制把想要的樣式覆蓋上去。
## 上機考
### 做出 CSS 的三種水平垂直居中方法
```css=
/* 設在子元素,內容只有一行文字時才適用 */
.method1 {
margin: auto;
line-height: 父層高度;
}
/* 設在父元素 */
.method2 {
display: flex;
justify-content: center;
align-items: center;
}
/* 設在子元素 */
.method3 {
position: absolute; /*父層需加上 position: relative; */
transform: translate(-50%, -50%);
top: 50%;
left: 50%;
}
/* 設在父元素 */
.method4{
display: flex; /* 子元素設置 margin: auto; */
}
```
[參考文章 - CSS 多種居中方法](https://medium.com/%E9%BA%A5%E5%85%8B%E7%9A%84%E5%8D%8A%E8%B7%AF%E5%87%BA%E5%AE%B6%E7%AD%86%E8%A8%98/%E9%80%8F%E9%81%8E-css-%E5%9E%82%E7%9B%B4%E7%BD%AE%E4%B8%AD%E7%9A%84%E5%95%8F%E9%A1%8C%E8%88%87%E8%A7%A3%E6%B1%BA%E6%96%B9%E5%BC%8F-bf2fd91af3f9)
### 原生 JS Todolist
題目要求:
可以新增項目
完成的項目會有刪除線
可以刪除單個項目
網頁重整也能夠保存資料(使用 cookie or localstorage)
```javascript=
const data = JSON.parse(localStorage.getItem("datas")) || [];
oInput.addEventListener("keyup", function (e) {
if (e.keyCode == 13) {
let obj = {};
obj.content = oInput.value;
obj.checked = false;
data.push(obj);
oInput.value = "";
init();
}
});
oUl.addEventListener("click", function (e) {
let i = e.target.getAttribute("data-num");
if (e.target.nodeName == "A" && e.target.getAttribute("class") == "delete") {
e.preventDefault();
data.splice(i, 1);
} else {
data[i].checked = !data[i].checked;
}
init();
});
function init() {
let str = "";
data.forEach(function (item, i) {
if (item.checked) {
str += `<li data-num=${i}><del>${item.content}</del><a class="delete" href="#" data-num=${i}>刪除</a></li>`;
} else {
str += `<li data-num=${i}>${item.content}<a class="delete" href="#" data-num=${i}>刪除</a></li>`;
}
});
oUl.innerHTML = str;
localStorage.setItem("datas", JSON.stringify(data));
}
init();
```
[參考文章 - localstorage 用法](https://medium.com/%E9%BA%A5%E5%85%8B%E7%9A%84%E5%8D%8A%E8%B7%AF%E5%87%BA%E5%AE%B6%E7%AD%86%E8%A8%98/javascript-localstorage-%E7%9A%84%E4%BD%BF%E7%94%A8-e0da6f402453)
### 原生 JS 做出 99乘法表
```javascript=
function test() {
for (var i = 1; i < 10; i++) {
for (var j = 1; j < 10; j++) {
console.log(`${i} * ${j} = ${i * j}`);
}
}
}
test();
```
### 用 AJAX 撈取前十筆資料
```javascript=
// XML 最基礎的原生方法
var req = new XMLHttpRequest();
req.open("GET", '網址');
req.send();
req.onreadystatechange = function () {
if (this.readyState == 4 && this.status == 200) {
var data = JSON.parse(req.responseText);
for (i = 0; i < 10; i++) {
console.log(data[i].name);
}
}
};
// ES6 的 fetch 方法
fetch('網址')
.then(res => {
return res.json();
})
.then(result => {
result.data.forEach((item, i) => {
if(i < 10) {
console.log(item.name);
}
})
});
// 引用 axios 方法
axios.get('網址')
.then((res) => {
res.data.forEach((item, i) => {
if(i < 10) {
console.log(item.name);
}
})
});
```