目录
面试官:什么是防抖和节流,他们的应用场景有哪些?
面试官:js中什么是可选操作符,如何访问数组?
面试官:请简述一下 event loop
面试官:请简述 node/v8 中的垃圾回收机制
面试官:如何删除项目中没有使用到的 package?
面试官:请你谈谈 js 中在 new 的时候发生了什么?
面试官:浏览器的剪切板中如何监听复制事件?
面试官:如何实现页面文本不可复制?
面试官:异步加载 JS 脚本时,async 与 defer 有何区别?
面试官:前端如何实现文件上传功能?
每天10道题,100天后,搞定所有前端面试的高频知识点,加油!!!,在看文章的同时,希望不要直接看答案,先思考一下自己会不会,如果会,自己的答案是什么?想过之后再与答案比对,是不是会更好一点,当然如果你有比我更好的答案,欢迎评论区留言,一起探讨技术 之美。
我:呃~,防抖和节流以及他们的应用场景总结如下:
防抖:顾名思义就是防止抖动,以免把一次事件误认为是多次,敲键盘就是一个每天都会接触到的防抖操作,在JS中有好多防抖的场景,例如:登录、发短信等按钮避免用户点击太快,以致于发送了多次请求,需要防抖;调整浏览器窗口大小时,resize 次数过于频繁,造成计算过多,此时需要一次到位,就用到了防抖;代码如下,可以看出来防抖重在清零:
function debounce(f, wait) { let timer; return (...args) => { clearTimeout(timer); timer = setTimeout(() => { f(...args); }, wait); }; }
节流:节流,顾名思义,控制水的流量。控制事件发生的频率,如控制为 1s 发生一次,甚至 1 分钟发生一次。与服务端(server)及网关(gateway)控制的限流 (Rate Limit) 类似,在JS中有好多节流的场景,例如:scroll事件,每隔一秒计算一次位置信息等;浏览器播放事件,每个一秒计算一次进度信息等;input 框实时搜索并发送请求展示下拉列表,每隔一秒发送一次请求 (也可做防抖);代码如下,可以看出来节流重在加锁:
function throttle(f, wait) { let timer; return (...args) => { if (timer) { return; } timer = setTimeout(() => { f(...args); timer = null; }, wait); }; }
总结:
防抖:防止抖动,单位时间内事件触发会被重置,避免事件被误伤触发多次。代码实现重在清零 clearTimeout。防抖可以比作等电梯,只要有一个人进来,就需要再等一会儿。业务场景有避免登录按钮多次点击的重复提交。
节流:控制流量,单位时间内事件只能触发一次,与服务器端的限流 (Rate Limit) 类似。代码实现重在开锁关锁 timer=timeout; timer=null。节流可以比作过红绿灯,每等一个红灯时间就可以过一批。
我:呃~,?. 操作符,可以嵌套获取对象的属性值。通过获取对象属性获得的值可能是 undefined 或 null 时,可选链操作符提供了一种方法来简化被连接对象的值访问。具体使用方式如下:
const o = {};
// 添加可选链之前
o && o.a && o.a.b && o.a.b.c && o.a.b.c.d;
// 添加可选链之后
o?.a?.b?.c?.d;
const obj = { a: [1, 2], b() {} };
// 访问数组
obj?.a?.[0];
//使用方法
obj?.b?.();
我:呃~,因为 JS 是单线程的,单线程就意味着,所有任务需要排队,前一个任务结束,才会执行后一个任务。为了解决排除等待问题,JS 的任务分为同步任务(synchronous)和异步任务(asynchronous)。 所有同步任务都在主线程上执行,形成一个 Stack)。异步任务(如果是 WebAPI 则会进入 WebAPI,例如 ajax setTimeout)不进入主线程,而是进入另一 Callback Queue。同步任务顺序执行,只有执行栈中的同步任务执行完了,系统才会读取任务队列中可以执行的异步任务,才会把此异步任务从事件队列中放入执行栈中执行,如此循环,直至所有任务执行完毕。这就是 EventLoop。代码如下:
setTimeout(() => console.log(0));
new Promise((resolve) => {
console.log(1);
resolve(2);
console.log(3);
}).then((o) => console.log(o));
new Promise((resolve) => {
console.log(4);
resolve(5);
})
.then((o) => console.log(o))
.then(() => console.log(6));
输出内容如下:
我:呃~,v8 中的垃圾回收机制分为如下三种:
Scavenge:工作在新生代,把 from space 中的存活对象移至 to space
Mark-Sweep:标记清除。新生代的某些对象由于过度活跃会被移至老生代,此时对老生代中活对象进行标记,并清理死对象
Mark-Compac:标记整理。
当一个函数执行结束之后,JavaScript 引擎会通过向下移动 ESP 来销毁该函数保存在栈中的执行上下文。 要回收堆中的垃圾数据,就需要用到 JavaScript 中的垃圾回收器了。 代际假说(The Generational Hypothesis),是垃圾回收领域中一个重要的术语,后续垃圾回收的策略都是建立在该假说的基础之上的,所以很是重要。 代际假说有以下两个特点:
1)大部分对象在内存中存在的时间很短,简单来说,就是很多对象一经分配内存,很快就变得不可访问;
2)是不死的对象,会活得更久。 在 V8 中会把堆分为新生代和老生代两个区域,新生代中存放的是生存时间短的对象,老生代中存放的生存时间久的对象。 ● 副垃圾回收器,主要负责新生代的垃圾回收。 ● 主垃圾回收器,主要负责老生代的垃圾回收。
使用增量标记算法,可以把一个完整的垃圾回收任务拆分为很多小的任务,这些小的任务执行时间比较短,可以穿插在其他的 JavaScript 任务中间执行,增强用户体验。
我:呃~,可以采用 depcheck 来完成这件事,代码如下:
npm install depcheck -g
我:呃~,在js中进行new的时候,发生了下面的四步:
1)创建了一个新对象
2)链接到原型
3)绑定这个指向
4)返回这个对象
function _new() {
let obj = {};
let Con = [].shift.call(arguments);
obj.__proto__ = Con.prototype;
let result = Con.apply(obj, arguments);
return typeof obj === "object" ? obj : {};
}
我:呃~,可以采用原生js发方式进行监听,如下:
// 在 HTML 元素上
// 在 JS 中获取具体元素
document.querySelector("p").oncopy = cb;
document.oncopy = cb;
// 或
document.querySelector("p").addEventListener("copy", cb);
document.addEventListener("copy", cb);
我:呃~,有CSS和JS两种方法,以下任选其一或结合使用即可。
使用CSS如下:
user-select: none;
使用JS如下,监听 selectstart 事件,禁止选中,当用户选中一片区域时,将触发 selectstart 事件,Selection API 将会选中一片区域。禁止选中区域即可实现页面文本不可复制:
document.body.onselectstart = (e) => { e.preventDefault(); }; document.body.oncopy = (e) => { e.preventDefault(); };
我:呃~,在正常情况下,即