【八股】前端

1. HTML

HTML5的语义化指的是合理正确的使用语义化标签来创建页面标签,正确的标签做正确的事,有利于SEO;即使在没有样式 CSS 情况下也以一种文档格式显示,并且是容易阅读的; header 、nav、main(文档的主体)、arcticle、section(某个区域)、aside(侧栏)、footer

data-属性包括两部分:语法 <element data-属性名=“属性值”>;属性名不应该包含任何大写字母,并且在前缀 "data-“之后必须有至少一个字符;属性值可以是任意字符串。(用户代理会完全忽略前缀为"data-” 的自定义属性。)
data-是H5新增的为前端开发者提供自定义的属性,这些属性集可以通过对象的 dataset 属性获取,不支持该属性的浏览器可以通过 getAttribute 方法获取。

  1. data-* 属性用于存储页面或应用程序的私有自定义数据。
  2. data-* 属性赋予我们在所有 HTML 元素上嵌入自定义 data 属性的能力。
  3. 存储的(自定义)数据能够被页面的 JavaScript 中利用,让页面拥有更好的交互体验(不需要使用 Ajax 或去服务端查询数据)。

SVG 是一种使用 XML 描述 2D 图形的语言。Canvas 通过 JavaScript 来绘制 2D 图形。
SVG 基于 XML,这意味着 SVG DOM 中的每个元素都是可用的。您可以为某个元素附加 JavaScript 事件处理器。在 SVG 中,每个被绘制的图形均被视为对象。如果 SVG 对象的属性发生变化,那么浏览器能够自动重现图形。
Canvas 是逐像素进行渲染的。在 canvas 中,一旦图形被绘制完成,它就不会继续得到浏览器的关注。如果其位置发生变化,那么整个场景也需要重新绘制,包括任何或许已被图形覆盖的对象。

在开发网页时,尽量将css放置在页面的header里,js不管是外部引入的或嵌入的js片段都建议放在页面的尾部
因为页面在加载时,浏览器会识别该文档问CSS,并行下载,不会停止对当前文档的加载。放在头部可以保证在加载html生成DOM tree的时候,就可以同时对DOM tree进行渲染,让页面逐步呈现,提高了用户体验, 可以防止闪跳,白屏或者布局混乱。
外部引入js文件阻塞其他资源下载,也会阻塞该js引入位置以下的页面的内容呈现,而且js可能会改变DOM tree的结构,需要一个稳定的DOM tree,所以要放置在页面的最下面。

  • 什么是渐进式渲染

渐进式渲染是用于提高网页性能(尤其是提高用户感知的加载速度),以尽快呈现页面的技术。
在以前互联网带宽较小的时期,这种技术更为普遍。如今,移动终端的盛行,而移动网络往往不稳定,渐进式渲染在现代前端开发中仍然有用武之地。
一些举例:
图片懒加载——页面上的图片不会一次性全部加载。当用户滚动页面到图片部分时,JavaScript 将加载并显示图像。
确定显示内容的优先级(分层次渲染)——为了尽快将页面呈现给用户,页面只包含基本的最少量的 CSS、脚本和内容,然后可以使用延迟加载脚本或监听DOMContentLoaded/load事件加载其他资源和内容。
异步加载 HTML 片段——当页面通过后台渲染时,把 HTML 拆分,通过异步请求,分块发送给浏览器。

  • meta viewport原理

meta viewport 标签的作用是让当前 viewport 的宽度等于设备的宽度,同时不允许用户进行手动缩放
viewport的原理:移动端浏览器通常都会在一个比移动端屏幕更宽的虚拟窗口中渲染页面,这个虚拟窗口就是 viewport; 目的是正常展示没有做移动端适配的网页,让他们完整的展示给用户;
Viewport :字面意思为视图窗口,在移动 web 开发中使用。表示将设备浏览器宽度虚拟成一个特定的值(或计算得出),这样利于移动 web 站点跨设备显示效果基本一致。移动版的 Safari 浏览器最新引进了 viewport 这个 meta tag,让网页开发者来控制 viewport 的大小和缩放,其他手机浏览器也基本支持。
在移动端浏览器当中,存在着两种视口,一种是可见视口(也就是我们说的设备大小),另一种是视窗视口(网页的宽度是多少)。
举个例子:如果我们的屏幕是 320 像素 * 480 像素的大小(iPhone4),假设在浏览器中,320 像素的屏幕宽度能够展示 980 像素宽度的内容。那么 320 像素的宽度就是可见视口的宽度,而能够显示的 980 像素的宽度就是视窗视口的宽度。
为了显示更多的内容,大多数的浏览器会把自己的视窗视口扩大,简易的理解,就是让原本 320 像素的屏幕宽度能够容下 980 像素甚至更宽的内容(将网页等比例缩小)。
Viewport 属性值
width 设置 layout viewport 的宽度,为一个正整数,或字符串"width-device"
initial-scale 设置页面的初始缩放值,为一个数字,可以带小数
minimum-scale 允许用户的最小缩放值,为一个数字,可以带小数
maximum-scale 允许用户的最大缩放值,为一个数字,可以带小数
height 设置 layout viewport 的高度,这个属性对我们并不重要,很少使用
user-scalable 是否允许用户进行缩放,值为"no"或"yes", no 代表不允许,yes 代表允许这些属性可以同时使用,也可以单独使用或混合使用,多个属性同时使用时用逗号隔开就行了。

  • 4.页面导入样式时,使用link和@import有什么区别?

link是xhtml标签,除了加载css外,还可以定义RSS等其他事务;@import属于CSS范畴,只能加载CSS
link引用CSS时候,页面载入时同时加载;@import需要在页面完全加载以后加载,而且@import被引用的CSS会等到引用它的CSS文件被加载完才加载
link是xhtml标签,无兼容问题;@import是在css2.1提出来的,低版本的浏览器不支持
link支持使用javascript控制去改变样式,而@import不支持
link方式的样式的权重高于@import的权重
import在html使用时候需要<style type="text/css">标签

  • 介绍一下你对浏览器内核的理解

主要分成两部分:渲染引擎(Layout Engine或Rendering Engine)和JS引擎。
渲染引擎:负责取得网页的内容(HTML、XML、图像等等)、整理讯息(例如加入CSS等),以及计算网页的显示方式,然后会输出至显示器或打印机。浏览器的内核的不同对于网页的语法解释会有不同,所以渲染的效果也不相同。
JS引擎:解析和执行javascript来实现网页的动态效果。
最开始渲染引擎和JS引擎并没有区分的很明确,后来JS引擎越来越独立,内核就倾向于只指渲染引擎。
其他

2. CSS

伪类:用于向某些选择器添加特殊的效果
伪元素:用于将特殊的效果添加到某些选择器,伪元素代表了某个元素的子元素,这个子元素虽然在逻辑上存在,但却并不实际存在于文档树中

:hover 将样式添加到鼠标悬浮的元素
:active 将样式添加到被激活的元素
:focus 将样式添加到获得焦点的元素
:link 将样式添加到未被访问过的链接
:visited 将样式添加到被访问过的链接
:first-child 将样式添加到元素的第一个子元素

::first-line 将样式添加到文本的首行
::first-letter 将样式添加到文本的首字母
::before 在某元素之前插入某些内容
::after 在某元素之后插入某些内容

  • CSS实现隐藏元素的方式

display:none 不会渲染该元素,所以该元素不会占位置,也不会响应绑定的事件;
visibility: hidden 元素在页面中会保留位置,但是不会响应绑定的事件;
opacity: 0 将元素的透明度设置为0, 元素在页面中会保留位置,且也能响应元素绑定的监听事件;
transform: scale(0, 0); 将元素缩放为0,元素保留位置,监听事件无效
position: relative / absolute; 让元素移出可视范围内,绝对定位不保留位置,相对定位保留位置。

  • 水平居中和垂直居中

水平居中
行内元素:父元素设置text-align:center;
定宽块元素:设置 margin-left / margin-right: auto;
不定宽块状元素: 将需要居中的元素转为行内元素或者行内块元素,再给父元素设置 text-align:center;
其它块元素:
   a. 父元素设置 display:flex; justify-content:center;
   b. 父元素开启相对定位,position: relative; 要居中的元素开启绝对定位,position: absolute;
       要居中的元素先向右平移父元素宽度的50%,left: 50%;
       然后向左平移回自身宽度的50%,transform: translateX(-50%);

垂直居中
行内元素: 父元素设置 line-height: 50px;
定宽块元素:设置 margin-top / margin-bottom: auto;
其它块元素:
   a. display: flex; align-items: center;
   b. 父元素开启相对定位,position: relative; 要居中的元素开启绝对定位,position: absolute;
       要居中的元素先向下平移父元素高度的50%,top: 50%;
       然后向上平移回自身高度的50%,transform: translateY(-50%);

  1. 设置元素可见性: none 不可见;
  2. 设置元素本身的盒子模型类型:block、inline;
  3. 设置内部元素的布局方式:flex、table、grid、ruby、 flow、flow-root;
  4. 在使用 table 和 ruby 这种复杂的布局方式时,设置子元素的内部显示类型:table-xxx、ruby-xxx;
  • 请解释*{box-sizing:border-box;}的作用,并说明使用它的好处

怪异盒模型,设置width和height时不仅包含了内容区的宽高,还包括了两侧的padding和border的距离。盒子模型会自动根据padding和border的值来调整content的值,就不需要手动调整。

  • 浮动元素引起的问题和解决办法,元素浮动后的display值

display
table-xxx: block;
inline、inline-table: block
inline-flex: flex
inline-grid: grid
其他值不变
一个元素浮动之后,它会被移出正常的文档流,然后向左或者向右平移,一直平移直到碰到了所处的容器的边框,或者碰到另外一个浮动的元素。
两个浮动元素的垂直外边距将不会折叠。当应用于浮动元素时,它将元素的外边界移动到所有相关的浮动元素外边框边界的下方。这会影响后面浮动元素的布局,后面的浮动元素的位置无法高于它之前的元素。

问题
1.由于浮动元素已脱离文档流,所以父元素无法被撑开,影响与父级元素同级的元素。
2.影响与浮动元素同级的非浮动元素:与它相邻的文本和行内元素会被它挤到后面,形成文字环绕的效果;在它后面的块元素会依次向上移动,并被它挡住。

解决方法
1.父元素:
a. 开启 BFC,或者添加clearfix样式;
b. 在浮动元素后面添加一个空白标签,为空白标签添加 clear: both; 样式,如:
   为父元素添加 after 伪元素(父元素添加 class=“clearfix”);
   为父元素添加after 和 before 双伪元素(父元素添加 class=“clearfix”);
2.与它同级的元素:
设置 clear: left/right/both;

::after{
	    content: "";
	    display: block;
	    clear:both;
}
.clearfix::before, .clearfix::after{
    content: "";
    display: table; 
    clear: both;
}

BFC(Block formatting context)直译为“块级格式化上下文”。BFC它是一个独立的渲染区域,只有Block-level box(块元素)参与,它规定了内部的Block-level box如何布局,并且与这个区域外部毫不相关。可以理解成:创建了 BFC的元素就是一个独立的盒子,里面的子元素不会在布局上影响外面的元素(里面怎么布局都不会影响外部)。(BFC仍属于文档中的普通流)

  1. 根元素()
  2. 浮动元素(float 值不为 none)
  3. 绝对定位元素(position 值为 absolute 或 fixed)
  4. 行内块元素(display 值为 inline-block)
  5. 表格单元格(display 值为 table-cell,HTML 表格单元格默认值)
  6. 表格标题(display 值为 table-caption,HTML 表格标题默认值)
  7. 匿名表格单元格元素(display 值为 table、table-row、 table-row-group、table-header-group、table-footer-group(分别是 HTML table、tr、tbody、thead、tfoot 的默认值)或 inline-table)
  8. overflow 值不为 visible、clip 的块元素
  9. display 值为 flow-root 的元素
  10. contain 值为 layout、content 或 paint 的元素
  11. 弹性元素(display 值为 flex 或 inline-flex 元素的直接子元素),如果它们本身既不是 flex、grid 也不是 table 容器
  12. 网格元素(display 值为 grid 或 inline-grid 元素的直接子元素),如果它们本身既不是 flex、grid 也不是 table 容器
  13. 多列容器(column-count 或 column-width (en-US) 值不为 auto,包括column-count 为 1)
    column-span 值为 all 的元素始终会创建一个新的 BFC,即使该元素没有包裹在一个多列容器中 (规范变更, Chrome bug)

1)防止外边距重叠。
bfc导致的属于同一个bfc中的子元素的margin重叠(Box垂直方向的距离由margin决定。属于同一个BFC的两个相邻Box的margin会发生重叠)
我们可以在div外面包裹一层容器,并触发该容器生成一个BFC。那么两个div便不属于同一个BFC,就不会发生margin重叠了。
2)清除浮动的影响
块级子元素浮动,如果块级父元素没有设置高度,其会有高度塌陷的情况发生。
原因:子元素浮动后,均开启了BFC,父元素不会被子元素撑开。
解决方法:由第六条原理得,计算BFC的高度时,浮动元素也参与计算。所以只要将父容器设置为bfc
就可以把子元素包含进去:这个容器将包含浮动的子元素,它的高度将扩展到可以包含它的
子元素,在这个BFC,这些元素将会回到页面的常规文档流。
3)防止文字环绕

  • link和@import引入css的区别

区别
1.从属关系: @import只有导入样式表的效果,而link不仅可以导入css文件还可以定义RSS、ref等
2.加载顺序: @import引入的css在页面加载完毕后被加载,link标签引入的css是同时被加载
3.兼容性: @import是CSS2.1的语法,需要IE5+中识别,link是HTML的元素标签,不存在兼容性问题
4.权重: link引入的css样式权重大于@import引入的样式
5.DOM可控性: 可通过js操作DOM来插入link标签改变样式,但不能通过插入@import来改变样式(因为dom方法是基于文档的)
建议
推荐使用link标签插入样式,而不推荐使用@import的方式

  • 解释一下css3的flexbox,以及适用场景

手机端中比较常用的三段式布局, 头尾固定高度 中间自适应 它可以修改父元素下所有子元素的位置 和排序方式 相对于浮动 更加强大 要注意的是指定flex之后,子元素的float、clear和vertical-align属性将失效

  • inline和inline-block的区别

block; 块元素:一个块状元素独占一行;可设宽、高、行高、顶和底边距离;宽默认为父元素的100%;
inline;行内元素:和其他元素共处一行;不可设宽、高、行高、顶和底边距离;宽即所包含的文字图片之宽;
inline-block 行内块元素:和其他元素共处一行;可设宽、高、行高、顶和底边距离;<input> 、<img> 、<button> 、<texterea> 、<label>

.只有垂直方向的外边距会出现合并,水平方向上不会出现外边距合并;垂直方向上相邻的元素才会有外边距合并的问题。
兄弟元素间的外边距合并
1.若两者都是正值,取值为两者间最大值;
2.若两个外边距一正一负,取值为两者之和;
3.若两个外边距都是负值,取值为绝对值较大的外边距;
父子元素间的外边距合并
父元素没有设置 border 和 padding 时,父子元素的上下外边距会出现合并。

<div class="header"></div>  
	<div class="container">
        <div class="center"></div>
        <div class="left"></div>
        <div class="right"></div>
	</div>
<div class="footer"></div>

圣杯布局:将父元素的padding值空出来为左右部分布局

@leftWidth:200px;
@rightWidth:150px;
.header, .footer{
    width: 100%;
    height: 30px;
    background-color: rgb(189, 205, 219);
}
.footer{
    clear:both;  // 清除浮动造成的影响
}
.container{  
    background-color: aliceblue;
    overflow: hidden;  // 开启 BFC
    padding: 0 @rightWidth 0 @leftWidth; // 两侧留出left和right的宽度
    .all{
        float: left;
        position: relative;  // 相对于自己的初始位置进行定位 
        height: 400px;
    }
    .center{
        width: 100%;   
        background-color: rgb(233, 255, 231);
    }
    .left{
        width: @leftWidth;
        background-color: azure;
        margin-left: -100%;
        left: -@leftWidth;
    }
    .right{
        width: @rightWidth;
        background-color: azure;
        margin-right: -@rightWidth;
    }
}

双飞翼布局:将父元素的margin值空出来为左右部分布局

@leftWidth:200px;
@rightWidth:150px;
.header, .footer{
    width: 100%;
    height: 30px;
    background-color: rgb(189, 205, 219);
}
.footer{
    clear:both;  // 清除浮动造成的影响
}
.container{  
    background-color: aliceblue;
    position: relative;  // 开启 BFC
    margin: 0 @rightWidth 0 @leftWidth; // 两侧留出left和right的宽度
    .all{
        float: left;
        position: relative;  // 相对于自己的初始位置进行定位 
        height: 400px;
    }
    .center{
        width: 100%;   
        background-color: rgb(233, 255, 231);
    }
    .left{
        width: @leftWidth;
        background-color: azure;
        left: -100%;
        margin-left: -@leftWidth;
    }
    .right{
        width: @rightWidth;
        background-color: azure;
        margin-right: -@rightWidth;
    }
}

flex布局

<div class="header"></div>  
    <div class="container">
        <div class="left  all"></div>
        <div class="center all"></div>
        <div class="right  all"></div>
    </div>
<div class="footer"></div>
@leftWidth:200px;
@rightWidth:150px;
.header, .footer{
    width: 100%;
    height: 30px;
    background-color: rgb(189, 205, 219);
}
.container{  
    background-color: aliceblue;
    display: flex;
    .all{
        height: 400px;
    }
    .center{
        flex: 1;   
        background-color: rgb(233, 255, 231);
    }
    .left{
        width: @leftWidth;
        background-color: azure;
    }
    .right{
        width: @rightWidth;
        background-color: azure;
    }
}
  • 移动端布局方案
  • overflow:hidden的缺点

overflow:hidden并不是万能的,要想彻底剪裁它的所有子元素,它不但要有overflow:hidden,而且还要作为所有子元素的包含块(position定位为relative或absolute)。

  • padding百分比是相对于父级宽度还是自身的宽度

  • css3动画,transition和animation的区别,animation的属性,加速度,重力的模拟实现

  • CSS3 如何实现旋转图片transform: rotate

  • CSS中的单位

  • CSS 选择器的优先级

内联样式,优先级1000
id选择器,优先级100
类和伪类,优先级10
元素选择器,优先级1
通配选择器,优先级0
继承的样式没有优先级
当选择器包含多种选择器时,需要将多种选择器的优先级相加然后进行比较。但是注意,选择器的优先级计算不会超过他的最大数量级,如果选择器的优先级一样,则使用靠后的样式。
并集选择器的优先级时单独计算。
可以在样式的最后添加一个!important,则此时该样式会获得一个最高的一个优先级,将会超过所有的样式甚至超过内联样式,所以在开发中尽量避免使用。

  • 雪碧图

解决图片闪烁问题:将多个小图片保存到一个大图片中,然后通过调整 background-position 来显示图片的不同部分,这样图片就可以同时加载到网页中,有效避免闪烁问题。这项技术在网页中应用十分广泛,被称为 CCS-Sprite,这种图被称为雪碧图。

媒体查询的目的是为了页面能够在不同的显示器 ,不同大小的屏幕下也能够正常显示;原理是定义一系列的条件,用这些条件去检查显示器设备,如果符合规定的条件就应用对应的css样式。

  • CSS 的加载是异步的吗?表现在什么地方?

浏览器会同步加载外部的CSS,在下载和解析CSS时会影响所有页面呈现。也就是加载 css 会阻塞 dom 树的渲染,但并不会阻塞 DOM 树的构建,理解为:在加载 css 同时,也在构建 dom 树,只是没有应用样式。提前加载资源:rel=“preload” 属性,告诉浏览器这个资源随后会用到,需提前加载好,当浏览器需要时直接从缓存中拿

  • 常遇到的浏览器兼容性问题有哪些?常用的hack的技巧

浏览器默认的margin和padding不同;重置样式,都设为0
超链接访问过后hover样式就不出现了,被点击访问过的超链接样式不再具有hover和active了;改变CSS属性的排列顺序:a:link {} -> a:visited {} -> a:hover {} -> a:active {}

  • 解释一下"::before"和":after"中的双冒号和单冒号的区别

在 CSS 中伪类一直用 : 表示,如 :hover, :active 等
伪元素在CSS1中已存在,当时语法是用 : 表示,如 :before 和 :after
后来在CSS3中修订,伪元素用 :: 表示,如 ::before 和 ::after,以此区分伪元素和伪类
由于低版本IE对双冒号不兼容,开发者为了兼容性各浏览器,继续使使用 :after 这种老语法表示伪元素
综上所述:::before 是 CSS3 中写伪元素的新语法; :after 是 CSS1 中存在的、兼容IE的老语法

  • 对移动端开发了解多少?(响应式设计、Zepto;@media、viewport、JavaScript 正则表达式判断平台。)

3. JS

  • js的基本类型有哪些?引用类型有哪些?null 和 undefined 的区别。

基本数据类型:Number、String、Boolean、Null、Undefined、Symbol、bigInt
引用数据类型:Function、Object、Array、Date、RegExp(正则)
undefined:
变量被声明了但没有赋值时,就等于 undefined
调用函数时,应该提供的参数没有提供,该参数等于 undefined
对象没有赋值的属性,该属性的值为 undefined
函数没有返回值时,默认返回 undefined
null: 代表“空值”,代表一个空对象指针,使用typeof运算得到 “object”,所以你可以认为它是一个特殊的对象值。

var n = null;
var u = undefined;

console.log(typeof n); // object
console.log(typeof u); // undefined
console.log(n == undefined); // true
console.log(n === undefined); // false
console.log(u == null); // true
console.log(u === null); // false
console.log(Number(n)); // 0
console.log(Number(u)); // NaN
  • 判断变量类型
  1. instanceof: 只能判断引用数据类型,语法某个实例对象 instanceof 某个构造函数, 其原理是 检测构造函数的 prototype 属性是否出现在实例对象的原型链上。
    检测 Number、String、Boolean 输出false
    检测 Null、Undefined 报错(Null/Undefined 未定义)
    检测 Function、Array、Object、Date、RegExp等输出true
  2. typeof:
    检测 Number、String、Boolean、Undefined、Function 时输出数据类型Number / String / Boolean / Undefined / Function
    检测 其他类型的数据时输出Objectconsole.log(typeof null); // object);
  3. constructor: 实例对象显式原型上的constructor指向它的构造函数,根据实例对象寻找属性的顺序,若实例对象上没有实例属性或方法,就去原型链上寻找,因此,实例对象也能使用constructor属性。
    检测 Null、Undefined 报错(没有constructor属性)
    检测其他类型的数据输出它们的构造函数[Function: Number]
  4. Object.prototype.toString.call() 可以判断任意一种数据类型。
  5. ES6新增方法:判断 Number 和 Array
    console.log(Number.isFinite(1)); // true
    console.log(Number.isInteger(1)); // true
    console.log(Number.isNaN(1)); // false
    console.log(Number.isSafeInteger(1)); // true
    console.log(Number.isNaN(NaN)); // true
    console.log(Array.isArray([1,2,3])); // true
  • 如何判断一个变量是Array类型?如何判断一个变量是Number类型?
var arr = [1, 2, 3];
console.log(arr instanceof Array); // true
console.log(Array.isArray(arr)); // true
console.log(arr.constructor === Array); // true
console.log(Object.prototype.toString.call(arr)) // [object Array]
var num = 1;
console.log(typeof num); // number
console.log(num.constructor === Number); // true
console.log(Object.prototype.toString.call(num)) // [object Number]
  • 引用类型和基本类型有什么区别?

JS中的变量都保存在栈内存中:
基本数据类型的值直接在栈内存中存储,值与值之间独立存在,修改一个变量不会影响其他的变量;
对于引用数据类型,变量保存的是对象的地址,对象保存在堆内存中,每创建一个新的对象,就会在堆内存中开辟出一个新的空间。如果两个变量保存的是同一个地址,那么修改一个变量之后,另一个会跟着改变。

事件的冒泡指事件的向上传导,当后代元素上的事件被触发时,祖先元素的相同事件也会被触发。在开发中,大部分情况下冒泡都是有用的。如果不希望事件冒泡,可以通过事件对象取消冒泡event.cancelBubble=true;event.stopPropagation();
事件的传播分成三个阶段:
捕获阶段:从最外层的祖先元素向目标元素进行事件的捕获,默认此时不会触发事件;
目标阶段:事件捕获到目标元素,捕获结束后开始在目标元素上触发事件;
冒泡阶段:事件从目标元素向祖先元素传递,依次触发祖先元素上的事件。
IE8及以下浏览器没有捕获阶段。

  • 事件委托(手写例子)

只绑定一次事件,即可应用到多个元素上,即使元素是后来添加的。因此可以考虑将事件绑定给这些元素共同的祖先元素上,这样,当后代元素上的事件触发时,会一直冒泡到祖先元素上,从而通过祖先元素的响应函数来处理事件。需要判断触发事件的元素是不是期望的元素。通过委派可以减少事件绑定的次数。

<div class="outer">
    <div class="up smile"></div>
    <div class="mid smile"></div>
    <div class="down smile"></div>
    <div class="first"></div>
    <div class="second"></div>
    <div class="third"></div>
</div>
var inner = document.querySelectorAll(".outer div");
var outer = document.querySelector(".outer");
outer.onclick = function(event){
    console.log(event.target);
    if(event.target.id == 'smile'){
        console.log(event.target.className);
    }
}
  • 阻止默认事件

在事件的回调函数中加上event.preventDefault();
或者在回调函数的末尾 return false;

  • 对闭包的理解?什么时候构成闭包?闭包的实现方法?闭包的优缺点?

当一个嵌套的内部(子)函数引用了嵌套的外部(父)函数的变量(函数) 时,就产生了闭包。闭包存在于嵌套的内部函数中,可以认为闭包就是能够读取其他函数内部变量的函数。函数内部的变量会一直存在于内存中,不会被立即释放。
产生闭包的条件: 存在函数嵌套;内部函数引用了外部函数的数据;执行了内部函数的定义。
闭包在嵌套的内部函数定义执行完时产生(不需要调用),在嵌套的内部函数成为垃圾对象时死亡。
优点:
延长局部变量的生命周期
使函数内部的变量在函数执行完后仍然存活在内存中;
在函数外部也可以操作(读 / 写)函数内部的数据(变量 / 函数)
缺点:
函数执行完后,函数内的局部变量没有释放,占用内存时间会变长
容易造成内存泄露

  • 内存泄漏的原因和场景

原因:占用的内存没有及时释放,内存泄露积累多了就容易导致内存溢出(一种程序运行出现的错误,当程序运行需要的内存超过了剩余内存时抛出该错误。);
常见的内存泄露情况:意外的全局变量、没有及时清理的计时器或回调函数;闭包;

  • call,apply,bind

call和apply都是改变上下文中的this并立即执行这个函数,bind方法不立即执行而是返回了一个新的函数,之后随时调用,并且可以将参数在执行的时候添加;
call() 和 apply() 调用时可以指定一个对象作为第一个参数,此时这个对象就会成为函数执行时的 this;后面传入的参数都会作为实参传递给函数fun(); 注意:call() 方法可以将实参在对象之后依次传递,apply()方法需要将实参封装到一个数组中统一传递。

  • 显示原型和隐式原型,手绘原型链,原型链是什么?为什么要有原型链

每个 函数function 都有一个属性prototype,即显式原型;每个 实例对象 都有一个属性__proto__,即隐式原型。
函数的prototype属性在定义函数时自动添加,默认值是一个空 Object 实例对象(Object除外,Object.prototype = null);
实例对象的__proto__属性在创建对象时自动添加,默认值为其构造函数的 prototype 属性值。
可以直接操作显式原型,但不能直接操作隐式原型(ES6之前)。

查找对象的属性( 方法 ): 访问一个对象的属性时,先在自身属性中查找,找到就返回;如果没找到,那么沿着__proto__这条链向上查找,找到就返回;如果查到 Object 的原型对象(Object.prototype.proto = null)都没找到,那么返回 undefined。
function Fun()相当于var Fun = new Function(),即每一个函数都是Function的实例,因此每一个函数都有一个__proto__属性;由于Function = new Function(),这导致了Function()的显式原型和隐式原型指向同一个位置,即Function.proto === Function.prototype;最终,所有函数的__proto__都是一样的。
构造函数的实例对象自动拥有构造函数原型对象的属性,利用的就是原型链。

创建一个新的对象obj
将对象与构造函数通过原型链连接起来
将构造函数中的this绑定到新建的对象obj上
返回新建的对象

function Person(name, age, gender, ads){
	this.name = name;
	this.age = age;
	this.gender = gender;
	this.address = ads;
	this.sayName = function(){
		console.log(this.name);
	}
}
function newPerson(fun, ...arguments){
    var obj = {};
    obj.__proto__ = fun.prototype;
    let result = fun.apply(obj, arguments);
    return (result instanceof Object)? result : obj;
}
var pers = newPerson(Person, "kirlant", 16, "female", "empire");
console.log(typeof pers);
console.log(pers);
pers.sayName();
var idx = 0;
var timer = setInterval(function(){
    ++idx;
    console.log('hello, '+ idx);
    if(idx == 5){
        clearInterval(timer);
    }
}, 2000);
  • 指出JS的宿主对象和原生对象的区别,为什么扩展JS内置对象不是好的做法?有哪些内置对象和内置函数?

内置对象:ECMAScript实现提供的、独立于宿主环境的所有对象,在ECMAScript程序开始执行时出现;Global和Math(它们也是本地对象,根据定义,每个内置对象都是本地对象),内置对象是本地对象的一种
原生对象:ECMA-262定义的类(引用类型),Object、Function、Array、String、Boolean、Number、Date、RegExp、Error
宿主对象:由JS运行环境提供的对象,主要指由浏览器提供的对象,比如BOM、DOM……

Attribute就是dom节点自带的属性,例如 id、class、title等; Property是这个DOM元素作为对象,其附加的内容,例如childNodes、firstChild等。

  • document load 和document DOMContentLoaded两个事件的区别

DOMContentLoaded: DOM解析完成即触发此事件,不等待styles, images等资源的加载;
load: 页面上所有的资源(图片,音频,视频等)被加载以后才会触发load事件,简单来说,页面的load事件会在DOMContentLoaded被触发之后才触发。
结合DOM文档加载的加载步骤,DOMContentLoaded事件/Load事件,触发时机如下:
1、解析HTML结构。
2、加载外部脚本和样式表文件。
3、解析并执行脚本代码。// 部分脚本会阻塞页面的加载
4、DOM树构建完成。//DOMContentLoaded 事件
5、加载图片等外部文件。
6、页面加载完毕。//load 事件

  • === == , [] === [], undefined === undefined,[] == [], undefined == undefined
console.log([] == []); // false
console.log([] === []); // false
console.log(undefined == undefined ); // true
console.log(undefined === undefined ); // true
console.log("undefined" === undefined); // false
console.log("undefined" == undefined); // false
  • 什么是"use strict",好处和坏处

"严格模式"是一种在JavaScript代码运行时自动实行更严格解析和错误处理的方法。这种模式使得Javascript在更严格的条件下运行。
严格模式下,变量必须先声明再使用,不能删除已经声明好的变量,全局作用域中的thisundefined而不是window,函数参数不能重名。

优点:
消除Javascript语法的一些不合理、不严谨之处,减少一些怪异行为;
消除代码运行的一些不安全之处,保证代码运行的安全;
提高编译器效率,增加运行速度;
为未来新版本的Javascript做好铺垫。
缺点:
现在网站的 JS 都会进行压缩,一些文件用了严格模式,而另一些没有。这时这些本来是严格模式的文件,被 merge 后,这个串就到了文件的中间,不仅没有指示严格模式,反而在压缩后浪费了字节。

  • JS如何实现重载和多态

同一个方法,通过传递实参的不同(arguments)完成不同的功能,我们把这个也可以理解为重载。
根据arguments个数实现重载;检测数据类型实现重载;
多态:通过指向父类的引用,来调用在不同子类中实现的方法。

function test(){
    var l = arguments.length;
    if(l==0){
        return 0;
    }else if(l==1){
        return arguments[0];
    }else{
        return arguments[l-1]+arguments[l-2];
    }
}

function test(){
    let l = arguments.length;
    let res = [];
    for(let i=0; i<l; ++i){
        res.unshift(typeof arguments[i]); 
    }
    return res;
}
console.log(test());  // []
console.log(test(1));  // [ 'number' ]
console.log(test([1,2,3], 4));  // [ 'number', 'object' ]

// 元素自身的宽/高, 内容区 + 内边框 + 边框
console.log(upDiv.offsetWidth);
console.log(upDiv.offsetHeight);
// 元素与最近的开启了定位的祖先元素的距离, 元素的margin + 祖先的padding
console.log(upDiv.offsetLeft);
console.log(upDiv.offsetTop);
// 元素的宽/高, 内容区 + 内边距
console.log(upDiv.clientWidth);
console.log(upDiv.clientHeight);
// 元素的边框
console.log(upDiv.clientLeft);
console.log(upDiv.clientTop);
// 元素的滚动条位置, 内容区 + 内边距
console.log(upDiv.scrollWidth);
console.log(upDiv.scrollHeight);

元素内无内容或者内容不超过可视区,滚动不出现或不可用的情况下:
scrollWidth=clientWidth,两者皆为内容可视区的宽度; offsetWidth为元素的实际宽度。
元素的内容超过可视区,滚动条出现和可用的情况下: scrollWidth>clientWidth。
scrollWidth为实际内容的宽度, clientWidth是内容可视区的宽度, offsetWidth是元素的实际宽度。

  • 如何实现图片滚动懒加载

图片懒加载就是当图片非常多,一屏无法完全显示的时候,我们就会把暂时看不到的图片,延时等滚轴滚动到时再进行加载,也算是一种性能优化,图片分开下载就是大幅度提高了首次加载页面的速度;
1.监听图片高度实现懒加载: 当图片距离浏览器顶部的高度小于浏览器视窗的高度时加载图片; 问题在于即使资源已经加载完毕,也会一直重复触发加载的动作;
2.IntersectionObserver函数:观察元素是否对于屏幕的可视区域可见,使用observe方法进行监听,unobserve方法取消监听,能够避免重复触发的问题。

<!-- 先用一张同样大小的图片占位 -->
<img data-src="./imgs/bf.jpg" src="./imgs/0.jpg" alt="">
<img data-src="./imgs/ssm.jpg" src="./imgs/0.jpg" alt="">
<img data-src="./imgs/xh.jpg" src="./imgs/0.jpg" alt="">
// 1. 为 window 绑定滚轮事件,随时判断图片距离可视窗口的高度,刀到了应该加载的时候,就将图片的真正路径从自定义属性中取出来,给src
var imgs = document.querySelectorAll('img');
window.addEventListener('scroll', function(){
    imgs.forEach(img => {
        let imgToTop = img.getBoundingClientRect().top;
        if(imgToTop < window.innerHeight){
            let imgsrc = img.getAttribute('data-src');
            img.setAttribute('src', imgsrc);
            console.log('已加载'+imgsrc);
        }
    });
});
var imgs = document.querySelectorAll('img'); // 被监听的元素
// @param: entries 一个数组,里面是所有要观察的对象
const observer = new IntersectionObserver(entries => {
    entries.forEach( entry =>{
        if(entry.isIntersecting){ // 如果该对象出现在可视区域里
            let img = entry.target; // 要展示的图片节点
            img.setAttribute('src', img.getAttribute('data-src'));
            observer.unobserve(img); // 图片加载完成,停止监听此元素
        }
    });
});
imgs.forEach(img =>{
    observer.observe(img); // 开始监听元素
});
var arr = [1, 2, 3];

var arr_copy = [].concat(arr);
var arr_copy = arr.slice();
var arr_copy = [...arr];
var arr_copy = JSON.parse(JSON.stringify(arr));
var arr_copy = [];
arr.forEach(a =>{ arr_copy.push(a);});

arr_copy[0] = 4;
console.log(arr, arr_copy); //[1, 2, 3] [4, 2, 3]
var obj = {
    name: 'kirlant',
    age:1002
};

var obj_copy = {...obj}; // 只能拷贝一层
var obj_copy = JSON.parse(JSON.stringify(obj));// 可以拷贝多层
var obj_copy = Object.assign({}, obj);// 只能拷贝一层

obj_copy.name = 'garcia';
console.log(obj, obj_copy); //{ name: 'kirlant', age: 1002 } { name: 'garcia', age: 1002 }

script(主程序代码)——>process.nextTick——>promise——>setTimeout

setTimeout(function(){console.log(1)},0);

new Promise(function(resolve,reject){
   console.log(2);
   setTimeout(function(){resolve()},0)
}).then(function(){console.log(3)
}).then(function(){console.log(4)});
console.log(6);
// 2 6 1 3 4

原始事件模型、标准事件模型、IE事件模型

js的垃圾回收机制是为了防止内存泄漏(已经不需要的某一块内存还一直存在着),垃圾回收机制就是不停寻找这些不再使用的变量,并且释放掉它所指向的内存。
垃圾回收有两种方式: 标记清除、引用计数
标记清除:大部分浏览器使用这种垃圾回收,当变量进入执行环境(声明变量)的时候,垃圾回收器将该变量进行了标记,当该变量离开环境的时候,将其再度标记,随之进行删除。
引用计数:这种方式常常会引起内存的泄露,主要存在于低版本的浏览器。它的机制就是跟踪某一个值得引用次数,当声明一个变量并且将一个引用类型赋值给变量得时候引用次数加1,当这个变量指向其他一个时引用次数减1,当为0时出发回收机制进行回收。

在标签中直接绑定、获取DOM元素后动态绑定、事件监听addEventListener

  • DOM事件中 target 和 currentTarget 的区别

target是事件触发的真实元素, currentTarget是事件绑定的元素
事件处理函数中的this指向是中为currentTarget
currentTarget和target,有时候是同一个元素,有时候不是同一个元素 (因为事件冒泡):
当事件是子元素触发时,currentTarget为绑定事件的元素,target为子元素
当事件是元素自身触发时,currentTarget和target为同一个元素。

抛出异常后:
如果子函数没有进行异常处理,则把错误继续抛给调用它的父函数,如果它的父函数也没有对异常的处理,则沿着函数调用栈继续往下抛,直至抛到最顶层的函数中,也就是全局环境中,然后如果也没有,则程序终止运行,js线程被终止运行了;
如果使用try catch捕获异常,出现的异常将会在catch进行处理(error.stack的信息会通过一个error传递给catch),想怎么处理就怎么处理,处理完后后续的代码会继续执行;
如果用try catch finaily 捕获异常,不管有没有异常,都会调用finaily;

WebSocket规范定义了在Web浏览器和服务器之间建立“套接字”连接的API。 简而言之:客户端和服务器之间存在持久连接,并且双方可以随时开始发送数据。
客户端通过称为WebSocket握手的过程建立WebSocket连接。该过程从客户端向服务器发送常规HTTP请求开始。此请求中包含Upgrade头信息,通知服务器客户端希望建立WebSocket连接。
如果服务器支持WebSocket协议,它将同意Upgrade,并将通过响应中的Upgrade头进行通信。
连接建立后,服务器通过Upgrade进行回复.
握手完成,初始HTTP连接被替换为使用相同底层TCP / IP连接的WebSocket连接。此时,任何一方都可以开始发送数据。
借助WebSocket可以随心所欲地传输尽可能多的数据,而不会产生与传统HTTP请求相关的开销。数据通过WebSocket作为消息传输,每个消息由一个或多个包含您要发送的数据(有效负载)的帧组成。 为了确保消息在到达客户端时能够被正确地重建,每个帧都以4-12字节的有效负载数据作为前缀。 使用这种基于帧的消息传递系统有助于减少传输的非有效载荷数据量,从而显着减少延迟。

  • 手指点击可以触控的屏幕时,是什么事件?
  • 什么是函数柯里化?以及说一下JS的API有哪些应用到了函数柯里化的实现
function sum(x, y, z) {
    return x + y + z
}

function curry(fn) {
    let len = fn.length;
    let fnArgs = [];
    return function argsCollector(...args){
        fnArgs.push(...args);
        if (fnArgs.length === len) {  // 参数收集完成
            // return fn.apply(this, fnArgs);
            return fn.call(this, ...fnArgs);
        } else {                     // 继续收集参数
            return argsCollector;
        }
    }
}

const curriedSum = curry(sum);
const res = curriedSum(1);
console.log(res); // [Function: argsCollector]
console.log(res(2, 3)); // 6

async 会将其后的函数的返回值封装成一个 Promise 对象,而 await 会等待这个 Promise 完成,并将其 resolve 的结果返回出来。
async 用于申明一个 function 是异步的,而 await 用于等待一个异步方法执行完成。async 函数(包含函数语句、函数表达式)会返回一个 Promise 对象,如果在函数中 return 一个直接量,async 会把这个直接量通过 Promise.resolve() 封装成 Promise 对象。在没有 await 的情况下执行 async 函数,它会立即执行,返回一个 Promise 对象,并且不会阻塞后面的语句。
await 是个运算符,用于组成表达式,await 表达式的运算结果取决于它等的东西。如果它等到的不是一个 Promise 对象,那 await 表达式的运算结果就是它等到的东西。如果它等到的是一个 Promise 对象,await 就忙起来了,它会阻塞后面的代码,等着 Promise 对象 resolve,然后得到 resolve 的值,作为 await 表达式的运算结果。

  • 卡顿现象-节流和防抖

事件触发非常频繁,每一次触发都需要执行回调函数时,如果事件很短并且回调函数内部有计算,那么很可能出现浏览器卡顿。
方案: 闭包 / 定时器 / lodash插件
节流:在规定的间隔时间范围内不会重复触发回调,只有大于这个时间间隔才会触发回调,把频繁触发变为少量触发。
防抖:前面所有的触发都被取消,最后一次执行在规定的时间之后才会触发,也就是说连续快速的触发只执行一次。

/*  闭包实现
    @param: fn 要被节流的函数
    @param: delay 设置的执行周期
*/
function throttle(fn, delay){
    // 记录上一次函数被触发的时间
    var lastTime = 0;
    // 闭包
    return function(){
        // 记录当前函数被触发的时间
        var nowTime = Date.now();
        if(nowTime - lastTime > delay){
            fn();
            // 更新时间
            lastTime = nowTime;
        }
    }
}
/*  定时器实现
    @param: fn 要被节流的函数
    @param: delay 设置的执行周期
*/
function throttle(fn, delay){
    let timer;
    return function(){
        let context = this;  // 指向绑定了事件的那个元素
        let args = arguments; // 触发的事件的信息
        if(!timer){ // 如果 timer 不存在,那么创建一个只执行一次的定时器,调用想要触发的事件函数
            timer = setTimeout(()=>{
                fn.apply(context,args);
                timer = null;
            }, delay);
        }
    };
};
// 举例:给一个 div(已经将节点获取到up中) 绑定鼠标移动事件
var up = document.getElementById('up');
function moveInUp(){
    console.log('move~');
}
up.onmousemove = throttle(moveInUp, 3000);
var center = document.querySelector('.center');

center.onclick = debounce(function(){
    console.log('center');
}, 400);

function debounce(fn, delay) {
    let time = null;
    return function () {
        if (time !== null) {
            clearTimeout(time);
        }
        time = setTimeout(() => {
            fn.call(this);
            //利用call(),让this的指针从指向 window 转成指向当前元素
        }, delay)
    }
}

4. 框架

  1. props:父子组件通信,可以传递函数(本质是子给父传数据)和数据(本质是父给子传数据);
    数组形式 [‘参数名’];对象形式 { 参数名:参数类型}
    路由的props
    布尔模式 将 route.params 设置为组件的 props
    对象模式 传递静态值;
    函数模式 传递动态值。
  2. 自定义事件
    子组件给父组件传数据 $on$emit
  3. 全局事件总线 $bus
    Vue.prototype.$bus = this;
  4. 消息订阅与发布 pubsub-js
  5. Vuex
  6. 插槽slot
    父子组件通信,一般是结构;默认插槽、具名插槽、作用域插槽
  7. 属性修饰符 sync

Vue事件绑定

  1. 原生DOM绑定系统事件<button @click="handler">啾咪</button> 点击button触发click事件调用handler函数;
  2. Event 组件绑定事件<Event @click="handler">啾咪</Event> Event 不是原生DOM节点,此时认为click不是原生DOM事件而是自定义事件,所以不会触发,不会调用handler函数;加上natave修饰符才能让它认识DOM原生事件<Event @click.native="handler">啾咪</Event> 此时实际上是给Event组件的根节点绑定了click事件(利用了事件委派)
  3. 原生DOM绑定自定义事件<button @myEvent="handler">啾咪</button>,不要这么做哦没有意义,因为没办法触发 $emit 函数。

v-model
主要结合表单元素使用,收集表单数据;
可以实现数据的双向绑定:原生DOM中有一个oninput事件,当表单元素文本发生改变时会触发回调 => vue2中可以通过value和input事件实现v-model功能。
一种父子组件通信方法:
父组件中<Son :value="msg" @input="msg=$event" />, 等同于 <Son v-model="msg" />
子组件中<input type="text" :value="value" @input="$emit('input', $event.target.value)"/> input是原生DOM事件,输入数据发生改变时触发,在它的事件回调函数里触发绑定的自定义事件input,把修改后的值作为参数传进去。父组件接收到这个参数,修改自己的msg然后去使用。

属性修饰符 sync
父组件中<Son :value.sync="msg" /> 表示向子组件中通过props传递一个value,同时自动给子组件绑定一个叫做update:value的自定义事件
子组件中<input type="text" :value="value" @input="$emit('update:value', $event.target.value)"/>

$attrs$listeners
$attrs是组件的一个属性,可以接收父组件传来的props数据。如果子组件已经通过props接收了数据,那$attrs就获取不到了。
$listeners是组件的一个属性,可以接收父组件传来的自定义事件。
可以在子组件中用v-bind给标签绑定所有传过来的props数据,此时不能:进行简写;可以在子组件中用v-on给标签绑定所有传过来的自定义事件,此时不能@进行简写;

/*  父组件  */
<template>
  <div>
    <h2>自定义带Hover提示的按钮</h2>
     <!-- 使用我封装的 AttrsAndListenersTest 组件时,传入需要的参数-->
    <AttrsAndListenersTest type="success" icon="el-icon-delete" size="mini" title="提示" @click.native="handler"/>
  </div>
</template>

<script>
import AttrsAndListenersTest from './AttrsAndListenersTest.vue';
export default {
    name:"GroupOrder",
    components:{
      AttrsAndListenersTest
    },
    methods:{
        handler(){
            alert("给子组件绑定了一个自定义事件(native),并被调用了");
        }
    }
}
</script>

<style>

</style>
/*  子组件  */
<template>
  <div>
    <a :title="title">
      <!-- v-bind 绑定所有props数据, v-on 绑定所有自定义事件 -->
      <el-button v-bind="$attrs" v-on="$listeners"></el-button>
    </a>
  </div>
</template>

<script type="text/ecmascript-6">
// 记得先在 main.js里按需引入 button 组件
export default {
    name:'AttrsAndListenersTest',
    props:['title']
}
</script>
<style>

</style>

5. 浏览器相关

当协议、子域名、主域名、端口号中任意一个不相同时,都算作不同域。不同域之间相互请求资源,就算作“跨域”。浏览器的同源策略会导致跨域,可以分为DOM同源策略和XMLHttpRequest同源策略。同源策略主要用来防止CSRF(跨站请求伪造)攻击。
DOM同源策略禁止对不同源页面DOM进行操作。这里主要场景是iframe跨域的情况,不同域名的iframe是限制互相访问的。
XmlHttpRequest同源策略禁止使用XHR对象向不同源的服务器地址发起HTTP请求。

举例(XmlHttpRequest):

  1. 用户登录了自己的银行页面 http://mybank.com,http://mybank.com向用户的cookie中添加用户标识。
  2. 用户浏览了恶意页面 http://evil.com。执行了页面中的恶意AJAX请求代码。
  3. http://evil.com向http://mybank.com发起AJAX HTTP请求,请求会默认把http://mybank.com对应cookie也同时发送过去。
  4. 银行页面从发送的cookie中提取用户标识,验证用户无误,response中返回请求数据。
  5. 此时数据就泄露了。而且由于Ajax在后台执行,用户无法感知这一过程。

举例(DOM):

  1. 做一个假网站,里面用iframe嵌套一个银行网站 http://mybank.com。
  2. 把iframe宽高啥的调整到页面全部,这样用户进来除了域名,别的部分和银行的网站没有任何差别。
  3. 用户输入账号密码,我们的主网站可以跨域访问到http://mybank.com的dom节点,就可以拿到用户的输入了,那么就完成了一次攻击。

xss 跨站脚本攻击。xss攻击的主要目的是想办法获取目标攻击网站的cookie, 因为有了cookie相当于有了session。恶意攻击者往Web页面里插入恶意Script代码,当用户浏览该网页之时,嵌入其中Web里面的Script代码会被执行,从而达到恶意攻击用户的目的,避免采取的措施:编码、过滤、校验

csrf 跨站请求伪造,CSRF攻击者在用户已经登录目标网站之后,诱使用户访问一个攻击页面,利用目标网站对用户的信任,以用户身份在攻击页面对目标网站发起伪造用户操作的请求,达到攻击目的。防御手段:1. 尽量使用POST,限制GET,2. 加验证码

  • script脚本阻塞有什么解决方法?defer和async的区别?
  • 浏览器如何加载页面

从浏览器地址栏的请求链接开始,浏览器通过DNS解析查到域名映射的IP地址,成功之后浏览器端向此IP地址取得连接,成功连接之后,浏览器端将请求信息通过HTTP协议向此IP地址所在服务器发起请求,服务器接受到请求之后等待处理,最后向浏览器端发回响应,此时在HTTP协议下,浏览器从服务器接收到 text/html类型的代码,浏览器开始显示此html,并获取其中内嵌资源地址,然后浏览器再发起请求来获取这些资源,并在浏览器的html中显示

访问的数据量过大的时候用缓存明显不太合适的时候。可以用按需加载

  • web存储

Cookie: 携带用户的标识,帮助服务端区分不同的客户端。HTTP请求发送Set-Cookie HTTP作为响应的一部分,通过key=value的形成存储字符串。前端收到响应后,会自动将cookie保存在浏览器本地存储中,并在每次请求的时候都携带此字符串。一般情况下,cookie是产生于服务器端,保存于客户端,最大为4kb; 也可以通过js来产生cookie。
localStorage和sessionStorage将更多的会话数据保存在浏览器端,以及减少网络传输,提升网站性能。
localStorage:

  1. 只能存储字符串类型的对象;
  2. 除非手动清除localStorage信息,否则它们将永远存在;
  3. 在相同的协议、相同的主机名、相同的端口下,能读取/修改到同一份localStorage数据;
    sessionStorage:
  4. 页面会话在浏览器打开期间一直保持,并且重新加载或恢复页面仍会保持原来的页面会话。
  5. 打开多个相同的URL的Tabs页面,会创建各自的sessionStorage。
  6. 关闭对应浏览器tab,会清除对应的sessionStorage;
  7. 除了协议、主机名、端口外,还要求在同一窗口(也就是浏览器的标签页)下,才能读取/修改到同一份localStorage数据。

HTTP状态码(HTTP Status Code)是一种表示网页服务器响应状态的三位数字编码。通过这些数字,可以简化状态的表达。状态码有几十种,其中首位数字为1-5。根据这5个数字,状态码可以分为5类。1开头的表示请求正在处理;2开头请求已经成功处理;3开头表示重定向;4开头表示请求错误;5开头表示服务器错误。
在嗅探抓包过程中,常见的有两种200和304。这两个状态码都关系到能否获取重要信息。当客户第一次请求服务器资源,服务器成功返回资源,这时状态码为200。所以,状态码为200的数据包往往包含用户从服务器获取的数据。
每个资源请求完成后,通常会被缓存在客户端,并会记录资源的有效时间和修改时间。当客户再次请求该资源,客户端首先从缓存中查找该资源。如果该资源存在,并且在有效期,则不请求服务器,就不会产生对应的请求数据包。
如果不在有效期,客户端会请求服务器,重新获取。服务器会判断修改时间,如果没有修改过,就会返回状态码304,告诉客户端该资源仍然有效,客户端会直接使用缓存的资源。针对304的响应,渗透人员可以分析对应的请求包,获取资源路径。如果该资源不限制访问,就可以直接请求获取。否则,就需要进行Cookie劫持,进行获取。

  • 什么是预加载、懒加载

预加载:提前加载图片,当用户需要查看时可直接从本地缓存中渲染
懒加载:也就是延迟加载。当访问一个页面的时候,先把img元素或是其他元素的背景图片路径替换成一张大小为1*1px图片的路径(这样就只需请求一次,俗称占位图),只有当图片出现在浏览器的可视区域内时,才设置图片真正的路径,让图片显示出来。这就是图片懒加载

6. ES6

es6的继承和es5的继承有什么区别

ES5的继承时通过prototype或构造函数机制来实现。ES5的继承实质上是先创建子类的实例对象,然后再将父类的方法添加到this上(Parent.apply(this))。
ES6的继承机制完全不同,实质上是先创建父类的实例对象this(所以必须先调用父类的super()方法),然后再用子类的构造函数修改this:ES6通过class关键字定义类,里面有构造方法,类之间通过extends关键字实现继承。子类必须在constructor方法中调用super方法,否则新建实例报错。因为子类没有自己的this对象,而是继承了父类的this对象,然后对其进行加工。如果不调用super方法,子类得不到this对象。

generator(生成器)是ES6标准引入的新的数据类型。一个generator看上去像一个函数,但可以返回多次。

  • ES6和node的commonjs 模块化规范区别

7. 计算机网络

  • HTTP协议头含有哪些重要的部分,HTTP状态码,TLS ( Transport Layer Security,传输层安全协议 )
  • 网络url输入到输出经历了什么
  • 性能优化为什么要减少 HTTP 访问次数?

减少DNS请求所耗费的时间;
减少服务器压力.这个通常是被考虑最多的,也是我用来讲解给别人听的最大理由,因为每个http请求都会耗费服务器资源,特别是一些需要计算合并等操作的服务器,耗费服务器的cpu资源可不是开玩笑的事情,硬盘可以用钱买来,cpu资源可就没那么廉价了.
减少http请求头.当我们对服务器发起一个请求的时候,我们会携带着这个域名下的cookie和一些其他的信息在http头部里,然后服务器响应请求的时候也会带回一些cookie之类的头部信息.这些信息有的时候会很大,在这种请求和响应的时候会影响带宽性能.

1.客户端进行DNS域名解析,得到对应的IP地址
2.根据这个IP,找到对应的服务器建立连接(三次握手)
3.建立TCP连接后发起HTTP请求(一个完整的http请求报文)
4.服务器响应HTTP请求,客户端得到html代码
5.客户端解析html代码,用html代码中的资源(如js,css,图片等等)渲染页面。
6.服务器关闭TCP连接(四次挥手)

  • https 有几次握手和挥手 (7次握手 4次挥手) ?https的原理
  • TCP连接的特点,TCP连接如何保证安全可靠的?

1.连接管理
TCP是面向连接的,三次握手四次挥手都保证了连接的可靠性。
2.序列号
TCP是面向字节流的,它要求数据报按次序到达。发送端会为每一个字节流添加一个序号,会按照序号的顺序向接收端发送数据报,而接收端也会按照顺序接受这些数据报,如果中间一个序列号的数据包丢了,虽然它还是会接受这些数据包,但是它也会重复发送丢失的数据报之前的确认报文段,来告诉发送端重发这个数据报,以保证发送端发送丢失的数据报。
3.超时重传
当TCP发送了一个数据包时,会启动一个定时器,如果在有一定的时间内没有收到接收端的确认报文段,就会重新发送这个数据包。
4.流量控制
发送端会根据接收端处理数据的能力调整发送速度。首先,接受端将自己处理数据的缓冲区的空闲区域的大小写入到TCP报文头部的窗口大小中,通过ACK确认报文段发送给发送端,发送端接收到这个报文之后,会根据窗口的信息调整自己发送数据的能力。比如,接收端的缓冲区快满了,它将窗口大小调小,发送端接收到这个信息后会放缓发送数据报的速度。接受端的缓冲区彻底满了之后,它会将窗口大小的信息设置为0,发送端接收到窗口信息后,会停止发送信息,但是会隔一段时间发送一个探测的报文段,来查看接收端的缓冲区状态。
5.拥塞控制
由慢启动,拥塞避免,快重传,快恢复四个核心算法组成。
①慢启动:当主机启动开始发送数据时,会先探测性的调到一个很小的窗口大小,(因为它不知道网络状态,一下子放入大量数据报可能会导致网络拥塞或者丢包),随着发送的次数增加,窗口大小也成倍增加。
②拥塞避免:为了避免窗口拥塞,会放缓窗口的增加速度,每次会使窗口的大小+1。
③快重传:当发送端连续三次接收到同一个报文段的确认报文时,会直接启动快重传算法,发送这个报文段后边的丢失的那个报文段。
④快恢复:为了避免是因为直接重传而造成网络堵塞,在它之后会继续执行第二步拥塞算法。

  • 为什么TCP连接需要三次握手,两次不可以吗,为什么

如果使用的是两次握手建立连接,假设有这样一种场景,客户端发送了第一个请求连接并且没有丢失,只是因为在网络结点中滞留的时间太长了,由于TCP的客户端迟迟没有收到确认报文,以为服务器没有收到,此时重新向服务器发送这条报文,此后客户端和服务器经过两次握手完成连接,传输数据,然后关闭连接。此时此前滞留的那一次请求连接,网络通畅了到达了服务器,这个报文本该是失效的,但是,两次握手的机制将会让客户端和服务器再次建立连接,这将导致不必要的错误和资源的浪费。
如果采用的是三次握手,就算是那一次失效的报文传送过来了,服务端接受到了那条失效报文并且回复了确认报文,但是客户端不会再次发出确认。由于服务器收不到确认,就知道客户端并没有请求连接。

GET在浏览器回退时是无害的,而POST会再次提交请求。
GET产生的URL地址可以被Bookmark,而POST不可以。
GET请求会被浏览器主动cache,而POST不会,除非手动设置。
GET请求只能进行url编码,而POST支持多种编码方式。
GET请求参数会被完整保留在浏览器历史记录里,而POST中的参数不会被保留。
GET请求在URL中传送的参数是有长度限制的,而POST么有。
对参数的数据类型,GET只接受ASCII字符,而POST没有限制。
GET比POST更不安全,因为参数直接暴露在URL上,所以不能用来传递敏感信息。
GET参数通过URL传递,POST放在Request body中。
GET产生一个TCP数据包;POST产生两个TCP数据包: 对于GET方式的请求,浏览器会把http header和data一并发送出去,服务器响应200(返回数据);而对于POST,浏览器先发送header,服务器响应100 continue,浏览器再发送data,服务器响应200 ok(返回数据)。

浏览器对于同一域名下允许的并发请求数作了限制,通常同一域名下最大并发请求数为6个;
原因:
1、为了适应当时服务器的负载能力;之前的服务器的负载能力并没有这么强,高并发的请求可能会导致服务器无法正常提供服务甚至崩溃;
2、如果浏览器允许的最大并发请求书较大,容易造成DDoS(Distributed Denial of Sevice)攻击等安全隐患;
3、过多的并发请求可能会造成浏览器阻塞,使之处于“假死”的无响应状态;
4、浏览器目前已经支持了长连接,可以在同一个TCP连接中完成多个请求,没有必要再进行代价较大的重新开启新请求的操作;
域名发散:域名发散就是为了突破浏览器对于同一域名并发请求数的限制,使用域名发散为同一个服务申请多个域名,从而可以一定程度上提高并发量;当然,由于建立新的请求需要一定的代价,因此需要在域名发散与域名收敛间进行trade off,通常发散的域名个数为2-4个;
域名收敛:域名收敛就是将静态资源放在一个域名下不进行发散,这主要是为了适应移动端的发展需求;通常DNS是一个开销较大的操作,而移动端由于网络带宽和实时性、资源等的限制,这些开销对移动端的用户体验是致命的,因此需要进行域名收敛;

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
前端八股文是指在前端开发领域,一些常见的面试题目和技术要点的总结,通常以提问的形式呈现在GitHub上,供面试准备和学习使用。 GitHub是一个开源代码库托管平台,以版本控制系统Git为基础。在GitHub上,开发者可以创建自己的代码仓库,存放自己开发的项目代码,并与其他开发者共享、协作。同时,GitHub也是前端开发者交流、学习的重要平台之一,许多前端开发的优秀项目、教程和资源都可以在GitHub上找到。 前端开发的八股文是一种面试备考的指导性资料,涵盖了前端开发的常见知识点和技能要求。它们通常包括HTML、CSS、JavaScript等基础知识、常见的前端框架和类库,以及一些实际项目中常见的技术难点和解决方案。通过学习和掌握这些知识点,前端开发者可以更好地应对面试,提高自己的竞争力。 GitHub上有许多前端八股文的项目,从基础知识到高级技巧都有所涉及。这些项目一般以问题+答案的形式展示,通过阅读问题和答案,前端开发者可以系统性地学习和巩固前端开发的各个方面的知识点。同时,这些项目通常也会提供一些练习题目,帮助开发者进一步巩固学习成果。 总而言之,前端八股文是一份充实的学习资料,帮助前端开发者在面试中取得好的表现。GitHub提供了许多前端八股文的资源,通过学习这些资源,前端开发者可以提升自己的技能水平,更好地适应不断发展的前端技术领域。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值