反转字符串 ‘I love you’
将‘I love you’变为’you love I‘
关键点用join把数组转变为字符串(join可以指定字符串的连接符)
const str = 'i love you';
const temp = str.split(' ');
temp.reverse();
console.log(temp.join(' ')) // you love i
将[1,2,3,4,5,6,7,8,9,10]转变为二维数组[[1,2,3],[4,5,6],[7,8,9],[10]]
思路: 将一维数组每隔三项放一起,可以利用arr.slice(i, i+3),所以关键是slice的索引设置,这时候用forEach和map不合适因为不需要对每一项遍历,所以使用for循环
const arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13];
const result = [];
// 循环的i每次都+3
for (let i = 0; i <= arr.length - 1; i += 3) {
result.push(arr.slice(i, i + 3))
}
console.log(result) // [ [ 1, 2, 3 ], [ 4, 5, 6 ], [ 7, 8, 9 ], [ 10, 11, 12 ], [ 13 ] ]
简短优雅地利用js实现 sleep 函数
最佳方案:利用Promise()结合setTimeout 实现sleep 然后在async await中使用
const log = console.log;
const sleep = (timeout) => {
return new Promise((resolve)=>{
setTimeout(()=>{
resolve();
}, timeout)
})
}
const main = async()=>{
await sleep(1000);
log(1);
await sleep(2000);
log(2);
await sleep(3000);
log(3);
}
字符串替换
str='aa{a.b}ccdd{a.c}aa123{a.d}',obj={b: 789,c:56}, 输出'aa789ccdd56aa123',即括号里的用obj里面的值替换?
function parseString(str, obj) {
Object.keys(obj).forEach(key => {
str = str.replace(new RegExp(`{a.${key}}`, 'g'), obj[key]);
});
return str;
}
字符串中的第一个唯一字符
给定一个字符串,找到它的第一个不重复的字符,并返回它的索引。如果不存在则返回 -1。
示例 1:
输入: s = "leetcode"
输出: 0
示例 2:
输入: s = "loveleetcode"
输出: 2
示例 3:
输入: s = "aabb"
输出: -1
// 遍历字符串的每一项 判断lastIndexOf 和 indexOf这两项是否相等, 如果相等就证明是第一个唯一的字符串
var firstUniqChar = function(s) {
for(let i=0;i<s.length;i++){
if(s.lastIndexOf(s[i])==s.indexOf(s[i])){
let a = s.indexOf(s[i])
return a;
}
}return -1;
};
console.log(firstUniqChar("loveleetcode"));
1 compose
题目描述:实现一个 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 a = compose(fn1, fn2, fn3, fn4);
console.log(a(1)); // 1+4+3+2+1=11
复制代码
实现代码如下:
function compose(...fn) {
if (!fn.length) return (v) => v;
if (fn.length === 1) return fn[0];
// 相当于每一个函数的执行结果又作为下一个函数的参数
return fn.reduce(
(pre, cur) =>
(...args) =>
pre(cur(...args))
);
}
复制代码
2 settimeout 模拟实现 setinterval(带清除定时器的版本)
题目描述:setinterval 用来实现循环定时调用 可能会存在一定的问题 能用 settimeout 解决吗
实现代码如下:
function mySettimeout(fn, t) {
let timer = null;
function interval() {
fn();
timer = setTimeout(interval, t);
}
interval();
return {
cancel:()=>{
clearTimeout(timer)
}
}
}
// let a=mySettimeout(()=>{
// console.log(111);
// },1000)
// let b=mySettimeout(() => {
// console.log(222)
// }, 1000)
复制代码
扩展:我们能反过来使用 setinterval 模拟实现 settimeout 吗?
const mySetTimeout = (fn, time) => {
const timer = setInterval(() => {
clearInterval(timer);
fn();
}, time);
};
// mySetTimeout(()=>{
// console.log(1);
// },1000)
复制代码
扩展思考:为什么要用 settimeout 模拟实现 setinterval?setinterval 的缺陷是什么?
答案请自行百度哈 这个其实面试官问的也挺多的 小编这里就不展开了
4 数组去重
实现代码如下:
function uniqueArr(arr) {
return [...new Set(arr)];
}
复制代码
5 数组扁平化
题目描述:实现一个方法使多维数组变成一维数组
最常见的递归版本如下:
function flat(arr, depth = 1) {
if (depth > 0) {
// 以下代码还可以简化,不过为了可读性,还是....
return arr.reduce((pre, cur) => {
return pre.concat(Array.isArray(cur) ? flat(cur, depth - 1) : cur);
}, []);
}
return arr.slice();
}
复制代码
扩展思考:能用迭代的思路去实现吗?
实现代码如下:
function flatter(arr) {
if (!arr.length) return;
while (arr.some((item) => Array.isArray(item))) {
arr = [].concat(...arr);
}
return arr;
}
// console.log(flatter([1, 2, [1, [2, 3, [4, 5, [6]]]]]));
复制代码
7 实现有并行限制的 Promise 调度器
入参是tasks 里面是一个promise对象的数组,limit是限制并发的数量,
runningCount
:用于跟踪当前正在执行的任务数量。completedCount
:记录已经完成的任务数量。results
:用于存储成功完成的任务的结果。failedTasks
:存储失败的任务及其相关错误信息。
executeNextTask
函数:
- 当还有可执行任务(
runningCount < limit
且completedCount < tasks.length
)时,获取下一个任务。 - 执行任务,如果成功,更新相关状态并执行下一个任务;如果失败,将任务和错误信息存入
failedTasks
并执行下一个任务。
retryFailedTasks
函数:
- 当有失败任务且还有执行能力(
runningCount < limit
)时,取出失败任务重新执行。 - 成功则更新状态,失败则再次放入
failedTasks
。
在主逻辑中:
- 首先通过循环启动最多
limit
个任务的执行。 - 然后通过一个定时检查的
interval
来判断是否所有任务都完成(包括成功和失败处理完毕)。 - 如果完成则清除定时并解析
results
;否则调用retryFailedTasks
尝试重新执行失败任务。
function limitedConcurrencyExecutor(tasks, limit) {
let runningCount = 0;
let completedCount = 0;
let results = [];
let failedTasks = [];
function executeNextTask() {
if (runningCount < limit && completedCount < tasks.length) {
const task = tasks[completedCount];
runningCount++;
task.then(result => {
runningCount--;
results.push(result);
completedCount++;
executeNextTask();
}).catch(error => {
runningCount--;
failedTasks.push({ task, error });
executeNextTask();
});
}
}
for (let i = 0; i < limit; i++) {
executeNextTask();
}
function retryFailedTasks() {
while (failedTasks.length > 0 && runningCount < limit) {
const { task, error } = failedTasks.shift();
runningCount++;
task.then(result => {
runningCount--;
results.push(result);
retryFailedTasks();
}).catch(error => {
runningCount--;
failedTasks.push({ task, error });
retryFailedTasks();
});
}
}
return new Promise((resolve) => {
const interval = setInterval(() => {
if (completedCount === tasks.length && failedTasks.length === 0) {
clearInterval(interval);
resolve(results);
} else {
retryFailedTasks();
}
}, 100);
});
}
// 示例用法
const tasks = [
new Promise((resolve) => setTimeout(() => resolve('Task 1 result'), 1000)),
new Promise((reject) => setTimeout(() => reject('Task 2 error'), 2000)),
new Promise((resolve) => setTimeout(() => resolve('Task 3 result'), 500))
];
limitedConcurrencyExecutor(tasks, 2).then(results => console.log(results));
8 new 操作符
题目描述:手写 new 操作符实现
实现代码如下:
function myNew(fn, ...args) {
let obj = Object.create(fn.prototype);
let res = fn.call(obj, ...args);
if (res && (typeof res === "object" || typeof res === "function")) {
return res;
}
return obj;
}
用法如下:
// // function Person(name, age) {
// // this.name = name;
// // this.age = age;
// // }
// // Person.prototype.say = function() {
// // console.log(this.age);
// // };
// // let p1 = myNew(Person, "lihua", 18);
// // console.log(p1.name);
// // console.log(p1);
// // p1.say();
复制代码
10 深拷贝(考虑到复制 Symbol 类型)
题目描述:手写 new 操作符实现
实现代码如下:
function deepCopy(obj) {
if (typeof obj!== 'object' || obj === null) {
return obj;
}
let newObj;
if (Array.isArray(obj)) {
newObj = [];
for (let item of obj) {
newObj.push(deepCopy(item));
}
} else {
newObj = {};
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
newObj[key] = deepCopy(obj[key]);
}
}
}
return newObj;
}
复制代码
12 柯里化
题目描述:柯里化(Currying),又称部分求值(Partial Evaluation),是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数而且返回结果的新函数的技术。核心思想是把多参数传入的函数拆成单参数(或部分)函数,内部再返回调用下一个单参数(或部分)函数,依次处理剩余的参数。
实现代码如下:
function currying(fn, ...args) {
const length = fn.length;
let allArgs = [...args];
const res = (...newArgs) => {
allArgs = [...allArgs, ...newArgs];
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))
复制代码
16 快排--时间复杂度 nlogn~ n^2 之间
题目描述:实现一个快排
实现思想: 任意选一个数字作为中间数,然后递归把所有小于中间数的放左边,大于的放右边,每个数字都重复这一步
实现代码如下:
function quickSort(arr) {
// 前面的if判断必须加 否则filter会报错 :Maximum call stack size exceeded
if (arr.length < 2) {
return arr;
}
const cur = arr[arr.length - 1];
const left = arr.filter((v, i) => v <= cur && i !== arr.length - 1);
const right = arr.filter((v) => v > cur);
return [...quickSort(left), cur, ...quickSort(right)];
}
// console.log(quickSort([3, 6, 2, 4, 1]));
复制代码
20 防抖节流
题目描述:手写防抖节流
实现代码如下:
// 防抖
function debounce(fn, delay = 300) {
//默认300毫秒
let timer;
return function () {
const args = arguments;
if (timer) {
clearTimeout(timer);
}
timer = setTimeout(() => {
fn.apply(this, args); // 改变this指向为调用debounce所指的对象,且apply的第二个参数可以是数组也可以是类数组对象,所以没必要把arguments转为数组
}, delay);
};
}
window.addEventListener(
"scroll",
debounce(() => {
console.log(111);
}, 1000)
);
// 节流
// 设置一个标志
// throttle.js
// 使用时间戳
window.addEventListener(
"scroll",
throttle(() => {
console.log(111);
}, 1000)
);
复制代码
21 写版本号排序的方法
题目描述:有一组版本号如下['0.1.1', '2.3.3', '0.302.1', '4.2', '4.3.5', '4.3.4.5']。现在需要对其进行排序,排序的结果为 ['4.3.5','4.3.4.5','2.3.3','0.302.1','0.1.1']
实现代码如下:
arr.sort((a, b) => {
let i = 0;
const arr1 = a.split(".");
const arr2 = b.split(".");
while (true) {
const s1 = arr1[i];
const s2 = arr2[i];
i++;
if (s1 === undefined || s2 === undefined) {
return arr2.length - arr1.length;
}
if (s1 === s2) continue;
return s2 - s1;
}
});
console.log(arr);
复制代码
// 节流
function throttle(func, delay) {
let lastCallTime = 0;
return function (...args) {
const now = new Date().getTime();
if (now - lastCallTime >= delay)
{
func.apply(this, args);
lastCallTime = now;
}
};
}
实现如何取消 promise
Promise.race()方法可以用来竞争 Promise 可以借助这个特性 自己包装一个 空的 Promise 与要发起的 Promise 来实现
function wrap(pro) {
let obj = {};
// 构造一个新的promise用来竞争
let p1 = new Promise((resolve, reject) => {
obj.resolve = resolve;
obj.reject = reject;
});
obj.promise = Promise.race([p1, pro]);
return obj;
}
let testPro = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(123);
}, 1000);
});
let wrapPro = wrap(testPro);
wrapPro.promise.then((res) => {
console.log(res);
});
wrapPro.resolve("被拦截了");
复制代码
promise.all的原理
Promise.all
接受一个可迭代对象(通常是一个数组)作为参数,其中每个元素都是一个Promise
实例。- 它会创建一个新的
Promise
对象来跟踪所有传入的Promise
的状态。 - 然后遍历传入的
Promise
数组。 - 对于每个
Promise
,它会等待其完成(要么成功完成,要么失败)。 - 成功完成时,将结果保存起来;失败时,立即拒绝新创建的跟踪
Promise
。 - 只有当所有的
Promise
都成功完成时,新创建的跟踪Promise
才会成功完成,并将所有成功的结果以数组的形式返回。 - 如果其中任何一个
Promise
失败,新创建的跟踪Promise
就会立即失败,并返回第一个失败的Promise
的错误原因。
33 实现一个对象的 flatten 方法
题目描述:
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
// }
复制代码
实现代码如下:
function isObject(val) {
return typeof val === "object" && val !== null;
}
function flatten(obj) {
if (!isObject(obj)) {
return;
}
let res = {};
const dfs = (cur, prefix) => {
if (isObject(cur)) {
if (Array.isArray(cur)) {
cur.forEach((item, index) => {
dfs(item, `${prefix}[${index}]`);
});
} else {
for (let k in cur) {
dfs(cur[k], `${prefix}${prefix ? "." : ""}${k}`);
}
}
} else {
res[prefix] = cur;
}
};
dfs(obj, "");
return res;
}
flatten();
复制代码
React实现点击按钮数字+1
import React, { useState } from 'react';
const Test = function () {
const [count, setCount] = useState(0);
const handleClick = () => {
setCount(count + 1)
}
return <div onClick={handleClick}>click me {count}</div>;
};
实现每间隔1s打印当前时间
import React, { useState, useRef, useEffect } from 'react';
const Test = () => {
const [time, setTime] = useState(new Date());
// ref解决闭包陷阱问题,这样能保证ref一直是一个引用值
let timer = useRef();
useEffect(() => {
timer.current = setInterval(() => {
setTime(new Date());
}, 1000);
return () => clearInterval(timer.current);
}, [])
return (
<div>{time.toLocaleString()}</div>
)
}
ts实现两个数相加的函数,两个参数的类型必须同时为字符串或者数字
type Combinable = string | number;
function add<T extends Combinable>(a: T, b: T): Combinable {
if (typeof a === 'string') {
return a + b;
} else {
return (a as number) + (b as number);
}
}
let a: Combinable = 123;
let b: Combinable = 123;
add(a, b);
要求输出数组,数组里面的每一项是由随机数生成
const arr = [];
for(let i=0;i<100;i++){
const arrNum=parseInt(Math.random()*100)+1;
const flag=true;
for(let j=0;j<=arr.length;j++){
if(arrNum==arr[j]) {
flag = false;
break;
}
}
if(flag){
arr.push(arrNum);
}else{
i--;
}
}
一个数组里面有若干个对象,要求去除重复的对象
遍历数组然后使用JSON.stringify()将每一项转为字符串,然后对比字符串是否相等来取除
function removeDuplicateObjects(arr) {
const uniqueObjects = [];
const seen = new Set();
for (const obj of arr) {
const objString = JSON.stringify(obj);
if (!seen.has(objString)) {
uniqueObjects.push(obj);
seen.add(objString);
}
}
return uniqueObjects;
}
实现斐波那契树列
前端面试官
斐波那契数列(Fibonacci sequence),又称黄金分割数列,指的是这样一个数列:0、1、1、2、3、5、8、13、21、34、…… ,在数学上,斐波那契数列以如下递推的方法定义:,, (,)
以下是使用 JavaScript 递归实现斐波那契数列的代码:
JAVASCRIPTCopy
function fibonacci(n) {
if (n <= 0) {
return '输入应为正整数';
} else if (n === 1) {
return 0;
} else if (n === 2) {
return 1;
} else {
return fibonacci(n - 1) + fibonacci(n - 2);
}
}
for(let i = 1; i <= 10; i++) {
console.log(fibonacci(i));
}
需要注意的是,递归实现斐波那契数列在数值较大时可能会出现性能问题,因为会有大量的重复计算。
20. 实现简单路由
⼀个简单的路由系统可以通过JavaScript对象和事件监听器来实现。下⾯是⼀个简单的基
于浏览器的路由⽰例,使⽤URL的hash部分来模拟路由变化,并触发相应的事件或函
数。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-sc
ale=1.0">
<title>Simple Router</title>
<script>
const routes = {
'/': function() {
document.getElementById('content').textContent = 'Home Page';
},
'/about': function() {
document.getElementById('content').textContent = 'About Page';
},
'/contact': function() {
document.getElementById('content').textContent = 'Contact Page';
}
};
function initRoute() {
const hash = window.location.hash.substr(1);
if (routes[hash]) {
routes[hash]();
} else {
routes['/']();
}
}
window.addEventListener('hashchange', function() {
initRoute();
});
window.onload = initRoute;
</script>
</head>
<body>
<nav>
<a href="#/">Home</a> |
<a href="#/about">About</a> |
<a href="#/contact">Contact</a>
</nav>
16. ⽤Promise实现图⽚的异步加载
在JavaScript中,我们可以使⽤Promise来实现图⽚的异步加载。Promise是⼀种表⽰异
步操作可能完成(或失败)的对象。
以下是⼀个简单的例⼦,说明如何使⽤Promise来加载图⽚:
function loadImage(url) {
return new Promise((resolve, reject) => {
let img = new Image()
img.onload = () => {
resolve(img)
}
img.onerror = (error) => {
reject(error)
}
img.src = url
})
}
// 使⽤⽅式
loadImage('example.com/path/to/ima…')
红灯 3s 亮⼀次,绿灯 1s 亮⼀次,⻩灯 2s 亮⼀次;如何让三个灯不断交替重复亮灯?
分别定义三个间隔定时器,然后分别是3s 2s 1s后让他透明度设置为0
let redLight = document.getElementById('red');
let greenLight = document.getElementById('green');
let yellowLight = document.getElementById('yellow');
let redInterval = setInterval(function() {
redLight.style.opacity = 1;
setTimeout(function() {
redLight.style.opacity = 0;
}, 3000);
}, 9000);
let greenInterval = setInterval(function() {
setTimeout(function() {
greenLight.style.opacity = 1;
setTimeout(function() {
greenLight.style.opacity = 0;
}, 1000);
}, 3000);
}, 9000);
let yellowInterval = setInterval(function() {
setTimeout(function() {
setTimeout(function() {2024前端⾼频—⼿写代码篇
26
yellowLight.style.opacity = 1;
setTimeout(function() {
yellowLight.style.opacity = 0;
}, 2000);
}, 4000);
}, 0);
}, 9000);
实现 add(1)(2)(3)
在JavaScript中,你可以通过创建⼀个返回函数的函数来实现这种链式调⽤的模式。这种
模式通常被称为柯⾥化(Currying)的⼀种形式
function add() {
let sum = 0
function innerAdd(num) {
sum += num
return innerAdd
}
innerAdd.getResult = function() {
return sum
}
return innerAdd
}
let result = add(1)(2)(3)
console.log(result.getResult())
在这个例⼦中, add 函数返回了⼀个名为 innerAdd 的内部函数。 innerAdd 函数接受⼀个 数字参数,将其加到 sum 变量上,然后返回⾃⼰。因此,你可以连续调⽤ add(1)(2)(3) ,每次调⽤都会将新的数字添加到 sum 中。
需要注意的是,由于 innerAdd 是⼀个函数,如果你直接打印 add(1)(2)(3) ,它将输出函
数本⾝⽽不是结果。为了解决这个问题,我们覆盖了 innerAdd 的 getResult ⽅法,使其返
回 sum 的值。这样,当你尝试打印 add(1)(2)(3) 时,实际上会调⽤
innerAdd.getResult() ,从⽽得到结果。
实现倒计时组件(美团)
要求组件每隔1s刷新一次屏幕,props为starttime endtime 还有结束的回调
import React, { useState, useEffect } from 'react';
function Countdown({ startTime, endTime, onEnd }) {
const [countdown, setCountdown] = useState(startTime);
useEffect(() => {
const interval = setInterval(() => {
if (countdown > endTime) {
setCountdown(countdown - 1);
} else {
clearInterval(interval);
onEnd();
}
}, 1000);
return () => clearInterval(interval);
}, [countdown, endTime, onEnd]);
return <div>{countdown}</div>;
}
export default Countdown;
实现一个用于组件间事件通信的class(美团)
方法要有事件监听 事件发出 事件取消
class EventEmitter {
constructor() {
this.events = {};
}
on(eventName, callback) {
if (!this.events[eventName]) {
this.events[eventName] = [];
}
this.events[eventName].push(callback);
}
emit(eventName, ...args) {
if (this.events[eventName]) {
this.events[eventName].forEach(callback => callback(...args));
}
}
off(eventName, callback) {
if (this.events[eventName]) {
this.events[eventName] = this.events[eventName].filter(cb => cb !== callback);
}
}
}
// 使用示例
const emitter = new EventEmitter();
const callback = (data) => {
console.log(`Received data: ${data}`);
};
emitter.on('customEvent', callback);
emitter.emit('customEvent', 'Hello from emitter!');
emitter.off('customEvent', callback);
对象过滤(京东)
把一个多层对象中空数组 空对象 空字符串去除掉,
const obj={
a: 1,b:'2',c:[],// xd:{
aa: 1,
bb:'2',
Cc:1l x
dd: {},il x
ee: 0,
e:{},11 x
// 定义一个过滤函数
function filterEmpty(obj) {
const filtered = {};
Object.keys(obj).forEach(key => {
const value = obj[key];
if (Array.isArray(value)) {
if (value.length > 0) {
filtered[key] = value;
// 如果是数组且非空,则保留
}
} else if (typeof value === 'object' && value !== null) {
const nestedFiltered = filterEmpty(value);
if (Object.keys(nestedFiltered).length > 0) {
filtered[key] = nestedFiltered;
// 如果是对象且非空,则保留
}
} else if (value !== undefined && value !== null && value !== '') {
filtered[key] = value;
// 其他情况非空则保留
}
});
return filtered;
}