JavaScript 前端面試
progressive enhancement
從最基本的功能出發,在保證系統在任何環境中的可用性的基礎上,逐步增加功能及提高使用者經驗。
graceful degradation
在一開始建構一個系統或網站時,就針對最新、最完善的環境來設計,然後針對其它環境進行測時與修復。使用這個方案時,我們首先會挑選一個較完善的平台完成所有的功能和經驗,然後再針對無法支援所有功能的平台或環境撰寫候選方案,讓那些較舊的平台不致於無法使用主要的功能。
Flash of Unstyled Content (FOUC)
網頁的內容已經讀取、顯示,但樣式尚未載入完成的那一瞬間。
Solution
<html>
<head>
<style>
body {
display: none;
}
</style>
</head>
<body>
<script>
window.onload(() => {
//拿掉一開始設定的 display: none
})
</script>
</body>
</html>
this 如何作用
const a = {
whoIsThis() {
console.log(this);
}
}
a.whoIsThis() // a;
const b = {};
b.whoIsThis = a.whoIsThis;
b.whoIsThis() // b;
prototype 如何運作
當 function 創建時,JavaScript Engine 會為其增加一 prototype Object 屬性, 而 prototype 中有一 constructor 屬性, 指向 function。
function somethingAboutPrototype () {};
console.log(typeof somethingAboutPrototype.prototype) // object;
console.log(somethingAboutPrototype.prototype.constructor) // somethingAboutPrototype;
console.log(somethingAboutPrototype.prototype.constructor === somethingAboutPrototype) // true
function Animal () {
this.meterPerSecond = 0;
}
Animal.prototyp.move = function (time) {
console.log(`move ${this.meterPerSecond * time}`);
}
小知識
所有東西其實都是繼承自 Object
如何 new
以 Animal 為例:
const a = new Animal();
// Equal To
const a = new Object();
a.__proto__ = Animal.prototype;
Animal.call(a);
原型鍊
以 a 為例:呼叫 a.move 會先搜尋 a 身上是否有 move 存在,如果沒有則搜尋 a.__proto__.move 是否存在。
AMD vs CommonJs vs ES6
CommonJs
sync
// root/somemodule.js
exports = {
hello() {
console.log("hello");
}
}
// root/index.js
const SomeModule = import("./somemodule");
SomeModule.hello();
NodeJs
sync
// root/somemodule.js
module.exports = {
hello() {
console.log("hello");
}
}
// root/index.js
const SomeModule = import("./somemodule");
SomeModule.hello();
AMD (Asynchronous Module Definition)
async
// root/somemodule.js
define(() => {
return {
hello() {
console.log("hello");
}
}
});
// root/index.js
require(["/somemodule"], (SomeModule) => {
SomeModule.hello();
})
ES6
async
// root/somemodule.js
export hello() {
console.log("hello");
}
// root/index.js
import SomeModule from "./somemodule";
SomeModule.hello();
IIFE (Immediately Invoked Function Expression)
定義完馬上就執行的 function
undefined null undeclared
undefined
- 未定義、未賦值
- 沒有定義
return值的 functionreturn undefined
null
- 沒有值 (often retrieved in a place where an object can be expected but no object is relevant. )
NaN
- Not a Number
undeclared
- 語法錯誤,表示變數尚未被宣告
Closure
function generateHello () {
// every variables undeclared in this scope can only accessed in returned function
const dialog = `Hello!`;
return function hello (name) {
// dialog can only access in hello function;
console.log(dialog + name + "!!");
}
}
const hello = generateHello("Albert") // Hello!Albert!
generateHello.dialog // variable dialog is undeclared
dialog // variable dialog is undeclared
Native Object vs. Host Object
Native Object
ECMAScript 的實作,例如 Math、Date
Host Object
由運行環境提供的 Object 以完整執行 ECMAScript,例如 window
Document.write()
把參數中的字符串視為 DOM 插入文件中。
呼叫 Document.write() 時,會自動呼叫 Document.open();如果是在已經呼叫完 Document.close() 時呼叫 Document.write(),則文件中已存在的所有內容將被清除。
常規的呼叫流程:
document.open();
document.write("<h1>HaHa</h1");
document.close();
用在何時?
載入第三方的 script,例如 Google Analyst。因為使用 document.write 插入的 <script/> 不會影響 (block) web 的載入。
Feature Detection, Feature inference, UA String
Feature Detection
檢查 Browser 是否支援某功能。
if ("geolocation" in navigator) {
navigator.geolocation.getCurrentPosition(function(position) {
// show the location on a map, perhaps using the Google Maps API
});
} else {
// Give the user a choice of static maps instead perhaps
}
Feature inference
如果 A 存在,則 B 應該也存在。
UA String
navigator.userAgent 為字符串,說明當前的環境,例如 safari 10.2。但有些 Browser 騙人,給出不符合當前環境的值。
Ajax (Asynchronous JavaScript and XML)
在不重新整理整個頁面的情況下與 Server 互動,取得需要的資料。
-
使用 XMLHttpRequest Object 與 Server 互動:
const httpRequest = XMLHttpRequest(); /** * @param {string} method of HTTP request * @param {string} url * @param {boolean} asyc */ httpRequest.open("GET", "url", true); /** * @param {any?} arg */ httpRequest.send(); -
如果要 "POST",必須設定 MIME Header:
httpRequest.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); -
與 Server 回傳值互動:
httpRequest.onreadystatechange = function () { if (httpRequest.readyState === XMLHttpRequest.DONE) { if (httpRequest.status === 200) { console.log(httpRequest.responseText || httpRequest.responseXML); } } } -
CORS (Cross-Origin Resource Sharing):
- 不同 domain、http / https、port 的資源不能存取。
- Frontend 可以發起 Request、但無法透過 JavaScript 去存取 Response。
- 如果要順利存取資源,則 Server 必須在 Header 中加上
Access-Control-Allow-Origin。- 可以用 JSONP 跳過。
- 非簡易 method 需要額外的 Preflighted Request。
- 簡易 method:GET、HEAD、POST。
JSONP (Json with Padding)
html 中的 <script> 是同源政策的例外(也就是可以獲取不同 Domain 的內容)。
- 以此方式獲得的資料在 Browser 是作為 JavaScript 處理。
- 只能用 GET
// In HTML: 通常視需求動態插入 document
<script type="text/javascript" src="url/callback=parseResponse"></script>
// In Javascript
function parseResponse(data) {
console.log(data);
}
Hoisting
JavaScript 在執行前會先將宣告放入記憶體。
hello(); // 宣告在後卻可以正常執行
function hello () {
console.log("Hello!");
}
== vs. === vs. Object.is()
== Abstract Equality Comparison
-
不同類型:
false -
new String("0") === "0":false -
-0 === +0:true
=== Strict Equality Comparison
null == undefined:true0 == "0":truenew String("0") == "0":true
Object.is()
Object.is('foo', 'foo'); // true
Object.is(window, window); // true
Object.is('foo', 'bar'); // false
Object.is([], []); // false
var foo = { a: 1 };
var bar = { a: 1 };
Object.is(foo, foo); // true
Object.is(foo, bar); // false
Object.is(null, null); // true
// Special Cases
Object.is(0, -0); // false
Object.is(-0, -0); // true
Object.is(NaN, 0/0); // true
Same-Origin Policy
document, script 只能存取相同來源的資源
- 同 domain 視為 same-origin
- 同 domian 不同 port 視為 different-origin
SEO for Singal Page Application
Server Side Render
Immutable, Mutable
- Primitive Type 都是 Immutable, 數值被修改時會在記憶體指派個新位置。
- Reference Type 都是 Mutable。
- Object 中的 property 如果是使用
Object.defineProperty()添加的,預設為 immutable - Immutable 可以:
- 降低記憶體使用量
- 改善效能
- Thread Safty (但 JavaScript 為 Single Thread)
Event Loop
- 分成 Stack 跟 Queue
- Step 1 -- 執行 Main thread。
- Step 2 -- 呼叫 method 後堆到 Stack 中,並執行 method 中的邏輯;執行結束移出 Stack。
- Step 3 -- Stack 都跑完+Main thread 執行完後跑最早放進的 Queue,回到 Step 1。
- 堆到 Stack:
- Function call
new Promise()的 callback
- 堆到 Queue:Main Thread 跑完後會馬上接著跑 MicroTask;MicroTask 跑完後才會再早放進把 MacroTask 的 queue 拿出來執行。
- MacroTask:
Event Trigger、setTimeout、setInterval、setImmediate、requestAnimationFrame - MicroTask:
Promise.prototype.then、Promise.prototype.catch、Object.observe、MutationObserver
- MacroTask:
Block UI with MicroTask
由於 MicroTask 是緊接著 Main Thread 且先於 MacroTask 的,因此會造成 UI Block。
JavaScript 直譯器
graph LR;
subgraph Main Thread
o1[原始碼]
end
subgraph 直譯器
c1(語法基本單元化)
c2(抽象結構樹 AST)
c3(程式生成)
end
subgraph 執行
e1(預編譯)
e2(執行)
end
o1-->c1
c1-->c2
c2-->c3
c3-->e1
e1-->e2
預編譯
-
語法基本單元化 Tokenzing:
// before var a = 1 // after ['var', 'a', '=', 'b'] -
抽象結構樹 AST Abstract Syntax Tree:
// before var a = 1 // after { type: 'Program', sourceType: 'script', body: [ { type: 'VariableDeclarator', kind: 'var', id: { type: 'Indicator', name: 'a' }, init: { type: 'Literal', value: 1, raw: '1' } } ] }
執行
- 預編譯:依據 AST 編譯 JS,先宣告 variables、再宣告 methods。
- 執行:逐行執行預編譯的結果。
Arrow Function VS Function
| Function | Arrow Function | |
|---|---|---|
| scope | 有;this 跟著外層最近一層 object | 無;this 跟著宣告時的 scope |
| arguments | 有 | 無 |