本文參考文章《一名【合格】前端工程師的自檢清單》, 並對其中的部分題目進行了解答,如有遺漏或錯誤之處望你們指出糾正,共同進步。(點擊題目展開答案!) javascript
此文章 Markdown 源文件地址:github.com/zxpsuper/bl…css
前端工程師吃飯的傢伙,深度、廣度同樣都不能差。html
JavaScript中的每個值都有它本身的類型,JavaScript規定了七種語言類型,他們是:前端
Undefined Null Boolean String Number Symbol Object
vue
對象數據被存儲於堆中 (如對象、數組、函數等,它們是經過拷貝和new出來的)。html5
引用類型的數據的地址指針是存儲於棧中的,當咱們想要訪問引用類型的值的時候,須要先從棧中得到對象的地址指針,而後,在經過地址指針找到堆中的所須要的數據。java
ES6
引入了一種新的原始數據類型 Symbol
,表示獨一無二的值。node
symbol
類型的 key
不能被 Object.keys
和 for..of
循環枚舉。所以可看成私有變量使用。react
let mySymbol = Symbol('key');
// 第一種寫法
let a = {};
a[mySymbol] = 'Hello!';
// 第二種寫法
let a = {
[mySymbol]: 'Hello!'
};
複製代碼
JavaScript
中的變量分爲基本類型和引用類型:webpack
基本類型: 保存在棧內存中的簡單數據段,它們的值都有固定的大小,保存在棧空間,經過按值訪問
引用類型: 保存在堆內存中的對象,值大小不固定,棧內存中存放的該對象的訪問地址指向堆內存中的對象,JavaScript
不容許直接訪問堆內存中的位置,所以操做對象時,實際操做對象的引用
String(), Number(), Boolean()
裝箱:就是把基本類型轉變爲對應的對象。裝箱分爲隱式和顯示
// 隱式裝箱: 每當讀取一個基本類型的值時,後臺會建立一個該基本類型所對應的對象。
// 在這個基本類型上調用方法,實際上是在這個基本類型對象上調用方法。
// 這個基本類型的對象是臨時的,它只存在於方法調用那一行代碼執行的瞬間,執行方法後馬上被銷燬。
let num=123;
num.toFixed(2); // '123.00'//上方代碼在後臺的真正步驟爲
var c = new Number(123);
c.toFixed(2);
c = null;
// 顯式裝箱: 經過內置對象 Boolean、Object、String 等能夠對基本類型進行顯示裝箱。
var obj = new String('123');
複製代碼
拆箱: 拆箱與裝箱相反,把對象轉變爲基本類型的值。
Number([1]); //1
// 轉換演變:
[1].valueOf(); // [1];
[1].toString(); // '1';Number('1'); //1
複製代碼
JavaScript中的變量分爲基本類型和引用類型:
基本類型: 保存在棧內存中的簡單數據段,它們的值都有固定的大小,保存在棧空間,經過按值訪問
引用類型: 保存在堆內存中的對象,值大小不固定,棧內存中存放的該對象的訪問地址指向堆內存中的對象,JavaScript
不容許直接訪問堆內存中的位置,所以操做對象時,實際操做對象的引用
Number
轉換的值不一樣,Number(null)
輸出爲 0
, Number(undefined)
輸出爲 NaN
null
表示一個值被定義了,可是這個值是空值
undefined
表示缺乏值,即此處應該有值,可是尚未定義
typeof
—— 返回給定變量的數據類型,可能返回以下字符串:'undefined'——Undefined
'boolean'——Boolean
'string'——String
'number'——Number
'symbol'——Symbol
'object'——Object / Null (Null 爲空對象的引用)
'function'——Function
// 對於一些如 error() date() array()沒法判斷,都是顯示object類型
複製代碼
instanceof
檢測 constructor.prototype
是否存在於參數 object
的原型鏈上,是則返回 true
,不是則返回 false
。alert([1,2,3] instanceof Array) // true
alert(new Date() instanceof Date) // true
alert(function(){this.name="22";} instanceof Function) //true
alert(function(){this.name="22";} instanceof function) //false // instanceof 只能用來判斷兩個對象是否屬於實例關係,而不能判斷一個對象實例具體屬於哪一種類型。 複製代碼
constructor
—— 返回對象對應的構造函數。alert({}.constructor === Object); => true
alert([].constructor === Array); => true
alert('abcde'.constructor === String); => true
alert((1).constructor === Number); => true
alert(true.constructor === Boolean); => true
alert(false.constructor === Boolean); => true
alert(function s(){}.constructor === Function); => true
alert(new Date().constructor === Date); => true
alert(new Array().constructor === Array); => true
alert(new Error().constructor === Error); => true
alert(document.constructor === HTMLDocument); => true
alert(window.constructor === Window); => true
alert(Symbol().constructor); => undefined
// null 和 undefined 是無效的對象,沒有 constructor,所以沒法經過這種方式來判斷。
複製代碼
Object.prototype.toString()
默認返回當前對象的 [[Class]]
。這是一個內部屬性,其格式爲 [object Xxx]
,是一個字符串,其中 Xxx
就是對象的類型。Object.prototype.toString.call(new Date);//[object Date]
Object.prototype.toString.call(new String);//[object String]
Object.prototype.toString.call(Math);//[object Math]
Object.prototype.toString.call(undefined);//[object Undefined]
Object.prototype.toString.call(null);//[object Null]
Object.prototype.toString.call('') ; // [object String]
Object.prototype.toString.call(123) ; // [object Number]
Object.prototype.toString.call(true) ; // [object Boolean]
Object.prototype.toString.call(Symbol()); //[object Symbol]
Object.prototype.toString.call(new Function()) ; // [object Function]
Object.prototype.toString.call(new Date()) ; // [object Date]
Object.prototype.toString.call([]) ; // [object Array]
Object.prototype.toString.call(new RegExp()) ; // [object RegExp]
Object.prototype.toString.call(new Error()) ; // [object Error]
Object.prototype.toString.call(document) ; // [object HTMLDocument]
Object.prototype.toString.call(window) ; //[object global] window 是全局對象 global 的引用
// 比較全面
複製代碼
隱式轉換通常說的是 Boolean
的轉換
在 if
語句中,null
,""
,undefinded
, 0
, false
都會被轉化爲 false
通常應用於對接口數據判空時使用
精度丟失緣由,說是 JavaScript
使用了 IEEE 754
規範,二進制儲存十進制的小數時不能完整的表示小數
可以表示的最大數字 Number.MAX_VALUE
等於 1.7976931348623157e+308
,最大安全數字 Number.MAX_SAFE_INTEGER
等於 9007199254740991
避免精度丟失
100
或 1000
,變成整數再運算BigInt
大整數,它能夠表示任意大小的整數,注意只能表示整數,而不受安全整數的限制A. 全部的引用類型(數組、對象、函數),都具備對象特性,便可自由擴展屬性;
B. 全部的引用類型(數組、對象、函數),都有一個`__proto__`屬性(隱式原型),屬性值是一個普通的對象;
C. 全部的函數,都具備一個 `prototype`(顯式原型),屬性值也是一個普通對象;
D. 全部的引用類型(數組、對象、函數),其隱式原型指向其構造函數的顯式原型;`(obj._proto_ === Object.prototype)`;
E. 當試圖獲得一個對象的某個屬性時,若是這個對象自己沒有這個屬性,那麼會去它的 `__proto__` (即它的構造函數的 `prototype`)中去尋找;
複製代碼
簡單說就是判斷實例對象的__proto__
是否是強等於對象的prototype
屬性,若是不是繼續往原型鏈上找,直到 __proto__
爲 null
爲止。
function instanceOf(obj, object) {//obj 表示實例對象,object 表示對象
var O = object.prototype;
obj = obj.__proto__;
while (true) {
if (obj === null)
return false;
if (O === obj) // 這裏重點:當 O 嚴格等於 obj 時,返回 true
return true;
obj = obj.__proto__;
}
}
複製代碼
執行上下文 就是當前 JavaScript
代碼被解析和執行時所在環境的抽象概念, JavaScript
中運行任何的代碼都是在執行上下文中運行。
執行上下文總共有三種類型:全局執行上下文, 函數執行上下文, Eval
函數執行上下文
執行棧,在其餘編程語言中也被叫作調用棧,具備 LIFO(後進先出)結構,用於存儲在代碼執行期間建立的全部執行上下文。
詳情請點擊:《繼承的幾種實現方式》
new
一個對象的詳細過程:function Test() {}
const test = new Test();
複製代碼
建立一個對象 const obj = {}
設置新對象的 constructor
屬性爲構造函數的名稱,設置新對象的__proto__
屬性指向構造函數的 prototype
對象
obj.constructor = Test;
obj.__proto__ = Test.prototype;
複製代碼
使用新對象調用函數,函數中的 this 被指向新實例對象 Test.call(obj)
將初始化完畢的新對象地址,保存到等號左邊的變量中
new
操做符function myNew(Obj,...args){
var obj = Object.create(Obj.prototype);//使用指定的原型對象及其屬性去建立一個新的對象
Obj.apply(obj,args); // 綁定 this 到obj, 設置 obj 的屬性
return obj; // 返回實例
}
複製代碼
ES6
類的底層仍是經過構造函數去建立的。// es6 Parent類實現
class Parent {
constructor(name,age){
this.name = name;
this.age = age;
}
speakSomething(){
console.log("I can speek chinese");
}
}
// 轉化爲
var Parent = function () {
function Parent(name, age) {
_classCallCheck(this, Parent); // 判斷實例 Parent instanceof Parent(函數)是否爲true
this.name = name;
this.age = age;
}
// 此方法經過使用 Object.defineProperty 爲 function Parent 的 prototype 添加屬性值
_createClass(Parent, [{
key: "speakSomething",
value: function speakSomething() {
console.log("I can speek chinese");
}
}]);
return Parent;
}();
複製代碼
ES6
的繼承實現//定義子類,繼承父類
class Child extends Parent {
static width = 18
constructor(name,age){
super(name,age);
}
coding(){
console.log("I can code JS");
}
}
// 轉化爲
var Child = function (_Parent) {
_inherits(Child, _Parent);
function Child(name, age) {
_classCallCheck(this, Child);
return _possibleConstructorReturn(this, (Child.__proto__ || Object.getPrototypeOf(Child)).call(this, name, age));
}
_createClass(Child, [{
key: "coding",
value: function coding() {
console.log("I can code JS");
}
}]);
return Child;
}(Parent);
複製代碼
這裏其實就是多了一個 _inherits(Child, _Parent);
方法,實現瞭如下功能,具體可看文章《ES6類以及繼承的實現原理》
//實現的結果是:
subClass.prototype.__proto__ = superClass.prototype
subClass.__proto__ = superClass // 實現靜態屬性的繼承
複製代碼
詞法做用域也稱靜態做用域,javascript
採用靜態做用域
靜態做用域 —— 函數的做用域基於函數建立的位置。
動態做用域 —— 函數的做用域基於函數的使用位置。
var value = 1;
function foo() {
console.log(value);
}
function bar() {
var value = 2;
foo();
}
bar(); // 輸出 1 。JavaScript 採用的是詞法做用域,也稱爲靜態做用域。相同的,動態做用域此代碼應該輸出 2
複製代碼
做用域(scope)就是變量訪問規則的有效範圍。
在 JavaScript
中全局變量的做用域是全局的,在代碼的任何地方都是有定義的。然而函數的參數和局部變量只在函數體內有定義。另外局部變量的優先級要高於同名的全局變量,也就是說當局部變量與全局變量重名時,局部變量會覆蓋全局變量。
原理:閉包就是可以讀取其餘函數內部變量的函數。因爲在Javascript語言中,只有函數內部的子函數才能讀取局部變量,所以能夠把閉包簡單理解成"定義在一個函數內部的函數"。
因此,在本質上,閉包就是將函數內部和函數外部鏈接起來的一座橋樑。
做用:閉包能夠用在許多地方。它的最大用處有兩個,一個是前面提到的能夠讀取函數內部的變量,另外一個就是讓這些變量的值始終保持在內存中。
應用:1. 匿名自執行函數 2. 結果緩存 3. 封裝局部變量
堆棧溢出 的產生是因爲過多的函數調用,致使調用堆棧沒法容納這些調用的返回地址,通常在遞歸中產生。堆棧溢出極可能由無限遞歸(Infinite recursion)產生,但也可能僅僅是過多的堆棧層級.
參考連接:《內存泄漏與避免》
在 try
語句中,在執行 return
語句時,要返回的結果已經準備好了,就在此時,程序轉到 finally
執行了。
在轉去以前,try
中先把要返回的結果存放到局部變量中去,執行完 finally
以後,在從中取出返回結果。
所以,即便finally
中對返回的結果進行了改變,可是不會影響返回結果。
它應該使用棧保存返回值。
JavaScript
如何實現異步編程:
callback
(回調函數) 回調函數表明着,當某個任務處理完,而後須要作的事。好比讀取文件,鏈接數據庫,等文件準備好,或數據庫鏈接成功執行編寫的回調函數,又好比像一些動畫處理,當動畫走完,而後執行回調。
發佈訂閱模式 顧名思義,即是先訂閱了事件,有人一發布事件你就知道了,接着執行後面的操做。
Promise
Promise
,簡單說就是一個容器,裏面保存着某個將來纔會結束的事件的結果,相比回調函數,Promise
提供統一的 API
,各類異步操做均可以用一樣的方法進行處理。
Generator
(生成器)函數 Generator
函數是 ES6
提供的一種異步編程解決方案,其行爲相似於狀態機。
async/await
async/await
本質上仍是基於 Generator
函數,能夠說是 Generator
函數的語法糖,async
就至關於以前寫的run函數(執行Generator
函數的函數),而 await
就至關於 yield
,只不過 await
表達式後面只能跟着 Promise
對象,若是不是 Promise
對象的話,會經過 Promise.resolve
方法使之變成 Promise
對象。async
修飾 function
,其返回一個 Promise
對象。
宏任務: setTimeout,setInterval,setImmediate (Node獨有),requestAnimationFrame (瀏覽器獨有),I/O,UI rendering (瀏覽器獨有)
微任務: process.nextTick (Node獨有),Promise,Object.observe,MutationObserver
// 執行順序,先微隊列,後宏隊列。
console.log(1);
setTimeout(() => {
console.log(2);
setTimeout(() => {
console.log(8);
})
Promise.resolve().then(() => {
console.log(3)
});
});
new Promise((resolve, reject) => {
console.log(4)
setTimeout(() => {
console.log(10);
})
resolve()
}).then(() => {
console.log(5);
Promise.resolve().then(() => {
console.log(11)
});
setTimeout(() => {
console.log(13);
})
})
setTimeout(() => {
Promise.resolve().then(() => {
console.log(9)
});
console.log(6);
setTimeout(() => {
console.log(12);
})
})
console.log(7);
複製代碼
從頭到尾執行一次代碼,根據上面分類規則分至不一樣隊列, new promise( function )
也是當即執行。setTimeout
的回調函數屬於宏隊列(macrotask)
,resolve
的回調函數屬於微隊列
// 棧區(stack)
console.log(1);
console.log(4);
console.log(7);
複製代碼
// 宏隊列
() => {
console.log(2);
setTimeout(() => {
console.log(8);
})
Promise.resolve().then(() => {
console.log(3)
});
}
() => {
console.log(10);
}
() => {
Promise.resolve().then(() => {
console.log(9)
});
console.log(6);
setTimeout(() => {
console.log(12);
})
}
複製代碼
// 微隊列
() => {
console.log(5);
Promise.resolve().then(() => {
console.log(11)
});
setTimeout(() => {
console.log(13);
})
}
複製代碼
優先執行微隊列,微隊列執行過程當中產生的微隊列和宏隊列置於隊列末尾排序執行,而宏隊列產生的微隊列和宏隊列於新的隊列中等待。。
執行微隊列:(分類)
// 棧區(stack)
console.log(1);
console.log(4);
console.log(7);
//////////
console.log(5);
複製代碼
// 微隊列
() => {
console.log(11)
});
複製代碼
// 宏隊列
() => {
console.log(2);
setTimeout(() => {
console.log(8);
})
Promise.resolve().then(() => {
console.log(3)
});
}
() => {
console.log(10);
}
() => {
Promise.resolve().then(() => {
console.log(9)
});
console.log(6);
setTimeout(() => {
console.log(12);
})
}
() => {
console.log(13);
}
複製代碼
此時新增了一個微隊列console.log(11)
,由於是微隊列產生的,繼續執行:
// 棧區(stack)
console.log(1);
console.log(4);
console.log(7);
//////////
console.log(5);
/////////
console.log(11)
複製代碼
// 微隊列-空
複製代碼
// 宏隊列
() => {
console.log(2);
setTimeout(() => {
console.log(8);
})
Promise.resolve().then(() => {
console.log(3)
});
}
() => {
console.log(10);
}
() => {
Promise.resolve().then(() => {
console.log(9)
});
console.log(6);
setTimeout(() => {
console.log(12);
})
}
() => {
console.log(13);
}
複製代碼
執行完微隊列後執行宏隊列:
// 棧區(stack)
console.log(1);
console.log(4);
console.log(7);
//////////
console.log(5);
/////////
console.log(11);
/////////
console.log(2);
console.log(10);
console.log(6);
console.log(13);
複製代碼
// 微隊列
() => {
console.log(3)
}
() => {
console.log(9)
}
複製代碼
// 宏隊列
() => {
console.log(8);
}
() => {
console.log(12);
}
複製代碼
接下來執行微隊列後宏隊列,即:
// 棧區(stack)
console.log(1);
console.log(4);
console.log(7);
//////////
console.log(5);
/////////
console.log(11);
/////////
console.log(2);
console.log(10);
console.log(6);
console.log(13);
////////
console.log(3)
console.log(9)
////////
console.log(8);
console.log(12);
複製代碼
// 一個 promise 的 function
function delay(time) {
return new Promise((resolve, reject) => {
console.log(`wait ${time}s`)
setTimeout(() => {
console.log('execute');
resolve()
}, time * 1000)
})
}
const arr = [3, 4, 5];
複製代碼
reduce
arr.reduce((s, v) => {
return s.then(() => delay(v))
}, Promise.resolve())
複製代碼
async
+ 循環 + await
(
async function () {
for (const v of arr) {
await delay(v)
}
}
)()
複製代碼
let p = Promise.resolve()
for (const i of arr) {
p = p.then(() => delay(i))
}
複製代碼
function dispatch(i, p = Promise.resolve()) {
if (!arr[i]) return Promise.resolve()
return p.then(() => dispatch(i + 1, delay(arr[i])))
}
dispatch(0)
複製代碼
window.requestAnimationFrame
和 document.createDocumentFragment()
實現, 可參考文章【如何解決頁面加載海量數據而不凍結前端UI】DOM
結構最簡單化。可參考文章【大數據如何在前端流暢展現】,不過他的 Demo
有點問題.ECMAScript
是 JavaScript
的規範,JavaScript
是 ECMAScript
的實現。
在使用 setInterval
方法時,每一次啓動都須要對 setInterval
方法返回的值作一個判斷,判斷是不是空值,若不是空值,則要中止定時器並將值設爲空,再從新啓動,若是不進行判斷並賦值,有可能會形成計時器循環調用,在同等的時間內同時執行調用的代碼,並會隨着代碼的運行時間增長而增長,致使功能沒法實現,甚至佔用過多資源而卡死奔潰。所以在每一次使用setInterval方法時,都須要進行一次判斷。
let timer = setInterval(func, 1000)
// 在其餘地方再次用到setInterval(func, 1000)
if (timer !== null) {
clearInterval(timer)
timer = null
}
timer = setInterval(func, 1000)
複製代碼
setIntervalFunc = () =>{
console.log(1) //使用遞歸
setTimeout(setIntervalFunc, 1000);
};
setInterval()
複製代碼
郵箱校驗:
function isEmail(emailStr) {
return /^[a-zA-Z0-9]+([._-]*[a-zA-Z0-9]*)*@[a-zA-Z0-9]+.[a-zA-Z0-9{2,5}$]/.test(emailStr);
}
複製代碼
URL解析:
function isUrl(urlStr) {
return /^(?:http(s)?:\/\/)?[\w.-]+(?:\.[\w\.-]+)+[\w\-\._~:/?#[\]@!\$&'\*\+,;=.%]+$/.test(value)
}
複製代碼
數組去重:
// set結構
let arr = [1, 1, 2, 2, 3, 3]
arr2 = [...new Set(arr)]
console.log(arr2) // [1,2,3]
// Object.keys(), 利用屬性 key 的惟一性
let arrObj = [1, 1, 2, 2, 3, 3]
arrObj2 = {}
for (i in arrObj) {
arrObj2[arrObj[i]] = true
}
let arrObj3 = Object.keys(arrObj2)
console.log(arrObj3)
// 利用 indexOf() 查詢數組內是否已經包含該元素
var arrIndexOf = ['a','c','b','d','a','b']
var arrIndexOf2 = [];
for(var i = 0;i<arrIndexOf.length;i++){
if(arrIndexOf2.indexOf(arrIndexOf[i])<0){
arrIndexOf2.push(arrIndexOf[i]);
}
}
console.log(arrIndexOf2)// ['a', 'c', 'b', 'd']
複製代碼
語義化標籤:<header> <footer> <nav> <section> <article> <aside> 等
標籤分類:
文檔標籤(10 個):<html>、<head>、<body>、<title>、<meta>、<base> 、<style>、<link>、<script>、<noscript>
表格標籤(10 個):<table>、<thead>、<tbody>、<tfoot>、<tr>、<td>、<th> 、<col>、<colgroup>、<caption>
表單標籤(10 個):<from>、<input>、<textarea>、<button>、<select> 、<optgroup>、<option>、<label>、<fieldset>、<legend>
列表標籤(6個):<ul>、<ol>、<li>、<dl>、<dt>、<dd>
多媒體標籤(5個):<img>、<map>、<area>、<object>、<param>
文章標籤:<h1> - <h6> 、<p>、<br>、<span>、<bdo>、<pre>、<acronym>、<abbr>、<blockquote>、<q>、<ins>、<del>、<address>
字體樣式標籤:<tt>、<i>、<b>、<big>、<small>、<em>、<strong>、<dfn>、<code>、<samp>、<kbd>、<var>、<cite>、<sup>、<sub>
在不一樣的場景使用不一樣的標籤,更能顯示清晰的結構。
<head>
標籤用於定義文檔的頭部,它是全部頭部元素的容器。<head>
中的元素能夠引用腳本、指示瀏覽器在哪裏找到樣式表、提供元信息等等。能夠包含的標籤有: <base>, <link>, <meta>, <script>, <style>, 以及 <title>。
<title>
定義文檔的標題,它是 head 部分中惟一必需的元素。
<meta>
元素可提供有關頁面的元信息(meta-information),好比針對搜索引擎和更新頻度的描述和關鍵詞。使用方法參考【meta標籤詳解】
w3c
盒子模型的範圍包括 margin、border、padding、content
,而且 content
部分不包含其餘部分ie
盒子模型的範圍也包括 margin、border、padding、content
,和標準 w3c
盒子模型不一樣的是:ie
盒子模型的 content
部分包含了 border
和 pading
。不一樣級別優先級: !important > 行內樣式 > ID選擇器 > 類選擇器 > 元素 > 通配符 > 繼承 > 瀏覽器默認屬性
相同級別優先級: 內聯(行內)樣式 > 內部樣式表 > 外部樣式表 > 導入樣式(@import)。
可繼承屬性:
字體系列屬性, font-family, font-weight, font-size, font-style...
文本系列屬性, text-indent, text-align, line-heigh, word-spacing, letter-spacing, text-transform, color
元素可見性:visibility, 光標屬性:cursor
複製代碼
AT rule:
1、什麼是 at-rules
eg:@charset "utf-8";
at-rule
是 CSS
樣式聲明,以 @
開頭,緊跟着是標識符(charset)
,最後以分號(;)結尾。
2、幾個 at-rules
一、@charset
—定義被樣式表使用的字符集
二、@import
——告訴 CSS
引擎包含外部的 CSS
樣式表
三、@namespace
——告訴 CSS
引擎全部的內容都必須考慮使用 XML
命名空間前綴
四、嵌套at-rules
(1)@media
——條件組規則。若是設備符合標準定義的條件查詢則使用該媒體
(2)@font-face
——描述了一個將從外部下載的字體
(3)@keyframes
——描述了中間步驟在 CSS
動畫的序列
(4)@page
——描述了文件的佈局變化,當要打印文檔時。
(5)@supports
——條件組規則,若是瀏覽器知足給出的規則,則把它應用到內容中
(6)@document
——條件組規則,若是被用到文檔的 CSS
樣式表知足了給定的標準,那麼將被應用到全部的內容中。
僞類:用於向某些選擇器添加特殊的效果. :active, :focus, :link, :visited, :hover, :first-child
僞元素:用於將特殊的效果添加到某些選擇器. :before, :after, :first-line, :first-letter
僞類和僞元素的根本區別在於:它們是否創造了新的元素(抽象)。從咱們模仿其意義的角度來看,若是須要添加新元素加以標識的,就是僞元素,反之,若是隻須要在既有元素上添加類別的,就是僞類。
HTML 文檔流的排版規則: 把元素按從上而下,從左到右的順序默認排列。不在一行的元素從上而下,在一行的從左到右排列。
CSS 幾種定位的規則:
static
定位(普通流定位)float
定位(浮動定位), 有兩個取值:left
(左浮動)和 right
(右浮動)。浮動元素會在沒有浮動元素的上方,效果上看是遮擋住了沒有浮動的元素,有float樣式規則的元素是脫離文檔流的,它的父元素的高度並不能有它撐開。
relative
定位(相對定位), 相對本元素的左上角進行定位,top,left,bottom,right
均可以有值。雖然通過定位後,位置可能會移動,可是本元素並無脫離文檔流,還佔有原來的頁面空間。absolute
定位(絕對定位), 相對於祖代中有 relative
(相對定位)而且離本元素層級關係上是最近的元素的左上角進行定位,若是在祖代元素中沒有有 relative
定位的,就默認相對於body
進行定位。絕對定位是脫離文檔流的fixed
定位(固定定位),這種定位方式是相對於整個文檔的,只需設置它相對於各個方向的偏移值,就能夠將該元素固定在頁面固定的位置,一般用來顯示一些提示信息,脫離文檔流;雪碧圖實現原理: CSS Sprite
,是一種 CSS
圖像合併技術,該方法是將小圖標和背景圖像合併到一張圖片上,而後利用 css
的背景定位來顯示須要顯示的圖片部分。
參考文章: 【CSS實現水平垂直居中的1010種方式】
BFC(Block formatting context)
直譯爲"塊級格式化上下文"。它是一個獨立的渲染區域,只有塊級元素參與, 它規定了內部的塊級元素如何佈局,而且與這個區域外部絕不相干。
BCF 能夠解決的問題:浮動定位,消除外邊距摺疊,清除浮動,自適應多欄佈局
BFC的建立:根元素或包含根元素的元素,浮動元素(float
不爲none
),絕對定位元素( position
爲 absolute
或者 fixed
),display
爲 inline-block,table-cell,table-caption,overflow
值不爲 visible
,彈性元素( flex
佈局),網格元素( grid
佈局)
CSS模塊化方案: 文件細化,命名約定,CSS Modules , css in js
如何防止 CSS 阻塞渲染:
CSS
是阻塞渲染的資源。須要將它儘早、儘快地下載到客戶端,以便縮短首次渲染的時間。
有一些 CSS
樣式只在特定條件下(例如顯示網頁或將網頁投影到大型顯示器上時)使用,咱們能夠經過 CSS
「媒體類型」和「媒體查詢」來解決這類用例:
<link href="print.css" rel="stylesheet" media="print">
<link href="other.css" rel="stylesheet" media="(min-width: 40em)">
首屏相關的關鍵 `CSS` 使用阻塞渲染的方式加載,全部的非關鍵 `CSS` 在首屏渲染完成後加載。
複製代碼
參考文章:【瀑布流佈局的實現】
// 圓形
.circle{
width:100px;
height:100px;
border-radius:50%;
background:blue;
}
// 三角形
.triangle {
width: 0;
height: 0;
border: 50px solid blue;
/* 經過改變邊框顏色,能夠改變三角形的方向 */
border-color: blue transparent transparent transparent;
}
// 扇形,扇形是由一個圓形和一個矩形進行組合獲得的,用矩形遮住圓形的一部分就造成了扇形。
.sector {
width: 142px;
height: 142px;
background: #fff;
border-radius: 50%;
background-image: linear-gradient(to right, transparent 50%, #655 0);
}
.sector::before {
content: '';
display: block;
margin-left: 50%;
height: 100%;
width: 100%;
background-color: inherit;
transform-origin: left;
/*調整角度,改變扇形大小*/
transform: rotate(230deg);
}
// 菱形
.rhombus {
width: 200px;
height: 200px;
transform: rotateZ(45deg) skew(30deg, 30deg);
background: blue;
}
複製代碼
CSS3 新增了 transition-timing-function 屬性,它的取值就能夠設置爲一個三次貝塞爾曲線方程。
聖盃佈局, 兩邊頂寬,中間自適應的三欄佈局。
關於編譯原理,不須要理解很是深刻,可是最基本的原理和概念必定要懂,這對於學習一門編程語言很是重要
代碼就是程序員用開發工具所支持的語言寫出來的源文件,是一組由字符、符號或信號碼元以離散形式表示信息的明確的規則體系。
計算機源代碼最終目的是將人類可讀文本翻譯成爲計算機可執行的二進制指令,這種過程叫編譯,它由經過編譯器完成。
parseInt(str, radix)
將一個 radix 進制的 str 轉化爲十進制,parseInt('23',8) // 19
,將八進制的‘23’轉化爲10進制的‘19’
number.toString(radix)
將一個數字轉化爲 radix 進制的數字字符串
0x11.toString(8) // 21
0x11.toString(10) // 17
0x11.toString(2) // 10001
複製代碼
協議,網絡協議的簡稱,網絡協議是通訊計算機雙方必須共同聽從的一組約定。如怎麼樣創建鏈接、怎麼樣互相識別等。只有遵照這個約定,計算機之間才能相互通訊交流。它的三要素是:語法、語義、時序。
TCP/IP 網絡協議族的構成: TCP/IP
協議是 Internet
最基本的協議。由傳輸層的 TCP
協議和網絡層的 IP
協議組成。
TCP
負責發現傳輸的問題,一有問題就發出信號,要求從新傳輸,直到全部數據安全正確地傳輸到目的地。而 IP
是給因特網的每一臺聯網設備規定一個地址。
應用層
應用層決定了向用戶提供應該服務時通訊的活動。
TCP/IP
協議族內預存了各種通用的應用服務。好比,FTP
(File Transfer Protocol,文件傳輸協議)和 DNS
(Domain Name System,域名系統)服務就是其中的兩類。HTTP
協議也處於該層。
傳輸層
傳輸層對上層應用層,提供處於網絡鏈接中兩臺計算機之間的數據傳輸。
在傳輸層有兩個性質不一樣的協議:TCP
(Transmission Control Protocol,傳輸控制協議)和 UDP
(User Data Protocol,用戶數據報協議)。
網絡層(又名網絡互連層)
網絡層用來處理在網絡上流動的數據包。數據包是網絡傳輸的最小數據單位。該層規定了經過怎樣的路徑(所謂的傳輸路線)到達對方計算機,並把數據包傳送給對方。
與對方計算機之間經過多臺計算機或網絡設備進行傳輸時,網絡層所起的所用就是在衆多的選項內選擇一條傳輸路線。
鏈路層(又名數據鏈路層,網絡接口層)
用來處理鏈接網絡的硬件部分。包括控制操做系統、硬件的設備驅動、NIC(Network Interface Card,網絡適配器,即網卡),及光纖等物理可見部分(還包括鏈接器等一切傳輸媒介)。硬件上的範疇均在鏈路層的做用範圍以內。
三次握手和四次揮手詳細原理:
三次握手:避免鏈接請求的數據包丟失,數據傳輸過程由於網絡併發量很大在某結點被阻塞
四次揮手: TCP鏈接是全雙工通道,須要雙向關閉。
參考文章: 【TCP/IP協議族】
TCP
的協議:FTP
(文件傳輸協議)、Telnet
(遠程登陸協議)、SMTP
(簡單郵件傳輸協議)、POP3
(和 SMTP
相對,用於接收郵件)、HTTP
協議等。
TCP
提供可靠的、面向鏈接的數據傳輸服務。使用 TCP
通訊以前,須要進行「三次握手」創建鏈接,通訊結束後還要使用「四次揮手」斷開鏈接。
DNS 的做用:DNS是互聯網的一項服務。它做爲將域名和IP地址相互映射的一個分佈式數據庫,可以令人更方便地訪問互聯網。
DNS解析過程:
一、在瀏覽器中輸入 www.qq.com
域名,操做系統會先檢查本身本地的 hosts
文件是否有這個網址映射關係,若是有,就先調用這個 IP
地址映射,完成域名解析。
二、若是 hosts
裏沒有這個域名的映射,則查找本地 DNS解析器緩存
,是否有這個網址映射關係,若是有,直接返回,完成域名解析。
三、若是 hosts
與 本地DNS解析器緩存
都沒有相應的網址映射關係,首先會找 TCP/ip
參數中設置的 首選DNS服務器
,在此咱們叫它 本地DNS服務器
,此服務器收到查詢時,若是要查詢的域名,包含在本地配置區域資源中,則返回解析結果給客戶機,完成域名解析,此解析具備權威性。
四、若是要查詢的域名,不禁 本地DNS服務器區域解析
,但該服務器已緩存了此網址映射關係,則調用這個 IP
地址映射,完成域名解析,此解析不具備權威性。
五、若是 本地DNS服務器
本地區域文件與緩存解析都失效,則根據 本地DNS服務器
的設置(是否設置轉發器)進行查詢,若是未用轉發模式,本地 DNS
就把請求發至 13
臺根 DNS
,根 DNS
服務器收到請求後會判斷這個域名 (.com)
是誰來受權管理,並會返回一個負責該頂級域名服務器的一個IP
。本地DNS服務器
收到 IP
信息後,將會聯繫負責 .com
域的這臺服務器。這臺負責 .com
域的服務器收到請求後,若是本身沒法解析,它就會找一個管理 .com
域的下一級 DNS
服務器地址(http://qq.com)
給 本地DNS服務器
。當 本地DNS服務器
收到這個地址後,就會找 http://qq.com
域服務器,重複上面的動做,進行查詢,直至找到 www.qq .com
主機。
六、若是用的是轉發模式,此 DNS
服務器就會把請求轉發至上一級 DNS
服務器,由上一級服務器進行解析,上一級服務器若是不能解析,或找根 DNS
或把轉請求轉至上上級,以此循環。無論是 本地DNS服務器
用是是轉發,仍是根提示,最後都是把結果返回給 本地DNS服務器
,由此 DNS
服務器再返回給客戶機。
DNS 優化:減小DNS的請求次數;進行DNS預獲取 。
減小DNS的請求次數————在項目中減小不一樣域名的http請求,儘可能少的域名減小DNS的請求數
DNS預獲取————減小用戶的等待時間,提高用戶體驗 。
默認狀況下瀏覽器會對頁面中和當前域名(正在瀏覽網頁的域名)不在同一個域的域名進行預獲取,而且緩存結果,這就是隱式的 DNS Prefetch。若是想對頁面中沒有出現的域進行預獲取,那麼就要使用顯示的 DNS Prefetch 了。
<meta http-equiv="x-dns-prefetch-control" content="on">
<link rel="dns-prefetch" href="//www.itechzero.com">
<link rel="dns-prefetch" href="//api.share.baidu.com">
<link rel="dns-prefetch" href="//bdimg.share.baidu.com">
複製代碼
CDN
的全稱是 Content Delivery Network
,即內容分發網絡。其基本思路是儘量避開互聯網上有可能影響數據傳輸速度和穩定性的瓶頸和環節,使內容傳輸的更快、更穩定。
通常狀況下,瀏覽器都會對單個域名下的併發請求數(文件加載)進行限制,一般最多有 4
個,那麼第 5
個加載項將會被阻塞,直到前面的某一個文件加載完畢。由於 CDN
文件是存放在不一樣區域(不一樣 IP
)的,因此對瀏覽器來講是能夠同時加載頁面所需的全部文件(遠不止 4
個),從而提升頁面加載速度。
二、文件可能已經被加載過並保存有緩存
一些通用的 js
庫或者是 css
樣式庫,如 jQuery
,在網絡中的使用是很是廣泛的。當一個用戶在瀏覽你的某一個網頁的時候,頗有可能他已經經過你網站使用的 CDN
訪問過了其餘的某一個網站,恰巧這個網站一樣也使用了 jQuery
,那麼此時用戶瀏覽器已經緩存有該 jQuery
文件(同 IP
的同名文件若是有緩存,瀏覽器會直接使用緩存文件,不會再進行加載),因此就不會再加載一次了,從而間接的提升了網站的訪問速度。
三、分佈式的數據中心
假如你的站點佈置在北京,當一個香港或者更遠的用戶訪問你的站點的時候,他的數據請求勢必會很慢很慢。而 CDN
則會讓用戶從離他最近的節點去加載所需的文件,因此加載速度提高就是理所固然的了。
參考文章:【HTTP 請求詳解】
HTTP協議的六種請求方法:
GET: 發送請求來得到服務器上的資源,請求體中不會包含請求數據,請求數據放在協議頭中
POST: 和 get
同樣很常見,向服務器提交資源讓服務器處理,好比提交表單、上傳文件等,可能致使創建新的資源或者對原有資源的修改。提交的資源放在請求體中。不支持快取。非冪等
HEAD: 本質和 get
同樣,可是響應中沒有呈現數據,而是 http
的頭信息,主要用來檢查資源或超連接的有效性或是否能夠可達、檢查網頁是否被串改或更新,獲取頭信息等,特別適用在有限的速度和帶寬下。
PUT: 和 post
相似,html
表單不支持,發送資源與服務器,並存儲在服務器指定位置,要求客戶端事先知道該位置;好比 post
是在一個集合上(/province)
,而 put
是具體某一個資源上(/province/123)
。因此 put
是安全的,不管請求多少次,都是在 123
上更改,而 post
可能請求幾回建立了幾回資源。冪等
DELETE: 請求服務器刪除某資源。和 put
都具備破壞性,可能被防火牆攔截
CONNECT: HTTP/1.1
協議中預留給可以將鏈接改成管道方式的代理服務器。就是把服務器做爲跳板,去訪問其餘網頁而後把數據返回回來,鏈接成功後,就能夠正常的 get
、post
了。
7: OPTIONS: 獲取 http
服務器支持的 http
請求方法,容許客戶端查看服務器的性能,好比 ajax
跨域時的預檢等。 8: TRACE: 回顯服務器收到的請求,主要用於測試或診斷。通常禁用,防止被惡意攻擊或盜取信息。
1XX:信息狀態碼
2XX:成功狀態碼
3XX:重定向
4XX:客戶端錯誤
5XX: 服務器錯誤
緩存處理,在 HTTP1.0
中主要使用 header
裏的 If-Modified-Since,Expires
來作爲緩存判斷的標準,HTTP1.1
則引入了更多的緩存控制策略例如 Entity tag,If-Unmodified-Since, If-Match, If-None-Match
等更多可供選擇的緩存頭來控制緩存策略。
帶寬優化及網絡鏈接的使用,HTTP1.0
中,存在一些浪費帶寬的現象,例如客戶端只是須要某個對象的一部分,而服務器卻將整個對象送過來了,而且不支持斷點續傳功能,HTTP1.1
則在請求頭引入了 range
頭域,它容許只請求資源的某個部分,即返回碼是 206(Partial Content)
,這樣就方便了開發者自由的選擇以便於充分利用帶寬和鏈接。
錯誤通知的管理,在 HTTP1.1
中新增了24
個錯誤狀態響應碼,如 409(Conflict)
表示請求的資源與資源的當前狀態發生衝突;410(Gone)
表示服務器上的某個資源被永久性的刪除。
Host頭處理,在 HTTP1.0
中認爲每臺服務器都綁定一個惟一的 IP
地址,所以,請求消息中的 URL
並無傳遞主機名 (hostname)
。但隨着虛擬主機技術的發展,在一臺物理服務器上能夠存在多個虛擬主機(Multi-homed Web Servers)
,而且它們共享一個 IP
地址。HTTP1.1
的請求消息和響應消息都應支持 Host
頭域,且請求消息中若是沒有 Host
頭域會報告一個錯誤(400 Bad Request)
。
長鏈接,HTTP 1.1
支持長鏈接(PersistentConnection)
和請求的流水線(Pipelining)
處理,在一個 TCP
鏈接上能夠傳送多個 HTTP
請求和響應,減小了創建和關閉鏈接的消耗和延遲,在 HTTP1.1
中默認開啓 Connection: keep-alive
,必定程度上彌補了 HTTP1.0
每次請求都要建立鏈接的缺點。
HTTP2.0和HTTP1.X相比的新特性
新的二進制格式(Binary Format),HTTP1.x
的解析是基於文本。基於文本協議的格式解析存在自然缺陷,文本的表現形式有多樣性,要作到健壯性考慮的場景必然不少,二進制則不一樣,只認 0
和 1
的組合。基於這種考慮 HTTP2.0
的協議解析決定採用二進制格式,實現方便且健壯。
多路複用(MultiPlexing)
,即鏈接共享,即每個 request
都是是用做鏈接共享機制的。一個 request
對應一個 id
,這樣一個鏈接上能夠有多個 request
,每一個鏈接的 request
能夠隨機的混雜在一塊兒,接收方能夠根據 request
的 id
將 request
再歸屬到各自不一樣的服務端請求裏面。
header
壓縮,如上文中所言,對前面提到過 HTTP1.x
的 header
帶有大量信息,並且每次都要重複發送,HTTP2.0
使用 encoder
來減小須要傳輸的 header
大小,通信雙方各自 cache
一份 header fields
表,既避免了重複 header
的傳輸,又減少了須要傳輸的大小。
服務端推送(server push),HTTP2.0
具備 server push
功能。
參考文章:【一個故事講完 https】
參考文章:【設計模式】
觀察者模式和發佈訂閱模式最大的區別就是發佈訂閱模式有個事件調度中心。
// 觀察者模式
class Subject{
constructor(){
this.subs = [];
}
addSub(sub){
this.subs.push(sub);
}
notify(){
this.subs.forEach(sub=> {
sub.update();
});
}
}
class Observer{
update(){
console.log('update');
}
}
let subject = new Subject();
let ob = new Observer();
//目標添加觀察者了
subject.addSub(ob);
//目標發佈消息調用觀察者的更新方法了
subject.notify(); //update
複製代碼
// 發佈訂閱者模式
class PubSub {
constructor() {
this.subscribers = {}
}
subscribe(type, fn) {
if (!Object.prototype.hasOwnProperty.call(this.subscribers, type)) {
this.subscribers[type] = [];
}
this.subscribers[type].push(fn);
}
unsubscribe(type, fn) {
let listeners = this.subscribers[type];
if (!listeners || !listeners.length) return;
this.subscribers[type] = listeners.filter(v => v !== fn);
}
publish(type, ...args) {
let listeners = this.subscribers[type];
if (!listeners || !listeners.length) return;
listeners.forEach(fn => fn(...args));
}
}
let ob = new PubSub();
ob.subscribe('add', (val) => console.log(val));
ob.publish('add', 1);
複製代碼
據我瞭解的大部分前端對這部分知識有些欠缺,甚至抵觸,可是,若是突破更高的天花板,這部分知識是必不可少的,並且我親身經歷——很是有用!
參考文章:【遞歸實現深拷貝】
參考文章:【函數的防抖與節流】
function sleep(time) {
return new Promise((resolve,reject) => setTimeout(resolve, time))
}
sleep(3000).then(() => {console.log('沉睡3000ms')})
複製代碼
this
是否爲函數,防止 Function.prototype.myCall()
直接調用context
爲可選參數,若是不傳的話默認上下文爲 window
context
建立一個 Symbol
(保證不會重名)屬性,將當前函數賦值給這個屬性Symbol
屬性Function.prototype.myCall = function(context = window, ...args) {
if (this === Function.prototype) {
return undefined; // 用於防止 Function.prototype.myCall() 直接調用
}
context = context || window;
const fn = Symbol();
context[fn] = this;
const result = context[fn](...args);
delete context[fn];
return result;
};
複製代碼
apply
實現相似 call
,參數爲數組
Function.prototype.myApply = function(context = window, args) {
if (this === Function.prototype) {
return undefined; // 用於防止 Function.prototype.myCall() 直接調用
}
const fn = Symbol();
context[fn] = this;
let result;
if (Array.isArray(args)) {
result = context[fn](...args);
} else {
result = context[fn]();
}
delete context[fn];
return result;
};
複製代碼
由於 bind()
返回一個方法需手動執行,所以利用閉包實現。
Function.prototype.myBind = function(context, ...args1) {
if (this === Function.prototype) {
throw new TypeError('Error');
}
const _this = this;
return function F(...args2) {
// 判斷是否用於構造函數
if (this instanceof F) {
return new _this(...args1, ...args2);
}
return _this.apply(context, args1.concat(args2));
};
};
複製代碼
參考文章:【手動實現 promise】
function EventEmitter() {
this._events = Object.create(null);
}
// 向事件隊列添加事件
// prepend爲true表示向事件隊列頭部添加事件
EventEmitter.prototype.addListener = function (type, listener, prepend) {
if (!this._events) {
this._events = Object.create(null);
}
if (this._events[type]) {
if (prepend) {
this._events[type].unshift(listener);
} else {
this._events[type].push(listener);
}
} else {
this._events[type] = [listener];
}
};
// 移除某個事件
EventEmitter.prototype.removeListener = function (type, listener) {
if (Array.isArray(this._events[type])) {
if (!listener) {
delete this._events[type]
} else {
this._events[type] = this._events[type].filter(e => e !== listener && e.origin !== listener)
}
}
};
// 向事件隊列添加事件,只執行一次
EventEmitter.prototype.once = function (type, listener) {
const only = (...args) => {
listener.apply(this, args);
this.removeListener(type, listener);
}
only.origin = listener;
this.addListener(type, only);
};
// 執行某類事件
EventEmitter.prototype.emit = function (type, ...args) {
if (Array.isArray(this._events[type])) {
this._events[type].forEach(fn => {
fn.apply(this, args);
});
}
};
// 測試一下
var emitter = new EventEmitter();
var onceListener = function (args) {
console.log('我只能被執行一次', args, this);
}
var listener = function (args) {
console.log('我是一個listener', args, this);
}
emitter.once('click', onceListener);
emitter.addListener('click', listener);
emitter.emit('click', '參數');
emitter.emit('click');
emitter.removeListener('click', listener);
emitter.emit('click');
複製代碼
參考文章:【Vue 雙向數據綁定原理】
let Myjson = {
parse: function(jsonStr) {
return eval('(' + jsonStr + ')');
},
stringify: function(jsonObj) {
var result = '',
curVal;
if (jsonObj === null) {
return String(jsonObj);
}
switch (typeof jsonObj) {
case 'number':
case 'boolean':
return String(jsonObj);
case 'string':
return '"' + jsonObj + '"';
case 'undefined':
case 'function':
return undefined;
}
switch (Object.prototype.toString.call(jsonObj)) {
case '[object Array]':
result += '[';
for (var i = 0, len = jsonObj.length; i < len; i++) {
curVal = JSON.stringify(jsonObj[i]);
result += (curVal === undefined ? null : curVal) + ",";
}
if (result !== '[') {
result = result.slice(0, -1);
}
result += ']';
return result;
case '[object Date]':
return '"' + (jsonObj.toJSON ? jsonObj.toJSON() : jsonObj.toString()) + '"';
case '[object RegExp]':
return "{}";
case '[object Object]':
result += '{';
for (i in jsonObj) {
if (jsonObj.hasOwnProperty(i)) {
curVal = JSON.stringify(jsonObj[i]);
if (curVal !== undefined) {
result += '"' + i + '":' + curVal + ',';
}
}
}
if (result !== '{') {
result = result.slice(0, -1);
}
result += '}';
return result;
case '[object String]':
return '"' + jsonObj.toString() + '"';
case '[object Number]':
case '[object Boolean]':
return jsonObj.toString();
}
}
};
複製代碼
參考文章:【圖片懶加載】
參考文章:【輸入URL至頁面渲染】
現代 JavaScript
教程:zh.javascript.info/
阮一峯的 ECMAScript 6
教程:es6.ruanyifeng.com/
HTML meta
標籤總結與屬性使用介紹:segmentfault.com/a/119000000…
CSS
編碼指導:github.com/chadluo/CSS…
大前端開發者須要瞭解的基礎編譯原理和語言知識:fullstack.blog
正則表達式 30 分鐘入門教程:deerchao.net/tutorials/r…
用動畫的形式呈現解 LeetCode
題目的思路:github.com/MisterBooo/…
JavaScript
數據結構和算法:github.com/ConardLi/aw…
30-seconds-of-code
(裏面有不少 js
代碼很是巧妙,我正在將它翻譯成中文):github.com/ConardLi/30…
《重學前端》中的瀏覽器原理章節:time.geekbang.org/column/arti…
圖解瀏覽器的基本工做原理:zhuanlan.zhihu.com/p/47407398
七天學會 NodeJS
:github.com/nqdeng/7-da…
Node.js
模塊加載與運行原理:efe.baidu.com/blog/nodejs…
TypeScript Handbook
:zhongsp.gitbooks.io/typescript-…
React.js
小書:huziketang.mangojuice.top/books/react…
React
深刻系列:juejin.im/post/5cad39…
Webpack React
小書:fakefish.github.io/react-webpa…
Vue.js
技術揭祕:github.com/ustbhuangyi…
Vuex
在 Vue
中管理狀態:sabe.io/tutorials/g…
你須要 Mobx
仍是 Redux
?:juejin.im/post/5a7fd7…
Underscore
源碼分析:yoyoyohamapi.gitbooks.io/undersercor…
微信小程序開發資源彙總:github.com/justjavac/a…
騰訊移動 Web
前端知識庫:github.com/AlloyTeam/M…
一口(很長的)氣了解babel
:zhuanlan.zhihu.com/p/43249121
Webpack
傻瓜式指南:zhuanlan.zhihu.com/p/20367175
Webpack
原理:segmentfault.com/a/119000001…
廖雪峯的 git
教程:www.liaoxuefeng.com/wiki/001373…
前端開發者必備的 Nginx
知識:juejin.im/post/5c85a6…
使用 Jenkins
進行持續集成:www.liaoxuefeng.com/article/001…
常見六大 Web
安全攻防解析:github.com/ljianshu/Bl…
新人如何快速融入技術實力強的前端團隊:juejin.im/post/5cb860…
印記中文(各類中文開發文檔):www.docschina.org/
前端學習方法:github.com/helloqingfe…
如何在工做內外得到持續的技術成長:juejin.im/post/5cbd74…
優秀的前端博客彙總:github.com/foru17/fron…
冴羽的博客:github.com/mqyqingfeng…
大深海的博客:github.com/chenshenhai…
木易楊的博客: github.com/yygmind/blo…
MuYunyun的博客: github.com/MuYunyun/bl…
互聯網術語大全:www.jianshu.com/p/9a7ca206c…
互聯網溝通、問答、學習的藝術:zhuanlan.zhihu.com/p/41431775
常常加班至深夜,怎樣才能保持身體健康:www.zhihu.com/question/21…
此文章 Markdown 源文件地址:github.com/zxpsuper/bl…