記錄一些前端常用的基礎知識點
技能樹
BFC
BFC 定義: BFC(Block formatting context)直譯爲"塊級格式化上下文"。它是一個獨立的渲染區域,只有Block-level box參與, 它規定了內部的Block-level Box如何佈局,並且與這個區域外部毫不相干。
BFC佈局規則:
- 內部的Box會在垂直方向,一個接一個地放置。
- Box垂直方向的距離由margin決定。屬於同一個BFC的兩個相鄰Box的margin會發生重疊
- 每個元素的margin box的左邊, 與包含塊border box的左邊相接觸(對於從左往右的格式化,否則相反)。即使存在浮動也是如此。
- BFC的區域不會與float box重疊。
- BFC就是頁面上的一個隔離的獨立容器,容器裏面的子元素不會影響到外面的元素。反之也如此。
- 計算BFC的高度時,浮動元素也參與計算
哪些元素會生成BFC:
- 根元素
- float屬性不爲none
- position爲absolute或fixed
- display爲inline-block, table-cell, table-caption, flex, inline-flex
- overflow不爲visible
瀏覽器渲染頁面過程
- 用戶輸入URL地址
- 對URL地址進行DNS域名解析
- 建立TCP連接(三次握手)
- 瀏覽器發送HTTP請求報文
- 服務器返回HTTP響應報文
- 關閉TCP連接(四次揮手)
- 瀏覽器解析文檔資源並渲染頁面
TCP
TCP三次握手
TCP四次揮手
JS單線程運行機制
- 消息隊列:消息隊列是一個先進先出的隊列,它裏面存放着各種消息。
- 事件循環:事件循環是指主線程重複從消息隊列中取消息、執行的過程。
主線程只會做一件事情,就是從消息隊列裏面取消息、執行消息,再取消息、再執行。當消息隊列爲空時,就會等待直到消息隊列變成非空。而且主線程只有在將當前的消息執行完成後,纔會去取下一個消息。這種機制就叫做事件循環機制,取一個消息並執行的過程叫做一次循環。消息就是註冊異步任務時添加的回調函數。
事件循環
macroTask(宏任務): 主代碼塊, setTimeout, setInterval, setImmediate, requestAnimationFrame, I/O, UI rendering
microTask(微任務): process.nextTick, Promise, Object.observe, MutationObserver
事件
事件流
- 事件捕獲階段
- 處於目標階段
- 事件冒泡階段
事件委託
不在事件的發生地(直接dom)上設置監聽函數,而是在其父元素上設置監聽函數,通過事件冒泡,父元素可以監聽到子元素上事件的觸發,通過判斷事件發生元素DOM的類型,來做出不同的響應。
舉例:最經典的就是ul和li標籤的事件監聽
HTML
基礎標籤
<head></head>
<meta />
<link rel="stylesheet" href="" />
<title></title>
<body></body>
<center></center>
<section></section>
<article></article>
<aside></aside>
<div></div>
<ul></ul>
<li></li>
<p></p>
<h1></h1>
~
<h6></h6>
<button></button>
<input type="text" />
<a href=""></a>
<span></span>
<strong></strong>
<i></i>
CSS
CSS 樣式
優先級: 行內樣式 > 鏈接式 > 內嵌式 > @import 導入式
選擇器
/* 選擇所有元素 */
* {
}
/* 選擇 div 元素 */
div {
}
/* 選擇類名元素 */
.class {
}
/* 選擇 id 元素 */
#id {
}
/* 選擇 div 元素內的所有 p 元素 */
div p {
}
/* 選擇 div 元素內下一層級的 p 元素 */
div > p {
}
css選擇器權重: !important -> 行內樣式 -> #id -> .class -> 元素和僞元素 -> * -> 繼承 -> 默認
文本溢出
// 文本溢出單行顯示
.single {
overflow: hidden;
text-overflow:ellipsis;
white-space: nowrap;
}
// 文本溢出多行顯示
.multiple {
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 3;
overflow: hidden;
}
CSS3 新特性
- transition:過渡
- transform:旋轉、縮放、移動或者傾斜
- animation:動畫
- gradient:漸變
- shadow:陰影
- border-radius:圓角
Javascript
原型與原型鏈
- 實例的 proto 屬性(原型)等於其構造函數的 prototype 屬性。
- Object.proto === Function.prototype
- Function.prototype.proto === Object.prototype
- Object.prototype.proto === null
繼承實現
function extend(child, parent) {
var F = function() {}; // 空函數爲中介,減少實例時佔用的內存
F.prototype = parent.prototype; // f繼承parent原型
child.prototype = new F(); // 實例化f,child繼承,child、parent原型互不影響
child.prototype.constructor = child; // child構造函數指會自身,保證繼承統一
child.super = parent.prototype; // 新增屬性指向父類,保證子類繼承完備
}
深拷貝
function deepCopy(s, t) {
t = t || (Object.prototype.toString.call(t) === "[object Array]" ? [] : {});
for (var i in s) {
if (typeof s[i] === "object") {
t[i] = deepCopy(s[i], t[i]);
} else {
t[i] = s[i];
}
}
return t;
}
Ajax
var ajax = {};
ajax.get = function(url, fn) {
var xhr = new XMLHttpRequest();
xhr.open("GET", url, true);
xhr.onreadystatechange = function() {
if (
xhr.readyState === 4 &&
(xhr.status === 200 || xhr.status === 403)
) {
fn.call(this, xhr.responseText);
}
};
xhr.send();
};
ajax.post = function(url, data, fn) {
var xhr = new XMLHttpRequest();
xhr.open("POST", url, true);
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
xhr.onreadystatechange = function() {
if (
xhr.readyState === 4 &&
(xhr.status === 200 || xhr.status === 403)
) {
fn.call(this, xhr.responseText);
}
};
xhr.send(data);
};
格式化日期
function formatDate(date, format) {
if (arguments.length === 0) return null;
format = format || "{y}-{m}-{d} {h}:{i}:{s}";
if (typeof date !== "object") {
if ((date + "").length === 10) date = parseInt(date) * 1000;
date = new Date(date);
}
const dateObj = {
y: date.getFullYear(),
m: date.getMonth() + 1,
d: date.getDate(),
h: date.getHours(),
i: date.getMinutes(),
s: date.getSeconds(),
a: date.getDay()
};
const dayArr = ["一", "二", "三", "四", "五", "六", "日"];
const str = format.replace(/{(y|m|d|h|i|s|a)+}/g, (match, key) => {
let value = dateObj[key];
if (key === "a") return dayArr[value - 1];
if (value < 10) {
value = "0" + value;
}
return value || 0;
});
return str;
}
new 實現
function New(Class) {
let obj = {};
obj.__proto__ = Class.prototype;
let res = Class.call(obj);
return typeof res === 'object' ? res : obj;
}
call 實現
Function.prototype.callfb = function (ctx) {
if (typeof this !== 'function') {
throw new Error('Function undefined');
}
ctx = ctx || window;
const fn = ctx.fn;
ctx.fn = this;
const args = [...arguments].slice(1);
const res = ctx.fn(...args);
ctx.fn = fn;
return res;
}
apply 實現
Function.prototype.applyFb = function (ctx) {
if (typeof this !== 'function') {
throw new Error('Function undefined');
}
ctx = ctx || window;
const fn = ctx.fn;
ctx.fn = this;
const arg = arguments[1];
const res = Array.isArray(arg) ? ctx.fn(...arg) : ctx.fn();
ctx.fn = fn;
return res;
}
bind 實現
Function.prototype.bindFb = function (ctx) {
const fn = this;
const args = [...arguments].slice(1);
const F = function () {};
const fBind = function () {
return fn.apply(this instanceof fBind ? this : ctx, args.concat(...arguments))
}
if (fn.prototype) {
F.prototype = fn.prototype;
}
fBind.prototype = new F();
return fBind;
}
instanceof 實現
function instanceofFb(left, right) {
let proto, prototype = right.prototype;
proto = left.__proto__;
while (proto) {
if (proto === prototype) {
return true;
}
proto = proto.__proto__;
}
return false;
}
Promise 實現
function promiseFb(fn) {
const _this = this;
this.state = 'pending'; // 初始狀態爲pending
this.value = null;
this.resolvedCallbacks = []; // 這兩個變量用於保存then中的回調,因爲執行完Promise時狀態可能還是pending
this.rejectedCallbacks = []; // 此時需要吧then中的回調保存起來方便狀態改變時調用
function resolve(value) {
if (_this.state === 'pending') {
_this.state = 'resolved';
_this.value = value;
_this.resolvedCallbacks.map(cb => { cb(value) }); // 遍歷數組,執行之前保存的then的回調函數
}
}
function reject(value) {
if (_this.state === 'pending') {
_this.state = 'rejected';
_this.value = value;
_this.rejectedCallbacks.map(cb => { cb(value) });
}
}
try {
fn(resolve, reject);
} catch (e) {
reject(e);
}
}
promiseFb.prototype.then = function (onFulfilled, onRejected) {
// 因爲then的兩個參數均爲可選參數,
// 所以判斷參數類型本身是否爲函數,如果不是,則需要給一個默認函數如下(方便then不傳參數時可以透傳)
// 類似這樣: Promise.resolve(4).then().then((value) => console.log(value))
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : fn => fn;
onRejected = typeof onRejected === 'function' ? onRejected : e => { throw e };
switch (this.state) {
case 'pending':
// 若執行then時仍爲pending狀態時,添加函數到對應的函數數組
this.resolvedCallbacks.push(onFulfilled);
this.rejectedCallbacks.push(onRejected);
break;
case 'resolved':
onFulfilled(this.value);
break;
case 'rejected':
onRejected(this.value);
break;
default: break;
}
}
debounce 防抖
function debounce(fn, wait, immediate) {
let timer;
return function () {
if (immediate) {
fn.apply(this, arguments);
}
if (timer) clearTimeout(timer);
timer = setTimeout(() => {
fn.apply(this, arguments);
}, wait)
}
}
throttle 節流
function throttle(fn, wait) {
let prev = new Date();
return function () {
const now = new Date();
if (now - prev > wait) {
fn.apply(this, arguments);
prev = now;
}
}
}
雙向綁定
雙向綁定:視圖(View)的變化能實時讓數據模型(Model)發生變化,而數據的變化也能實時更新到視圖層.
Object.defineProperty
<!DOCTYPE html>
<html lang="en">
<head>
<title>mvvm</title>
</head>
<body>
<p>數據值:<span id="data"></span></p>
<p><input type="text" onkeyup="keyup()"></p>
<script>
var obj = {
data: ''
}
function keyup(e) {
e = e || window.event;
obj.data = e.target.value; // 更新數據值
}
Object.defineProperty(obj, 'data', {
get: function () {
return this.data;
},
set: function (newVal) {
document.getElementById('data').innerText = newVal; // 更新視圖值
}
})
</script>
</body>
</html>
Proxy
<!DOCTYPE html>
<html lang="en">
<head>
<title>mvvm</title>
</head>
<body>
<p>數據值:<span id="data"></span></p>
<p><input type="text" onkeyup="keyup()"></p>
<script>
var obj = new Proxy({}, {
get: function (target, key, receiver) {
return Reflect.get(target, key, receiver);
},
set: function (target, key, value, receiver) {
if (key === 'data') {
document.getElementById('data').innerText = value; // 更新視圖值
}
return Reflect.set(target, key, value, receiver);
}
})
function keyup(e) {
e = e || window.event;
obj.data = e.target.value; // 更新數據值
}
</script>
</body>
</html>
算法
冒泡排序
兩兩對比
function bubble(arr) {
const len = arr.length;
for (let i = 0; i < len; i++) {
for (let j = 0; j < len - i - 1; j++) {
if (arr[j] > arr[j + 1]) {
let temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
return arr;
}
選擇排序
尋找最小的數,將索引保存
function selection(arr) {
const len = arr.length;
let minIndex, temp;
for (let i = 0; i < len - 1; i++) {
minIndex = i;
for (let j = i + 1; j < len; j++) {
if (arr[j] < arr[minIndex]) {
minIndex = j;
}
}
temp = arr[i];
arr[i] = arr[minIndex];
arr[minIndex] = temp;
}
return arr;
}
Webpack
常用loader
- file-loader: 加載文件資源,如 字體 / 圖片 等,具有移動/複製/命名等功能;
- url-loader: 通常用於加載圖片,可以將小圖片直接轉換爲 Date Url,減少請求;
- babel-loader: 加載 js / jsx 文件, 將 ES6 / ES7 代碼轉換成 ES5,抹平兼容性問題;
- ts-loader: 加載 ts / tsx 文件,編譯 TypeScript;
- style-loader: 將 css 代碼以<style>標籤的形式插入到 html 中;
- css-loader: 分析@import和url(),引用 css 文件與對應的資源;
- postcss-loader: 用於 css 的兼容性處理,具有衆多功能,例如 添加前綴,單位轉換 等;
- less-loader / sass-loader: css預處理器,在 css 中新增了許多語法,提高了開發效率;
常用plugin
- UglifyJsPlugin: 壓縮、混淆代碼;
- CommonsChunkPlugin: 代碼分割;
- ProvidePlugin: 自動加載模塊;
- html-webpack-plugin: 加載 html 文件,並引入 css / js 文件;
- extract-text-webpack-plugin / mini-css-extract-plugin: 抽離樣式,生成 css 文件;
- DefinePlugin: 定義全局變量;
- optimize-css-assets-webpack-plugin: CSS 代碼去重;
- webpack-bundle-analyzer: 代碼分析;
- compression-webpack-plugin: 使用 gzip 壓縮 js 和 css;
- happypack: 使用多進程,加速代碼構建;
- EnvironmentPlugin: 定義環境變量;