千分符
'12345678'// '12,345,678'
str.replace(/\B(?=(\d{3})+$)/g, ',')
分析:
- \B(\b)匹配到的是一个位置,\B代表非单词边界(\b表示单词边界)单词边界
- ?=exp表示匹配符合后面正则exp的位置
首先匹配第一个非单词边界(1和2之间),然后?=预测后面的内容为3个连续\d{3}的数字,+做循环,匹配完若为结尾则成功;接着从第二个非单词边界(2和3之间)... 最后查询完符合匹配就是在1和2之间 4和5之间
防抖节流-高频考题
防抖
事件被触发n秒后再执行回调,如果n秒内又被触发则重新计时
比如给输入框在输入的时候要去请求接口,不能说输入一个字就去请求一次,而是等用户全部输入完再去请求,上代码
function debounce(fn,delay){
let timer = null
return function(...args){
timer && clearTimeout(timer)
timer = setTimeout(()=>{
fn.apply(this,args)
},delay)
}
}
节流
规定在一个单位时间内,只能触发一次函数。如果这个单位时间内触发多次函数,只有一次生效。
function throttle(fn,delay){
let t1 = 0
return function(...args){
let t2 = Date.now()
if(t2-t1 >= delay){
fn.apply(this,args)
t1 = t2
}
}
}
call & apply & bind
call
Function.prototype.call = function(context,...args){
context = context ? Object(context) : window
args ||= []
const sym = Symbol()
context[sym] = this
const res = context[sym](...args)
delete context[sym]
return res
}
apply
Function.prototype.call = function(context,args){
context = context ? Object(context) : window
args ||= []
const sym = Symbol()
context[sym] = this
const res = context[sym](...args)
delete context[sym]
return res
}
bind
Function.prototype.bind = function(context,...args){
context = context ? Object(context) : window
args ||= []
const self = this
const resFn = function(...args2){
context.apply(this instanceof resFn ? this : context,args.concat(args2))
}
resFn.prototype = Object.create(self.prototype)
return resFn
}
具体可以参考我这篇文章
柯里化
如何实现 add(2)(3)(4)=9?
function curry(fn,...args){
const length = fn.length
const allArgs = [...args]
const resFn = (...args2)=>{
allArgs.push(...args2)
if(allArgs.length === length){
return fn(...allArgs)
}else{
return resFn
}
}
return resFn
}
const add = (a,b,c)=>a+b+c
const addFn = curry(add)
addFn(2)(3)(4)
发布订阅模式
class EventEmitter {
constructor() {
// key: 事件名
// value: callback [] 回调数组
this.events = {}
}
on(name, callback) {
if (this.events[name]) {
this.events[name].push(callback)
} else {
this.events[name] = [callback]
}
}
off(name, callback) {
if (!this.events[name]) return;
if (!callback) {
// 如果没有callback,就删掉整个事件
this.events[name] = undefined;
}
this.events[name] = this.events[name].filter((item) => item !== callback);
}
once(name,callback){
funtion fn(){
callback()
this.off(name,callback)
}
this.on(name,fn)
}
emit(name, ...args) {
if (!this.events[name]) return
this.events[name].forEach(cb => cb(...args))
}
}
树转列表
const data = [
{
id: 1,
text: '节点1',
parentId: 0,
children: [
{
id: 2,
text: '节点1_1',
parentId: 1
}
]
}
]
function tree2list(tree){
const res = []
function fn(tree){
tree.forEach(function(item){
if(item.children){
fn(item.children)
}
res.push(item)
})
}
fn(tree)
return res
}
列表转树
[
{
id: 1,
text: '节点1',
parentId: 0
},
{
id: 2,
text: '节点1_1',
parentId: 1
}
...
]
function list2tree(list){
const obj = {}
const res = []
arr.forEach(item => {
obj[item.id] = {
...item,
children:[]
}
})
Object.values(obj).forEach(item => {
const parentId = item.parentId
if(+parentId === 0){
res.push(item)
}else{
obj[parentId].children.push(item)
}
})
return res
}
JSON2DOM
{
tag: 'DIV',
attrs:{
id:'app'
},
children: [
{
tag: 'SPAN',
children: [
{ tag: 'A', children: [] }
]
},
{
tag: 'SPAN',
children: [
{ tag: 'A', children: [] },
{ tag: 'A', children: [] }
]
}
]
}
// 把上诉虚拟Dom转化成下方真实Dom
<div id="app">
<span>
<a></a>
</span>
<span>
<a></a>
<a></a>
</span>
</div>
function jsoń2dom(obj){
const dom = document.createElement(obj.tag)
const attrs = obj.attrs || {}
const children = obj.children || []
for(let k in attrs){
dom.setAttribute(k,attrs[k])
}
children.forEach(item => {
dom.appendChild(json2dom(item))
})
return dom
}
DOM2JSON
function dom2json(domTree) {
const obj = {}
obj.tag = domTree.tagName
obj.children = []
domTree.childNodes.forEach(child=>{
if(child.nodeType!==1)return
obj.children.push(dom2json(child))
})
return obj
实现有并行限制的 Promise 调度器
class Scheduler {
constructor(limit) {
this.limit = limit
this.tasks = []
}
start() {
for (let i = 0; i < this.limit; i++) {
this.run()
}
}
add(timer, n) {
const task = () => {
return new Promise(resolve => {
setTimeout(() => {
console.log(n)
resolve()
}, timer)
})
}
this.tasks.push(task)
}
async run() {
if (!this.tasks.length ) return
const t = this.tasks.shift()
await t()
this.run()
}
}
const scheduler = new Scheduler(2);
const addTask = (...args) => {
scheduler.add(...args)
};
addTask(500, "5")
addTask(300, "3")
addTask(100, "1")
addTask(400, "4")
scheduler.start()// 3 1 5 4
LazyMan
// lazyMan(“Hank”).eat(“supper”).sleepFirst(5)输出
// //等待5秒
// Wake up after 5
// Hi This is Hank!
// Eat supper
class LazyMan{
constructor(name){
this.name = name
const t = ()=>{
console.log('this is' + this.name)
this.run()
}
this.task = [t]
setTimeout(()=>{
this.run()
})
}
run(){
const t = this.task.shift()
t && t()
}
eat(food){
this.task.push(() => {
console.log(`${this.name}吃${food}`)
this.run()
})
return this
}
sleep(time){
const t = () => {
console.log(`sleep等${time}秒`)
setTimeout(() => {
this.run()
}, time * 1000)
}
this.task.push(t)
return this
}
sleepFirst(time){
const t = () => {
console.log(`sleepFirst等${time}秒`)
setTimeout(() => {
this.run()
}, time * 1000)
}
this.task.unshift(t)
return this
}
}
function lazyMan(name){
return new LazyMan(name)
}
深拷贝
new
function myNew(Fn,...args){
const obj = Object.create(Fn.prototype)
const res = Fn.apply(obj,args)
return typeof res === 'object' ? res : obj
}
寄生组合继承
function inheritPrototype(super,sub){
sub.prototype = Object.create(super.prototype,{
constructor:{
enumerable:false,
configurable:true,
writable:true,
value:sub
}
})
}
function Super(){}
function Sub(...args){
Super.apply(this,args)
}
inheritPrototype(Super,Sub)