前端面试题
HTML
HTML5新特性有哪些
1、语义化标签
标签 | 语义 |
---|---|
<header> |
头部标签 |
<nav> |
导航标签 |
<section> |
定义文档某个区域的标签 |
<article> |
内容标签 |
<aside> |
侧边栏标签 |
<footer> |
尾部标签 |
<audio> |
定义声音内容 |
<video> |
定义视频内容 |
<source> |
定义媒介源 |
<canvas> |
定义图像 |
<command> |
定义命令按钮 |
<datalist> |
定义下拉列表 |
<details> |
定义元素的细节 |
<dialog> |
定义对话框或窗口 |
2、视频和音频<audio>
和<video>
audio,video(source标签定义type属性)
属性:src(音频或视频链接),width,height,controls(显示控件)
3、表单
input新增输入类型:date,color,month,email,tel,url等
新增表单元素:keygen(公钥私钥),datalist,progress,meter,output
新增表单属性:placeholder,min/max,pattern,required,width,height,multitype等
4、拖拽API
draggable="true"
来设置此元素是否可以进行拖拽操作
拖拽元素的事件监听:(应用于拖拽元素)
ondragstart
当拖拽开始时调用ondragleave
当鼠标离开拖拽元素时调用ondragend
当拖拽结束时调用ondrag
整个拖拽过程都会调用
目标元素的事件监听:(比如说,你想把元素A拖拽到元素B里,那么元素B就是目标元素。页面中任何一个元素都可以成为目标元素。)
- ondragenter 当拖拽元素进入时调用
- ondragover 当拖拽元素停留在目标元素上时,就会连续一直触发(不管拖拽元素此时是移动还是不动的状态)
- ondrop 当在目标元素上松开鼠标时调用
- ondragleave 当鼠标离开目标元素时调用
5、地理定位
6、Canvas绘图
使用 <canvas>
标签在页面上创建画布元素,画布一旦创建,就可以使用Javascript提供的一套强大的Canvas API编写代码,在画布中绘制任意图形,甚至加入高级动画。
7、svg绘图:可伸缩矢量图形
8、web worker
(28条消息) 前端中的Web Worker详解_daysRoc的博客-CSDN博客_web worker
9、Web Storage
localStorage.setItem(key,value);//保存数据
let value = localStorage.getItem(key);//读取数据
localStorage.removeItem(key);//删除单个数据
localStorage.clear();//删除所有数据
let key = localStorage.key(index);//得到某个索引的值
localStorage:永久存储,存储在浏览器端,存储容量为8M,保存的数据为字符串
sessionStorage:会话存储,窗口关闭即清除,存储在浏览器端,存储容量为8M,保存的数据为字符串
cookie:存储在浏览器端,每次发送http请求时携带,存储容量为4k,保存的数据为字符串
session:存储在服务器端,安全性更高,保存的数据为对象
10、Web Socket
Web Socket
是 HTML5 的新协议,允许服务器端向客户端发送数据,是一种全双工、双向的通信方式,并且它可以保持长时间通信
列举常用的结构标签,并描述其作用
结构标签是专门用于标识页面的不同结构,相对于使用元素而言,结构标签可以实现标签的语义化。
常用的结构标签有以下几种:
- header标签,用于定义文档的页眉。
- nav标签,用于定义页面的导航链接部分。
- section标签,用于定义文档中的节,表示文档中的一个具体的组成部分。
- article标签,常用于定义独立于文档其他部分的内容。
- aside标签,常用于定义页面的一些额外组成部分,如广告栏,侧边栏和相关引用信息。
- footer标签,定义某区域的脚注信息。
label标签有什么用
用
要为
<form>
<label for="male">男</label>
<input type="radio" name="sex" id="male" />
<br />
<label for="female">女</label>
<input type="radio" name="sex" id="female" />
</form>
文件上传用哪个标签,不使用标签如何用JS控制文件上传
<input type="file" name="fileBase" value="fileBase" />
<script>
标签会阻塞加载吗?
会
<script>
标签每次出现,都会让页面等待脚本的解析和执行,无论当前的 JavaScript 代码是内嵌的还是包含在外链文件中,页面的下载和渲染都必须停下来等待脚本执行完成。
<!DOCTYPE html>
<!DOCTYPE>
声明必须是 HTML 文档的第一行,位于 <html>
标签之前。
<!DOCTYPE>
声明不是 HTML 标签;它是指示 web 浏览器关于页面使用哪个 HTML 版本进行编写的指令。
!DOCTYPE
告诉浏览器以HTML5标准解析页面,如果不写,则进入混杂模式
- 严格模式(标准模式):以w3c标准解析代码
- 混杂模式(怪异模式):浏览器用自己的方式解析代码,混杂模式通常模拟老式浏览器的行为,以防止老站点无法工作
- HTML5 没有 DTD(文件类型定义) ,因此也就没有严格模式与混杂模式的区别,HTML5 有相对宽松的方法,实现时,已经尽可能大的实现了向后兼容(HTML5 没有严格和混杂之分)。
defer
<script type="text/javascript" defer="defer">
alert(document.getElementById("p1").firstChild.nodeValue);
</script>
defer 属性规定是否对脚本执行进行延迟,直到页面加载为止。
如果您的脚本不会改变文档的内容,可将 defer 属性加入到 <script>
标签中,以便加快处理文档的速度。因为浏览器知道它将能够安全地读取文档的剩余部分而不用执行脚本,它将推迟对脚本的解释,直到文档已经显示给用户为止。
iframe
1、什么是iframe
- iframe是html元素,用于在网页中内嵌另一个网页。
- iframe默认有一个宽高,存在边界
- iframe是一个行内快级元素,可以通过display修改
2、iframe元素属性介绍
- src : 指定内联网页的地址
- frameborder: iframe默认有个边界,可以设置frameborder为0清除边界。
- width,height: 控制iframe的宽高。
- name: 框架的名称
- scrolling: 是否可滚动,yes ,no , auto
3、iframe互相操作
- 首先明确一点,每个iframe里各自维护自己的全局window对象。
- 另外明确一点,只有同域才能进行iframe之间的读改写,跨域时,只能进行简单的路由跳转。
- 在父级使用window.frames[name]可以获取子iframe的window对象,相应的可以获取document对象,从而对子iframe进行dom操作。
- 在子iframe想要操作父元素的iframe,直接使用子元素的window.parent来获取父级元素的window对象,从而获取document来操作dom。
4、iframe之间的通信
-
发送信息:
当我们要向指定iframe发送信息时,首先要获取该iframe自己的window对象,然后使用该window对象的postMessage发送消息。otherWindow.postMessage(data, orgin,[transfer]);
data是待发送的数据
orgin是发送的地址,为‘*’表示无限制,该参数必传,否则会报错 -
接受信息:
在要接受信息的地方,我们使用window的onmessage事件来接受消息,该事件会返回一个事件对象,其中data包含了返回的数据,orgin返回发送源。 -
安全问题:当我们明确知道orgin是谁时,不要使用‘*’,当要接受信息时,先判断orgin是否是我们要接受的源,在做后续操作。
5、注意事项
获取子元素的document时要确保子元素所有dom元素已经挂载完毕,因此在原生的写法时,必须写在window的onload事件中。
6、优点
- 解决加载缓慢的第三方内容如图标和广告等的加载问题
- iframe无刷新文件上传
- iframe跨域通信
7、缺点
- iframe会阻塞主页面的Onload事件
- 无法被一些搜索引擎索引到
- 页面会增加服务器的http请求
- 会产生很多页面,不容易管理。
CSS
常用的行内元素、块级元素和行内块级元素
行内元素
<a> <span> <strong> <b> <em> <i> <del> <s> <ins> <u>
特点:
- 相邻的元素在同一行
- 宽度和高度直接设置无效
- 默认宽度是本身内容的宽度
- 行内元素能容纳文本和其他行内元素
- 注意:链接里面不能放链接,链接在特殊情况下能放块级元素
块元素
<h1>~<h6>,<p>,<div>,<ul>,<ol>,<li>
特点:
- 独占一行
- 高度,宽度,外边距以及内边距都可以控制
- 宽度是容器(父级宽度)的100%
- 是一个容器及盒子,里面可以放行内和块级元素
- 注意:文字类块级元素不可以放其他块级元素 如h、p标签
行内块元素
<img/> <input/> <td>
特点:
- 和相邻的行内元素在一行,相邻的之间有空白间隙,一行可有多个
- 默认宽度就是本身内容高度
- 高度,宽度,行高,外边距以及内边距都可以设置
inline
和inline-block
的区别
内联元素是不可以控制宽和高、margin等;并且在同一行显示,不换行。
块级元素时可以控制宽和高、margin等,并且会换行。
- inline:使用此属性后,元素会被显示为内联元素,元素则不会换行。
- block:使用此属性后,元素会被现实为块级元素,元素会进行换行。
- inline-block:是使元素以块级元素的形式呈现在行内。意思就是说,让这个元素显示在同一行不换行, 但是又可以控制高度和宽度,这相当于内敛元素的增强。
flex布局
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zaXhTnII-1665279010368)(前端面试题.assets/flex.962f8ac4.png)]
任何一个容器都可以指定为 Flex 布局。行内元素也可以使用Flex 布局。 Webkit 内核的浏览器,必须加上-webkit前缀。
flex容器有两根轴:水平主轴就是x轴(main axis)和竖直轴也是y轴(cross axis)
flex容器的子元素的float,clear,vertical-align属性将失效
flex容器属性:
- flex-direction:决定项目的排列方向。
- flex-wrap:即一条轴线排不下时如何换行。
- flex-flow:是flex-direction属性和flex-wrap属性的简写形式,默认值为row nowrap。
- justify-content:定义了项目在主轴上的对齐方式。(justify)
- align-items:定义项目在交叉轴上如何对齐。
- align-content:定义了多根轴线的对齐方式。如果项目只有一根轴线,该属性不起作用。(换行会产生多轴)
关于flex:1:是flex-grow
(盒子的扩展比率)、flex-shrink
(收缩)、flex-basis
(伸缩基准值)的缩写,默认0 1 auto,若取1:1 1 0
定位布局
1、固定定位(fixed)
当元素的position属性设置为fixed时,这个元素就被固定了,被固定的元素不会随着滚动条的拖动而改变位置(在视野中),相对屏幕的可视区进行定位。
固定定位,使用top、bottom、left和right这4个属性来设置元素相对浏览器窗口的位置,造成元素完全脱离文档流,改变元素类型,多用于“回顶部”特效和固定栏目的设置。
2、相对定位(relative)
当元素的position属性设置为relative时,此时元素便具有相对定位的特性。相对定位的元素,其位置是相对于它的原始位置计算而来的,同样具有top、bottom、left和right这4个属性。相对定位不使元素脱离文档流,不改变元素的本身类型.
3、绝对定位(absolute)
相对于它的第一个position不为static的父元素定位,如果没有,就相对于body
绝对定位absolute,是定位属性中出场频率最高的一个,一般配合relative使用(子绝父相),真正的实现了“指哪打哪”的效果。一个元素变成了绝对定位元素,这个元素就完全脱离正常文档流了,绝对定位元素的前面或者后面的元素会认为这个元素并不存在,即这个元素“浮”在其他元素上面。绝对定位会改变元素的本身类型(行内元素会变成块元素),其位置如果有定位父级相对于定位父级发生偏移,如果没有定位父级相对于整个文档发生偏移。
4、静态定位(static)
如果没有指定元素的position属性值,元素也就是静态定位。static是position属性的默认值,它 表示块保留在原本应该在的位置,不会重新定位。
5、粘性定位(sticky)
粘性定位相对于固定定位(fixed)和相对定位(relative)的结合,设置一个(top、bottom、left、right)值,在值内是相对定位,滚动超过则转为固定定位
盒模型
由margin、border、padding、content组成 盒子的大小 = content + border + padding + margin
**标准盒模型:**width指content部分的宽度
**IE盒模型:**width表示content+padding+border
box-sizing: content-box; 是W3C盒子模型 默认为此项
box-sizing: border-box; 是IE盒子模型
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FPeoBCLL-1665279010369)(前端面试题.assets/标准盒模型.jpg)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-maHG97lW-1665279010369)(前端面试题.assets/IE盒模型.jpg)]
BFC
BFC(Block formatting context)直译为"块级格式化上下文"
它是页面中的一块渲染区域,有一套渲染规则,决定了其子元素如何布局,以及和其他元素之间的关系和作用。
如何创建BFC
- float的值不是none。
- position的值不是static或者relative。
- display的值是inline-block、table-cell、flex、table-caption或者inline-flex
- overflow的值不是visible
BFC的应用
- 编码外边距重叠
- 清除浮动
- 自适应两栏布局(防止浮动元素的覆盖问题)
外边距折叠(Margin Collapse)
首先我们知道当在相邻的块级元素的 margin 会发生合并现象(称为:外边距折叠)
解决办法
- 为目标元素外新建一个父元素,并设置 overflow: hidden
- 设置其中一个为display: inline-block
避免方法:
- 浮动元素不会与任何元素发生叠加,也包括它的子元素
- 绝对定位元素和其他任何元素之间不发生外边距叠加,也包括它的子元素
- inline-block 元素和其他任何元素之间不发生外边距叠加,也包括它的子元素
- 创建了 BFC 的元素不会和它的子元素发生外边距叠加 (兄弟元素还是会叠加的哦)
margin塌陷
在非IE浏览器(如Firefox)下,当容器的高度为auto,且容器的内容中有浮动(float为left或right)的 元素,在这种情况下,容器的高度不能自动伸长以适应内容的高度,使得内容溢出到容器外面而影响 (甚至破坏)布局的现象。这个现象叫浮动溢出,为了防止这个现象的出现而进行的CSS处理,就叫CSS 清除浮动。
- 为父元素添加overflow:hidden
- 多添加一个盒子,添加属性clear:both清除浮动,在所有浮动元素下方添加一个该属性,可以消除float的破坏性,但会增加不必要的标签。 (不用了)
- 使用伪元素清除浮动(推荐)
.clearfix:after{
content:'';
display:block;
clear:both;
}
.clearfix:after {
<----在类名为“clearfix”的元素内最后面加入内容;
content: "."; <----内容为“.”就是一个英文的句号而已。也可以不写。
display: block; <----加入的这个元素转换为块级元素。
clear: both; <----清除左右两边浮动。
visibility: hidden; <----可见度设为隐藏。注意它和display:none;是有区别的。display:hidden;仍然占据空间,只是看不到而已;
line-height: 0; <----行高为0;
height: 0; <----高度为0;
font-size:0; <----字体大小为0;
}
.clearfix {
*zoom:1;} <----这是针对于IE6的,因为IE6不支持:after伪类,这个神奇的
zoom:1让IE6的元素可以清除浮动来包裹内部元素。
CSS的单位
em:em作为font-size的单位时,其代表父元素的字体大小,em作为其他属性单位时,代表自身字体大小
**pt:**磅(1 pt 等于 1/72 英寸)
rem:相对于根元素<html>
,这样就意味着,我们只需要在根元素确定一个 px 字号,则可以来算出元素的宽高 实现办法:
window.onresize=function(){
var html = document.getElementsByTagName("html")[0];
var width = window.innerWidth; // 获取窗口的文档显示区的宽度
var font_Size = 16/1920*width; // 设计稿以1920为准,在1920的设计稿中:16px = 1rem
html.style.fontSize = font_Size+"px";
};
**vw、vh:**100vw代表视口宽度100%
水平居中 垂直居中
水平垂直居中的flex实现:
方法一:
div{
//父元素
display:flex;
justify-content:center; //水平
align-items:center; //垂直
}
方法二:
div{
//父元素
display:flex
}
div{
//子元素
margin:auto;
}
水平垂直居中的绝对定位实现:
.father{
position: relative
}
.box {
position: absolute;;
top: 50%;
left: 50%;
margin-left: -50px; //宽的一半
margin-top: -50px; //高的一半
//translate(-50%,-50%)
}
文本/行内
.text{
text-align:center; //水平
line-height: 10px //垂直
}
请解释什么是雪碧图/精灵图(css sprites),以及如何实现?
雪碧图是把多张图片整合到一张上的图片。它被运用在众多使用了很多小图标的网站上(Gmail 在使用)。实现方法:
- 使用生成器将多张图片打包成一张雪碧图,并为其生成合适的 CSS。
- 每张图片都有相应的 CSS 类,该类定义了
background-image
、background-position
和background-size
属性。 - 使用图片时,将相应的类添加到你的元素中。
如何做响应式布局?
-
百分比布局
-
使用媒体查询
@media only screen and (min-width: 1200px) { .example { background: pink;} }
-
使用rem
-
vw,vh布局
-
flex布局
CSS3新特性
CSS3有哪些新特性?CSS3新特性详解 - 知乎 (zhihu.com)
transition
box-shadow
rgba
flex
CSS选择器优先级
选择器的优先级:内联样式 > ID 选择器 (100)> 类选择器 = 属性选择器 = 伪类选择器(10) > 标签选择器 = 伪元素选择器(1)
!important 优先级最高
display:none
和visibility:hidden
的区别
- display:none是彻底消失,不在文档流中占位,浏览器也不会解析该元素;visibility:hidden是视觉上消失了,可以理解为透明度为0的效果,在文档流中占位,浏览器会解析该元素;
- 使用visibility:hidden比display:none性能上要好,display:none切换显示时visibility,页面产生回流(当页面中的一部分元素需要改变规模尺寸、布局、显示隐藏等,页面重新构建,此时就是回流。所有页面第一次加载时需要产生一次回流),而visibility切换是否显示时则不会引起回流。
CSS 选择符有哪些?
- id 选择器( # myid)
- 类选择器(.myclassname)
- 标签选择器(div, h1, p)
- 相邻选择器(h1 + p)
- 子选择器(ul > li)
- 后代选择器(li a)
- 通配符选择器( * )
- 属性选择器(a[rel = “external”])
- 伪类选择器(a:hover, li:nth-child)
利用css绘制三角形
1、border
div{
width:0
height:0;
border:50px solid transparent;
border-bottom:#eee;
}
注意:无法只设置单独一边,这样没有效果,要将其他三个设置为透明
2、 linear-gradient
.triangle {
width: 100px;
height: 100px;
background: linear-gradient(45deg, deeppink, deeppink 50%, transparent 50%, transparent 100%);
}
3、clip-path
(12条消息) clip-path介绍_定栓的博客-CSDN博客_clippath
.triangle{
width: 160px;
height: 200px;
background-color: skyblue;
clip-path: polygon(0 0, 0% 100%, 100% 50%);
}
画一条0.5px的线
1.采用meta viewport
的方式 (暂没看懂)
<meta name="viewport" content="width=device-width, initial-scale=0.5, minimum-scale=0.5, maximum-scale=0.5"/>
这样子就能缩放到原来的0.5倍,如果是1px那么就会变成0.5px
要记得viewport只针对于移动端,只在移动端上才能看到效果
2.采用transform: scale()
的方式
#line{
border-bottom:1px solid black;
transform:scaleY(0.5);
}
正方形变成圆形
本质是在正方形四个角添加圆,圆的半径是 border-radius 的值
故想得到圆形,将 border-radius 设为正方形边长的一半或者是50%即可
三列均分布局
(25条消息) 前端之三列布局_Cassiel.Lee的博客-CSDN博客_三列布局
- 使用布局flex,每个子元素设置
flex:1;
双飞翼布局
写一个左中右布局,其中左,右两块固定宽200,中间自适应布局
<div class="l"></div>
<div class="r"></div>
<div class="m"></div> //要写在后面
* {
margin: 0;
padding: 0;
}
.l {
width: 200px;
height: 50px;
float: left;
background-color: purple;
}
.r {
width: 200px;
height: 50px;
float: right;
margin-left: 200px;
background-color: red;
}
.m {
width: 100%;
height: 50px;
padding: 0 200px;
background-color: pink;
}
顶部左侧固定布局
CSS继承有哪些
css继承样式有:visibility、cursor、letter-spacing、color、font、text-indent、text-align、list-style、list-style-type、border-collapse等
单行文本省略处理
div{
overflow:hidden;
text-overflow:ellipsis;
white-space:nowrap;
}
多行文本省略处理
div{
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 3; // 最多显示几行
overflow: hidden;
}
JavaScript
不支持冒泡的事件
妈(mouseenter)妈(mouseleave)不(blur)放(focus)心你(load)(unload)(resize)
mouseenter和mouseover的区别
mouseenter 只有经过自身时才会触发事件,经过自身里的子盒子不会触发,因为不会冒泡,同样的还有mouseleave
mouseover 经过自身和自身的子盒子都会触发事件
7种基本数据类型*
原始类型:Number、String、Boolean、Null、Undefined、Symbol(ES6新增)
复杂数据类型:Object
如何判断一个数是整数
- 对 1 取模% 等于0即为整数
- Number.isInteger()
0.1+0.2===0.3?
js中0.1+0.2为什么不等于0.3 - 简书 (jianshu.com)
不是,小数点在计算机中是以二进制表示,而有些小数用二进制表示是无穷,所以才会出现上面这种精确度的问题。
2的53次方怎么表示
- 2**53
- Math.pow(2,53)
null 和 undefined 区别
undefined理解为不存在,表示缺少值,即此处应该有值,但没有定义,转为数值时为NaN,typeof返回‘undefined’字符。
1. 变量被声明了,但没有赋值时,就等于undefined。
2. 调用函数时,应该提供的参数没有提供,该参数等于undefined。
3. 对象没有赋值的属性,该属性的值为undefined。
4. 函数没有返回值时,默认返回undefined。
null表示没有对象,即该处不应该有值,转换为数值时为0,typeof返回‘object‘
1. 作为函数的参数,表示该函数的参数不是对象。
2. 作为对象原型链的终点。
window.onload执行时间
window.onload() 方法用于在网页加载完毕后立刻执行的操作,即当 HTML 文档加载完毕后,立刻执行某个方法。
为什么使用 window.onload()?
因为 JavaScript 中的函数方法需要在 HTML 文档渲染完成后才可以使用,如果没有渲染完成,此时的 DOM 树是不完整的,这样在调用一些 JavaScript 代码时就可能报出"undefined"错误。
如何判断一个JS变量的类型*
1、typeof
对大多数的类型判断都是正确的,返回的都是小写的字符串类型,但是无法区分数组,null
,和真正的Object
,它的判断都是 “object”。
NAN——not a number 属于一个报错,typeof NAN 是 number
typeof null 是 object ,但是null是原始数据类型,这个属于历史弥留问题,(为什么会有这个问题?(14条消息) JavaScript “typeof“判断“null “为什么是“object”_小马哥呀!的博客-CSDN博客_typeof判断null)
2、instanceof
instanceof
运算符与 typeof
运算符相似,用于识别正在处理的对象的类型。instanceof
运算符用来判断一个构造函数的 prototype 属性所指向的对象是否存在另外一个要检测对象的原型链上。
简单来说:instanceof 用于判断一个变量是否某个对象的实例
var a = new Array()
alert(a instanceof Array) //true
3、Object.prototype.toString.call(变量) 各种类型都合适
Object.prototype.toString.call(null);// ”[object Null]”
Object.prototype.toString.call(undefined);// ”[object Undefined]”
Object.prototype.toString.call(“abc”);// ”[object String]”
Object.prototype.toString.call(123);// ”[object Number]”
Object.prototype.toString.call(true);// ”[object Boolean]”
// 除此之外还有 ”[object Function]”、”[object Date]”、”[object Array]”
4、使用constructor检测
instanceof 的原理
instanceof 主要的实现原理就是只要右边变量的 prototype 在左边变量的原型链上即可。因此,instanceof 在查找的过程中会遍历左边变量的原型链,直到找到右边变量的 prototype,如果查找失败,则会返回 false,告诉我们左边变量并非是右边变量的实例。
let、var、const区别*
从以下三个方面说:
**变量提升方面:**var声明的变量存在变量提升,即变量可以在声明之前调用,值为undefined。 let和const不存在变量提升问题( 注意这个‘问题’后缀,其实是有提升的,只不过是let和const具有一个暂时性死区的概念,即没有到其赋值时,之前就不能用 ),即它们所声明的变量一定要在声明后使用,否则报错。
**块级作用域方面:**var不存在块级作用域,let和const存在块级作用域
**声明方面:**var允许重复声明变量,let和const在同一作用域不允许重复声明变量。其中const声明一个只读的常量(因为如此,其声明时就一定要赋值,不然报错)。一旦声明,常量的值就不能改变。
拓展:如何使const声明的对象内属性不可变,只可读呢?
使用 Object.freeze(obj) 冻结obj,就能使其内的属性不可变,但它有局限,就是obj对象中要是有属性是对象,该对象内属性还能改变,要全不可变,就需要使用递归等方式一层一层全部冻结。
如果const声明了一个对象,对象里的属性是可以改变的
const obj={ name:'蟹黄'}; obj.name='同学'; console.log(obj.name);//同学
因为const声明的obj只是保存着其对象的引用地址,只要地址不变,就不会出错。
以下代码输出什么?为什么呢?
for(var i = 0; i < 5; i++){
setTimeout(function timer(){
console.log(i);
},0)
}
答:55555
因为 var 没有块级作用域,setTimeout是异步任务,会将其放入异步任务队列中,等待主线程执行栈完成才执行,而等到执行栈完成的时候,i 已经变成4了。解决方法是把var改成let,let有块级作用域。或者将定时器用一个立即执行函数包起来,同时里面设一个变量存储每次的 i
var 和 let 在 for 循环中的问题
首先我们知道在for循环中,异步打印循环变量得到的值是一样的,都是循环最后一次的 i 的值
for(var i = 0;i<10;i++){
setTimeout(function(){
console.log(i)
},100)
} // 输出全是10
// 输出全是10的原因是因为i是全局变量,最后访问的都是全局变量i,而每次循环改变i的值就是改变全局变量的值,故而输出值均为10
// 之前对于这种问题的解决办法是通过闭包来实现
for(var i = 0;i<10;i++){
(function(){
var j = i;
setTimeout(function(){
console.log(j);
},100)
}())
} // 输出0123456789;
上面解决办法的闭包实现,其实就是通过函数来构建一个作用域,每个作用域存储不同的i值,然后异步调用的函数是通过作用域链的调用规则访问到他创建时所在作用域的变量,就是创建时j的值,而j的值是局部变量,所以就能打印出0123456789;
而 let 呢,因为他是块级作用域,在 for 循环中,很明显我们是能找到一个{}
构成的代码块的
for(let i = 0;i<10;i++){
setTimeout(function(){
console.log(i)
},100)
} // 0123456789;
作用域链
作用域链:
当使用一个变量时,首先查找当前变量对象上是否有该值,如果不存在该变量就向他的包含对象询问是否有该变量,如果依然不存在继续向包含对象的包含对象询问,直到找到全局对象,如果依然不存在就说明该变量不存在,如果中途在任何一个变量对象查找到该变量就返回该变量的值。
包含对象:
外层执行环境的变量对象就是当前执行环境的包含对象,这里我们可以简单的理解为包含对象与当前的变量对象是从属关系。
如何区分slice、splice和split
slice(切割):
slice 能够截取 字符串 或 数组,并返回一个新的 字符串 或 数组(不改变原字符串或数组)
let str = 'hello world'
// 截取下标 [2,5) 区间的字符串,左闭右开,结果为:llo
let str2 = str.slice(2,5)
splice(拼接):
splice 是一个专门用于 数组操作 的方法,堪称最强大的数组操作方法。它可以对数组中的元素进行删除、插入和替换。替换原数组,返回删除的元素数组。
注意:splice 操作的是原数组! arr.splice(start,num,arg1,arg2...)
- 功能一:删除数组元素:
let arr = [1,2,3,4,5,6,7,8,9]
// 从 下标为 3 的项开始,依次向后删除 2 个元素,返回被删除的元素数组:[4, 5],原数组改变为:[1, 2, 3, 6, 7, 8, 9]
arr.splice(3,2)
对数组中的元素进行删除、插入和替换,替换原数组,返回删除的元素数组(堪称最强大的数组操作方法)
- 从 start下标 开始,删除 num 个元素,再在此处插入 arg1,arg2,arg3…
- 如果只填了 start 却没填 其它参数,则代表删除其后的所有元素
- 功能二:删除并插入元素:
如果我们对上面的代码进行更改,则可以完成插入元素的功能。
let arr = [1,2,3,4,5,6,7,8,9]
// 从 下标为 3 的项开始,依次向后删除 2 个元素,并将 1, 2, 3 添加到被删除的元素的位置。返回被删除的元素数组:[4, 5],原数组改变为:[1, 2, 3, 1, 2, 3, 6, 7, 8, 9]
arr.splice(3,2,1,2,3)
- 功能三:单纯插入数组:
let arr = [1,2,3,4,5,6,7,8,9]
// 从 下标为 3 的项开始,依次向后添加 1, 2, 3 等元素。返回被删除的元素数组:[],原数组改
变为:[1, 2, 3, 1, 2, 3, 4, 5, 6, 7, 8, 9]
arr.splice(3,0,1,2,3)
split(分离):
以基于指定的分隔符将一个字符串分割成 多个子字符串,并将结果放在一个数组中
str.split('分隔字符串')
let str = 'this is a test'
str.split('s')
// 返回结果为["thi", " i", " a te","t"]
JS闭包概念
- 自由变量:可以理解为,凡是跨了自己的作用域的变量都叫自由变量。
- 闭包:引用了另一个函数作用域中变量的函数,通常是在嵌套函数中实现的。
要点:
- 函数声明时,会生成一个独立的作用域
- 同一作用域的对象可以互相访问
- 作用域呈层级包含状态,形成作用域链,子作用域的对象可以访问父作用域的对象,反之不能;另 外子作用域会使用最近的父作用域的对象
特点:
- 函数嵌套函数
- 外部函数可以访问内部函数的变量
- 参数和变量不会被垃圾回收 因为作用域链不被释放(相较于全局变量,其变量不可被访问,所以更加安全)
最简单的闭包:
function fuc1(){
var a = 1 , b = 2;
function close(){
//闭包在此
return a + b;
}
return close;
}
闭包的基础应用:隐藏数据
var foo = (function(){
var secret = 'secret'
//闭包内的函数可以访问secret变量,而secret变量对于外部却是隐藏的
return{
get_secert:function(){
//通过定义的接口来访问secret
return secret
},
new_secret: function(new_secret){
//通过定义的接口来修改secret
secret = new_secret
}
}
}())
foo.get_secret();//得到 "secret"
foo.secret; //Type error 访问不能
foo.new_secret('a new secret'); //通过函数接口,我们访问并修改了secret变量
foo.get_secret(); // 得到 'a new secret'
JS闭包应用(防抖,节流)
- 防抖推迟函数的执行(搜索框等待用户输完n秒之后请求)
- 节流为每隔n秒执行一次(频繁点击按钮,一秒只反应一次)
防抖:事件处理函数只执行最后一次。
function debounce(fn,delay){
let timer = null;
return function(){
if(timer){
clearTimeout(timer)
}else{
timer=setTimeout(fn,delay)
}
}
}
设置一个计时器,规定在延迟时间后触发函数,但是在延迟时间内如果再次触发,就会取消之前的计时器。如此,只有最后一次操作能触发。
节流:如果短时间内大量触发同一事件,那么在函数执行一次之后,该函数在指定的时间期限内不再工作,直至过了这段时间才重新生效。
function throttle(fn,delay){
let valid = true
return function(){
if(!valid){
return false //休息时间暂不接客
}
//工作时间,执行函数并且在间隔时间内把状态为设为无效
valid = false
setTimeout(()=>{
fn()
valid = true
},delay)
}
}
请注意,节流函数并不止上面这种实现方案
- 例如可以完全不借助setTimeout,可以把状态位换成时间戳,然后利用时间戳差值是否大于指定间隔时间来做判定。
- 也可以直接将setTimeout的返回的标记当做判断条件-判断当前定时器是否存在,如果存在表示还在冷却,并且在执行fn之后消除定时器表示激活,原理都一样
//时间戳版本
function throttle(func, wait) {
var context, args;
var previous = 0;
return function() {
var now = new Date();
context = this;
args = arguments;
if (now - previous > wait) {
func.apply(context, args);
previous = now;
}
}
}
//定时器版本
function throttle(func, wait) {
var timeout<