面试完50个人后我写下这篇总结

  2019年的春节来的似乎格外的早,过完年相信很多童鞋都开始蠢蠢欲动了;笔者总结了多篇教程,结合平时自己的面试经历,整理了这份文档,希望帮助大家来突击一下前端知识的盲区。

一、CSS

盒模型

  CSS盒模型本质上是一个盒子,封装周围的HTML元素,它包括:边距margin,边框border,填充padding,和实际内容content。盒模型允许我们在其它元素和周围元素边框之间的空间放置元素。

Box-Model.jpg

box-sizing: content-box(W3C盒模型,又名标准盒模型):元素的宽高大小表现为内容的大小。 box-sizing: border-box(IE盒模型,又名怪异盒模型):元素的宽高表现为内容 + 内边距 + 边框的大小。背景会延伸到边框的外沿。

CSS3的新特性

transition和animation的区别:

  Animation和transition大部分属性是相同的,他们都是随时间改变元素的属性值,他们的主要区别是transition需要触发一个事件才能改变属性,而animation不需要触发任何事件的情况下才会随时间改变属性值,并且transition为2帧,从from …. to,而animation可以一帧一帧的。

CSS选择器及其优先级

css-priority.jpg

BFC

BFC(Block Formatting Context)格式化上下文,是Web页面中盒模型布局的CSS渲染模式,指一个独立的渲染区域或者说是一个隔离的独立容器。

BFC应用

触发BFC条件

BFC的特性

  • 内部的Box会在垂直方向上一个接一个的放置。
  • 垂直方向上的距离由margin决定
  • bfc的区域不会与float的元素区域重叠。
  • 计算bfc的高度时,浮动元素也参与计算
  • bfc就是页面上的一个独立容器,容器里面的子元素不会影响外面元素。

谢小飞博客专用防爬虫链接,想要看最新的前端博客请点这里

div水平居中

  1. 行内元素
1
2
3
.parent {
text-align: center;
}
  1. 块级元素

谢小飞博客专用防爬虫链接,想要看最新的前端博客请点这里

1
2
3
.son {
margin: 0 auto;
}
  1. flex布局

谢小飞博客专用防爬虫链接,想要看最新的前端博客请点这里

1
2
3
4
.parent {
display: flex;
justify-content: center;
}
  1. 绝对定位定宽
1
2
3
4
5
6
.son {
position: absolute;
width: 宽度;
left: 50%;
margin-left: -0.5*宽度
}

谢小飞博客专用防爬虫链接,想要看最新的前端博客请点这里

  1. 绝对定位不定宽
1
2
3
4
5
.son {
position: absolute;
left: 50%;
transform: translate(-50%, 0);
}
  1. left/right: 0
1
2
3
4
5
6
7
.son {
position: absolute;
width: 宽度;
left: 0;
right: 0;
margin: 0 auto;
}

div垂直居中

  1. 行内元素
1
2
3
4
5
6
.parent {
height: 高度;
}
.son {
line-height: 高度;
}
  1. table
  2. 谢小飞博客专用防爬虫链接,想要看最新的前端博客请点这里

1
2
3
4
5
6
7
.parent {
display: table;
}
.son {
display: table-cell;
vertical-align: middle;
}
  1. flex
1
2
3
4
.parent {
display: flex;
align-items: center;
}
  1. 绝对定位定高
1
2
3
4
5
6
.son {
position: absolute;
top: 50%;
height: 高度;
margin-top: -0.5高度;
}

谢小飞博客专用防爬虫链接,想要看最新的前端博客请点这里

  1. 绝对定位不定高
    1
    2
    3
    4
    5
    .son {
    position: absolute;
    top: 50%;
    transform: translate( 0, -50%);
    }
  2. 谢小飞博客专用防爬虫链接,想要看最新的前端博客请点这里

  1. top/bottom: 0;
    1
    2
    3
    4
    5
    6
    7
    .son {
    position: absolute;
    height: 高度;
    top: 0;
    bottom: 0;
    margin: auto 0;
    }

绝对定位和相对定位

  • absolute 绝对定位 相对于最近的已定位的祖先元素, 有已定位(指position不是static的元素)祖先元素, 以最近的祖先元素为参考标准。如果无已定位祖先元素, 以body元素为偏移参照基准, 完全脱离了标准文档流。

  • fixed 固定定位的元素会相对于视窗来定位,这意味着即便页面滚动,它还是会停留在相同的位置。一个固定定位元素不会保留它原本在页面应有的空隙。

共同点:改变行内元素的呈现方式,都脱离了文档流;不同点:absolute的”根元素“是可以设置的,fixed的“根元素”固定为浏览器窗口

flex布局

采用 Flex 布局的元素,称为 Flex 容器(flex container),简称”容器”。它的所有子元素自动成为容器成员,称为 Flex 项目(flex item),简称“项目”。

flex.jpeg

谢小飞博客专用防爬虫链接,想要看最新的前端博客请点这里

  1. 父元素属性

谢小飞博客专用防爬虫链接,想要看最新的前端博客请点这里

谢小飞博客专用防爬虫链接,想要看最新的前端博客请点这里

谢小飞博客专用防爬虫链接,想要看最新的前端博客请点这里

属性名 属性值 备注
display flex 定义了一个flex容器,它的直接子元素会接受这个flex环境
flex-direction row,row-reverse,column,column-reverse 决定主轴的方向
flex-wrap nowrap,wrap,wrap-reverse 如果一条轴线排不下,如何换行
flex-flow [flex-direction] , [flex-wrap] flex-direction属性和flex-wrap属性的简写形式,默认值为row nowrap
justify-content flex-start,flex-end,center,space-between,space-around 设置或检索弹性盒子元素在主轴(横轴)方向上的对齐方式
align-items flex-start,flex-end,center,baseline,stretch 设置或检索弹性盒子元素在侧轴(纵轴)方向上的对齐方式
  1. 子元素属性

谢小飞博客专用防爬虫链接,想要看最新的前端博客请点这里

谢小飞博客专用防爬虫链接,想要看最新的前端博客请点这里

谢小飞博客专用防爬虫链接,想要看最新的前端博客请点这里

谢小飞博客专用防爬虫链接,想要看最新的前端博客请点这里

属性名 属性值 备注
order [int] 默认情况下flex order会按照书写顺序呈现,可以通过order属性改变,数值小的在前面,还可以是负数。
flex-grow [number] 设置或检索弹性盒的扩展比率,根据弹性盒子元素所设置的扩展因子作为比率来分配剩余空间
flex-shrink [number] 设置或检索弹性盒的收缩比率,根据弹性盒子元素所设置的收缩因子作为比率来收缩空间
flex-basis [length], auto 设置或检索弹性盒伸缩基准值
align-self auto,flex-start,flex-end,center,baseline,stretch 设置或检索弹性盒子元素在侧轴(纵轴)方向上的对齐方式,可以覆盖父容器align-items的设置

让元素消失

visibility:hidden、display:none、z-index=-1、opacity:0

  1. opacity:0,该元素隐藏起来了,但不会改变页面布局,并且,如果该元素已经绑定了一些事件,如click事件也能触发
  2. visibility:hidden,该元素隐藏起来了,但不会改变页面布局,但是不会触发该元素已经绑定的事件
  3. display:none, 把元素隐藏起来,并且会改变页面布局,可以理解成在页面中把该元素删掉
  4. z-index=-1置于其他元素下面

清除浮动

  1. 在浮动元素后面添加 clear:both 的空 div 元素,
1
2
3
4
5
<div class="container">
<div class="left"></div>
<div class="right"></div>
<div style="clear:both"></div>
</div>
  1. 给父元素添加 overflow:hidden 或者 auto 样式,触发BFC。
  2. 谢小飞博客专用防爬虫链接,想要看最新的前端博客请点这里

1
2
3
4
<div class="container">
<div class="left"></div>
<div class="right"></div>
</div>
1
2
3
4
5
6
.container{
width: 300px;
background-color: #aaa;
overflow:hidden;
zoom:1; /*IE6*/
}
  1. 使用伪元素,也是在元素末尾添加一个点并带有 clear: both 属性的元素实现的。
1
2
3
4
<div class="container clearfix">
<div class="left"></div>
<div class="right"></div>
</div>

谢小飞博客专用防爬虫链接,想要看最新的前端博客请点这里

1
2
3
4
5
6
7
8
9
10
.clearfix{
zoom: 1; /*IE6*/
}
.clearfix:after{
content: ".";
height: 0;
clear: both;
display: block;
visibility: hidden;
}

推荐使用第三种方法,不会在页面新增div,文档结构更加清晰。

calc函数

calc函数是css3新增的功能,可以使用calc()计算border、margin、pading、font-size和width等属性设置动态值。

1
2
3
4
5
6
7
8
9
#div1 {
position: absolute;
left: 50px;
width: calc( 100% / (100px * 2) );
//兼容写法
width: -moz-calc( 100% / (100px * 2) );
width: -webkit-calc( 100% / (100px * 2) );
border: 1px solid black;
}

注意点:

    谢小飞博客专用防爬虫链接,想要看最新的前端博客请点这里

  • 需要注意的是,运算符前后都需要保留一个空格,例如:width: calc(100% - 10px);
  • calc()函数支持 “+”, “-“, “*”, “/“ 运算;
  • 对于不支持 calc() 的浏览器,整个属性值表达式将被忽略。不过我们可以对那些不支持 calc()的浏览器,使用一个固定值作为回退。

移动端rem

rem官方定义『The font size of the root element』,即根元素的字体大小。rem是一个相对的CSS单位,1rem等于html元素上font-size的大小。所以,我们只要设置html上font-size的大小,就可以改变1rem所代表的大小。

1
2
3
4
5
6
7
8
(function () {
var html = document.documentElement;
function onWindowResize() {
html.style.fontSize = html.getBoundingClientRect().width / 20 + 'px';
}
window.addEventListener('resize', onWindowResize);
onWindowResize();
})();

谢小飞博客专用防爬虫链接,想要看最新的前端博客请点这里

移动端1px

一般来说,在PC端浏览器中,设备像素比(dpr)等于1,1个css像素就代表1个物理像素;但是在retina屏幕中,dpr普遍是2或3,1个css像素不再等于1个物理像素,因此比实际设计稿看起来粗不少。

  1. 伪元素+scale
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<style>
.box{
width: 100%;
height: 1px;
margin: 20px 0;
position: relative;
}
.box::after{
content: '';
position: absolute;
bottom: 0;
width: 100%;
height: 1px;
transform: scaleY(0.5);
transform-origin: 0 0;
background: red;
}
</style>

<div class="box"></div>

  1. border-image
1
2
3
4
5
div{
border-width: 1px 0px;
-webkit-border-image: url(border.png) 2 0 stretch;
border-image: url(border.png) 2 0 stretch;
}

两边宽度固定中间自适应的三栏布局

圣杯布局和双飞翼布局是前端工程师需要日常掌握的重要布局方式。两者的功能相同,都是为了实现一个两侧宽度固定,中间宽度自适应的三栏布局。

圣杯布局

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<style>
body{
min-width: 550px;
}
#container{
padding-left: 200px;
padding-right: 150px;
}
#container .column{
float: left;
}
#center{
width: 100%;
}
#left{
width: 200px;
margin-left: -100%;
position: relative;
right: 200px;
}
#right{
width: 150px;
margin-right: -150px;
}
</style>
<div id="container">
<div id="center" class="column">center</div>
<div id="left" class="column">left</div>
<div id="right" class="column">right</div>
</div>

谢小飞博客专用防爬虫链接,想要看最新的前端博客请点这里

Layout.gif

双飞翼布局

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<style>
body {
min-width: 500px;
}
#container {
width: 100%;
}
.column {
float: left;
}
#center {
margin-left: 200px;
margin-right: 150px;
}
#left {
width: 200px;
margin-left: -100%;
}
#right {
width: 150px;
margin-left: -150px;
}
</style>
<div id="container" class="column">
<div id="center">center</div>
</div>
<div id="left" class="column">left</div>
<div id="right" class="column">right</div>

伪类和伪元素

css引入伪类和伪元素概念是为了格式化文档树以外的信息。也就是说,伪类和伪元素都是用来修饰不在文档树中的部分。

before-after.jpg

伪类

伪类存在的意义是为了通过选择器找到那些不存在DOM树中的信息以及不能被常规CSS选择器获取到的信息。

  1. 获取不存在于DOM树中的信息。比如标签的:link、visited等,这些信息不存在于DOM树结构中,只能通过CSS选择器来获取;
  2. 谢小飞博客专用防爬虫链接,想要看最新的前端博客请点这里

  3. 获取不能被常规CSS选择器获取的信息。比如:要获取第一个子元素,我们无法用常规的CSS选择器获取,但可以通过 :first-child 来获取到。

weilei.png

伪元素

伪元素用于创建一些不在文档树中的元素,并为其添加样式。比如说,我们可以通过:before来在一个元素前增加一些文本,并为这些文本添加样式。虽然用户可以看到这些文本,但是这些文本实际上不在文档树中。常见的伪元素有:::before::after::first-line::first-letter::selection::placeholder

因此,伪类与伪元素的区别在于:有没有创建一个文档树之外的元素。

::after和:after的区别

在实际的开发工作中,我们会看到有人把伪元素写成:after,这实际是 CSS2 与 CSS3新旧标准的规定不同而导致的。

CSS2 中的伪元素使用1个冒号,在 CSS3 中,为了区分伪类和伪元素,规定伪元素使用2个冒号。所以,对于 CSS2 标准的老伪元素,比如:first-line:first-letter:before:after,写一个冒号浏览器也能识别,但对于 CSS3 标准的新伪元素,比如::selection,就必须写2个冒号了。

CSS画圆半圆扇形三角梯形

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
div{
margin: 50px;
width: 100px;
height: 100px;
background: red;
}
/* 半圆 */
.half-circle{
height: 50px;
border-radius: 50px 50px 0 0;
}
/* 扇形 */
.sector{
border-radius: 100px 0 0;
}
/* 三角 */
.triangle{
width: 0px;
height: 0px;
background: none;
border: 50px solid red;
border-color: red transparent transparent transparent;
}
/* 梯形 */
.ladder{
width: 50px;
height: 0px;
background: none;
border: 50px solid red;
border-color: red transparent transparent transparent;
}

谢小飞博客专用防爬虫链接,想要看最新的前端博客请点这里

white-space几个属性值的区别

css中white-space这个属性,用来设置元素对内容中的空格的处理方式,有几个可选值:normal,nowrap,pre,pre-wrap,pre-line。没有设置white-space属性,则默认为white-space:normal。其他几个属性区别如下:

  1. white-space:nowrap不换行
  2. white-space:pre保留空格不换行
  3. white-space:pre-wrap保留空格换行
  4. white-space:pre-line合并空格换行

demo如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<style>
.nowrap-box{
white-space: nowrap;
}
.pre-box{
white-space: pre;
}
.pre-wrap-box{
white-space: pre-wrap;
}
.pre-line-box{
white-space: pre-line;
}
</style>
<div class="nowrap-box">
谢小飞前端测试white-space:nowrap属性。分享前端最新技术文章、工具资源,专注前端技术开发。关注我们,每天读壹篇文章,和我们一起学习进步。公众号:前端壹读
</div>
<div class="pre-box">
谢小飞前端测试white-space:pre属性。
分享前端最新技术文章、工具资源,专注前端技术开发。关注我们,每天读壹篇文章,和我们一起学习进步<br/> 公众号:前端壹读
</div>
<div class="pre-wrap-box">
谢小飞前端测试white-space:pre-wrap属性。
分享前端最新技术文章、工具资源,专注前端技术开发。关注我们,每天读壹篇文章,和我们一起学习进步<br/> 公众号:前端壹读
</div>
<div class="pre-line-box">
谢小飞前端测试white-space:pre-line属性。
分享前端最新技术文章、工具资源,专注前端技术开发。关注我们,每天读壹篇文章,和我们一起学习进步<br/> 公众号:前端壹读
</div>

谢小飞博客专用防爬虫链接,想要看最新的前端博客请点这里

white-space.png

谢小飞博客专用防爬虫链接,想要看最新的前端博客请点这里

谢小飞博客专用防爬虫链接,想要看最新的前端博客请点这里

谢小飞博客专用防爬虫链接,想要看最新的前端博客请点这里

属性名 源码空格 源码换行 br换行 容器边界换行
normal 合并 忽略 换行 换行
nowrap 合并 忽略 换行 不换行
pre 保留 换行 换行 不换行
pre-wrap 保留 换行 换行 换行
pre-line 合并 换行 换行 换行

浏览器解析CSS为什么从右开始

CSS选择器的解析是从右向左解析的。若从左向右的匹配,发现不符合规则,需要进行回溯,会损失很多性能。若从右向左匹配,先找到所有的最右节点,对于每一个节点,向上寻找其父节点直到找到根元素或满足条件的匹配规则,则结束这个分支的遍历。比如.box .left p,会在页面中找到所有的p标签,然后在p标签中找其父元素有.left类的p元素,再找祖父元素有.box的p标签。

谢小飞博客专用防爬虫链接,想要看最新的前端博客请点这里

两种匹配规则的性能差别很大,是因为从右向左的匹配在第一步就筛选掉了大量的不符合条件的最右节点(叶子节点),而从左向右的匹配规则的性能都浪费在了失败的查找上面。

CSS如何性能优化

  1. 避免过度约束
  2. 避免后代选择符
  3. 避免链式选择符
  4. 使用紧凑的语法
  5. 避免不必要的命名空间
  6. 避免不必要的重复
  7. 最好使用表示语义的名字。一个好的类名应该是描述他是什么而不是像什么
  8. 避免!important,可以选择其他选择器
  9. 尽可能的精简规则,可以合并不同类里的重复规则

谢小飞博客专用防爬虫链接,想要看最新的前端博客请点这里

二、JS

JS数据类型

JS基本有5种简单数据类型:String,Number,Boolean,Null,Undefined。引用数据类型:Object,Array,Function。

判断数据类型的方法

在写业务逻辑的时候,经常要用到JS数据类型的判断,面试常见的案例深浅拷贝也要用到数据类型的判断。

typeof

1
2
3
4
5
6
7
8
console.log(typeof 2);               // number
console.log(typeof true); // boolean
console.log(typeof 'str'); // string
console.log(typeof undefined); // undefined
console.log(typeof []); // object
console.log(typeof {}); // object
console.log(typeof function(){}); // function
console.log(typeof null); // object

优点:能够快速区分基本数据类型
缺点:不能将Object、Array和Null区分,都返回object

instanceof

1
2
3
4
5
6
console.log(2 instanceof Number);                    // false
console.log(true instanceof Boolean); // false
console.log('str' instanceof String); // false
console.log([] instanceof Array); // true
console.log(function(){} instanceof Function); // true
console.log({} instanceof Object); // true

优点:能够区分Array、Object和Function,适合用于判断自定义的类实例对象
缺点:Number,Boolean,String基本数据类型不能判断

Object.prototype.toString.call()

1
2
3
4
5
6
7
8
9
10
var toString = Object.prototype.toString;

console.log(toString.call(2)); //[object Number]
console.log(toString.call(true)); //[object Boolean]
console.log(toString.call('str')); //[object String]
console.log(toString.call([])); //[object Array]
console.log(toString.call(function(){})); //[object Function]
console.log(toString.call({})); //[object Object]
console.log(toString.call(undefined)); //[object Undefined]
console.log(toString.call(null)); //[object Null]

优点:精准判断数据类型
缺点:写法繁琐不容易记,推荐进行封装后使用

var,let,const的区别

letES6 新添加申明变量的命令,它类似于 var,但是有以下不同:

  • var 声明的变量,其作用域为该语句所在的函数内,且存在变量提升现象
  • let 声明的变量,其作用域为该语句所在的代码块内,不存在变量提升
  • const声明的变量不允许修改

谢小飞博客专用防爬虫链接,想要看最新的前端博客请点这里

null和undefined区别

Undefined类型只有一个值,即undefined。当声明的变量还未被初始化时,变量的默认值为undefined。用法:

  • 变量被声明了,但没有赋值时,就等于undefined。
  • 调用函数时,应该提供的参数没有提供,该参数等于undefined。
  • 对象没有赋值的属性,该属性的值为undefined。
  • 函数没有返回值时,默认返回undefined。
  • 谢小飞博客专用防爬虫链接,想要看最新的前端博客请点这里

Null类型也只有一个值,即null。null用来表示尚未存在的对象,常用来表示函数企图返回一个不存在的对象。用法

  • 作为函数的参数,表示该函数的参数不是对象。
  • 作为对象原型链的终点。

定义函数的方法

  1. 函数声明
1
2
3
4
5
//ES5
function getSum(){}
function (){}//匿名函数
//ES6
()=>{}

谢小飞博客专用防爬虫链接,想要看最新的前端博客请点这里

  1. 函数表达式
1
2
3
4
//ES5
var getSum=function(){}
//ES6
let getSum=()=>{}
  1. 构造函数
1
const getSum = new Function('a', 'b' , 'return a + b')

谢小飞博客专用防爬虫链接,想要看最新的前端博客请点这里

JS作用域的理解

JS中的作用域分为两种:全局作用域和函数作用域。函数作用域中定义的变量,只能在函数中调用,外界无法访问。没有块级作用域导致了if或for这样的逻辑语句中定义的变量可以被外界访问,因此ES6中新增了let和const命令来进行块级作用域的声明。

更多作用域的了解可以看JS作用域

闭包的理解

简单来说闭包就是在函数里面声明函数,本质上说就是在函数内部和函数外部搭建起一座桥梁,使得子函数可以访问父函数中所有的局部变量,但是反之不可以,这只是闭包的作用之一,另一个作用,则是保护变量不受外界污染,使其一直存在内存中,在工作中我们还是少使用闭包的好,因为闭包太消耗内存,不到万不得已的时候尽量不使用。

更多闭包的内容可以看JS闭包

数组去重

1
2
3
4
5
6
7
8
9
10
11
12
let arr = [1,'1',2,'2',1,2,'x','y','f','x','y','f'];
function unique1(arr){
let result = [arr[0]];
for (let i = 1; i < arr.length; i++) {
let item = arr[i];
if(result.indexOf(item) == -1){
result.push(item);
}
}
return result;
}
console.log(unique1(arr));

更多JS去重的方法JS数组去重

call,apply和bind区别

三个函数的作用都是将函数绑定到上下文中,用来改变函数中this的指向;三者的不同点在于语法的不同。

1
2
fun.call(thisArg[, arg1[, arg2[, ...]]])
fun.apply(thisArg, [argsArray])

谢小飞博客专用防爬虫链接,想要看最新的前端博客请点这里

所以applycall的区别是call方法接受的是若干个参数列表,而apply接收的是一个包含多个参数的数组。

而bind()方法创建一个新的函数, 当被调用时,将其this关键字设置为提供的值,在调用新函数时,在任何提供之前提供一个给定的参数序列。

1
2
var bindFn = fun.bind(thisArg[, arg1[, arg2[, ...]]])
bindFn()

Demos:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
var name = 'window';
var sayName = function (param) {
console.log('my name is:' + this.name + ',my param is ' + param)
};
//my name is:window,my param is window param
sayName('window param')

var callObj = {
name: 'call'
};
//my name is:call,my param is call param
sayName.call(callObj, 'call param');


var applyObj = {
name: 'apply'
};
//my name is:apply,my param is apply param
sayName.apply(applyObj, ['apply param']);

var bindObj = {
name: 'bind'
}
var bindFn = sayName.bind(bindObj, 'bind param')
//my name is:bind,my param is bind param
bindFn();

==和===区别

  • ==, 两边值类型不同的时候,要先进行类型转换,再比较
  • ===,不做类型转换,类型不同的一定不等。

==类型转换过程:

  1. 如果类型不同,进行类型转换
  2. 判断比较的是否是 null 或者是 undefined, 如果是, 返回 true .
  3. 判断两者类型是否为 string 和 number, 如果是, 将字符串转换成 number
  4. 判断其中一方是否为 boolean, 如果是, 将 boolean 转为 number 再进行判断
  5. 谢小飞博客专用防爬虫链接,想要看最新的前端博客请点这里

  6. 判断其中一方是否为 object 且另一方为 string、number 或者 symbol , 如果是, 将 object 转为原始类型再进行判断

经典面试题:[] == ![] 为什么是true

转化步骤:

  1. !运算符优先级最高,![]会被转为为false,因此表达式变成了:[] == false
  2. 根据上面第(4)条规则,如果有一方是boolean,就把boolean转为number,因此表达式变成了:[] == 0
  3. 根据上面第(5)条规则,把数组转为原始类型,调用数组的toString()方法,[]转为空字符串,因此表达式变成了:'' == 0
  4. 根据上面第(3)条规则,两边数据类型为string和number,把空字符串转为0,因此表达式变成了:0 == 0
  5. 两边数据类型相同,0==0为true

深拷贝和浅拷贝

浅拷贝

1
2
3
4
5
6
7
function simpleClone(obj) {
var result = {};
for (var i in obj) {
result[i] = obj[i];
}
return result;
}

深拷贝,遍历对象中的每一个属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function deepClone(obj) {
let result;
if (typeof obj == 'object') {
result = isArray(obj) ? [] : {}
for (let i in obj) {
result[i] = isObject(obj[i])||isArray(obj[i])?deepClone(obj[i]):obj[i]
}
} else {
result = obj
}
return result
}
function isObject(obj) {
return Object.prototype.toString.call(obj) == "[object Object]"
}
function isArray(obj) {
return Object.prototype.toString.call(obj) == "[object Array]"
}

谢小飞博客专用防爬虫链接,想要看最新的前端博客请点这里

防抖和节流

debounce_throttle.png

防抖

1
2
3
4
5
6
7
8
9
function debounce(fn, delay) {
let timer = null;
return function () {
if (timer) clearTimeout(timer);
timer = setTimeout(() => {
fn.apply(this, arguments);
}, delay);
}
}

节流

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function throttle(fn, cycle) {
let start = Date.now();
let now;
let timer;
return function () {
now = Date.now();
clearTimeout(timer);
if (now - start >= cycle) {
fn.apply(this, arguments);
start = now;
} else {
timer = setTimeout(() => {
fn.apply(this, arguments);
}, cycle);
}
}
}

  • cookie用来保存登录信息,大小限制为4KB左右
  • localStorage是Html5新增的,用于本地数据存储,保存的数据没有过期时间,一般浏览器大小限制在5MB
  • sessionStorage接口方法和localStorage类似,但保存的数据的只会在当前会话中保存下来,页面关闭后会被清空。
  • 谢小飞博客专用防爬虫链接,想要看最新的前端博客请点这里

谢小飞博客专用防爬虫链接,想要看最新的前端博客请点这里

名称 生命期 大小限制 与服务器通信
cookie 一般由服务器生成,可设置失效时间。如果在浏览器端生成Cookie,默认是关闭浏览器后失效 4KB 每次都会携带在HTTP头中,如果使用cookie保存过多数据会带来性能问题
localStorage 除非被清除,否则永久保存 5MB 仅在浏览器中保存,不与服务器通信
sessionStorage 仅在当前会话下有效,关闭页面或浏览器后被清除 5MB 仅在浏览器中保存,不与服务器通信

0.1+0.2!=0.3怎么处理

把需要计算的数字升级(乘以10的n次幂)成计算机能够精确识别的整数,等计算完成后再进行降级(除以10的n次幂),即:

1
(0.1*10 + 0.2*10)/10 == 0.3 //true

更多关于浮点数精度处理请看JS中浮点数精度问题

谢小飞博客专用防爬虫链接,想要看最新的前端博客请点这里

JS实现继承

首先创建一个父类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 定义一个动物类
function Animal(name, color) {
// 属性
this.name = name || 'Animal';
this.color = color || ['black'];
// 实例方法
this.sleep = function () {
console.log(this.name + '正在睡觉!');
}
}
// 原型方法
Animal.prototype.eat = function (food) {
console.log(this.name + '正在吃:' + food);
};

原型链继承

new了一个空对象,这个空对象指向Animal并且Cat.prototype指向了这个空对象,这种就是基于原型链的继承。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function Cat(name) {
this.name = name || 'tom'
}
Cat.prototype = new Animal()

var cat = new Cat()
cat.color.push('red')
cat.sleep() //tom正在睡觉!
cat.eat('fish') //tom正在吃:fish
console.log(cat.color) //["black", "red"]
console.log(cat instanceof Animal) //true
console.log(cat instanceof Cat) //true
var new_cat = new Cat()
console.log(new_cat.color) //["black", "red"]

构造继承

1
2
3
4
5
6
7
8
9
10
11
12
13
function Dog(name) {
Animal.call(this)
this.name = name || 'mica'
}
var dog = new Dog()
dog.color.push('blue')
dog.sleep() // mica正在睡觉!
dog.eat('bone') //Uncaught TypeError: dog.eat is not a function
console.log(dog.color) //["black", "blue"]
console.log(dog instanceof Animal) //false
console.log(dog instanceof Dog) //true
var new_dog = new Dog()
console.log(new_dog.color) //["black"]
  • 特点:可以实现多继承(call多个),解决了所有实例共享父类实例属性的问题。
  • 缺点:1.只能继承父类实例的属性和方法;2.不能继承原型上的属性和方法。

组合继承

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function Mouse(name){
Animal.call(this)
this.name = name || 'jerry'
}
Mouse.prototype = new Animal()
Mouse.prototype.constructor = Mouse

var mouse = new Mouse()
mouse.color.push('yellow)
mouse.sleep() //jerry正在睡觉!
mouse.eat('carrot') //jerry正在吃:carrot
console.log(mouse instanceof Animal)//true
console.log(mouse instanceof Mouse)//true
var new_mouse = new Mouse()
console.log(new_mouse.color) //["black"]

三、vue

mvc和mvvm理解

MVC

MVC即Model View Controller,简单来说就是通过controller的控制去操作model层的数据,并且返回给view层展示。

mvc.png

  • View 接受用户交互请求
  • View 将请求转交给Controller处理
  • Controller 操作Model进行数据更新保存
  • 数据更新保存之后,Model会通知View更新
  • View 更新变化数据使用户得到反馈

MVVM

MVVM即Model-View-ViewModel,将其中的 View 的状态和行为抽象化,让我们可以将UI和业务逻辑分开。MVVM的优点是低耦合、可重用性、独立开发。

谢小飞博客专用防爬虫链接,想要看最新的前端博客请点这里

mvvm.jpg

  • View 接收用户交互请求
  • View 将请求转交给ViewModel
  • ViewModel 操作Model数据更新
  • Model 更新完数据,通知ViewModel数据发生变化
  • ViewModel 更新View数据

谢小飞博客专用防爬虫链接,想要看最新的前端博客请点这里

MVVM模式和MVC有些类似,但有以下不同

  • ViewModel 替换了 Controller,在UI层之下
  • ViewModel 向 View 暴露它所需要的数据和指令对象
  • ViewModel 接收来自 Model 的数据

概括起来,MVVM是由MVC发展而来,通过在Model之上而在View之下增加一个非视觉的组件将来自Model的数据映射到View中。

响应原理

vue采用数据劫持结合发布者-订阅者模式的方式,通过Object.defineProperty劫持data属性的setter,getter,在数据变动时发布消息给订阅者,触发相应的监听回调。

生命周期函数

  • beforeCreate(创建前) vue实例的挂载元素$el和数据对象 data都是undefined, 还未初始化
  • created(创建后) 完成了 data数据初始化, el还未初始化
  • beforeMount(载入前) vue实例的$el和data都初始化了, 相关的render函数首次被调用
  • 谢小飞博客专用防爬虫链接,想要看最新的前端博客请点这里

  • mounted(载入后) 此过程中进行ajax交互
  • beforeUpdate(更新前)
  • updated(更新后)
  • beforeDestroy(销毁前)
  • destroyed(销毁后)

组件data为什么返回函数

组件中的data写成一个函数,数据以函数返回值形式定义,这样每复用一次组件,就会返回一份新的data。如果单纯的写成对象形式,就使得所有组件实例共用了一份data,造成了数据污染。

vue给对象新增属性页面没有响应

由于Vue会在初始化实例时对属性执行getter/setter转化,所以属性必须在data对象上存在才能让Vue将它转换为响应式的。Vue提供了$set方法用来触发视图更新。

1
2
3
4
5
6
7
8
9
10
11
12
13
export default {
data(){
return {
obj: {
name: 'fei'
}
}
},
mounted(){
this.$set(this.obj, 'sex', 'man')
}

}

v-if和v-show区别

v-if 是真正的条件渲染,因为它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建;也是惰性的:如果在初始渲染时条件为假,则什么也不做——直到条件第一次变为真时,才会开始渲染条件块。

v-show 就简单得多——不管初始条件是什么,元素总是会被渲染,并且只是简单地基于 CSS 的 “display” 属性进行切换。

所以,v-if 适用于在运行时很少改变条件,不需要频繁切换条件的场景;v-show 则适用于需要非常频繁切换条件的场景。

v-model双向绑定原理

v-model本质上是语法糖,v-model 在内部为不同的输入元素使用不同的属性并抛出不同的事件。

所以我们可以v-model进行如下改写:

1
2
3
<input v-model="sth" />
// 等同于
<input :value="sth" @input="sth = $event.target.value" />

这个语法糖必须是固定的,也就是说属性必须为value,方法名必须为:input。

知道了v-model的原理,我们可以在自定义组件上实现v-model。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
//Parent
<template>
{{num}}
<Child v-model="num">
</template>
export default {
data(){
return {
num: 0
}
}
}

//Child
<template>
<div @click="add">Add</div>
</template>
export default {
props: ['value'],
methods:{
add(){
this.$emit('input', this.value + 1)
}
}
}

key的作用

    谢小飞博客专用防爬虫链接,想要看最新的前端博客请点这里

  1. 让vue精准的追踪到每一个元素,高效的更新虚拟DOM
  2. 触发过渡
1
2
3
<transition>
<span :key="text">{{ text }}</span>
</transition>

当text改变时,这个元素的key属性就发生了改变,在渲染更新时,Vue会认为这里新产生了一个元素,而老的元素由于key不存在了,所以会被删除,从而触发了过渡。

scoped属性作用

在Vue文件中的style标签上有一个特殊的属性,scoped。当一个style标签拥有scoped属性时候,它的css样式只能用于当前的Vue组件,可以使组件的样式不相互污染。如果一个项目的所有style标签都加上了scoped属性,相当于实现了样式的模块化。

scoped属性的实现原理是给每一个dom元素添加了一个独一无二的动态属性,给css选择器额外添加一个对应的属性选择器,来选择组件中的dom。

1
2
3
4
5
6
7
8
<template>
<div class="box">dom</div>
</template>
<style lang="scss" scoped>
.box{
background:red;
}
</style>

vue将代码转译成如下:

1
2
3
4
5
6
.box[data-v-11c6864c]{
background:red;
}
<template>
<div class="box" data-v-11c6864c>dom</div>
</template>

谢小飞博客专用防爬虫链接,想要看最新的前端博客请点这里

scoped样式穿透

scoped虽然避免了组件间样式污染,但是很多时候我们需要修改组件中的某个样式,但是又不想去除scoped属性。

  1. 使用/deep/
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//Parent
<template>
<div class="wrap">
<Child />
</div>
</template>

<style lang="scss" scoped>
.wrap /deep/ .box{
background: red;
}
</style>

//Child
<template>
<div class="box"></div>
</template>
  1. 使用两个style标签
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//Parent
<template>
<div class="wrap">
<Child />
</div>
</template>

<style lang="scss" scoped>
//其他样式
</style>
<style lang="scss">
.wrap .box{
background: red;
}
</style>

//Child
<template>
<div class="box"></div>
</template>

谢小飞博客专用防爬虫链接,想要看最新的前端博客请点这里

ref的作用

  1. 获取dom元素this.$refs.box
  2. 获取子组件中的datathis.$refs.box.msg
  3. 调用子组件中的方法this.$refs.box.open()

computed和watch区别

1.当页面中有某些数据依赖其他数据进行变动的时候,可以使用计算属性computed。

computed.png

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<template>{{fullName}}</template>
export default {
data(){
return {
firstName: 'xie',
lastName: 'yu fei',
}
},
computed:{
fullName: function(){
return this.firstName + ' ' + this.lastName
}
}
}

谢小飞博客专用防爬虫链接,想要看最新的前端博客请点这里

2.watch用于观察和监听页面上的vue实例,如果要在数据变化的同时进行异步操作或者是比较大的开销,那么watch为最佳选择。

watch.png

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<template>{{fullName}}</template>
export default {
data(){
return {
firstName: 'xie',
lastName: 'xiao fei',
fullName: 'xie xiao fei'
}
},
watch:{
firstName(val) {
this.fullName = val + ' ' + this.lastName
},
lastName(val) {
this.fullName = this.firstName + ' ' + val
}
}
}

vue路由有几种模式

  1. hash模式

即地址栏URL中的#符号,它的特点在于:hash 虽然出现URL中,但不会被包含在HTTP请求中,对后端完全没有影响,不需要后台进行配置,因此改变hash不会重新加载页面。

    谢小飞博客专用防爬虫链接,想要看最新的前端博客请点这里

  1. history模式

利用了HTML5 History Interface 中新增的pushState() 和replaceState() 方法(需要特定浏览器支持)。history模式改变了路由地址,因为需要后台配置地址。

组件之间的传值通信

  1. 父组件给子组件传值通过props
  2. 子组件给父组件传值通过$emit触发回调
  3. 兄弟组件通信,通过实例一个vue实例eventBus作为媒介,要相互通信的兄弟组件之中,都引入eventBus
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//main.js
import Vue from 'vue'
export const eventBus = new Vue()

//brother1.vue
import eventBus from '@/main.js'
export default{
methods: {
toBus () {
eventBus.$emit('greet', 'hi brother')
}
}
}

//brother2
import eventBus from '@/main.js'
export default{
mounted(){
eventBus.$on('greet', (msg)=>{
this.msg = msg
})
}
}

axios拦截器怎么配

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 添加请求拦截器
axios.interceptors.request.use(function (config) {
// 在发送请求之前做些什么
return config;
}, function (error) {
// 对请求错误做些什么
return Promise.reject(error);
});
// 添加响应拦截器
axios.interceptors.response.use(function (response) {
// 对响应数据做点什么
return response;
}, function (error) {
// 对响应错误做点什么
return Promise.reject(error);
});

谢小飞博客专用防爬虫链接,想要看最新的前端博客请点这里

四、浏览器

浏览器渲染机制

  1. 构建DOM树(parse):渲染引擎解析HTML文档,首先将标签转换成DOM树中的DOM node
  2. 构建渲染树(construct):解析对应的CSS样式文件信息
  3. 布局渲染树(reflow/layout):从根节点递归调用,计算每一个元素的大小、位置等,给出每个节点所应该在屏幕上出现的精确坐标;
  4. 绘制渲染树(paint/repaint):遍历渲染树,使用UI后端层来绘制每个节点。

重绘和重排的区别

重绘(repaint或redraw):当盒子的位置、大小以及其他属性,例如颜色、字体大小等都确定下来之后,浏览器便把这些原色都按照各自的特性绘制一遍,将内容呈现在页面上。重绘是指一个元素外观的改变所触发的浏览器行为,浏览器会根据元素的新属性重新绘制,使元素呈现新的外观。

重绘发生在元素的可见的外观被改变,但并没有影响到布局的时候。比如,仅修改DOM元素的字体颜色(只有Repaint,因为不需要调整布局)

重排(重构/回流/reflow):当渲染树中的一部分(或全部)因为元素的规模尺寸,布局,隐藏等改变而需要重新构建, 这就称为回流(reflow)。每个页面至少需要一次回流,就是在页面第一次加载的时候。

触发重排的条件:任何页面布局和几何属性的改变都会触发重排:

  • 页面渲染初始化(无法避免)
  • 添加或删除可见的DOM元素
  • 元素位置的改变,或者使用动画
  • 元素尺寸的改变——大小,外边距,边框
  • 浏览器窗口尺寸的变化
  • 填充内容的改变,比如文本的改变或图片大小改变而引起的计算值宽度和高度的改变

谢小飞博客专用防爬虫链接,想要看最新的前端博客请点这里

重排必定会引发重绘,但重绘不一定会引发重排。

几种请求方法

GET、POST、HEAD、PUT、DELETE、CONNECT、OPTIONS、TRACE

get和post区别

谢小飞博客专用防爬虫链接,想要看最新的前端博客请点这里

谢小飞博客专用防爬虫链接,想要看最新的前端博客请点这里

请求方式 GET POST
参数位置 参数拼接到url的后面 参数在请求体中
参数大小 受限于浏览器url大小,一般不超过32K 1G
服务器数据接收 接收1次 根据数据大小,可分多次接收
适用场景 从服务器端获取数据 向服务器提交数据
安全性 参数携带在url中,安全性低 相对于GET请求,安全性更高

谢小飞博客专用防爬虫链接,想要看最新的前端博客请点这里

如何解决跨域

  1. CORS
  2. jsonp
  3. 服务器代理

更多CORS请看彻底读懂前端跨域CORS

JSONP原理

由于浏览器的同源策略限制,不允许跨域请求;但是页面中的 script、img、iframe标签是例外,不受同源策略限制。

Jsonp 就是利用script标签跨域特性进行请求。

JSONP 的原理就是,先在全局注册一个回调函数,定义回调数据的处理;与服务端约定好一个同名回调函数名,服务端接收到请求后,将返回一段 Javascript,在这段 Javascript 代码中调用了约定好的回调函数,并且将数据作为参数进行传递。当网页接收到这段 Javascript 代码后,就会执行这个回调函数。

JSONP缺点:它只支持GET请求,而不支持POST请求等其他类型的HTTP请求。

缓存的理解

缓存分为强缓存和协商缓存。强缓存不过服务器,协商缓存需要过服务器,协商缓存返回的状态码是304。两类缓存机制可以同时存在,强缓存的优先级高于协商缓存。当执行强缓存时,如若缓存命中,则直接使用缓存数据库中的数据,不再进行缓存协商。

  1. 强缓存
  1. 协商缓存
  • Last-Modified 和 If-Modified-Since
  • Etag 和 If-None-Match

更多缓存内容请看前端也要懂Http缓存机制

XSS和CSRF区别

  1. 跨站脚本攻击(Cross Site Scripting),为了不和层叠样式表 CSS 混淆,故将跨站脚本攻击缩写为 XSS。恶意攻击者往 Web 页面里插入恶意 Script 代码,当用户浏览该页之时,嵌入其中 Web 里面的 Script 代码会被执行,从而达到恶意攻击用户的目的。

    谢小飞博客专用防爬虫链接,想要看最新的前端博客请点这里

  1. 跨站请求伪造(Cross-site request forgery),是伪造请求,冒充用户在站内的正常操作。我们知道,绝大多数网站是通过 cookie 等方式辨识用户身份,再予以授权的。所以要伪造用户的正常操作,最好的方法是通过 XSS 或链接欺骗等途径,让用户在本机(即拥有身份 cookie 的浏览器端)发起用户所不知道的请求。

区别:

  • 原理不同,CSRF是利用网站A本身的漏洞,去请求网站A的api;XSS是向目标网站注入JS代码,然后执行JS里的代码。
  • CSRF需要用户先登录目标网站获取cookie,而XSS不需要登录
  • CSRF的目标是用户,XSS的目标是服务器
  • XSS是利用合法用户获取其信息,而CSRF是伪造成合法用户发起请求

HTTP与HTTPS的区别

  • HTTP的URL由http://起始且默认使用端口80,而HTTPS的URL由https://起始且默认使用端口443
  • HTTP是超文本传输协议,信息是明文传输,HTTPS则是具有安全性的 SSL 加密传输协议
  • HTTP的连接很简单,是无状态的,HTTPS 协议是由 SSL+HTTP 协议构建的可进行加密传输、身份认证的网络协议,比 http 协议安全
  • 谢小飞博客专用防爬虫链接,想要看最新的前端博客请点这里

HTTP状态码

1xx表示客户端应该继续发送请求

2xx表示成功的请求

  • 200表示OK,正常返回信息
  • 201表示请求成功且服务器创建了新的资源
  • 202表示服务器已经接受了请求,但还未处理

3xx表示重定向

4xx表示客户端错误

  • 401表示服务器无法理解请求的格式
  • 402表示请求未授权
  • 403表示禁止访问
  • 404表示请求的资源不存在,一般是路径写错了

谢小飞博客专用防爬虫链接,想要看最新的前端博客请点这里

5xx表示服务器错误

  • 500表示最常见的服务器错误
  • 503表示服务器暂时无法处理请求

五、性能优化

六、参考

flex
CSS清除浮动
圣杯布局和双飞翼布局的理解与思考
一图秒懂函数防抖和函数节流
web前端面试总结
公司要求会使用框架vue,面试题会被问及哪些
30道Vue面试题
总结了17年初到18年初百场前端面试的面试经验
面试分享:两年工作经验成功面试阿里P6总结


本网所有内容文字和图片,版权均属谢小飞所有,任何媒体、网站或个人未经本网协议授权不得转载、链接、转贴或以其他方式复制发布/发表。如需转载请关注公众号【前端壹读】后回复【转载】。