1.?写React/Vue项目时为什么要在列表组件中写key,其作用是什么?
参考文章:写React/Vue项目时为什么要在列表组件中写key,其作用是什么? - 栗子君* - 博客园
key的作用是什么?
key是给每一个vnode的唯一id,可以依赖key,更准确,更快地拿到oldVnode中对应的vnode节点
(1)?更准确
因为带key就不是就地复用了,在someNode函数 a.key === b.key 对比中可以避免就地复用的情况,所以会更加准确。
(2)更快
利用key的唯一性生成map对象来获取对应节点,比遍历方式更快。
2. [‘1‘,‘2‘,‘3‘].map(parseInt)结果
参考文章:[‘1‘,‘2‘,‘3‘].map(parseInt)结果讲解_杏子-CSDN博客

map将数组的每个元素传递给指定的函数处理,并返回处理后的数组,所以 ['1','2','3'].map(parseInt) 就是将字符串1,2,3作为元素;0,1,2作为下标分别调用 parseInt 函数。即分别求出 parseInt('1',0), parseInt('2',1), parseInt('3',2)的结果。


3.什么是防抖和节流,有什么区别?如何实现?
参考文章:什么是防抖和节流?有什么区别?如何实现?_AC总有一天的博客-CSDN博客_什么是防抖和节流?有什么区别?如何实现
?1.防抖?
触发高频事件后n秒内函数只会执行一次,如果n秒内高频事件再次被触发,则重新计算时间
防抖就是在函数需要频繁触发情况时,只有足够空闲的时间,才执行一次(公交司机等人上齐后才出站)
?2.节流
高频事件触发,但在n秒内只会执行一次,所以节流会稀释函数的执行频率
函数节流就是预定一个函数只有在大于等于执行周期是才执行,周期内调用不执行(水达到一定程度才滴落)
每次触发事件时都判断当前是否有等待执行的延时函数
4.?介绍一下Set、WeakSet、Map及WeakMap的区别?
参考文章:(第二题) 介绍一下Set、WeakSet、Map及WeakMap的区别_小柒的前端之旅-CSDN博客
Set?对象允许你存储任何类型的唯一值,无论是原始值或者是对象引用。
Set对象是值的集合,你可以按照插入的顺序迭代它的元素。 Set中的元素只会出现一次,即 Set 中的元素是唯一的。
WeakSet?对象允许你将弱保持对象存储在一个集合中。
WeakSet?对象是一些对象值的集合, 并且其中的每个对象值都只能出现一次。在WeakSet的集合中是唯一的
它和?Set?对象的区别有两点:
- 与
Set相比,WeakSet?只能是对象的集合,而不能是任何类型的任意值。 WeakSet持弱引用:集合中对象的引用为弱引用。 如果没有其他的对WeakSet中对象的引用,那么这些对象会被当成垃圾回收掉。 这也意味着WeakSet中没有存储当前对象的列表。 正因为这样,WeakSet?是不可枚举的。
Map
Map?对象保存键值对,并且能够记住键的原始插入顺序。任何值(对象或者原始值) 都可以作为一个键或一个值。
WeakMap
WeakMap?对象是一组键/值对的集合,其中的键是弱引用的。其键必须是对象,而值可以是任意的。
WeakMap 的 key 只能是?Object?类型。?原始数据类型?是不能作为 key 的(比如?Symbol)。
键名是弱引用,键值可以是任意的,键名所指向的对象可以被垃圾回收,此时键名是无效的
不能遍历,方法有get、set、has、delete
5. 介绍一下深度优先遍历和广度优先遍历,如何实现?
参考文章:介绍下深度优先遍历和广度优先遍历,如何实现? - 白不了的黑发 - 博客园
其实简单来说 深度优先就是自上而下的遍历搜索 广度优先则是逐层遍历, 如下图所示、
1.深度优先

??2.广度优先

两者的区别
对于算法来说 无非就是时间换空间 空间换时间
- 深度优先不需要记住所有的节点, 所以占用空间小, 而广度优先需要先记录所有的节点占用空间大
- 深度优先有回溯的操作(没有路走了需要回头)所以相对而言时间会长一点
深度优先采用的是堆栈的形式, 即先进后出 广度优先则采用的是队列的形式, 即先进先出
6. 请分别用深度优先和广度优先实现一个拷贝函数?
// 如果是对象/数组,返回一个空的对象/数组,
// 都不是的话直接返回原对象
// 判断返回的对象和原有对象是否相同就可以知道是否需要继续深拷贝
// 处理其他的数据类型的话就在这里加判断
function getEmpty(o) {
if (Object.prototype.toString.call(o) === "[object Object]") {
return {};
}
if (Object.prototype.toString.call(o) === "[object Array]") {
return [];
}
return o;
}
function deepCopyBFS(origin) {
let queue = [];
let map = new Map(); // 记录出现过的对象,用于处理环
let target = getEmpty(origin);
if (target !== origin) {
queue.push([origin, target]);
map.set(origin, target);
}
while (queue.length) {
let [ori, tar] = queue.shift();
for (let key in ori) {
// 处理环状
if (map.get(ori[key])) {
tar[key] = map.get(ori[key]);
continue;
}
tar[key] = getEmpty(ori[key]);
if (tar[key] !== ori[key]) {
queue.push([ori[key], tar[key]]);
map.set(ori[key], tar[key]);
}
}
}
return target;
}
function deepCopyDFS(origin) {
let stack = [];
let map = new Map(); // 记录出现过的对象,用于处理环
let target = getEmpty(origin);
if (target !== origin) {
stack.push([origin, target]);
map.set(origin, target);
}
while (stack.length) {
let [ori, tar] = stack.pop();
for (let key in ori) {
// 处理环状
if (map.get(ori[key])) {
tar[key] = map.get(ori[key]);
continue;
}
tar[key] = getEmpty(ori[key]);
if (tar[key] !== ori[key]) {
stack.push([ori[key], tar[key]]);
map.set(ori[key], tar[key]);
}
}
}
return target;
}
// test
[deepCopyBFS, deepCopyDFS].forEach((deepCopy) => {
console.log(deepCopy({ a: 1 }));
console.log(deepCopy([1, 2, { a: [3, 4] }]));
console.log(
deepCopy(function () {
return 1;
})
);
console.log(
deepCopy({
x: function () {
return "x";
},
val: 3,
arr: [1, { test: 1 }],
})
);
let circle = {};
circle.child = circle;
console.log(deepCopy(circle));
});
?7、ES5/ES6 的继承除了写法以外还有什么区别?
参考文章:[JavaScript][ES6]ES5/ES6 的继承除了写法以外还有什么区别?_DaftJayee的博客-CSDN博客??????
1.class 声明会提升,但不会初始化赋值。Foo 进入暂时性死区,类似于 let、const 声明变量。
2.class 声明内部会启用严格模式。?
3.class 的所有方法(包括静态方法和实例方法)都是不可枚举的。
4.class 的所有方法(包括静态方法和实例方法)都没有原型对象 prototype,所以也没有[[construct]],不能使用 new 来调用。
5.必须使用 new 调用 class。
6.class 内部无法重写类名。
8、SetTimeout、Promise、Async/Await 的区别
参考文章:https://blog.csdn.net/u013692823/article/details/105810280
1.SetTImeout ? ? 注意setTimeout是异步执行函数 , 当js主线程运行到此函数时,不会等待settimeout中的回调函数 ,会直接进行settimeout下面的语句(尽管setTimeout的延迟时间为0时) 当执行完当前事件循环的时候,settimeout中的回调会在下次(或者某一个)事件循环中被执行
2.Promise?
? ?Promise 本身是同步的立即执行函数,当在执行体中执行resolve()或者reject的时候,此时是异步操作 ?会先执行then/catch(异步执行)等,等主栈完成后,才会去执行resolve()/reject中的方法,
3.async/await ? ? async函数返回一个promise对象,当函数执行的时候,一旦遇到await就会先返回,等到触发的异步操作完成(await的函数),在执行函数体后面的语句,可以理解为,async让出了线程,跳出了async函数体,因此await函数后的语句相当于在then回调中执行.
? ? await的含义为等待,也就是 async 函数需要等待await后的函数执行完成并且有了返回结果(Promise对象)之后,才能继续执行下面的代码。await通过返回一个Promise对象来实现同步的效果。
9、Async/Await 如何通过同步的方式实现异步?
参考文章:[每日一题]面试官问:Async/Await 如何通过同步的方式实现异步?_saucxs的博客-CSDN博客??????
10、异步笔试题
async function async1() {
console.log("async1 start");
await async2();
console.log("async1 end");
}
async function async2() {
console.log("async2");
}
console.log("script start");
setTimeout(function() {
console.log("setTimeout");
}, 0);
async1();
new Promise(function(resolve) {
console.log("promise1");
resolve();
}).then(function() {
console.log("promise2");
});
console.log("script end");
//输出结果
// script start
// async1 start
// async2
// promise1
// script end
// async1 end
// promise2
// undefined
// setTimeout
11、算法手写题
已知如下数组:
var arr = [ [1, 2, 2], [3, 4, 5, 5], [6, 7, 8, 9, [11, 12, [12, 13, [14] ] ] ], 10];
编写一个程序将数组扁平化去并除其中重复部分数据,最终得到一个升序且不重复的数组
公司:携程
Array.from(new Set(arr.flat(Infinity))).sort((a, b) => {
return a - b;
});
拆解:
arr.flat(Infinity); // 1.所有元素放到同一数组
// ?[1, 2, 2, 3, 4, 5, 5, 6, 7, 8, 9, 11, 12, 12, 13, 14, 10]
Array.from(new Set(arr.flat(Infinity))).sort((a, b) => {
return a - b;
}); // 2.去重及排序
// [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]
|