(一)、被裁之后自己的总结的技术点面经(八股文) 难度指数 2~3颗星
(二)、作者面试中面到的读代码题 难度指数 2~3颗星
(三)、JS异步原理与进阶-Event Loop事件循环 难度指数 1~3颗星
(四)、从JS基础知识到Web API(ajax-跨域-存储) 难度指数 1~3颗星
(五)、关于http, 强制缓存-协商缓存看这个 难度指数 2~4颗星
(六)、手写深度比较,模拟loadsh isEqual 难度指数 1~3颗星
(七)、Typescript基础篇 难度指数 1颗星
(八)、TS核心语法+各种实战应用(上) 难度指数 3颗星
1.完成一个字符串拼接的方式
- 不能直接使用 ES6 的 String.prototype.repeat
- 将 str 重复 count 次进行拼接, 然后返回拼接得到的字符串
repeat('abc', 3) => 'abcabcabc'
repeat('abc', 0) => ''
repeat('', 10) => ''
function repeat(str, count) {
if (!count || !str) return ''
let result = '';
for (let i = 1; i <= count; i++) {
result = result.concat(str) // 简写 result += str
}
return result
}
console.log(repeat('abc', 3), '------');
2.对如下的树型数据, 进行数据结构上的扁平化处理
- 根据下面的输入样例, 和输出样例, 完成 flattenTree()方法的实现
- 输入是一个树状的数据结构 : TreeNode {val: string, id: number, children: TreeNode[]} (可参考输入样例)
- 输出是一个扁平的列表 : Array<{val: string, id: number, parentId: number}>(可参考输出样例)
{
val: "A",
id: 1,
children: [
{
val: "B",
id: 2,
children: [
{
val: "D",
id: 4,
children: [],
},
{
val: "E",
id: 5,
children: [],
},
],
},
{
val: "C",
id: 3,
children: [
{
val: "F",
id: 6,
children: [],
},
],
},
],
};
[
{ val: 'A', id: 1, parentId: null },
{ val: 'B', id: 2, parentId: 1 },
{ val: 'D', id: 4, parentId: 2 },
{ val: 'E', id: 5, parentId: 2 },
{ val: 'C', id: 3, parentId: 1 },
{ val: 'F', id: 6, parentId: 3 }
]
/**
* 树的扁平化, 可参考下面的输入样例和输出样例
*/
function flattenTree(node, parentId) {
let result = [];
result.push({ val: node.val, id: node.id, parentId: parentId ? parentId : null});
if (node.children.length > 0) {
for (let child of node.children) {
result = result.concat(flattenTree(child, node.id));
// result.push(...flattenTree(child, node.id));
}
}
return result;
}
const flattenedTree = flattenTree(tree);
console.log(flattenedTree, '---test---');
3. 有效括号 isValid
示例 1:
输入:s = "()" 输出:true
示例 2:
输入:s = "(()())" 输出:true
示例 3:
输入: s = '((())())' 输出: true
示例 4:
输入:s = ")(" 输出:false
示例 5:
输入:s = "(" 输出:false
示例 6:
输入:s = ")" 输出:false
示例 7:
输入:s = "(((" 输出:false
示例 8:
输入:s = "())" 输出:false
/**
* @return {boolean}
*/
var isValid = function(s) {
// 判断字符串的长度是否等于奇数,等于则停止执行
if(s.length % 2 === 1) return false
// 1.新建一个栈
const stack = [];
// 2.通过for循环来扫描字符串的长度
for(let i = 0; i < s.length; i += 1){
// 3.声明变量char
const char = s[i]; // i 作为index
// 4. 如果遇到左括号就推入栈中
if(char === '(' || char === '{' || char === '['){
stack.push(char);
} else {
// 5. 如果遇到右括号就要判断是否和栈顶元素匹配
// 就是获取数组最后一项
const top = stack[stack.length - 1]
if(
// 判断它和栈顶元素类型是否匹配
(top === '(' && char === ')') ||
(top === '{' && char === '}') ||
(top === '[' && char === ']')
){
// 6. 类型相同则在栈中消除掉
stack.pop();
} else {
return false
}
}
}
// 判断栈是否为空
return stack.length === 0;
};
// 时间复杂度为O(n) s.length 的长度
// 空间复杂度也是O(n) stack可能会占用n个内存单元s.length
4.实现红绿灯交通信号灯控制, 完成题目 2.4.1, 和 题目 2.4.2 (附加题)
- 通过HTML、CSS和JavaScript实现一个简单的红绿灯界面,并通过实现红灯、绿灯和黄灯的循环切换。
- 每个灯持续亮起一段时间后自动熄灭,并切换到下一个灯, 并且一直循环下去
- 提示 : 逻辑部分使用 Promise 实现, 也可以用 async/await 实现
4-1 附加题
- 第一个实现的小题, 你需要根据上面的描述
- 红灯亮起持续 1 秒, 绿灯亮起持续 2 秒, 黄灯亮起 1.5 秒
function red() {
// 你的代码实现
}
function green() {
// 你的代码实现
}
function yellow() {
// 你的代码实现
}
// ** 以下是 start 的调用 ( 你不需要动 ) **
function start() {
red()
.then(() => green())
.then(() => yellow())
.then(() => start())
}
start()
<head>
<style>
.light {
width: 100px;
height: 100px;
border-radius: 50%;
margin-bottom: 10px;
}
.red {
background-color: red;
}
.yellow {
background-color: yellow;
}
.green {
background-color: green;
}
// 手动添加active样式
.red-active {
box-shadow: 0 0 20px red;
}
.yellow-active {
box-shadow: 0 0 20px yellow;
}
.green-active {
box-shadow: 0 0 20px green;
}
</style>
</head>
<body>
<div id="traffic-light">
<div class="light red"></div>
<div class="light yellow"></div>
<div class="light green"></div>
</div>
<script>
// 题解
function delay (duration) {
return new Promise(resolve => setTimeout(resolve, duration));
}
const redLight = document.querySelector('.red');
const yellowLight = document.querySelector('.yellow');
const greenLight = document.querySelector('.green');
function red () {
// 请写出你的代码 已完成
return delay(1000)
.then(() => {
redLight.classList.add('red-active');
yellowLight.classList.remove('yellow-active');
greenLight.classList.remove('green-active');
return delay(1000);
});
}
function green () {
// 请写出你的代码 已完成
return delay(1000)
.then(() => {
redLight.classList.remove('red-active');
yellowLight.classList.remove('yellow-active');
greenLight.classList.add('green-active');
return delay(2000);
});
}
function yellow () {
// 请写出你的代码 已完成
return delay(2000)
.then(() => {
redLight.classList.remove('red-active');
yellowLight.classList.add('yellow-active');
greenLight.classList.remove('green-active');
return delay(1500);
});
}
// 此处不用动
function start () {
red()
.then(() => green())
.then(() => yellow())
.then(() => start())
}
start()
</script>
</body>
</html>
4-2 (附加题)
- 第二个实现的小题, 题目的背景一样, 但你要尝试提供一个实现让整体的过程变成一个可配置的方式
// 如果你喜欢, 也可以把 start 改成 async function
function start() {
// 你的代码实现
}
// 红灯 (持续 1 秒) => 绿灯 (持续 0.8 秒) =>
黄灯 (持续 1 秒) => 绿灯 (持续 0.5 秒) => ... (再回到红灯)
// ** 以下是 start 的调用 ( 你不需要动 ) **
start([
{type: 'red', interval: 1000},
{type: 'green', interval: 800},
{type: 'yellow', interval: 1000},
{type: 'green', interval: 500},
]);
<script>
function delay (duration) {
return new Promise(resolve => setTimeout(resolve, duration));
}
async function changeLight (light, duration) {
light.classList.add(`${light.classList[1]}-active`);
await delay(duration);
light.classList.remove(`${light.classList[1]}-active`);
}
async function start (configurations) {
for (const config of configurations) {
const { type, interval } = config;
const light = document.querySelector(`.${type}`);
await changeLight(light, interval);
}
// 等待所有状态变化结束后重新执行 start,实现交通灯状态的循环
await start(configurations);
}
const trafficLightConfigurations = [
{ type: 'red', interval: 1000 },
{ type: 'green', interval: 800 },
{ type: 'yellow', interval: 1000 },
{ type: 'green', interval: 500 }
];
start(trafficLightConfigurations);
</script>
5.多维数组转为一维数组(扁平化)
const arr1 = [
{
id: 1,
val: 'a',
children: [
{
id: 2,
val: 'b',
children: [
{
id: 3,
val: 'c'
}
]
},
{
id: 4,
val: 'd'
}
]
},
{
id: 5,
val: 'e'
}
]
[
{
id: 1,
val: 'a',
parentId: null,
},
{
id: 2,
val: 'b',
parentId: 1,
},
{
id: 3,
val: 'c',
parentId: 2,
},
{
id: 4,
val: 'd',
parentId: 1,
},
{
id: 5,
val: 'e',
parentId: null,
}
]
function tree (arr, parentId = null) {
const result = [];
arr.map(item => {
result.push({ id: item.id, val: item.val, parentId: item.id ? item.id : parentId })
if (Array.isArray(item.children) && item.children.length > 0) {
result.push(...tree(item.children, item.id))
}
})
return result
}
6.版本号排序
/**
* 排序
* ['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', '4.2',' '2.3.3, '0.302.1', '0.1.1'] // 输出
*/
实现
function compareVersions(versionA, versionB) {
const partsA = versionA.split('.');
const partsB = versionB.split('.');
for (let i = 0; i < Math.max(partsA.length, partsB.length); i++) {
const partA = parseInt(partsA[i] || 0);
const partB = parseInt(partsB[i] || 0);
if (partA > partB) {
return -1;
} else if (partA < partB) {
return 1;
}
}
return 0;
}
function sortVersions(versions) {
return versions.sort(compareVersions);
}
const versions = ['0.1.1', '2.3.3', '0.302.1', '4.2', '4.3.5', '4.3.4.5'];
const sortedVersions = sortVersions(versions);
console.log(sortedVersions, '======'); // ['4.3.5', '4.3.4.5', '4.2',' '2.3.3, '0.302.1', '0.1.1']
7.将数组转为树
输入:
const arr = [
{ id: 1, name: '部门A', parentId: 0 },
{ id: 2, name: '部门B', parentId: 1 },
{ id: 3, name: '部门C', parentId: 1 },
{ id: 4, name: '部门D', parentId: 2 },
{ id: 5, name: '部门E', parentId: 2 },
{ id: 6, name: '部门F', parentId: 3 }
]
[
{
id: 1,
name: '部门A',
children: [
{
id: 2,
name: '部门B',
children: [
{
id: 4,
name: '部门D',
children: []
},
{
id: 5,
name: '部门E',
children: []
}
]
},
{
id: 3,
name: '部门C',
children: [
{
id: 6,
name: '部门F',
children
}
]
}
]
}
]
// 解题思路: 遍历数组方式 效果较慢
function convert(arr) {
const map = {};
const tree = [];
// 将每个节点以 id 为键存储到 map 对象中
arr.forEach(item=> {
map[item.id] = { ...item, children: [] };
})
// 遍历数组,将每个节点添加到其父节点的 children 数组中
arr.forEach(item=> {
if (item.parentId !== 0) {
map[item.parentId].children.push(map[item.id])
} else {
tree.push(map[item.id])
}
})
return tree
}
// 解题思路: 使用一个Map来维护关系,便于查找 广度优先遍历
function convert (arr) {
const nodeParent = new Map();
const result = [];
// 构建映射关系
arr.forEach(node => {
node.children = [];
nodeParent.set(node.id, node)
})
// 构建树形结构
arr.forEach(node => {
const parentId = node.parentId;
const parentNode = nodeParent.get(parentId);
if (parentNode) {
parentNode.children.push(node);
} else {
result.push(node);
}
})
return result
}
8.实现返回出现最多的单词
// 题解1
const str = 'The quick brown fox jumps over the lazy dog. The dog slept over the verandah.';
function fn(str) {
let str1 = str.toLowerCase().replace('.', ' ').split(' ');
const wordCount = {};
for (let i = 0; i < result.length; i++) {
const word = result[i];
// 如果单词已经在 wordCount 中,则将其出现次数加 1
if (wordCount[word]) {
wordCount[word] = wordCount[word] + 1;
} else {
// 如果单词不在 wordCount 中,则将其添加并设置出现次数为 1
wordCount[word] = 1;
}
}
let maxWord = '', maxCount = 0;
for (const word in wordCount) {
// 检查当前单词出现的次数是否大于最大次数
if (wordCount[word] > maxCount) {
// 如果是,则更新最大次数和最大单词
maxWord = word;
maxCount = wordCount[word];
}
}
return maxWord;
}
console.log(fn(str));
// 题解2
const test = 'The quick brown fox jumps over the lazy dog. The dog slept over the verandah.';
function fn(str) {
const words = str.toLowerCase().split(/[ .]/);
const wordCount = {};
words.forEach((word) => {
wordCount[word] = (wordCount[word] || 0) + 1;
});
let maxWord = '';
let maxCount = 0;
for (const word in wordCount) {
if (wordCount[word] > maxCount) {
maxWord = word;
maxCount = wordCount[word];
}
}
return maxWord;
}
console.log(fn(test));
9.千分位分割
// 题解1
const str = '10000000000000'
const s = str.replace(/\B(?=(\d{3})+(?!\d))/g, ",");
console.log(s);
// 题解2
const test = '1000000000000'
function spl (A) {
const numArr = A.split('')
const len = numArr.length
for (let i = len - 3; i > 0; i-=3) {
numArr.splice(i, 0, ',') // 参数1: 起始位置 参数2:
}
console.log(numArr);
return numArr.join('')
}
console.log(spl(test));
10.js实现二分查找
function binarySearch(arr, target) {
let left = 0;
let right = arr.length - 1;
while (left <= right) {
const mid = Math.floor((left + right) / 2);
const midVal = arr[mid];
if (midVal === target) {
return mid; // 找到目标元素,返回索引
} else if (midVal < target) {
left = mid + 1; // 目标元素在右半部分,缩小左边界
} else {
right = mid - 1; // 目标元素在左半部分,缩小右边界
}
}
return -1; // 未找到目标元素,返回 -1
}
// 示例
const sortedArray = [1, 3, 5, 7, 9, 11, 13];
const targetElement = 7;
const index = binarySearch(sortedArray, targetElement);
if (index !== -1) {
console.log(`目标元素 ${targetElement} 在索引 ${index} 处找到。`);
} else {
console.log(`未找到目标元素 ${targetElement}。`);
}
11.封装防抖节流函数
// 防抖
function debounce (fn, delay) { //
let timeId;
if (timeId) {
clearInterval(timeId)
}
return function(params) {
timeId = setTimeout(() => {
fn.apply(this, params)
}, delay)
}
}
window.addEventListener('resize', debounce(()=> {
//
}, 300))
// 节流
function throttle (fn, delay) {
let timeId;
return function () {
if (timeId) return
timeId = setTimeout(() => {
fn.apply(this, arguments)
timeId = 0
}, delay)
}
}
window.addEventListener('scroll', throttle(()=> {
//
}, 300))
12.代码题-布局
1.实现页面顶部固定50px高度下部自适应
2.下部内容垂直及水平居中
a.内容第一行文字不折行且超出容器部分显示
b.第二行为向下的一个三角形
<style>
* {
padding: 0;
margin: 0;
}
.header {
width: 100%;
height: 50px;
background-color: red;
}
.content {
display: flex;
justify-content: center;
align-items: center;
height: calc(100vh - 50px);
width: 100vw;
background-color: aqua;
}
</style>
<div>
<header class="header">顶部</header>
<div class="content">
<p>
我是文字我是文字我是文字我是文字我是文字我是文字我是文字我是文字
我是文字我是文字我是文字我是文字我是文字我是文字我是文字
我是文字我是文字我是文字我是文字我是文字我是文字我是文字
我是文字我是文字我是文字我是文字我是文字我是文字我是文字我是文字我是文字
</p>
</div>
</div>
13.打点定时器
- 从数字start到end(包含start和end),每隔100毫秒console.log 一个数字,每次数字增幅为1
- 返回的对象中需要包含一个 cancel 方法,用于停止定时操作。
- 第一个数需要立即输出
function timer(start,end){
// TODO:你的代码
let currentNumber = start;
console.log(currentNumber);
const intervalId = setInterval(() => {
currentNumber++;
console.log(currentNumber);
if (currentNumber === end) {
clearInterval(intervalId);
}
}, 100);
// 返回包含 cancel 方法的对象
return {
cancel: () => clearInterval(intervalId),
};
}
const demo = timer(1, 10);
console.log(demo, '===')
// 停止定时操作
timer.cancel();
14.将绝对定位元素(已知ID)从位置(0,0)渐变移动到(100px,100px),动画结束后执行alert(1)
<div id="ele" style="position:'absolute'"></div>
function animate(){
//TODO: 你的代码
const element = document.getElementById('ele');
let currentX = 0;
let currentY = 0;
const targetX = 100;
const targetY = 100;
const duration = 1000; // 动画持续时间,单位毫秒
const startTime = performance.now(); // 记录动画开始时间
function updatePosition(timestamp) {
const elapsedTime = timestamp - startTime;
if (elapsedTime >= duration) {
// 动画结束后执行 alert(1)
alert(1);
return;
}
const progress = elapsedTime / duration;
currentX = progress * (targetX - 0) + 0;
currentY = progress * (targetY - 0) + 0;
element.style.left = currentX + 'px';
element.style.top = currentY + 'px';
// requestAnimationFrame 由浏览器提供的
requestAnimationFrame(updatePosition);
}
requestAnimationFrame(updatePosition);
}
animate()
15.实现自动补全
import React, { useState, useEffect } from 'react';
function Autocomplete() {
const [keyword, setKeyword] = useState('');
const [suggestions, setSuggestions] = useState([]);
useEffect(() => {
// 发送 AJAX 请求获取建议列表
if (keyword.trim() !== '') {
fetch(`https://api.com/suggest/${keyword}`)
.then((response) => response.json())
.then((data) => setSuggestions(data))
.catch((error) => console.error('Error fetching suggestions:', error));
} else {
setSuggestions([]); // 如果输入框为空,清空建议列表
}
}, [keyword]);
const handleInputChange = (event) => {
setKeyword(event.target.value);
};
const handleSuggestionClick = (suggestion) => {
setKeyword(suggestion); // 点击建议项时,更新输入框内容
setSuggestions([]); // 清空建议列表
};
return (
<div className="container">
<input
type="text"
value={keyword}
onChange={handleInputChange}
placeholder="输入关键词"
/>
<div className="suggest">
{suggestions.map((suggestion, index) => (
<div
key={index}
className="suggestion-item"
onClick={() => handleSuggestionClick(suggestion)}
>
{suggestion}
</div>
))}
</div>
</div>
);
}
export default Autocomplete;
16.重复字符
function findMostFrequentChar(str) {
const charCount = {};
for (let i = 0; i < str.length; i++) {
const char = str[i];
// 如果字符已经在 charCount 中,则将其出现次数加 1
if (charCount[char]) {
charCount[char] = charCount[char] + 1;
} else {
// 如果字符不在 charCount 中,则将其添加并设置出现次数为 1
charCount[char] = 1;
}
}
let maxChar = '', maxCount = 0;
for (const char in charCount) {
// 检查当前字符出现的次数是否大于最大次数
if (charCount[char] > maxCount) {
// 如果是,则更新最大次数和最大字符
maxChar = char;
maxCount = charCount[char];
}
}
return { char: maxChar, count: maxCount };
}
const str = 'abcccbbaaa';
const result = findMostFrequentChar(str);
console.log(`最多的字符是:${result.char},出现次数:${result.count}`);
17.数组转树
const arr = [
{
parentTagIndex: null,
serviceName: "",
tagIndex: 44,
tagName: "商业平台",
tagType: "SCM_C1",
},
{
parentTagIndex: null,
serviceName: "",
tagIndex: 36,
tagName: "物联网",
tagType: "SCM_C1",
},
{
parentTagIndex: 44,
serviceName: "",
tagIndex: 3578,
tagName: "农业平台",
tagType: "SCM_C2",
},
{
parentTagIndex: 36,
serviceName: "",
tagIndex: 104,
tagName: "图云业务",
tagType: "SCM_C2",
},
{
parentTagIndex: 3578,
serviceName: "",
tagIndex: 3579,
tagName: "装修平台",
tagType: "SCM_C3",
},
{
parentTagIndex: 3578,
serviceName: "",
tagIndex: 5099,
tagName: "审核平台",
tagType: "SCM_C3",
},
{
parentTagIndex: 104,
serviceName: "",
tagIndex: 6000,
tagName: "决策系统",
tagType: "SCM_C3",
},
];
{
"id": 44,
"label": "商业平台",
"children": [
{
"id": 3578,
"label": "农业平台",
"children": [
{
"id": 3579,
"label": "装修平台"
},
{
"id": 5099,
"label": "审核平台"
}
],
}
],
"id": 36,
...省略
},
1.数组中每个元素代表一个节点,每个节点的 tagIndex 属性是该节点的唯一标识符id
2.如果一个节点有子节点,那么该节点的 tagIndex 属性会与其子节点的 parentTagIndex 属性相匹配
表示父子关系
3.从数组中找到根节点(没有父节点的节点),它们的 parentTagIndex 属性为 null。
4.对于每个根节点,递归地查找其子节点(子节点的parentTagIndex与根节点的tagIndex相匹配),并将子节点添加为根节点的 children 属性的值。
5.使用递归,一直找,直到找到所有的子节点并组织成树形结构。
// 解题1
function arrayToTree(arr, parentTagIndex = null) {
const result = [];
// 解题1
arr.forEach(item => {
if (item.parentTagIndex === parentTagIndex) {
const children = arrayToTree(arr, item.tagIndex);
const node = { id: item.tagIndex, label: item.tagName };
if (Array.isArray(children) && children.length > 0) {
node.children = children;
}
result.push(node);
}
});
return result;
}
// 解题2
function arrayToTree(arr, parentTagIndex = null) {
const result = [];
for (const item of arr) {
if (item.parentTagIndex === parentTagIndex) {
const children = arrayToTree(arr, item.tagIndex);
const node = {
id: item.tagIndex,
label: item.tagName,
};
if (children.length > 0) {
node.children = children;
}
result.push(node);
}
}
return result;
}
18.实现心跳组件
import React, { useEffect } from 'react';
export const HeartBeat = () => {
useEffect(() => {
let timer = 0;
function fn () {
console.log('hello');
timer = setTimeout(fn,1000) // 心跳组件避免使用setInterval,有坑
}
timer = setTimeout(fn,1000)
return () => {
clearTimeout(timer)
}
})
return <div>心跳组件</div>
}
19.用 React hooks 写一个 input 组件
- 用 React hooks 写一个 input 组件
- 可以接受 disabled、placeholder 等属性
- value 与 onChange 事件绑定组件状态
// 子组件
import React, { useState } from 'react';
const InputComponent = ({ disabled, placeholder, value, onChange }) => {
const [inputValue, setInputValue] = useState(value);
const handleChange = (event) => {
const newValue = event.target.value;
setInputValue(newValue);
if (onChange) {
onChange(newValue);
}
};
return (
<input
type="text"
disabled={disabled}
placeholder={placeholder}
value={inputValue}
onChange={handleChange}
/>
);
};
export default InputComponent;
// 父组件
import React, { useState } from 'react';
import InputComponent from './input';
export const ParentComponent = () => {
const [inputValue, setInputValue] = useState('');
const handleInputChange = (newValue) => {
setInputValue(newValue);
};
return (
<div>
<InputComponent
disabled={false}
placeholder="Enter text here"
value={inputValue}
onChange={handleInputChange}
/>
<p>Value: {inputValue}</p>
</div>
);
};