您的位置:首页 > 娱乐 > 八卦 > 前端面试代码题

前端面试代码题

2024/10/6 18:26:39 来源:https://blog.csdn.net/qq_27638851/article/details/140324817  浏览:    关键词:前端面试代码题

本文总结面试过程中遇到的代码题。

1. 变量提升

2. 数组相关的方法

注意返回true值是保留不是过滤,别记反。没啥,就是gap半年在面不写会忘的。。。

arr.filter((item, index, current) => {return arr.indexOf(item) == index});。可以去重

filter本质还是建了一个新的数组,回调函数里返回true则保留。上面一行代码表示重复的只往里面放第一次。

双指针去重:

使用双指针去重排序好的数组,相等的话fast指针右移一位,不相等的话slow右移一位,将fast位置的值保存,fast右移一位比较下一次:

function doublePointer(nums) {if(nums.length < 2) {return nums;}let slow = 0;let fast = 1;let result = [nums[0]];while(fast < nums.length) {if (result[slow] == nums[fast]) {fast++;} else {slow++;result[slow] = nums[fast];fast++;}}return result;
}
const arr = [1,1,2,2,4,4];
console.log(doublePointer(arr));

输入[1,1,2,2,4,4]输出[1,2,4]。

如果数组是个对象:

const arr2 = [{id:1,time: 2},{id:1,time: 4},{id:2,time: 2},{id:2,time: 4},{id:3,time: 5},

{id:3,time: 2}];

 现在需要根据id去重,id相等的情况下,只保留time最大的对象,输出:

[ { id: 1, time: 4 }, { id: 2, time: 4 }, { id: 3, time: 5 } ]

function doublePointerByorder(nums) {if(nums.length < 2) {return nums;}let slow = 0;let fast = 1;let result = [nums[0]];while(fast < nums.length) {if (result[slow].id == nums[fast].id) {if(result[slow].time < nums[fast].time) {result[slow] = nums[fast];}fast++;} else {slow++;result[slow] = nums[fast];fast++;}}return result;
}

console.log(doublePointerByorder(arr2));即可。

当然这里可以使用空对象去记,每次取对象里id对应的那个time比较,选择是否覆盖。遍历完使用for in循环将对象转数组。不过双指针效率非常高,如果是数据量很大的话,用双指针会比较合适。

3. 引用相关

引用类型变量的等于号赋值,不会相互影响。

但是通过a.b的方式会影响,此时算浅复制,如下所示。

person1 = {name: 3};
members = person1;
members.name = 4;   // person1.name=4

下面看一个引用相关的经典案例,也是面试时第一次遇到时没有写出来的问题,将数组转成树形结构:

function arrayToTree(items) {const tree = {};const map = {};items.forEach((item, index) => {const { id, parentId } = item;if (!map[id]) map[id] = { children: [] };const node = { ...item, children: map[id].children};map[id] = node;if (parentId) {if (!map[parentId]) {map[parentId] = { children: [] };} map[parentId].children.push(node);} else {tree[id] = node;}});console.log(map);
}// 示例数组
const items = [{ id: 1, parentId: null, value: "A" },{ id: 2, parentId: 1, value: "B" },{ id: 3, parentId: 1, value: "C" },{ id: 4, parentId: 2, value: "D" },{ id: 5, parentId: 3, value: "E" }
];// 转换为树
const tree = arrayToTree(items);

可以直接复制看效果,就跟俄罗斯套娃一样,第二层加到第一层好理解,难点在于第三层加到第二层时,第一层的第二层下面也多了第三层的数据, 其实仔细想一想就能明白了,a["1"]下面有个a["1"].children["2"],他的值和a["2"]不仅相等而且引用都相同,且执行a["1"].children.push(a["2"]);。这样的好处时到a["3"]={...item, children:[]}时,执行a["2"].children.push(a["3"]),由于是操作里面的数据,所以有相同引用的地方(例如a=b,b.push也会影响a.push)也会push,所以你会看到a["1"].children["2"].children里面有个a["3"]。这样树结构就对啦。

还不理解可以试试下面的做法:

如上所示:b1看作是a1的一级节点,修改b1会影响a1。尽管上面是循环,但是每个作用域里的引用相同的。

3. 宏任务微任务

2 4 5同步代码,3微任务,1宏任务。唯一的坑就是4会打印,return 3就不会执行 

4. 比较运算符

隐式转换比较时,数组调用了toString方法。

5. 防抖节流手写

自己要写一写,如果看过没写过,面试要手撕,大概率撕不出来。下面分别是:防抖立即执行和延迟执行,节流立即执行和延迟执行

function debounce(fn, delay) {let timer = null;return function (...args) {timer && clearTimeout(timer);timer = setTimeout(() => {fn.apply(this, args);}, delay);}
}function debounce_immediate(fn, delay, immediate = false) {let timer = null;let flag = true;return function (...args) {timer && clearTimeout(timer);if (immediate) {if (flag) {fn.apply(this, args);flag = false;}timer = setTimeout(() => {flag = true;}, delay);} else {timer = setTimeout(() => {fn.apply(this, args);}, delay);}}
}function throttle(fn, delay) {let lastTime = 0;let timer = null;return function (...args) {const nowTime = Date.now();if (nowTime - lastTime > delay) {fn.apply(this, args);lastTime = nowTime;}}
}function throttle_D(func, delayTime) {let delay = delayTime || 1000;let timer = null;return function (...args) {if (!timer) {timer = setTimeout(function () {func.apply(this, args);timer = null;}, delay);}}
}

记得带上args,防抖和节流本质的区别是防抖一直在重置定时器,节流不是的,节流一直触发的话在一定时间间隔内都会执行且只执行一次。防抖如果一直触发个一分钟,那也只会执行一次(立即执行或者最后一次点击时触发延迟执行)。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com