//防抖
function debounce(fn){
let timeout = null
return function(){
clearTimeout(timeout)
timeout = setTimeout(()=>{
fn.apply(this,arguments)
},500)
}
}
//节流
function throttle(fn){
let canRun = true
return function(){
if(!canRun) return false
canRun = false
setTimeout(()=>{
fn.apply(this,arguments)
canRun = true
},500)
}
}
//函数柯里化
function add(){
let _args = Array.prototype.slice.call(arguments)
var _adder = function(){
_args.push(...arguments)
return _adder
}
_adder.toString = function(){
return _args.reduce((a,b)=>{
return a+b
})
}
return _adder
}
console.log(add(1)(2)(3))
console.log(add(1, 2)(3))
//函数柯理化 ----- 头条
function curry(fn,...args){
return function cur(){
if(arguments.length === 0){
return fn.apply(this,args)
}else{
args = args.concat([...arguments])
return cur
}
}
}
function sum(a, b, c) {
return a + b + c
}
let myCurry = curry(sum, 3)
console.log(myCurry(2, 4)())
//js reduce: https://juejin.cn/post/6935737320335228936
/*1、定义和用法
reduce() 方法接收一个函数作为累加器,数组中的每个值(从左到右)开始缩减,最终计算为一个值。reduce() 可以作为一个高阶函数,用于函数的 compose。
注意: reduce() 对于空数组是不会执行回调函数的。
2、语法
array.reduce(function(total, currentValue, currentIndex, arr), initialValue)
1、total (必需。初始值, 或者计算结束后的返回值)。
2、currentValue (必需。当前元素)
3、currentIndex (可选。当前元素的索引)
4、arr (可选。当前元素所属的数组对象。)
5、initialValue (可选。传递给函数的初始值)*/
//5、reduce简单用法:作为累加器函数,进行求和、求乘
var arr = [1, 2, 3, 4]
var sum = arr.reduce((x,y)=>x+y)
var mul = arr.reduce((x,y)=>x*y)
console.log( sum )
console.log( mul )
//6、reduce高级用法:
//(1)计算数组中每个元素出现的次数
let language = ['Chinese', 'English', 'Japanese', 'Chinese', 'English']
let languageNum = language.reduce((total,cur)=>{
if(cur in total){
total[cur]++
}else{
total[cur] = 1
}
return total
},{})
console.log(languageNum)
//(2)数组去重
let arr2 = [1,2,3,4,4,1]
let newArr = arr2.reduce((total,cur)=>{
if(!total.includes(cur)){
return total.concat(cur)
}else{
return total
}
},[])
console.log(newArr)
//(3)求数组最大值(最小值同理)
let arr3 = [23,123,342,12]
let max = arr3.reduce((total,cur,index,arr) => {
return total > cur ? total : cur
})
console.log(max) //342
//(4)求最大值
let arr4 = [68,168,666,12]
let max4 = arr4.reduce((total,cur,index,arr) => {
return total > cur ? total : cur
})
console.log(max4) //666
//(5)将字符串转换为整数
let str = '1688'
let strParseInt = str.split('') // 得到 ['1', '6', '8', '8']
.map(item => {return item.charCodeAt() - 48}) // 得到 [1, 6, 8, 8]
.reduce((a, b) => {return a * 10 + b}) // 得到 1688
console.log(strParseInt)
//(6)二维数组转化为一维
let arr6= [[0, 1], [2, 3], [4, 5]]
let newArr6 = arr6.reduce((total,cur)=>{
return total.concat(cur)
},[])
console.log(newArr6)
//(7)多维数组转化为一维
/*let arr7 = [[0, 1], [2, 3], [4,[5,6,7]]]
const newArr7 = function(arr7){
return arr7.reduce((total,cur)=>total.concat(Array.isArray(cur)?newArr7(cur):cur),[])
}
console.log(newArr7(arr7))
//(8)对象里的属性求和
var result = [{
subject: 'male',
score: 45
}, {
subject: 'female',
score: 50
}]
var sum = result.reduce(function(total, cur) {
return cur.score + total
}, 0)
console.log(sum) //95
/*
//reduce在海外H5的应用
import resources from '@/utils/locales/resources.js'
import i18n from "i18next"
//获取不确定路径深度的值
const getObj = (obj,path,defaultVal) => {
let pathCopy = path.split('.')
return pathCopy.reduce((pre,cur)=>{
return (pre||{})[cur]
},obj) || defaultVal
}
let t = (type)=>{
let obj = resources[i18n.language]['translation']
return getObj(obj,type,'')
}
export default t*/
//实现原生reduce函数
Array.prototype.reduceFunc = function(fn,initValue){
let acc = initValue || this[0]
const startIndex = initValue?0:1
for(let i=startIndex
acc = fn(acc,this[i],i,this)
}
return acc
}
var arr = [1,2,3,4]
var result=arr.reduceFunc((a,b)=>{
return a+b
},0)
console.log(result)
//实现bind函数
Function.prototype.bind = function(fn){
const args = [].slice.call(arguments,1)
const context = this
return function(){
const _args = args.concat([].slice.call(arguments,1))
return context.apply(fn,_args)
}
}
//js实现filter
Array.prototype.my_filter = function (callback) {
if (!Array.isArray(this) || !this.length || typeof callback !== 'function') {
return []
} else {
let result = []
for (let index = 0
const element = this[index]
if (callback(element, index, this)) {
result.push(element)
}
}
return result
}
}
let arr = [1, 2, 3, 4, 5]
let res = arr.my_filter((ele, i) => {
return ele % 2 === 0
})
console.log(res)//[2,4]
//js实现开方函数
/*
https://www.cnblogs.com/mapingchuan/p/13038961.html
js实现sqrt开方函数(二分法)
每次取一半的数和当前值做对比,如果比当前值大:下次的起始值不变,末尾值减一半(起始值与末尾值的和的一半);如果比当前值小:下次的起始值加一半(起始值与末尾值的和的一半),末尾值不变;直到起始值与末尾值的差值在指定范围内才结束,注意由于浮点数计算问题需要阻止死循环的情况(“起始值与末尾值的和的一半”与原来的值仍旧相同)
如1000:第一次的值为(0,1000)
1、 500*500 >1000 下一次用(0,500)
2、 250*250 >1000 下一次用(0,250)
3、 125*125 >1000 下一次用 (0,125)
4、 62.5*62.5 >1000 下一次用 (0,62.5)
5、 31.25*31.25 <1000 下一次用 (31.25,62.5)
6、 46.875*46.875 >1000 下一次用 (31.25,46.875)
...
...
...
*/
function sqrt(num) {
function sqrtWrapper(min, max) {
let current = (min + max) / 2
let nextMin = min, nextMax = max
if (current * current > num) {
nextMax = current
} else {
nextMin = current
}
if (min === nextMin && max === nextMax) {
return current
}
else if (nextMax - nextMin < (1 / new Array(17).fill(10).reduce((a, b) => a * b, 1))) {
return current
} else {
return sqrtWrapper(nextMin, nextMax)
}
}
return sqrtWrapper(0, num)
}
console.time()
console.log(sqrt(3))
console.timeEnd()
//js实现useState baidu
//useState
let _state=[]
let index = 0
const myUseState = num =>{
const currentIndex = index
_state[currentIndex] = _state[currentIndex] === undefined ?num:_state[currentIndex]
const setN = num1=>{
_state[currentIndex]=num1
render()
}
index+=1
return [_state[currentIndex],setN]
}
function render(){
index = 0
ReactDOM.render(<App />,rootElement)
}
//instensof实现
function instensof(left,right){
let leftVal = left.__proto__
let rightVal = right.prototype
while(true){
if(leftVal === null){
return false
}
if(leftVal === rightVal){
return true
}
leftVal = leftVal.__proto__
}
}
//
// new实现原理
function _new(Func, ...args) {
//默认创建一个实例对象(而且是属于当前这个类的一个实例)
let obj = {}
//也会把类当做普通函数执行
//执行的时候要保证函数中的this指向创建的实例
let result = Func.call(obj, ...args)
//若客户自己返回引用值,则以自己返回的为主,否则返回创建的实例
if ((result !== null && typeof result === "object") || (typeof result === "function")) {
return result
}
return obj
}
//js实现深拷贝
let deepCopy = (data) => {
const t = typeOf(data)
let o
if (t
=== 'array') {
o = []
} else if (t === 'object') {
o = {}
} else {
return data
}
if (t === 'array') {
for (let i = 0
o.push(deepCopy(data[i]))
}
} else if (t === 'object') {
for (let i in data) {
o[i] = deepCopy(data[i])
}
}
return o
}
function typeOf(obj) {
const toString = Object.prototype.toString
const map = {
'[object Boolean]': 'boolean',
'[object Number]': 'number',
'[object String]': 'string',
'[object Function]': 'function',
'[object Array]': 'array',
'[object Date]': 'date',
'[object RegExp]': 'regExp',
'[object Undefined]': 'undefined',
'[object Null]': 'null',
'[object Object]': 'object'
}
return map[toString.call(obj)]
}
//冒泡排序 https://segmentfault.com/a/1190000014175918
/*
数组中有 n 个数,比较每相邻两个数,如果前者大于后者,就把两个数交换位置;
这样一来,第一轮就可以选出一个最大的数放在最后面;
那么经过 n-1(数组的 length - 1) 轮,就完成了所有数的排序。
*/
/*
性能
时间复杂度: 平均时间复杂度O(n*n) 、最好情况O(n)、最差情况O(n*n)
空间复杂度: O(1)
稳定性:稳定
时间复杂度指的是一个算法执行所耗费的时间
空间复杂度指运行完一个程序所需内存的大小
稳定指,如果a=b,a在b的前面,排序后a仍然在b的前面
不稳定指,如果a=b,a在b的前面,排序后可能会交换位置*/
/*冒泡 升序*/
var arr = [3,4,1,2]
function bubbleSort(arr){
var len = arr.length-1
var flag = true
for(var i = 0
for(var j=0
if(arr[j]>arr[j+1]){
var temp = arr[j]
arr[j]=arr[j+1]
arr[j+1]=temp
flag=false
}
}
if(flag){
break
}
}
return arr
}
console.log(bubbleSort(arr))
//快速排序 https://segmentfault.com/a/1190000017814119
/*
快速排序的3个基本步骤:
从数组中选择一个元素作为基准点
排序数组,所有比基准值小的元素摆放在左边,而大于基准值的摆放在右边。每次分割结束以后基准值会插入到中间去。
最后利用递归,将摆放在左边的数组和右边的数组在进行一次上述的1和2操作。*/
/*在平均状况下,排序n个项目要O(nLogn)次比较。在最坏状况下则需要O(n^2)次比较,但这种状况并不常见*/
var arrQuick = [15,13,3,2,7]
function quickSort(arrQuick){
if(arrQuick.length<=1){
return arrQuick
}
var pivotIndex = Math.floor(arrQuick.length/2)
var pivot = arrQuick.splice(pivotIndex,1)[0]
var left = []
var right = []
for(var i=0
if(arrQuick[i]<pivot){
left.push(arrQuick[i])
}else{
right.push(arrQuick[i])
}
}
return quickSort(left).concat([pivot],quickSort(right))
}
console.log(quickSort(arrQuick))
//promise实现控制并发
//使用递归控制并发
function request(urls,max,callback){
let urlsCopy = [...urls] //浅拷贝复制参数,防止修改外部变量
let index = 0 //当前并发数
function req(){
index++
console.log('当前并发数-----',index)
fetch(urlsCopy.shift()).then(res=>{ //把数组的第一个元素从其中删除,并返回第一个元素的值,且改变原数组
index--
if(urlsCopy.length>0){ //如果传入的urlsCopy还有长度,则递归
req()
}else if(index===0){ //如果传入的urlsCopy长度为0,且当前并发数也为0,说明所有任务执行完毕,则调用callback
callback && callback()
}
})
if(index<max){ //这里如果当前并发数小于设置的最大并发数max,则递归
req()
}
}
req()
}
function fetch(url){
let time = Math.random()*1000 +1000 //模拟异步任务耗时,1000ms~2000ms随机
console.log("time-------",time)
return new Promise((resolve,reject)=>{ //模拟网络请求
try{
setTimeout(()=>{resolve()},time)
}catch(e){
reject(e)
}
})
}
let urlArray=[1,2,3,4,5,6,7,8,9] //模拟请求的url数组
function cb(){ //任务都执行完成后的回调
console.log('finally')
console.timeEnd()
//console.log(11,urlArray)
}
console.time()//打印任务开始时间
request(urlArray,8,cb)
//js转为树层结构
var source = [{
id: 1,
pid: 0,
name: 'body'
}, {
id: 2,
pid: 1,
name: 'title'
}, {
id: 3,
pid: 1,
name: 'div'
}, {
id: 4,
pid: 3,
name: 'span'
}, {
id: 5,
pid: 3,
name: 'icon'
}, {
id: 6,
pid: 4,
name: 'subspan'
}]
function toTree(data) {
let result = []
if(!Array.isArray(data)) {
return result
}
data.forEach(item => {
delete item.children
})
let map = {}
data.forEach(item => {
map[item.id] = item
})
data.forEach(item => {
let parent = map[item.pid]
if(parent) {
(parent.children || (parent.children = [])).push(item)
} else {
result.push(item)
}
})
return result
}
console.log(toTree(source))
//A-1 B-2
function convert(num) {
let len = str.length
let index = 0
let res = ''
function fn(num) {
let _s = Math.floor(num / len) //取商
let _y = num % len //取余
if (num < len) {
res = str[num - 1]
return res
}
if (_s > len) {
index++
fn(_s)
} else {
let _str = ''
for (let i = 0
_str += str[i]
}
res = _str += str[_y - 1]
return res
}
return res
}
fn(num)
return res
}
console.log(convert(1))
console.log(convert(27))
console.log(convert(79))
console.log(convert(556))
//比较版本号
function compareVersion(version1, version2) {
version1 = version1.split(".")
version2 = version2.split(".")
var len = Math.max(version1.length,version2.length)
//如果当前版本号小于len,补0
while (version1.length < len){
version1.push("0")
}
while (version2.length < len){
version2.push("0")
}
for(var i = 0
var num1 = parseInt(version1[i])
var num2 = parseInt(version2[i])
if(num1 > num2){
return 1
}else{
return -1
}
}
return 0
}
// 测试用例
console.log(compareVersion("0.20.7", "0.20.8") === -1)
console.log(compareVersion("0.20.9", "0.20.8") === 1)
console.log(compareVersion("0.20.08", "0.20.8") === 0)
console.log(compareVersion("0.20.08", "0.20.8.1") === -1)
console.log(compareVersion("0.20.8.0", "0.20.8") === 0)
console.log(compareVersion("0.20.8.1", "0.20.8") === 1)
console.log(compareVersion("0.020", "0.20") === 0)
//js实现反转链表:https://leetcode-cn.com/problems/reverse-linked-list-ii/solution/fan-zhuan-lian-biao-ii-by-leetcode-solut-teyq/
//js实现反转单向链表:https://segmentfault.com/a/1190000022040766
/*function ListNode(x){
this.val = x
this.next = null
}*/
//时间复杂度O(n),空间复杂度O(1)
function ReverseList(pHead) {
//保存当前结点
let cur=pHead
if(cur==null||cur.next==null) return cur
let prev=null
let next=null
while(cur){
next=cur.next
cur.next=prev
prev=cur
cur=next
}
return prev
}
// 二叉树路径和为target的所有路径
function fn(root,target){
let result = []
function handle(root,target,paths,path){
if(root === null) return []
path = [...path,root.value]
paths.push(root.value)
if(root.left === null && root.right === null && sum === target){
result.push(paths)
return
}
handle(root.left,target-root.value,paths,path)
handle(root.right,target-root.value,paths,path)
}
handle(root,target,result,[])
}
//中间件
function fn1(ctx,next){
console.log(1)
next()
console.log(11)
}
function fn2(ctx,next){
console.log(2)
next()
ctx.id = 5
console.log(22,ctx)
}
function fn3(ctx,next){
ctx.id++
console.log(3)
next()
console.log(33)
}
function compose(arr){
let index = 0
let len = arr.length
return function (ctx){
function next(){
index++
if(index>=len) return
arr[index](ctx,next)
}
arr[index](ctx,next)
}
}
compose([fn1,fn2,fn3])({id:4})
// 判断小括号是否成对出现
function isToggle(str){
let arr= []
for(let i =0
if (str[i] === '('){
arr.push('(')
}else if (str[i] === ')'){
if (arr.length === 0){
return false
}else{
arr.pop()
}
}
}
if (arr.length === 0 ) {
return true
}else{
return false
}
}
console.log(isToggle(')221px(2323)(238(23)324)'))
console.log(isToggle('221px(2323)(238(23)324)('))
//js实现链表:https://segmentfault.com/a/1190000019076293
class LinkedList {
constructor() {
this.head = null
this.tail = null
this.length = 0
}
//isEmpty() 方法是一个帮助函数,如果链表为空,则返回true。
isEmpty() {
return this.length === 0
}
//这个实用程序方法用于打印链表中的节点,仅用于调试目的。
printList () {
const nodes = []
let current = this.head
while (current) {
nodes.push(current.value)
current = current.next
}
return nodes.join(' -> ')
}
push(value) {
const node = Node(value)
// The list is empty
if (this.head === null) {
this.head = node
this.tail = node
this.length++
return node
}
this.tail.next = node
this.tail = node
this.length++
}
pop() {
if (this.isEmpty()) {
return null
}
const nodeToRemove = this.tail
// There's only one node!
if (this.head === this.tail) {
this.head = null
this.tail = null
this.length--
return nodeToRemove
}
let currentNode = this.head
let secondToLastNode
// Start at the front and iterate until
// we find the second to last node
while (currentNode) {
if (currentNode.next === this.tail) {
// Move the pointer for the second to last node
secondToLastNode = currentNode
break
}
currentNode = currentNode.next
}
// Pop off that node
secondToLastNode.next = null
// Move the tail to the second to last node
this.tail = secondToLastNode
this.length--
// Initialized to this.tail
return nodeToRemove
}
get(index) {
// Index is outside the bounds of the list
if (index < 0 || index > this.length) {
return null
}
if (this.isEmpty()) {
return null
}
// We're at the head!
if (index === 0 ) {
return this.head
}
let current = this.head
let iterator = 0
while (iterator < index) {
iterator++
current = current.next
}
return current
}
delete(index) {
// Index is outside the bounds of the list
if (index < 0 || index > this.length - 1) {
return null
}
if (this.isEmpty()) {
return null
}
if (index === 0) {
const nodeToDelete = this.head
this.head = this.head.next
this.length--
return nodeToDelete
}
let current = this.head
let previous
let iterator = 0
while (iterator < index) {
iterator++
previous = current
current = current.next
}
const nodeToDelete = current
// Re-direct pointer to skip the element we're deleting
previous.next = current.next
// We're at the end
if(previous.next === null) {
this.tail = previous
}
this.length--
return nodeToDelete
}
}
//JavaScript 发布-订阅模式:https://segmentfault.com/a/1190000019260857
/*
1. 定义
发布-订阅模式其实是一种对象间一对多的依赖关系,当一个对象的状态发送改变时,所有依赖于它的对象都将得到状态改变的通知。
订阅者(Subscriber)把自己想订阅的事件注册(Subscribe)到调度中心(Event Channel),
当发布者(Publisher)发布该事件(Publish Event)到调度中心,也就是该事件触发时,
由调度中心统一调度(Fire Event)订阅者注册到调度中心的处理代码。*/
/*实现思路:
1、创建一个对象
2、在该对象上创建一个缓存列表(调度中心)
3、on方法用来把函数fn都加到缓存列表中(订阅者注册事件到调度中心)
4、emit方法取到arguments里第一个当作event,根据event值去执行对应缓存列表中的函数(发布者发布事件到调度中心,调度中心处理代码)
5、off方法可以根据event值取消订阅(取消订阅)
6、once方法只监听一次,调用完毕后删除缓存函数(订阅一次)*/
/*1. 优点
对象之间解耦
异步编程中,可以更松耦合的代码编写
2. 缺点
创建订阅者本身要消耗一定的时间和内存
虽然可以弱化对象之间的联系,多个发布者和订阅者嵌套一起的时候,程序难以跟踪维护*/
let eventEmitter = {
// 缓存列表
list: {},
// 订阅
on (event, fn) {
let _this = this
// 如果对象中没有对应的 event 值,也就是说明没有订阅过,就给 event 创建个缓存列表
// 如有对象中有相应的 event 值,把 fn 添加到对应 event 的缓存列表里
(_this.list[event] || (_this.list[event] = [])).push(fn)
return _this
},
// 监听一次
once (event, fn) {
// 先绑定,调用后删除
let _this = this
function on () {
_this.off(event, on)
fn.apply(_this, arguments)
}
on.fn = fn
_this.on(event, on)
return _this
},
// 取消订阅
off (event, fn) {
let _this = this
let fns = _this.list[event]
// 如果缓存列表中没有相应的 fn,返回false
if (!fns) return false
if (!fn) {
// 如果没有传 fn 的话,就会将 event 值对应缓存列表中的 fn 都清空
fns && (fns.length = 0)
} else {
// 若有 fn,遍历缓存列表,看看传入的 fn 与哪个函数相同,如果相同就直接从缓存列表中删掉即可
let cb
for (let i = 0, cbLen = fns.length
cb = fns[i]
if (cb === fn || cb.fn === fn) {
fns.splice(i, 1)
break
}
}
}
return _this
},
// 发布
emit () {
let _this = this
// 第一个参数是对应的 event 值,直接用数组的 shift 方法取出
let event = [].shift.call(arguments),
fns = [..._this.list[event]]
// 如果缓存列表里没有 fn 就返回 false
if (!fns || fns.length === 0) {
return false
}
// 遍历 event 值对应的缓存列表,依次执行 fn
fns.forEach(fn => {
fn.apply(_this, arguments)
})
return _this
}
}
function user1 (content) {
console.log('用户1订阅了:', content)
}
function user2 (content) {
console.log('用户2订阅了:', content)
}
function user3 (content) {
console.log('用户3订阅了:', content)
}
function user4 (content) {
console.log('用户4订阅了:', content)
}
// 订阅
eventEmitter.on('article1', user1)
eventEmitter.on('article1', user2)
eventEmitter.on('article1', user3)
// 取消user2方法的订阅
eventEmitter.off('article1', user2)
eventEmitter.once('article2', user4)
// 发布
eventEmitter.emit('article1', 'Javascript 发布-订阅模式')
eventEmitter.emit('article1', 'Javascript 发布-订阅模式')
eventEmitter.emit('article2', 'Javascript 观察者模式')
eventEmitter.emit('article2', 'Javascript 观察者模式')
// eventEmitter.on('article1', user3).emit('article1', 'test111')
/*
用户1订阅了: Javascript 发布-订阅模式
用户3订阅了: Javascript 发布-订阅模式
用户1订阅了: Javascript 发布-订阅模式
用户3订阅了: Javascript 发布-订阅模式
用户4订阅了: Javascript 观察者模式
*/
/*Promise规定:
1、promise存在三个状态(state)pending、fulfilled、rejected
2、pending(等待态)为初始态,并可以转化为fulfilled(成功态)和rejected(失败态)
3、成功时,不可转为其他状态,且必须有一个不可改变的值(value)
4、失败时,不可转为其他状态,且必须有一个不可改变的原因(reason)
5、new Promise((resolve,reject)=>{resolve(value)}) resolve为成功,接收参数value,状态改变为fulfilled,不可再次改变
6、new Promise((resolve,reject)=>{reject(reason)}) reject为失败,接收参数为reason,状态改为rejected,不可再次改变
7、若是executor函数报错直接执行reject()
*/
// Promise 实现
function myPromise(fn){
let self = this
self.value = null
self.callbackList = []
self.status = 'pendding'
function resolve(value){
if(self.status === 'pendding'){
self.status = 'fulfilled'
self.value = value
self.callbackList.forEach((callback)=>{
callback && callback(value)
})
}
}
function reject(value){
if(self.status === 'pendding'){
self.status = 'rejected'
self.value = value
self.callbackList.forEach((callback)=>{
callback && callback(value)
})
}
}
fn(resolve,reject)
}
myPromise.prototype.then = function (callback){
this.callbackList.push(callback)
}
//Promise.all实现
Promise.myall = function(arr){
let result = []
let count = 0
return new Promise((resolve, reject)=>{
for (let i = 0
Promise.resolve(arr[i]).then((res)=>{
result[i] = res
count++
if(count >= arr.length){
resolve(result)
}
},(err)=>{
reject(err)
})
}
})
}
var p1 = new Promise((resolve)=>{setTimeout(()=>{return resolve('p1')
var p2 =new Promise((resolve,reject)=>{setTimeout(()=>{return reject(2)
var p3 =Promise.resolve(2)
var p = Promise.myall([p1,p2,p3])
p.then(e=>{console.log(e)})