参考:f.zuo11.com推荐的js面试题
改内容由AI提供,可能会有错误。
1. 实现promise.all()
Promise.all = function (promises) {
return new Promise((resolve, reject) => {
let result = [];
let count = 0;
for (let i = 0; i < promises.length; i++) {
promises[i].then((data) => {
result[i] = data;
if (++count === promises.length) {
resolve(result);
}
}, reject);
}
});
};let p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("p1");
}, 1000);
});
let p2 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("p2");
}, 2000);
});
let p3 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("p3");
}, 3000);
});
Promise.all([p1, p2, p3]).then((data) => {
console.log(data);
});
//输出结果:["p1", "p2", "p3"]这个promise.all()例子有什么用呢? 假设我们有一个页面,需要请求多个接口,然后渲染页面,这时候就可以用promise.all()来实现。
可以举例子,需要请求什么接口么?
- 请求用户信息
- 请求用户的订单信息
- 请求用户的地址信息
- 请求用户的优惠券信息
可以看到,这四个接口是没有依赖关系的,可以同时请求,这时候就可以用promise.all()来实现。
实现这个1. 请求用户信息 2. 请求用户的订单信息 3. 请求用户的地址信息 4. 请求用户的优惠券信息请求promise.all()
// 定义请求函数
function fetchUserInfo() {
// 返回一个 Promise,模拟异步请求用户信息接口
return new Promise((resolve, reject) => {
setTimeout(() => {
const userInfo = { name: 'Alice', age: 25 };
resolve(userInfo);
}, 2000);
});
}
function fetchUserOrders() {
// 返回一个 Promise,模拟异步请求用户订单信息接口
return new Promise((resolve, reject) => {
setTimeout(() => {
const orders = ['Order 1', 'Order 2', 'Order 3'];
resolve(orders);
}, 3000);
});
}
function fetchUserAddresses() {
// 返回一个 Promise,模拟异步请求用户地址信息接口
return new Promise((resolve, reject) => {
setTimeout(() => {
const addresses = ['Address 1', 'Address 2', 'Address 3'];
resolve(addresses);
}, 2500);
});
}
function fetchUserCoupons() {
// 返回一个 Promise,模拟异步请求用户优惠券信息接口
return new Promise((resolve, reject) => {
setTimeout(() => {
const coupons = ['Coupon 1', 'Coupon 2', 'Coupon 3'];
resolve(coupons);
}, 1500);
});
}
// 使用 Promise.all() 请求多个接口
Promise.all([
fetchUserInfo(),
fetchUserOrders(),
fetchUserAddresses(),
fetchUserCoupons()
])
.then(([userInfo, userOrders, userAddresses, userCoupons]) => {
// 在这里进行页面渲染逻辑,将接口返回的数据渲染到页面上
console.log('用户信息:', userInfo);
console.log('用户订单:', userOrders);
console.log('用户地址:', userAddresses);
console.log('用户优惠券:', userCoupons);
})
.catch(error => {
// 处理错误情况
console.error('请求接口发生错误:', error);
});
//输出结果
//用户信息: {name: "Alice", age: 25}
//用户订单: (3) ["Order 1", "Order 2", "Order 3"]
//用户地址: (3) ["Address 1", "Address 2", "Address 3"]
//用户优惠券: (3) ["Coupon 1", "Coupon 2", "Coupon 3"]
//这样就实现了多个接口同时请求,然后渲染页面。promise.all()的实现原理
promise.all()的实现原理就是,将多个promise对象放到一个数组中,然后遍历这个数组,将每个promise对象的结果放到一个数组中,然后返回这个数组。
2. Array.prototype.flat 数组扁平化
Array.prototype.flat()方法会按照一个可指定的深度递归遍历数组,将所有元素与遍历到的子数组中的元素合并为一个新数组返回。
var arr1 = [1, 2, [3, 4]];
arr1.flat();
// [1, 2, 3, 4]写一个原生的Array.prototype.flat
Array.prototype.flat = function (depth = 1) {
let result = [];
this.forEach((item) => {
if (Array.isArray(item) && depth > 0) {
result.push(...item.flat(depth - 1));
} else {
result.push(item);
}
});
return result;
};3. instanceof
instanceof 运算符用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上。
function Car(make, model, year) {
this.make = make;
this.model = model;
this.year = year;
}
const auto = new Car('Honda', 'Accord', 1998);
console.log(auto instanceof Car);
// expected output: trueinstanceof的实现原理
function myInstanceof(left, right) {
let prototype = right.prototype;
left = left.__proto__;
while (true) {
if (left === null || left === undefined) {
return false;
}
if (prototype === left) {
return true;
}
left = left.__proto__;
}
}交通灯
function red() {
console.log("red");
}
function green() {
console.log("green");
}
function yellow() {
console.log("yellow");
}
let light = function (timer, cb) {
return new Promise((resolve, reject) => {
setTimeout(() => {
cb();
resolve();
}, timer);
});
};
let step = function () {
Promise.resolve()
.then(() => {
return light(3000, red);
})
.then(() => {
return light(2000, green);
})
.then(() => {
return light(1000, yellow);
})
.then(() => {
step();
});
};
step();如何停止上面的promise
let shouldStop = false;
function red() {
console.log("red");
}
function green() {
console.log("green");
}
function yellow() {
console.log("yellow");
}
let light = function (timer, cb) {
return new Promise((resolve, reject) => {
setTimeout(() => {
cb();
resolve();
}, timer);
});
};
let step = function () {
if (shouldStop) {
return;
}
Promise.resolve()
.then(() => {
return light(3000, red);
})
.then(() => {
return light(2000, green);
})
.then(() => {
return light(1000, yellow);
})
.then(() => {
step();
});
};
// 停止函数的执行
function stop() {
shouldStop = true;
}
// 启动函数的执行
function start() {
shouldStop = false;
step();
}
start(); // 启动函数的执行
// 运行一段时间后调用 stop() 函数停止执行
setTimeout(stop, 10000);4. 数组去重
let arr = [1, 2, 3, 4, 5, 1, 2, 3, 4, 5];
let newArr = [...new Set(arr)];
console.log(newArr); // [1, 2, 3, 4, 5]6. 数组乱序
let arr = [1, 2, 3, 4, 5];
let newArr = arr.sort(() => Math.random() - 0.5);
console.log(newArr); // [3, 1, 5, 2, 4]7. 数组最大值
let arr = [1, 2, 3, 4, 5];
let max = Math.max(...arr);
console.log(max); // 58. 封装异步的fetch,使用async await方式来使用
function fetch(url) {
return new Promise((resolve, reject) => {
let xhr = new XMLHttpRequest();
xhr.open("GET", url, true);
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
resolve(xhr.responseText);
} else {
reject(xhr.responseText);
}
}
};
xhr.send(null);
});
}
async function getData() {
let data = await fetch("https://janeanne218.github.io/myblog2/");
console.log(data);
}
getData();9. 封装一个工具函数输入一个promiseA返回一个promiseB如果超过1s没返回则抛出异常如果正常则输出正确的值
//封装一个工具函数输入一个promiseA返回一个promiseB如果超过1s没返回则抛出异常如果正常则输出正确的值
function timeout(promise) {
return new Promise((resolve, reject) => {
let timer = setTimeout(() => {
reject("超时");
}, 1000);
promise.then((res) => {
clearTimeout(timer);
resolve(res);
});
});
}
let promise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("成功");
}, 500);
});
timeout(promise).then((res) => {
console.log(res);
});10. 使用 Promise 封装 AJAX 请求
function ajax(url) {
return new Promise((resolve, reject) => {
let xhr = new XMLHttpRequest();
xhr.open("GET", url, true);
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
resolve(xhr.responseText);
} else {
reject(xhr.responseText);
}
}
};
xhr.send(null);
});
}
ajax("https://janeanne218.github.io/myblog2/").then((res) => {
console.log(res);
});我们能反过来使用 setinterval 模拟实现 settimeout 吗?
function mySetTimeout(fn, time) {
let timer = setInterval(() => {
fn();
clearInterval(timer);
}, time);
}
mySetTimeout(() => {
console.log("hello");
}, 1000);异步任务:依次发送 3 次网络请求,拿到服务器数据
function fetch(url) {
return new Promise((resolve, reject) => {
let xhr = new XMLHttpRequest();
xhr.open("GET", url, true);
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
resolve(xhr.responseText);
} else {
reject(xhr.responseText);
}
}
};
xhr.send(null);
});
}
let urls = [
"https://janeanne218.github.io/myblog2/",
"https://janeanne218.github.io/myblog2/",
"https://janeanne218.github.io/myblog2/",
];
let promises = urls.map((url) => fetch(url));
Promise.all(promises).then((res) => {
console.log(res);
});实现网络请求超时判断,超过三秒视为超时
function fetch(url) {
return new Promise((resolve, reject) => {
let xhr = new XMLHttpRequest();
xhr.open("GET", url, true);
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
resolve(xhr.responseText);
} else {
reject(xhr.responseText);
}
}
};
xhr.send(null);
});
}
function timeout(promise) {
return new Promise((resolve, reject) => {
let timer = setTimeout(() => {
reject("超时");
}, 3000);
promise.then((res) => {
clearTimeout(timer);
resolve(res);
});
});
}
timeout(fetch("https://janeanne218.github.io/myblog2/"))
.then((res) => {
console.log(res);
})
.catch((err) => {
console.log(err);
});实现一个函数,可以对 url 中的 query 部分做拆解,返回一个 key - value 形式的 object
function parseQuery(url) {
let obj = {};
let query = url.split("?")[1];
let queryArr = query.split("&");
queryArr.forEach((item) => {
let key = item.split("=")[0];
let value = item.split("=")[1];
obj[key] = value;
});
return obj;
}
let url = "https://janeanne218.github.io/myblog2/?name=anne&age=18";
console.log(parseQuery(url)); // {name: "anne", age: "18"}settimeout系统补偿时间
setTimeout(() => {
console.log(1);
}, 0);
new Promise((resolve, reject) => {
console.log(2);
resolve();
}).then(() => {
console.log(3);
});
console.log(4);
// 2 4 3 1为什么是2 4 3 1的输出顺序? 因为setTimeout(fn, 0)并不是立即执行fn,而是将fn放入宏任务队列中,等待执行,而Promise.then(fn)是将fn放入微任务队列中,等待执行,所以先执行2,再执行4,再执行3,最后执行1。 这里宏任务队列,和微任务对立的区别
宏任务队列:setTimeout、setInterval、setImmediate、I/O、UI rendering
微任务队列:process.nextTick、Promise.then、Object.observe、MutationObserver
什么是宏任务队列?什么是微任务队列?
宏任务队列:宏任务队列是一个存放宏任务的队列,宏任务的执行由主线程来执行,宏任务队列可以有多个,排在前面的先执行,后面的后执行。
微任务队列:微任务队列是一个存放微任务的队列,微任务的执行由js引擎来执行,微任务队列只有一个,排在前面的先执行,后面的后执行。
什么是系统补偿时间?系统补偿时间与宏任务和微任务有什么关系?
系统补偿时间:系统补偿时间是指js引擎为了保证宏任务和微任务的执行顺序而设置的一个时间,这个时间一般是4ms,也就是说,如果在4ms内,宏任务执行完毕,那么就会立即执行微任务,如果4ms内宏任务没有执行完毕,那么就会等待宏任务执行完毕后立即执行微任务。
setTimeout准时
function setTimeoutWithOffset(fn, interval, ...args) {
let startTime = Date.now()
let count = 0// 执行次数
let timer = null
const task = () => {
// offset = timeNow - (startTime + count * interval)
const offset = Date.now() - (startTime + count * interval)
console.log(`第${count + 1}次 系统延迟时间${offset}`)//
timer = setTimeout(() => {
fn.apply(this, args);
count++
task();
}, interval - offset);
};
task();
return () => clearTimeout(timer);
}
//TEST
const stop = setTimeoutWithOffset(() => console.log(123), 1000)
setTimeout(() => {
stop()
}, 5000);请求五秒未完成则终止
//请求五秒未完成则终止
function fetch(url) {
return new Promise((resolve, reject) => {
let xhr = new XMLHttpRequest();
xhr.open("GET", url, true);
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
resolve(xhr.responseText);
} else {
reject(xhr.responseText);
}
}
};
xhr.send(null);
});
}
function timeout(promise, interval) {
return new Promise((resolve, reject) => {
let timer = setTimeout(() => {
reject("超时");
}, interval);
promise.then((res) => {
clearTimeout(timer);
resolve(res);
});
});
}
timeout(fetch("https://janeanne218.github.io/myblog2/"), 5000)
.then((res) => {
console.log(res);
})
.catch((err) => {
console.log(err);
});JS 异步数据流,实现并发异步请求,结果顺序输出
function fetch(url) {
return new Promise((resolve, reject) => {
let xhr = new XMLHttpRequest();
xhr.open("GET", url, true);
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
resolve(xhr.responseText);
} else {
reject(xhr.responseText);
}
}
};
xhr.send(null);
});
}
function fetchAll(urls) {
let arr = [];
urls.forEach((url) => {
arr.push(fetch(url));
});
return Promise.all(arr);
}
fetchAll([
"https://janeanne218.github.io/myblog2/",
"https://janeanne218.github.io/myblog2/",
"https://janeanne218.github.io/myblog2/",
])
.then((res) => {
console.log(res);
})
.catch((err) => {
console.log(err);
});什么是异步数据流?什么是并发异步请求?
异步数据流:异步数据流是指多个异步请求的数据流,异步数据流的数据是按照请求的顺序来的,比如:请求1,请求2,请求3,那么数据流的数据就是请求1的数据,请求2的数据,请求3的数据。
并发异步请求:并发异步请求是指多个异步请求同时发出,不需要等待上一个请求的结果,比如:请求1,请求2,请求3,那么请求1,请求2,请求3同时发出,不需要等待上一个请求的结果。
异步数据流和并发异步请求的应用场景?
异步数据流:异步数据流的应用场景是在需要按照顺序来获取数据的时候,比如:需要获取用户的信息,需要获取用户的订单信息,需要获取用户的地址信息,这三个请求的数据是按照顺序来的,这个时候就可以使用异步数据流。
并发异步请求:并发异步请求的应用场景是在需要同时获取多个数据的时候,比如:需要获取用户的信息,需要获取用户的订单信息,需要获取用户的地址信息,这三个请求的数据是不需要按照顺序来的,这个时候就可以使用并发异步请求。
Promise 串行
function fetch(url) {
return new Promise((resolve, reject) => {
let xhr = new XMLHttpRequest();
xhr.open("GET", url, true);
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
resolve(xhr.responseText);
} else {
reject(xhr.responseText);
}
}
};
xhr.send(null);
});
}
function fetchAll(urls) {
let arr = [];
urls.forEach((url) => {
arr.push(fetch(url));
});
return Promise.all(arr);
}
fetchAll([
"https://janeanne218.github.io/myblog2/",
"https://janeanne218.github.io/myblog2/",
"https://janeanne218.github.io/myblog2/",
])
.then((res) => {
console.log(res);
})
.catch((err) => {
console.log(err);
});Promise 并行
function fetch(url) {
return new Promise((resolve, reject) => {
let xhr = new XMLHttpRequest();
xhr.open("GET", url, true);
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
resolve(xhr.responseText);
} else {
reject(xhr.responseText);
}
}
};
xhr.send(null);
});
}
function fetchAll(urls) {
let arr = [];
urls.forEach((url) => {
arr.push(fetch(url));
});
return Promise.all(arr);
}
fetchAll([
"https://janeanne218.github.io/myblog2/",
"https://janeanne218.github.io/myblog2/",
"https://janeanne218.github.io/myblog2/",
])
.then((res) => {
console.log(res);
})
.catch((err) => {
console.log(err);
});promise的串行原理以及并行原理
promise的串行原理:promise的串行原理是通过promise的then方法来实现的,比如:promise1.then(res => {return promise2}).then(res => {return promise3}),这样就实现了promise的串行。
promise的并行原理:promise的并行原理是通过promise的all方法来实现的,比如:Promise.all([promise1, promise2, promise3]),这样就实现了promise的并行。
promise的串行和并行的区别?
串行和异步数据流是一样的,都是按照顺序来获取数据,但是串行是通过promise的then方法来实现的,异步数据流是通过promise的all方法来实现的。
处理高并发, 100 条数据,带宽为 10, 跑满带宽
//应用场景:批量上传文件
function asyncPool(limit,dates,fn) {
let count = 0;// 计数器
const exec = [];// 正在执行的任务
const values = [];// 执行完成的任务
const _asyncPool = () => {// 递归执行
if (count === dates.length) { // 执行完毕
return Promise.resolve();// 返回一个成功的promise
}
const item = dates[count++];// 取出第一个任务
const p = Promise.resolve().then(()=>fn(item));// 执行任务
values.push(p);// 保存任务的执行结果
const _p = p.then(()=>exec.splice(exec.indexOf(_p), 1));// 执行完毕,从正在执行的任务中移除
exec.push(_p);// 保存正在执行的任务
let next = Promise.resolve();// 下一个任务
if (exec.length >= limit) { // 达到最大并发数
next = Promise.race(exec);// 等待最快的任务执行完毕
}
return next.then(()=>_asyncPool());// 递归执行
}
return _asyncPool().then(()=>Promise.all(values));// 返回所有任务的执行结果
}设计一个简单的任务队列, 要求分别在 1,3,4 秒后打印出 "1", "2", "3";
class Queue {
constructor() {
this.queue = [];
}
task(time, fn) {
this.queue.push({ time, fn });
return this;
}
start() {
this.queue.forEach((item) => {
setTimeout(() => {
item.fn();
}, item.time);
});
}
}
new Queue()
.task(1000, () => {
console.log(1);
})
.task(3000, () => {
console.log(2);
})
.task(4000, () => {
console.log(3);
})
.start();请实现一个批量请求函数 multiRequest(urls, maxNum),要求如下:
• 要求最大并发数 maxNum • 每当有一个请求返回,就留下一个空位,可以增加新的请求 • 所有请求完成后,结果按照 urls 里面的顺序依次打出
function multiRequest(urls = [], maxNum) {
const len = urls.length;
const result = new Array(len).fill(false);
let count = 0;
return new Promise((resolve, reject) => {
while (count < maxNum) {
next();
}
function next() {
let current = count++;
if (current >= len) {
!result.includes(false) && resolve(result);
return;
}
const url = urls[current];
console.log(`开始 ${current}`, new Date().toLocaleString());
fetch(url)
.then((res) => {
console.log(`完成 ${current}`, new Date().toLocaleString());
result[current] = res;
if (current < len) {
next();
}
})
.catch((err) => {
console.log(`结束 ${current}`, new Date().toLocaleString());
result[current] = err;
if (current < len) {
next();
}
});
}
});
}实现有并行限制的 Promise 调度器
//原理:通过一个计数器来控制并发数量,每次并发执行完毕后,再继续执行下一个任务,直到任务全部执行完毕
class Scheduler {
constructor() {// 任务队列
this.queue = [];// 最大并发数
this.maxCount = 2;// 当前并发数
this.runCounts = 0;// 等待队列
}
add(promiseCreator) {
this.queue.push(promiseCreator);// 执行任务
}
taskStart() {
for (let i = 0; i < this.maxCount; i++) {
this.request();// 先执行最大并发数个任务
}
}
request() {// 没有任务了或者超出最大并发数,直接return
if (!this.queue || !this.queue.length || this.runCounts >= this.maxCount) {
return;
}
this.runCounts++;// 取出一个任务执行
this.queue
.shift()()
.then(() => {
this.runCounts--;
this.request();
});
}
}
// 测试代码
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(1000, "1");
addTask(500, "2");
addTask(300, "3");
addTask(400, "4");
scheduler.taskStart();// output: 2 3 1 4使用 Promise 改写回调地狱
回调地狱:多个异步操作依次执行,后一个异步操作依赖前一个异步操作的结果,如果前一个异步操作失败,后一个异步操作就不执行了
//原理:通过promise的then方法来实现
function ajax(url) {
return new Promise((resolve, reject) => {
var xhr = new XMLHttpRequest();
xhr.open("GET", url);
xhr.responseType = "json";
xhr.onload = function () {
if (this.status === 200) {
resolve(this.response);
} else {
reject(new Error(this.statusText));
}
};
xhr.send();
});
}
ajax("/api/users.json")
.then((res) => {
console.log(res);
return ajax("/api/urls.json");
})
.then((res) => {
console.log(res);
});设计一个函数,该函数的参数为可同时发送请求的大小,返回一个函数,该函数的参数为要请求的 url。 实现的效果为,同时发送 n 个请求,当有请求返回后往请求队列里 push 新的请求,并输出刚刚结束的请求的返回值
function createRequestQueue(concurrency) {
let count = 0; // 当前正在执行的请求数量
const requestQueue = []; // 请求队列
async function makeRequest(url) {
try {
const response = await fetch(url); // 发送请求
const result = await response.json(); // 解析响应数据
console.log("Response:", result); // 输出刚刚结束的请求的返回值
} catch (error) {
console.error("Error:", error);
} finally {
count--; // 请求完成,减少计数器
if (requestQueue.length > 0) {
// 如果还有待发送的请求,则将新的请求加入请求队列
const nextRequest = requestQueue.shift();
makeRequest(nextRequest);
}
}
}
return function sendRequest(url) {
if (count < concurrency) {
// 如果正在执行的请求数量小于并发数,则立即发送请求
count++;
makeRequest(url);
} else {
// 否则将请求加入请求队列
requestQueue.push(url);
}
};
}
// 示例用法
const sendRequest = createRequestQueue(3); // 设置并发数为3
sendRequest("https://example.com/url1");
sendRequest("https://example.com/url2");
sendRequest("https://example.com/url3");
sendRequest("https://example.com/url4");
// ...Promise.retry 超时重新请求,并在重试一定次数依然失败时输出缓存内容
function delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
async function retry(fn, maxRetries, timeout) {
let retries = 0;
let cache;
while (retries < maxRetries) {
try {
const result = await Promise.race([
fn(),
delay(timeout).then(() => { throw new Error('Timeout'); })
]);
// 请求成功,返回结果
return result;
} catch (error) {
console.error(`Attempt #${retries + 1} failed: ${error.message}`);
retries++;
// 如果是超时错误,则继续重试
if (error.message === 'Timeout') {
continue;
}
// 缓存请求结果
cache = error;
}
}
// 达到最大重试次数后,输出缓存内容
console.log('Max retries reached, returning cache:', cache);
return cache;
}
// 示例用法
const fetchData = () => {
// 模拟网络请求,这里使用一个异步的 Promise 来模拟
return new Promise((resolve, reject) => {
const random = Math.random();
setTimeout(() => {
if (random < 0.7) {
resolve('Data fetched successfully');
} else {
reject('Failed to fetch data');
}
}, 1000);
});
};
retry(fetchData, 3, 2000)
.then(result => console.log('Final result:', result))
.catch(error => console.error('All retries failed:', error));写一个 mySetInterVal(fn, a, b),每次间隔 a,a+b,a+2b 的时间,然后写一个 myClear,停止上面的 mySetInterVal
function mySetInterVal(fn, a, b) {
let timer = null;
let count = 0;
function interval() {
timer = setTimeout(() => {
fn();
count++;
interval();
}, a + b * count);
}
interval();
return {
clear: function () {
clearTimeout(timer);
},
};
}产生一个不重复的随机数组
function randomArray(n, min, max) {
const arr = [];
for (let i = 0; i < n; i++) {
const random = Math.floor(Math.random() * (max - min + 1)) + min;
if (arr.indexOf(random) === -1) {
arr.push(random);
} else {
i--;
}
}
return arr;
}await async 如何实现
function asyncToGenerator(generatorFunc) {
// 返回的是一个新的函数
return function () {
// 先调用generator函数 生成迭代器
// 对应 var gen = testG()
const gen = generatorFunc.apply(this, arguments);
// 返回一个promise 因为外部是用.then的方式 或者await的方式去使用这个函数的返回值的
// var test = asyncToGenerator(testG)
// test().then(res => console.log(res))
return new Promise((resolve, reject) => {
// 内部定义一个step函数 用来一步一步的跨过yield的阻碍
// key有next和throw两种取值,分别对应了gen的next和throw方法
// arg参数则是用来把promise resolve出来的值交给下一个yield
function step(key, arg) {
let generatorResult;
// 这个方法需要包裹在try catch中
// 如果报错了 就把promise给reject掉 外部通过.catch可以获取到错误
try {
generatorResult = gen[key](arg);
} catch (error) {
return reject(error);
}
// gen.next() 得到的结果是一个 { value, done } 的结构
const { value, done } = generatorResult;
if (done) {
// 如果已经完成了 就直接resolve这个promise
// 这个done是在最后一次调用next后才会为true
// 以本文的例子来说 此时的结果是 { done: true, value: 'success' }
// 这个value也就是generator函数最后的返回值
return resolve(value);
} else {
// 除了最后结束的时候外,每次调用gen.next()
// 其实是返回 { value: Promise, done: false } 的结构,
// 这里要注意的是Promise.resolve可以接受一个promise为参数
// 并且这个promise参数被resolve的时候,这个then才会被调用
return Promise.resolve(
// 这个value对应的是yield后面的promise
value
).then(
// value这个promise被resove的时候,就会执行next
// 并且只要done不是true的时候 就会递归的往下解开promise
// 对应gen.next().value.then(value => {
// gen.next(value).value.then(value2 => {
// gen.next()
//
// // 此时done为true了 整个promise被resolve了
// // 最外部的test().then(res => console.log(res))的then就开始执行了
// })
// })
function onResolve(val) {
step("next", val);
},
// 如果promise被reject了 就再次进入step函数
// 不同的是,这次的try catch中调用的是gen.throw(err)
// 那么自然就被catch到 然后把promise给reject掉啦
function onReject(err) {
step("throw", err);
}
);
}
}
step("next");
});
};
}计算ul下有多少个li
const ulElement = document.querySelector('#readme > div.Box-body.px-5.pb-5 > article > ul:nth-child(35)');
const liElements = ulElement.querySelectorAll('li');
console.log('Number of li elements:', liElements.length);
// 假设 `nodeList` 是一个 NodeList 对象
const nodeList = document.querySelectorAll('#readme > div.Box-body.px-5.pb-5 > article > ul:nth-child(35) li');
// 使用 Array.from() 将 NodeList 转换为数组,并使用 map() 方法获取每个元素的 innerText
const innerTextArray = Array.from(nodeList).map((element) => element.innerText);
console.log(innerTextArray);实现圆形环状进度条
<!DOCTYPE html>
<html>
<head>
<style>
.progress-ring {
width: 100px;
height: 100px;
background-color: lightgray;
border-radius: 50%;
position: relative;
overflow: hidden;
}
.progress-ring .circle {
width: 100%;
height: 100%;
position: absolute;
transform: rotate(-90deg);
}
.progress-ring .mask {
width: 100%;
height: 100%;
clip: rect(0, 50px, 100px, 0);
background-color: blue;
position: absolute;
}
</style>
</head>
<body>
<div class="progress-ring">
<div class="circle">
<div class="mask"></div>
</div>
</div>
<script>
function setProgress(percent) {
const mask = document.querySelector('.mask');
const circle = document.querySelector('.circle');
const radius = circle.clientWidth / 2;
const circumference = 2 * Math.PI * radius;
const offset = circumference - percent / 100 * circumference;
mask.style.strokeDasharray = `${circumference} ${circumference}`;
mask.style.strokeDashoffset = offset;
}
// 调用 setProgress 函数设置进度百分比
setProgress(75);
</script>
</body>
</html>1px问题
1 px问题"是指在高分辨率屏幕上显示像素精细度为1像素的细线或细节时,由于屏幕像素密度的限制,可能导致这些线或细节显示不清晰或模糊的情况。这种情况通常发生在使用低分辨率图像或设计进行显示的情况下,在高分辨率屏幕上细微的细节可能会失去清晰度,呈现出锯齿状或不平滑的外观。
在传统屏幕上,每个像素对应一个物理像素点,因此可以轻松显示1像素的精细线条。但是,随着高分辨率(如Retina显示器)的出现,设备的像素密度增加,将更多的物理像素放入相同的空间中,从而使1像素的细线条变得难以清晰地显示。
为了解决1 px问题,可以采用一些技术手段,例如:
使用矢量图形:使用矢量图形格式(如SVG),它们可以无损缩放而不会受到像素密度影响。 使用CSS的transform: scale()属性:通过缩放元素来实现清晰的细线条效果。 使用像素比媒体查询:根据设备的像素比(如@media (-webkit-min-device-pixel-ratio: 2))应用不同的样式或图像资源,以适应高分辨率屏幕。 这些方法可以提供更好的显示效果,并解决在高分辨率屏幕上出现的1 px问题。
TS
mypick
设计模式相关
单例模式
单例模式原理:保证一个类仅有一个实例,并提供一个访问它的全局访问点。 单例模式应用场景:全局缓存、全局状态管理、浏览器中的 window 对象、登录框、购物车、vuex、redux、react-router、vue-router 等。 我要如何才算学会了单例模式呢?
// 1. 使用命名空间
var namespace1 = {
a: function () {
console.log(1)
},
b: function () {
console.log(2)
}
}
// 2. 使用闭包封装私有变量
var user = (function () {
var _name = 'sven',
_age = 29
return {
getUserInfo: function () {
return _name + '-' + _age
}
}
})()
// 3. 使用 ES6 的 class
class SingleObject {
login() {
console.log('login...')
}
}
SingleObject.getInstance = (function () {
let instance
return function () {
if (!instance) {
instance = new SingleObject()
}
return instance
}
})()
let obj1 = SingleObject.getInstance()
obj1.login()
let obj2 = SingleObject.getInstance()
obj2.login()
console.log('obj1 === obj2', obj1 === obj2)
// 4. 使用代理模式
var CreateDiv = function (html) {
this.html = html
this.init()
}
CreateDiv.prototype.init = function () {
var div = document.createElement('div')
div.innerHTML = this.html
document.body.appendChild(div)
}
var ProxySingletonCreateDiv = (function () {
var instance
return function (html) {
if (!instance) {
instance = new CreateDiv(html)
}
return instance
}
})()
var a = new ProxySingletonCreateDiv('sven1')
var b = new ProxySingletonCreateDiv('sven2')
console.log(a === b)组件
全选
歌词滚动
const lyrics = ``
const lyricsList = lyrics.split('\n').filter(line => line.trim() !== '');
const ul = document.createElement('ul');
for (let i = 6; i < lyricsList.length; i++) {
const line = lyricsList[i];
const [time, text] = line.split(']');
const formattedTime = time.slice(1);
const li = document.createElement('li');
li.textContent = text.trim();
li.setAttribute('data-time', formattedTime);
ul.appendChild(li);
}
// Add the ul element to the DOM
document.body.appendChild(ul);