Appearance
Array.prototype.sort
js
;[3, 1, 4].sort((a, b) => a - b) // 1,3,4
;[3, 1, 4].sort((a, b) => b - a) // 4,3,1currying 柯里化函数
js
function currying(fn) {
// 通用的柯里化函数
var args = []
return function() {
if (arguments.length === 0) {
return fn.apply(this, args)
} else {
args.push(arguments)
return arguments.callee
}
}
}
var cost = currying(function() {
let result = 0
let len = arguments.length
while (len--) {
result += arguments[len][0]
}
return len
})惰性代码
js
function isEnterKey(keyCode) {
var finalCode
// 遥控器 确认按键
if (keyCode === 13 || keyCode === 14) {
finalCode = keyCode
isEnterKey = function(keyCode) {
return finalCode === keyCode
}
}
}addEventListener 兼容优化写法
js
function addEvent(element, eventName, callback, options) {
if (element.addEventListener) {
// 重写函数
addEvent = function(element, eventName, callback, options) {
return element.addEventListener(eventName, callback, options)
}
} else if (element.attachEvent) {
addEvent = function(element, eventName, callback, options) {
element.attachEvent("on" + eventName, callback, options)
}
} else {
addEvent = function() {
element["on" + eventName] = callback
}
}
addEvent(element, eventName, callback, options)
}同理解绑函数也是同上
js
function _off(el, event, fn) {
if (el.removeEventListener) {
el.removeEventListener(event, fn, false)
} else if (el.detachEvent) {
el.detachEvent("on" + event, fn.bind(el))
} else {
el["on" + event] = null
}
}为元素添加 on 和 trgger 方法
js
// on
Element.prototype.on = Element.prototype.addEventListener
NodeList.prototype.on = function(event, fn) {
Array.prototype.forEach.call(this, el => {
el.on(event, fn)
})
return this
}
// trigger使用代理的单例模式
js
function Man() {}
function createSingleClass(Fn) {
var instance
return function newFn() {
if (this instanceof newFn) {
if (!instance) {
instance = new Fn(...arguments)
}
return instance
}
}
}翻转数字
12345 => 54321
js
function reverserNumber(number) {
var newNumber = 0
while (number >= 1) {
var pop = number % 10
number = Math.floor(number / 10)
newNumber = newNumber * 10 + pop
}
return newNumber
}找到第一个未重复的字符
js
function findFirstNumber(str) {
var i = -1,
s
while ((s = str[++i])) {
if (str.lastIndexOf(s) === i) {
return i
}
}
}限制并发数并尽快的完成任务
需要上传多张,要求尽快的上传完成,但是同时上传的格式为 m
js
function loadImg(url) {
return new Promise(resolve => {
setTimeout(() => {
console.log(`${url} is loaded`)
resolve(url)
}, url * 100)
})
}
// promise 版本
async function limitLoadAwait(urls, handler, limit) {
let sequence = [].concat(urls)
let result = []
let promises = sequence.splice(0, limit).map(async (url, index) => {
result.push(await handler(url))
return index
})
await sequence.reduce(async (pCollect, url, currentIndex) => {
await pCollect
let fastIndex = await Promise.race(promises)
promises[fastIndex] = handler(url).then(res => {
result.push(res)
return fastIndex
})
return promises
}, Promise.resolve())
await Promise.all(promises)
return result
}
// async await 版本
async function limitLoadAwait(urls, handler, limit) {
let sequence = [].concat(urls)
let result = []
let promises = sequence.splice(0, limit).map(async (url, index) => {
result.push(await handler(url))
return index
})
await sequence.reduce(async (pCollect, url, currentIndex) => {
await pCollect
let fastIndex = await Promise.race(promises)
promises[fastIndex] = handler(url).then(res => {
result.push(res)
return fastIndex
})
return promises
}, Promise.resolve())
await Promise.all(promises)
return result
}巧妙结合 promise 和 reduce
需要连续调用三个接口, 如果接口返回 true,继续调用下个接口。直到返回 false 或者到末尾
js
function uploadImg(url) {
return new Promise(resolve => {
setTimeout(() => {
resolve(url)
}, 1000)
})
}
function uploadOneByOne(urls, handler) {
return urls.reduce((p, url) => {
return p.then(res => {
if (res) {
return handler(url)
} else {
return Promise.resolve(false)
}
})
}, Promise.resolve(true))
}
uploadOneByOne([true, true, true, false, true], uploadImg).then(console.log)上述方法缺点是不能提前跳出
循环队列的思考和实现
传统方式实现队列, 借助数组的 pop 和 unshift 方法
js
function Queue() {
var _data = []
this.enQueue = function(value) {
_data.push(value)
}
this.deQueue = function() {
return _data.unshift()
}
}这种方法的弊端是每次入队出队都需要挪动整个队列。 考虑引入头部指针和尾指针
js
function Queue() {
var _data = []
var head = 0,
tail = 0
this.enQueue = function(value) {
_data[head++] = value
}
this.deQueue = function() {
if (_data.length === 0) {
return new Error("队列为空")
} else {
return _data[tail++]
}
}
}这种策略空间换时间的方案,长时间使用后会溢出。所以有循环队列的方案
js
function Queue(initLength) {
var _data = new Array(initLength),
tail = -1,
head = -1,
length = 0
dataLength = initLength
this.enQueue = function(value) {
console.log(value, head, tail)
if (length === dataLength) {
// 溢出
this.resetQueue()
}
head = (head + 1) % dataLength
_data[head] = value
length++
}
this.deQueue = function() {
if (length === 0) {
throw new Error("empty")
}
tail = (tail + 1) % dataLength
length--
var result = _data[tail]
_data[tail] = undefined
return result
}
this.resetQueue = function() {
// 扩容
dataLength = dataLength * 2
_data.length = dataLength
// 如果
if (head < tail) {
var headList = _data.splice(0, head + 1)
_data.splice(tail, 0, ...headList)
}
}
this.toString = function() {
console.log(_data)
}
}动态的去扩大长度,还有 deQueue 应该动态减少长度
经典算法-洗牌算法
js
function shuffleArray(arr) {
var len = arr.length
while (len--) {
var index = Math.floor(Math.random() * len)
;[arr[index], arr[len]] = [arr[len], arr[index]]
}
return arr
}位运算详解
与或非异或
判断是否为奇数 n & 1n | 0 === n n ^ n === 0 n ^ 0 === n~~表示向 0 取整
js
function power(n) {
return 1 << n
}n >>> 0 的含义
toNumber(不能转成 number 的变成 1) => 整数(~~n) => toUint32(负数 + 2 的 30 次方)
GC 那些事儿
- 引用计数法. 对每个开辟的空间进行引用计数, 缺点是回带来内存溢出(循环引用的情况)
- 标记回收法:从根开始,递归的标记能访问的对象,标记完成后,去虽有未标记的对象进行回收
常见的内存泄露情况及处理方案
- 被遗忘的定时器
- 意外的全局变量(使用严格模式)
- 脱离 dom 的引用
- 闭包
脱离的 dom 的引用举例
html
<script>
let div = document.getElementById("fa")
document.body.removeChild(div) // dom删除了
//div=null //切断div对div的引用
console.log(div)
</script>一行代码实现 flat 函数
js
function flat(arr) {
return [].concat.apply([], arr)
}