# 前端基础
# html
# 1.1 html标签的类型(head, body,!Doctype) 他们的作用是什么
!DOCTYPE 标签
- 它是指示 web 浏览器关于页面使用哪个 HTML 版本进行编写的指令
head:
- 是所有头部元素的容器, 绝大多数头部标签的内容不会显示给读者
- 该标签下所包含的部分可加入的标签有 base, link, meta, script, style和title
body:
- 用于定义文档的主体, 包含了文档的所有内容
- 该标签支持 html 的全局属性和事件属性
# 1.2 h5新特性
- 新增选择器 document.querySelector、document.querySelectorAll
- 拖拽释放(Drag and drop) API
- 媒体播放的 video 和 audio
- 本地存储 localStorage 和 sessionStorage
- 离线应用 manifest
- 桌面通知 Notififications
- 语意化标签 article、footer、header、nav、section
- 增强表单控件 calendar、date、time、email、url、search
- 地理位置 Geolocation
- 多任务 webworker
- 全双工通信协议 websocket
- 历史管理 history
- 跨域资源共享(CORS) Access-Control-Allow-Origin
- 页面可见性改变事件 visibilitychange
- 跨窗口通信 PostMessage
- Form Data 对象
- 绘画 canvas
# 1.3 伪类和伪元素
伪类:用于已有元素处于某种状态时为其添加对应的样式,这个状态是根据用户行为而动态变化的
例如:当用户悬停在指定元素时,可以通过:hover来描述这个元素的状态,虽然它和一般css相似,可以为 已有元素添加样式,但是它只有处于DOM树无法描述的状态下才能为元素添加样式,所以称为伪类
伪元素:用于创建一些不在DOM树中的元素,并为其添加样式
例如,我们可以通过:before来在一个元素之前添加一些文本,并为这些文本添加样式,虽然用户可以看见 这些文本,但是它实际上并不在DOM文档中
# 1.4 html语义化
title | 页面主体内容 |
---|---|
hn | h1~h6,分级标题,<h1> 与 <title> 协调有利于搜索引擎优化 |
ul li | 无序列表 |
ol li | 有序列表 |
header | 页眉通常包括网站标志、主导航、全站链接以及搜索框 |
nav | 标记导航,仅对文档中重要的链接群使用 |
main | 页面主要内容,一个页面只能使用一次。如果是web应用,则包围其主要功能 |
article | 定义外部的内容,其中的内容独立于文档的其余部分 |
section | 定义文档中的节(section、区段)。比如章节、页眉、页脚或文档中的其他部分 |
aside | 定义其所处内容之外的内容。如侧栏、文章的一组链接、广告、友情链接、相关产品列表等 |
footer | 页脚,只有当父级是body时,才是整个页面的页脚 |
small | 呈现小号字体效果,指定细则,输入免责声明、注解、署名、版权 |
strong | 和 em 标签一样,用于强调文本,但它强调的程度更强一些 |
em | 将其中的文本表示为强调的内容,表现为斜体 |
mark | 使用黄色突出显示部分文本 |
figure | 规定独立的流内容(图像、图表、照片、代码等等)(默认有40px左右margin) |
figcaption | 定义 figure 元素的标题,应该被置于 figure 元素的第一个或最后一个子元素的位置 |
cite | 表示所包含的文本对某个参考文献的引用,比如书籍或者杂志的标题 |
progress | 定义运行中的进度(进程) |
address | 作者、相关人士或组织的联系信息(电子邮件地址、指向联系信息页的链接) |
blockquoto | 定义块引用,块引用拥有它们自己的空间 |
del、ins、code | 移除的内容、添加的内容、标记代码 |
语义化优点
- 易于用户阅读,样式丢失的时候能让页面呈现清晰的结构
- 有利于SEO,搜索引擎根据标签来确定上下文和各个关键字的权重
- 方便屏幕阅读器解析,如盲人阅读器根据语义渲染网页
- 有利于开发和维护,语义化更具可读性,代码更好维护,与CSS3关系更和谐
# 1.5 引入样式时,link和@import的区别?
- 链接样式时,link只能在HTML页面中引入外部样式
- 导入样式表时,@import 既可以在HTML页面中导入外部样式,也可以在css样式文件中导入外部css样式
# 1.6 介绍一下你对浏览器内核的理解
主要分成两部分:渲染引擎(Layout Engine或Rendering Engine)和js引擎
- 渲染引擎:负责取得网页的内容(HTML、XML、图像等等)、整理讯息(例如加入CSS等),以及计算网页的显示方式,然后会输出至显示器或打印机。浏览器的内核的不同对于网页的语法解释会有不同,所以渲染的效果也不相同。
- js引擎:解析和执行JavaScript来实现网页的动态效果
# 1.7 常见的浏览器内核有哪些
- Trident( MSHTML ):IE MaxThon TT The World 360 搜狗浏览器
- Geckos:Netscape6及以上版本 FireFox Mozilla Suite/SeaMonkey
- Presto:Opera7及以上(Opera内核原为:Presto,现为:Blink)
- Webkit:Safari Chrome
# 1.8 label标签的作用是什么? 是怎么用的?
- label标签用来定义表单控件间的关系
- 当用户选择该标签时,浏览器会自动将焦点转到和标签相关的表单控件上
- label 中有两个属性是非常有用的, FOR和ACCESSKEY
- FOR属性功能:表示label标签要绑定的HTML元素,你点击这个标签的时候,所绑定的元素将获取焦点
# 1.9 title与h1的区别、b与strong的区别、i与em的区别?
- title属性没有明确意义,只表示标题;h1表示层次明确的标题,对页面信息的抓取也有很大的影响
- strong标明重点内容,语气加强含义;b是无意义的视觉表示
- em表示强调文本;i是斜体,是无意义的视觉表示
- 视觉样式标签:b i u s
- 语义样式标签:strong em ins del code
# 1.10 元素的alt和title有什么不同?
- 在alt和title同时设置的时候,alt作为图片的替代文字出现,title是图片的解释文字
# 1.11 浏览器页面有哪三层构成,分别是什么,作用是什么?
- 浏览器页面构成:结构层、表示层、行为层
- 分别是:HTML、CSS、JavaScript
- 作用:HTML实现页面结构,CSS完成页面的表现与风格,JavaScript实现一些客户端的功能与业务。
# 1.12 网页制作会用到的图片格式有哪些?
- Webp:WebP格式,谷歌(google)开发的一种旨在加快图片加载速度的图片格式。并能节省大量的服务器带宽资源和数据空间。Facebook Ebay等知名网站已经开始测试并使用WebP格式。
- Apng:是PNG的位图动画扩展,可以实现png格式的动态图片效果,有望代替GIF成为下一代动态图标准
# 1.13 viewport 所有属性?
- width :设置layout viewport的宽度,为一个正整数,或字符串'device-width'
- initial-scale:设置页面的初始缩放值,为一个数字,可以带小数
- minimum-scale:允许用户的最小缩放值,为一个数字,可以带小数
- maximum-scale:允许用户的最大缩放值,为一个数字,可以带小数
- height:设置layout viewport的高度,这个属性对我们并不重要,很少使用
- user-scalable:是否允许用户进行缩放,值为‘no’或者‘yes’
安卓中还支持:target-densitydpi,表示目标设备的密度等级,作用是决定css中的1px 代表多少物理像素
# 1.14 meta标签的name属性值?
name 属性主要用于描述网页,与之对应的属性值为content,content中的内容主要是便于搜索引擎机器人查找信息和分类信息用的
A 、Keywords(关键字)说明:keywords用来告诉搜索引擎你网页的关键字是什么。
B 、description(网站内容描述) 说明:description用来告诉搜索引擎你的网站主要内容。
C 、robots(机器人向导)说明:robots用来告诉搜索机器人哪些页面需要索引,哪些页面不需要索引。
# 1.15 a标签中 如何禁用href 跳转页面 或 定位链接?
e.preventDefault();
href="javascript:void(0);
# 1.16 video标签的几个属性方法
- src:视频的URL
- poster:视频封面,没有播放时显示的图片
- preload:预加载
- autoplay:自动播放
- loop:循环播放
- controls:浏览器自带的控制条
- width:视频宽度
- height:视频高度
# 1.17 块级元素、行内元素、行内块元素
块级元素:
特点:可设置宽高边距,占满整行,会自动换行
示例:div、 p、 h1 、h6、ol、ul、dl、table、address、blockquote、form
行内元素:
特点:无法设置宽高边距,不会占满整行,不会自动换行
示例:a、strong、b、em、i、del、s、ins、u、span
行内块元素:
特点:可设置宽高,占满整行,但不会自动换行
示例:img、input
# 1.18 web标准和w3c标准
web标准:分为结构、表现和行为
W3C标准:提出了更规范的要求
1、结构方面:标签字母要小写,标签要闭合,标签要正确嵌套
2、css和js方面:尽量使用外链写法,少用行内样式,属性名要见名知意
# 1.19 前端需要注意哪些SEO
1、合理的title、description、keywords:搜素时对这三项的权重逐个减少,title强调重点,重要关键词不要超过两次,而且要靠前,不同页面title要有所不同,description高度概括页面内容,长度合适,不过分堆砌关键词,不同页面description有所不同,keywords列出重要关键词即可
2、语义化的html代码,符合W3C标准
3、提高网站速度
4、重要HTML代码放前面
5、重要内容不要用js输出:爬虫不会执行js获取内容
6、少用 iframe:搜索引擎不会抓取 iframe 中的内容
7、非装饰性图片必须加 alt
# 1.20 canvas和svg的区别
canvas | svg |
---|---|
通过js绘制2D图形,按像素进行渲染,当位置发生改变会重新进行绘制 | 使用XML绘制的2D图形,可以为元素添加js处理器 |
依赖分辨率 | 不依赖分辨率 |
不支持事件处理器 | 支持事件处理器 |
弱的文本渲染能力 | 最适合带有哦大型渲染区域的应用程序(如谷歌地图) |
能以.png或.jpg格式保存结果图像 | 复杂度高会减慢渲染速度 |
最适合图像密集型游戏,其中的许多对象会被频繁重绘 | 不适合游戏应用 |
# CSS
# 1.1 标准盒模型和IE盒模型两者的区别是什么?
概念
CSS盒模型本质上是一个盒子,封装周围的HTML元素,它包括: 外边距(margin)
、 边框
(border)
、 内边距(padding)
、实际内容(content)
四个属性
设置盒子模型
- box-sizing:content-box;(标准)
- box-sizing:border-box;(IE)
区别
- 标准的(W3C)盒模型:元素的实际宽度等于设置的宽高 + border + padding (默认方式)
- IE盒模型: 元素的实际宽度就等于设置的宽高,即使定义有 border 和 padding 也不会改变元素的实际宽度,即 ( Element width = width )
# 1.2 盒子塌陷是什么?
盒子塌陷
本应该在父盒子内部的元素跑到了外部。
为什么会出现盒子塌陷?
当父元素没设置足够大小的时候,而子元素设置了浮动的属性,子元素就会跳出父元素的边界(脱离文
档流),尤其是当父元素的高度为auto时,而父元素中又没有其它非浮动的可见元素时,父盒子的高度
就会直接塌陷为零, 我们称这是CSS高度塌陷
解决塌陷的方法
- 设置宽高
- 设置BFC
- 清楚浮动
- 给父盒子添加border
- 给父盒子设置padding-top
# 1.3 继承相关
css的继承:就是给父级设置一些属性,子级继承了父级的该属性,这就是我们的css中的继承
常用无继承性的属性
display:规定元素应该生成的框的类型
文本属性:
vertical-align:垂直文本对齐
text-decoration:规定添加到文本的装饰
text-shadow:文本阴影效果
white-space:空白符的处理
unicode-bidi:设置文本的方向
盒子模型的属性:width、height、margin、padding、border
背景属性:background
定位属性:float、clear、position、top、right、bottom、left、min-width、min-height、max
width、max-height、overflow、clip、z-index
有继承性的属性
font:字体系列属性
文本系列属性:
text-indent:文本缩进
text-align:文本水平对齐
line-height:行高
word-spacing:增加或减少单词间的空白(即字间隔)
letter-spacing:增加或减少字符间的空白(字符间距)
text-transform:控制文本大小写
direction:规定文本的书写方向
color:文本颜色 a元素除外
元素可见性:visibility
表格布局属性:caption-side、border-collapse、border-spacing、empty-cells、table-layout
列表布局属性:list-style-type、list-style-image、list-style-position、list-style
生成内容属性:quotes
光标属性:cursor
# 1.4 行内元素可以设置padding,margin吗?
- 行内元素的margin左右有效,上下无效
- 行内元素的padding左右有效 ,但是由于设置padding上下不占页面空间,无法显示效果,所以无效
# 1.5 什么是边距重叠?什么情况下会发生边距重叠?如何解决边距重叠?
边距重叠:两个box如果都设置了边距,那么在垂直方向上,两个box的边距会发生重叠,以绝对值大的那个为最终结果显示在页面上
边距重叠分为两种:
同级关系的重叠:
同级元素在垂直方向上外边距会出现重叠情况,最后外边距的大小取两者绝对值大的那个
父子关系的边距重叠:
嵌套崩塌
父子关系,如果子元素设置了外边距,在没有把父元素变成BFC的情况下,父元素也会产生外边距。
给父元素添加overflow:hidden 这样父元素就变为 BFC,不会随子元素产生外边距。
# 1.6 BFC是什么?
文档有几种流
- 定位流
- 绝对定位方案,盒从常规流中被移除,不影响常规流的布局;
- 它的定位相对于它的包含块,相关CSS属性:top、bottom、left、right;
- 如果元素的属性position为absolute或fixed,它是绝对定位元素;
- 对于position: absolute,元素定位将相对于上级元素中最近的一个relative、fixed、
- absolute,如果没有则相对于body;
- 浮动流
- 左浮动元素尽量靠左、靠上,右浮动同理
- 这导致常规流环绕在它的周边,除非设置 clear 属性
- 浮动元素不会影响块级元素的布局
- 但浮动元素会影响行内元素的布局,让其围绕在自己周围,撑大父级元素,从而间接影响块级元素布局
- 最高点不会超过当前行的最高点、它前面的浮动元素的最高点
- 不超过它的包含块,除非元素本身已经比包含块更宽
- 行内元素出现在左浮动元素的右边和右浮动元素的左边,左浮动元素的左边和右浮动元素的
- 右边是不会摆放浮动元素的
- 普通流
- 在常规流中,盒一个接着一个排列;
- 在块级格式化上下文里面, 它们竖着排列;
- 在行内格式化上下文里面, 它们横着排列;
- 当position为static或relative,并且float为none时会触发常规流;
- 对于静态定位(static positioning),position: static,盒的位置是常规流布局里的位置;
- 对于相对定位(relative positioning),position: relative,盒偏移位置由top、bottom、
- left、right属性定义。即使有偏移,仍然保留原有的位置,其它常规流不能占用这个位置。
定义
BFC的基本概念–BFC就是“块级格式化上下文”的意思,也有译作“块级格式化范围”。
通俗的讲,就是一个特殊的块,内部有自己的布局方式,不受外边元素的影响。
布局规则
- 内部的 Box 会在垂直方向,一个接一个地放置
- 垂直方向上的距离由margin决定。(完整的说法是:属于同一个BFC的两个相邻Box的margin会发生重叠(塌陷),与方向无关。)
- 每个元素的左外边距与包含块的左边界相接触(从左向右),即使浮动元素也是如此。(这说明BFC中子元素不会超出他的包含块,而position为absolute的元素可以超出他的包含块边界)
- BFC的区域不会与float的元素区域重叠
- 计算BFC的高度时,浮动子元素也参与计算
- BFC就是页面上的一个隔离的独立容器,容器里面的子元素不会影响到外面元素,反之亦然
哪些元素会创建 BFC
- 根元素
- float 属性不为 none
- position 为 absolute 或 fixed
- display 为 inline-block, table-cell, table-caption, flex, inline-flex
- overflow 不为 visible
场景
清除元素内部浮动
只要把父元素设为BFC就可以清理子元素的浮动了,最常见的用法就是在父元素上设置overflow: hidden样式,对于IE6加上zoom:1就可以了。
计算BFC的高度时,自然也会检测浮动或者定位的盒子高度。
解决外边距合并问题(嵌套崩塌)
外边距合并的问题。
盒子垂直方向的距离由margin决定。属于同一个BFC的两个相邻盒子的margin会发生重叠
属于同一个BFC的两个相邻盒子的margin会发生重叠,那么我们创建不属于同一个BFC,就不会发生margin重叠了。
制作右侧自适应的盒子问题
普通流体元素BFC后,为了和浮动元素不产生任何交集,顺着浮动边缘形成自己的封闭上下文
# 1.7 块元素居中
- 我们可以利用margin:0 auto来实现元素的水平居中。
- 利用绝对定位,设置四个方向的值都为0,并将margin设置为auto,由于宽高固定,因此对应方向实现平分,可以实现水
平和垂直方向上的居中。
- 利用绝对定位,先将元素的左上角通过top:50%和left:50%定位到页面的中心,然后再通过margin负值来调整元素
的中心点到页面的中心。
- 利用绝对定位,先将元素的左上角通过top:50%和left:50%定位到页面的中心,然后再通过translate来调整元素
的中心点到页面的中心。
- 使用flex布局,通过align-items:center和justify-content:center设置容器的垂直和水平方向上为居中对
齐,然后它的子元素也可以实现垂直和水平的居中。
对于宽高不定的元素,后面两种方法,可以实现元素的垂直和水平的居中。
# 1.8 CSS 优化、提高性能的方法有哪些?
加载性能:
- css压缩:将写好的css进行打包压缩,可以减小文件体积
- css单一样式:当需要下边距和左边距的时候,很多时候会选择使用margin-left:20px;margin-bottom:30px
- 减少使用@import,建议使用link,因为后者在页面加载时一起加载,前者是等待页面加载完成之后再进行加载
选择器性能:
- 关键选择器(key selector)。选择器的最后面的部分为关键选择器(即用来匹配目标元素的部分)。CSS选择符是从右到左进行匹配的。
- 当使用后代选择器的时候,浏览器会遍历所有子元素来确定是否是指定的元素等等
- 如果规则拥有ID选择器作为其关键选择器,则不要为规则增加标签。
- 过滤掉无关的规则(这样样式系统就不会浪费时间去匹配它们了)。
- 尽量少的去对标签进行选择,而是用class。
- 尽量少的去使用后代选择器,降低选择器的权重值。后
- 代选择器的开销是最高的,尽量将选择器的深度降到最低,最高不要超过三层,更多的使用类来关联每一个标签元素。
- 了解哪些属性是可以通过继承而来的,然后避免对这些属性重复指定规则。
渲染性能:
- 属性值为0时,不加单位。
- 可以省略小数点之前的0。
- 标准化各种浏览器前缀:带浏览器前缀的在前。标准属性在后。
- 选择器优化嵌套,尽量避免层级过深。
- css雪碧图,同一页面相近部分的小图标,方便使用,减少页面的请求次数,但是同时图片本身会变大,使用时,优劣考虑清楚,再使用。
- 不滥用web字体。对于中文网站来说WebFonts可能很陌生,国外却很流行。web fonts通常体积庞大,而且一些浏览器在下载web fonts时会阻塞页面渲染损伤性能。
可维护性、健壮性:
- 将具有相同属性的样式抽离出来,整合并通过class在页面中进行使用,提高css的可维护性。
- 样式与内容分离:将css代码定义到外部css中
# 1.9 行内元素和块级元素什么区别,然后怎么相互转换
块级元素
总是从新的一行开始,即各个块级元素独占一行,默认垂直向下排列
高度、宽度、margin及padding都是可控的,设置有效,有边距效果
宽度没有设置时,默认为100%
块级元素中可以包含块级元素和行内元素
行内元素
和其他元素都在一行,即行内元素和其他行内元素都会在一条水平线上排列
高度、宽度是不可控的,设置无效,由内容决定
根据标签语义化的理念,行内元素最好只包含行内元素,不包含块级元素
转换
- display:inline;转换为行内元素
- display:block;转换为块状元素
- display:inline-block;转换为行内块状元素
# 1.10 min-width/max-width 和 min-height/max-height 属性间的覆盖规则?
- max-width 会覆盖 width,即使 width 是行内样式或者设置了 !important。
- min-width 会覆盖 max-width,此规则发生在 min-width 和 max-width 冲突的时候;
# 1.11 浏览器是怎样解析CSS选择器的?
CSS选择器的解析是从右向左解析的
原因:
从右向左的匹配在第一步就筛选掉了大量的不符合条件的最右节点(叶子节点),而从左向右的匹配规则的性能都浪
费在了失败的查找上面
# 1.12 width:auto 和 width:100%的区别
- width:100%会使元素box的宽度等于父元素的contentbox的宽度
- width:auto会使元素撑满整个父元素,margin、border、padding、content区域会自动分配水平空间。
# 1.13 display、position和float的相互关系?
- 首先我们判断display属性是否为none,如果为none,则position和float属性的值不影响元素最后的表现
- 然后判断position的值是否为absolute或者fixed,如果是,则float属性失效,并且display的值应该被 设置为table或者block,具体转换需要看初始转换值
- 如果position的值不为absolute或者fixed,则判断float属性的值是否为none,如果不是,则display 的值则按上面的规则转换。注意,如果position的值为relative并且float属性的值存在,则relative相对 于浮动后的最终位置定位
- 如果float的值为none,则判断元素是否为根元素,如果是根元素则display属性按照上面的规则转换,如果不是, 则保持指定的display属性值不变
总的来说,可以把它看作是一个类似优先级的机制,"position:absolute"和"position:fixed"优先级最高,有它存在 的时候,浮动不起作用,'display'的值也需要调整;其次,元素的'float'特性的值不是"none"的时候或者它是根元素 的时候,调整'display'的值;最后,非根元素,并且非浮动元素,并且非绝对定位的元素,'display'特性值同设置值。
# 1.14 IFC 是什么?
IFC指的是行级格式化上下文,它有这样的一些布局规则:
- 行级上下文内部的盒子会在水平方向,一个接一个地放置。
- 当一行不够的时候会自动切换到下一行。
- 行级上下文的高度由内部最高的内联盒子的高度决定
# 1.15 为什么不建议使用统配符初始化 css 样式
- 采用*{pading:0;margin:0;}这样的写法好处是写起来很简单,但是是通配符,需要把所有的标签都遍历一遍,当网站较大时, 样式比较多,这样写就大大的加强了网站运行的负载,会使网站加载的时候需要很长一段时间,因此一般大型的网站都有分层次的一 套初始化样式
- 出于性能的考虑,并不是所有标签都会有padding和margin,因此对常见的具有默认padding和margin的元素初始化即 可,并不需使用通配符*来初始化
# 1.16 CSS3 新特新
- 新增各种 CSS 选择器
:not(p)
选择每个非p的元素;p:empty
选择每个没有任何子级的p元素(包括文本节点) - 边框(Borders)
- 背景 background-clip(规定背景图的绘制区域),background-origin,background-size
- 线性渐变 (Linear Gradients) 向下/向上/向左/向右/对角方向
- 文本效果 阴影text-shadow,textwrap,word-break,word-wrap
- 2D 转换 transform:scale(0.85,0.90) | translate(0px,-30px) | skew(-9deg,0deg) |rotate() 3D转换 perspective();transform是向元素应用 2D 或者 3D 转换
- 过渡 transition
- 动画
- 多列布局 (multi-column layout)
- 盒模型
- flex 布局
- 多媒体查询 **定义两套css,当浏览器的尺寸变化时会采用不同的属性
# 1.17 position 跟 display、float 这些特性相互叠加后会怎么样?
- display 属性规定元素应该生成的框的类型;position属性规定元素的定位类型;float属性是一种布局方式,定义元素在哪个方向浮动。
- 类似于优先级机制:position:absolute/fixed优先级最高,有他们在时,float不起作用,display值需要调整。float 或者absolute定位的元素,只能是块元素或表格。
# 1.18 什么是CSS 预处理器?为什么使用?
- Less、Sass、Stylus,用来预编译Sass或less,增强了css代码的复用性,还有层级、mixin、变量、循环、函数等,具有很方便的UI组件模块化开发能力,极大的提高工作效率
为什么要使用?
- 可嵌套性
- 变量
- Mixins(混合@mixin):可重用性高,可以注入任何东西
- @extend:允许一个选择器继承另一个选择器
- @function:函数功能,用户使用@function 可以去编写自己的函数
- 引用父元素&:在编译时,&将被替换成父选择符
- 计算功能
- 组合连接: #{} :变量连接字符串
- 循环语句:(很少用到)
- if 语句:(很少用到)
# 1.19 浏览器是怎样解析的?
- HTML 被 HTML 解析器解析成 DOM 树;2. CSS 被 CSS 解析器解析成 CSSOM 树;
- 结合 DOM 树和 CSSOM 树,生成一棵渲染树(Render Tree),这一过程称为 Attachment;
- 生成布局(flow),浏览器在屏幕上“画”出渲染树中的所有节点;
- 将布局绘制(paint)在屏幕上,显示出整个页面。
# 1.20 在网页中的应该使用奇数还是偶数的字体?为什么呢?
- 使用偶数字体
- Windows 自带的点阵宋体(中易宋体)从 Vista 开始只提供 12、14、16 px 这三个大小的点阵,而 13、15、17 px时用的是小一号的点。(即每个字占的空间大了 1 px,但点阵没变),于是略显稀疏
# 1.21 元素竖向的百分比设定是相对于容器的高度吗?
当按百分比设定一个元素的宽度时,它是相对于父容器的宽度计算的,但是,对于一些表示竖向距离的属性,例如 padding-top , padding-bottom , margin-top , margin-bottom 等,当按百分比设定它们时,依据的也是父容器的宽度,而不是高度
# 1.22 怎么让谷歌支持小于12px的文字?
使用 scale
# 1.23 li 与 li 之间有看不见的空白间隔是什么原因引起的?有什么解决办法?
行框的排列会受到中间空白(回车空格)等的影响,因为空格也属于字符,这些空白也会被应用样式,占据空间,所以会有间隔,把字符大小设为0,就没有空格了
解决方法:
- 可以将代码全部写在一排
- 浮动li中float:left
- 在ul中用font-size:0(谷歌不支持);
- 可以将 ul{letter-spacing: -4px;};li{letter-spacing: normal;}
# 1.24 display:inline-block 什么时候会显示间隙?
- 有空格时候会有间隙 解决:s除空格
- margin正值的时候 解决:margin使用负值
- 使用font-size时候 解决:font-size:0、letter-spacing、word-spacing
# 1.25 png、jpg、gif 这些图片格式解释一下,分别什么时候用。有没有了解过webp?
- png是便携式网络图片(Portable Network Graphics)是一种无损数据压缩位图文件格式.优点是:压缩比高,色彩好。 大多数地方都可以用。
- jpg是一种针对相片使用的一种失真压缩方法,是一种破坏性的压缩,在色调及颜色平滑变化做的不错。在www上,被用来储存和传输照片的格式。
- gif是一种位图文件格式,以8位色重现真色彩的图像。可以实现动画效果.
- webp格式是谷歌在2010年推出的图片格式,压缩率只有jpg的2/3,大小比png小了45%。缺点是压缩的时间更久了,兼容性不好,目前谷歌和opera支持。
# 1.26 style 标签写在 body 后与 body前有什么区别?
页面加载自上而下 当然是先加载样式。 写在 body 标签后由于浏览器以逐行方式对HTML文档进行解析,当解析到写在尾部的样式表(外联或写在 style 标签)会导致浏览器停止之前的渲染,等待加载且解析样式表完成之后重新渲染,在windows的IE下可能会出现 FOUC 现象(即样式失效导致的页面闪烁问题)
# 1.27 ::before 和::after 中双冒号和单冒号有什么区别、作用?
区别
在css中伪类一直用:表示,如 :hover
,:active
等
伪元素在CSS1中已存在,当时语法使用 : 表示 ,如::before
和:after
后来在CSS3中修订,伪元素用 ::表示,如 ::before
和 ::after
,以此区分伪元素和伪类
由于低版本 IE 对双冒号不兼容,开发者为了兼容性各浏览器,继续使使用 :after 这种老语法表示伪元素
单冒号:
CSS3表示伪类;双冒号::
CSS3伪元素
作用:
::before 和::after 的主要作用是在元素内容前后加上指定内容
伪类与伪元素都是用于向选择器加特殊效果
伪类与伪元素的本质区别就是是否抽象创造了新元素
伪类只要不是互斥可以叠加使用
伪元素在一个选择器中只能出现一次,并且只能出现在末尾
伪类与伪元素优先级分别与类、标签优先级相同
# 1.28 CSS3新增伪类,以及伪元素?
CSS3 新增伪类
p:first-of-type 选择属于其父元素的首个
元素的每个
元素
p:last-of-type 选择属于其父元素的最后
元素的每个
元素
p:nth-child(n) 选择属于其父元素的第 n 个子元素的每个
元素
p:nth-last-child(n) 选择属于其父元素的倒数第 n 个子元素的每个
元素
p:nth-of-type(n) 选择属于其父元素第 n 个
元素的每个
元素
p:nth-last-of-type(n)
选择属于其父元素倒数第 n 个
元素的每个
元素
p:last-child 选择属于其父元素最后一个子元素的每个
元素
p:target
选择当前活动的
元素
:not(p) 选择非
元素的每个元素
:enabled 控制表单控件的可用状态
:disabled
控制表单控件的禁用状态
:checked
单选框或复选框被选中
伪元素
::first-letter 将样式添加到文本的首字母
::first-line 将样式添加到文本的首行
::before 在某元素之前插入某些内容
::after 在某元素之后插入某些内容
# 1.29 未知高度元素垂直居中、垂直居中的实现方式有哪些?
- 绝对定位+css3 transform:translate(-50%,-50%)
.wrap{
position:relative;
}
.child{
position: absolute;
top:50%;
left:50%;
-webkit-transform:translate(-50%,-50%);
}
- css3 的flex布局
.wrap{
display:flex;
justify-content:center;
}
.child{
align-self:center;
}
- table布局
<div class="wrap">
<div class="child">
<div>sadgsdgasgd</div>
</div>
</div>
<style>
.wrap{
display:table;
text-align:center;
}
.child{
background:#ccc;
display:table-cell;
vertical-align:middle;
}
.child div{
width:300px;
height:150px;
background:red;
margin:0 auto;
}
</style>
# 1.30 图片垂直居中
1. 使用flex实现图片垂直居中
<div class="flexbox">
<img src="1.jpg" alt="">
</div>
<style>
.flexbox{
width: 300px;
height: 250px;
background:#fff;
display: flex;
align-items:center
}
.flexbox img{
width: 100px;
height: 100px;
align-items: center;
}
</style>
- 利用Display: table;实现img图片垂直居中
<div class="tablebox">
<div id="imgbox">
<img src="1.jpg" alt="">
</div></div>
<style>
.tablebox{width: 300px;height: 250px;background: #fff;display: table}
#imgbox{display: table-cell;vertical-align: middle;}
#imgbox img{width: 100px}
</style>
- 用绝对定位实现垂直居中
<div class="posdiv">
<img src="1.jpg" alt=""></div>
<style>
.posdiv{
width: 300px;height: 250px;
background: #fff;
position: relative;
margin:0 auto}
.posdiv img{width: 100px;position: absolute;top: 50%;margin-top: -50px;}
</style>
# 1.31 文本元素居中
- 水平居中:text-align
- 垂直居中:line-height 和height设置一样
- 多行文本垂直居中
- 父级元素高度不固定(padding-top和padding-bottom)
- 父级元素高度固定 (vertical-align:middle +display:table-cell )
# 1.32 CSS实现一个等腰三角形
主要是通过把宽高设置成0,边框宽度设置宽一些,设置其中三个边透明,只留一个边显示
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>测试</title>
<style type="text/css">
div {
width:0px;height:0px;margin:100px auto;
border-left:80px solid transparent;
border-right:80px solid transparent;
border-bottom:138.56px solid #A962CE; /*--三角形的高--*/
}
</style>
</head>
<body>
<div>
</div>
</body>
</html>
# 1.33 画 0.5px 的直线
使用scale缩放
<style> .hr.scale-half { height: 1px; transform: scaleY(0.5); } </style> <p>1px + scaleY(0.5)</p> <div class="hr scale-half"></div> <!-- Chrome/Safari都变虚了,只有Firefox比较完美看起来是实的而且还很细,效果和直接设置0.5px一 样。所以通过transform: scale会导致Chrome变虚了,而粗细几乎没有变化。但是如果加上transformorigin: 50% 100%: --> .hr.scale-half { height: 1px; transform: scaleY(0.5); transform-origin: 50% 100%; }
线性渐变linear-gradient
<style> .hr.gradient { height: 1px; background: linear-gradient(0deg, #fff, #000); } </style> <p>linear-gradient(0deg, #fff, #000)</p> <div class="hr gradient"></div>
boxshadow
<style> .hr.boxshadow { height: 1px; background: none; box-shadow: 0 0.5px 0 #000; } </style> <p>box-shadow: 0 0.5px 0 #000</p> <div class="hr boxshadow"></div>
viewport
<meta name="viewport" content="width=device-width,initial-sacle=0.5"> <!-- width=device-width表示将viewport视窗的宽度调整为设备的宽度,这个宽度通常是指物理上宽度 initial-sacle=0.5 缩放0.5 -->
# 1.34 移动端适配方案
- viewport 适配
<meta name="viewport" content="width=750,initial-scale=0.5">
<!--
initial-scale = 屏幕的宽度 / 设计稿的宽度
为了适配其他屏幕,需要动态的设置 initial-scale 的值
-->
<head>
<script>
const WIDTH = 750
const mobileAdapter = () => {
let scale = screen.width / WIDTH
let content = `width=${WIDTH}, initial-scale=${scale}, maximumscale=${scale}, minimum-scale=${scale}`
let meta = document.querySelector('meta[name=viewport]')
if (!meta) {
meta = document.createElement('meta')
meta.setAttribute('name', 'viewport')
document.head.appendChild(meta)
}
meta.setAttribute('content',content)
}
mobileAdapter()
window.onorientationchange = mobileAdapter //屏幕翻转时再次执行
</script>
</head>
缺点:边线问题,不同尺寸下,边线的粗细是不一样的(等比缩放后),全部元素都是等比缩放,实际显示效果可能不太好
vw 适配(部分等比缩放)
- 开发者拿到设计稿(假设设计稿尺寸为750px,设计稿的元素标注是基于此宽度标注)
- 开始开发,对设计稿的标注进行转换,把px换成vw。比如页面元素字体标注的大小是32px,换成vw为 (100/750)*32 vw
- 对于需要等比缩放的元素,CSS使用转换后的单位
- 对于不需要缩放的元素,比如边框阴影,使用固定单位px
<head> <meta name="viewport" content="width=device-width, initial-scale=1, maximumscale=1, minimum-scale=1"> <script> const WIDTH = 750 //:root { --width: 0.133333 } 1像素等于多少 vw document.documentElement.style.setProperty('--width', (100 / WIDTH)) </script> </head>
rem适配
弹性盒适配(合理布局)
# 1.35 link 和 @import 的区别
- 引入的内容不同
link 除了引用样式文件,还可以引用图片等资源文件,而 @import 只引用样式文件
- 加载顺序不同
link 引用 CSS 时,在页面载入时同时加载;@import 需要页面网页完全载入以后加载
- 兼容性不同
link 是 XHTML 标签,无兼容问题;@import 是在 CSS2.1 提出的,低版本的浏览器不支持
- 对 JS 的支持不同
link 支持使用 Javascript 控制 DOM 去改变样式;而 @import 不支持
# 1.36 iframe有什么优点、缺点?
优点:
- iframe能够原封不动的把嵌入的网页展现出来。
- 如果有多个网页引用iframe,那么你只需要修改iframe的内容,就可以实现调用的每一个页面内 容的更改,方便快捷。
- 网页如果为了统一风格,头部和版本都是一样的,就可以写成一个页面,用iframe来嵌套,可以 增加代码的可重用。
- 如果遇到加载缓慢的第三方内容如图标和广告,这些问题可以由iframe来解决。
缺点:
- iframe会阻塞主页面的onload事件;
- iframe和主页面共享连接池,而浏览器对相同域的连接有限制,所以会影响页面的并行加载。会 产生很多页面,不容易管理。
- iframe框架结构有时会让人感到迷惑,如果框架个数多的话,可能会出现上下、左右滚动条,会 分散访问者的注意力,用户体验度差。
- 代码复杂,无法被一些搜索引擎索引到,这一点很关键,现在的搜索引擎爬虫还不能很好的处理 iframe中的内容,所以使用iframe会不利于搜索引擎优化(SEO)。
- 很多的移动设备无法完全显示框架,设备兼容性差。
- iframe框架页面会增加服务器的http请求,对于大型网站是不可取的。
# JavaScript
# 1.1 let var const的区别?
var ES5变量声明方式
- 在变量未赋值时,变量undefined(为使用声明变量时也为undefined)
- 作用域 var的作用域为方法作用域;只要在方法内定义了,整个方法内的定义变量后的代码都可以使用
let ES6变量声明方式
- 在变量为声明前直接使用会报错
- 作用域 let为块级作用域 通常let比var范围要小
- let进制重复声明变量,否则会报错;var可以重复声明
const ES6变量声明
- const为常量声明方式;声明变量时必须初始化,在后面出现的代码中不能再修改常量的值
- const实际上保证的,并不是变量的值不得改动,而时变量指向的哪个内存地址不得改动
# 1.2 js数据类型,区别
基本数据类型:
number,string,boolean,null,undefined,symbol,bigint
引用数据类型:
object,function
object:普通对象,数组对象,正则对象,日期对象,math数学函数对象。
两种数据存储方式:
基本数据类型是直接存储再栈中的简单数据段,占据空间小、大小固定,属于被频繁使用的数据。栈是存储基本数据类型值和执行代码的空间。
引用数据类型是存储在堆内存中,占据空间大、大小不固定。引用数据类型在栈中存储了指针,该指针指向队中该实体的起始地址,当解释器寻找引用值时,会检索其在栈中的地址,取得地址后从堆中获得实体。
两种数据类型的区别:
- 堆比栈空间大,栈比堆运行速度快
- 堆内存是无序存储,可以根据引用直接获取
- 基础数据类型比较稳定,而且相对来说占用的内存小
- 引用数据类型大小是动态的,而且是无限的
# 1.3 Javascript 创建对象的几种方式?
- 简单对象的创建 使用对象字面量的方式{}
const Cat = {};
- new 一个function
function Person(){
}
const personOne=new Person();
- 使用工厂方式来创建(Object 关键字)
const wcDog =new Object();
- 使用 Object.create() 创建对象(使用现有对象作为原型)
const person = Object.create(anotherPerson);
- 使用 ES6 中的类(Class)创建对象(其实质还是使用构造函数):
class Person {
constructor(name) {
this.name = name;
}
}
const person = new Person('John');
# 1.4 ==、===和object.is 区别
- == 值比较值
- ===值和类型都比较
- object.is 和 === 区别 +-0 false NaN true
console.log(+0 === -0); //true
console.log(NaN === NaN); //false
Object.is(+0,-0) //false
Object.is(NaN,NaN) //true
# 1.5 如何区分数组和对象?
- 通过 ES6 中的 Array.isArray
Array.isArray([]) //true
Array.isArray({}) //false
- 通过 instanceof 来识别
[] instanceof Array //true
{} instanceof Array //false
- 通过调用 constructor 来识别
{}.constructor //返回 object
[].constructor //返回 Array
- 通过 Object.prototype.toString.call 方法来识别
Object.prototype.toString.call([]) //["object Array"]
Object.prototype.toString.call({}) //["object Object"]
# 1.6 作用域和作用域链
作用域就是一个独立的地盘,让变量不会外泄、暴露出去。也就是说作用域最大的用处就是隔离 变量,不同作用域下同名变量不会有冲突
- ES6 之前 JavaScript 没有块级作用域,只有全局作用域和函数作用域。
- ES6 的到来,为我们提供了‘块级作用域’,可通过新增命令 let 和 const 来体现
什么是作用域链?
当代码在一个环境中执行时,会创建变量对象的一个作用域链
由子级作用域返回父级作用域中寻找变量,就叫做作用域链
作用域链中的下一个变量对象来自包含环境,也叫外部环境。而再下一个变量对象则来自中的最后一个对象
作用域链前端始终都是当前执行的代码所在环境的变量对象,如果环境是函数,则将其活动对象作为变量对象
如何延长作用域链?
执行环境的类型只有两种,全局和局部(函数)。但是有些语句可以在作用域链的前端临时增加一个变量对象,该变量对象会在代码执行后被移除具体来说就是执行这两个语句时,作用域链都会得到加强
try - catch 语句的 catch 块;会创建一个新的变量对象,包含的是被抛出的错误对象的声明
with 语句。with 语句会将指定的对象添加到作用域链中
# 1.7 constructor的理解
创建的每个函数都有一个prototype(原型)对象,这个属性是一个指针,指向一个对象。在默认情况下,所有原型对象都会自动获得一个constructor属性,之后属性是一个指向prototype属性坐在函数的指针。当调用构造函数创建一个新实例后,该实例的内部将包含一个指针,指向构造函数的原型对象。注意当将构造函数的prototype设置为等于一个以对象字面量形式创建的新对象时,constructor属性不再指向该构造函数
# 1.8 webworker和websocket
web socket:在一个单独的持久连接上提供全双工、双向的通信。使用自定义的协议(ws://、wss://),同源策略对web socket不适用。 web worker:运行在后台的JavaScript,不影响页面的性能。 创建worker:var worker = new Worker(url); 向worker发送数据:worker.postMessage(data); 接收worker返回的数据:worker.onmessage 终止一个worker的执行:worker.terminate();
# 1.9 XML与JSON的区别?
- 数据体积方面。JSON相对于XML来讲,数据的体积小,传递的速度更快些。
- 数据交互方面。JSON与JavaScript的交互更加方便,更容易解析处理,更好的数据交互。
- 数据描述方面。JSON对数据的描述性比XML较差。
- 传输速度方面。JSON的速度要远远快于XML。
# 1.10 map 和 forEach 的区别?
相同点:
- 都是循环遍历数组中的每一项
- 每次执行匿名函数都支持三个参数,参数分别为item(当前每一项),index(索引值), arr(原数组)
- 匿名函数中的this都是指向window
- 只能遍历数组
不同点:
- map()会分配内存空间存储新数组并返回,forEach()不会返回数据。
- forEach()允许callback更改原始数组的元素。map()返回新的数组。
# 1.11 for of 可以遍历哪些对象?
for..of..: 它是es6新增的一个遍历方法,但只限于迭代器(iterator), 所以普通的对象用for..of遍历 是会报错的。 可迭代的对象:包括Array, Map, Set, String, TypedArray, arguments对象等等
# 1.13 new操作符具体干了什么呢?
- 创建空对象; var obj = {};
- 设置新对象的constructor属性为构造函数的名称,设置新对象的proto属性指向构造函数的 prototype对象; obj.proto = ClassA.prototype; 扩展了新对象的原型链。
- 使用新对象调用函数,函数中的this被指向新实例对象: ClassA.call(obj); //{}.构造函数();
- 返回this指针。当存在显示的返回时,返回return后面的内容。新建的空对象作废。
# 1.14 作用域
作用域就是一个独立的地盘,让变量不会外泄、暴露出去。也就是说作用域最大的用处就是隔离 变量,不同作用域下同名变量不会有冲突
- ES6 之前 JavaScript 没有块级作用域,只有全局作用域和函数作用域。
- ES6 的到来,为我们提供了‘块级作用域’,可通过新增命令 let 和 const 来体现
# 1.15 javascript中arguments相关的问题
arguments 在js中,我们在调用有参数的函数时,当往这个调用的有参函数传参时,js会把所传的参数全部存到一 个叫arguments的对象里面。它是一个类数组数据 作用 有了arguments这个对象之后,我们可以不用给函数预先设定形参了,可以动态地通过arguments为函 数加入参数
# 1.16 instanceOf作用 即原理
instanceof主要作用就是判断一个实例是否属于某种类型
function new_instance_of(leftVaule, rightVaule) {
let rightProto = rightVaule.prototype; // 取右表达式的 prototype 值
leftVaule = leftVaule.__proto__; // 取左表达式的__proto__值
while (true) {
if (leftVaule === null) {
return false;
}
if (leftVaule === rightProto) {
return true;
}
leftVaule = leftVaule.__proto__
}
}
其实 instanceof 主要的实现原理就是只要右边变量的 prototype 在左边变量的原型链上即可。因此,instanceof 在查找的过程中会遍历左边变量的原型链,直到找到右边变量的 prototype,如果查找失败,则会返回 false,告诉我们左边变量并非是右边变量的实例。
# 1.17 数组和伪数组的区别?
- 定义
- 数组是一个特殊对象,与常规对象的区别:
- 当由新元素添加到列表中时,自动更新length属性
- 设置length属性,可以截断数组
- 从Array.protoype中继承了方法
- 属性为'Array'
- 类数组是一个拥有length属性,并且他属性为非负整数的普通对象,类数组不能直接调用数组方法。
- 区别 本质:类数组是简单对象,它的原型关系与数组不同
let arrayLike = {
length: 10,
};
console.log(arrayLike instanceof Array); // false
console.log(arrayLike.__proto__.constructor === Array); // false
console.log(arrayLike.toString()); // [object Object]
console.log(arrayLike.valueOf()); // {length: 10}
let array = [];
console.log(array instanceof Array); // true
console.log(array.__proto__.constructor === Array); // true
console.log(array.toString()); // ''
console.log(array.valueOf()); // []
- 类数组转换为数组
- 转换方法
- 使用 Array.from()
- 使用 Array.prototype.slice.call()
- 使用 Array.prototype.forEach() 进行属性遍历并组成新的数组
- 转换须知
- 转换后的数组长度由 length 属性决定。索引不连续时转换结果是连续的,会自动补位
let al1 = {
length: 4,
0: 0,
1: 1,
3: 3,
4: 4,
5: 5,
};
console.log(Array.from(al1)) // [0, 1, undefined, 3]
// 2.仅考虑 0或正整数 的索引
let al2 = {
length: 4,
'-1': -1,
'0': 0,
a: 'a',
1: 1
};
console.log(Array.from(al2)); // [0, 1, undefined, undefined]
// 3.使用slice转换产生稀疏数组
let al2 = {
length: 4,
'-1': -1,
'0': 0,
a: 'a',
1: 1
};
console.log(Array.prototype.slice.call(al2)); //[0, 1, empty × 2]
// 4.使用数组方法操作类数组注意地方
let arrayLike2 = {
2: 3,
3: 4,
length: 2,
push: Array.prototype.push
}
// push 操作的是索引值为 length 的位置
arrayLike2.push(1);
console.log(arrayLike2); // {2: 1, 3: 4, length: 3, push: ƒ}
arrayLike2.push(2);
console.log(arrayLike2); // {2: 1, 3: 2, length: 4, push: ƒ}
# 1.18 介绍下 Set、Map、WeakSet 和 WeakMap的区别?
Set
- 成员不能重复;
- 只有键值,没有键名,有点类似数组;
- 可以遍历,方法有 add、delete、has
WeakSet
- 成员都是对象(引用);
- 成员都是弱引用,随时可以消失(不计入垃圾回收机制)。可以用来保存 DOM 节点,不容易造成内存泄露;
- 不能遍历,方法有 add、delete、has ;
Map
- 本质上是键值对的集合,类似集合;
- 可以遍历,方法很多,可以跟各种数据格式转换;
WeakMap
- 只接收对象为键名(null 除外),不接受其他类型的值作为键名;
- 键名指向的对象,不计入垃圾回收机制;
- 不能遍历,方法同 get、set、has、delete ;
# 1.19 简单说说 js 中有哪几种内存泄露的情况
- 意外的全局变量;
- 闭包;
- 未被清空的定时器;
- 未被销毁的事件监听;
- DOM 引用;
# 1.20 json和xml数据的区别?
- 数据体积方面:xml是重量级的,json是轻量级的,传递的速度更快些。
- 数据传输方面:xml在传输过程中比较占带宽,json占带宽少,易于压缩。
- 数据交互方面:json与javascript的交互更加方便,更容易解析处理,更好的进行数据交互
- 数据描述方面:json对数据的描述性比xml较差
- xml和json都用在项目交互下,xml多用于做配置文件,json用于数据交互。
# 1.21 JavaScript有几种方法判断变量的类型?
- 使用typeof检测当需要判断变量是否是number, string, boolean, function, undefined等类型时,可以使用typeof进行判断。
- 使用instanceof检测instanceof运算符与typeof运算符相似,用于识别正在处理的对象的类型。与typeof方法不同的是,instanceof 方法要求开发者明确地确认对象为某特定类型。
- 使用constructor检测constructor本来是原型对象上的属性,指向构造函数。但是根据实例对象寻找属性的顺序,若实例对象上没有实例属性或方法时,就去原型链上寻找,因此,实例对象也是能使用constructor属性的。
# 1.22 Math.min()< Math.max()
false 按常规的思路,这段代码应该输出 true,毕竟最小值小于最大值。但是却输出 false
# 1.23 promise和 async await 区别?
概念 Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强 大,简单地说,Promise好比容器,里面存放着一些未来才会执行完毕(异步)的事件的结果,而 这些结果一旦生成是无法改变的 async await也是异步编程的一种解决方案,他遵循的是Generator 函数的语法糖,他拥有内置执 行器,不需要额外的调用直接会自动执行并输出结果,它返回的是一个Promise对象 两者的区别
- Promise的出现解决了传统callback函数导致的“地域回调”问题,但它的语法导致了它向纵向 发展行成了一个回调链,遇到复杂的业务场景,这样的语法显然也是不美观的。而async await代码看起来会简洁些,使得异步代码看起来像同步代码,await的本质是可以提供等同 于”同步效果“的等待异步返回能力的语法糖,只有这一句代码执行完,才会执行下一句。
- async await与Promise一样,是非阻塞的。
- async await是基于Promise实现的,可以说是改良版的Promise,它不能用于普通的回调函 数。
# 1.24 defer和async区别?
defer
要等到整个页面在内存中正常渲染结束(DOM结构完全生成,以及其他脚本执行完成),才会执行。多个defer脚本会按照它们在页面出现的顺序加载。==“渲染完再执行”==async
一旦下载完,渲染引擎就会中断渲染,执行这个脚本以后,再继续渲染。多个async脚本是不能保证加载顺序的。==“下载完就执行”==
# 1.25 同步和异步
同步
- 指在 主线程上排队执行的任务,只有前一个任务执行完毕,才能继续执行下一个任务。
- 也就是调用一旦开始,必须这个调用 返回结果(划重点——)才能继续往后执行。程序的执行顺序 和任务排列顺序是一致的。
异步
- 异步任务是指不进入主线程,而进入 任务队列的任务,只有任务队列通知主线程,某个异步任务可以执行了,该任务才会进入主线程。
- 每一个任务有一个或多个 回调函数。前一个任务结束后,不是执行后一个任务,而是执行回调函数,后一个任务则是不等前一个任务结束就执行。
- 程序的执行顺序和任务的排列顺序是不一致的,异步的。
- 我们常用的setTimeout和setInterval函数,Ajax都是异步操作。
# 1.26 javascript中arguments相关的问题?
arguments
在js中,我们在调用有参数的函数时,当往这个调用的有参函数传参时,js会把所传的参数全部存到一个叫arguments的对象里面。它是一个类数组数据
由来
Javascrip中每个函数都会有一个Arguments对象实例arguments,引用着函数的实参。它是寄生在js函数当中的,不能显式创建,arguments对象只有函数开始时才可用
作用
有了arguments这个对象之后,我们可以不用给函数预先设定形参了,可以动态地通过arguments为函数加入参数
# 1.27 null 和 undefined 的区别,如何让一个属性变为null
undefined
- 声明了一个变量,但没有赋值
- 访问对象上不存在的属性
- 函数定义了形参,但没有传递实参
- 使用 void 对表达式求值
null
- null是一个空值
- null 有属于自己的类型 Null,而不属于Object类型
- 二进制的前三位为 0 会被 typeof 判断为对象类型
# 1.28 简单说说 js 中有哪几种内存泄露的情况
- 意外的全局变量;
- 闭包;
- 未被清空的定时器;
- 未被销毁的事件监听;
- DOM 引用;
# 1.29 call appy bind的作用和区别?
作用:
都可以改变函数内部的this指向
区别点:
- call 和 apply 会调用函数,并且改变函数内部this指向。
- call 和 apply 传递的参数不一样,call 传递参数arg1,arg2...形式 apply 必须数组形式[arg]
- bind 不会调用函数,可以改变函数内部this指向
# 1.30 this指向(普通函数、箭头函数)
- 谁调用了函数或者方法,那么这个函数或者对象中的this就指向谁
- 匿名函数中的this:匿名函数的执行具有全局性,则匿名函数中的this指向是window,而不是调用该匿名函数的对象
箭头函数中的this
- 箭头函数中的this是在函数定义的时候就确定下来的,而不是在函数调用的时候确定的
- 箭头函数中的this指向父级作用域的执行上下文;
- 箭头函数无法使用apply、call和bind方法改变this指向,因为其this值在函数定义的时候就被确定下来
# 1.31 箭头函数能否当构造函数
箭头函数表达式的语法比函数表达式更简洁,并且没有自己的 this , arguments , super 或new.target 。箭头函数表达式更适用于那些本来需要匿名函数的地方,并且它不能用作构造函数
# 1.32 继承,优缺点 及方法有哪些?
继承的好处
a:提高了代码的复用性
b:提高了代码的维护性
c:让类与类之间产生了关系,是多态的前提
继承的弊端
类的耦合性增强了,但是开发的原则:高内聚,低耦合
# 1.32.1原型链继承
实现方式:将子类的原型链指向父类的对象实例
function Parent(){
this.name = "parent";
this.list = ['坤'];
}
Parent.prototype.sayHi = function(){
console.log('hello');
}
function Child(){
}
Child.prototype = new Parent();
let child = new Child();
console.log(child.name);
child.sayHi();
**原理:**子类实例child的__proto__
指向Child的原型链prototype,而Child.prototype指向Parent类的对象实例,该父类对象实例的 __proto__
指向Parent.prototype,所以Child可继承Parent的构造函数属性、方法和原型链属性、方法
**优点:**可继承构造函数的属性,父类构造函数的属性,父类原型的属性
缺点:
- 无法向父类构造函数传参;
- 共享父类实例的属性(若父类共有属性为引用类型,一个子类实例更改父类构造函数共有属性时会导致继承的共有属性发生变化)
var a = new Child();
var b = new Child();
a.list.push('rap');
console.log(b.list); // ['坤','rap']
# 1.32.2 构造函数继承
实现方式:在子类构造函数中使用call或者apply劫持父类构造函数方法,并传入参数
function Parent(name, id){
this.id = id
this.name = name
this.printName = function(){
console.log(this.name)
}
}
Parent.prototype.sayName = function(){
console.log(this.name)
};
function Child(name, id){
Parent.call(this, name, id) // Parent.apply(this, arguments);
}
var child = new Child("坤", "1")
child.printName() // 坤
child.sayName() // Error
**原理:**使用call或者apply改变子类函数的作用域,使this执行父类构造函数,子类因此可以继承父类共有属性
优点:可解决原型链继承 共享 的问题
缺点: 不可继承父类的原型方法,构造函数不可以被复用
# 1.32.3 组合继承
**原理:**综合使用构造函数继承和原型链继承
function Parent(name, id){
this.id = id;
this.name = name;
this.list = ['rap'];
this.printName = function(){
console.log(this.name);
}
}
Parent.prototype.sayName = function(){
console.log(this.name);
};
function Child(name, id){
Parent.call(this, name, id); // Parent.apply(this, arguments);
}
Child.prototype = new Parent();
var child = new Child("坤坤", "1");
child.printName(); // 坤坤
child.sayName() // 坤坤
var a = new Child();
var b = new Child();
a.list.push('篮球');
console.log(b.list); // ['rap']
**优点:**可继承父类原型上的属性,且可传参;每个新实例引入的构造函数是私有的
**缺点:**会执行两次父类的构造函数,消耗较大内存,子类的构造函数会代替原型上的那个父类构造函数
# 1.32.4 原型式继承
**原理:**类似Object.create,用一个函数包装一个对象,然后返回这个函数的调用,这个函数就变成了个可以随意增添属性的实例或对象,结果是将子对象的 __proto__
指向父对象
let parent = {
name: ['坤坤']
}
function copy(object) {
function fn() {}
fn.prototype = object
return new F()
}
var child = copy(parent)
缺点: 共享引用数据类型
# 1.32.5 寄生式继承
**原理:**扩展原型式继承
function copy(object) {
function fn() {}
fn.prototype = object
return new F()
}
function createObject(obj) {
let obj = copy(obj);
obj.getNames = function() {
console.log(this.names)
return this.names
}
return obj
}
**优点:**可添加新的属性和方法
# 1.32.6 寄生组合式继承
**原理:**改进组合继承,利用寄生式继承的思想继承原型
function inheritPrototype(subClass, superClass) {
// 复制一份父类的原型
let p = copy(superClass.prototype);
// 修正构造函数
p.constructor = subClass;
// 设置子类原型
subClass.prototype = p;
}
function Parent(name, id){
this.id = id;
this.name = name;
this.list = ['a'];
this.printName = function(){
console.log(this.name);
}
}
Parent.prototype.sayName = function(){
console.log(this.name);
};
function Child(name, id){
Parent.call(this, name, id);
// Parent.apply(this, arguments);
}
inheritPrototype(Child, Parent);
# 1.32.7 ES6 class extends
class A {
constructor() {
this.a = 'hello';
}
}
class B extends A {
constructor() {
super();
this.b = 'world';
}
}
let b = new B();
# 1.33 扩展运算符 ...
# 1.33.1哪些类型能被扩展操作符?
**类型:**数组、对象、字符串
复杂数据类型都可以,当要转化为可迭代数据结构时可设置对象的迭代器对扩展运算符扩展出来的值进行操作。
基础数据只有string可以使用扩展运算符,number,boolean,null,undefined无效
# 1.33.2 场景
// 1、函数调用
function add(x, y) {
return x + y
}
add(...[4, 38])
function f(a, b, c, d, e) { }
f(1, ...[1, 2], 2, ...[3])
//2.往数组里push多个元素
var arr1 = ['子异', '坤坤'];
var arr2 = ['说', '唱', '跳'];
arr1.push(...arr2);
console.log(arr1); //['子异', '坤坤','说', '唱', '跳']
//3.替代函数的apply方法
function f(a, b, c) { }
var args = [0, 1, 2];
f.apply(null, args); //ES5 的写法
f(...args); //ES6的写法
//4.求一个数组的最大数简化
Math.max.apply(null, [14, 3, 77]) //ES5 的写法
Math.max(...[14, 3, 77]) //ES6 的写法,等同于Math.max(14, 3, 77)
//5.扩展运算符后面可以放表达式
const arr = [...(5 > 0 ? ['a'] : []),'b'];
console.log(arr); //['a','b']
//6.与解构赋值结合,用于生成数组
const a1 = [1, 2];
const a2 = [...a1]; //写法1
const [...a2] = a1; //写法2
const [first, ...rest] = [1, 2, 3, 4, 5];
first //1
rest //[2, 3, 4, 5]
const [first, ...rest] = [];
first //undefined
rest //[]
const [first, ...rest] = ["foo"];
first //"foo"
rest //[]
//1234567891011121314151617
//7.合并数组
[...arr1, ...arr2, ...arr3] //[ 'a', 'b', 'c', 'd', 'e' ]
123
//8.数组的克隆
var arr1 = [0, 1, 2];
var arr2 = [...arr1];
arr1[0]=100;
console.log(arr2); //[0, 1, 2]
/* 乍一看,arr2与arr1不共用引用地址,arr2不随着arr1变化,接着往下看 */
var arr1 = [0, [1,11,111], 2];
var arr2 = [...arr1];
arr1[1][0]=100;
console.log(arr2); //[0, [100,11,111], 2]
# 1.34 实现异步的方法
# 1.34.1 回调函数
ajax(url, () => {
// 处理逻辑
ajax(url1, () => {
// 处理逻辑
ajax(url2, () => {
// 处理逻辑
})
})
})
**优点:**简单、容易理解和实现
**缺点:**不利阅读和维护,耦合度高,不能使用try...catch捕获,不能直接 return
# 1.34.2 promise
本意是承诺,这个承诺一旦从等待状态变成为其他状态就永远不能更改状态了
Promise的三种状态
Pending----Promise对象实例创建时候的初始状态
Fulfilled----可以理解为成功的状态
Rejected----可以理解为失败的状态
let p = new Promise((resolve, reject) => {
reject('reject')
resolve('success')//无效代码不会执行
})
p.then( value => {
console.log(value)
},
reason => {
console.log(reason)//reject
})
当我们在构造 Promise 的时候,构造函数内部的代码是立即执行的
new Promise((resolve, reject) => {
console.log('1')
resolve('success')
})
console.log('2')
promise的链式调用
每次调用返回的都是一个新的Promise实例(这就是then可用链式调用的原因)
如果then中返回的是一个结果的话会把这个结果传递下一次then中的成功回调
如果then中出现异常,会走下一个then的失败回调
在 then中使用了return,那么 return 的值会被Promise.resolve() 包装(见例1,2)
then中可以不传递参数,如果不传递会透到下一个then中(见例3)
catch 会捕获到没有捕获的异常
Promise.resolve(1)
.then(res => {
console.log(res)
return 2 //包装成 Promise.resolve(2)
})
.catch(err => 3)
.then(res => console.log(res))
ajax(url).then(res => {
console.log(res)
return ajax(url1)
}).then(res => {
console.log(res)
return ajax(url2)
}).then(res => console.log(res))
存在一个缺点:无法取消promise,错误需要通过回调函数捕获
# 1.34.3 生成器generator/yield
**特点:**控制函数的执行
- Generator 函数是一个状态机,封装了多个内部状态
- Generator 函数除了状态机,还是一个遍历器对象生成函数
- 可暂停函数, yield可暂停,next方法可启动,每次返回的是yield后的表达式结果
- yield表达式本身没有返回值,或者说总是返回undefined。next方法可以带一个参数,该参数就会被当作上一个yield表达式的返回值
function *foo(x) {
let y = 2 * (yield (x + 1))
let z = yield (y / 5)
return (x + y + z)
}
let it = foo(2)
console.log(it.next()) // => {value: 3, done: false}
console.log(it.next(20)) // => {value: 8, done: false}
console.log(it.next(30)) // => {value: 72, done: true}
// 解决回调地狱
function *fetch() {
yield ajax(url, () => {})
yield ajax(url1, () => {})
yield ajax(url2, () => {})
}
let it = fetch()
let result1 = it.next()
let result2 = it.next()
let result3 = it.next()
# 1.34.3 async/await
特点:
- async/await是基于Promise实现的,它不能用于普通的回调函数
- async/await与Promise一样,是非阻塞的
async function fn() {
return "async"
}
console.log(fn()) // -> Promise {<resolved>: "async"}
类似generator调用方式
async function ff() {
await ajax(url1, ()=>{})
await ajax(url2,()=>{})
await ajax(url3,()=>{})
}
await ff()
模拟一个并发请求
function readAll() {
read1()
read2()//这个函数同步执行
}
async function read1() {
let r = await ajax(url1,()=>{})
console.log(r)
}
async function read2() {
let r = await ajax(url2,()=>{})
console.log(r)
}
readAll()
# 1.35 循环i 一次性定时器中输出什么,如何解决?
下段代码输出
for (var i = 0; i< 10; i++){
setTimeout((i) => {
console.log(i)
}, 0)
}
期望输出0-9
结果输出 10个9,原因:setTimeout是异步的,而var 这个时候是全局变量,所以打印10个9
方法一:
for (var i = 0; i< 10; i++){
setTimeout((i) => {
console.log(i)
}, 1000,i)
}
方法二:
for (let i = 0; i< 10; i++){
setTimeout(() => {
console.log(i)
}, 1000)
}
方法三:
for (var i = 0; i< 10; i++){
((i)=>{
setTimeout(() => {
console.log(i)
},1000);
})(i)
}
方法四:
for (var i = 0; i< 10; i++){
setTimeout(console.log(i),1000);
}
for (var i = 0; i< 10; i++){
setTimeout((()=>console.log(i))(),1000);
}
方法五:
for (var i = 0; i< 10; i++){
try{
throw i
}catch(i){
setTimeout(() => {
console.log(i)
}, 1000)
}
}
# 1.36 为什么js是单线程
**用途:**js在创立之初主要是应用于用户与浏览器的交互,以及操作dom,这一特性决定了,只能是单线程,否则会带来复杂的同步问题。
例如: 如果js被设计了多线程,如果有一个线程要修改一个dom元素,另一个线程要删除这个dom,这时候就不能处理。避免这个问题,浏览器就设计了单线程,避免了这个麻烦
# 1.37 死锁
死锁是指两个或两个以上的进程在执行过程中,由于竞争资源而造成阻塞的现象,若无外力作用,它们都将无法继续执行
产生原因:
- 竞争资源引起进程死锁
- 可剥夺和非剥夺资源
- 竞争非剥夺资源
- 竞争临时性资源
- 进程推进顺序不当
产生条件:
互斥条件:涉及的资源是非共享的
- 涉及的资源是非共享的,一段时间内某资源只由一个进程占用,如果此时还有其它进程请求资源,则请求者只能等待,直至占有资源的进程用毕释放
不剥夺条件:不能强行剥夺进程拥有的资源
- 进程已获得的资源,在未使用完之前,不能被剥夺,只能在使用完时由自己释放
请求和保持条件:进程在等待一新资源时继续占有已分配的资源
- 指进程已经保持至少一个资源,但又提出了新的资源请求,而该资源已被其它进程占有,此时请求进程阻塞,但又对自己已获得的其它资源保持不放 环路等待条件:存在一种进程的循环链,链中的每一个进程已获得的资源同时被链中的下一个进程所请求 在发生死锁时,必然存在一个进程——资源的环形链
解决办法
只要打破四个必要条件中的一个就能有效防止死锁的发生
# 1.38 暂时性死区
暂时性死区的本质就是,只要一进入当前作用域,所要使用的变量就已经存在了,但是不可获取,只有等到声明变量的那一行代码出现,才可以获取和使用该变量
let 、const具有暂时性死区
var 不具有暂时性死区
# 浏览器
# 1.1 cookie sessionStorage localStorage 区别
共同点:
都是保存在浏览器端、且同源的
区别:
cookie数据始终在同源的http请求中携带(即使不需要),即cookie在浏览器和服务器间来回传递,而sessionStorage和localStorage不会自动把数据发送给服务器,仅在本地保存。cookie数据还有路径(path)的概念,可以限制cookie只属于某个路径下
存储大小限制也不同,cookie数据不能超过4K,同时因为每次http请求都会携带cookie、所以cookie只适合保存很小的数据,如会话标识。sessionStorage和localStorage虽然也有存储大小的限制,但比cookie大得多,可以达到5M或更大
数据有效期不同,sessionStorage:仅在当前浏览器窗口关闭之前有效;localStorage:始终有效,窗口或浏览器关闭也一直保存,因此用作持久数cookie:只在设置的cookie过期时间之前有效,即使窗口关闭或浏览器关闭
作用域不同,sessionStorage不在不同的浏览器窗口中共享,即使是同一个页面;localstorage在所有同源窗口中都是共享的;cookie也是在所有同源窗口中都是共享的
web Storage支持事件通知机制,可以将数据更新的通知发送给监听者
Storage的api接口使用更方便
# 1.2 如何写一个会过期的localStorage,说说想法
惰性删除 和 定时删除
惰性删除
惰性删除是指,某个键值过期后,该键值不会被马上删除,而是等到下次被使用的时候,才会被检查到过期,此时才能得到删除。
var lsc = (function (self) {
var prefix = 'lsc_'
/**
* 增加一个键值对数据
* @param key 键
* @param val 值
* @param expires 过期时间,单位为秒
*/
self.set = function(key, val, expires) {
key = prefix + key;
val = JSON.stringify({'val': val, 'expires': new Date().getTime() + expires * 1000});
localStorage.setItem(key, val);
};
/**
* 读取对应键的值数据
* @param key 键
* @returns {null|*} 对应键的值
*/
self.get = function(key) {
key = prefix + key;
var val = localStorage.getItem(key);
if (!val) {
return null;
}
val = JSON.parse(val);
if (val.expires < new Date().getTime()) {
localStorage.removeItem(key);
return null;
}
return val.val;
};
return self;
}(lsc || {}));
定时删除
定时删除是指,每隔一段时间执行一次删除操作
- 随机测试20个设置了过期时间的key。
- 删除所有发现的已过期的key。
- 若删除的key超过5个则重复步骤****1,直至重复500次。
var lsc = (function (self) {
var prefix = 'lsc_'
var list = [];
//初始化
self.init = function () {
var keys = Object.keys(localStorage);
var reg = new RegExp('^' + prefix);
var temp = [];
//遍历所有localStorage中的所有key
for (var i = 0; i < keys.length; i++) {
//找出可过期缓存的key
if (reg.test(keys[i])) {
temp.push(keys[i]);
}
}
list = temp;
};
self.init();
self.check = function () {
if (!list || list.length == 0) {
return;
}
var checkCount = 0;
while (checkCount < 500) {
var expireCount = 0;
// 随机测试20个设置了过期时间的key
for (var i = 0; i < 20; i++) {
if (list.length == 0) {
break;
}
var index = Math.floor(Math.random() * list.length);
var key = list[index];
var val = localStorage.getItem(list[index]);
// 从list中删除被惰性删除的key
if (!val) {
list.splice(index, 1);
expireCount++;
continue;
}
val = JSON.parse(val);
// 删除所有发现的已过期的key
if (val.expires < new Date().getTime()) {
list.splice(index, 1);
localStorage.removeItem(key);
expireCount++;
}
}
// 若删除的key不超过5个则跳出循环
if (expireCount <= 5 || list.length == 0) {
break;
}
checkCount++;
}
}
//每隔一秒执行一次定时删除
window.setInterval(self.check, 1000);
return self;
}(lsc || {}));
# 1.3 localStorage 能跨域吗
不能
解决办法
- 通过postMessage来实现跨源通信
- 可以实现一个公共的iframe部署在某个域名中,作为共享域
- 将需要实现localStorage跨域通信的页面嵌入这个iframe
# 1.4 memory cache 如何开启
memory cache 如何开启是一种比较特殊的缓存,他不受max-age、no-cache等配置的影响,即使我们不设置缓存,如果当前的内存空间比较充裕的话,一些资源还是会被缓存下来。但这种缓存是暂时的,一旦关闭了浏览器,这一部分用于缓存的内存空间就会被释放掉。如果真的不想使用缓存,可以设置no-store,这样,即便是内存缓存,也不会生效
# 1.5 localstorage的注意哪些问题
- 兼容性问题
- localStorage在浏览器的隐私模式下面是不可读取的
- localStorage本质上是对字符串的读取,如果存储内容多的话会消耗内存空间,会导致页面变卡
- localStorage不能被爬虫抓取到
# 1.6 浏览器输入URL发生了什么
- URL 解析
- DNS 查询
- TCP 连接
- 处理请求
- 接受响应
- 渲染页面
# 1.7 浏览器是如何渲染页面的?
不同浏览器内核渲染机制有所区别
- HTML 被 HTML 解析器解析成 DOM 树;
- CSS 被 CSS 解析器解析成 CSSOM 树;
- 结合 DOM 树和 CSSOM 树,生成一棵渲染树(Render Tree),这一过程称为 Attachment;
- 生成布局(flow),浏览器在屏幕上“画”出渲染树中的所有节点;
- 将布局绘制(paint)在屏幕上,显示出整个页面。
webkit
Gecko
# 1.8 重绘、重排
概念
- 重排(Reflow):当渲染树的一部分必须更新并且节点的尺寸发生了变化,浏览器会使渲染树中受到影响的部分失效,并重新构造渲染树
- 重绘(Repaint):是在一个元素的外观被改变所触发的浏览器行为,浏览器会根据元素的新属性重新绘制,使元素呈现新的外观。比如改变某个元素的背景色、文字颜色、边框颜色等等
区别:
重绘不一定需要重排(比如颜色的改变),重排必然导致重绘(比如改变网页位置)
引发重排
- 添加、删除可见的dom
- 元素的位置改变
- 元素的尺寸改变(外边距、内边距、边框厚度、宽高、等几何属性)
- 页面渲染初始化
- 浏览器窗口尺寸改变
- 获取某些属性。当获取一些属性时,浏览器为取得正确的值也会触发重排,它会导致队列刷新,这些属性包括:offsetTop、offsetLeft、 offsetWidth、offsetHeight、scrollTop、scrollLeft、scrollWidth、scrollHeight、clientTop、clientLeft、clientWidth、clientHeight、getComputedStyle() (currentStyle in IE)。所以,在多次使用这些值时应进行缓存
优化方案
浏览器会维护1个队列,把所有会引起重排,重绘的操作放入这个队列,等队列中的操作到一定数量或者到了一定时间间隔,浏览器就会flush队列,进行一批处理,这样多次重排,重绘变成一次重排重绘
减少 reflow/repaint:
不要一条一条地修改 DOM 的样式。可以先定义好 css 的 class,然后修改 DOM 的className。
不要把 DOM 结点的属性值放在一个循环里当成循环里的变量。
为动画的 HTML 元件使用 fixed 或 absoult 的 position,那么修改他们的 CSS 是不会reflow 的。
千万不要使用 table 布局。因为可能很小的一个小改动会造成整个 table 的重新布局。(table及其内部元素除外,它可能需要多次计算才能确定好其在渲染树中节点的属性,通常要花3倍于同等元素的时间。这也是为什么我们要避免使用table做布局的一个原因。)
不要在布局信息改变的时候做查询(会导致渲染队列强制刷新)
# 1.9 事件循环(Event loop)
主线程从"任务队列"中读取执行事件,这个过程是循环不断的,这个机制被称为事件循环
JavaScript 的事件分两种
- 宏任务:包括整体代码 script,setTimeout,setInterval
- 微任务:Promise.then(非 new Promise),process.nextTick(node 中)
具体执行:
事件的执行顺序——先执行宏任务,然后执行微任务,任务有同步的任务和异步的任务,同步的进入主线程,异步的进入 Event Table 并注册函数,异步事件完成后,会将回调函数放在队列中,如果还有异步的宏任务,那么就会进行循环执行上述的操作
主 线程会不断从任务队列中按顺序取任务执行,每执行完一个任务都会检查microtask队列是否为空(执行完一个 任务的具体标志是函数执行栈为空),如果不为空则会一次性执行完所有microtask。然后再进入下一个循环去 任务队列中取下一个任务执行
详细步骤:
选择当前要执行的宏任务队列,选择一个最先进入任务队列的宏任务,如果没有宏任务可以选择,则会 跳转至microtask的执行步骤。
将事件循环的当前运行宏任务设置为已选择的宏任务。
运行宏任务。
将事件循环的当前运行任务设置为null。
将运行完的宏任务从宏任务队列中移除。
microtasks步骤:进入microtask检查点。
更新界面渲染。
返回第一步。
执行进入microtask检查的的具体步骤如下:
- 设置进入microtask检查点的标志为true。
- 当事件循环的微任务队列不为空时:选择一个最先进入microtask队列的microtask;设置事
件循环的当 前运行任务为已选择的microtask;运行microtask;设置事件循环的当前运行任务
为null;将运行结束 的microtask从microtask队列中移除。
- 对于相应事件循环的每个环境设置对象(environment settings object),通知它们哪些
promise为 rejected。
- 清理indexedDB的事务。
- 设置进入microtask检查点的标志为false。
注意
当前执行栈执行完毕时会立刻先处理所有微任务队列中的事件,然后再去宏任务队列中取出一个事件。同一次事件循环中,微任务永远在宏任务之前执行
# 1.10 let a = 1 挂载在哪里?
var a 挂载在window下。而let是挂载在 全局函数下面
# 1.11 浏览器垃圾回收机制
浏览器的 Javascript 具有自动垃圾回收机制(GC:Garbage Collecation),执行环境会负责管理代码执行过程中使用的内存
原理:
垃圾收集器会定期(周期性)找出那些不在继续使用的变量,然后释放其内存
function fn1() {
var obj = {name: 'hanzichi', age: 10};
}
function fn2() {
var obj = {name:'hanzichi', age: 10};
return obj;
}
var a = fn1();
var b = fn2();
/*
首先定义了两个function,分别叫做fn1和fn2,当fn1被调用时,进入fn1的环境,会开辟一块内存存放对象{name: 'hanzichi', age: 10},而当调用结束后,出了fn1的环境,那么该块内存会被js引擎中的垃圾回收器自动释放;在fn2被调用的过程中,返回的对象被全局变量b所指向,所以该块内存并不会被释放
*/
问题
到底哪个变量是没有用的?
解决
所以垃圾收集器必须跟踪到底哪个变量没用,对于不再有用的变量打上标记,以备将来收回其内存。用于标记的无用变量的策略可能因实现而有所区别,通常
情况下有两种实现方式:标记清除和引用计数。引用计数不太常用,标记清除较为常用。
标记清除
function test(){
var a = 10 ; //被标记 ,进入环境
var b = 20 ; //被标记 ,进入环境
}
test(); //执行完毕 之后 a、b又被标离开环境,被回收。
引用计数
function test(){
var a = {} ; //a的引用次数为0
var b = a ; //a的引用次数加1,为1
var c =a; //a的引用次数再加1,为2
var b ={}; //a的引用次数减1,为1
}
test();
GC****方案
1. 基础方案
Javascript引擎基础GC方案是(simple GC):mark and sweep(标记清除),即:
- 遍历所有可访问的对象。
- 回收已不可访问的对象。
2. GC的缺陷
和其他语言一样,javascript的GC策略也无法避免一个问题:GC时,停止响应其他操作,这是为了安全考虑。而Javascript的GC在100ms甚至以上,对一般的应用还好,但对于JS游戏,动画对连贯性要求比较高的应用,就麻烦了。这就是新引擎需要优化的点:避免GC造成的长时间停止响应。
3. GC优化策略
- 分代回收(Generation GC) 这个和Java回收策略思想是一致的,也是V8所主要采用的。目的是通过区分“临时”与“持久”对象;多回收“临时对象”区(young generation),少回收“持久对象”区(tenured generation),减少每次需遍历的对象,从而减少每次GC的耗时
补充:对于tenured generation对象,有额外的开销:把它从young generation迁移到tenured generation,另外,如果被引用了,那引用的指向也需要修改。
- 增量GC 这个方案的思想很简单,就是“每次处理一点,下次再处理一点,如此类推”
这种方案,虽然耗时短,但中断较多,带来了上下文切换频繁的问题。
因为每种方案都其适用场景和缺点,因此在实际应用中,会根据实际情况选择方案。
比如:低 (对象/s) 比率时,中断执行GC的频率,simple GC更低些;如果大量对象都是长期“存活”,则分代处理优势也不大。
# 1.12 cookie
- cookie是什么?
cookie 是存储于访问者计算机中的变量。每当一台计算机通过浏览器来访问某个页面时,那么就可以通过 JavaScript 来创建和读取 cookie。
实际上 cookie 是存于用户硬盘的一个文件,这个文件通常对应于一个域名,当浏览器再次访问这个域名时,便使这个cookie可用。因此,cookie可以跨越一个域名下的多个网页,但不能跨越多个域名使用。
不同浏览器对 cookie 的实现也不一样。即保存在一个浏览器中的 cookie 到另外一个浏览器是 不能获取的。
- 怎么使用 cookie?
语法
document.cookie = "name=value;expires=evalue; path=pvalue; domain=dvalue;secure;”
- 注意事项
- cookie可能被禁用。当用户非常注重个人隐私保护时,他很可能禁用浏览器的cookie功能
- cookie是与浏览器相关的。这意味着即使访问的是同一个页面,不同浏览器之间所保存的cookie也是不能互相访问的
- cookie可能被删除。因为每个cookie都是硬盘上的一个文件,因此很有可能被用户删除
- cookie安全性不够高。所有的cookie都是以纯文本的形式记录于文件中,因此如果要保存用户名密码等信息时,最好事先经过加密处理
- cookie 在保存时,只要后面保存的 name 相同,那么就会覆盖前面的 cookie,注意是完全覆盖,包括失效时间,pat
- cookie禁用
sessionID通过cookie保存在客户端,如果将cookie禁用,必将对session的使用造成一定的影响
解决办法:url重写
# 1.13 调试工具
# a.谷歌浏览器
Elements:可查看网页页面代码(修改只是当前使用有效),也可实时调试修改页面ccs代码样式。
console:记录开发者开发过程中的日志信息,也可在里面写js代码。一般页面运行时js报错都是可以在这里看到反馈和定位bug原因及其位置。
Sources:断点调试JS,可以查看程序代码执行的过程,断点调试对于每一个程序员来说可是很重要。
Network:从发起网页页面请求开始,分析HTTP请求后得到的各个请求资源信息(“小编有时候就利用这下载一些网站不给下载的在线视频,比如教学视频”)。
Timeline:记录并分析网站的生命周期所发生的各类事件,分析渲染js执行的每一个阶段。
Application:记录网站加载的各个资源信息。
Security:判断网页是否安全。
Audits:对当前网页的网络利用及网页性能进行检测,并给出一些优化建议。
# b.其他
postman
Postman 是调试接口的最佳工具之一,使用 Postman,我们可以调整请求,分析响应和调试问题
CSSLint
CSSLint 是一个用来帮你找出 CSS 代码中问题的工具,它可做基本的语法检查以及使用一套预设的规则 来检查代码中的问题,规则是可以扩展的
Sentry
Sentry就是来帮我们解决这个问题的,它是是一个实时事件日志记录和聚合平台。它专门用于监视错误 和提取执行适当的事后操作所需的所有信息, 而无需使用标准用户反馈循环的任何麻烦
BrowserStack
BrowserStack 是一款提供网站浏览器兼容性测试的在线云端测试工具,从而开发测试人员不必再准备 很多虚拟机或者手机模拟器
# 网络传输
# 1.1 跨域
跨域是什么?
跨域(Cross-Origin)指的是在浏览器中,当一个请求的源(Origin)与目标资源的源不一致时,即发生跨域访问。在默认情况下,浏览器的同源策略(Same-Origin Policy)会阻止这种跨域访问。同源策略是为了保护用户的信息安全,防止恶意网站对其他网站的资源进行访问和操作。
同源策略规定几个约束
- 协议相同
- 域名相同
- 端口号相同
同源策略限制内容有
- cookie、localstorage、indexedDB 等
- dom节点
- ajax 请求
不受同源策略影响
当存在跨域请求时,浏览器的安全策略也不同
post、get、heade等请求,浏览器会自动发送一个跨域请求的预检请求(options)到目标资源的服务器,如果服务器返回的响应满足一定条件,浏览器会继续发送正式的请求,否则会跨域
put、delete、等,浏览器会先发送一个预检请求到目标资源的服务器服务器返回的响应满足条件后,浏览器发送正式请求。与简单请求不同的是,非简单请求血药确保服务器在响应中设置了跨域请求所允许的响应首部字段
解决方案
- jsonp
**原理:**利用