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