1.快速排序
function quickSort(arr) {
if (arr.length <= 1) {
return arr;
}
let pivot = arr[0];
let left = [];
let right = [];
for(let i = 1; i < arr.length; i++) {
if (arr[i] < pivot) {
left.push(arr[i]);
} else {
right.push(arr[i]);
}
}
return [...quickSort(left), pivot, ...quickSort(right)]
}
2.实现new方法
function New(contractor, ...args) {
const obj = Object.create(contractor.prototype);
const result = contractor.apply(obj, args);
return typeof result === 'object'? result : obj;
}
3.斐波拉契数列
3.1 递归实现
function fibonacci(n) {
if (n < 2) return n;
return fibonacci(n-1) + fibonacci(n-2);
}
3.2 ES6的数组交换实现
function fib(max) {
let a = 0; b = 1; arr = [0, 1];
while (arr.length < max) {
[a, b] = [b, a + b];
arr.push(b);
}
return arr;
}
4.防抖函数
function debounce(fn, delay) {
let timer = null;
return function () {
clearTimeout(timer);
timer = setTimeout(() => {
fn.apply(this, arguments);
})
}
}
5.节流函数
实现一
function throttle(fn, delay) {
let lastTime = 0;
return function () {
let nowTime = Date.now();
if (nowTime - lastTime > delay) {
fn.apply(this, arguments);
lastTime = nowTime;
}
}
}
实现二
function throttle(fn, delay) {
let flag = true;
return function () {
if (flag) {
flag = false
setTimeout(() => {
fn.apply(this, arguments);
flag = true;
}, delay)
}
}
}
6.对象深比较
function _isObj(obj) {
return Object.prototype.toString.call(obj) === '[object Object]';
}
function _deepCompare(a, b) {
// 1.判断一个或者两个都不是对象
if (!_isObj(a) ||!_isObj(b)) {
return a === b;
}
// 2.同一个对象
if (a === b) {
return true;
}
// 3.不是同一个对象
if (Object.keys(a).length !== Object.keys(b).length) {
return false;
}
for (let key in a) {
if (!_deepCompare(a[key], b[key])) {
return false;
}
}
return true;
}
7.虚拟滚动实现(item定高)
7.1 Transform
import "./styles.css";
import { useEffect, useState, useRef, useMemo } from "react";
export default function App(props) {
const { list, itemHeight } = props;
const [start, setStart] = useState(0);
const [count, setCount] = useState(0);
const scrollRef = useRef(null);
const contentRef = useRef(null);
const totalHeight = useMemo(() => list.length * itemHeight, [list.length]);
useEffect(() => {
setCount(Math.ceil(scrollRef.current.clientHeight / itemHeight));
}, [itemHeight]);
const scrollHandle = () => {
const { scrollTop } = scrollRef.current;
const newStart = Math.floor(scrollTop / itemHeight);
setStart(newStart);
contentRef.current.style.transform = `translate3d(0, ${
newStart * itemHeight
}px, 0)`;
};
const subList = list.slice(start, start + count);
return (
<div>
<div>
<div>
{subList.map((item, index) => {
return (
<div>
{item}
</div>
);
})}
</div>
</div>
</div>
);
}
7.2 Absolute
import "./styles.css";
import { useEffect, useState, useRef, useMemo } from "react";
export default function App(props) {
const { list, itemHeight } = props;
const [start, setStart] = useState(0);
const [count, setCount] = useState(0);
const scrollRef = useRef(null);
const totalHeight = useMemo(() => list.length * itemHeight, [list.length]);
useEffect(() => {
setCount(Math.ceil(scrollRef.current.clientHeight / itemHeight));
}, [itemHeight]);
const scrollHandle = () => {
const { scrollTop } = scrollRef.current;
const newStart = Math.floor(scrollTop / itemHeight);
setStart(newStart);
};
const subList = list.slice(start, start + count);
return (
<div>
<div>
{subList.map((item, index) => {
return (
<div>
{item}
</div>
);
})}
</div>
</div>
);
}
7.3 Padding
import "./styles.css";
import { useState, useRef, useMemo } from "react";
export default function App(props) {
const { list, itemHeight, count } = props;
const [start, setStart] = useState(0);
const scrollRef = useRef(null);
const totalHeight = useMemo(() => list.length * itemHeight, [list.length]);
const currentHeight = useMemo(() => count * itemHeight, [itemHeight, count]);
const paddingTop = useMemo(() => itemHeight * start, [start])
const paddingBottom = useMemo(() => totalHeight - currentHeight - itemHeight * start, [start]);
const scrollHandle = () => {
const { scrollTop, clientHeight } = scrollRef.current;
if (
scrollTop + clientHeight >= itemHeight * (start + count) ||
scrollTop <= itemHeight * start
) {
const newStart = Math.floor(scrollTop / itemHeight);
setStart(newStart);
}
};
const subList = list.slice(start, start + count);
return (
<div>
<div>
{subList.map((item, index) => {
return (
<div>
{item}
</div>
);
})}
</div>
</div>
);
}
8.倒计时
import React, { useState, useEffect } from "react";
const App = () => {
const [timeLeft, setTimeLeft] = useState(getTimeLeft());
useEffect(() => {
const timerId = setInterval(() => {
setTimeLeft(getTimeLeft());
}, 1000);
return () => clearInterval(timerId);
}, []);
function getTimeLeft() {
const futureDate = new Date("2024/1/1");
const currentDate = new Date();
const diffTime = Math.abs(futureDate - currentDate);
const days = Math.floor(diffTime / (1000 * 60 * 60 * 24));
const hours = Math.floor(
(diffTime % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60)
);
const minutes = Math.floor((diffTime % (1000 * 60 * 60)) / (1000 * 60));
const seconds = Math.floor((diffTime % (1000 * 60)) / 1000);
return {
days,
hours,
minutes,
seconds
};
}
const { days, hours, minutes, seconds } = timeLeft;
return (
<div>
<h1>2024年1月1日倒计时</h1>
<div>
{days} days {hours} hours : {minutes} minutes : {seconds} seconds
</div>
</div>
);
};
export default App;
9.图片懒加载
9.1 方式一
let imgs= document.getElementsByTagName("img");
let clientHeight = document.documentElement.clientHeight || document.body.clientHeight;
function lazyLoad() {
Array.from(imgs).forEach(function (img) {
let scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
let height = clientHeight + scrollTop - img.offsetTop;
if (height > 0 ) {
img.src = img[data-src];
}
})
}
window.onscroll = lazyLoad
9.2 方式二
let imgs= document.getElementsByTagName("img");
let clientHeight = document.documentElement.clientHeight || document.body.clientHeight;
function isIn(e) {
let bound = e.getBoundingClientRect();
return bound.top < clientHeight;
}
function lazyLoad() {
Array.from(imgs).forEach(function (img) {
if (isIn(img)) {
img.src = img[data-src];
}
})
}
window.onscroll = lazyLoad
10.控制请求并发数
10.1 方式一
function sendRequest(requests, limits, callBack) {
// 定义执行队列,表示所有待执行的任务
const promises = requests.slice(0);
// 定义开始时能执行的并发数
const counterNum = Math.min(promises.length, limits);
let currentCounterNum = 0; // 当前并发数
// 启动初次能执行的任务
function startRunTask() {
let i = 0;
while (i < counterNum) {
i++;
runTask();
}
}
// 取出任务并推送到执行器
runTask = () => {
const task = promises.shift();
task && runner(task);
}
// 执行器,这里去执行任务
runner = async (task) => {
try {
currentCounterNum++;
await task();
} catch (error) {
} finally {
currentCounterNum--;
pickTask();
}
}
// 捞起下一个任务
pickTask = () => {
if (currentCounterNum < counterNum && promises.length > 0) {
runTask();
} else if (promises.length === 0 && currentCounterNum === 0) {
callBack && callBack();
}
}
// 开始执行
startRunTask();
}
10.2 方式二
async function sendRequest(requestList, limit, callBack) {
// 维护一个promise队列
const promises = [];
// 当前的并发池,用Set结构方便删除
const pool = new Set();
// 开始并发执行所有的任务
for (let request of requestList) {
// 开始执行前,先await 判断当前的并发任务是否超过限制
if (pool.size >= limit) {
await Promise.race(pool)
}
const cb = () => {
pool.delete(request);// 删除请求结束后,从pool里面移除
}
request.then(cb, cb);
pool.add(request);
promises.push(request);
}
Promise.allSettled(promises).then(callBack, callBack);
}
测试代码
let requests = [];
for (let i = 0; i < 10; i++) {
requests.push(new Promise((resolve) => {
setTimeout(() => resolve(i), 200);
}))
}
function callBack() {
console.log('success');
}
sendRequest(requests, 3, callBack);
11.实现bind方法
Function.prototype.myBind = function() {
var thatFunc = this; //存外部this
var thatArg = arguments[0]; //获取上下文
var args = Array.prototype.slice.call(arguments, 1);
// 判断调用对象是否为函数
if (typeof thatFunc != 'function') {
throw new TypeError("caller must be function");
}
var fBound = function() {
return thatFunc.apply(this instanceof fBound ?
this : thatArg, args.concat(Array.prototype.slice.call(arguments)));
}
var fNOP = function () {};
fNOP.prototype = thatFunc.prototype;
fBound.prototype = new fNOP();//继承构造函数原型
return fBound;
}
测试代码
var obj = { name:"xiao ming" };
var greeting = function(site, lang){
this.value = 'cheering';
console.log("Welcome " + this.name + " to " + site +" learning " + lang);
};
var objGreeting = greeting.myBind(obj, 'juejin');
objGreeting("JS")
//var newObj = new objGreeting('JS');
console.log(newObj.value);
12.版本号排序
arr.sort((a, b) => {
const arr1 = a.split('.').map(Number);
const arr2 = b.split('.').map(Number);
const maxLen = Math.max(arr1.length, arr2.length);
for (let i = 0; i < maxLen; i++) {
const n1 = arr1[i] ?? 0;
const n2 = arr2[i] ?? 0;
if (n1 !== n2) {
return n2 - n1; // 降序
}
}
return 0;
});
13.获取网络文件并下载到本地
const getBlob = (url) => {
return new Promise((resolve) => {
const xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
xhr.responseType = "blob";
xhr.onload = () => {
if (xhr.status === 200) {
resolve(xhr.response);
}
}
xhr.send();
})
}
const saveAs = (blob, fileName) => {
let link = document.createElement("a");
link.href = window.URL.createObjectURL(blob);
link.download = fileName;
link.click();
}
//测试代码
getBlob("http://www.example.com/hello.jpg").then((blob) => saveAs(blob, "hello"))
14. JS保留2位小数
14.1 toFixed()
let num1 = 3.1415926;
let num2 = 3.1455926;
console.log(num1.toFixed(2)); // 3.14
console.log(num2.toFixed(2)); // 3.15
14.2 Math.round()
let num1 = Math.round(3.1415926 * 100) / 100;
let num2 = Math.round(3.1455926 * 100) / 100;
console.log(num1); // 3.14
console.log(num2); // 3.15
14.3 Math.floor()
let num1 = Math.floor(3.1415926 * 100) / 100;
let num2 = Math.floor(3.1455926 * 100) / 100;
console.log(num1); // 3.14
console.log(num2); // 3.14
14.4 正则表达式
let num1 = (3.1415926).toString().match(/^\d+(?:.\d{0,2})?/);
let num2 = (3.1455926).toString().match(/^\d+(?:.\d{0,2})?/);
console.log(num1[0]); // 3.14
console.log(num2[0]); // 3.14
15. 扁平数据结构转Tree
如下数据结构:
let arr = [
{id: 1, name: '部门1', pid: 0},
{id: 2, name: '部门2', pid: 1},
{id: 3, name: '部门3', pid: 1},
{id: 4, name: '部门4', pid: 3},
{id: 5, name: '部门5', pid: 4},
]
输出结果:
[
{
"id": 1,
"name": "部门1",
"pid": 0,
"children": [
{
"id": 2,
"name": "部门2",
"pid": 1,
"children": []
},
{
"id": 3,
"name": "部门3",
"pid": 1,
"children": [
// 结果 ,,,
]
}
]
}
]
15.1 递归遍历查找
/**
* 递归查找,获取children
*/
const getChildren = (data, result, pid) => {
for (const item of data) {
if (item.pid === pid) {
const newItem = {...item, children: []};
result.push(newItem);
getChildren(data, newItem.children, item.id);
}
}
}
/**
* 转换方法
*/
const arrayToTree = (data, pid) => {
const result = [];
getChildren(data, result, pid)
return result;
}
15.2 数据转Map
function arrayToTree(items) {
const result = []; // 存放结果集
const itemMap = {};
// 先转成map存储
for (const item of items) {
itemMap[item.id] = {...item, children: []}
}
for (const item of items) {
const id = item.id;
const pid = item.pid;
const treeItem = itemMap[id];
if (pid === 0) {
result.push(treeItem);
} else {
if (!itemMap[pid]) {
itemMap[pid] = {
children: [],
}
}
itemMap[pid].children.push(treeItem)
}
}
return result;
}
15.3 数据转Map优化
function arrayToTree(items) {
const result = []; // 存放结果集
const itemMap = {};
for (const item of items) {
const id = item.id;
const pid = item.pid;
if (!itemMap[id]) {
itemMap[id] = {
children: [],
}
}
itemMap[id] = {
...item,
children: itemMap[id]['children']
}
const treeItem = itemMap[id];
if (pid === 0) {
result.push(treeItem);
} else {
if (!itemMap[pid]) {
itemMap[pid] = {
children: [],
}
}
itemMap[pid].children.push(treeItem)
}
}
return result;
}
其他
1.相同的前端代码部署后在http和https中表现有什么不一样?
https可以请求servicework,调用http资源,摄像头和地理位置等
2.前端监控是怎么做的?
监控时机:页面加载/运行时(js执行,页面重绘重排,动画帧率)/错误异常(js,promise,资源,接口请求)
监控方式:Sentry, Performance,WebVitals
监控指标:FCP/LCP/CLS/FID/NIP/TBT
3.servicework的生命周期
注册/安装/等待/激活/运行/终止
4.做过Next项目吗? SSR SSG ISR 使用场景,性能优化
5.react渲染对象遇到性能问题怎么办?
使用immer
6.vit原理
1.内置服务器拦截http请求 import createApp from 'vue'; node_modules路径转移 import createApp from '/@modules/vue';
2.import *.vue替换 es-module-lexer分析import
3.核心编译技巧 ESBuild+ESModule+按需
4.热更新原理websocket