Javascript

手写js高级知识点

实现原生的 AJAX 请求 # js 复制 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 const ajax = { get(url, fn) { const xhr = new XMLHttpRequest(); xhr.open("GET", url, true); // 第三个参数异步与否 xhr.onreadystatechange = function () { if (xhr.readyState === 4) { fn(xhr.responeText); } }; xhr.send(); }, post(url, data, fn) { 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.responeText); } }; xhr.send(data); }, }; 手写 new 的过程 # js 复制 1 2 3 4 5 6 7 8 9 function myNew(fn, ...args) { const obj = {}; obj.__proto__ = fn.prototype; fn.apply(obj, args); return obj; } instanceof 关键字 # js 复制 1 2 3 4 5 6 7 8 9 10 11 12 13 function instanceOf(father, child) { const fp = father.prototype; var cp = child.__proto__; while (cp) { if (cp === fp) { return true; } cp = cp.__proto__; } return false; } 实现防抖函数 # js 复制 1 2 3 4 5 6 7 8 9 10 11 12 function debounce(fn, delay = 500) { let timer; return function () { if (timer) { clearTimeout(timer); } const args = arguments; timer = setTimeout(() => { fn.apply(this, args); // 改变this指向为调用debounce所指的对象 }, delay); }; } 实现节流函数 # js 复制 1 2 3 4 5 6 7 8 9 10 11 12 function throttle(fn, delay = 200) { let flag = true; return function () { if (!flag) return; flag = false; const args = arguments; setTimeout(() => { fn.apply(this, args); flag = true; }, delay); }; } 实现数组去重 # js 复制 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 // 第一种:Map记录 function quchong1(arr) { const newArr = []; arr.reduce((pre, next) => { if (!pre.has(next)) { pre.set(next, 1); newArr.push(next); } return pre; }, new Map()); return newArr; } // 第二种:Set去重 function quchong2(arr) { return [...new Set(arr)]; } 用 setTimeout 实现 setInterval # js 复制 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 function mySetTimout(fn, delay) { let timer = null; const interval = () => { fn(); timer = setTimeout(interval, delay); }; setTimeout(interval, delay); return { cancel: () => { clearTimeout(timer); }, }; } // 测试 const { cancel } = mySetTimout(() => console.log(888), 1000); setTimeout(() => { cancel(); }, 4000); 用 setInterval 实现 setTimeout # js 复制 1 2 3 4 5 6 7 8 9 function mySetInterval(fn, delay) { const timer = setInterval(() => { fn(); clearInterval(timer); }, delay); } // 测试 mySetInterval(() => console.log(888), 1000); 实现一个 compose 函数 # js 复制 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 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 a = compose(fn1, fn2, fn3, fn4); console.log(a); console.log(a(1)); // 1+2+3+4=11 // 实现如下: function compose(...fn) { if (fn.length === 0) return (num) => num; if (fn.length === 1) return fn[0]; return fn.reduce((pre, next) => { return (num) => { return next(pre(num)); }; }); } 实现一个科里化函数 # js 复制 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 const add = (a, b, c) => a + b + c; const a = currying(add, 1); console.log(a(2, 3)); // + + 3=6 // 实现如下: function currying(fn, ...args1) { // 获取fn参数有几个 const length = fn.length; let allArgs = [...args1]; const res = (...arg2) => { allArgs = [...allArgs, ...arg2]; // 长度相等就返回执行结果 if (allArgs.length === length) { return fn(...allArgs); } else { // 不相等继续返回函数 return res; } }; return res; } // 测试: const add = (a, b, c) => a + b + c; const a = currying(add, 1); console.log(a(2, 3)); 实现一个 LRU 缓存函数 # js 复制 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 // 实现如下: class LRUCache { constructor(size) { this.size = size; this.cache = new Map(); } get(key) { const hasKey = this.cache.has(key); if (hasKey) { const val = this.cache.get(key); this.cache.delete(key); this.cache.set(key, val); return val; } else { return -1; } } put(key, val) { const hasKey = this.cache.has(key); if (hasKey) { this.cache.delete(key); } this.cache.set(key, val); if (this.cache.size > this.size) { this.cache.delete(this.cache.keys().next().value); } } } 简单实现 发布订阅模式 # js 复制 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 class EventEmitter { constructor() { this.cache = {}; } on(name, fn) { const tasks = this.cache[name]; if (tasks) { this.cache[name].push(fn); } else { this.cache[name] = [fn]; } } off(name, fn) { const tasks = this.cache[name]; if (task) { const index = tasks.findIndex((item) => item === fn); if (index >= 0) { this.cache[name].splice(index, 1); } } } emit(name, ...args) { // 复制一份。防止回调里继续on,导致死循环 const tasks = this.cache[name].slice(); if (tasks) { for (let fn of tasks) { fn(...args); } } } once(name, cb) { function fn(...args) { cb(args); this.off(name, fn); } this.on(name, fn); } } 实现 JSON.parse # js 复制 1 2 3 function parse(json) { return eval("(" + json + ")"); } 将 DOM 转化成树结构对象 # html 复制 1 2 3 4 5 6 7 <div> <span></span> <ul> <li></li> <li></li> </ul> </div> js 复制 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 // 将上方的DOM转化为下面的树结构对象 { tag: 'DIV', children: [ { tag: 'SPAN', children: [] }, { tag: 'UL', children: [ { tag: 'LI', children: [] }, { tag: 'LI', children: [] } ] } ] } // 实现如下: function dom2tree(dom) { const obj = {} obj.tag = dom.tagName obj.children = [] dom.childNodes.forEach(child => obj.children.push(dom2tree(child))) return obj } 将树结构转换为 DOM # js 复制 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 // 树结构 {tag: 'DIV', children: [ { tag: 'SPAN', children: [] }, { tag: 'UL', children: [ { tag: 'LI', children: [] }, { tag: 'LI', children: [] } ] } ] } // 实现如下: // 真正的渲染函数 function _render(vnode) { // 如果是数字类型转化为字符串 if (typeof vnode === "number") { vnode = String(vnode); } // 字符串类型直接就是文本节点 if (typeof vnode === "string") { return document.createTextNode(vnode); } // 普通DOM const dom = document.createElement(vnode.tag); if (vnode.attrs) { // 遍历属性 Object.keys(vnode.attrs).forEach((key) => { const value = vnode.attrs[key]; dom.setAttribute(key, value); }); } // 子数组进行递归操作 vnode.children.forEach((child) => dom.appendChild(_render(child))); return dom; } html 复制 1 2 3 4 5 6 7 8 <!-- 转换后的dom --> <div> <span></span> <ul> <li></li> <li></li> </ul> </div> 判断一个对象有环引用 # js 复制 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 var obj = { a: { c: [1, 2], }, b: 1, }; obj.a.c.d = obj; console.log(cycleDetector(obj)); // true // 实现思路:用一个数组存储每一个遍历过的对象,下次找到数组中存在,则说明环引用 function cycleDetector(obj) { const arr = [obj]; let flag = false; function cycle(o) { const keys = Object.keys(o); for (const key of keys) { const temp = o[key]; if (typeof temp === "object" && temp !== null) { if (arr.indexOf(temp) >= 0) { flag = true; return; } arr.push(temp); cycle(temp); } } } cycle(obj); return flag; } 计算一个对象的层数 # js 复制 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 const obj = { a: { b: [1] }, c: { d: { e: { f: 1 } } }, }; console.log(loopGetLevel(obj)); // 4 // 实现如下: function loopGetLevel(obj) { var res = 1; function computedLevel(obj, level) { var level = level ? level : 0; if (typeof obj === "object") { for (var key in obj) { if (typeof obj[key] === "object") { computedLevel(obj[key], level + 1); } else { res = level + 1 > res ? level + 1 : res; } } } else { res = level > res ? level : res; } } computedLevel(obj); return res; } 对象的扁平化 # js 复制 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 const obj = { a: { b: 1, c: 2, d: { e: 5 }, }, b: [1, 3, { a: 2, b: 3 }], c: 3, }; flatten(obj); // 结果返回如下 // { // 'a.b': 1, // 'a.c': 2, // 'a.d.e': 5, // 'b[0]': 1, // 'b[1]': 3, // 'b[2].a': 2, // 'b[2].b': 3 // c: 3 // } // 实现如下: const isObject = (val) => typeof val === "object" && val !== null; function flatten(obj) { if (!isObject(obj)) return; const res = {}; const dfs = (cur, prefix) => { if (isObject(cur)) { if (Array.isArray(cur)) { cur.forEach((item, index) => { dfs(item, `${prefix}[${index}]`); }); } else { for (let key in cur) { dfs(cur[key], `${prefix}${prefix ? "." : ""}${key}`); } } } else { res[prefix] = cur; } }; dfs(obj, ""); return res; } // 测试 console.log(flatten(obj)); 实现(a == && a == && a == 3)为 true # js 复制 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 // 第一种方法 var a = { i: 1, toString: function () { return a.i++; }, }; console.log(a == 1 && a == 2 && a == 3); // true // 第二种方法 var a = [1, 2, 3]; a.join = a.shift; console.log(a == 1 && a == 2 && a == 3); // true // 第三种方法 var val = 0; Object.defineProperty(window, "a", { get: function () { return ++val; }, }); console.log(a == 1 && a == 2 && a == 3); // true 实现限制并发的 Promise 调度器 # js 复制 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 addTask(1000, "1"); addTask(500, "2"); addTask(300, "3"); addTask(400, "4"); // 的输出顺序是:4 // 整个的完整执行流程: // 一开始1,2两个任务开始执行 // 500ms时,2任务执行完毕,输出2,任务3开始执行 // 800ms时,3任务执行完毕,输出3,任务4开始执行 // 1000ms时,1任务执行完毕,输出1,此时只剩下4任务在执行 // 1200ms时,4任务执行完毕,输出4 // 实现如下: class Scheduler { constructor(limit) { this.queue = []; this.limit = limit; this.count = 0; } add(time, order) { const promiseCreator = () => { return new Promise((resolve, reject) => { setTimeout(() => { console.log(order); resolve(); }, time); }); }; this.queue.push(promiseCreator); } taskStart() { for (let i = 0; i < this.limit; i++) { this.request(); } } request() { if (!this.queue.length || this.count >= this.limit) return; this.count++; this.queue .shift()() .then(() => { this.count--; this.request(); }); } } // 测试 const scheduler = new Scheduler(2); const addTask = (time, order) => { scheduler.add(time, order); }; addTask(1000, "1"); addTask(500, "2"); addTask(300, "3"); addTask(400, "4"); scheduler.taskStart(); 实现 lazyMan 函数 # js 复制 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 // 实现一个LazyMan,可以按照以下方式调用: LazyMan(“Hank”) /** 输出: * Hi! This is Hank! */ LazyMan(“Hank”).sleep(10).eat(“dinner”) /** 输出: * Hi! This is Hank! * //等待10秒.. * Wake up after 10 * Eat dinner~ */ LazyMan(“Hank”).eat(“dinner”).eat(“supper”) /** 输出: * Hi This is Hank! * Eat dinner~ * Eat supper~ */ LazyMan(“Hank”).eat(“supper”).sleepFirst(5) /** 输出: * //等待5秒 * Wake up after 5 * Hi This is Hank! * Eat supper */ // 实现如下: class _LazyMan { constructor(name) { this.tasks = [] const task = () => { console.log(`Hi! This is ${name}`) this.next() } this.tasks.push(task) setTimeout(() => { this.next() }, 0) } next() { const task = this.tasks.shift() task && task() } sleep(time) { this.sleepWrapper(time, false) return this } sleepFirst(time) { this.sleepWrapper(time, true) return this } sleepWrapper(time, first) { const task = () => { setTimeout(() => { console.log(`Wake up after ${time}`) this.next() }, time * 1000) } if (first) { this.tasks.unshift(task) } else { this.tasks.push(task) } } eat(food) { const task = () => { console.log(`Eat ${food}`); this.next(); }; this.tasks.push(task); return this; } } // 测试 const lazyMan = (name) => new _LazyMan(name) lazyMan('Hank').sleep(1).eat('dinner') lazyMan('Hank').eat('dinner').eat('supper') lazyMan('Hank').eat('supper').sleepFirst(5) 实现 add 函数 # js 复制 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 add(1)(2)(3)()=6 add(1,2,3)(4)()=10 function add(...args1) { let allArgs = [...args1] function fn(...args2) { if (!args2.length) return fn.toString() allArgs = [...allArgs, ...args2] return fn } fn.toString = function () { return allArgs.reduce((pre, next) => pre + next) } return fn } // 测试 console.log(add(1)(2)(3)()) console.log(add(1, 2)(3)()) 实现一个合格的深拷贝 # 深拷贝有这 个段位? ...

2024-06-16 · 15 分钟 · 7332 字 · Linlccc

ES6 常用代码块

ES个经常使用的技巧 # 打乱数组顺序 # js 复制 1 2 3 4 let arr = ["😄", 67, true, false, "55"]; arr = arr.sort(() => 0.5 - Math.random()); console.log(arr); // [ '😄', '55', 67, false, true ] 删除数字之外的所有字符 # js 复制 1 2 3 4 const str = "xieyezi is so hansome 223333"; const numbers = str.replace(/\D/g, ""); console.log(numbers); // 2321395994223333 反转字符串或者单词 # js 复制 1 2 3 4 5 6 7 8 9 10 11 12 const sentence = "xieyezi js so handsome, lol."; const reverseSentence = reverseBySeparator(sentence, ""); console.log(reverseSentence); // .lol ,emosdnah os sj izeyeix const reverseEachWord = reverseBySeparator(reverseSentence, " "); console.log(reverseEachWord); // izeyeix sj os ,emosdnah .lol function reverseBySeparator(string, separator) { return string.split(separator).reverse().join(separator); } 将十进制转换为二进制文件或十六进制数 # js 复制 1 2 3 const num = 45; num.toString(2); num.tostring(16); 合并多个对象 # js 复制 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 const city = { name: "Chongqing", population: "1,234,567,890", }; const location = { longitude: "116.4", latitude: "39.9", }; const fullCity = { ...city, ...location }; console.log(fullCity); // { // name: 'Chongqing', // population: '1,234,567,890', // longitude: '116.4', // latitude: '39.9' // } === 和 == 的区别 # js 复制 1 2 3 4 5 6 7 8 9 // == -> 类型转换 (浅比较) // === -> 无类型转换 (严格比较) 0 == false; // true 0 === false; // false 1 == "1"; // true 1 === "1"; // false null == undefined; // true null === undefined; // false 解构赋值 # js 复制 1 2 3 4 5 6 7 8 9 10 11 12 13 14 const forest = { location: "Sweden", animals: 3, animalsTypes: ["Lions", "Tigers", "Bears"], }; const { location, animals, animalsTypes } = forest; const [lions, tigers, bears] = animalsTypes; console.log(location); // Sweden console.log(animals); // 3 console.log(lions); // Lions console.log(tigers); // Tigers console.log(bears); // Bears 交换变量的值 # js 复制 1 2 3 4 let bears = "bears"; let tigers = ("tigers"[(bears, tigers)] = [tigers, bears]); console.log(bears); // tigers console.log(tribes); // bears 判断回文字符串 # js 复制 1 2 3 4 5 6 const isRevervse = (str1, str2) => { const normalize = (str) => str.toLowerCase().normalize("NFD").split("").reverse().join(""); return normalize(str1) === str2; }; console.log(isRevervse("anagram", "margana")); // true console.log(isRevervse("rac", "car")); // true 回文字符串: 正着写和反着写都一样的字符串 (特别感谢 @浮生阁阁主 勘误) ...

2024-05-07 · 3 分钟 · 1099 字 · Linlccc

常用js

防抖 # js 复制 1 2 3 4 5 6 7 8 9 10 11 12 13 14 function debounce(fn, delay) { let timer; return function (...args) { timer && clearTimeout(timer); timer = setTimeout(() => fn.apply(this, args), delay); }; } // 测试 function task(arg) { console.log("run task" + arg); } const debounceTask = debounce(task, 1000); window.addEventListener("scroll", () => debounceTask(11)); 节流 # js 复制 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 function throttle(fn, delay) { let last = 0; return function (...args) { const now = Date.now(); if (now - last < delay) return; last = now; fn.apply(this, args); }; } // 测试 function task() { console.log("run task"); } const throttleTask = throttle(task, 1000); window.addEventListener("scroll", throttleTask); 深拷贝 # js 复制 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 function deepClone(obj, cache = new WeakMap()) { if (typeof obj !== "object") return obj; // 普通类型,直接返回 if (obj === null) return obj; if (cache.get(obj)) return cache.get(obj); // 防止循环引用,程序进入死循环 if (obj instanceof Date) return new Date(obj); if (obj instanceof RegExp) return new RegExp(obj); // 找到所属原型上的constructor,所属原型上的constructor指向当前对象的构造函数 let cloneObj = new obj.constructor(); cache.set(obj, cloneObj); // 缓存拷贝的对象,用于处理循环引用的情况 for (let key in obj) { if (obj.hasOwnProperty(key)) { cloneObj[key] = deepClone(obj[key], cache); // 递归拷贝 } } return cloneObj; } // 测试 const obj = { name: "Jack", address: { x: 100, y: 200 } }; obj.a = obj; // 循环引用 const newObj = deepClone(obj); console.log(newObj.address === obj.address); // false 手写 Promise # js 复制 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 class MyPromise { constructor(executor) { // executor执行器 this.status = "pending"; // 等待状态 this.value = null; // 成功或失败的参数 this.fulfilledCallbacks = []; // 成功的函数队列 this.rejectedCallbacks = []; // 失败的函数队列 const that = this; function resolve(value) { // 成功的方法 if (that.status === "pending") { that.status = "resolved"; that.value = value; that.fulfilledCallbacks.forEach((myFn) => myFn(that.value)); //执行回调方法 } } function reject(value) { //失败的方法 if (that.status === "pending") { that.status = "rejected"; that.value = value; that.rejectedCallbacks.forEach((myFn) => myFn(that.value)); //执行回调方法 } } try { executor(resolve, reject); } catch (err) { reject(err); } } then(onFulfilled, onRejected) { if (this.status === "pending") { // 等待状态,添加回调函数到成功的函数队列 this.fulfilledCallbacks.push(() => { onFulfilled(this.value); }); // 等待状态,添加回调函数到失败的函数队列 this.rejectedCallbacks.push(() => { onRejected(this.value); }); } if (this.status === "resolved") { // 支持同步调用 console.log("this", this); onFulfilled(this.value); } if (this.status === "rejected") { // 支持同步调用 onRejected(this.value); } } } // 测试 function fn() { return new MyPromise((resolve, reject) => { setTimeout(() => { if (Math.random() > 0.6) { resolve(1); } else { reject(2); } }, 1000); }); } fn().then( (res) => { console.log("res", res); // res 1 }, (err) => { console.log("err", err); // err 2 }, ); 异步控制并发数 # js 复制 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 function limitRequest(urls = [], limit = 5) { return new Promise((resolve, reject) => { const len = urls.length; let count = 0; // 当前进行到第几个任务 const start = async () => { const url = urls.shift(); // 从数组中拿取第一个任务 if (url) { try { await axios.post(url); if (count == len - 1) { // 最后一个任务 resolve(); } else { count++; // 成功,启动下一个任务 start(); } } catch (e) { count++; // 失败,也启动下一个任务 start(); } } }; // 启动limit个任务 while (limit > 0) { start(); limit -= 1; } }); } // 测试 limitRequest(["http://xxa", "http://xxb", "http://xxc", "http://xxd", "http://xxe"]); 继承 # ES继承 # js 复制 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 function Parent(name) { this.name = name; } Parent.prototype.eat = function () { console.log(this.name + " is eating"); }; function Child(name, age) { Parent.call(this, name); this.age = age; } Child.prototype = Object.create(Parent.prototype); Child.prototype.constructor = Child; // 测试 let xm = new Child("xiaoming", 12); console.log(xm.name); // xiaoming console.log(xm.age); // 12 xm.eat(); // xiaoming is eating ES继承 # js 复制 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 class Parent { constructor(name) { this.name = name; } eat() { console.log(this.name + " is eating"); } } class Child extends Parent { constructor(name, age) { super(name); this.age = age; } } // 测试 let xm = new Child("xiaoming", 12); console.log(xm.name); // xiaoming console.log(xm.age); // 12 xm.eat(); // xiaoming is eating 数组排序 # sort 排序 # js 复制 1 2 3 4 5 6 7 8 9 // 对数字进行排序,简写 const arr = [3, 2, 4, 1, 5]; arr.sort((a, b) => a - b); console.log(arr); // [1, 2, 3, 4, 5] // 对字母进行排序,简写 const arr = ["b", "c", "a", "e", "d"]; arr.sort(); console.log(arr); // ['a', 'b', 'c', 'd', 'e'] 冒泡排序 # js 复制 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 function bubbleSort(arr) { let len = arr.length; for (let i = 0; i < len - 1; i++) { // 从第一个元素开始,比较相邻的两个元素,前者大就交换位置 for (let j = 0; j < len - 1 - i; j++) { if (arr[j] > arr[j + 1]) { let num = arr[j]; arr[j] = arr[j + 1]; arr[j + 1] = num; } } // 每次遍历结束,都能找到一个最大值,放在数组最后 } return arr; } //测试 console.log(bubbleSort([2, 3, 1, 5, 4])); // [1, 2, 3, 4, 5] 数组去重 # Set 去重 # js 复制 1 2 cosnt newArr = [...new Set(arr)]; const newArr = Array.from(new Set(arr)); indexOf 去重 # js 复制 1 2 3 4 5 6 7 8 9 10 11 12 13 function resetArr(arr) { let res = []; arr.forEach((item) => { if (res.indexOf(item) === -1) { res.push(item); } }); return res; } // 测试 const arr = [1, 1, 2, 3, 3]; console.log(resetArr(arr)); // [1, 2, 3] 获取 url 参数 # URLSearchParams 方法 # js 复制 1 2 3 4 // 创建一个URLSearchParams实例 const urlSearchParams = new URLSearchParams(window.location.search); // 把键值对列表转换为一个对象 const params = Object.fromEntries(urlSearchParams.entries()); split 方法 # js 复制 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 function getParams(url) { const res = {}; if (url.includes("?")) { const str = url.split("?")[1]; const arr = str.split("&"); arr.forEach((item) => { const key = item.split("=")[0]; const val = item.split("=")[1]; res[key] = decodeURIComponent(val); // 解码 }); } return res; } // 测试 const user = getParams("http://www.baidu.com?user=%E9%98%BF%E9%A3%9E&age=16"); console.log(user); // { user: '阿飞', age: '16' } 发布订阅模式 # js 复制 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 class EventEmitter { constructor() { this.cache = {}; } on(name, fn) { if (this.cache[name]) { this.cache[name].push(fn); } else { this.cache[name] = [fn]; } } off(name, fn) { const tasks = this.cache[name]; if (tasks) { const index = tasks.findIndex((f) => f === fn || f.callback === fn); if (index >= 0) { tasks.splice(index, 1); } } } emit(name, once = false) { if (this.cache[name]) { // 创建副本,如果回调函数内继续注册相同事件,会造成死循环 const tasks = this.cache[name].slice(); for (let fn of tasks) { fn(); } if (once) { delete this.cache[name]; } } } } // 测试 const eventBus = new EventEmitter(); const task1 = () => { console.log("task1"); }; const task2 = () => { console.log("task2"); }; eventBus.on("task", task1); eventBus.on("task", task2); eventBus.off("task", task1); setTimeout(() => { eventBus.emit("task"); // task2 }, 1000);

2024-06-16 · 4 分钟 · 1755 字 · Linlccc

常用js

防抖 # js 复制 1 2 3 4 5 6 7 8 9 10 11 12 13 14 function debounce(fn, delay) { let timer; return function (...args) { timer && clearTimeout(timer); timer = setTimeout(() => fn.apply(this, args), delay); }; } // 测试 function task(arg) { console.log("run task" + arg); } const debounceTask = debounce(task, 1000); window.addEventListener("scroll", () => debounceTask(11)); 节流 # js 复制 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 function throttle(fn, delay) { let last = 0; return function (...args) { const now = Date.now(); if (now - last < delay) return; last = now; fn.apply(this, args); }; } // 测试 function task() { console.log("run task"); } const throttleTask = throttle(task, 1000); window.addEventListener("scroll", throttleTask);

2024-06-16 · 1 分钟 · 130 字 · Linlccc