https://juejin.im/post/5d23e750f265da1b855c7bbe
1 (滴滴、饿了么)写 React / Vue 项目时为什么要在列表组件中写 key,其作用是什么?
react和vue都使用diff算法来对比新旧虚拟节点,从而更新节点。
当新节点和旧节点从头到尾交叉对比没有结果时,会根据新节点的key对比旧节点数组中的key,如果没有找到就认为是新增了一个节点。
如果没有key,那么就会采用循环遍历的方式查找就的节点
在不带key的情况下,节点可以复用,省去dom操作的开销,但是只适用于无状态组件的渲染。
带上dom虽然会增加开销,但是可以保证组件的状态正确,并且用户感受不到差距。
2['1', '2', '3'].map(parseInt) what & why ?
返回1,NaN,NaN
因为parseInt有三个参数,第一个是被处理的元素,第二个是该元素的索引。。。
parseInt(“1”,0)按照十进制处理,返回 整数 1
parseInt(“2”,1)按照1进制处理,2 不符合一进制表示法,所以返回NaN
parseInt(“3”,2)按照2进制处理,3不符合二进制表示法,所以返回NaN
3(挖财)什么是防抖和节流?有什么区别?如何实现?
防抖:触发高频事件后n秒后调用一次函数,如果在n秒之内,高频事件再次被触发,则重新计算时间
常见的比如input框输入与文本同步需要防抖
解决思路: 创建一个标记用来存放定时器的返回值,每次事件触发前,都清除之前的延时调用
然后又创建一个新的 setTimeout, 这样就能保证输入字符后的 interval 间隔内如果还有字符输入的话,就不会执行 fn 函数,只有在你停止输入的时候才会执行fn函数
<input id="inpu" />
function debounce(e) {
let timeout = null; // 创建一个标记用来存放定时器的返回值
return function () {
clearTimeout(timeout); // 每当用户输入的时候把前一个 setTimeout clear 掉
timeout = setTimeout(() => { // 然后又创建一个新的 setTimeout, 这样就能保证输入字符后的 interval 间隔内如果还有字符输入的话,就不会执行 fn 函数
console.log("e.target.value",e.target.value)
}, 500);
};
}
document.getElementById("inpu").addEventListener("keyup",debounce) // 防抖
节流:高频事件触发,但在n秒内只会执行一次,稀释函数的执行频率
思路:每次触发事件时都判断当前是否有等待执行的延时函数
function throttle(fn) {
let canRun = true; // 通过闭包保存一个标记
return function () {
if (!canRun) return; // 在函数开头判断标记是否为true,不为true则return
canRun = false; // 立即设置为false
setTimeout(() => { // 将外部传入的函数的执行放在setTimeout中
fn.apply(this, arguments);
// 最后在setTimeout执行完毕后再把标记设置为true(关键)表示可以执行下一次循环了。当定时器没有执行的时候标记永远是false,在开头被return掉
canRun = true;
}, 500);
};
}
function sayHi(e) {
console.log(e.target.innerWidth, e.target.innerHeight);
}
window.addEventListener('resize', throttle(sayHi));
比如打王者荣耀,用户高频率点击输出时,机器只周期性输出,1秒输出一次
总结:防抖和节流 都是防止某一时间函数高频率触发,但是他们俩的原理却不同
防抖是每一时间段内只能触发一次,而节流是周期间歇性触发函数
应用场景:
防抖:1.search搜索联想,用户在不断输入值时,用防抖来节约请求资源。
节流:1.鼠标不断点击触发,mousedown(单位时间内只触发一次)。
2.监听滚动事件,比如是否滑到底部自动加载更多,用throttle来判断。
4 介绍下 Set、Map、WeakSet 和 WeakMap 的区别?
Set(集合)
成员唯一、无序且不重复
[value, value],键值与键名是一致的(或者说只有键值,没有键名)
可以遍历,方法有:add、delete、has
WeakSet
成员都是对象
成员都是弱引用,可以被垃圾回收机制回收,可以用来保存DOM节点,不容易造成内存泄漏
不能遍历,方法有add、delete、has
Map (字典)
本质上是键值对的集合,类似集合
可以遍历,方法很多可以跟各种数据格式转换
WeakMap
只接受对象作为键名(null除外),不接受其他类型的值作为键名
键名是弱引用,键值可以是任意的,键名所指向的对象可以被垃圾回收,此时键名是无效的
不能遍历,方法有get、set、has、delete
5 介绍下深度优先遍历和广度优先遍历,如何实现?
深度遍历就是自上而下的遍历,广度遍历就是逐层遍历
深度遍历有回溯的操作(没有路走了需要回头),
深度优先采用的是堆栈的形式, 即先进后出
广度优先则采用的是队列的形式, 即先进先出
const data = [
{
name: 'a',
children: [
{ name: 'b', children: [{ name: 'e' }] },
{ name: 'c', children: [{ name: 'f' }] },
{ name: 'd', children: [{ name: 'g' }] },
],
},
{
name: 'a2',
children: [
{ name: 'b2', children: [{ name: 'e2' }] },
{ name: 'c2', children: [{ name: 'f2' }] },
{ name: 'd2', children: [{ name: 'g2' }] },
],
}
]
深度遍历(递归调用)
function getName(data){
let result=[];
data.forEach(
item=>{
const maps=(data)=>{
result.push(data.name)
//对象里的数组如果存在就继续调用
data.children&&data.children.forEach(
p=>maps(p)
)
}
maps(item)
}
)
return result.join(",")
}
console.log(getName(data))
//a,a2,b,c,d,b2,c2,d2,e,f,g,e2,f2,g2
广度遍历(数组循环)
function getNames(data){
let result=[];
let queue=data ;
while(queue.length>0){
[...queue].forEach(
p=>{
queue.shift();
result.push(p.name)
p.children&&(queue.push(...p.children))
}
)
}
return result.join(",")
}
console.log(getNames(datad))
//a,a2,b,c,d,b2,c2,d2,e,f,g,e2,f2,g2
6 :请分别用深度优先思想和广度优先思想实现一个拷贝函数?
我不会做
7 ES5/ES6 的继承除了写法以外还有什么区别?
es6 :
class 声明会提升,但不会初始化赋值。Foo 进入暂时性死区,类似于 let、const 声明变量。
class 声明内部会启用严格模式。
class 的所有方法(包括静态方法和实例方法)都没有原型对象 prototype(只有proto),所以也没有[[construct]],不能使用 new 来调用。 class 内部无法重写类名。
8 setTimeout、Promise、Async/Await 的区别
宏观任务和微观任务

Promise本身是一个立即执行函数,如果带then或者catch,有一步操作功能
async 函数返回一个 Promise 对象,await配合async触发异步操作,控制函数的执行顺序。async可以单独使用,await不可以。
8 (头条、微医)Async/Await 如何通过同步的方式实现异步
function fun1() {
console.log(1);
console.log(2);
}
function fun2() {
console.log(3);
console.log(4);
}
// async 实现的是一个异步操作,await 等待一个异步方法执行完成。
// async内使用await等待async的执行完成,就形成了异步函数的同步实现。
async function fun3() {
await fun1();
await console.log(5);
await fun2();
}
fun3();
9 // 编写一个程序将数组扁平化去并除其中重复部分数据,最终得到一个升序且不重复的数组
var arr = [ [1, 2, 2], [3, 4, 5, 5], [6, 7, 8, 9, [11, 12, [12, 13, [14] ] ] ], 10];
写法一: console.log("整理后", [...new Set(arr.flat(Infinity))].sort((a, b) => a - b))
// 整理后 (14) [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]
写法二 : 数据扁平化写法不同,其他都一样
1 let arrs = JSON.stringify(arr );
2 扩展运算符:
while (ary.some(Array.isArray)) {
ary = [].concat(...ary);
}
3 递归调用:
function maps(arr){
arr.map(
p =>{
if(p.constructor === Array){
return maps(p)
}else{
data.push(p)
}
}
)
}
10 Promise 构造函数是同步执行还是异步执行,那么 then 方法呢?
const promise = new Promise((resolve, reject) => {
console.log(1)
resolve()
console.log(2)
})
promise.then(() => {
console.log(3)
})
console.log(4)
//执行结果: 1243
Promise构造函数是同步执行的,then方法是异步执行的
扩展 :
console.log(1);
resolve(5);
console.log(2);
}).then(val => {
console.log(val);
});
promise.then(() => {
console.log(3);
});
console.log(4);
setTimeout(function() {
console.log(6);
});
// 124536
promise是微观任务,setTimeout是宏观任务,先执行微观任务,在执行宏观任务;微观任务里,先执行同步再执行异步 所以结果是 124536
11 如何实现一个 new
// 实现一个new
var Dog = function(name) {
this.name = name
}
Dog.prototype.bark = function() {
console.log('wangwang')
}
Dog.prototype.sayName = function() {
console.log('my name is ' + this.name)
}
let sanmao = new Dog('三毛')
sanmao.sayName()
sanmao.bark()
//打印: wangwang
// my name is 三毛
new的作用:1 创建一个新对象
2 新对象(实例)的proto指向构造函数的prototype ,实现继承
3 执行构造函数,传递参数,改变this指向 Dog.call(obj, ...args)
4 最后把new出来的对象赋值给 变量sanmao
网友评论