JavaScript的「手写」知识点
1、实现原生 AJAX 封装
const ajax = {
get(url, fn) {
const xhr = new XMLHttpRequest()
xhr.open('GET', url, true)
xhr.onreadystatechange = function() {
if(xhr.readyState === 4) {
fn(xhr.responseText)
}
}
xhr.send()
},
post(url, fn, data) {
const xhr = new XMLHttpRequest()
xhr.open('POST', url, true)
xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded')
xhr.onreadystatechange = function() {
if(xhr.readyState === 4) {
fn(xhr.responseText)
}
}
xhr.send(data)
}
}
2、实现 new 过程
function myNew(fn, ...args) {
const obj = {
}
obj.__proto__ = fn.prototype
fn.apply(obj, args)
return obj
}
3、打乱一个数组
// 方法1
const shuffle = (arr) => {
return arr.sort(() => {
return Math.random() > 0.5 ? 1 : -1
})
}
// 方法2
const shuffle = (arr) => {
let i = arr.length
while (i) {
let j = Math.floor(Math.random() * i--)
[arr[j], arr[i]] = [arr[i], arr[j]]
}
}
4、防抖函数
function debounce(fn, delay = 200) {
let timer
return function(...args) {
if (timer) {
clearTimeout(timer)
timer = null
}
timer = setTimeout(() => {
fn.apply(this, args)
clearTimeout(timer)
timer = null
}, delay)
}
}
5、节流函数
function throttle(fn, delay = 200) {
let flag = true
return function(...args) {
if(!flag) {
return
}
flag = false
let timer = setTimeout(() => {
fn.apply(this, args)
flag = true
clearTimeout(timer)
}, delay)
}
}
6、数组去重
// 方法1
const quchong = (arr) => {
return [...new Set(arr)]
}
// 方法2
const quchong = (arr) => {
const res = []
arr.reduce((pre, next) => {
if (!pre.has(next)) {
pre.set(next, 1)
res.push(next)
}
return pre
}, new Map())
return res
}
7、setTimeout 实现 setInterval
const mySetInterval = (fn, delay) => {
let timer = null
const interval = () => {
fn()
timer = setTimeout(interval, delay)
}
timer = setTimeout(interval, delay)
return {
cancel: () => {
clearTimeout(timer)
}
}
}
8、setInterval 实现 setTimeout
const mySetTimeout = (fn, delay) => {
const timer = setInterval(() => {
fn()
clearInterval(timer)
}, delay)
}
9、compose 函数
function fn1(x) {
return x + 1;
}
function fn2(x) {
return x + 2;
}
function fn3(x) {
return x + 3;
}
function fn4(x) {
return x + 4;
}
const compose = (...fns) => {
if (fns.length === 0) return (num) => num
if (fns.length === 1) return fns[0]
return fns.reduce((pre, next) => {
return (num) => {
return pre(next(num))
}
})
}
const a = compose(fn1, fn2, fn3, fn4)
console.log(a(1)); // 1+2+3+4=11
10、curring 函数
const add = (a, b, c) => a + b + c;
const currying = (fn, ...args) => {
let allArgs = [...args]
const num = fn.length
const res = (...args2) => {
allArgs = [...allArgs, ...args2]
if (allArgs.length === num) {
return fn(...allArgs)
} else {
return res
}
}
return res
}
const a = currying(add, 1);
console.log(a(2)(3)) // 1 + 2 + 3=6
11、LRU 算法
class LRUCache {
constructor(size) {
this.size = size
this.cache = new Map()
}
get(key) {
const hasKey = this.cache.has(key)
if (!hasKey) {
return -1
} else {
const val = this.cache.get(key)
this.cache.delete(key)
this.cache.set(key, val)
return val
}
}
put(key, value) {
const hasKey = this.cache.has(key)
if (hasKey) {
this.cache.delete(key)
}
this.cache.set(key, value)
if (this.cache.size > this.size) {
this.cache.delete(this.cache.keys().next().value)
}
}
}
12、发布订阅模式
class EventEmitter {
constructor() {
this.cache = []
}
on(name, fn) {
const tasks = this.cache[name]
if (tasks) {
tasks.push(fn)
} else {
this.cache[name] = [fn]
}
}
off(name, fn) {
if (!name) {
this.cache = []
return
}
const tasks = this.cache[name]
if (tasks) {
if (!fn) {
this.cache[name] = []
}
const index = tasks.findIndex(item => item === fn)
if (index >= 0) {
tasks.splice(index, 1)
}
}
}
emit(name, ...args) {
// 复制一份。防止回调里继续on,导致死循环
const tasks = this.cache[name].slice()
if (tasks) {
for (let task of tasks) {
task(...args)
}
}
}
once(name, cb) {
const fn = (...args) => {
cb(...args)
this.off(name, fn)
}
this.on(name, fn)
}
}
13、DOM 转 对象
const dom2tree = (node) => {
const obj = {
}
obj.tag = node.tagName
obj.children = []
node.childNodes.forEach(child => obj.children.push(dom2tree(child)))
return obj
}
14、对象 转 DOM
function _render(vnode) {
if (typeof vnode === 'number') {
vnode = String(vnode)
}
if (typeof vnode === 'string') {
return document.createTextNode(vnode)
}
const dom = document.createElement(vnode.tag)
if (vnode.attrs) {
Object.keys(attrs).forEach(key => {
const attr = artts[key]
dom.setAttribute(key, attr)
})
}
vnode.children.forEach(child => dom.appenChild(_render(child)))
return dom
}
15、判断对象环引用
const cycleDetector = (obj) => {
const arr = [obj]
let flag = false
const cycle = (o) => {
const values = Object.values(o)
for (let value of values) {
if (typeof value === 'object' && value !== null) {
if (arr.includes(value)) {
flag = true
return
}
arr.push(value)
cycle(value)
}
}
}
cycle(obj)
return flag
}