function _new(constructor, ...args) {
// 以構造函數的原型爲原型建立一個對象
const obj = Object.create(constructor.prototype)
const result = constructor.call(obj, ...args)
return result instanceof Object ? result : obj;
}
複製代碼
主要有構造函數繼承、原型繼承、組合繼承javascript
下面是一個組合繼承:css
function Parent (name) {
this.name = name;
this.colors = ['red', 'blue', 'green'];
}
Parent.prototype.getName = function () {
console.log(this.name)
}
function Child (name, age) {
// 構造函數繼承
Parent.call(this, name);
this.age = age;
}
// 原型繼承
Child.prototype = Object.create(Parent.prototype);
Child.prototype.constructor = Child;
const child1 = new Child("kevin", '18');
複製代碼
遞歸版本html
const isObject = obj => typeof obj === 'object' && obj !== null
function deepClone(object) {
const result = Array.isArray(object) ? [] : {}
for (const key in object) {
if (isObject(object[key])) {
result[key] = deepClone(object[key])
} else {
result[key] = object[key]
}
}
return result
}
複製代碼
循環版本前端
Function.prototype.call = function(context, ...args) {
const fn = this;
context.__fn = fn;
const result = context.__fn(...args)
delete context.__fn
return result
}
Function.prototype.apply = function(context, args) {
const fn = this;
context.__fn = fn;
const result = context.__fn(...args)
delete context.__fn
return result
}
Function.prototype.bind = function(context, prependArgs) {
const fn = this;
return function (...args) {
return fn.apply(context, prependArgs.concat(args))
}
}
複製代碼
BFC 就是塊級格式化上下文,它是 css 的渲染模式,能夠保證元素內部和外部不相互影響、清除浮動,咱們能夠經過如下方式建立 BFC:java
物理像素指設備顯示器最小的物理單位,好比 1900 * 1080 分辨率表示屏幕橫向有1900個物理像素,縱向有1080個物理像素 邏輯像素指脫離物理像素抽象出來的一個單位,好比css像素,通常由開發者指定,由底層系統轉換爲對應的邏輯像素。node
引伸:react
位圖和矢量圖的區別:css3
瀏覽器採用流式佈局 迴流:當盒模型發生變化,或者元素的大小、尺寸發生變化。 重繪:當元素樣式變化,但不影響它在文檔流之中的位置,好比 color background visibility 等。 如何避免迴流和重繪:算法
跨域是由於瀏覽器的同源策略,同源策略主要爲了規避兩種安全問題:數據庫
解決方式:
緩存命中規則:一個請求發起後,會前後檢查強緩存和協商緩存。強制緩存優先於協商緩存進行,若強制緩存(Expires和Cache-Control)生效則直接使用緩存,若不生效則進行協商緩存(Last-Modified / If-Modified-Since和Etag / If-None-Match),協商緩存由服務器決定是否使用緩存,若協商緩存失效,那麼表明該請求的緩存失效,從新獲取請求結果,再存入瀏覽器緩存中;生效則返回304,繼續使用緩存,主要過程以下:
XSS:跨站腳本攻擊,指攻擊者在頁面插入惡意腳本腳本,來獲取用戶信息或者控制頁面。原理是利用評論之類的功能注入惡意的 script 腳本,有多是持久化的。
防範措施:
CRSF:跨站請求僞造,指攻擊者利用受害者的cookie,僞造請求發給服務器。好比,用戶訪問釣魚網站,點擊僞造按鈕,發起請求,默認會當上用戶真實站點的cookie,服務器信賴cookie從而請求完成。
防範措施:
分爲對稱加密和不對稱加密,HTTPS的加密過程綜合了這兩種加密算法。
證書: 通訊的過程加密仍是不夠安全的。攻擊者能夠對客戶端僞形成服務器,對服務器僞形成客戶端,也就是中間人攻擊,抓包攻擊 Charles 就是採用這種方式攔截http請求。 所以,咱們還要一種手段來驗證客戶端或者服務器的身份,這就是證書。雙方通訊時,不只要使用加密算法機密,還要提交證書,驗證雙方的合法性,
session:當用戶登陸後,服務器會把用戶的認證信息保存到內存或數據庫中,並頒發給客戶端存儲起來(cookie,storage)。之後每次通訊都使用 session 來校驗用戶身份。 優勢:服務器保存,相對安全;能夠主動清除 session
缺點:有狀態,很差擴展;cookie 可能會被crsf;
JWT:JWT 本質上是時間換空間的思路,服務端經過用戶認證後,生成一個 json 對象頒發給客戶端,並使用簽名加密,之後每次通訊都使用這段 json 認證用戶身份。
優勢:無狀態,好擴展 缺點:默認沒法清除用戶認證狀態
遞歸版 遞歸版
// 深度遍歷
function deepFirstTraversal(node, callback) {
if (node.children) node.children.forEach(child => deepFirstTraversal(child, callback))
callback(node);
}
// 廣度遍歷
function breadthFirstTraversal(node, callback) {
callback(node);
if (node.children) node.children.forEach(child => breadthFirstTraversal(child, callback))
}
複製代碼
非遞歸版本
// 深度遍歷
function deepFirstTraversal(root, callback) {
// 使用兩個棧,使用棧1遍歷樹入棧2,出棧2的順序即深度優先遍歷
let node = root, nodes = [root], next = [], children, i, n;
// 入棧1
while (node = nodes.pop()) {
// 先入棧1父節點
next.push(node), children = node.children;
// 入棧2子節點
if (children) for (i = 0, n = children.length; i < n; ++i) {
nodes.push(children[i]);
}
}
// 出棧2 nodes
while (node = next.pop()) {
callback(node);
}
return this;
}
// 廣度遍歷
function breadthFirstTraversal(node, callback) {
const queue = [node]
while(queue.length) {
const current = queue.shift();
callback(current)
if (current.child) {
queue = queue.concat(current)
}
}
}
複製代碼
堆屬於二叉樹的一種,它的特色:
鏈表分爲單向和雙向鏈表,主要考察鏈表的新增、刪除、查找
主要是歸併和快速排序
function quickSort(originalArray) {
// 複製原數組
const array = [...originalArray]
// 數組長度小於1時,已排序
if (array.length <= 1) {
return array
}
const leftArray = []
const rightArray = []
// 選擇一個對比元素
const pivotElement = array.shift()
const centerArray = [pivotElement]
while (array.length) {
const currentElement = array.shift()
if (currentElement === pivotElement) {
centerArray.push(currentElement)
} else if (currentElement < pivotElement) {
leftArray.push(currentElement)
} else {
rightArray.push(currentElement)
}
}
// 遞歸排序左右數組
const leftArraySorted = quickSort(leftArray)
const rightArraySorted = quickSort(rightArray)
return leftArraySorted.concat(centerArray, rightArraySorted)
}
function mergeSort(originalArray) {
// If array is empty or consists of one element then return this array since it is sorted.
if (originalArray.length <= 1) {
return originalArray
}
// Split array on two halves.
const middleIndex = Math.floor(originalArray.length / 2)
const leftArray = originalArray.slice(0, middleIndex)
const rightArray = originalArray.slice(middleIndex, originalArray.length)
// Sort two halves of split array
const leftSortedArray = mergeSort(leftArray)
const rightSortedArray = mergeSort(rightArray)
// Merge two sorted arrays into one.
return mergeSortedArrays(leftSortedArray, rightSortedArray)
}
function mergeSortedArrays(leftArray, rightArray) {
let sortedArray = []
// In case if arrays are not of size 1.
while (leftArray.length && rightArray.length) {
let minimumElement = null
// Find minimum element of two arrays.
if (leftArray[0] < rightArray[0]) {
minimumElement = leftArray.shift()
} else {
minimumElement = rightArray.shift()
}
// Push the minimum element of two arrays to the sorted array.
sortedArray.push(minimumElement)
}
// If one of two array still have elements we need to just concatenate
// this element to the sorted array since it is already sorted.
if (leftArray.length) {
sortedArray = sortedArray.concat(leftArray)
}
if (rightArray.length) {
sortedArray = sortedArray.concat(rightArray)
}
return sortedArray
}
複製代碼
diff 算法
發佈訂閱
主要靠 hash 和 history API
說 fiber 以前,有兩點前置知識
fiber 產生的緣由在於:當 virtual dom 樹很是大時,整個 diff 的過程超過了 30 ms,大量的 js 運算阻塞了渲染線程,瀏覽器沒法正常響應用戶的交互行爲。
fiber 架構引入了分片機制,可讓一個 diff 可暫停,給瀏覽器渲染和響應交互的時間,再繼續以前 diff 的流暢。
引伸:CPU 時間分片
// 必定時間間隔內,重複調用函數會取消以前的調用,場景:輸入框搜索
function debounce(fn, timeout) {
let timer;
return function (...args) {
if (timer) {
clearTimeout(timer)
}
timer = setTimeout(() => {
fn.call(this, ...args)
timer = null
}, timeout);
}
}
// 限制函數調用的頻率,必定時間間隔內只能調用一次,場景:滾動事件監聽
function throttle(fn, timeout) {
let timer;
return function (...args) {
if (timer) return
timer = setTimeout(() => {
fn.call(this, ...args)
timer = null
}, timeout);
}
}
複製代碼
Promise.prototype.finally = function(callback) {
return this.then(
value => Promise.resolve(callback).then(() => value),
reason => Promise.reject(callback).catch(() => {
throw reason;
}),
)
}
Promise.prototype.race = function(ps) {
return new Promise((resolve, reject) => {
ps.forEach(p => p.then(resolve, reject));
})
}
Promise.prototype.all = function(ps) {
return new Promise((resolve, reject) => {
const next = gen(ps.length, resolve)
ps.forEach((p, index) => p.then(value => {
next(index, value)
}, reject));
})
}
function gen(length, resolve) {
let i = 0;
const values = []
return function(index, value) {
values[index] = value
if (++i === length) {
resolve(values)
}
}
}
複製代碼
function isCyclic(obj) {
// 存儲已遍歷的對象
const seenObjects = [];
// object 樹按廣度優先順序推入數組
function detect(object) {
if (object && typeof object === 'object') {
if (seenObjects.indexOf(object) !== -1) {
return true;
}
seenObjects.push(object);
// 遞歸遍歷子節點
for (const key in object) {
if (object.hasOwnProperty(key) && detect(object[key])) {
return true;
}
}
}
return false;
}
return detect(obj);
}
複製代碼
性能優化是一塊比較零碎的知識,藉助一些分類咱們能夠將其體系化
性能優化的前提是方向,瞭解應用性能的瓶頸在哪裏,須要性能數據的支撐。
store.subscribe
方法Provider
和 Consumer
@connect
, @withRouter