这里分享一份由字节前端面试官整理的「2021大厂前端面试手册」,内容囊括Html、CSS、Javascript、Vue、HTTP、浏览器面试题、数据结构与算法。全部整理在下方文档中,共计111道
HTML
-
HTML5有哪些新特性?
-
Doctype作⽤? 严格模式与混杂模式如何区分?它们有何意义?
-
如何实现浏览器内多个标签页之间的通信?
-
⾏内元素有哪些?块级元素有哪些? 空(void)元素有那些?⾏内元 素和块级元素有什么区别?
-
简述⼀下src与href的区别?
-
cookies,sessionStorage,localStorage 的区别?
-
HTML5 的离线储存的使用和原理?
-
怎样处理 移动端 1px 被 渲染成 2px 问题?
-
iframe 的优缺点?
-
Canvas 和 SVG 图形的区别是什么?
JavaScript
开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】
-
问:0.1 + 0.2 === 0.3 嘛?为什么?
-
JS 数据类型
-
写代码:实现函数能够深度克隆基本类型
-
事件流
-
事件是如何实现的?
-
new 一个函数发生了什么
-
什么是作用域?
-
JS 隐式转换,显示转换
-
了解 this 嘛,bind,call,apply 具体指什么
-
手写 bind、apply、call
-
setTimeout(fn, 0)多久才执行,Event Loop
-
手写题:Promise 原理
-
说一下原型链和原型链的继承吧
-
数组能够调用的函数有那些?
-
PWA使用过吗?serviceWorker的使用原理是啥?
-
ES6 之前使用 prototype 实现继承
-
箭头函数和普通函数有啥区别?箭头函数能当构造函数吗?
-
事件循环机制 (Event Loop)
黑色箭头为触发动作,红色箭头为执行动作。
非立即执行
立即执行
来看一下执行流程: 首先如果immediate
为true的情况:
第一次执行:timeout
为null
,则runNow
为true
,然后设置一个定时器,在指定的时间后设置timeout
为null
,这也就代表设置执行的间隔时间,最后判断runNow
是否执行函数。
第二次执行:
- 情况一:已超过设置时间:如果第二次触发执行已经超过设置的时间,此时
timeout
已经被定时器设置为null
,那么进入debounced
函数后,runNow
为true
,重新设置定时器,然后执行函数。 - 情况二:未超过设置时间:因为没有超过设置时间,所以
timeout
并未被定时器设置为null
,那么runNow
为false
,由于timeout
的定时器已经被清除,所以重置定时器,不会执行函数。
再来看一下immediate
为false
的情况:
其实这种情况和我们之前设置的是一样的,没有超过设置时间,则重置定时器,定时器在到达指定时间后自动执行一次函数。
两者之间最大的区别是:立即执行的功能会在第一次触发函数的时候执行一次,下次触发如果已到达设置时间,则直接执行一次。而非立即执行的功能第一次触发函数时只会设置一个定时器,时间到达后自动执行,如果在设置时间内触发只会重置定时器,永远不会立即执行函数。
取消
再增加一个需求:如果想要取消debounce
函数怎么办,比如 debounce
的时间间隔是 10 秒钟,immediate
为 true
,这样只有等 10 秒后才能重新触发事件,如果有一个取消功能,点击后取消防抖,再去触发,就可以立刻执行了。
debounced.cancel = function() {
// 删除定时器
clearTimeout(timeout);
// 设置timeout为null
timeout = null;
};
只需要将定时器清除,设置timeout
为null
即可,因为如果immediate
为 true
会直接执行一次函数,然后重新设置定时器 😂
完整实现:
最后完整的防抖函数如下:
function debounce(func, wait, immediate) {
let res, timeout, context, args;
const debounced = function () {
context = this;
args = arguments;
if (timeout) clearTimeout(timeout);
if (immediate) {
var runNow = !timeout;
timeout = setTimeout(function(){
timeout = null;
}, wait)
if (runNow) res = func.apply(context, args)
}
else {
timeout = setTimeout(function(){
func.apply(context, args)
}, wait);
}
return res;
};
debounced.cancel = function() {
clearTimeout(timeout);
timeout = null;
};
return debounced;
}
节流
节流也是用于减少触发执行的手段之一,但是思路和防抖是完全不一样的,
如果持续触发事件,每隔一段时间,只执行一次事件。也就是只按照设置的时间作为时间段,到达指定的时间后触发函数就会执行。没有到达指定的时间,无论如何触发函数都不会执行。
也就是没到点,无论你怎么撩,我都岿然不动 🤩
目前有两种实现方式:使用时间戳和设置定时器。
时间戳
当触发函数的时候,使用当前的时间戳与上一次触发函数所保存的时间戳相减,然后对比设置定时器的时间,决定是否执行函数。
const throttle = function(func, wait) {
let previous = 0, context, args;
return function() {
context = this
args = arguments
// 获取当前时间戳
let now = +new Date()
// 判断当前时间戳与上一次触发的时间差值是否大于等于指定时间
if((now - previous) >= wait) {
func.apply(context, args)
// 更新时间戳
previous = now
}
}
}
值得注意的是:js中可以在某个元素前使用 ‘+’ 号,这个操作是将该元素转换成Number
类型,如果转换失败,那么将得到 NaN
。
+new Date()
将会调用 Date.prototype
上的 valueOf()
方法,根据MDN,Date.prototype.value
方法等同于Date.prototype.getTime()
。
console.log(+new Date('2022-08-17'));
console.log(new Date('2022-08-17').getTime());
console.log(new Date('2022-08-17').valueOf());
console.log(new Date('2022-08-17') * 1);
// 结果都是相同的
设置定时器
设置定时器的实现思路是:在第一次触发时设置一个定时器,在指定时间之后设置变量为null
,下次触发函数判断变量是否为null
,来决定是否执行函数。
const throttle = function(func, wait) {
let timeout, context, args;
return function() {
context = this
args = arguments
// 允许执行
if(!timeout) {
// 设置定时器,到达时间后设置timeout为null
timeout = setTimeout(function() {
timeout = null
func.apply(context, args)
}, wait)
}
}
}
以上两种方式均可以满足一个基本的节流函数的写法,但是两种写法还是有一定的区别的:
- 第一种事件会立刻执行,第二种事件会在 n 秒后第一次执行
- 第一种事件停止触发后不会再执行事件,第二种事件停止触发后依然会再执行一次事件
既然执行时的行为不同,那么有没有办法将两者结合呢?
两者结合
将两者结合起来是要实现一个既能开始时执行一次函数,又能结束时再执行一次函数!
思路是这样的:如果触发函数时没有到达指定时间,则设置定时器,如果已经到达设置的时间,则直接进行执行。
function throttle(func, wait) {
let timeout, context, args, previous = 0;
const later = function() {
// 定时器执行时更新时间戳
previous = +new Date();
timeout = null;
// 执行函数
func.apply(context, args)
};
const throttled = function() {
let now = +new Date();
//下次触发 func 剩余的时间
let remaining = wait - (now - previous);
context = this;
args = arguments;
// 如果没有剩余的时间了或者更改了系统时间
if (remaining <= 0 || remaining > wait) {
// 清空定时器及timeout
if (timeout) {
clearTimeout(timeout);
timeout = null;
}
// 更新时间戳变量
previous = now;
func.apply(context, args);
} else if (!timeout) {
// 处理还没有到达指定时间的触发行为
// 此处设置定时器时间要设置剩余的时间,与上文中防抖函数中有区别
timeout = setTimeout(later, remaining);
}
};
return throttled;
}
还是依旧缕一下思路:
第一次触发 throttled
时,因为 previous
为 0 ,所以remaining <= 0
这个条件成立,执行func
函数,并且重置定时器及变量,最后将previous
跟更新为当前时间。
第二次触发:
- 未到达指定时间:如果没有到达指定时间,那么
remaining
为正数,所以不会进入remaining <= 0
这个执行语句,而是会设置定时器。不会执行函数。 - 到达指定时间:
remaining
为负数,执行函数,同第一次触发。
同样在定时器执行时,也会更新previous
和timeout
的值。
其实核心在于remaining
这个变量的运算。
控制执行时机
又又又来了一个需求,如果希望能够控制首次和末次要不要执行怎么办?
可以传递第三个参数:
leading:false
表示禁用第一次执行trailing: false
表示禁用停止触发的回调
function throttle(func, wait, options = {}) { //修改
let timeout, context, args, previous = 0;
const later = function() {
previous = options.leading === false ? 0 : +new Date(); //修改
timeout = null;
func.apply(context, args);
// 清空作用域及参数变量
if (!timeout) context = args = null; //修改
};
const throttled = function() {
let now = +new Date();
// 如果是首次触发,并且设置首次不执行函数。那么将previous与now进行同步
// now 与 previous 相减不小于0,则不会执行函数
if (!previous && options.leading === false) previous = now; // 新增
let remaining = wait - (now - previous);
context = this;
args = arguments;
if (remaining <= 0 || remaining > wait) {
if (timeout) {
clearTimeout(timeout);
timeout = null;
}
previous = now;
func.apply(context, args);
// 清空作用域及参数变量
if (!timeout) context = args = null; //修改
} else if (!timeout && options.trailing !== false) { // 修改
timeout = setTimeout(later, remaining);
}
};
return throttled;
}
我们要注意的是实现中有这样一个问题:
那就是 leading:false
和 trailing: false
不能同时设置。因为如果同时设置,那么就是既不开始触发也不结束时触发,那么函数将不会正常执行。
其实核心还是关于时间戳的加减法,无非就是根据功能来设置时间戳而已。
取消
与防抖函数的取消功能基本相同,重置各个作用变量:
throttled.cancel = function() {
clearTimeout(timeout);
previous = 0;
timeout = null;
}
完整实现
function throttle(func, wait, options = {}) {
let timeout, context, args, previous = 0;
const later = function() {
previous = options.leading === false ? 0 : +new Date();
timeout = null;
func.apply(context, args);
if (!timeout) context = args = null;
};
const throttled = function() {
### 最后
在面试前我花了三个月时间刷了很多大厂面试题,最近做了一个整理并分类,主要内容包括html,css,JavaScript,ES6,计算机网络,浏览器,工程化,模块化,Node.js,框架,数据结构,性能优化,项目等等。
包含了腾讯、字节跳动、小米、阿里、滴滴、美团、58、拼多多、360、新浪、搜狐等一线互联网公司面试被问到的题目,涵盖了初中级前端技术点。
* HTML5新特性,语义化
* 浏览器的标准模式和怪异模式
* xhtml和html的区别
* 使用data-的好处
* meta标签
* canvas
* HTML废弃的标签
* IE6 bug,和一些定位写法
* css js放置位置和原因
* 什么是渐进式渲染
* html模板语言
* meta viewport原理
![](https://img-blog.csdnimg.cn/img_convert/64590c67cb47cd552a892ba710477f64.png)
* **[开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】](https://bbs.csdn.net/topics/618166371)**
![](https://img-blog.csdnimg.cn/img_convert/17fdfcb90fad04acc5e7273d1f853880.png)