数组转树
function arrayToTree(arr) {
const map = new Map()
const idMap = new Map()
arr.forEach(i => {
const ids = map.get(i.parentId) || []
ids.push(i.id)
map.set(i.parentId, ids)
idMap.set(i.id, i)
})
return loop(map.get(
function loop(ids, parent = []) {
ids.forEach(id => {
const cur = idMap.get(id)
const next = map.get(id)
const o = { ...cur }
if (next) {
o.children = []
loop(next, o.children)
}
parent.push(o)
})
return parent
}
}
// 测试用例
const arr = [
{ id: 2, label:
{ id: 1, label:
{ id: 3, label:
{ id: 4, label:
{ id: 5, label:
{ id: 6, label:
{ id: 7, label:
{ id: 8, label:
{ id: 9, label:
]
打印结果:[
{
"id": 1,
"label": "1111",
"parentId": "",
"children": [
{
"id": 2,
"label": "2222",
"parentId": 1,
"children": [
{
"id": 3,
"label": "3333",
"parentId": 2,
"children": [
{
"id": 6,
"label": "6666",
"parentId": 3,
"children": [
{
"id": 7,
"label": "7777",
"parentId": 6
}
]
}
]
},
{
"id": 4,
"label": "4444",
"parentId": 2,
"children": [
{
"id": 5,
"label": "5555",
"parentId": 4
}
]
}
]
}
]
},
{
"id": 8,
"label": "8888",
"parentId": "",
"children": [
{
"id": 9,
"label": "9999",
"parentId": 8
}
]
}
]
发布-订阅
class dep {
constructor() {
this.deps = []
}
on(type, fn) {
if (!this.deps[type]) this.deps[type] = []
this.deps[type].push(fn)
}
notify(type, ...args) {
if (!this.deps[type]) throw new Error('不存在')
this.deps[type].forEach(dep => dep.call(this, ...args))
}
off(type, fn) {
const index = this.deps[type].findIndex(dep => dep === fn)
if (index < 0) throw new Error('不存在')
this.deps[type].splice(index, 1)
if (this.deps[type].length === 0) {
delete this.deps[type]
}
}
once(type, fn) {
const c = () => {
fn()
this.deps.off(type, c)
}
this.deps.on(type, c)
}
}
双向绑定
const obj = {}
const input = document.querySelector('input')
Object.defineProperty(obj, 'value', {
get() {
return obj[value]
},
set(newValue) {
if (obj[value] !== newValue) {
obj[value] = newValue
input.value = newValue
}
}
})
input.addEventListener('keyup', function(e) {
obj.value = e.target.value
})
分片渲染
let total = 10000
let pageNum = 20
let curTotal = 0
const container = document.querySelector('.container')
loop()
function loop() {
if (curTotal >= total) return
let nextTotal = Math.min(curTotal + pageNum, total)
for (let i = curTotal
const node = document.createElement('div')
node.innerText = curTotal
container.append(container)
}
curTotal = nextTotal
requestAnimationFrame(loop)
}
LRU
class LRU {
constructor(capacity) {
this.map = new Map()
this.capacity = capacity
}
get(key) {
if (this.map.has(key)) {
const temp = this.map.get(key)
this.map.delete(key)
this.map.set(key, temp)
return temp
} else return -1
}
put(key, value) {
if (this.map.has(key)) {
this.get(key)
} else {
this.map.set(key, value)
if (this.map.size > capacity) {
this.map.delete(this.map.keys().next().value)
}
}
}
}
深拷贝
const SetTag = '[object Set]'
const MapTag = '[object Map]'
const ArrayTag = '[object Array]'
const ObjectTag = '[object Object]'
const NumberTag = '[object Number]'
const StringTag = '[object String]'
const BooleanTag = '[object Boolean]'
const DateTag = '[object Date]'
const SymbolTag = '[object Symbol]'
const ErrorTag = '[object Error]'
const RegExpTag = '[object RegExp]'
const deepTag = [SetTag, MapTag, ArrayTag, ObjectTag]
function getType(target) {
return Object.prototype.toString.call(target)
}
function cloneOtherType(target, type) {
const Ctor = target.constructor
switch(type) {
case NumberTag:
case StringTag:
case BooleanTag:
case DateTag:
case ErrorTag: return new Ctor(target)
case SymbolTag: return Object(Symbol(target.description))
case RegExpTag: return new Ctor(target.source, target.flags)
}
}
function deepClone(target, map = new Map()) {
if (typeof target === 'symbol') return Symbol(target.description)
if (!target || typeof target !== 'object') return target
const type = getType(target)
if (!deepTag.includes(type)) return cloneOtherType(target, type)
if (map.has(target)) return map.get(target)
const res = new target.constructor()
map.set(target, res)
if (type === MapTag) {
target.forEach((value, key) => res.set(key, deepClone(value)))
return res
}
if (type === SetTag) {
target.forEach(item => res.add(deepClone(item)))
return res
}
Reflect.ownKeys(target).forEach(key => {
res[key] = deepClone(target[key], map)
})
return res
}
const target = {
field1: 1,
field2: undefined,
field3: {
child: 'child'
},
field4: [2, 4, 8],
empty: null,
map: new Map().set(1, 'map'),
set: new Set().add('set'),
bool: Boolean(true),
num: new Number(1),
str: new String(1),
date: new Date(),
objSymbol: Object(Symbol('2')),
[Symbol('key')]: 'symbol',
reg: /\d+de$/gm,
error: new Error('error'),
symbol: Symbol('1'),
}
target.target = target
console.log(deepClone(target))
懒加载
const imgs = document.querySelectorAll('.img')
const clientHeight = window.innerHeight || document.documentElement.clientHeight || docuement.body.clientHeight
function lazyload() {
let scrollTop = window.pageYScroll || document.documentElement.scrollTop || document.body.scrollTop
for (let i = 0; i < imgs.length; i++) {
let y = clientHeight + scrollTop - imgs[i].offsetTop
if (y > 0 && y < clientHeight + imgs[i].height) {
img[i].src = imgs[i].getAttribute('data')
}
}
}
window.addEventListener('resize', lazyload)
简单路由跳转
class Router {
constructor() {
this.routes = {}
this.freshRoute = this.freshRoute.bind(this)
this.currentHash = '/'
window.addEventListener('hash', this.freshRoute, false)
window.addEventListener('hashchange', this.freshRoute, false)
}
freshRoute() {
this.currentHash = window.location.hash.slice(1) || '/'
this.routes[this.currentHash]()
}
storeRoute(path, cb) {
this.routes[path] = cb || function(){}
}
}
创建对象
1. new Object()
2. 字面量
3. 工厂模式
function createObj(name) {
const obj = new Object()
obj.name = name
return obj
}
4. 构造函数
function Person(name) {
this.name = name
}
5. 原型对象
function Person(){}
Person.prototype.name = 'vancats'
6. 混合模式
function Person(name) {
this.name = name
}
Person.prototype.intr = function(){}
7. 动态混合
function Person(name) {
this.name = name
if (!Person.prototype.intr) {
Person.prototype.intr = function(){}
}
}
8. class
9. 稳妥构造函数
function Person(name) {
let person = {}
person.getName = function () { return name }
person.setName = function (val) { name = val }
return person
}
继承
function Person(name) {
this.name = name
}
Person.prototype.intr = function() {}
1. 寄生组合继承
function Student(name, age) {
Person.call(this, name)
this.age = age
}
Student.prototype = Object.create(Person.prototype)
Student.prototype.constructor = Student
2. class
class Student extends Person {
constructor(name) {
super(name)
}
}
3. 原型链继承:父类实例变成子类的原型(无法向父类传参)
function Student() { }
Student.prototype = new Person()
Student.prototype.name = 'vancats'
4. 构造函数继承
function Student(name, age) {
Person.call(this, name)
this.age = age
}
5. 实例继承
function Student(name, age) {
let person = new Person(name)
person.age = age
return person
}
6. 拷贝继承:无法获取父类不可 for in 的方法
function Student(name, age) {
let person = new Person(name)
for (var p in person) {
Student.prototype[p] = person[p]
}
this.age = age
}
7. 组合继承
function Student(name, age) {
Person.call(this, name)
this.age = age
}
Student.prototype = new Animal()
Student.prototype.constructor = Student