前端经典⾯试题60道,附答案!
⼤家好,我是漫步,今天分享⼀篇⽐较全的⾯试题,包含 JS、CSS、React、⽹络、浏览器、程序题等。
……
(以下所有答案仅供参考)
简答题
1、什么是防抖和节流?有什么区别?如何实现?
参考答案
防抖
触发⾼频事件后n秒内函数只会执⾏⼀次,如果n秒内⾼频事件再次被触发,则重新计算时间
思路:
每次触发事件时都取消之前的延时调⽤⽅法
function debounce(fn) {
let timeout = null; // 创建⼀个标记⽤来存放定时器的返回值
return function () {
clearTimeout(timeout); // 每当⽤户输⼊的时候把前⼀个 setTimeout clear 掉
timeout = setTimeout(() => { // 然后⼜创建⼀个新的 setTimeout, 这样就能保证输⼊字符后的 interval 间隔内如果还有字符输⼊的话,就不会执⾏ fn 函数          fn.apply(this, arguments);
}, 500);
};
}
function sayHi() {
console.log('防抖成功');
}
var inp = ElementById('inp');
inp.addEventListener('input', debounce(sayHi)); // 防抖
节流
⾼频事件触发,但在n秒内只会执⾏⼀次,所以节流会稀释函数的执⾏频率
思路:
个人电脑安装linux每次触发事件时都判断当前是否有等待执⾏的延时函数
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));
2、 get请求传参长度的误区、get和post请求在缓存⽅⾯的区别
误区:我们经常说get请求参数的⼤⼩存在限制,⽽post请求的参数⼤⼩是⽆限制的。
参考答案
实际上HTTP 协议从未规定 GET/POST 的请求长度限制是多少。对get请求参数的限制是来源与浏览器或web服务器,浏览器或web服务器限制了url的长度。为了明确这个概念,我们必须再次强调下⾯⼏点:
HTTP 协议 未规定 GET 和POST的长度限制
GET的最⼤长度显⽰是因为 浏览器和 web服务器限制了 URI的长度
不同的浏览器和WEB服务器,限制的最⼤长度不⼀样
要⽀持IE,则最⼤长度为2083byte,若只⽀持Chrome,则最⼤长度 8182byte
补充补充⼀个get和post在缓存⽅⾯的区别:
get请求类似于查的过程,⽤户获取数据,可以不⽤每次都与数据库连接,所以可以使⽤缓存。
post不同,post做的⼀般是修改和删除的⼯作,所以必须与数据库交互,所以不能使⽤缓存。因此get
请求适合于请求缓存。
3、模块化发展历程
可从IIFE、AMD、CMD、CommonJS、UMD、sure)、ES Module、<script type="module"> 这⼏个⾓度考虑。
参考答案
模块化主要是⽤来抽离公共代码,隔离作⽤域,避免变量冲突等。
IIFE:使⽤⾃执⾏函数来编写模块化,特点:在⼀个单独的函数作⽤域中执⾏代码,避免变量冲突。
(function(){
return {
data:[]
}
})()
AMD:使⽤requireJS 来编写模块化,特点:依赖必须提前声明好。
define('./index.js',function(code){
// code 就是index.js 返回的内容
})
CMD:使⽤seaJS 来编写模块化,特点:⽀持动态引⼊依赖⽂件。
define(function(require, exports, module) {
var indexCode = require('./index.js');
})
CommonJS:nodejs 中⾃带的模块化。
var fs = require('fs');
UMD:兼容AMD,CommonJS 模块化语法。
sure):webpack 2.x 版本中的代码分割。
ES Modules:ES6 引⼊的模块化,⽀持import 来引⼊另⼀个 js 。
import a from 'a';
4、npm 模块安装机制,为什么输⼊ npm install 就可以⾃动安装对应的模块?
crontab每个月执行一次
参考答案
1. npm 模块安装机制:
发出npm install命令
查询node_modules⽬录之中是否已经存在指定模块
npm 向 registry 查询模块压缩包的⽹址
下载压缩包,存放在根⽬录下的.npm⽬录⾥
解压压缩包到当前项⽬的node_modules⽬录
若存在,不再重新安装
若不存在
2. npm 实现原理
输⼊ npm install 命令并敲下回车后,会经历如下⼏个阶段(以 npm 5.5.1 为例):
1. 执⾏⼯程⾃⾝ preinstall
当前 npm ⼯程如果定义了 preinstall 钩⼦此时会被执⾏。
2. 确定⾸层依赖模块
⾸先需要做的是确定⼯程中的⾸层依赖,也就是 dependencies 和 devDependencies 属性中直接指定的模块(假设此时没有添加npm install 参数)。
⼯程本⾝是整棵依赖树的根节点,每个⾸层依赖模块都是根节点下⾯的⼀棵⼦树,npm 会开启多进程从每个⾸层依赖模块开始逐步寻更深层级的节点。
3. 获取模块
获取模块是⼀个递归的过程,分为以下⼏步:
获取模块信息。在下载⼀个模块之前,⾸先要确定其版本,这是因为 package.json 中往往是 semantic version(semver,语义化版本)。此时如果版本描述⽂件(npm-shrinkwrap.json 或 package-lock.json)中有该模块信息直接拿即可,如果没有则从仓库获取。如 packaeg.json 中某个包的版本是 ^1.1.0,npm 就会去仓库中获取符合 1.x.x 形式的最新版本。
获取模块实体。上⼀步会获取到模块的压缩包地址(resolved 字段),npm 会⽤此地址检查本地缓存,缓存中有就直接拿,如果没有则从仓库下载。
查该模块依赖,如果有依赖则回到第1步,如果没有则停⽌。
模块扁平化(dedupe)
上⼀步获取到的是⼀棵完整的依赖树,其中可能包含⼤量重复模块。⽐如 A 模块依赖于 loadsh,B 模块同样依赖于 lodash。在npm3 以前会严格按照依赖树的结构进⾏安装,因此会造成模块冗余。
从 npm3 开始默认加⼊了⼀个 dedupe 的过程。它会遍历所有节点,逐个将模块放在根节点下⾯,也就是 node-modules 的第⼀层。当发现有重复模块时,则将其丢弃。
这⾥需要对重复模块进⾏⼀个定义,它指的是模块名相同且 semver 兼容。每个 semver 都对应⼀段版本允许范围,如果两个模块的版本允许范围存在交集,那么就可以得到⼀个兼容版本,⽽不必版本号完全⼀致,这可以使更多冗余模块在 dedupe 过程中被去掉。
⽐如 node-modules 下 foo 模块依赖 lodash@^1.0.0,bar 模块依赖 lodash@^1.1.0,则 ^1.1.0 为兼容版本。
⽽当 foo 依赖 lodash@^2.0.0,bar 依赖 lodash@^1.1.0,则依据 semver 的规则,⼆者不存在兼容版本。会将⼀个版本放在node_modules 中,另⼀个仍保留在依赖树⾥。
举个例⼦,假设⼀个依赖树原本是这样:
node_modules
-- foo
---- lodash@version1
-- bar
---- lodash@version2
假设 version1 和 version2 是兼容版本,则经过 dedupe 会成为下⾯的形式:
node_modules
-- foo
-- bar
-- lodash(保留的版本为兼容版本)
假设 version1 和 version2 为⾮兼容版本,则后⾯的版本保留在依赖树中:
node_modules
-- foo
-- lodash@version1
-- bar
---- lodash@version2
web前端基础面试题安装模块
这⼀步将会更新⼯程中的 node_modules,并执⾏模块中的⽣命周期函数(按照 preinstall、install、postinstall 的顺序)。
执⾏⼯程⾃⾝⽣命周期
当前 npm ⼯程如果定义了钩⼦此时会被执⾏(按照 install、postinstall、prepublish、prepare 的顺序)。
最后⼀步是⽣成或更新版本描述⽂件,npm install 过程完成。
5、ES5的继承和ES6的继承有什么区别?
openssl升级风险
参考答案
ES5的继承时通过prototype或构造函数机制来实现。ES5的继承实质上是先创建⼦类的实例对象,然后再将⽗类的⽅法添加到this上(Parent.apply(this))。
ES6的继承机制完全不同,实质上是先创建⽗类的实例对象this(所以必须先调⽤⽗类的super()⽅法),然后再⽤⼦类的构造函数修改this。
具体的:ES6通过class关键字定义类,⾥⾯有构造⽅法,类之间通过extends关键字实现继承。⼦类必须在constructor⽅法中调⽤super⽅法,否则新建实例报错。因为⼦类没有⾃⼰的this对象,⽽是继承了⽗类的this对象,然后对其进⾏加⼯。如果不调⽤super⽅法,⼦类得不到this对象。
ps:super关键字指代⽗类的实例,即⽗类的this对象。在⼦类构造函数中,调⽤super后,才可使⽤this关键字,否则报错。
6、setTimeout、Promise、Async/Await 的区别
参考答案:
gongchenghuigch.github.io/2019/09/14/awat/
7、定时器的执⾏顺序或机制?
参考答案
因为js是单线程的,浏览器遇到setTimeout或者setInterval会先执⾏完当前的代码块,在此之前会把定时器推⼊浏览器的待执⾏事件队列⾥⾯,等到浏览器执⾏完当前代码之后会看⼀下事件队列⾥⾯有没有任务,有的话才执⾏定时器的代码。所以即使把定时器的时间设置为0还是会先执⾏当前的⼀些代码。
function test(){
var aa = 0;
var testSet = setInterval(function(){
aa++;
console.log(123);
if(aa<10){
clearInterval(testSet);
}idea
},20);
var testSet1 = setTimeout(function(){
console.log(321)
},1000);
for(var i=0;i<10;i++){
console.log('test');列表中字符串转数字
}
}
test()
输出结果:
test //10次
undefined
123
321
8、['1','2','3'].map(parseInt) 输出什么,为什么?
参考答案
输出:[1, NaN, NaN]

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。