前言
记录面试大厂的一些面试题,用作保留并分享(部分重复题目会剔除)
阿里
1、promise规范?有多少种状态
* Promise 必须处于以下三个状态之一: pending(等待状态), fulfilled(执行状态) 或者是 rejected(拒绝状态);
* Promise 的状态只能从 **pending->fulfilled** 或 **pending->rejected**;
* Promise 成功会有成功的value,失败会有失败的reason
* Promise 必须提供一个then方法来访问最终的结果,then方法接收两个参数 **promise.then(onFulfilled,onRejected)**
2、多promise对象怎么进行处理?
如果某结果需要多个对象全部成功返回处理后才能完成,可以使用 Promise.all 方法;
如果某结果需要任意一个对象返回后才能完成,可以使用 Promise.any方法 和 Promise.race方法。
下面展示 Promise.all 方法使用
console.log("start")
let p1 = new Promise((resolve,reject)=>{
setTimeout(()=>{
console.log(1000+"s, p1 done")
resolve()
}, 1000)
})
let p2 = new Promise((resolve, reject) => {
setTimeout(() => {
console.log(2000+"s, p2 done")
resolve()
}, 2000)
})
let p3 = new Promise((resolve, reject) => {
setTimeout(() => {
console.log(3000+"s, p3 done")
resolve()
}, 3000)
})
let r = Promise.all([p1,p2,p3]); // 等待
r.then(()=>{
console.log("All done")
})
3、JS的模块加载机制有哪些?
模块机制有amd,cmd和commonJs机制
amd对应的实现是requireJs,cmd对应实现是Seals,CommonJs对应实现是nodeJs
amd和cmd针对的是浏览器端,而commonJs针对的是服务端
amd是预加载,加载同时还会解析代码;(优点是快,缺点是加载顺序不固定,容易出问题,埋下大坑)
cmd是懒加载,加载后暂时不执行代码,需要时候再执行(优点是加载顺序可控,缺点是加载速度慢)
require和import的区别
require是amd的加载方式,import是es6中module的加载方式
require在文件任何时候都可以调用,但是import只能放在文件开头使用,因为它是编译时就调用,而require是在文件运行时调用
require的本质是将require的结果(其实就是各种对象,数字,函数等等)赋值给某个变量;
而import的实质是依赖于node的一种将es6转码为es5的一种加载方式,import最终会被转码为require
4、节流和防抖?
节流(throttle):
优化高频率执行js代码的一种手段,核心是保证一段时间内只执行一次核心代码,
让一个函数不要执行得太频繁,减少一些过快的调用来节流。
防抖(debounce):
指触发事件后在 n 秒内函数只能执行一次,如果在 n 秒内又触发了事件,则会重新计算函数执行时间。
先看一段无任何处理的onmousemove的代码,效果是移动的时候,数字+1。
let _text = document.getElementById('text');
let nowTime = new Date().getTime();
function addCount() {
let count = Number(_text.innerHTML);
_text.innerHTML = count + 1;
}
function normal_mouseMove() {
addCount();
}
_text.onmousemove = normal_mouseMove;
如果我们需要实现简单的 节流 功能,如执行核心代码addCount后,需隔 1 秒才能继续执行下一次,需要把监听改成这样:
function throttle_mouseMove() {
let _time = new Date().getTime();
if (_time > nowTime + 1000) {
nowTime = _time;
addCount();
}
}
_text.onmousemove = throttle_mouseMove;
如果我们需要实现简单的 防抖 功能,如执行核心代码addCount后,需停止 1 秒才能继续执行下一次,需要把监听改成这样:
function debounce_mouseMove() {
let _time = new Date().getTime();
if (_time > nowTime + 1000) {
addCount();
}
nowTime = _time;
}
5、前端性能优化?
(1)资源加载优化
静态资源合并压缩、CDN托管、资源缓存、非核心代码异步加载async/defer/动态脚本创建js、;
(2)渲染优化
CSS文件放在HEAD头部、JS文件放在BODY底部、图片懒加载、多个DOM操作合并到一个、DOM查询缓存、事件节流、事件防抖;
(3)工具优化 - webpack
用webpack优化前端性能是指优化webpack的输出结果,让打包的最终结果在浏览器运行快速高效。
1、压缩代码。删除多余的代码、注释、简化代码的写法等等方式。
可以利用webpack的UglifyJsPlugin和ParallelUglifyPlugin来压缩JS文件, 利用cssnano(css-loader?minimize)来压缩css
2、利用CDN加速。在构建过程中,将引用的静态资源路径修改为CDN上对应的路径。
可以利用webpack对于output参数和各loader的publicPath参数来修改资源路径
3、删除死代码(Tree Shaking)。将代码中永远不会走到的片段删除掉。
可以通过在启动webpack时追加参数--optimize-minimize来实现
4、提取公共代码。
6、React生命周期相关
参考:大话react生命周期2019:react-v16.3新生命周期总结
7、React有哪些性能优化?
参考: 这么高质量的React面试题 、 如何减少长列表的渲染时间
面对这个问题,虚拟列表是一个有效的设计思路。即永远只渲染当前用户可见,以及其前后的几个元素。 监听滑动事件 (scroll event),在滑动的时候调整当前窗口的可见元素,并渲染出来。 这样可以大大节约插入和移除大量 DOM 元素的时间,可以大大加快初次渲染时间,而且渲染时间不随着数据量增加而增加。
react-window 是一个非常好用的 React 虚拟列表组件库。使用方法在文档和 demo 里面已经很详细地描述了。
该库采用了以下几点优化:
- 如前面所述,只渲染当前可视以及前后若干个元素。最外层容器采用 relative 定位,内部元素使用 absolute 定位,并计算该定位的 offset,比如:第一个元素的 top 距离应该是 itemSize * index,如果列表是垂直方向排列的,那么 itemSize 就是单个元素的高度。
- 懒计算和缓存。因为每个元素的 offset 都是不同的,需要专门计算出来。如果一次性把所有列表元素的 offset 都计算出来,那么也会占用 O(n) 的时间。所以只在每次渲染可见元素之前计算 offset,也就是懒计算。同时利用了缓存来避免重复计算,如果一个元素先被从视窗移除再重新加载,即来回滑动列表,那么这个计算结构是可以直接从缓存中取出的。
- 使用纯组件(pure component)来避免重渲染。
最后是一个小设计,列表容器可以传入一个属性 isScrolling 告诉列表元素目前是否正在滑动。开始滑动 (on scroll) 的时候改变滑动状态为正在滑动,而结束滑动的时候会稍微等一段时间 (interval),才确定滑动停止。对这个滑动状态的更新函数有进行 debounce 处理,只有最后停止滑动一段时间才更新滑动状态为已停止,这样可以避免子元素的不必要渲染,如果子元素在滑动的时候会重绘的话。
8、Cookie、session、sessionStorage和localStorage具体什么区别?
9、事件代理有什么作用?优点?具体原理?
优点:相较于一般为每个子节点添加事件监听的方式,可以减少添加事件监听器的次数,提高性能;后续新增的子节点不需要重新添加事件监听;
window.onload = function(){
let _ul = document.querySelector('ul');
_ul.onclick = function(e){
let ev = e || window.event;
let target = ev.target || ev.srcElement;
if(target.nodeName.toLocaleLowerCase() === 'li'){
alert(target.innerHTML);
}
}
}
10、diff算法实现和原理?
11、React中setState原理?setState执行后发生了什么
参考: react的setstate原理
setState干了什么:
12、React-router实现原理?
React Router 是一个基于 React 的强大路由库,它可以让你向应用中快速地添加视图和数据流,其实现需依赖库history;
history 有三种实现方式:
1、BrowserHistory:用于支持 HTML5 历史记录 API 的现代 Web 浏览器
2、HashHistory:用于旧版Web浏览器
3、MemoryHistory:用作参考实现,也可用于非 DOM 环境,如 React Native 或测试
页面跳转实现
BrowserHistory:pushState、replaceState;
HashHistory:location.hash、location.replace
浏览器回退
BrowserHistory:popstate;
HashHistory:hashchang;
操作原理:
框架去拦截浏览器跳转,自己去同步UI组件
其中在react-router中,URL对应Location对象,而UI是由react components来决定的,这样就转变成location与components之间的同步问题
通过router声明了一份含有 path to component 的各个映射的路由表。
react-router 还提供的 Link 组件(如下),作为提供更新 url 的途径,
触发 Link 后最终将通过如上面定义的路由表进行匹配,并拿到对应的 component 及 state 进行 render 渲染页面。
13、React hooks相关
14、React 长列表优化
15、工程化相关:babel的工作原理?
babel是一个转译器,感觉相对于编译器compiler,叫转译器transpiler更准确,因为它只是把同种语言的高版本规则翻译成低版本规则,而不像编译器那样,输出的是另一种更低级的语言代码。 但是和编译器类似,babel的转译过程也分为三个阶段:parsing、transforming、generating,以ES6代码转译为ES5代码为例,babel转译的具体过程如下:
ES6代码输入 ==》 babylon进行解析 ==》 得到AST==》 plugin用babel-traverse对AST树进行遍历转译
==》 得到新的AST树==》 用babel-generator通过AST树生成ES5代码
此外,还要注意很重要的一点就是,babel只是转译新标准引入的语法,比如ES6的箭头函数转译成ES5的函数;而新标准引入的新的原生对象,部分原生对象新增的原型方法,新增的API等(如Proxy、Set等),这些babel是不会转译的。需要用户自行引入polyfill来解决
16、引起跨域的原因有哪些?怎么解决跨域?
引起跨域的原因:
浏览器的同源策略是浏览器上为安全性考虑实施的非常重要的安全策略。
从一个域上加载的脚本不允许访问另外一个域的文档属性。
URL由协议、域名、端口和路径组成,如果两个URL的协议、域名和端口都相同,则表示它们同源,反之则不同源。
在浏览器中,<script>、<img>、<iframe>、<link>等标签(包含src属性)都可以加载跨域资源,而不受同源限制,
但浏览器会限制脚本中发起的跨域请求。比如,使用 XMLHttpRequest 对象和Fetch发起 HTTP 请求就必须遵守同源策略。
解决跨域的方法:
(1)通过jsonp跨域:需要目标服务器配合一个callback函数。
(2)通过修改document.domain来跨子域
(3)使用window.name来进行跨域:window.name+iframe 需要目标服务器响应window.name。
(4)使用HTML5中新引进的window.postMessage方法来跨域:需要目标服务器或者说是目标页面写一个postMessage,主要侧重于前端通讯。
(5)使用代理服务器:如nginx反向代理,可以不用目标服务器配合,不过需要你搭建一个中转nginx服务器,用于转发请求。
(6)CORS 需要服务器设置header :Access-Control-Allow-Origin。
对于客户端,我们还是正常使用xhr对象发送ajax请求。
唯一需要注意的是,需要设置我们的xhr属性withCredentials为true,不然的话,cookie是带不过去;
设置: xhr.withCredentials = true;
对于服务器端,需要在 response header中设置如下两个字段:
Access-Control-Allow-Origin: http://www.YOURHOST.com
Access-Control-Allow-Credentials:true
腾讯篇
1. 移动端自适应布局**
flex布局参考链接:一劳永逸的搞定 flex 布局
// 自适应rem + flex布局
// 如果设计稿基于iphone6,横向分辨率为750,为方便计算,那么比例计算为 750 / 100 = 7.5rem
// 假设设计稿规范为640px,例如1rem=100px计算,就把html节点的font-size设为100px(deviceWidth/6.4)
// 在dom ready后,deviceWidth设备宽度可以通过document.documentElement.clientWidth;
function resize() {
var width = document.documentElement.clientWidth;
var scale = 6.4; //设计稿分线率除以100的比例值
document.documentElement.style.fontSize = width / scale + 'px';
}
window.onresize = function(){
resize();
}
window.onload = function(){
resize();
}
2. css动画和js动画的区别
JS动画:
缺点:
1.JS在浏览器的主线程中运行,而主线程还有其他的js脚本,样式布局,绘制任务等,
对其干扰可能导致线程出现阻塞,从而造成丢帧的情况。
2.对于简单的动画来说,JS动画代码复杂度高于CSS3动画。
优点:
1.JS动画可以在动画运行的任意时间节点,去做一系列的事件控制,自由度高。
2.动画效果比CSS3动画丰富,有些动画只有JS动画才能实现,比如曲线运动,冲击闪烁等。
3.CSS3有兼容性问题,而JS大多时候没有兼容性问题。
CSS3动画:
缺点:
1.CSS3动画只能暂停,不能在动画节点中添加事件操作。
2.代码冗长。想用CSS3实现稍微复杂一点的动画,最后CSS代码都会变得特别笨重。
优点:
1.浏览器可以对动画进行优化。
3. css绝对居中
先来一段html代码
<div class="parent">
<div class="child">
child -- 1
</div>
<div class="child center">
child -- 2
</div>
<div class="child">
child -- 3
</div>
</div>
/* 1 Flex布局 (不需明确宽高)*/
.parent {
height: 100%;
display: flex;
justify-content: center;
flex-direction: column;
}
.child.center{
background: red;
align-self: center;
}
/* 2 绝对定位+transform减去宽高50% (不需明确宽高) */
.child.center {
background: red;
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
}
/* 3 需要设置width和height,但不需要明确数值 */
.child.center {
margin: auto;
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
}
/* 4 需要设置margin负值,数值为自身宽高的一半 */
.child.center {
width: 400px;
height: 300px;
position: absolute;
top: 50%;
left: 50%;
margin-top: -200px;
margin-left: -150px;
}
4. 解释css中,块元素、行内元素和行内块元素
三者之间可以通过display转换,对应关系为:
行内元素display: inline;块元素display: block;行内块元素display: inline-block;
行内元素:
1、不会自动换行,多个行内元素并列时从左到右排列;
2、无法设置宽高;
3、margin只对左右有效、上下无效,仅左右能撑开空间;padding上下左右有效,但仅左右能撑开空间;
块元素:
1、设置宽高有效;
2、设置margin和padding有效;
3、自动换行,多个块元素并列时从上到下排列;
行内块元素:(img,)
1、设置宽高有效;
2、设置margin和padding有效;
3、不会自动换行,多个块元素并列时从左到右排列;
5. css盒模型
一般情况下,完整的盒子是由 margin(外边距)、border(边框)、padding(内边距)和content(内容)组成
宽高分别为margin + border + padding + content的宽高之和。
css属性中,box-sizing 的值,常用的有3个:content-box,border-box,inherit
content-box
这是由 CSS2.1 规定的宽度高度行为。
宽度和高度分别应用到元素的内容框。
在宽度和高度之外绘制元素的内边距和边框。
总结:盒子总宽度 = height + padding + margin + border
border-box
为元素设定的宽度和高度决定了元素的边框盒。
就是说,为元素指定的任何内边距和边框都将在已设定的宽度和高度内进行绘制。
通过从已设定的宽度和高度分别减去边框和内边距才能得到内容的宽度和高度。
总结:盒子总宽度 = height + border ;
inherit
规定应从父元素继承 box-sizing 属性的值。
6. 从用户输入URL确认,到浏览器渲染页面完成,中间发生了些什么
// 主干流程
1. 从浏览器接收url到开启网络请求线程(这一部分可以展开浏览器的机制以及进程与线程之间的关系)
2. 开启网络线程到发出一个完整的http请求(这一部分涉及到dns查询,tcp/ip请求,五层因特网协议栈等知识)
3. 从服务器接收到请求到对应后台接收到请求(这一部分可能涉及到负载均衡,安全拦截以及后台内部的处理等等)
4. 后台和前台的http交互(这一部分包括http头部、响应码、报文结构、cookie等知识,可以提下静态资源的cookie优化,以及编码解码,如gzip压缩等)
5. 单独拎出来的缓存问题,http的缓存(这部分包括http缓存头部,etag,catch-control等)
6. 浏览器接收到http数据包后的解析流程(解析html-词法分析然后解析成dom树、解析css生成css规则树、合并成render树,然后layout、painting渲染、复合图层的合成、GPU绘制、外链资源的处理、loaded和domcontentloaded等)
7. CSS的可视化格式模型(元素的渲染规则,如包含块,控制框,BFC,IFC等概念)
8. JS引擎解析过程(JS的解释阶段,预处理阶段,执行阶段生成执行上下文,VO,作用域链、回收机制等等)
9. 其它(可以拓展不同的知识模块,如跨域,web安全,hybrid模式等等内容)
参考: 从输入URL到页面加载的过程?如何由一道题完善自己的前端知识体系!
7. 浏览器的重排和重绘
1、重排
当渲染树的一部分必须更新并且节点的尺寸发生了变化,浏览器会使渲染树中受到影响的部分失效,并重新构造渲染树。
2、重绘
是在一个元素的外观被改变所触发的浏览器行为,浏览器会根据元素的新属性重新绘制,使元素呈现新的外观。
比如改变某个元素的背景色、文字颜色、边框颜色等等
3、引发重排
(1)添加、删除可见的dom
(2)元素的位置、尺寸改变
(3)页面渲染初始化
(4)浏览器窗口尺寸改变
(5)获取某些属性。当获取一些属性时,浏览器为取得正确的值也会触发重排,它会导致队列刷新,
这些属性包括:offsetTop、offsetLeft、offsetWidth、offsetHeight、scrollTop、scrollLeft、
scrollWidth、scrollHeight、clientTop、clientLeft、clientWidth、clientHeight、
getComputedStyle() (currentStyle in IE)。所以,在多次使用这些值时应进行缓存。
4、相关优化:
浏览器自己的优化:
浏览器会维护1个队列,把所有会引起重排,重绘的操作放入这个队列,等队列中的操作到一定数量或者到了一定时间间隔,浏览器就会flush队列,进行一批处理,这样多次重排,重绘变成一次重排重绘
减少 reflow/repaint:
(1)不要逐条条地修改 DOM 的样式。可以先定义好 css 的 class,然后修改 DOM 的 className。
(2)不要把 DOM 结点的属性值放在一个循环里当成循环里的变量。
(3)为动画的 HTML 元件使用 fixed 或 absoult 的 position,那么修改他们的 CSS 是不会 reflow 的。
(4)千万不要使用 table 布局。因为可能很小的一个小改动会造成整个 table 的重新布局。
(5)不要在布局信息改变的时候做查询(会导致渲染队列强制刷新)
9. HTTP缓存
参考: 彻底弄懂HTTP缓存机制及原理
10. http这一层有什么方法做优化
参考: HTTP协议优化措施
11. http、https和http2的区别
12. 什么是js闭包
参考: JS闭包的理解及常见应用场景
13. js内存回收机制
现在各大浏览器通常用采用的垃圾回收有两种方法:引用计数、标记清除。
1、引用计数
// 创建一个obj对象,由变量 obj 指向这个对象的两个属性
var obj = {
name: 'kim',
sex: 'man'
};
// name虽然设置为了null,但obj依旧有name属性的引用
o.name = null;
var s = o;
// 我们修改并释放了o对于对象的引用,但变量s依旧存在引用
o = null;
// 变量s也不再引用,对象很快会被垃圾回收器释放
s = null;
2、标记清除
这个算法把“对象是否不再需要”简化定义为“对象是否可以获得” 这个算法假定一个全局对象,然后定期的垃圾回收机制从全局开始,找所有从全局开始引用的对象,然后找这些对象引用的对象。这样就能找到所有可以获得的对象和不能获得的对象。 现代浏览器都使用了这个回收机制
14. js原型链相关
参考: js原型和原型链
15. js作用域相关
参考: 详解作用域
16. es6知识相关
参考: 前端面试题整理—ES6篇
17. webpack打包相关
参考: 关于webpack的面试题
18. 网站攻击&防御
SQL注入:对用户输入的信息进行校验,可通过正则、限制字符长度、对单引号和 双"-"进行转换等。
其他知名公司
1. 如何发起一个POST请求
var httpRequest = new XMLHttpRequest();//第一步:创建需要的对象
httpRequest.open('POST', 'url', true); //第二步:打开连接
httpRequest.setRequestHeader("Content-type","application/x-www-form-urlencoded");//设置请求头 注:post方式必须设置请求头(在建立连接后设置请求头)
httpRequest.send('name=teswe&ee=ef');//发送请求 将情头体写在send中
/**
* 获取数据后的处理程序
*/
httpRequest.onreadystatechange = function () {//请求后的回调接口,可将请求成功后要执行的程序写在其中
if (httpRequest.readyState == 4 && httpRequest.status == 200) {//验证请求是否发送成功
var json = httpRequest.responseText;//获取到服务端返回的数据
console.log(json);
}
};
2. 浏览器的缓存怎么控制(重复)
3. VUE组件通信各种方法,为什么用vuex,优缺点
4. 让自定义组件支持v-model
5. Webpack单独各项配置,从开发到部署发布
6. promise怎么创建,哪些方法,怎么实现支持链式调用、
7. 网络优化、性能优化
8. 重绘,重排
9. 加密请求数据
10. 后退页面保持原来状态
11. 网络请求优化
12. 代码性能优化
13. xss攻击防御
14. 适配移动端
15. 单独webpack配置各项作用
16. map,reduce
17. vue路由实现
18. 优化~事件代理实现
19. 原生JS应用
20. 原型以及原型链
1、如构造函数Person和实例p1:Person的prototype指向实例原型Person.prototype;
p1的原型__proto__指向实力原型Person.prototype;
2、当访问对象的一个属性或方法时,会先在自身寻找,若自身无该属性,就会去原型找,若再没有就再从原型找,直到Object对象的原型。
21. 继承
function Person(name) {
this.name = name;
this.show = function() {
return 'my name is ' + this.name;
}
}
Person.prototype.age = 18;
//组合继承
function p1(name) {
Person.call(this, name);
}
p1.prototype = new Person();
var person1 = new p1('hejian');
console.log(person1);
console.log(person1.age);
console.log(person1.show());
//寄生组合继承
function p2() {};
p2.prototype = Person.prototype;
function sub(name) {
Person.call(this, name);
}
sub.prototype = new p2();
var person2 = new sub('hejian2');
console.log(person2);
console.log(person2.age);
22. 闭包
//当函数可以记住并访问所在的词法作用域时,就产生了闭包
function bibao(){
var name = 'hejian';
function show(){
return console.log(name);
}
return show;
}
var n1 = bibao();
n1();
23. 函数作用域
//内部环境可以访问外部变量,但是外部变量不能访问内部变量。
var x = 10;
function fn(){
console.log(x);
}
function show(f){
var x = 20;
(function(){
f(); // 10
})()
}
show(fn);
24. 环境上下文
1、 单线程
2、 同步执行,只有栈顶的上下文处于执行中,其他上下文需要等待
3、 全局上下文只有唯一的一个,它在浏览器关闭时出栈
4、 函数的执行上下文的个数没有限制
5、 每次某个函数被调用,就会有个新的执行上下文为其创建,即使是调用的自身函数,也是如此。
25. 隐式转换
// 复杂数据类型在隐式转换时,
// 会先转成string(先调用valueOf方法获取原始值,再调用toString转为字符串),
// 再转(Number转成数字)成number
var a = {
i: 0,
valueOf: function() {
return ++a.i;
}
}
if(a == 1 && a == 2 && a == 3) {
console.log(1);
}
26. 函数式编程
27. vue框架和react框架对比
1、 react基于函数式编程思想开发,他的