Try   HackMD

前端工程師面試考題

JS

closure (閉包)

簡單來說在一個帶有參數 a 的函式中返回另一個使用到參數 a 的函式
此時在函式外可以獲取到參數 a 就稱為閉包
即把參數存到一個新的函數中 保留其記憶體位置 就是閉包

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 宣告的變數都會被提升,且函數被提升的權重會高於變數。
參考文章 - 這篇我大概也只看懂一半

undefined vs null

先上一段有趣的小東西:

console.log(undefined == null) console.log(undefined === null)

上方代碼輸出結果第一行會為真,第二行則為假
原因是這兩者都屬於 falsey 但是兩者的型別是不同的
在 JS 中 null 屬於 object 型別
而 undefined 的型別則還是 undefined
這也導致在第一行程式碼判斷中 falsey = falsey 所以是 true
然後第二行程式碼會判斷兩者型別 發現 object != undefined 所以返回 false
有趣吧?也附上一篇短短的參考文章

this 的 call 與 apply

call 與 apply 都會把傳入的第一個參數定義為 this 的指向
其他參數則依序作為原本的參數傳入函數裡
兩者的差別是 apply 傳入的其餘參數必需要用陣列方式傳入
參考文章

session/cookie/localstorage

session 被存在伺服器端,cookie 被存在客戶端
上述兩者在瀏覽器關閉時都會失效,
session 的安全性 > cookie 的安全性。
localstorage 被存在本地端,
除非被手動刪除,否則會永久保存在本地端。

scope (作用域)

每個執行環境都有一個參照的外部環境
這個外部環境會看你當前執行的程式碼處於哪種詞彙環境
即他看的是你在聲明時存在的位置所決定的,而不是執行或調用的位置外
所以下方程式碼:

function b (){ console.log(x); } function a (){ var x = 2; b(); } var x = 1; a();

在函數 b 的外部參考會是全域中的 x 而不是執行它的函數 a
所以輸出的 x 會是全域中的 1 ,這就是詞彙環境(看該程式碼實際上在物理上存在的位置決定)。
參考文章

prototype(原型)

要先知道,在 JS 中每個物件都有自己的原型(.prototype),
而物件的原型還會有其原型(.__proro__),
直到該原型的原型(.__proro__)輸出為 null 為止。
這過程就稱為一個原型鏈。

原型主要用於建構函數中的方法。
當創建建構函數後,
在 new 出來的物件中就可以調用到建構函數中的方法與其原型中的方法,
我們可以通過原型去給建構函數建立方法,
而不需要在每個 new 出來的物件中重複創建新的方法,
通過原型建立的方法可以讓所有 new 出來的物件都使用到,
假設你在每個 new 出來的物件中撰寫方法,
會導致佔用非常多記憶體位置,
但如果你是使用原型方式建立方法,
就只需要佔用一個記憶體位置了。
參考文章 - 內附程式碼說明,較清楚

== vs ===

雙等於:先轉換型別後比較,其值若相等則返回 true
全等於(三等於):嚴格比較,不會轉換型別,一開始先判斷型別,若不相等會直接返回 false
幾個要注意的判斷:

  • NaN == NaN // false
  • undefined === null // false
  • undefined == null // true
  • +0 === -0 // true
    要注意的重點:
  • object 與 array 判斷是否等於,看的會是其參考地址是否相等。

by value vs by reference(傳值與傳參)

可以簡單理解為物件與函數是傳參考 其他變數字串陣列等都是傳值
傳值表示每個東西都是獨立存在 只是把值複製一份給其他東西使用
傳參考則表示每個東西所指向的是同一個記憶體位置 修改其值就會導致共享該記憶體的其他東西一起被修改
參考文章

JS(ES6)

ES6 新增了哪些技術

class 物件導向設計,
箭頭函數,
函數參數預設值,
export&import,
模板字串(使用反引號拼接字串),
解構賦值,
promise,
let&const,
Symbol,
展開/其餘運算符。
參考文章 - 還有很多其他技術

promise

首先我們知道 JS 是屬於同步的程式語言,當遇到非同步事件時,他會先執行所有的同步事件,等所有同步事件處理完畢後才會執行那些非同步事件, JS 的非同步事件有計時器或 AJAX 事件等,假設需要在確保非同步事件完成後才繼續執行其他程式碼,就需要使用 promise 了。promise 的強大特性是他可以通過鏈接(.then().catch())執行下一個動作,而不需要使用回調地獄。
參考文章

解構賦值

可以簡單理解為等號右方的資料會對應到左方同位置的資料。
細節部分比較繁雜可以直接閱讀參考文章。
參考文章

var let const

var 的作用域是全域
let 的作用域是 block 即每個 {} 中獨立存在
const 是常數 聲明後即不能被修改 且在聲明時若沒有賦值會報錯
參考文章

為什麼要用箭頭函數

除了他可以有效的簡化寫法外,其 this 是完全綁定在語彙上的位置,也就是說在箭頭函數裡的 this 永遠都是語意上的 this ,不管是誰呼叫他,或是被如何 bind 、 call 、 apply ,他永遠都是拿到原先作用域的 this 。
這樣的做法可以讓你在物件上使用回調時,拿到正確的 this ,而非一言不合就 undefined
參考文章 - 箭頭函式與傳統函式之差異

Vue.js

為什麼要用 Vuex

在大型專案或專案結構較複雜時段於管理資料會方便很多
如兩個兄弟組件之間的溝通 在沒有使用 Vuex 的情況下會變得很麻煩
但使用 Vuex 就可以統一管理資料結構了

components 是否可以擁有自己的 style

可以,只需要在 style 標籤加上 scoped 屬性
即可把樣式限制在該元件中做使用

ajax

API 串接方法有哪些

GET: 讀取(獲取)資源
PUT: 更新資源
DELETE: 刪除資源
POST: 創建資源
PATCH: 更新部分資源

SSR 是什麼

他的中文翻作伺服器渲染,執行過程如下:
輸入網址 => 發送請求 => 接收響應(響應回來的直接是一個頁面) => 瀏覽器解析渲染畫面
另外有一個 CSR 中文翻作客戶端渲染,執行過程如下:
輸入網址 => 發送請求 => 接收響應(響應回來的是一個模板頁面 需通過 js 解析後才能渲染) => 在響應回來的模板中透過 js 逐行解析,當遇到 ajax 時再發送新的請求 => 最後渲染畫面
參考文章 - 這篇跪著讀

從輸入 url 到畫面渲染發生了什麼事

瀏覽器輸入網址後會先查找 IP 地址,
接著通過 TCP 三次握手確認雙方已建立好連結,
然後瀏覽器就可以針對 IP 地址發送請求並等待響應,
此時響應會被拆成很多個封包,最後封包合併後傳送 200 ok
表示響應接收成功,瀏覽器再解析響應數據並渲染畫面,
最後會進行四次揮手道別中斷雙方連結。
參考文章 - 這篇也很強大繼續跪著看

http vs https

https 在應用層與傳輸層之間多了一道 SSL 加密憑證
這個 SSL 可以防止在發送 HTTP 的 GET 或 POST 請求的時候被攔截獲取到資訊

何謂 AJAX 非同步 通常都如何實踐

在客戶端向伺服器發送請求時不需等待響應,可以繼續執行其他動作,且接收到響應後不會刷新整個頁面,而是利用 JS 與 DOM 進行局部內容替換,這就是所謂的 AJAX 非同步。
最原生的實現方式是使用 XMLHttpRequest 物件 但因為寫法過於繁雜
後來 jQuery 又推出了 $.ajax() 方法
接著是結合 ES6 promise 的 fetch() 方法
最後出現了依賴於 ES6 promise 的 axios 輕量級套件
參考文章

CSS

box-model 包含 font-size padding border 嗎

沒有包含文字大小。
盒模型指的是:寬度、高度、邊框、內距與外距這五者。

z-index 使用時機?

有設置 position(非預設值 static)的元素才能使用 z-index 屬性
且 z-index 的值須為整數

box-sizing: border-box; 用在哪?你平常會用嗎?

幾乎都會使用到,可以方便的計算元素大小,省去很多煩雜的計算。
他讓尺寸不再只是寬度與高度,而是寬度與高度外,又包含了 padding & border
參考文章

CSS 權重順序?

HTML tag 1分
class 10分
id 100分
行內 style 1000分
!imporant 10000分
參考文章

會如何管理 CSS 結構 會用哪些設計模式、技術優化它?

大型專案上較常使用 SCSS 撰寫
使用語意化方式命名 多數使用小駝峰命名法
了解 OOCSS 的結構與樣式分離、容器與內容分離概念
了解 BEM SMACSS 等概念
參考文章 - CSS 模組化有哪些方法

class 與 ID 的差異?

通常 id 都是跟 js 配合或作為錨點用的
且在 CSS 上會盡量避免使用 id ,因為兩者權重差很多
另外就是相同 class 可以有多個,但相同 id 只可以有一個
參考文章
ps 文章參考就好,親測 id 可以用數字,只是在樣式中用#數字會報錯,但JS不會出錯

是否習慣用格線系統?

我個人通常是有使用 Bootstrap 就會用 但基本上會看設計稿需求
平常手刻不會刻意製作 但不排斥 也有嘗試手寫過格線系統
參考文章 - 格線佈局的概念

通常如何解決跨瀏覽器問題?

使用 Sass 編譯 + CSS Reset
參考文章 - 我其實沒研究過這問題

display 有哪些值

none、inline、inline-block、block、inline-flex、flex、table、inline-grid、grid、unset
參考文章 - 其他我不知道,但還有好多

兩種 CSS Reset 差異

Normalize 保留一些常用的預設樣式 如列表 標題等
Reset 把所有瀏覽器預設樣式都清空了
參考文章

描述 svg gif png 差異與使用時機:

PNG:有效減少圖片檔案大小以及保留照片透明元素,圓角圖片就很適合使用。
SVG:向量檔,縮放不失真,用在 logo 很方便。
GIF:可以製作小型動態圖片檔案,適用於色彩簡單的檔案,也有支援透明背景。
參考文章

rem 是什麼

rem 是指 root 層級的文字大小,即 html 標籤的大小,通常是 16px ,可以通過設定 html 的 font-size 影響到所有使用 rem 單位的尺寸。
參考文章

!important 使用時機

用於強制性覆蓋樣式,因為 important 的權重在 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 多種居中方法

原生 JS Todolist

題目要求:
可以新增項目
完成的項目會有刪除線
可以刪除單個項目
網頁重整也能夠保存資料(使用 cookie or localstorage)

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 用法

原生 JS 做出 99乘法表

function test() { for (var i = 1; i < 10; i++) { for (var j = 1; j < 10; j++) { console.log(`${i} * ${j} = ${i * j}`); } } } test();

用 AJAX 撈取前十筆資料

// 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); } }) });