合并两个有序数组
数组乱序
从现有数组里随机取一个,放入新数组:(Fisher–Yates洗牌算法)
function shuffle(arr) {
let result = []
while(arr.length > 0) {
const randomIndex = Math.floor(Math.random() * arr.length)
result.push(arr[randomIndex])
arr.splice(randomIndex, 1)
}
return result
}
数组去重
遍历数组,不重复的放入新数组:
function unique(arr) {
let result = []
arr.forEach(item => {
if (!result.includes(item)) {
result.push(item)
}
})
return result
}
Set:
Array.from(new Set(arr))
数组扁平
遍历数组,非array加入新数组,array拍平后展开加入:
function flat(arr) {
let result = []
arr.forEach(item => {
if (item instanceof Array) {
result.push(...flat(item))
} else {
result.push(item)
}
})
return result
}
toString:
function flat(arr) {
return arr.toString().split(',').map(item => Number(item))
}
数组自带的flat:
arr.flat() // 参数是扁平化的深度,默认1
数组filter
遍历数组,符合callback条件的加入新数组:
Array.prototype.mockFilter = function(callback) {
let arr = this
let result = []
arr.forEach((item, index) => {
let flag = callback(item, index)
if (flag) {
result.push(item)
}
})
return result
}
数组reduce
设置合适的初始值,遍历数组,累加:
Array.prototype.mockReduce = function(callback, init) {
let arr = this
let acc = init
if (typeof init === 'undefined') {
acc = arr[0]
arr.shift()
}
arr.forEach(item => {
acc = callback(acc, item)
})
return acc
}
call
暂时把函数绑定到一个对象上,调用后删除。
Function.prototype.mockCall = function(target = window, ...args) { // ...是rest参数
target.func = this
let result = target.func(...args) // ...是展开运算符
delete target.func
return result
}
apply ⭐
暂时把函数绑定到一个对象上,调用后删除。
Function.prototype.mockApply = function(target = window, args) {
target.func = this
let result = target.func(...args)
delete target.func
return result
}
bind
返回一个新函数,执行的时候apply传入的this指向和两份参数。
Function.prototype.mockBind = function(target, ...args) {
let func = this
let retFunc = function() {
return func.apply(target, args.concat(...arguments))
}
return retFunc
}
instanceof
遍历对象的原型链,看是否等于构造函数的prototype或null。
function instanceOf(obj, func) {
let proto = obj.__proto__
let prototype = func.prototype
while (true) {
if (proto === prototype) return true
if (proto === null) return false
proto = proto.__proto__
}
}
new
创建一个空对象,绑定原型,调用构造函数,返回对象。
function mockNew(func, ...args) {
let obj = {}
obj.__proto__ = func.prototype
let result = func.apply(obj, args)
return result instanceof Object ? result : obj
}
继承
ES5:寄生组合继承
调用父构造函数,绑定原型,修正原型的constructor指向。
function Parent() {}
function child() {
Parent.call(this)
}
Child.prototype = Object.create(Parent.prototype)
Child.prototype.constructor = Child
ES6:extends
class Parent {}
class Child extends Parent {}
防抖 ⭐
触发后延迟执行,如果中间又被触发,就重新计时。
function debounce(func, wait) {
let timer = null
return function() {
if (timer) clearTimeout(timer)
timer = setTimeout(() => {
func.apply(this, arguments)
}, wait)
}
}
节流 ⭐
不管触发多少次,都按固定频率执行。
function throttle(func, wait) {
let timer = null
return function () {
if (timer) return
timer = setTimeout(() => {
func.apply(this, arguments)
timer = null
}, wait)
}
}
浅拷贝
基础类型直接返回,引用类型遍历赋值。
function shallowClone(obj) {
if (obj instanceof Object) {
let result = obj instanceof Array ? [] : {}
for (let key in obj) {
result[key] = obj[key]
}
return result
} else {
return obj
}
}
深拷贝
基础类型直接返回,引用类型遍历递归赋值。
function deepClone(obj) {
if (obj instanceof Object) {
let result = obj instanceof Array ? [] : {}
for (let key in obj) {
result[key] = deepClone(obj[key])
}
return result
} else {
return obj
}
}
eventEmitter
主要方法有:on、emit、off、once
class EventEmitter {
constructor() {
this.events = {}
}
on(name, cb) {
if (!this.events[name]) {
this.events[name] = [cb]
} else {
this.events[name].push(cb)
}
}
emit(name, ...args) {
if (this.evnets[name]) {
this.events.forEach(func => {
func.call(this, ...args)
})
}
}
off(name, cb) {
if (this.events[name]) {
this.events[name] = this.events[name].filter(func => {
return func !== cb
})
}
}
once(name, cb) {
let onlyOnce = () => {
cb.apply(this, arguments)
this.off(name, onlyOne)
}
this.on(name, onlyOnce)
}
}
currying
如果实参大于等于形参,直接执行;如果小于形参,返回一个函数继续接收参数。
function curry(func) {
function curried(...args) {
if (args.length >= func.length) {
return func.apply(this, args)
} else {
return function(...otherArgs) {
return curried.apply(this, args.concat(otherArgs))
}
}
}
return curried
}
// 测试
function sum (a, b, c) {
return a + b + c
}
const curriedSum = curry(sum)
console.log(curriedSum(1, 2, 3))
console.log(curriedSum(1)(2,3))
console.log(curriedSum(1)(2)(3))
冒泡排序
两两比较,大的往后放。
function bubbleSort(arr) {
for (let i = 0; i < arr.length - 1; i++) {
for (let j = 0; j < arr.length - i - 1; j++) {
if (arr[j] > arr[j + 1]) {
[arr[j], arr[j + 1]] = [arr[j + 1], arr[j]]
}
}
}
return arr
}
插入排序
两两比较,小于往前放,大于停止。
function insertSort(arr) {
for (let i = 0; i < arr.length - 1; i++) {
for (let j = i+ 1; j > 0; j--) {
if (arr[j] < arr[j - 1]) {
[arr[j - 1], arr[j]] = [arr[j], arr[j - 1]]
} else {
break
}
}
}
return arr
}
// 冒泡比较的是无序部分,插入比较的是有序部分,利用有序减少了比较次数,所以比冒泡快。
快速排序
取一个中间数,其余大的放左边,小的放右边,然后递归。
function quickSort(arr) {
if (arr.length <= 1) {
return arr
}
let left = [], right = [], middle = arr.splice(0, 1)
arr.forEach(item => {
if (item < middle) {
left.push(item)
} else {
right.push(item)
}
})
return quickSort(left).concat(middle, quickSort(right))
}
斐波那契(递归)
function fibonacci(n) {
if (n === 1 || n === 2) {
return 1
} else {
return fibonacci(n - 1) + fibonacci(n - 2)
}
}
斐波那契(迭代)
function fibonacci(n) {
let a = 1, b = 1;
while (n > 1) {
[a, b] = [b, a + b]
n--
}
return a
}
Promise
基础版本
class Promise{
constructor(executor){
this.state = 'pending';
this.value = undefined;
this.error = undefined;
const resolve = value => {
// 状态不可逆
if (this.state === 'pending') {
this.state = 'fulfilled';
this.value = value;
}
};
const reject = error => {
// 状态不可逆
if (this.state === 'pending') {
this.state = 'rejected';
this.error = error;
}
};
try{
// 作为参数传入的函数会立即执行
executor(resolve, reject);
} catch (err) {
reject(err);
}
}
then(onFulfilled,onRejected) {
if (this.state === 'fulfilled') {
onFulfilled(this.value);
};
if (this.state === 'rejected') {
onRejected(this.error);
};
}
}
解决异步
class Promise{
constructor(executor){
this.state = 'pending';
this.value = undefined;
this.error = undefined;
// 回调栈(用数组是因为Promise可以绑定多个回调函数,状态改变时一起执行)
this.onResolvedCallbacks = [];
this.onRejectedCallbacks = [];
const resolve = value => {
if (this.state === 'pending') {
this.state = 'fulfilled';
this.value = value;
// 如果是异步,在这里执行回调
this.onResolvedCallbacks.forEach(fn=>fn());
}
};
const reject = error => {
if (this.state === 'pending') {
this.state = 'rejected';
this.error = error;
// 如果是异步,在这里执行回调
this.onRejectedCallbacks.forEach(fn=>fn());
}
};
try{
executor(resolve, reject);
} catch (err) {
reject(err);
}
}
then(onFulfilled,onRejected) {
// 如果是同步,即状态已经改变,直接在then中执行回调
if (this.state === 'fulfilled') {
onFulfilled(this.value);
};
if (this.state === 'rejected') {
onRejected(this.error);
};
// 如果是异步,即处于pending状态,就先把回调存入回调栈,等状态改变后在resolve/reject中执行
if (this.state === 'pending') {
this.onResolvedCallbacks.push(()=>{
onFulfilled(this.value);
})
this.onRejectedCallbacks.push(()=>{
onRejected(this.error);
})
}
}
}
Promise.resolve
当知道Promise将始终解决时,可以使用Promise.resolve。
Promise.resolve = (value) => {
return new Promise((resolve, reject) => {
resolve(value);
})
}
Promise.resolve
当知道Promise将始终拒绝时,可以使用Promise.resolve。
Promise.reject = (error) => {
return new Promise((resolve, reject) => {
reject(error);
})
}
Promise.race
传入的所有promise有一个成功,就执行resolve。
Promise.race = (promises) => {
return new Promise((resolve, reject) => {
promises.forEach(promise => {
promise.then(resolve, reject);
})
})
}
Promise.all ⭐
传入的所有promise都成功,才执行resolve。
Promise.all = (promises) => {
return new Promise((resolve, reject) => {
let result = [];
let index = 0;
promises.forEach(promise => {
promise.then(value => {
result.push(value);
index++;
if (index === promises.length) {
resolve(result);
})
}, reject)
})
})
}
优化
- 输入值类型判断。
- 拷贝一份输入的数组,以免操作改变原数组。