面试题目,结合网络上的内容参考并总结,会持续更新到找到工作为止,如果有疑问或者错误,欢迎提出.
css样式优先级:
行内样式>内联样式>外链样
css选择器工作模式:
从右向左匹配 从左向右匹配看似高效,其实会把前一次匹配后的每一个后代都要遍历一遍,它的范围是逐渐变大的,否则就有可能遗漏.而从右向左匹配是先匹配最末端的选择器,这样再次匹配的时候只需要去找匹配到的选择器的父级就可以了,这样它的范围是逐渐变小的.
同源策略:
协议,域名和端口都相同时成为一个域,其他域的脚本无法访问除了本域意外的内容.这就是同源策略,有了这个策略,网络才相对的安全.同时我们也可以使用一些手段来进行跨域请求,比如:
- cors跨域资源共享:通过在请求中添加一个
Origin
字段,表示需要跨域请求的地址,如果服务器端允许这个域访问,响应头里就会有Access-Control-Allow-Origin
字段,表示同意请求.
它分为简单请求和非简单请求: 简单请求:
HEAD,GET,POST
并且请求头里不超出一下几种字段:
Accept
Accept-Language
Content-Language
Last-Event-ID
Content-Type
:只限于三个值application/x-www-form-urlencoded、multipart/form-data、text/plain
非简单请求:PUT,DELETE
,或者content-type
是application/json
的请求
非简单请求会先发送一个试探的请求OPTIONS
,同样请求头要有Origin
,并且加上Access-Control-Request-Method
和Access-Control-Request-Headers
,服务器对试探请求相应成功后响应头上会有Access-Control-Allow-Origin
字段,接下来的请求就会和简单请求一样了.
跨域资源共享的内容参考了阮一峰大大的文章
- JSONP:利用script标签的src属性没有跨域限制的特点,在需要跨域请求的时候动态生成一个script标签,src属性为需要跨域的地址,并且提供一个回调函数来接受数据,然后服务器会返回jsonp的包装,形如callback({...}),然后浏览器就会电泳callback回调函数,并且把解析后的json对象作为参数,然后就可以在这个回调函数里处理请求到的数据,只能使用get方法,并且本地的回调函数需要在全局作用域下.
- window.name
- domain
- iframe
- 什么时候才会有跨域的需求? 前端和后端使用不同的端口时,属于不同域,不在同源策略之内,这是需要进行跨域相关的配置.
浮点数计算
参考地址:
0.1+0.2=0.30000000000000004
js中的数字采用的是64位的双精度浮点数,它的最高位表示符号,接着11位表示指数,余下的52位为有效数字. 符号位决定的数字的正负,指数部分决定了数值的大小,有效数字部分表示精度
这里要提一下二进制如何表示小数.
- 就比如这个0.1.
- 取它的小数部分,当然就是0.1了,乘2得到0.2,这是取它的整数部分第一位表示二进制小数的第一位(0).
- 再乘2,得0.4,取整数部分第一位(0),得到二进制小数的第二位(0).
- 再乘2,得0.8,取整数部分第一位(0).得到二进制小数的第三位(0).
- 再乘2,得1.6,取整数部分第一位(1),得到二进制小数第四位(1).
- 依次类推,会发现,0.1的二进制小数是这样的:
0.0001 1001 1001 1001....(无限循环)
- 而0.2的二进制小数为:
0.0011 0011 0011 0011....(无限循环)
而js遵循IEEE754标准的64位双精度浮点数的小数部分最多只支持53位二进制位,所以0.1和0.2的二进制数相加如下:
0.1 : 0.0001 1001 1001 1001 1001 1001 1001 1001 1001 1001 1001 1001 1001
0.2 : 0.0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011
结果: 0.0100 1100 1100 1100 1100 1100 1100 1100 1100 1100 1100 1100 1100
- 这里再将结果转换成十进制数:
0+(0 * 2^-1)+(1 * 2^-1)+(0 * 2^-3)+(0 * 2^-4)+(1 * 2^-5)+(1 * 2^-6)+(0 * 2^-7)+(0 * 2^-8)...(总共52位)
最终得到的结果0.30000000000000004
,所以在进行算数计算的时候就会产生误差.
那么如何消除这种误差呢?
有效数字最多可以表示53位二进制数,也就是2^53=9007199254740992
,对应的科学计数法为9.007199254740992e+16
,这也是js最多能表示的精度,它的小数部分长度为16.
所以我们可以使用toPrecision
方法来做精度计算,超过的精度胡自动做凑整处理比如0.30000000000000004.toPrecision(16)=0.3000000000000000
,去掉末尾的0后正好是0.3
其实在整数中也同样存在着精度问题:我们知道js能表示的最大数字(2^53 - 1,Number.MAX_SAFE_INTEGER,9007199254740991)
和最小数字(-(2^53 - 1),Number.MIN_SAFE_INTEGER,-9007199254740991)
。
超过了这个范围的数字的计算就会不准确,但是我们可以通过引入插件来解决,比如bignumber.js之类的,原理是用过把数字当作字符串,重新实现计算的逻辑,缺点就是性能比原生差很多.
toPrecision
和toFixed
的区别?
toPrecision
是处理精度,精度是从左至右第一个不为0的数开始数起.toFixed
是小数点后指定位数取整,从小数点开始数起.
遇到浮点数误差问题时可以直接使用 https://github.com/dt-fe/number-precision
完美支持浮点数的加减乘除、四舍五入等运算。
前端页面优化
http请求优化
- 减少http请求
- 首先是压缩,合并js,css文件,可以配置webpack使其合并成一个唯一的出口文件.
- 延迟加载 适合延迟加载的东西很多,最需要的当然是图片.原理就是首先将要延迟加载的图片src替换为空白或者指定的loading图,当达到指定条件的时候再将真是图片的资源路径替换到src属性上让浏览器去加载. 首屏可是使用定时器,其他屏可以通过当前元素的位置,和滚动的距离来判断是否需要加载图片
- 雪碧图 在容器元素上设置
background-image
属性,在子元素上设置background-position
属性来确定其位置
- 预加载 延迟加载的目的是为了减少不必要的请求,用户有需求的时候才去请求资源,而预加载是预先判断需要加载的数据,预先加载,并且在达到指定条件之后直接从浏览器缓存中获取
- 静态资源使用单独的域名和服务器 将静态资源放在额外的服务器上和域名上,这样主域名就可以减少http请求数,减少主服务器的带宽使用,主服务器就可以更快的响应请求.
- http缓存 http缓存策略可以把未过期或者未更改的资源重复使用,同样也可以减少服务器的带宽使用.
- ajax局部加载 在达到某些条件后,可以使用ajax刷新局部页面,这样也可以减少不必要的请求.
页面/性能优化
- 引用优化
将css文件放在html文档的head标签内,将js文件放在body标签的最下面.
css文件时异步加载的,浏览器构建dom树的同时如果有对应的css样式就是直接渲染出来,这样会让样式更早的呈现在页面上.
而js文件下载的时候会阻塞dom数的构建,只有js下载完成后才会继续构建接下来的dom树,将js文件放在最下面可以在视觉上减少白屏的现象.
- 将js动画替换成css动画
主流浏览器已经全部支持的css3
在js中使用动画是利用定时器循环改变dom元素的位置来达到动画效果,这样会造成浏览器的回流,会影响前端页面的性能,如果一定要使用js实现动画,可以在动画之前使其脱离正常文档流,在动画结束之后再让其回归文档流.
而使用css3动画后,在性能上,浏览器会新建一个图层来运行动画,并且可以使用硬件加速.在效果上js动画的最小移动单位为1px,而css3动画可以打破这个束缚,让动画看起来更加流畅.
http缓存策略
http缓存是减少http请求,减少请求带宽,增加页面访问速度的有效办法.
http缓存主要依靠http报文中的字段来判断是都对数据进行缓存.
- 首先是强缓存:
-
通过cache-control设置max-age=[秒]来设置一个资源的最大保存事件,他是一个相对事件,意思是存请求成功之后的n秒之内,浏览器都直接从本地获取已缓存的资源.
-
expires:服务器通过设置expires响应头,值为一个格林尼治时间,属于绝对时间,可以告诉浏览器缓存过气的时间,在这个过气事件之前,再次请求的数据都来自本地,在这个时刻之后才会向服务器请求资源.
expires有一个漏洞,就是他设置的过期事件是需要和本地操作系统的时间进行对比的,如果更改了本地的时间,那么这个对比就会不准确,而max-age则不会出现这样的问题,通常为了兼容http1.0会将expires和max-age同时设置,在同时设置后,浏览器会优先匹配max-age的规则.
- 协商缓存
-
last-modified:服务器在第一次接收到请求的时候,可以设置一个last-modified字段,用来保存这个资源的最后更改日期,浏览器接收到响应后,再次请求的时候,会自动在请求头中添加if-modified-since字段,它的值就是第一次响应时的last-modified的值,然后后台通过判断这个值与文件最后修改时间是否相同,如果相同,则可以返回304状态码提示浏览器使用缓存中的数据,如果不同则重新设置last-modified的时间,并返回最新数据.
-
ETag:服务器在相应请求的时候通过某种算法,给资源计算出一个唯一的标识符,在相应请求的时候会带上这个标识符,放在ETag字段上,而浏览器在下次请求的时候会自动带上一个If-None-Match字段,值就是第一次请求时的ETag字段的值,服务器通过鼻尖请求头中If-None-Match的值和服务器上资源的ETag值,如果匹配则返回304状态码,提示浏览器直接使用本地缓存,如果不匹配则重新下载资源.
在以几个字段同时设置的情况下,会先使强缓存策略生效,如果强缓存过期了,才会使用协商缓存策略,而如果协商缓存策略也同时失效,才会重新下载资源.
- 还有一个问题如果强缓存和协商缓存都未失效,而我们又想让浏览器更新资源,这是该怎么做?
可以通过设置url上的querystring来解决,一般有两种解决方式:
- 使用版本号,比如http://url.com?version=20180201
- 使用hash值,比如http://url.com/?hash=sdfkljlkjasdklxclkv13
这样浏览器就会从服务器更新资源.
原型链
- 原型基础知识:
- 每个构造函数上都有个
prototype
属性 ,prototype
是一个对象 - prototype对象上有一个
constructor
属性,这个属性指向当前的构造函数 - 每个对象上都有一个
__proto__
属性,这个属性指向所属类的原型
- 属性和方法查找顺序
- 若已找到私有的属性和方法,则不会再向原型上继续查找
- 若未找到私有属性和方法,则通过
__proto__
去当前实例所属类的原型上查找 - 若还未找到,则继续通过
__proto__
继续往上级(父类)查找,直到找到Object
类的原型上
若都没有找到,则返回
undefined
(查找对象的属性和方法,如果没有这个方法返回的是undefined,不是报错) Object类是所有类型的基类,其他类为Object的派生类
- 原型链
- 通过
__proto__
属性一级一级的往父类上查找,就形成了一个原型链__proto__
属性是实现原型继承的关键
ie中因安全考虑 禁止手动操作
__proto__
属性
继承
- 原型链继承:直接让子类的prototype属性指向父类的实例,这样在子类的原型上拥有了父类的私有和公有属性.
- 借用构造函数(经典继承):在子类的构造函数中使用父类.call(this)方法,在构造新的子类实例的时候其实也执行了父类的初始化代码,并且改变了this指向,这样子类就可以使用父类的私有属性了
- 原型式继承Object.create():create函数内部创建了一个临时的中间类,让这个类的原型等于传进来的原型对象,最后返回了这个中间类的实例,让子类的prototype属性等于返回的实例,这样就拿到了父类的公有方法.
作用域链
全局作用域
:浏览器加在页面时形成,谁都可以访问,全局下定义的变量是全局变量私有作用域
:函数运行时形成,只有自己可以访问,私有变量:带var关键词和形参
变量的查找: 1.先看是否是私有的,如果是私有的(带var或形参)则就是它 2.如果不是私有的则往上级作用域查找,如果还没找到则继续往上级作用域查找,直到找到window下,
- 通过函数形成的作用域一级一级的向上查找,就形成了
作用域链
。
若window下也没有则会报错 Uncaught ReferenceError: xxx is not defined
上级作用域:在哪定义的,上级作用域就是谁
项目中大量的静态资源如何处理
- 静态资源如js,css尽可能合并,图片合成单张雪碧图,减少http请求次数.
- 使用单独的域名和服务器来储存静态资源,并设置缓存策略,这样主服务器可以只接受业务请求,可以有效分流,主服务器可以更快的相应.
- 直接使用CDN服务,将静态资源保存在cdn服务器中,不同地区的用户访问网站时,cdn服务器会查找最近的拥有静态资源的服务器并返回资源,达到访问加速的目的,并且cdn服务器也很安全,可以有效防止攻击.