前端面试锦集
网友:hs
文档中心
2022-04-27 11:58:27
基本面试: 做过最满意的项目是什么?项目背景?为什么要做这件事情?最终达到什么效果?你处于什么样的角色,起到了什么方面的作用?在项目中遇到什么技术问题?具体是如何解决的? 如果再做这个项目,你会在哪些方面进行改善? 基础扎实: IT行业,哪些发展好的同学都是具备扎实基础知识,如果理解计算机基础会更好,因为我们面临很多非前端计算问题的。 主动思考: 被动完成任务成长会很慢的,需要有自己的想法,而不是仅仅是完成任务的。 自动学习: 前端领域知识淘汰速度很快,需要经常学习新知识。 追溯深度: 遇到问题是多研究背后深层次的原因,而不是绕过去。如遇到一个bug,有时间一定要理解本质原因。 宽阔视野: 创新往往来自不同领域的交集,如果理解更多领域,就会有更多的想法。 学习总结: 谈到某个技术的时候,需扪心自问,学这个是为了做什么?如何学习的?可以从什么渠道了解最新的知识?
目录概览
HTML
1.Doctype
2.对语义化的理解
3.link和@import引入css样式区别
4.HTML5新特性
4.1常见语义化标签
4.2新增表单元素
4.3新增视频 和音频 标签
CSS
1.css水平、垂直居中的写法,请至少写出4种?
2.文本超出省略号表示
3.让图文不可复制
4.行内元素有哪些?块级元素有哪些? 空(void)元素有那些?
5.画一条0.5px的直线
6.怎样处理 移动端 1px 被渲染成 2px 问题?
7.盒子模型
8.flex自适应布局
9.清除浮动的几种方式,及原理?
10.1rem、1em、1vh、1px各自代表的含义?
11.消除 transition 闪屏
12.css3 有哪些新的属性
13.什么是CSS 预处理器 / 后处理器?大家为什么要使用他们?
14.CSS优化、提高性能的方法有哪些?
15.可能用到的meta标签
16.消除 transition 闪屏
JavaScript
推荐的JavaScript经典面试题
1.根据你的理解,请简述JavaScript脚本的执行原理?
2.JS代码规范
3.数据类型
4.用js递归的方式写1到100求和
5.数组去重
6.对Function与Object的理解
7.什么是闭包
8.3==true 打印出什么
9.判断变量是不是数组的几个方法
10.this
11.拷贝
12.Promise
13.Var 、Let 和 const 的区别。
14.[对js垃圾回收机制的理解](https://blog.csdn.net/weixin_45788691/article/details/108381897)
15.js面向对象中继承实现
Ⅰ.原型链(prototype chaining)
Ⅱ.call()/apply()
Ⅲ.混合方式(prototype和call()/apply()结合)
Ⅳ .对象冒充
非技术型
1.get、post的区别
2.如何中断ajax请求?
3.三次握手
4.四次挥手
5.从输入url到页面加载完成发生了什么?——前端角度
6.你所知道的http的响应码及含义?
7.描述一下cookies,sessionStorage和localStorage的区别?
8.怎么让Chrome支持小于12px 的文字?
框架通识(vue、uniapp)
网络相关
1.AJAX的工作原理
2.强缓存
3.协商缓存
4.选择合适的缓存策略
5.预加载
6.预渲染
性能优化
1.前端性能优化的七大手段
2.防抖
3.节流
4.优化渲染过程
5.文件优化
5.1图片优化:
5.2图片加载优化:
5.3选择正确的图片格式:
6.其他文件优化
7.CDN
8.使用 Webpack 优化项目
9.监控
10.如何渲染几万条数据并不卡住界面
前端工程化
前端面试中,回答不出的那些问题
前端面试官的套路,你懂吗?
前端面试常考的手写代码不是背出来的!
常规交谈
HTML
1.Doctype
告知浏览器解析器用什么文档标准解析这个文档
2.对语义化的理解
title,header,nav,main,article,h1~h6,ul,ol,address,canvas,dialog,aside,section,figure,details,mark
代码结构清晰,易于阅读,利于开发和维护
提高体验,在样式加载失败时,页面结构清晰
方便其他设备解析(如屏幕阅读器)根据语义渲染网页
有利于搜索引擎优化(SEO),搜索引擎爬虫会根据不同的标签来赋予不同的权重
3.link和@import引入css样式区别
1 属性差别。link属于XHTML 标签,而@import完全是CSS 提供的语法规则。 2 加载顺序的差别。当一个页面被加载的时候(就是被浏览者浏览的时候),link引用的CSS会同时被加载,而@import引用的CSS会等到页面全部被下载完再被加载。所以有时候浏览@import加载CSS的页面时开始会没有样式(就是闪烁) 3 兼容性的差别。由于@import是CSS2.1提出的所以老的浏览器不支持,@import只有在IE5以上的才能识别,而link标签无此问题。
4.HTML5新特性
4.1常见语义化标签
定义了文档的尾部区域
定义文档的导航
定义文档中的节(section、区段)
定义页面独立的内容区域
定义页面的侧边栏内容 定义对话框,比如提示框
4.2新增表单元素
元素规定输入域的选项列表,使用 元素的 list 属性与 元素的 id 绑定 提供一种验证用户的可靠方法,标签规定用于表单的密钥对生成器字段。 用于不同类型的输出
4.2.1新增表单属性
placehoder 属性,简短的提示在用户输入值前会显示在输入域上。即我们常见的输入框默认提示,在用户输入后消失。
required 属性,是一个 boolean 属性。要求填写的输入域不能为空
pattern 属性,描述了一个正则表达式用于验证 元素的值。
min 和 max 属性,设置元素最小值与最大值。
step 属性,为输入域规定合法的数字间隔。
height 和 width 属性,用于 image 类型的 标签的图像高度和宽度。
autofocus 属性,是一个 boolean 属性。规定在页面加载时,域自动地获得焦点。
multiple 属性 ,是一个 boolean 属性。规定 元素中可选择多个值。
4.3新增视频 和音频 标签
Canvas绘图
SVG绘图
地理定位
拖放API
Web Worker
Web Storage
WebSocket
CSS
史上最全的CSS经典面试题
1.css水平、垂直居中的写法,请至少写出4种?
水平居中
text-align : center; margin : 0 auto; position : absolute +left : 50%+ transform : translateX ( -50%) display : flex + justify-content : center
垂直居中
设置line-height 等于heightposition:absolute +top : 50%+ transform : translateY ( -50%) display : flex + align-items : centerdisplay : table+display : table-cell + vertical-align : middle;
2.文本超出省略号表示
单行文本
.xxx { width : 300px; overflow : hidden; text-overflow : ellipsis; white-space : nowrap; }
多行文本
.xxx { display : -webkit-box; word-break : break-all; -webkit-box-orient : vertical; -webkit-line-clamp : 4; overflow : hidden; text-overflow : ellipsis; }
3.让图文不可复制
-webkit-user-select : none; -ms-user-select : none; -moz-user-select : none; -khtml-user-select : none; user-select : none;
4.行内元素有哪些?块级元素有哪些? 空(void)元素有那些?
行内元素:a、b、span、img、input、strong、select、label、em、button、textarea 块级元素:div、ul、li、dl、dt、dd、p、h1-h6、blockquote 空元素:即系没有内容的HTML元素,例如:br、meta、hr、link、input、img
5.画一条0.5px的直线
height : 1px; transform : scale ( 0.5) ;
6.怎样处理 移动端 1px 被渲染成 2px 问题?
1、局部处理
meta 标签中的 viewport 属性 ,initial-scale 设置为 1
rem 按照设计稿标准走,外加利用 transfrome 的 scale(0.5) 缩小一倍即可;
2、全局处理
meta 标签中的 viewport 属性 ,initial-scale 设置为 0.5
rem 按照设计稿标准走即可
7.盒子模型
box-sizing: content-box(W3C盒子模型):元素的宽高大小表现为内容的大小。
box-sizing: border-box(IE盒子模型):元素的宽高表现为内容 + 内边距 + 边框的大小。背景会延伸到边框的外沿。
8.flex自适应布局
flex-direction : row; flex-wrap : wrap; flex-flow : || ; justify-content : space-around; align-items : center; align-content : center; align-self : center;
9.清除浮动的几种方式,及原理?
浮动元素碰到包含它的边框或者浮动元素的边框停留。由于浮动元素不在文档流中,所以文档流的块框表现得就像浮动框不存在一样,浮动元素会漂浮在文档流的块框上. (1)父级div定义height。 (2)结尾处加空div标签clear:both。 (3)父级div定义伪类:after和zoom。 (4)父级div定义overflow:hidden。 (5)父级div定义overflow:auto。 (6)父级div也浮动,需要定义宽度。 (7)父级div定义display:table。 (8)结尾处加br标签clear:both。 (Q2)比较好的是第3种方式,好多网站都这么用 父级添加overflow属性(父元素添加overflow:hidden)
.fahter { width : 400px; border : 1px solid deeppink; overflow : hidden; }
使用after伪元素清除浮动(推荐使用)
.clearfix:after { content : "" ; display : block; height : 0; clear : both; visibility : hidden; } .clearfix { *zoom : 1; } <div class="fahter clearfix" > <div class="big" >big
<div class=
"small" >small
<!--<div class=
"clear" >额外标签法
-->
<div class=
"footer" >
优点:符合闭合浮动思想,结构语义化正确 缺点:ie6-7不支持伪元素:after,使用zoom:1触发hasLayout. 使用before和after双伪元素清除浮动
.clearfix:after,.clearfix:before { content : "" ; display : table; } .clearfix:after { clear : both; } .clearfix { *zoom : 1; } <div class="fahter clearfix" > <div class="big" >big
<div class="small" >small <div class="footer" >
优点:代码更简洁 缺点:用zoom:1触发hasLayout.
10.1rem、1em、1vh、1px各自代表的含义?
rem是全部的长度都相对于根元素元素。通常做法是给html元素设置一个字体大小,然后其他元素的长度单位就为rem。
子元素字体大小的em是相对于父元素字体大小,元素的width/height/padding/margin用em的话是相对于该元素的font-size。
vw/vh视窗的宽度和高度,相当于 屏幕宽度和高度的 1%,不过,处理宽度的时候%单位更合适,处理高度的 话 vh 单位更好。
px像素(Pixel)。相对长度单位。像素px是相对于显示器屏幕分辨率而言的。
一般电脑的分辨率有{19201024}等不同的分辨率,1920 1024 前者是屏幕宽度总共有1920个像素,后者则是高度为1024个像素
11.消除 transition 闪屏
.css { -webkit-transform-style : preserve-3d; -webkit-backface-visibility : hidden; -webkit-perspective : 1000; }
过渡动画(在没有启动硬件加速的情况下)会出现抖动的现象, 以上的解决方案只是改变视角来启动硬件加速的一种方式;启动硬件加速的另外一种方式:
.css { -webkit-transform : translate3d ( 0,0,0) ; -moz-transform : translate3d ( 0,0,0) ; -ms-transform : translate3d ( 0,0,0) ; transform : translate3d ( 0,0,0) ; }
启动硬件加速 最常用的方式:translate3d、translateZ、transform opacity 属性/过渡动画(需要动画执行的过程中才会创建合成层,动画没有开始或结束后元素还会回到之前的状态) will-chang 属性(这个比较偏僻),一般配合opacity与translate使用(而且经测试,除了上述可以引发硬件加速的属性外,其它属性并不会变成复合层)。 弊端:硬件加速会导致 CPU 性能占用量过大,电池电量消耗加大 ;因此尽量避免泛滥使用硬件加速。
12.css3 有哪些新的属性
圆角–border-radius
阴影–box-shadow
文字特效–text-shadow
渐变–gradient
旋转–transform(rotate:旋转、scale:缩放、translate:定位、skew:倾斜)
多背景
rgba
边框背景–border-image
服务器端字体:font-face
@font-face { font-family : 'MyFont' ; src : url('myfont.eot') ; src : local ( 'myfont.ttf' ) , url('myfont.woff') format ( 'woff' ) , url('myfont.ttf') format ( 'truetype' ) ; } h2 { font-family : "MyFont" ; }
13.什么是CSS 预处理器 / 后处理器?大家为什么要使用他们?
CSS 预处理器为 CSS 增加一些编程的特性,无需考虑浏览器的兼容性问题 ”,例如你可以在 CSS 中使用变量、简单的逻辑程序、函数 (如右侧代码编辑器中就使用了变量$color)等等在编程语言中的一些基本特性,可以让你的 CSS 更加简洁、适应性更强、可读性更佳,更易于代码的维护等诸多好处。 结构清晰,便于扩展。可以方便地屏蔽浏览器私有语法差异。封装对浏览器语法差异的重复处理,减少无意义的机械劳动。可以轻松实现多重继承。完全兼容 CSS 代码,可以方便地应用到老项目中,LESS 只是在 CSS 语法上做了扩展,所以老的 CSS 代码也可以与 LESS 代码一同编译。
预处理器例如:LESS、Sass、Stylus,用来预编译Sass或less,增强了css代码的复用性,还有层级、mixin、变量、循环、函数等,具有很方便的UI组件模块化开发能力,极大的提高工作效率。 后处理器例如:PostCSS,通常被视为在完成的样式表中根据CSS规范处理CSS,让其更有效;目前最常做的是给CSS属性添加浏览器私有前缀,实现跨浏览器兼容性的问题。
14.CSS优化、提高性能的方法有哪些?
避免过度约束
避免后代选择符
避免链式选择符使用紧凑的语法
避免不必要的命名空间
避免不必要的重复,最好使用表示语义的名字。一个好的类名应该是描述他是什么而不是像什么
避免!important,可选择其他选择器
尽可能的精简规则,你可以合并不同类里的重复规则
修复解析错误
避免使用多类选择符
移除空的css规则
正确使用display属性:由于display作用,某些样式组合会无效,徒增样式体积的同时也影响解析性能。 display:inline后不应该再使用width、height、margin、padding以及float。 display:inline-block后不应该再使用float。 display:block后不应该再使用vertical-align。 display:table-*后不应该再使用margin或者float。
不滥用浮动:虽然浮动不可避免,但不可否认很多css bug是由于浮动而引起。
不滥用web字体 对于中文网站来说Web Fonts可能很陌生,国外却很流行。web fonts通常体积庞大,而且一些浏览器在下载web fonts时会阻塞页面渲染损伤性能。
不声明过多的font-size:这是设计层面的问题,设计精良的页面不会有过多的font-size声明。
不在选择符中使用ID标识符,主要考虑到样式重用性以及与页面的耦合性。
不给h1~h6元素定义过多的样式
全站统一定义一遍heading元素即可,若需额外定制样式,可使用其他选择符作为代替。
值为0时不需要任何单位
使用CSS渐变等高级特性,需指定所有浏览器的前缀
避免让选择符看起来像正则表达式
CSS3添加了一些类似~=等复杂属性,也不是所有浏览器都支持,需谨慎使用。
遵守盒模型规则
15.可能用到的meta标签
< ! -- 设置缩放 --> < meta name= "viewport" content= "width=device-width, initial-scale=1, user-scalable=no, minimal-ui" /> < ! -- 可隐藏地址栏,仅针对IOS的Safari(注:IOS7.0版本以后,safari上已看不到效果) --> < meta name= "apple-mobile-web-app-capable" content= "yes" /> < ! -- 仅针对IOS的Safari顶端状态条的样式(可选default/black/black-translucent ) --> < meta name= "apple-mobile-web-app-status-bar-style" content= "black" /> < ! -- IOS中禁用将数字识别为电话号码/忽略Android平台中对邮箱地址的识别 --> < meta name= "format-detection" content= "telephone=no, email=no" /> < ! -- 启用360浏览器的极速模式( webkit) --> < meta name= "renderer" content= "webkit" > < ! -- 避免IE使用兼容模式 --> < meta http-equiv= "X-UA-Compatible" content= "IE=edge" > < ! -- 针对手持设备优化,主要是针对一些老的不识别viewport的浏览器,比如黑莓 --> < meta name= "HandheldFriendly" content= "true" > < ! -- 微软的老式浏览器 --> < meta name= "MobileOptimized" content= "320" > < ! -- uc强制竖屏 --> < meta name= "screen-orientation" content= "portrait" > < ! -- QQ强制竖屏 --> < meta name= "x5-orientation" content= "portrait" > < ! -- UC强制全屏 --> < meta name= "full-screen" content= "yes" > < ! -- QQ强制全屏 --> < meta name= "x5-fullscreen" content= "true" > < ! -- UC应用模式 --> < meta name= "browsermode" content= "application" > < ! -- QQ应用模式 --> < meta name= "x5-page-mode" content= "app" > < ! -- windows phone 点击无高光 --> < meta name= "msapplication-tap-highlight" content= "no" >
16.消除 transition 闪屏
.css { -webkit-transform-style : preserve-3d; -webkit-backface-visibility : hidden; -webkit-perspective : 1000; }
过渡动画(在没有启动硬件加速的情况下)会出现抖动的现象, 以上的解决方案只是改变视角来启动硬件加速的一种方式;启动硬件加速的另外一种方式:
.css { -webkit-transform : translate3d ( 0,0,0) ; -moz-transform : translate3d ( 0,0,0) ; -ms-transform : translate3d ( 0,0,0) ; transform : translate3d ( 0,0,0) ; }
启动硬件加速: 最常用的方式:translate3d、translateZ、transform opacity 属性/过渡动画(需要动画执行的过程中才会创建合成层,动画没有开始或结束后元素还会回到之前的状态) will-chang 属性(这个比较偏僻),一般配合opacity与translate使用(而且经测试,除了上述可以引发硬件加速的属性外,其它属性并不会变成复合层)。 弊端: 硬件加速会导致 CPU 性能占用量过大,电池电量消耗加大 ;因此尽量避免泛滥使用硬件加速。
JavaScript
推荐的JavaScript经典面试题
数据类型到一些隐式转换这些基础知识,看代码说输出,v8底层执行机制、垃圾回收、闭包、作用域、作用域链,原型、原型链,手写代码,如:防抖、节流、bind、call、apply、深拷贝、浅拷贝、Promise、async、await、webpack、框架、http等。
1.根据你的理解,请简述JavaScript脚本的执行原理?
JavaScript是一种动态、弱类型、基于原型的语言,通过浏览器可以直接执行,当浏览器遇到 标记的时候,浏览器会执行之间的javascript代码。嵌入的js代码是顺序执行的,每个脚本定义的全局变量和函数,都可以被后面执行的脚本所调用。 变量的调用,必须是前面已经声明,否则获取的变量值是undefined。
2.JS代码规范
变量名推荐使用驼峰法 来命名
通常运算符 ( = + - * / ) 前后需要添加空格
通常使用 4 个 空格符号来缩进代码块
一条语句通常以分号 作为结束符
对象中将左花括号与类名放在同一行、冒号与属性值间有个空格、字符串使用双引号,数字不需要、最后一个属性-值对后面不要添加逗号、将右花括号独立放在一行,并以分号作为结束符号。
每行代码字符小于 80 ,如果一个 JavaScript 语句超过了 80 个字符,建议在 运算符或者逗号后换行。
产品线公用全局变量
全局变量使用"g_ "打头,建议通过window.g_xxx定义
常量名全部大写,单词间用下划线分隔。
减少全局函数,尽量使用对象
建议使用严格的条件判断符。如:=== 、 !==
if,else尽量使用{}括起来
3.数据类型
基本数据类型:string、number、null、undefined、boolean 引用数据类型:Object、Array、Date、RegExp 变量:var关键字来定义变量(ES5)、let命令来声明变量(ES6)、const命令声明一个只读的常量(ES6)
4.用js递归的方式写1到100求和
递归我们经常用到,vue在实现双向绑定进行数据检验的时候用的也是递归,但要我们面试的时候手写一个递归,如果对递归的概念理解不透彻,可能还是会有一些问题。
function add ( num1, num2) { var num = num1+ num2; if ( num2+ 1 > 100 ) { return num; } else { return add ( num, num2+ 1 ) } } var sum = add ( 1 , 2 ) ;
5.数组去重
此题看着简单,但要想面试官给你高分还是有难度的。至少也要写出几种方法
js:
var arr= [ '12' , '32' , '89' , '12' , '12' , '78' , '12' , '32' ] ; function unique1 ( array) { var n = [ ] ; for ( var i = 0 ; i < array. length; i++ ) { if ( n. indexOf ( array[ i] ) == - 1 ) n. push ( array[ i] ) ; } return n; } arr= unique1 ( arr) ; function unique2 ( array) { var n = { } , r = [ ] , type; for ( var i = 0 ; i < array. length; i++ ) { type = typeof array[ i] ; if ( ! n[ array[ i] ] ) { n[ array[ i] ] = [ type] ; r. push ( array[ i] ) ; } else if ( n[ array[ i] ] . indexOf ( type) < 0 ) { n[ array[ i] ] . push ( type) ; r. push ( array[ i] ) ; } } return r; } function unique3 ( array) { var n = [ array[ 0 ] ] ; for ( var i = 1 ; i < array. length; i++ ) { if ( array. indexOf ( array[ i] ) == i) n. push ( array[ i] ) ; } return n; }
es6:
arr= [ ... new Set ( arr) ] ; function dedupe ( array) { return Array. from ( new Set ( array) ) ; }
6.对Function与Object的理解
Function 函数就是对象,代表函数的对象就是函数对象。所有的函数对象是被Function这个函数对象构造出来的。Function是最顶层的构造器。它构造了系统中所有的对象,包括用户自定义对象,系统内置对象,甚至包括它自已。这也表明Function具有自举性(自已构造自己的能力)。这也间接决定了Function的call和constructor逻辑相同。每个对象都有一个constructor 属性,用于指向创建其的函数对象。 a、函数与对象具有相同的语言地位 b、没有类,只有对象 c、函数也是一种对象,所谓的函数对象 d、对象是按引用来传递的 Object 对于Object它是最顶层的对象,所有的对象都将继承Object的原型,但是你也要明确的知道Object也是一个函数对象,所以说Object是被Function构造出来的。
function Function ( ) { } var a = new Object ( function ( ) { } ) ;
7.什么是闭包
闭包是由函数与创建该函数的环境所组成的
优点:减少全局变量污染,希望一个变量长期存储在内存中(缓存变量)
缺点:影响脚本性能,常驻内存,增加内存使用量
8.3==true 打印出什么
会打印出false,这里会将true转变成1
9.判断变量是不是数组的几个方法
var a= [ ] ; a. constructor=== Array a instanceof Array === true ⚠️ 注意:以上方法在跨frame时会有问题,跨frame实例化的对象不共享原型var iframe = document. createElement ( 'iframe' ) ; document. body. appendChild ( iframe) ; xArray = window. frames[ window. frames. length- 1 ] . Array; var arr = new xArray ( 1 , 2 , 3 ) ; alert ( arr instanceof Array ) ; alert ( arr. constructor === Array) ; 解决:Object. prototype. toString. call ( a) Array. isArray ( a)
10.this
function foo ( ) { console. log ( this . a) } var a = 1 foo ( ) var obj = { a: 2 , foo: foo} obj. foo ( ) var c = new foo ( ) c. a = 3 console. log ( c. a)
function a ( ) { return ( ) => { return ( ) => { console. log ( this ) } } } console. log ( a ( ) ( ) ( ) )
箭头函数其实是没有 this 的,这个函数中的 this 只取决于他外面的第一个不是箭头函数的函数的 this。在这个例子中,因为调用 a 符合前面代码中的第一个情况,所以 this 是 window。并且 this 一旦绑定了上下文,就不会被任何代码改变。
11.拷贝
浅拷贝只复制指向某个对象的指针,而不复制对象本身,新旧对象还是共享同一块内存。 深拷贝会另外创造一个一模一样的对象,新对象跟原对象不共享内存,修改新对象不会改到原对象。 具体在业务中使用哪个,还得根据自己的业务需求。当然,深拷贝的方法也有很多个,这里只列出常用的一种方法,JSON.parse(JSON.stringify())
浅拷贝:定义了一个obj对象,和一个newObj对象,并让newObj的person等于指向obj对象(实则是将newObj的person属性指向obj对象的指针),所以改变newObj.person.name的值,实则是改变obj.name的值。
var obj = { id: 123 , name: "小三" , address: "china" } var newObj = { } newObj. person = obj; newObj. person. name = "小三" console. log ( obj. name) ;
深拷贝 :定义obj对象和newObj对象,在给newObje对象的person属性赋值的时候,我们用JSON.parse(JSON.stringify(obj))方法将obj深拷贝了一份,也就是说重新申请了一个空间,与原对象不共享内存,属于完全独立的存在,所以在改变newObj.person.name属性之后,obj.name是不会跟着发生改变的。
var obj = { id: 123 , name: "小三" , address: "china" } var newObj = { } newObj. person = JSON . parse ( JSON . stringify ( obj) ) ; newObj. person. name= "小三" console. log ( obj. name) ;
12.Promise
Promise 是 ES6 新增的语法,解决了回调地狱的问题。 可以把 Promise 看成一个状态机。初始是 pending 状态,可以通过函数 resolve 和 reject ,将状态转变为 resolved 或者 rejected 状态,状态一旦改变就不能再次变化。
then 函数会返回一个 Promise 实例,并且该返回值是一个新的实例而不是之前的实例。因为 Promise 规范规定除了 pending 状态,其他状态是不可以改变的,如果返回的是一个相同实例的话,多个 then 调用就失去意义了。 对于 then 来说,本质上可以把它看成是 flatMap
const PENDING = "pending" ; const RESOLVED = "resolved" ; const REJECTED = "rejected" ; function MyPromise ( fn) { let _this = this ; _this. currentState = PENDING ; _this. value = undefined; _this. resolvedCallbacks = [ ] ; _this. rejectedCallbacks = [ ] ; _this. resolve = function ( value) { if ( value instanceof MyPromise ) { return value. then ( _this. resolve, _this. reject) } setTimeout ( ( ) => { if ( _this. currentState === PENDING ) { _this. currentState = RESOLVED ; _this. value = value; _this. resolvedCallbacks. forEach ( cb => cb ( ) ) ; } } ) } ; _this. reject = function ( reason) { setTimeout ( ( ) => { if ( _this. currentState === PENDING ) { _this. currentState = REJECTED ; _this. value = reason; _this. rejectedCallbacks. forEach ( cb => cb ( ) ) ; } } ) } try { fn ( _this. resolve, _this. reject) ; } catch ( e ) { _this. reject ( e) ; } } MyPromise. prototype. then = function ( onResolved, onRejected) { var self = this ; var promise2; onResolved = typeof onResolved === 'function' ? onResolved : v => v; onRejected = typeof onRejected === 'function' ? onRejected : r => throw r; if ( self. currentState === RESOLVED ) { return ( promise2 = new MyPromise ( function ( resolve, reject) { setTimeout ( function ( ) { try { var x = onResolved ( self. value) ; resolutionProcedure ( promise2, x, resolve, reject) ; } catch ( reason ) { reject ( reason) ; } } ) ; } ) ) ; } if ( self. currentState === REJECTED ) { return ( promise2 = new MyPromise ( function ( resolve, reject) { setTimeout ( function ( ) { try { var x = onRejected ( self. value) ; resolutionProcedure ( promise2, x, resolve, reject) ; } catch ( reason ) { reject ( reason) ; } } ) ; } ) ) ;
if ( self. currentState === PENDING ) { return ( promise2 = new MyPromise ( function ( resolve, reject) { self. resolvedCallbacks. push ( function ( ) { try { var x = onResolved ( self. value) ; resolutionProcedure ( promise2, x, resolve, reject) ; } catch ( r ) { reject ( r) ; } } ) ; self. rejectedCallbacks. push ( function ( ) { try { var x = onRejected ( self. value) ; resolutionProcedure ( promise2, x, resolve, reject) ; } catch ( r ) { reject ( r) ; } } ) ; } ) ) ; } } ; function resolutionProcedure ( promise2, x, resolve, reject) { if ( promise2 === x) { return reject ( new TypeError ( "Error" ) ) ; } if ( x instanceof MyPromise ) { if ( x. currentState === PENDING ) { x. then ( function ( value) { resolutionProcedure ( promise2, value, resolve, reject) ; } , reject) ; } else { x. then ( resolve, reject) ; } return ; } let called = false ; if ( x !== null && ( typeof x === "object" || typeof x === "function" ) ) { try { let then = x. then; if ( typeof then === "function" ) { then. call ( x, y => { if ( called) return ; called = true ; resolutionProcedure ( promise2, y, resolve, reject) ; } , e => { if ( called) return ; called = true ; reject ( e) ; } ) ; } else { resolve ( x) ; } } catch ( e ) { if ( called) return ; called = true ; reject ( e) ; } } else { resolve ( x) ; } }
以上就是根据 Promise / A+ 规范来实现的代码,可以通过 promises-aplus-tests 的完整测试
13.Var 、Let 和 const 的区别。
var 声明的变量会挂载在 window 上,而 let 和 const 声明的变量不会:var 声明变量存在变量提升,let 和 const 不存在变量提升let 和 const 声明形成块作用域同一作用域下 let 和 const 不能声明同名变量,而 var 可以 Const 1、一旦声明必须赋值,不能使用 null 占位。2、声明后不能再修改 3、如果声明的是复合类型数据,可以修改其属性
14.对js垃圾回收机制的理解
15.js面向对象中继承实现
面向对象的基本特征有:封闭、继承、多态。
Ⅰ.原型链(prototype chaining)
function car ( price) { this . price = price; } car. prototype. sayPrice = function ( ) { console. log ( "Price is " + this . price) ; } var oCar = new car ( "100W" ) ; oCar. sayPrice ( ) ; function toyCar ( price) { this . price = price; } toyCar. prototype = new car ( ) var oCar2 = new toyCar ( "10CNY" ) ; oCar2. sayPrice ( ) ;
Ⅱ.call()/apply()
function useCall ( a, b) { this . a = a; this . b = b; this . say = function ( ) { console. log ( "I'm " + this . a+ " You're " + this . b) ; } } function callThefunction ( ) { var args = arguments; useCall. call ( this , args[ 0 ] , args[ 1 ] ) ; } var testCall1 = new useCall ( "Not YY" , "Not TT" ) ; testCall1. say ( ) ; var testCall2 = new callThefunction ( "YY" , "TT" ) ; testCall2. say ( ) ;
Ⅲ.混合方式(prototype和call()/apply()结合)
function house ( size, price) { this . size = size; this . price = price; } house. prototype. showArea = function ( ) { console. log ( "面积为" + this . size) ; } house. prototype. sayPrice = function ( ) { console. log ( "价钱为" + this . price) ; } function maofan ( size, price) { house. call ( this , size, price) ; } maofan. prototype = new house ( ) ; var newmaofan = new maofan ( "20Square meters " , "1000CNY" ) ; newmaofan. showArea ( ) ;
Ⅳ .对象冒充
function Person ( name, age) { this . name = name; this . age = age; this . show = function ( ) { console. log ( this . name+ ", " + this . age) ; } } Person. prototype. sayHi = function ( ) { alert ( 'hi' ) ; } function Student ( name, age) { this . student = Person; this . student ( name, age) ; delete this . student; } var s = new Student ( "小明" , 17 ) ; s. show ( ) ; var p = new Person ( "小花" , 18 ) ; p. show ( ) ;
非技术型
1.get、post的区别
get传参方式是通过地址栏URL传递,是可以直接看到get传递的参数,post传参方式参数URL不可见,get把请求的数据在URL后通过?连接,通过&进行参数分割。psot将参数存放在HTTP的包体内
get传递数据是通过URL进行传递,对传递的数据长度是受到URL大小的限制,URL最大长度是2048个字符。post没有长度限制
get后退不会有影响,post后退会重新进行提交
get请求可以被缓存,post不可以被缓存
get请求只URL编码,post支持多种编码方式
get请求的记录会留在历史记录中,post请求不会留在历史记录
get只支持ASCII字符,post没有字符类型限制
2.如何中断ajax请求?
一种是设置超时时间让ajax自动断开,另一种是手动停止ajax请求,其核心是调用XML对象的abort方法,ajax.abort()
3.三次握手
客户端发syn包给服务端,等待服务器确认(syn:同步序列编号(Synchronize Sequence Numbers))
服务端发syn+ack包给客户端
客户端发确认包ack给服务端
4.四次挥手
中断连接端可以是Client端,也可以是Server端。
关闭主动方发送fin包
被动方发送ack包
被动方关闭连接,发送fin包
主动方发送ack包确认
5.从输入url到页面加载完成发生了什么?——前端角度
浏览器的地址栏输入URL并按下回车。
浏览器查找当前URL是否存在缓存,并比较缓存是否过期。
DNS解析URL对应的IP。
根据IP建立TCP连接(三次握手)。
HTTP发起请求。
服务器处理请求,浏览器接收HTTP响应。
渲染页面,构建DOM树。
关闭TCP连接(四次挥手)。
6.你所知道的http的响应码及含义?
1xx(临时响应) 100: 请求者应当继续提出请求。 101(切换协议) 请求者已要求服务器切换协议,服务器已确认并准备进行切换。
2xx(成功) 200:正确的请求返回正确的结果 201:表示资源被正确的创建。比如说,我们 POST 用户名、密码正确创建了一个用户就可以返回 201。 202:请求是正确的,但是结果正在处理中,这时候客户端可以通过轮询等机制继续请求。
3xx(已重定向) 300:请求成功,但结果有多种选择。 301:请求成功,但是资源被永久转移。 303:使用 GET 来访问新的地址来获取资源。 304:请求的资源并没有被修改过
4xx(请求错误) 400:请求出现错误,比如请求头不对等。 401:没有提供认证信息。请求的时候没有带上 Token 等。 402:为以后需要所保留的状态码。 403:请求的资源不允许访问。就是说没有权限。 404:请求的内容不存在。
5xx(服务器错误) 500:服务器错误。 501:请求还没有被实现。
7.描述一下cookies,sessionStorage和localStorage的区别?
相同点:都会在浏览器端保存,有大小和同源限制。
8.怎么让Chrome支持小于12px 的文字?
做移动端的时候,设计师图片上的文字假如是10px,我们实现在网页上之后。往往设计师回来找我们,这个字体能小一些吗?我设计的是10px?为啥是12px?其实我们都知道,谷歌Chrome最小字体是12px,不管你设置成8px还是10px,在浏览器中只会显示12px,那么如何解决这个坑爹的问题呢?
针对谷歌浏览器内核,加webkit前缀,用transform:scale()这个属性进行缩放!
p span { font-size : 10px; -webkit-transform : scale ( 0.8) ; display : block; } haorooms博客测试10px
框架通识(vue、uniapp)
1.MVVM
View:界面
Model:数据模型
ViewModel:作为桥梁负责沟通 View 和 Model MVVM模式有几大好处: 1、低耦合。View可以独立于Model变化和修改,一个ViewModel可以绑定到不同的View上,当View变化的时候Model可以不变,当Model变化的时候View也可以不变。 2、可重用性。可以把一些视图的逻辑放在ViewModel里面,让很多View重用这段视图逻辑。 3、独立开发。开发人员可以专注与业务逻辑和数据的开发(ViewModel)。设计人员可以专注于界面(View)的设计。 4、可测试性。可以针对ViewModel来对界面(View)进行测试
2.路由原理
前端路由实现起来其实很简单,本质就是监听 URL 的变化,然后匹配路由规则,显示相应的页面,并且无须刷新。目前单页面使用的路由就只有两种实现方式
www.test.com/##/ 就是 Hash URL,当 ## 后面的哈希值发生变化时,不会向服务器请求数据,可以通过 hashchange 事件来监听到 URL 的变化,从而进行跳转页面。 History 模式是 HTML5 新推出的功能,比之 Hash URL 更加美观
3.脏数据检测
当触发了指定事件后会进入脏数据检测,这时会调用 $digest 循环遍历所有的数据观察者,判断当前值是否和先前的值有区别,如果检测到变化的话,会调用 $watch 函数,然后再次调用 $digest 循环直到发现没有变化。循环至少为二次 ,至多为十次。
脏数据检测虽然存在低效的问题,但是不关心数据是通过什么方式改变的,都可以完成任务,但是这在 Vue 中的双向绑定是存在问题的。并且脏数据检测可以实现批量检测出更新的值,再去统一更新 UI,大大减少了操作 DOM 的次数。所以低效也是相对的,这就仁者见仁智者见智了。
网络相关
1.AJAX的工作原理
ajax是一种通过后台与服务器进行少量的数据交换,使页面实现异步更新是一种创建交互式网页应用的网页开发技术。
1、创建ajax对象(XMLHttpRequest/ActiveXObject(Microsoft.XMLHttp)) 2、判断数据传输方式(GET/POST) 3、打开链接 open() 4、发送 send() 5、当ajax对象完成第四步(onreadystatechange)数据接收完成,判断http响应状态(status)200-300之间或者304(缓存)执行回调函数
异步请求响应快,用户体验好;页面无刷新、数据局部更新;按需取数据,减少了冗余请求和服务器的负担。 异步回调问题、this指向问题、路由跳转back问题;对搜索引擎的支持比较弱,对于一些手机还不是很好的支持
DNS 解析也是需要时间的,可以通过预解析的方式来预先获得域名所对应的 IP。
< link rel= "dns-prefetch" href= "//yuchengkai.cn" / >
缓存对于前端性能优化来说是个很重要的点,良好的缓存策略可以降低资源的重复加载提高网页的整体加载速度。 通常浏览器缓存策略分为两种:强缓存和协商缓存。
2.强缓存
实现强缓存可以通过两种响应头实现:Expires 和 Cache-Control 。强缓存表示在缓存期间不需要请求,state code 为 200。
Expires: Wed, 22 Oct 2018 08 : 41 : 00 GMT Cache- control: max- age= 30
3.协商缓存
如果缓存过期了,我们就可以使用协商缓存来解决问题。协商缓存需要请求,如果缓存有效会返回 304。 协商缓存需要客户端和服务端共同实现,和强缓存一样,也有两种实现方式。
Last-Modified 和 If-Modified-Since: Last-Modified 表示本地文件最后修改日期,If-Modified-Since 会将 Last-Modified 的值发送给服务器,询问服务器在该日期后资源是否有更新,有更新的话就会将新的资源发送回来。 但是如果在本地打开缓存文件,就会造成 Last-Modified 被修改,所以在 HTTP / 1.1 出现了 ETag 。
ETag 和 If-None-Match: ETag 类似于文件指纹,If-None-Match 会将当前 ETag 发送给服务器,询问该资源 ETag 是否变动,有变动的话就将新的资源发送回来。并且 ETag 优先级比 Last-Modified 高。
4.选择合适的缓存策略
对于大部分的场景都可以使用强缓存配合协商缓存解决,但是在一些特殊的地方可能需要选择特殊的缓存策略
对于某些不需要缓存的资源,可以使用 Cache-control: no-store ,表示该资源不需要缓存
对于频繁变动的资源,可以使用 Cache-Control: no-cache 并配合 ETag 使用,表示该资源已被缓存,但是每次都会发送请求询问资源是否更新。
对于代码文件来说,通常使用 Cache-Control: max-age=31536000, 并配合策略缓存使用,然后对文件进行指纹处理,一旦文件名变动就会立刻下载新的文件。
在开发中,可能会遇到这样的情况。有些资源不需要马上用到,但是希望尽早获取,这时候就可以使用预加载。
5.预加载
预加载其实是声明式的 fetch ,强制浏览器请求资源,并且不会阻塞 onload 事件,可以使用以下代码开启预加载.
< link rel= "preload" href= "http://example.com" / >
预加载可以一定程度上降低首屏的加载时间,因为可以将一些不影响首屏但重要的文件延后加载,唯一缺点就是兼容性不好。
6.预渲染
< link rel= "prerender" href= "http://example.com" / >
预渲染虽然可以提高页面的加载速度,但是要确保该页面百分百会被用户在之后打开,否则就白白浪费资源去渲染
性能优化
1.前端性能优化的七大手段
减少请求数量
减小资源大小
优化网络连接
优化资源加载
减少重绘回流
性能更好的API
webpack优化
日常开发中遇到滚动事件中需要做个复杂计算或者实现一个按钮的防二次点击操作。防抖和节流的作用都是防止函数多次调用。区别在于,假设一个用户一直触发这个函数,且每次触发函数的间隔小于wait,防抖的情况下只会调用一次,而节流的 情况会每隔一定时间(参数wait)调用函数。
2.防抖
function now ( ) { return + new Date ( ) } function debounce ( func, wait = 50 , immediate = true ) { let timer, context, args const later = ( ) => setTimeout ( ( ) => { timer = null if ( ! immediate) { func. apply ( context, args) context = args = null } } , wait) return function ( ... params) { if ( ! timer) { timer = later ( ) if ( immediate) { func. apply ( this , params) } else { context = this args = params } } else { clearTimeout ( timer) timer = later ( ) } } }
对于按钮防点击来说的实现:如果函数是立即执行的,就立即调用,如果函数是延迟执行的,就缓存上下文和参数,放到延迟函数中去执行。一旦我开始一个定时器,只要我定时器还在,你每次点击我都重新计时。一旦你点累了,定时器时间到,定时器重置为 null,就可以再次点击了。对于延时执行函数来说的实现:清除定时器ID,如果是延迟调用就调用函数。
3.节流
防抖动和节流本质是不一样的。防抖动是将多次执行变为最后一次执行,节流是将多次执行变成每隔一段时间执行。
_. throttle = function ( func, wait, options) { var context, args, result; var timeout = null ; var previous = 0 ; if ( ! options) options = { } ; var later = function ( ) { previous = options. leading === false ? 0 : _. now ( ) ; timeout = null ; result = func. apply ( context, args) ; if ( ! timeout) context = args = null ; } ; return function ( ) { var now = _. now ( ) ; if ( ! previous && options. leading === false ) previous = now; var remaining = wait - ( now - previous) ; context = this ; args = arguments; if ( remaining <= 0 || remaining > wait) { if ( timeout) { clearTimeout ( timeout) ; timeout = null ; } previous = now; result = func. apply ( context, args) ; if ( ! timeout) context = args = null ; } else if ( ! timeout && options. trailing !== false ) { timeout = setTimeout ( later, remaining) ; } return result; } ; } ;
4.优化渲染过程
4.1懒执行
懒执行就是将某些逻辑延迟到使用时再计算。该技术可以用于首屏优化,对于某些耗时逻辑并不需要在首屏就使用的,就可以使用懒执行。懒执行需要唤醒,一般可以通过定时器或者事件的调用来唤醒。
4.2懒加载
懒加载就是将不关键的资源延后加载。 懒加载的原理就是只加载自定义区域(通常是可视区域,但也可以是即将进入可视区域)内需要加载的东西。对于图片来说,先设置图片标签的 src 属性为一张占位图,将真实的图片资源放入一个自定义属性中,当进入自定义区域时,就将自定义属性替换为 src 属性,这样图片就会去下载资源,实现了图片懒加载。 懒加载不仅可以用于图片,也可以使用在别的资源上。比如进入可视区域才开始播放视频等等。
5.文件优化
5.1图片优化:
减少像素点、减少每个像素点能够显示的颜色
5.2图片加载优化:
不用图片。很多时候会使用到很多修饰类图片,其实这类修饰图片完全可以用 CSS 去代替。
对于移动端来说,屏幕宽度就那么点,完全没有必要去加载原图浪费带宽。一般图片都用 CDN 加载,可以计算出适配屏幕的宽度,然后去请求相应裁剪好的图片。
小图使用 base64 格式
将多个图标文件整合到一张图片中(雪碧图)
5.3选择正确的图片格式:
对于能够显示 WebP 格式的浏览器尽量使用 WebP 格式。因为 WebP 格式具有更好的图像数据压缩算法,能带来更小的图片体积,而且拥有肉眼识别无差异的图像质量,缺点就是兼容性并不好
小图使用 PNG,其实对于大部分图标这类图片,完全可以使用 SVG 代替
照片使用 JPEG
6.其他文件优化
CSS 文件放在 head 中
服务端开启文件压缩功能
将 script 标签放在 body 底部,因为 JS 文件执行会阻塞渲染。当然也可以把 script 标签放在任意位置然后加上 defer ,表示该文件会并行下载,但是会放到 HTML 解析完成后顺序执行。对于没有任何依赖的 JS 文件可以加上 async ,表示加载和渲染后续文档元素的过程将和 JS 文件的加载与执行并行无序进行。
执行 JS 代码过长会卡住渲染,对于需要很多时间计算的代码可以考虑使用 Webworker。Webworker 可以让我们另开一个线程执行脚本而不影响渲染。
7.CDN
静态资源尽量使用 CDN 加载,由于浏览器对于单个域名有并发请求上限,可以考虑使用多个 CDN 域名。对于 CDN 加载静态资源需要注意 CDN 域名要与主站不同,否则每次请求都会带上主站的 Cookie。
8.使用 Webpack 优化项目
对于 Webpack4,打包项目使用 production 模式,这样会自动开启代码压缩
使用 ES6 模块来开启 tree shaking,这个技术可以移除没有使用的代码
优化图片,对于小图可以使用 base64 的方式写入文件中
按照路由拆分代码,实现按需加载
给打包出来的文件名添加哈希,实现浏览器缓存文件
9.监控
对于代码运行错误,通常的办法是使用 window.onerror 拦截报错。该方法能拦截到大部分的详细报错信息,但是也有例外
对于跨域的代码运行错误会显示 Script error. 对于这种情况我们需要给 script 标签添加 crossorigin 属性
对于某些浏览器可能不会显示调用栈信息,这种情况可以通过 arguments.callee.caller 来做栈递归
对于异步代码来说,可以使用 catch 的方式捕获错误。比如 Promise 可以直接使用 catch 函数,async await,可以使用 try catch,但是要注意线上运行的代码都是压缩过的,需要在打包时生成 sourceMap 文件便于 debug。
对于捕获的错误需要上传给服务器,通常可以通过 img 标签的 src 发起一个请求。
10.如何渲染几万条数据并不卡住界面
语法: window.requestAnimationFrame(callback); callback: 下一次重绘之前更新动画帧所调用的函数(即上面所说的回调函数)。该回调函数会被传入DOMHighResTimeStamp参数,该参数与performance.now()的返回值相同,它表示requestAnimationFrame() 开始去执行回调函数的时刻。 返回值: 一个 long 整数,请求 ID ,是回调列表中唯一的标识。是个非零值,没别的意义。你可以传这个值给 window.cancelAnimationFrame() 以取消回调函数。
< ! DOCTYPE html> < html lang= "en" > < head> < meta charset= "UTF-8" / > < meta name= "viewport" content= "width=device-width, initial-scale=1.0" / > < meta http- equiv= "X-UA-Compatible" content= "ie=edge" / > < title> Document< / title> < / head> < body> < ul> 数据条渲染: < / ul> < script> setTimeout ( ( ) => { const total = 10000000 const once = 20 const loopCount = total / once var countOfRender = 0 var ul = document. querySelector ( 'ul' ) console. time ( 'loopTime' ) ; function add ( ) { const fragment = document. createDocumentFragment ( ) for ( var i = 0 ; i < once; i++ ) { const li = document. createElement ( 'li' ) li. innerText = Math. floor ( Math. random ( ) * total) fragment. appendChild ( li) } ul. appendChild ( fragment) countOfRender += 1 loop ( ) } console. timeEnd ( 'loopTime' ) ; function loop ( ) { if ( countOfRender < loopCount) { window. requestAnimationFrame ( add) } } loop ( ) } , 0 ) < / script> < / body> < / html>
前端工程化
前端工程化就是为了让前端开发能够“自成体系”,个人认为主要应该从模块化 、组件化 、规范化 、自动化 四个方面思考。
前端面试中,回答不出的那些问题
前端面试官的套路,你懂吗?
前端面试常考的手写代码不是背出来的!
常规交谈
创作打卡挑战赛 赢取流量/现金/CSDN周边激励大奖