js基础
1)对js的理解? 2)请说出以下代码输出的值? 3)把以下代码,改写成依次输出0-9 4)如何区分数组对象,普通对象,函数对象 5)面向对象、面向过程 6)面向对象的三大基本特性 7)XML和JSON的区别? 8)Web Worker 和webSocket? 9)Javascript垃圾回收方法? 10)new操作符具体干了什么呢? 11)js延迟加载的方式有哪些? 12)WEB应用从服务器主动推送Data到客户端有那些方式?
[
{id: 1, name: 'A', parentId: null},
{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: 7, name: 'G', parentId: 3},
{id: 8, name: 'H', parentId: 4},
]
在这个例子中,我们可以看到每个节点都有一个唯一的id,一个名称和一个parentId,parentId是指它的父节点的id,如果没有父节点,parentId为null。让你看看你们这帮小学生的思路和我的有何不同。
function buildTree(nodes) {
const tree = []
const map = {}
nodes.forEach(node => {
map[node.id] = node
node.children = []
})
nodes.forEach(node => {
if (node.parentId) {
map[node.parentId].children.push(node)
} else {
tree.push(node)
}
})
return tree
}
看到这里,你不禁感慨,这代码写得真好!这不是小学生可以想到的,只有我这样的神仙才能想到啊!(滑稽)
不过,哪有这么简单的事呢?咳咳,这个代码还可以再优化哦!比如,我们可以减少循环次数,使用哈希表来存储节点,甚至还能使用并发机制来提高代码性能!
当然,如果你在面试的时候讲出了这些高级操作,面试官肯定会惊为天人,毫不犹豫地把 offer 递给你了。当然,如果面试官是我这种幽默的人,听完你的回答,估计会冒出一句:“哇,好厉害啊!可以详细讲讲怎么优化吗?”
减少循环次数
在之前的代码中,我们使用了两个forEach循环来遍历节点数组。但是,这会导致多次循环遍历同一个数组,从而降低性能。为了减少循环次数,我们可以使用单个for循环来遍历节点数组。
以下是修改后的代码:
function buildTree(nodes) {
// 这里新增一个排序,避免节点顺序不对照成的问题
nodes.sort((a, b) => a.id - b.id)
const tree = []
const map = {}
for (let i = 0; i < nodes.length; i++) {
const node = nodes[i]
map[node.id] = node
node.children = []
const parentId = node.parentId
if (!parentId) {
tree.push(node)
} else {
if (!map[parentId]) {
map[parentId] = { id: parentId, children: [] }
}
map[parentId].children.push(node)
}
}
return tree
}
我们将两个forEach循环替换为单个for循环。我们还添加了一个条件语句,以检查节点是否有父节点。如果没有父节点,我们将其添加到树的根节点中,否则我们将其添加到其父节点的children数组中。
使用哈希表
在之前的代码中,我们使用一个映射来存储节点。但是,对于较大的数据集,使用哈希表可以更快地查找节点。因此,我们可以使用JavaScript中的对象来代替映射。
以下是修改后的代码:
function buildTree(nodes) {
const tree = {}
const map = {}
for (let i = 0; i < nodes.length; i++) {
const node = nodes[i]
map[node.id] = node
node.children = []
const parentId = node.parentId
if (!parentId) {
tree[node.id] = node
} else {
if (!map[parentId]) {
map[parentId] = { id: parentId, children: [] }
}
map[parentId].children.push(node)
}
}
return Object.values(tree)
}
我们将tree从数组改为对象,并使用node.id作为键。我们还将映射从Map对象改为普通对象。这样可以使代码更加简洁,并减少了一些额外的函数调用。
将数据进行排序
如果数据是按照id顺序排列的,可以使用二分查找来查找父节点,从而减少遍历的次数。因此,我们可以将节点数组按照id排序,以优化性能。
以下是修改后的代码:
function buildTree(nodes) {
const tree = {}
const map = {}
nodes.sort((a, b) => a.id - b.id)
for (let i = 0; i < nodes.length; i++) {
const node = nodes[i]
map[node.id] = node
node.children = []
const parentId = node.parentId
if (!parentId) {
tree[node.id] = node
} else {
const parentIndex = binarySearch(nodes, parentId)
if (parentIndex !== -1) {
const parentNode = nodes[parentIndex]
if (!parentNode.children) {
parentNode.children = []
}
parentNode.children.push(node)
} else {
if (!map[parentId]) {
map[parentId] = { id: parentId, children: [] }
}
map[parentId].children.push(node)
}
}
}
return Object.values(tree)
}
function binarySearch(arr, id) {
let start = 0
let end = arr.length - 1
while (start <= end) {
const mid = Math.floor((start + end) / 2)
const midId = arr[mid].id
if (midId === id) {
return mid
} else if (midId < id) {
start = mid + 1
} else {
end = mid - 1
}
}
return -1
}
我们将节点数组按照id排序,并使用二分查找来查找父节点的索引。如果能够找到父节点,我们将当前节点添加到父节点的children数组中。否则,我们将当前节点添加到一个临时的映射中,并在之后的遍历中再次查找其父节点。这个版本的代码可以在数据集较大时更快地查找父节点。
避免重复遍历
如果同一个父节点有多个子节点,我们在找到该父节点之后,可以缓存其索引,以避免重复遍历。我们可以在map对象中添加一个__index属性,以存储该节点在节点数组中的索引。
以下是修改后的代码:
function buildTree(nodes) {
const tree = {}
const map = {}
nodes.sort((a, b) => a.id - b.id)
for (let i = 0; i < nodes.length; i++) {
const node = nodes[i]
map[node.id] = node
node.children = []
const parentId = node.parentId
if (!parentId) {
tree[node.id] = node
} else {
let parentIndex = map[parentId]?.__index
if (parentIndex === undefined) {
parentIndex = binarySearch(nodes, parentId)
if (parentIndex !== -1) {
map[parentId] = nodes[parentIndex]
map[parentId].__index = parentIndex
} else {
if (!map[parentId]) {
map[parentId] = { id: parentId, children: [] }
}
}
}
if (parentIndex !== undefined) {
const parentNode = nodes[parentIndex]
if (!parentNode.children) {
parentNode.children = []
}
parentNode.children.push(node)
} else {
map[parentId].children.push(node)
}
}
}
return Object.values(tree)
}
function binarySearch(arr, id) {
let start = 0
let end = arr.length - 1
### 最后
面试一面会问很多基础问题,而这些基础问题基本上在网上搜索,面试题都会很多很多。最好把准备一下常见的面试问题,毕竟面试也相当与一次考试,所以找工作面试的准备千万别偷懒。面试就跟考试一样的,时间长了不复习,现场表现肯定不会太好。表现的不好面试官不可能说,我猜他没发挥好,我录用他吧。
**[开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】](https://docs.qq.com/doc/DSmRnRGxvUkxTREhO)**
**96道前端面试题:**
- 
**常用算法面试题:**
- 
**前端基础面试题:**
内容主要包括**HTML,CSS,JavaScript,浏览器,性能优化**
- 