數(shù)組扁平化
let flat = arr => arr.reduce((list, v) => list.concat(Array.isArray(v) ? flat(v) : v), [])
v2
function flat(arr) {
let res = []
for (let item of arr) {
if (Array.isArray(item)) {
res = res.concat(flat(item))
} else {
res.push(item)
}
}
return res
}
類型
加法操作符的基本規(guī)則
- 兩者都為數(shù)字 進(jìn)行普通的數(shù)字相加
- 一方為字符串 則轉(zhuǎn)換另一方為字符串后進(jìn)行字符串拼接
- 一方為對(duì)象類型 轉(zhuǎn)換為字符串后 繼續(xù)應(yīng)用上一條規(guī)則
- null + 1 = 1 原因是null被轉(zhuǎn)換成了數(shù)字 這和紅寶書上有沖突汰翠,應(yīng)該null undefined會(huì)和另外一項(xiàng)進(jìn)行自適應(yīng) 如果是數(shù)字 自己也變數(shù)字龄坪,如果是字符 自己也變字符
相等操作符的運(yùn)算規(guī)則
[] == ![] -> [] == false -> [] == 0 -> [].valueOf() == 0 -> [].toString() == 0 -> ‘’ == 0 -> 0 == 0 -> true
繼承
寄生組合式繼承
function Person(name) {
this.name = name
}
Person.prototype.sayName = function() {
console.log(this.name)
}
function Boy(name) {
this.sex = 'box'
Person.call(this, name)
}
let prototype = Object.create(Person.prototype)
prototype.constructor = Boy
Boy.prototype = prototype
let boy1 = new Boy("TOM")
boy1.sayName()
特點(diǎn) 繼承的只是方法,父類實(shí)例不共享變量复唤,并且不需要New一個(gè)多余的父類變量健田,只需要繼承父類的原型。這里面的構(gòu)造函數(shù)沒有被覆蓋佛纫,只是建立再一個(gè)新的對(duì)象上妓局。
用Class
class Person {
constructor(name) {
this.name = name
}
sayName() {
console.log(this.name)
}
}
class Boy extends Person {
constructor(name) {
super(name)
}
}
let boy1 = new Boy("Tom")
boy1.sayName()
算法
快速排序
function quick_sort1(arr) {
if (!arr || arr.length < 2) return arr
const pivot = arr.pop()
const left = arr.filter(v => v <= pivot)
const right = arr.filter(v => v > pivot)
return quick_sort1(left).concat([pivot], quick_sort1(right))
}
function quickSort2(arr) {
if (arr.length <= 1) {
return arr
}
let pi = Math.floor(arr.length / 2)
let p = arr.splice(pi, 1)[0]
let left = []
let right = []
let i = arr.length
while (i--) {
let cur = arr[i]
if (cur < p) {
left.push(cur)
} else {
right.push(cur)
}
}
return quickSort2(left).concat([p], quickSort2(right))
}
function quick_sort3(arr, start, end) {
let mid = arr[start],
p1 = start,
p2 = end
while (p1 < p2) {
swap(arr, p1, p1 + 1)
while (compare(arr[p1], mid) >= 0 && p1 < p2) {
swap(arr, p1, p2--)
}
p1++
}
if (start < p1 - 1) quick_sort3(arr, start, p1 - 1)
if (p1 < end) quick_sort3(arr, p1, end)
}
異步
簡單的Promise(無法.then連續(xù)返回新Promise 不符合A+規(guī)范)
const PromiseStateMap = {
pending: 'pending',
resolved: 'resolved',
rejected: 'rejected'
}
class Promise {
static resolve(val) {
if (val instanceof Promise) {
return val
}
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(val)
})
})
}
static reject(val) {
if (val instanceof Promise) {
return val
}
return new Promise((resolve, reject) => {
setTimeout(() => {
reject(val)
})
})
}
constructor(fn) {
this.val = null
this.state = PromiseStateMap.pending
this.resolvedCbs = []
this.rejectedCbs = []
fn(
res => {
this.val = res
this.state = PromiseStateMap.resolved
for (let fn of this.resolvedCbs) {
fn(this.val)
}
},
res => {
this.val = res
this.state = PromiseStateMap.rejected
for (let fn of this.rejectedCbs) {
fn(this.val)
}
}
)
}
then(onResolved, onRejected) {
if (typeof onResolved === 'function') {
this.resolvedCbs.push(onResolved)
}
if (typeof onRejected === 'function') {
this.rejectedCbs.push(onRejected)
}
}
}
進(jìn)程與線程
進(jìn)程是 CPU 資源分配的最小單位;線程是 CPU 調(diào)度的最小單位
可以認(rèn)為一個(gè)進(jìn)程就是一個(gè)正在運(yùn)行中的程序呈宇,而一個(gè)線程是這個(gè)程序中的執(zhí)行流好爬,現(xiàn)在的操作系統(tǒng)都是多進(jìn)程的,可以同時(shí)運(yùn)行多個(gè)程序甥啄。
手寫call,apply,bind
Function.prototype.mycall = function(ctx, ...args) {
if (!ctx || typeof ctx !== 'object') {
console.error('ctx must be a object!')
}
let fn = this
let key = Symbol()
ctx[key] = fn
let res = ctx[key](...args)
return res
}
Function.prototype.myapply = function(ctx, args) {
if (!ctx || typeof ctx !== 'object') {
console.error('ctx must be a object!')
}
let fn = this
let key = Symbol()
ctx[key] = fn
let res = ctx[key](...args)
return res
}
Function.prototype.mybind = function(ctx) {
if (!ctx || typeof ctx !== 'object') {
console.error('ctx must be a object!')
}
let fn = this
return function(...args) {
let key = Symbol()
ctx[key] = fn
let res = ctx[key](...args)
return res
}
}
實(shí)現(xiàn)instanceOf
instanceOf的原理是沿著left對(duì)象的原型鏈進(jìn)行檢查 看是否和right構(gòu)造函數(shù)的原型對(duì)象相等
function myInstanceOf(left, right) {
let targetProto = right.prototype
let curProto = Object.getPrototypeOf(left)
while (curProto) {
if (curProto === targetProto) {
return true
} else {
curProto = Object.getPrototypeOf(curProto)
}
}
return false
}
VDOM
VDOM是一種技術(shù)存炮,一種理念,他把真實(shí)的DOM與JS中的對(duì)象進(jìn)行一種映射蜈漓,也就是說在JS這一端與真實(shí)DOM端建立了一層抽象
他的好處有如下幾點(diǎn)
- 增強(qiáng)Diff的性能 可以通過對(duì)比前后VnodeTree來找到更新的節(jié)點(diǎn)穆桂,進(jìn)行局部更新。
- 建立抽象層融虽,方便移植到多平臺(tái)享完,進(jìn)行SSR
打開瀏覽器 發(fā)生
大綱
DNS解析 多級(jí)緩存
獲取IP 進(jìn)行TCP鏈接 三次握手 SYN SYN_ACK ACK
如果是https 進(jìn)行TLS加密
獲取到過程底層其實(shí)是按包來的
獲取HTML文件 中間加載各種資源 幾乎并發(fā) 最多6個(gè)套接字同時(shí)進(jìn)行 資源的緩存機(jī)制強(qiáng)緩存與協(xié)商緩存 資源可能會(huì)gzip
合并兩棵樹 DOM 和 CSSDOM樹 進(jìn)行合并
進(jìn)行首次繪制 用戶看到內(nèi)容
算法部分
按位實(shí)現(xiàn)加法
function sum(a, b) {
if (a == 0) return b
if (b == 0) return a
let newA = a ^ b
let newB = (a & b) << 1
return sum(newA, newB)
}
所有的排序
function swap(arr, a, b) {
let temp = arr[a]
arr[a] = arr[b]
arr[b] = temp
}
let testArr = [10, 3, 50, 11, 88, 2390, 1, 2, 11]
// 冒泡排序
function bsort(arr) {
let len = arr.length
arr = arr.slice()
for (let i = 0; i < len; i++) {
for (let j = 0; j < len - i; j++) {
if (arr[j] > arr[j + 1]) {
swap(arr, j, j + 1)
}
}
}
return arr
}
// 插入排序
function iSort(arr) {
let len = arr.length
for (let i = 1; i < len; i++) {
let cur = arr[i]
let j = i - 1
while (j >= 0 && arr[j] > cur) {
arr[j + 1] = arr[j]
j--
}
arr[j + 1] = cur
}
return arr
}
// 快速排序
function qsort(arr) {
let len = arr.length
if (len <= 1) {
return arr
}
let pivotIndex = Math.floor(len / 2)
let pivot = arr.splice(pivotIndex, 1)[0]
let left = []
let right = []
for (item of arr) {
if (item < pivot) {
left.push(item)
} else {
right.push(item)
}
}
return qsort(left).concat([pivot], qsort(right))
}
// 選擇排序
function ssort(arr) {
let len = arr.length
arr = arr.slice()
for (let i = 0; i < len; i++) {
let index = i
for (let j = i; j < len; j++) {
if (arr[j] < arr[index]) {
index = j
}
}
swap(arr, i, index)
}
return arr
}
// 歸并排序
function msort(arr) {
let len = arr.length
if (len <= 1) {
return arr
}
let middle = Math.floor(len / 2)
let left = msort(arr.slice(0, middle))
let right = msort(arr.slice(middle, len))
let start1 = 0,
start2 = 0,
end1 = middle,
end2 = len - middle
let res = []
while (start1 < end1 && start2 < end2) {
left[start1] < right[start2]
? res.push(left[start1++])
: res.push(right[start2++])
}
while (start1 < end1) {
res.push(left[start1++])
}
while (start2 < end2) {
res.push(right[start2++])
}
return res
}
顏色排序算法
function tSort(arr) {
let len = arr.length
let left = -1
let right = len
for (let i = 0; i < right; i++) {
if (arr[i] === 0) {
swap(arr, i, ++left)
}
if (arr[i] === 2) {
swap(arr, i--, --right)
}
}
return arr
}
線性統(tǒng)計(jì) 獲取第K大的值
小心splice[]
注意 p = left.length + 1
function select(nums, i) {
let len = nums.length
if (len <= 1) {
return nums[0]
}
let pivotIndex = Math.floor(len / 2)
let pivot = nums.splice(pivotIndex, 1)[0]
let left = []
let right = []
for (num of nums) {
if (num < pivot) {
left.push(num)
} else {
right.push(num)
}
}
let p = left.length + 1
if (p === i) {
return pivot
}
if (i < p) {
return select(left, i)
} else {
return select(right, i - p)
}
}
堆排序 最大最小堆
建堆的時(shí)候小心順序 i = floor(len / 2) i >=0 i --
一定要從后往前建
小心heapsize--的順序
let array = [5, 2, 6, 1, 6, 8, 2, 39, 2, 6, 89, 5, 6, 4, 7]
const swap = (arr, a, b) => {
let temp = arr[a]
arr[a] = arr[b]
arr[b] = temp
}
const left = i => i * 2
const right = i => i * 2 + 1
function HEAPIFY(arr, i) {
let l = left(i)
let r = right(i)
let largest = i
if (l < arr.heapsize && arr[l] > arr[largest]) {
largest = l
}
if (r < arr.heapsize && arr[r] > arr[largest]) {
largest = r
}
if (largest !== i) {
swap(arr, i, largest)
HEAPIFY(arr, largest)
}
}
function BUILD_HEAP(arr) {
arr.heapsize = arr.length
for (let i = Math.floor(arr.length / 2); i >= 0; i--) {
HEAPIFY(arr, i)
}
return arr
}
function HEAP_SORT(arr) {
BUILD_HEAP(arr)
for (let i = arr.length - 1; i >= 0; i--) {
swap(arr, 0, i)
arr.heapsize--
HEAPIFY(arr, 0)
}
return arr
}
單向鏈表 以及 反轉(zhuǎn)功能
// 單項(xiàng)鏈表
class ListNode {
constructor(val, next) {
this.val = val
this.next = next
}
}
class LinkList {
constructor() {
this.head = null
this.tail = null
}
add(val) {
if (!this.head || !this.tail) {
this.head = this.tail = new ListNode(val, null)
} else {
this.tail.next = new ListNode(val, null)
this.tail = this.tail.next
}
}
reverse() {
if (!this.head | !this.tail) return
let pre = null
let current = this.head
let next = null
while (current) {
next = current.next
current.next = pre
pre = current
current = next
}
let temp = this.head
this.head = this.tail
this.tail = temp
}
}
let linklist = new LinkList()
linklist.add('a')
linklist.add('b')
linklist.add('c')
linklist.add('d')
linklist.reverse()
樹的遍歷 遞歸和迭代實(shí)現(xiàn)
function TreeNode(val) {
this.val = val
this.left = this.right = null
}
function traverse(root) {
if (root) {
console.log(root)
if (root.left) {
traverse(root)
}
if (root.right) {
traverse(root)
}
}
}
function pre(root) {
if (root) {
let stack = []
stack.push(root)
while (stack.length > 0) {
let item = stack.pop()
console.log(item)
if (item.right) {
stack.push(right)
}
if (item.left) {
stack.push(left)
}
}
}
}
function mid(root) {
if (root) {
let stack = []
stack.push(root)
while (stack.length >= 1) {
if (root) {
stack.push(root)
root = root.left
} else {
let item = stack.pop()
console.log(item)
root = item.right
}
}
}
}
function pos(root) {
if (root) {
let stack1 = []
let stack2 = []
stack1.push(root)
while (stack1.length >= 1) {
let item = stack1.pop()
stack2.push(item)
if (item.right) {
stack1.push(item.right)
}
if (item.left) {
stack1.push(item.left)
}
}
while (stack2.length >= 1) {
console.log(stack2.pop())
}
}
}
前驅(qū)節(jié)點(diǎn) 后驅(qū)節(jié)點(diǎn)
前驅(qū)先看Left 后續(xù)全部right
后續(xù)先看right 后續(xù)全部left
function successor(node) {
if (node.right) {
return findLeft(node.right)
} else {
let parent = node.parent
while (parent && parent.left === node) {
node = parent
parent = node.parent
}
return parent
}
}
function findLeft(node) {
while (node) {
if (!node.left) {
return node
}
node = node.left
}
}
function predecessor(node) {
if (node.left) {
return getRight(node.left)
} else {
let parent = node.parent
while (parent && parent.right === node) {
node = parent
parent = node.parent
}
return parent
}
}
function getRight(node) {
while (node) {
if (!node.right) {
return node
}
node = node.right
}
}
獲取樹的最大深度
function MAX_DEPTH(root) {
if (!root) {
return 0;
}
return Math.max(MAX_DEPTH(root.left), MAX_DEPTH(root.right)) + 1;
}
帥的不行Fib
const fib = n =>
Array(n)
.fill(1)
.reduce(nums => [nums[1], nums[0] + nums[1]], [0, 1])[0]
最小硬幣算法
/**
* @param {*} coins 硬幣數(shù)組
* @param {*} m 目標(biāo)金額
* @returns {number} 如果擁有解則為一個(gè)整數(shù) 如果沒有則為正無限
*/
function min_coins(coins, m) {
let table = [0]
let i = 1
while (i <= m) {
table[i] = Infinity
for (coin of coins) {
if (i >= coin) {
table[i] = Math.min(table[i], table[i - coin] + 1)
}
}
i++
}
return table[m]
}
console.log(min_coins([3, 6, 8], 16))
01背包問題
想象出一個(gè)表格 行是物品 列是當(dāng)前容量
外循環(huán)是物品 里面是金額
注意哦 一層是物品 二層是空間
空間要從0開始 一層直接開始
/**
* @param {*} w 物品重量
* @param {*} v 物品價(jià)值
* @param {*} C 總?cè)萘? * @returns
*/
function knapsack(w, v, C) {
let len = w.length
let table = new Array(len).fill(new Array(C + 1).fill(0))
for (let j = 0; j <= C; j++) {
table[0][j] = j >= w[0] ? v[0] : 0
}
for (let i = 1; i < len; i++) {
let cw = w[i]
for (let j = 0; j <= C; j++) {
table[i][j] = table[i - 1][j]
if (j >= cw) {
table[i][j] = Math.max(table[i][j], v[i] + table[i - 1][j - cw])
}
}
}
return table[len - 1][C]
}
console.log(knapsack([1, 2, 3], [3, 7, 12], 5))
最長遞增子序列
要注意最后的Max哦
第一層循環(huán)從1開始
function lis(n) {
let len = n.length
let array = new Array(len).fill(1)
for (let i = 1; i < len; i++) {
for (let j = 0; j < i; j++) {
if (n[i] > n[j]) {
array[i] = Math.max(array[i], array[j] + 1)
}
}
}
return Math.max.apply(Math, array)
}
console.log(lis([0, 3, 4, 17, 2, 8, 6, 10, 11]))
你在工作中遇到的兼容性問題
- URLSearchParams 兼容性問題 在IE和Edge下不可用 解決 使用polyfill
- 微信IOS下不會(huì)主動(dòng)加載音頻文件 解決 先放置一個(gè)空的mp3文件 然后首次加載直接load方法+永遠(yuǎn)不摧毀這個(gè)audio
- 微信的登錄跳轉(zhuǎn)緩存問題 解決 將初始化函數(shù)傳入到一個(gè)全局變量中 登錄完成后手動(dòng)觸發(fā)所有回調(diào)
轉(zhuǎn)美式3個(gè)數(shù)字一個(gè),
function commafy(num) {
num = num.toString().split('.')
let head = num[0]
let tail = num[1]
head = head
.split('')
.reverse()
.map((v, i) => (i && i % 3 === 0 ? v + ',' : v))
.reverse()
.join('')
return head + '.' + tail
}
隨機(jī)化數(shù)組
注意,Math.random() 是無法獲取一個(gè)等于1的數(shù)字的 排除1
function shuffle(arr) {
return arr.sort(() => Math.random() - 0.5)
}
function shuffle(arr) {
arr = arr.slice()
let len = arr.length
for (let i = 0; i < len; i++) {
let swapIndex = getRandomInt(0, i)
let temp = arr[i]
arr[i] = arr[swapIndex]
arr[swapIndex] = temp
}
return arr
}
function getRandomInt(min, max) {
return Math.floor(Math.random() * (max - min + 1) + min)
}
嚴(yán)格模式
使用"use strict"可以開啟嚴(yán)格模式有额,在嚴(yán)格模式下將會(huì)開啟更加嚴(yán)格的代碼錯(cuò)誤檢查般又,很多在非嚴(yán)格模式下允許的操作會(huì)在嚴(yán)格模式下被禁止
列如
- 阻止那些意外被創(chuàng)建的全局變量,message = 1 如果該變量沒有被聲明巍佑,則不會(huì)幫你自動(dòng)創(chuàng)建
- 不允許重復(fù)的對(duì)象key
3 不允許重復(fù)的參數(shù)名
4 evel將剝脫在外部作用域創(chuàng)建變量的能力
閉包
閉包是指一個(gè)有權(quán)訪問另外一個(gè)函數(shù)作用域中的變量的函數(shù)
創(chuàng)建閉包最簡單的方法就是從一個(gè)函數(shù)里面返回另一個(gè)匿名函數(shù)
閉包是本質(zhì)是其內(nèi)部的函數(shù)引用了外部函數(shù)的活動(dòng)對(duì)象茴迁。只要內(nèi)部函數(shù)的引用不釋放,這個(gè)活動(dòng)對(duì)象不會(huì)被清除引用和回收萤衰。內(nèi)部函數(shù)將會(huì)一直有能力訪問其外部引用的變量笋熬。
閉包的真正用途是為了保存狀態(tài),讓變量不被回收腻菇。
這里可以談一下著名的SICP 里面用閉包實(shí)現(xiàn)了序?qū)Ω烀撤N意義上說昔馋,他和OOP的對(duì)象很類似,都能存放狀態(tài)糖耸。
函數(shù)節(jié)流與函數(shù)防抖
function debounce(fn, dealy) {
let timer = null
return function(...args) {
const ctx = this
if (timer) {
clearTimeout(timer)
}
timer = setTimeout(() => {
fn.apply(ctx, args)
}, dealy)
}
}
function throttle(fn, interval) {
let last = 0
return function(...args) {
const now = Date.now()
const ctx = this
if (now - last > interval) {
last = now
fn.apply(ctx, args)
}
}
}
function compose(fn, dealy, interval) {
let d = debounce(fn, dealy)
let t = throttle(fn, interval)
return function(...args) {
let ctx = this
d.apply(ctx, args)
t.apply(ctx, args)
}
}
function compose(fn, dealy, interval) {
let timer = null
let last = 0
return function(...args) {
const ctx = this
const now = Date.now()
const run = () => {
fn.apply(ctx, args)
}
if (now - last > interval) {
run()
last = now
return
}
if (timer) {
clearTimeout(timer)
}
timer = setTimeout(run, dealy)
}
}
談?wù)勈裁此闳珬9こ處?/h2>
一 基本掌握前后端開發(fā)的某種技術(shù)秘遏,如一門后端語言+前端框架
二 在思維方式上 要做到?jīng)]有局限,不會(huì)只注意某種環(huán)節(jié)或者技術(shù)嘉竟,可以從更高的層次觀察整個(gè)產(chǎn)品邦危,學(xué)歷能力要非常出色,出現(xiàn)什么問題能夠迅速找到相關(guān)技術(shù)學(xué)習(xí)并解決問題