目录
c2.一个div嵌套三个div 外层div为flex布局 实现内层俩个div靠左 第三个div靠右,怎么写样式
2.写个工具函数对promise封装,输入一个promise a 返回一个promise b 如果1秒内a 没有结果 则在1秒时抛出错误,否则b和a等价
3.编写一个倒计时ui组件 组件有俩个参数:截止时间deadline和时间到期时执行的回调函数cb
5.要求3秒后打印red,接着打印参数b的值,接着打印yellow
22. 给时间戳输出对应文案:如 [一分钟内]刚刚、x分钟前、x小时前、昨天 xx:xx ,前天,【一星期内】星期几,【超过一星期】xx月xx日xx时xx分,【不在当年】xx年xx月xx日xx时xx分
23.有一种花, 两种鸟, 花定时开放,鸟看到花开会叫, 鸟的叫声不一样,用代码来实现这样一种场景
c1.两个嵌套div,外层div宽度500px,内层div宽度20%,要求把内层div设置成正方形,怎么写样式
<div class="outer">
<div class="inner"></div>
</div>
.outer {
width: 500px; /*0 外层 div 宽度固定为 500px */
height: auto; /*0 可以根据内容自动调整高度 */
background-color: lightgray; /*0 用于区分外层 div */
}
.inner {
width: 20%; /* 内层 div 宽度为外层的 20% */
padding-top: 20%; /* 通过 padding-top 来设置高度为宽度的 100%,即实现正方形 */
background-color: lightblue; /*0 用于区分内层 div */
}
/*
高度的实现 通过 padding-top: 20%,这是因为在 CSS 中,padding-top 是相对于宽度计算的,因此设置 padding-top 为 20% 会使得高度和宽度相等,从而实现正方形效果。
*/
c2.一个div嵌套三个div 外层div为flex布局 实现内层俩个div靠左 第三个div靠右,怎么写样式
<div class="outer">
<div class="left1">Left 1</div>
<div class="left2">Left 2</div>
<div class="right">Right</div>
</div>
.outer {
display: flex; /* 设置外层 div 为 flex 布局 */
}
.right {
margin-left: auto; /* 将右侧的 div 推到最右边 */
}
1.实现一个符合 LRU (最近最少使用) 缓存的类
class LRUCache {
constructor(capacity) {
this.capacity = capacity; // 缓存的容量
this.cache = new Map(); // 使用 Map 存储缓存项,保证 O(1) 的查找、删除、插入
}
// 获取缓存项
get(key) {
if (this.cache.has(key)) {
// 如果缓存中存在该 key,将其移到最近使用的位置
const value = this.cache.get(key);
this.cache.delete(key); // 先删除,再重新插入(确保更新顺序)
this.cache.set(key, value); // 重新插入到 Map 的末尾
return value;
}
return -1; // 如果 key 不存在,返回 -1
}
// 插入或更新缓存项
put(key, value) {
if (this.cache.has(key)) {
// 如果缓存中存在该 key,先删除旧的值
this.cache.delete(key);
} else if (this.cache.size >= this.capacity) {
// 如果缓存已满,删除最久未使用的元素
// Map 中的第一个元素是最久未使用的元素
this.cache.delete(this.cache.keys().next().value);
}
// 插入新的键值对
this.cache.set(key, value);
}
}
// 测试用例
const lruCache = new LRUCache(3); // 初始化缓存,容量为 3
lruCache.put(1, 1); // 缓存是 {1=1}
lruCache.put(2, 2); // 缓存是 {1=1, 2=2}
lruCache.put(3, 3); // 缓存是 {1=1, 2=2, 3=3}
console.log(lruCache.get(1)); // 返回 1,缓存是 {1=1, 2=2, 3=3}
lruCache.put(4, 4); // 该操作会导致 2 被移除,缓存是 {1=1, 3=3, 4=4}
console.log(lruCache.get(2)); // 返回 -1 (未找到)
console.log(lruCache.get(3)); // 返回 3
console.log(lruCache.get(4)); // 返回 4
2.写个工具函数对promise封装,输入一个promise a 返回一个promise b 如果1秒内a 没有结果 则在1秒时抛出错误,否则b和a等价
function withTimeout(promise, timeout = 1000) {
return new Promise((resolve, reject) => {
// 创建一个定时器来在超时时间后抛出错误
const timer = setTimeout(() => {
reject(new Error('Operation timed out'));
}, timeout);
// 处理传入的 promise
promise
.then((result) => {
clearTimeout(timer); // 在 Promise 解析后清除定时器
resolve(result);
})
.catch((error) => {
clearTimeout(timer); // 如果 Promise 被拒绝,清除定时器
reject(error);
});
});
}
// 使用示例
const promiseA = new Promise((resolve) => {
setTimeout(() => {
resolve('Result from promiseA');
}, 500); // 500ms 后 resolve
});
withTimeout(promiseA, 1000)
.then((result) => {
console.log(result); // 如果在 1 秒内 resolve, 输出结果
})
.catch((error) => {
console.error(error); // 超时或 promiseA 拒绝时输出错误
});
3.编写一个倒计时ui组件 组件有俩个参数:截止时间deadline和时间到期时执行的回调函数cb
import React, { useEffect, useState } from 'react';
const Countdown = ({ deadline, cb }) => {
const [timeLeft, setTimeLeft] = useState(calculateTimeLeft(deadline));
// 计算剩余时间的函数
function calculateTimeLeft(deadline) {
const difference = new Date(deadline) - new Date();
if (difference > 0) {
return {
hours: Math.floor((difference / (1000 * 60 * 60)) % 24),
minutes: Math.floor((difference / 1000 / 60) % 60),
seconds: Math.floor((difference / 1000) % 60),
};
} else {
return null; // 如果时间到了,返回null
}
}
// 倒计时更新逻辑
useEffect(() => {
const timer = setInterval(() => {
const newTimeLeft = calculateTimeLeft(deadline);
setTimeLeft(newTimeLeft);
if (!newTimeLeft) {
clearInterval(timer); // 清除计时器
if (cb) cb(); // 执行回调函数
}
}, 1000);
return () => clearInterval(timer); // 清除副作用
}, [deadline, cb]);
// 倒计时的 UI 展示
return (
<div>
{timeLeft ? (
<div>
<span>{timeLeft.hours}h </span>
<span>{timeLeft.minutes}m </span>
<span>{timeLeft.seconds}s</span>
</div>
) : (
<div>Time's up!</div>
)}
</div>
);
};
export default Countdown;
4.实现一个带有并发限制的异步调度器 Scheduler
class Scheduler {
constructor() {
this.tasks = []; // 存储任务
this.runningCount = 0; // 当前正在运行的任务数
this.maxConcurrency = 2; // 最大并发度
}
// 添加任务
add(task) {
return new Promise((resolve) => {
const wrappedTask = async () => {
await task(); // 执行任务
resolve(); // 任务完成后解析 Promise
};
this.tasks.push(wrappedTask);
});
}
// 运行任务
async run() {
const runTask = async (task) => {
this.runningCount++; // 增加正在运行的任务数
await task(); // 执行任务
this.runningCount--; // 任务完成,减少计数
this.executeNext(); // 继续执行下一个任务
};
this.executeNext = () => {
if (this.tasks.length === 0 || this.runningCount >= this.maxConcurrency) return; // 任务队列为空或达到并发限制
const task = this.tasks.shift(); // 取出下一个任务
runTask(task); // 运行任务
};
// 启动最大并发数的任务
for (let i = 0; i < this.maxConcurrency; i++) {
this.executeNext();
}
}
// 创建任务
addTask(runTime, taskId) {
const task = () => new Promise(resolve => {
// console.log(`Task ${taskId} started, will take ${runTime} ms`);
setTimeout(() => {
// console.log(`Task ${taskId} completed`);
console.log(taskId);
resolve();
}, runTime);
});
return this.add(task); // 添加任务并返回 Promise
}
}
// 使用示例
const scheduler = new Scheduler();
// 添加任务
scheduler.addTask(2000, 1); // Task 1: 2000 ms
scheduler.addTask(1000, 2); // Task 2: 1000 ms
scheduler.addTask(1500, 3); // Task 3: 1500 ms
scheduler.addTask(500, 4); // Task 4: 500 ms
// 运行调度器
scheduler.run();
class Scheduler {
constructor() {
// to do
this.tasks = []
this.runningCount = 0
this.maxCount = 2
}
add(task) {
//to do
return new Promise((resolve)=>{
const wrappedTask = async ()=>{
await task()
resolve()
}
this.tasks.push(wrappedTask)
})
}
run() {
// to do
const runTask = async (task)=>{
this.runningCount++
await task()
this.runningCount--
this.executeNext()
}
this.executeNext = ()=>{
if(this.tasks.length===0 || this.runningCount>=this.maxCount)
return
else {
const task = this.tasks.shift()
runTask(task)
}
}
for(let i=0;i<this.maxCount;i++)
this.executeNext()
}
}
const timeout = (time) => new Promise(resolve => {
setTimeout(resolve, time)
})
const scheduler = new Scheduler()
const addTask = (time, order) => {
scheduler.add(() => timeout(time)).then(() => console.log(order))
}
addTask(400, 4)
addTask(200, 2)
addTask(400, 3)
addTask(100, 1) //2 4 1 3
scheduler.run(); // to do
// 以下是不写run方法:
class Scheduler {
constructor(max) {
this.max = max;
this.count = 0; // 用来记录当前正在执行的异步函数
this.queue = new Array(); // 表示等待队列
}
async add(promiseCreator) {
/*
此时count已经满了,不能执行;本次add需要阻塞在这里,将resolve放入队列中等待唤醒,
等到count<max时,从队列中取出执行resolve,执行,await执行完毕,本次add继续
*/
if (this.count >= this.max) {
await new Promise((resolve, reject) => this.queue.push(resolve));
}
this.count++;
let res = await promiseCreator();
this.count--;
if (this.queue.length) {
// 依次唤醒add;若队列中有值,将其resolve弹出,并执行;以便阻塞的任务,可以正常执行
this.queue.shift()();
}
return res;
}
}
5.要求3秒后打印red,接着打印参数b的值,接着打印yellow
class Task {
constructor() {
this.taskQueue = []; // 任务队列
}
// 添加任务的方法,支持传递参数
add(task, ...args) {
this.taskQueue.push(() => new Promise(resolve => task(resolve, ...args)));
return this; // 链式调用
}
// 运行任务队列的方法
run() {
// 使用 reduce 链式执行任务
this.taskQueue.reduce((prevTask, currTask) => {
return prevTask.then(() => currTask());
}, Promise.resolve());
}
}
// 定义 task1、task2、task3
function task1(next) {
setTimeout(() => {
console.log('red');
next(); // 调用 next 以触发下一个任务
}, 3000);
}
function task2(next, b) {
setTimeout(() => {
console.log(b); // 打印参数 b
next(); // 调用 next 以触发下一个任务
}, 3000);
}
function task3(next) {
setTimeout(() => {
console.log('yellow');
next(); // 调用 next 以触发下一个任务
}, 2000);
}
// 使用 Task 类来添加和执行任务
const task = new Task();
task.add(task1).add(task2, 1).add(task3).run();
6.用链表实现一个队列js实现
class Node {
constructor(value) {
this.value = value; // 节点的值
this.next = null; // 指向下一个节点的指针
}
}
class Queue {
constructor() {
this.front = null; // 队列的头部(出队的一端)
this.rear = null; // 队列的尾部(入队的一端)
this.length = 0; // 队列中的元素数量
}
// 入队操作:在队列末尾添加元素
enqueue(value) {
const newNode = new Node(value); // 创建一个新的节点
if (this.rear) {
this.rear.next = newNode; // 将新节点添加到尾部
}
this.rear = newNode; // 更新尾部为新节点
if (!this.front) {
this.front = newNode; // 如果队列为空,头部也指向新节点
}
this.length++; // 增加队列的长度
}
// 出队操作:移除并返回队列头部的元素
dequeue() {
if (!this.front) {
return null; // 如果队列为空,返回 null
}
const removedNode = this.front; // 保存当前头部节点
this.front = this.front.next; // 将头部更新为下一个节点
if (!this.front) {
this.rear = null; // 如果移除后队列为空,尾部也要置为 null
}
this.length--; // 减少队列长度
return removedNode.value; // 返回移除的节点的值
}
// 查看队列头部元素
peek() {
return this.front ? this.front.value : null; // 如果队列为空,返回 null
}
// 判断队列是否为空
isEmpty() {
return this.length === 0;
}
// 获取队列长度
size() {
return this.length;
}
}
const queue = new Queue();
queue.enqueue(10);
queue.enqueue(20);
queue.enqueue(30);
console.log(queue.peek()); // 输出 10
console.log(queue.dequeue()); // 输出 10
console.log(queue.size()); // 输出 2
console.log(queue.isEmpty()); // 输出 false
7.在字符串中找出最大数字及其变式
// 1.基本实现
function findLargestNumber(str) {
// 使用正则表达式匹配字符串中的所有数字
const numbers = str.match(/\d+/g);
// 如果没有匹配到数字,返回 null
if (!numbers) return null;
// 使用 Math.max 找出最大值
const maxNumber = Math.max(...numbers.map(Number));
return maxNumber;
}
// 2 不用正则,考虑字符串里无数字情况
function findLargestNumber(str) {
let maxNumber = -Infinity; // 初始化为负无穷大,方便处理没有数字的情况
let currentNumber = 0;
let inNumber = false; // 标记是否在处理数字
let hasNumber = false; // 标记是否有遇到数字
for (let i = 0; i < str.length; i++) {
const char = str[i];
// 如果字符是数字
if (char >= '0' && char <= '9') {
currentNumber = currentNumber * 10 + (char - '0'); // 构建当前数字
inNumber = true;
hasNumber = true; // 标记已经遇到了数字
} else {
// 遇到非数字字符时,检查当前数字并更新最大值
if (inNumber) {
maxNumber = Math.max(maxNumber, currentNumber);
currentNumber = 0; // 重置当前数字
inNumber = false;
}
}
}
// 处理最后一个数字
if (inNumber) {
maxNumber = Math.max(maxNumber, currentNumber);
}
// 如果没有找到任何数字,返回 null
return hasNumber ? maxNumber : null;
}
// 3.允许str中有负数
function findLargestNumber(str) {
let maxNumber = -Infinity; // 初始化为负无穷大
let currentNumber = 0;
let sign = 1; // 用于标记当前数字的正负
let inNumber = false; // 标记是否在处理数字
let hasNumber = false; // 标记是否遇到了数字
for (let i = 0; i < str.length; i++) {
const char = str[i];
// 检查负号,负号后面必须跟数字
if (char === '-' && i + 1 < str.length && str[i + 1] >= '0' && str[i + 1] <= '9') {
sign = -1;
}
// 如果是数字,构建当前数字
else if (char >= '0' && char <= '9') {
currentNumber = currentNumber * 10 + (char - '0');
inNumber = true;
hasNumber = true;
}
// 非数字或负号,处理当前数字
else if (inNumber) {
maxNumber = Math.max(maxNumber, currentNumber * sign);
currentNumber = 0;
inNumber = false;
sign = 1; // 重置正负标记
}
}
// 处理最后一个数字
if (inNumber) {
maxNumber = Math.max(maxNumber, currentNumber * sign);
}
// 如果没有数字,返回 null
return hasNumber ? maxNumber : null;
}
8.单链表判断是否有环及其变式
// 1.快慢指针实现
function hasCycle(head) {
let slow = head;
let fast = head;
while (fast !== null && fast.next !== null) {
slow = slow.next; // 慢指针每次走一步
fast = fast.next.next; // 快指针每次走两步
if (slow === fast) { // 快慢指针相遇,说明有环
return true;
}
}
return false; // 快指针遇到null,说明没有环
}
// 2.怎么确定环的长度
function cycleLength(head) {
let slow = head;
let fast = head;
// 先找到快慢指针相遇的地方
while (fast !== null && fast.next !== null) {
slow = slow.next;
fast = fast.next.next;
if (slow === fast) {
// 计算环的长度
let length = 0;
do {
fast = fast.next;
length++;
} while (slow !== fast);
return length;
}
}
return 0; // 没有环
}
// 3.怎么确定入环点
// 相遇时,快指针已经走了比慢指针多 n 圈,即从起点走到环的入口与从相遇点走到环的入口需要的步数相同。因此将慢指针重置到链表头部,与快指针一起一步步移动,最终会在环的入口节点相遇。
function findCycleStart(head) {
let slow = head;
let fast = head;
// 首先通过快慢指针找到相遇点
while (fast !== null && fast.next !== null) {
slow = slow.next;
fast = fast.next.next;
if (slow === fast) {
// 找到相遇点后,将慢指针移回到起点
slow = head;
// 两个指针每次走一步,相遇点即为入环点
while (slow !== fast) {
slow = slow.next;
fast = fast.next;
}
return slow; // 入环点
}
}
return null; // 没有环
}
9.实现函数柯里化
const curry = (fn) => {
return function curried(...args){
if(args.length>=fn.length){
return fn(...args)
} else {
return function(...args2){
return curried(...args, ...args2)
}
}
}
}
10.给一个嵌套对象查找其属性路径对应的值用数组形式返回
function findNestedProperties(obj) {
const result = [];
function recurse(currentObj, currentPath) {
for (const key in currentObj) {
if (currentObj.hasOwnProperty(key)) {
const newPath = currentPath ? `${currentPath}${key}` : key;
// 检查是否为基本数据类型或数组
if (typeof currentObj[key] !== 'object' || currentObj[key] === null || Array.isArray(currentObj[key])) {
result.push({ key: newPath, value: currentObj[key] });
} else {
// 如果是对象,则递归
recurse(currentObj[key], newPath);
}
}
}
}
recurse(obj, '');
return result;
}
11.实现一个对象浅比较
function shallowEqual(obj1, obj2) {
// 如果引用相同,直接返回 true
if (obj1 === obj2) return true;
// 如果其中一个是 null 或 undefined,另一个不是,直接返回 false
if (obj1 == null || obj2 == null) return false;
// 获取对象的属性名称
const keys1 = Object.keys(obj1);
const keys2 = Object.keys(obj2);
// 如果两个对象的属性数量不同,直接返回 false
if (keys1.length !== keys2.length) return false;
// 遍历属性,比较每个属性的值
for (let key of keys1) {
// 使用严格相等比较值
if (obj1[key] !== obj2[key]) {
return false;
}
}
return true;
}
// 示例用法
const obj1 = { a: 1, b: 2 };
const obj2 = { a: 1, b: 2 };
const obj3 = { a: 1, b: 3 };
console.log(shallowEqual(obj1, obj2)); // true
console.log(shallowEqual(obj1, obj3)); // false
12. 兔子吃草
// 找到有毒的草堆,需要多少只兔子
function findPoisonousGrass(numGrass, poisonedIndex) {
// 计算需要多少位二进制来表示所有草堆的编号
const binaryLength = Math.ceil(Math.log2(numGrass)); // 需要的二进制位数
// 将有毒草堆编号转换为二进制字符串,补齐到 binaryLength 位
const poisonedBinary = poisonedIndex.toString(2).padStart(binaryLength, '0');
// 计算需要死亡的兔子的数量
let deadRabbitCount = 0;
for (let i = 0; i < poisonedBinary.length; i++) {
if (poisonedBinary[i] === '1') {
deadRabbitCount++;
}
}
// 返回死亡兔子的数量
return deadRabbitCount;
}
// 假设毒草是第 678 堆草
const numGrass = 1000;
const poisonedIndex = 678; // 假设第 678 堆草是有毒的
const deadRabbitCount = findPoisonousGrass(numGrass, poisonedIndex);
console.log(`The number of rabbits that need to die: ${deadRabbitCount}`);
// 哪几只兔子会死
function findPoisonousGrass(numGrass, poisonedIndex) {
// 计算需要多少位二进制来表示所有草堆的编号
const binaryLength = Math.ceil(Math.log2(numGrass)); // 需要的二进制位数
// 将有毒草堆编号转换为二进制字符串,补齐到 binaryLength 位
const poisonedBinary = poisonedIndex.toString(2).padStart(binaryLength, '0');
// 计算哪些兔子需要死
const rabbitsToDie = [];
for (let i = 0; i < binaryLength; i++) {
if (poisonedBinary[i] === '1') {
rabbitsToDie.push(i + 1); // 兔子的编号从1开始
}
}
console.log(`The rabbits that need to die: ${rabbitsToDie.join(', ')}`);
}
// 假设毒草是第 678 堆草
const numGrass = 1000;
const poisonedIndex = 678;
findPoisonousGrass(numGrass, poisonedIndex);
13. 实现一个分页
[1],2,3...99,100 1,2,[3]4,...99,100 1,...,4,[5],6,...99,100
const generatePageNumbers = () => {
const pages = [];
// Always show the first page
pages.push(1);
// Add "..." if there is a gap between first page and current page
if (currentPage > 3) {
pages.push('...');
}
// Show two pages before the current page, but don't go below 1
for (let i = Math.max(currentPage - 2, 2); i < currentPage; i++) {
pages.push(i);
}
// Show the current page
pages.push(currentPage);
// Show two pages after the current page, but don't go beyond totalPages
for (let i = currentPage + 1; i <= Math.min(currentPage + 2, totalPages - 1); i++) {
pages.push(i);
}
// Add "..." if there is a gap between last page and current page
if (currentPage < totalPages - 2) {
pages.push('...');
}
// Always show the last page
if (totalPages > 1) {
pages.push(totalPages);
}
return pages;
};
14.求子数组的最大和、最大积
// 子数组最大和
function maxSubArray(nums) {
let maxSum = nums[0]; // 初始化最大子数组和
let currentSum = nums[0]; // 当前子数组和
let start = 0, end = 0; // 最大子数组的起始和结束索引
let tempStart = 0; // 临时记录当前子数组的起始索引
for (let i = 1; i < nums.length; i++) {
// 如果当前元素和当前和之和大于当前元素,则继续累加,否则重新开始新子数组
if (currentSum + nums[i] > nums[i]) {
currentSum += nums[i];
} else {
currentSum = nums[i];
tempStart = i; // 重新开始一个新子数组
}
// 更新最大和和子数组的起始和结束位置
if (currentSum > maxSum) {
maxSum = currentSum;
start = tempStart;
end = i;
}
}
// 提取最大子数组
const subarray = nums.slice(start, end + 1);
return { maxSum, subarray };
}
// 示例
const nums = [-2, 1, -3, 4, -1, 2, 1, -5, 4];
const result = maxSubArray(nums);
console.log("最大子数组和:", result.maxSum);
console.log("最大子数组:", result.subarray);
// 子数组最大积1
function maxProductSubArray(nums) {
let maxProduct = nums[0]; // 当前子数组的最大积
let minProduct = nums[0]; // 当前子数组的最小积
let result = nums[0]; // 记录结果,最终返回的最大积
for (let i = 1; i < nums.length; i++) {
// 如果当前数是负数,交换最大积和最小积
if (nums[i] < 0) {
[maxProduct, minProduct] = [minProduct, maxProduct];
}
// 更新最大积和最小积
maxProduct = Math.max(nums[i], maxProduct * nums[i]);
minProduct = Math.min(nums[i], minProduct * nums[i]);
// 更新结果
result = Math.max(result, maxProduct);
}
return result;
}
// 示例
const nums = [2, 3, -2, 4];
const result = maxProductSubArray(nums);
console.log("最大子数组积:", result);
// 动态规划法求积
class Solution {
maxProduct(nums) {
if (nums.length === 0) return 0;
let maxStack = [nums[0]];
let minStack = [nums[0]];
for (let i = 1; i < nums.length; i++) {
const n1 = maxStack[maxStack.length - 1] * nums[i];
const n2 = minStack[minStack.length - 1] * nums[i];
const n3 = nums[i];
maxStack.push(Math.max(n1, n2, n3));
minStack.push(Math.min(n1, n2, n3));
}
return Math.max(...maxStack);
}
}
// 示例
const solution = new Solution();
const nums = [2, 3, -2, 4];
const result = solution.maxProduct(nums);
console.log(result); // 输出 6
15.实现useFetch
import { useState, useEffect, useCallback } from 'react';
function useFetch(url, options = {}) {
const [data, setData] = useState(null); // 存储请求的数据
const [loading, setLoading] = useState(true); // 请求状态:加载中
const [error, setError] = useState(null); // 存储错误信息
const [shouldFetch, setShouldFetch] = useState(true); // 控制是否进行请求
// fetchData 用于发起请求
const fetchData = useCallback(async () => {
setLoading(true); // 请求开始,设置加载状态
setError(null); // 清空之前的错误
try {
const response = await fetch(url, options); // 发起 fetch 请求
if (!response.ok) { // 检查响应状态
throw new Error(`HTTP Error! Status: ${response.status}`);
}
const result = await response.json(); // 假设返回的是 JSON 格式数据
setData(result); // 将结果存储到 data 中
} catch (error) {
setError(error.message); // 捕获错误并存储
} finally {
setLoading(false); // 请求完成,更新加载状态
}
}, [url, options]);
// 使用 useEffect 来控制发起请求
useEffect(() => {
if (shouldFetch) {
fetchData(); // 如果 shouldFetch 为 true,发起请求
setShouldFetch(false); // 请求完成后,重置 shouldFetch
}
}, [url, options, shouldFetch, fetchData]);
// refetch 方法,用于手动重新发起请求
const refetch = () => {
setShouldFetch(true); // 设置 shouldFetch 为 true,触发请求
};
return { data, loading, error, refetch }; // 返回请求状态和 refetch 方法
}
export default useFetch;
16.用node.j s 写完整的web应用
// app.js
const express = require('express');
const mongoose = require('mongoose');
const bodyParser = require('body-parser');
const path = require('path');
const app = express();
const User = require('./models/User'); // 引入数据库
// 连接 MongoDB 数据库
mongoose.connect('mongodb://localhost:27017/myapp', { useNewUrlParser: true, useUnifiedTopology: true })
.then(() => console.log('Connected to MongoDB'))
.catch(err => console.log('MongoDB connection error:', err));
// 设置视图引擎为 EJS
app.set('view engine', 'ejs');
app.set('views', path.join(__dirname, 'views'));
// 中间件设置
app.use(bodyParser.urlencoded({ extended: true })); // 解析 URL 编码的请求体
app.use(bodyParser.json()); // 解析 JSON 请求体
// 静态文件托管
app.use(express.static(path.join(__dirname, 'public')));
// 首页路由
app.get('/', (req, res) => {
res.render('index', { title: 'My Node Web App' });
});
// 简单的 POST 路由
app.post('/submit', (req, res) => {
const { name, email } = req.body;
const newUser = new User({ name, email });
await newUser.save();
res.send(`Data has been saved to the database: Name = ${name}, Email = ${email}`);
});
// 404 路由
app.use((req, res) => {
res.status(404).send('Page not found');
});
// 启动服务器
const PORT = 3000;
app.listen(PORT, () => {
console.log(`Server running at http://localhost:${PORT}`);
});
// models/User.js 提交表单时将数据存储到数据库
const mongoose = require('mongoose');
const userSchema = new mongoose.Schema({
name: String,
email: String
});
const User = mongoose.model('User', userSchema);
module.exports = User;
// <!-- views/index.ejs -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><%= title %></title>
<link rel="stylesheet" href="/styles.css">
</head>
<body>
<h1>Welcome to <%= title %></h1>
<form action="/submit" method="POST">
<label for="name">Name:</label>
<input type="text" name="name" id="name" required><br><br>
<label for="email">Email:</label>
<input type="email" name="email" id="email" required><br><br>
<button type="submit">Submit</button>
</form>
</body>
</html>
17.Langchain模型优化代码
from langchain.chat_models import ChatOpenAI
from langchain.prompts import PromptTemplate
from langchain.chains import LLMChain
# 创建一个 OpenAI 模型实例
llm = ChatOpenAI(model="gpt-4", temperature=0)
# 创建一个提示模板,用于代码优化
prompt = PromptTemplate(
input_variables=["code"],
template="你是一个前端开发专家,下面的 JavaScript 代码存在性能或可读性问题,请优化这段代码并给出优化后的版本。代码如下:\n{code}\n\n优化后的代码:"
)
# 使用 LLMChain 创建链
chain = LLMChain(llm=llm, prompt=prompt)
# 示例代码
code_to_optimize = """
function sumArray(arr) {
let total = 0;
for (let i = 0; i < arr.length; i++) {
total += arr[i];
}
return total;
}
"""
# 调用 LangChain 链生成优化后的代码
optimized_code = chain.run({"code": code_to_optimize})
print(optimized_code)
18.发布订阅类EventEmitter
class EventEmitter {
constructor() {
this.list = {}
}
// 订阅事件(subscribe)
on(event, callback) {
if (!this.list[event]) {
this.list[event] = []
}
this.list[event].push(callback)
}
// 发布事件(publish)
emit(event, ...args) {
if (this.list[event]) {
this.list[event].forEach(fn => {
fn.apply(this, args)
});
}
}
// 取消订阅(unsubscribe)
off(event, callback) {
if (this.list[event] && callback) {
this.list[event] = this.list[event].filter(fn => fn !== callback)
}
}
// once方法用于仅订阅一次事件,即回调函数只会被执行一次
once(event, callback) {
const onceCallback = (...args) => {
callback.apply(this, args)
this.off(event, onceCallback)
}
this.on(event, onceCallback)
}
}
function hello(...data) {
console.log('hello' + data);
}
const emitter = new EventEmitter()
// 使用 once 方法订阅事件,回调函数只会被执行一次
emitter.once('onSell', hello)
// 触发事件,只会执行一次回调函数
emitter.emit('onSell', '1', '2', '3')
emitter.emit('onSell', '1', '2', '3')
19. 画一个表盘,实现分针和秒钟的旋转动画
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>表盘动画</title>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<div class="clock">
<div class="dial">
<div class="hand second-hand"></div>
<div class="hand minute-hand"></div>
</div>
</div>
<script src="script.js"></script>
</body>
</html>
/* styles.css */
body {
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
margin: 0;
background: #f5f5f5;
}
.clock {
width: 200px;
height: 200px;
border: 8px solid #333;
border-radius: 50%;
position: relative;
background: white;
box-shadow: 0 0 20px rgba(0, 0, 0, 0.1);
}
.dial {
position: relative;
width: 100%;
height: 100%;
}
.hand {
position: absolute;
bottom: 50%;
left: 50%;
transform-origin: bottom;
transform: rotate(0deg);
transition: transform 0.1s ease-in-out;
}
.second-hand {
width: 2px;
height: 90px;
background: red;
z-index: 2;
}
.minute-hand {
width: 4px;
height: 70px;
background: black;
z-index: 1;
}
// script.js
function setClock() {
const now = new Date();
// 计算当前秒和分的角度
const seconds = now.getSeconds();
const minutes = now.getMinutes();
const secondDegree = (seconds / 60) * 360;
const minuteDegree = (minutes / 60) * 360 + (seconds / 60) * 6; // 添加秒钟影响
// 更新样式
const secondHand = document.querySelector('.second-hand');
const minuteHand = document.querySelector('.minute-hand');
secondHand.style.transform = `rotate(${secondDegree}deg)`;
minuteHand.style.transform = `rotate(${minuteDegree}deg)`;
}
// 每秒更新一次
setInterval(setClock, 1000);
// 初始设置
setClock();
20.模板引擎(模板中有空格)
function templateEngine(template, data) {
return template.replace(/{{\s*(\w+)\s*}}/g, (_, key) => {
// 从数据中获取对应值
const value = data[key.trim()];
// 如果存在值,替换占位符;否则返回占位符本身
return value !== undefined ? value : `{{${key}}}`;
});
}
// 示例用法
const template = `
Hello, {{ name }}!
Today is {{ day }}.
Weather: {{weather}}
`;
const data = {
name: "Alice",
day: "Monday",
weather: "Sunny"
};
console.log(templateEngine(template, data));
21.实现2数近似和
/**
* 找到数组中所有两个数的和最接近目标值的数对,并返回它们的下标
* @param {number[]} nums - 整数数组
* @param {number} target - 目标值
* @return {number[][]} - 所有最接近目标值的数对的下标
*/
function twoSumClosest(nums, target) {
if (nums.length < 2) {
throw new Error("数组中至少需要两个元素");
}
// 创建一个包含元素值和原始下标的数组
const numsWithIndices = nums.map((num, index) => ({ num, index }));
// 对新的数组按照元素值进行排序
numsWithIndices.sort((a, b) => a.num - b.num);
let left = 0;
let right = numsWithIndices.length - 1;
let closestSum = numsWithIndices[left].num + numsWithIndices[right].num;
let minDiff = Math.abs(closestSum - target);
let result = [[numsWithIndices[left].index, numsWithIndices[right].index]];
while (left < right) {
const currentSum = numsWithIndices[left].num + numsWithIndices[right].num;
const currentDiff = Math.abs(currentSum - target);
if (currentDiff < minDiff) {
// 找到新的更接近的和,清空之前的结果并添加当前对
closestSum = currentSum;
minDiff = currentDiff;
result = [[numsWithIndices[left].index, numsWithIndices[right].index]];
} else if (currentDiff === minDiff) {
// 找到相同最小差值的和,添加到结果中
const newPair = [numsWithIndices[left].index, numsWithIndices[right].index];
// 确保小的下标在前,避免 [1,0] 和 [0,1] 这样的重复
newPair.sort((a, b) => a - b);
// 检查是否已经存在该对
const lastPair = result[result.length - 1];
if (!(lastPair[0] === newPair[0] && lastPair[1] === newPair[1])) {
result.push(newPair);
}
}
// 根据当前和与目标值的比较,移动指针
if (currentSum < target) {
left++;
} else if (currentSum > target) {
right--;
} else {
// 当 currentSum === target 时,同时移动两个指针以查找其他可能的数对
left++;
right--;
}
}
return result;
}
// 示例用法
const nums1 = [-1, 2, 1, -4];
const target1 = 1;
const result1 = twoSumClosest(nums1, target1);
console.log(`最接近目标 ${target1} 的数对的下标是:`, result1);
// 输出: 最接近目标 1 的数对的下标是: [ [ 0, 1 ] ]
const nums2 = [-1, 2, 1, -4, 3, 0];
const target2 = 1;
const result2 = twoSumClosest(nums2, target2);
console.log(`最接近目标 ${target2} 的数对的下标是:`, result2);
// 输出: 最接近目标 1 的数对的下标是: [ [ 0, 1 ], [ 2, 5 ] ]
const nums3 = [1, 2, 3, 2, 1];
const target3 = 4;
const result3 = twoSumClosest(nums3, target3);
console.log(`最接近目标 ${target3} 的数对的下标是:`, result3);
// 输出: 最接近目标 4 的数对的下标是: [ [ 1, 2 ], [ 1, 3 ], [ 2, 4 ] ]
console.log(twoSumClosest([2, 7, 11, 15], 10)); // 输出: [0, 1] (2+7最接近10)
console.log(twoSumClosest([1, 3, 5, 8], 7)); // 输出: [1, 2] (3+5最接近7)
console.log(twoSumClosest([3, 8, 12, 17], 20)); // 输出: [1, 2] (8+12最接近20)
console.log(twoSumClosest([1, 1, 1, 1], 10)); // 输出: [0, 1] (1+1最接近10)
console.log(twoSumClosest([-1, 2, 1, -4], 1)); // 输出: [1, 2] (2+1最接近1)
22. 给时间戳输出对应文案:如 [一分钟内]刚刚、x分钟前、x小时前、昨天 xx:xx ,前天,【一星期内】星期几,【超过一星期】xx月xx日xx时xx分,【不在当年】xx年xx月xx日xx时xx分
function timeAgoIntl(timestamp){
let inputTime
if(typeof timestamp === 'number'){
if (timestamp.toString().length === 10){
inputTime = new Date(timestamp*1000)
} else {
inputTime = new Date(timestamp)
}
} else if(typeof timestamp === 'string'){
inputTime = new Date(timestamp)
} else if(timestamp instanceof Date){
inputTime = timestamp
} else {
throw new Error('invalid timestamp')
}
const now = new Date()
const diffInSeconds = (now - inputTime) / 1000
const rtf = new Intl.RelativeTimeFormat('zh-CN',{numeric: 'auto'})
const thresholds = [
{limit:60, divisor:1, unit:'second'},
{limit:3600, divisor:60, unit:'minute'},
{limit:86400, divisor:3600, unit:'hour'},
{limit:604800, divisor:86400, unit:'day'},// todo
]
for(let i=0; i<thresholds.length; i++){
if(diffInSeconds<thresholds[i].limit){
const value = Math.floor(diffInSeconds/thresholds[i].divisor)
return rtf.format(-value,thresholds[i].unit)
}
}
}
const timestampsIntl = [
Date.now()-30*1000, // 刚刚
Date.now()-5*60*1000, // 5分钟前
Date.now()-5*3600*1000, // 5小时前
Date.now()-5*86400*1000, // 5天前
]
timestampsIntl.forEach(ts => {
console.log(timeAgoIntl(ts))
})
23.有一种花, 两种鸟, 花定时开放,鸟看到花开会叫, 鸟的叫声不一样,用代码来实现这样一种场景
class EventBus {
constructor(){
this.list = {}
}
on(fnName,fn){
if(!this.list[fnName]) this.list[fnName] = []
this.list[fnName].push(fn)
}
emit(fnName,...args){
if(this.list[fnName]){
this.list[fnName].forEach(fn => {
fn.apply(this,args)
});
}
}
}
const events = new EventBus()
// 发布者
class Flower{
constructor(){}
open(delay){
setTimeout(()=>{
events.emit('flowerOpen')
}, delay)
}
}
// 订阅者
class Bird{
constructor(name,sound){
this.name = name
this.sound = sound
events.on('flowerOpen', this.makeSound.bind(this))
}
makeSound(){
console.log(this.name, this.sound)
}
}
// 测试
const myFlower = new Flower()
const bird1 = new Bird('A', '布谷布谷')
const bird2 = new Bird('B', '叽叽喳喳')
myFlower.open(1000)