HTML
1.常见的行内,块级元素
块级元素:
特点:可设置宽高边距,占满整行,会自动换行
示例:div、 p、 h1 、h6、ol、ul、dl、table、address、blockquote、form
行内元素:
特点:无法设置宽高 以设置左右方向的内边距和外边距,会影响其他元素和文本的位置,不会占满整行,不会自动换行,行内元素设置垂直方向的 padding 或 padding-bottom 时,它会影响元素的实际高度,因为内边距会被添加到元素的内容框和行框之间,从而改变元素的尺寸。
示例:a、strong、b、em、i、del、s、ins、u、span
行内块元素:
特点:可设置宽高,占满整行,但不会自动换行
示例:img、input
2.简述一下 src 与 href 的区
src 用于替换当前元素,href 用于在当前文档和引用资源之间确立联系。
src 是 source 的缩写,指向外部资源的位置,指向的内容将会嵌入到文档中当前标签所在 位置;在请求 src 资源时会将其指向的资源下载并应用到文档内,例如 js 脚本,img 图片 和 frame 等元素。
<script src =”js.js”></script>
当浏览器解析到该元素时,会暂停其他资源的下载和处理,直到将该资源加载、编译、执行 完毕,图片和框架等元素也如此,类似于将所指向资源嵌入当前标签内。这也是为什么将 js 脚本放在底部而不是头部。 href 是 Hypertext Reference 的缩写,指向网络资源所在位置,建立和当前元素(锚点) 或当前文档(链接)之间的链接,如果我们在文档中添加
<link href=”common.css” rel=”stylesheet”/>
那么浏览器会识别该文档为 css 文件,就会并行下载资源并且不会停止对当前文档的处理。 这也是为什么建议使用 link 方式来加载 css,而不是使用@import 方式。
3.描述 cookies, sessionStorage 和 localStorage 的区别?
cookie 是⽹站为了标示用户身份而储存在用户本地终端 ( Client Side)上的数据 ( 通常 经过加密)
cookie数据始终在同源的http请求中携带 ( 即使不需要), 记会在浏览器和服务器间来回 传递
sessionStorage 和 localStorage 不会自动把数据发给服务器,仅在本地保存
存储⼤小: cookie 数据⼤小不能超过4k sessionStorage 和 localStorage 虽然也有存储⼤小的限制,但比 cookie ⼤得 多, 可以达*到5M或更⼤
有期时间: localStorage 存储持久数据, 浏览器关闭后数据不丢失除⾮主动删除数据 sessionStorage 数据在当前浏览器窗⼝关闭后自动删除 cookie 设置的 cookie 过期时间之前⼀直有效, 即使窗⼝或浏览器关
4.a标签中 active hover link visited 正确的设置顺序是什么?
a:link
a:visited
a:hover
a:active
5.html5有哪些新特性?
HTML5 现在已经不是 SGML 的子集,主要是关于图像,位置,存储,多任务等功能的增加。
(1)绘画 canvas;
(2)用于媒介回放的 video 和 audio 元素;
(3)本地离线存储 localStorage 长期存储数据,浏览器关闭后数据不丢失;
(4)sessionStorage 的数据在浏览器关闭后自动删除;
(5)语意化更好的内容元素,比如 article、footer、header、nav、section;
(6)表单控件,calendar、date、time、email、url、search;
(7)新的技术webworker, websocket, Geolocation;
6.前端页面渲染过程
下面我们来逐一介绍:
1、构建DOM树与CSSOM树
这两者是比较相似的,所以我们放在一起来说。html与css都是拥有层级关系的结构,所以我们将其解析为树结构。
(1)拿到html文件后,首先会将字节转换为字符,确认tokens标签,然后转换为节点,通过节点构建DOM树。
(2)同样的,对应的css也会将字节转换为字符,确认tokens标签,然后转换为节点,通过节点构建CSSOM树。在解析html的过程中,遇到head标签中引用的css,会暂停DOM树的构建,先将css解析并构建CSSOM,然后再继续解析html。这是因为如果后面的html用到了css样式,而样式没有提前解析,就会出现无样式状态。
(3)加载js,在页面渲染期间,如果遇到javascript,浏览器的渲染引擎会暂停工作,先交给javascript引擎来执行需要的js代码。因为如果后面的DOM中涉及到js修改的节点,会造成两次渲染,所以要进行js阻塞。
2、构建渲染树
在DOM树和CSSOM树构建的过程中,渲染树会同时进行构建,它将DOM与CSS进行合并,形成渲染树。
3、重绘与重排
完成渲染树的构建之后,页面会进行初始化渲染,也就是重排(回流),这时一个完整的页面就渲染完成了
7.回流和重绘
回流(reflow):当render tree中的元素的宽高、布局、显示、隐藏或元素内部文字结结构发生改变时,会影响自身及其父元素、甚至追溯到更多的祖先元素发生改变,则会导致元素内部、周围甚至整个页面的重新渲染,页面发生重构,回流就产生了。
重绘(repaint):元素的结构(宽高、布局、显示隐藏、内部文字大小)未发生改变,只是元素的外观样式发生改变,比如背景颜色、内部文字颜色、边框颜色等。此时会引起浏览器重绘,显然重绘的速度快于回流。
回流一定会触发重绘,重绘不一定触发回流。
回流重绘对性能的影响
这里了解一个知识点:渲染css样式会影响js执行的时间,使得加载js脚本变慢。原因如下:
浏览器渲染一个网页的时候会启用两条线程:一条渲染javascript 脚本,另一条渲染 ui 即css 样式的渲染。但这两条线程是互斥的,当javascript 线程运行的时候 ui 线程则会中止暂停,反之亦然。因为当ui 线程运行对页面进行渲染的时候, js 脚本难免会涉及到页面视图上的一些样式的改变,为了使这个改变更加准确 js 脚本只好等待ui 线程渲染完成的时候才去执行。
所以当一个页面的元素样式改动频繁的时候ui 线程就会持续渲染,造成js 代码反应慢半拍,卡顿的情况。回流和重绘都会使得ui线程渲染时间加长,太多就会使得网站性能变差,因此要尽量减少reflow和repaint。
如何减少回流和重绘
导致回流发生的情况如下:
- 改变窗口大小
- 改变文字大小
- 内容的改变,如用户在输入框中敲字
- 激活伪类,如:hover
- 操作class属性
- 脚本操作DOM
- 计算offsetWidth和offsetHeight
- 设置style属性
对应的css属性如下:
- 盒子模型相关属性
- 定位及浮动属性
- 节点内部的文字结构

导致重绘的css属性如下:

减少回流和重绘注意点如下:
css
避免设置多层内联样式。
如果需要设置动画效果,最好将元素脱离正常的文档流。
避免使用CSS表达式(例如:calc())。
123
JavaScript
1.避免频繁操作样式,最好将样式列表定义为class并一次性更改class属性。
2.避免频繁操作DOM,创建一个documentFragment,在它上面应用所有DOM操作,最后再把它添加到文档中。
3.可以先为元素设置为不可见:display: none,操作结束后再把它显示出来。
8.iframe的有缺带点
iframe的优点:
1、iframe能够原封不动的把嵌入的网页展现出来;
2、如果有多个网页引用iframe,那么只需要修改iframe的内容,就可以实现调用每一个页面的更改,方便快捷;
3、网页如果为了统一风格,头部和版本都是一样的,就可以写成一个页面,用iframe嵌套,可以增加代码的可重用;
4、如果遇到加载缓慢的第三方内容,如图标或广告,这些问题可以由iframe来解决;
5、iframe会堵塞主页面的Onload事件;
6、iframe和主页面共享连接池,而浏览器对相同域的连接有限制,所以会影响页面的并行加载。
iframe的缺点:
1、iframe会阻塞主页面的Onload事件;
2、iframe和主页面共享链接池,而浏览器对相同城的链接有限制,所以会影响页面的并行加载;
3、使用iframe之前需要考虑这两个缺点,如果需要使用iframe,最好是通过JavaScript;
4、动态给iframe添加src属性值,这样可以可以绕开以上两个问题
5、不利于seo
6、代码复杂,无法一下被搜索引擎索引到
7、iframe框架页面会增加服务器的http请求,对于大型网站不可取。
8、很多的移动设备无法完全显示框架,设备兼容性差。
CSS
1.display 的 block、inline 和 inline-block 的区别
(1)block:会独占一行,多个元素会另起一行,可以设置 width、 height、margin 和 padding 属性;
(2)inline:元素不会独占一行,设置 width、height 属性无效。 但可以设置水平方向的 margin 和 padding 属性,不能设置垂直方向 的 padding 和 margin;
(3)inline-block:将对象设置为 inline 对象,但对象的内容作为 block 对象呈现,之后的内联对象会被排列在同一行内。
2.display: none; 与 visibility: hidden; 的区别
- display:none ;会让元素完全从渲染树中消失, 渲染的时候
不占据任何空间; - visibility: hidden ;不会让元素从渲染树消失, 渲染师元素继续占据空间, 只是内 容不可⻅
- display: none ;是
⾮继承属性, ⼦孙节点消失由于元素从渲染树消失造成, 通过修改 ⼦孙节点属性⽆法显示 ; - visibility: hidden; 是
继承属性, ⼦孙节点消失由于继承 了 hidden , 通过设置 visibility: visible; 可以让⼦孙节点显式
修改常规流中元素的 display 通常会造成⽂档重排 。修改 visibility 属性只会造成 本元素的重绘。 读屏器不会读取 display: none ;元素内容;会读取 visibility: hidden; 元素内容
opacity 属性表示元素的透明度,将元素的透明度设置为0后,在我们⽤户眼中,元素也是隐藏的 不会引发重排,⼀般情况下也会引发重绘
.transparent {
opacity:0;
}
由于其仍然是存在于⻚⾯上的,所以他⾃身的的事件仍然是可以触发的,但被他遮挡的元素是不能触发 其事件的 需要注意的是:其⼦元素不能设置opacity来达到显示的效果 特点:改变元素透明度,元素不可⻅,占据⻚⾯空间,可以响应点击事件
clip-path
.hide {
clip-path: polygon(0px 0px,0px 0px,0px 0px,0px 0px);
}
特点:元素不可⻅,占据⻚⾯空间,⽆法响应点击事件
3. 对 BFC 的理解,如何创建 BFC
块格式化上下文(Block Formatting Context,BFC)是 Web 页面的 可视化 CSS 渲染的一部分,是布局过程中生成块级盒子的区域,也是 浮动元素与其他元素的交互限定区域。 通俗来讲:BFC 是一个独立的布局环境,可以理解为一个容器,在这 个容器中按照一定规则进行物品摆放,并且不会影响其它环境中的物 品。如果一个元素符合触发 BFC 的条件,则 BFC 中的元素布局不受外 部影响。
- 创建 BFC 的条件
- 根元素:body;
- 元素设置浮动:float 除 none 以外的值;
- 元素设置绝对定位:position (absolute、fixed);
- display 值为:inline-block、table-cell、table-caption、flex 等;
- overflow 值为:hidden、auto、scroll;
BFC 的特点:
- 垂直方向上,自上而下排列,和文档流的排列方式一致。
- 在 BFC 中上下相邻的两个容器的
margin 会重叠 - 计算 BFC 的高度时,需要计算浮动元素的高度 BFC 区域不会与浮动的容器发生重叠
- BFC 是独立的容器,容器内部元素不会影响外部元素
- 每个元素的左 margin 值和容器的左 border 相接触
BFC 的作用:
解决 margin 的重叠问题:由于 BFC 是一个独立的区域,内部的元素 和外部的元素互不影响,将两个元素变为两个 BFC,就解决了 margin 重叠的问题。
解决高度塌陷的问题:在对子元素设置浮动后,父元素会发生高度塌 陷,也就是父元素的高度变为 0。解决这个问题,只需要把父元素变 成一个 BFC。常用的办法是给父元素设置 overflow:hidden
外边距折叠是 CSS 中的一个特性,它可以导致垂直相邻的外边距合并成一个外边距,从而影响元素之间的间距。以下是外边距折叠的一般规则:
-
兄弟元素折叠: 当两个垂直相邻的兄弟元素都具有外边距时,它们的外边距可能会折叠成一个外边距。折叠的大小取两者中的较大者,而不是简单地将它们相加。
-
父子元素折叠: 父元素的外边距与第一个/最后一个子元素的外边距可能会折叠。这种情况只在
没有边框、内边距、块格式化上下文、清除浮动等分隔的情况下才会发生。 -
空块元素折叠: 一个没有内容,只有外边距的空块级元素的外边距可能会折叠。
问题:相邻两个盒子垂直方向上的margin会发生重叠,只会取比较大的margin
解决:(1)设置padding代替margin
(2)设置float
(3)设置overflow
(4)设置position:absolute 绝对定位
(5)设置display: inline-block
4.css3中有哪些新特性
-
选择器和伪类:
- 各种新的选择器,如属性选择器、子选择器、相邻兄弟选择器等。
- 伪类和伪元素的扩展,如
:nth-child()、:nth-of-type()、:not()、:before、:after等。
-
盒模型和布局:
- 圆角边框(
border-radius) - 阴影效果(
box-shadow) - 内容框分割(
box-sizing) - 弹性盒子布局(Flexbox)
- 网格布局(Grid)
- 圆角边框(
-
颜色和渐变:
- RGBA和HSLA颜色表示法
- 线性渐变(
linear-gradient) - 径向渐变(
radial-gradient)
-
文本排版和效果:
- 自定义字体(
@font-face) - 多列文本布局(
column-count、column-gap等) - 文本阴影(
text-shadow) - 文本溢出省略号(
text-overflow: ellipsis)
- 自定义字体(
-
过渡和动画:
- 过渡效果(
transition) - 关键帧动画(
@keyframes、animation)
- 过渡效果(
-
变换和变形:
- 2D和3D变换(
transform) - 3D变换(
perspective、transform-style)
- 2D和3D变换(
-
背景和渐变:
- 背景图像大小调整(
background-size) - 多重背景图像(
background-image) - 多重背景渐变
- 背景图像大小调整(
-
多媒体查询和响应式设计:
- 媒体查询(
@media) - 视口单位(
vw、vh、vmin、vmax)
- 媒体查询(
-
过滤和混合:
- 图像滤镜(
filter) - 混合模式(
mix-blend-mode)
- 图像滤镜(
-
边框和滚动:
-
多边框(
border-image) -
自定义滚动条样式(
::-webkit-scrollbar)
-
5.css定位
1.1 静态定位(static) - 了解
- 静态定位是元素的默认定位方式,无定位的意思。它相当于 border 里面的none,静态定位static,不要定位的时候用。
- 语法:
选择器 { position: static;
}
- 静态定位 按照标准流特性摆放位置,它没有边偏移。
- 静态定位在布局时我们几乎不用的
1.2 相对定位(relative) - 重要
- 相对定位是元素在移动位置的时候,是相对于它自己原来的位置来说的(自恋型)。
- 语法:
选择器 { position: relative;
}
-
相对定位的特点:(务必记住)
-
1.它是相对于自己原来的位置来移动的(移动位置的时候参照点是自己原来的位置)。
-
2.原来在标准流的位置继续占有,后面的盒子仍然以标准流的方式对待它。
因此,相对定位并没有脱标。它最典型的应用是给绝对定位当爹的。
-
-
效果图:

1.3 绝对定位(absolute) - 重要
1.3.1 绝对定位的介绍
- 绝对定位是元素在移动位置的时候,是相对于它祖先元素来说的(拼爹型)。
- 语法:
选择器 { position: absolute; }
- 完全脱标 —— 完全不占位置;
- 父元素没有定位,则以浏览器为准定位(Document 文档)。

3.父元素要有定位
-
元素将依据最近的已经定位(绝对、固定或相对定位)的父元素(祖先)进行定位。
-

-
绝对定位的特点总结:(务必记住)
1.如果没有祖先元素或者祖先元素没有定位,则以浏览器为基准定位(Document 文档)。
2.如果祖先元素有定位(相对、绝对、固定定位),则以最近一级的有定位祖先元素为参考点移动位置。
3.绝对定位不再占有原先的位置。所以绝对定位是脱离标准流的。(脱标)
6.css实现水平垂直居中
1.利⽤定位+margin:auto
.father { width: 300px; height: 300px; background-color: pink; position: relative; } .son { position: absolute; width: 150px; height: 150px; background-color: greenyellow; top:0; left:0; right:0; bottom:0; margin:auto ; }⽗级设置为相对定位,⼦级绝对定位 ,并且四个定位属性的值都设置了0,那么这时候如果⼦级没有设 置宽⾼,则会被拉开到和⽗级⼀样宽⾼ 这⾥⼦元素设置了宽⾼,所以宽⾼会按照我们的设置来显示,但是实际上⼦级的虚拟占位已经撑满了整 个⽗级,这时候再给它⼀个 margin:auto 它就可以上下左右都居中了
2.利⽤定位+margin:负值
.father { width: 300px; height: 300px; background-color: pink; position: relative; } .son { position: absolute; width: 150px; height: 150px; background-color: greenyellow; top: 50%; left: 50%; margin-left:-75px; margin-top:-75px; }3.利⽤定位+transform
.father { width: 300px; height: 300px; background-color: pink; position: relative; } .son { position: absolute; width: 150px; height: 150px; background-color: greenyellow; top: 50%; left: 50%; transform: translate(-50%,-50%); }4.flex弹性布局
.father { width: 300px; height: 300px; background-color: pink; display: flex; align-items: center; justify-content: center; } .son { width: 150px; height: 150px; background-color: greenyellow; }7.css哪些属性可被子元素继承
1. 字体属性:
font、font-style、font-variant、font-weight、font-size、line-height等属性是字体样式的属性,都可以被子元素继承。
2. 文本属性:
color、text-indent、text-align、text-decoration、text-transform、letter-spacing、word-spacing等属性也是可以被子元素继承的属性。
3. 元素可见性:
visibility属性可以被子元素继承,它可以将元素隐藏起来,但不会改变网页的布局。
4. 表格属性:
border-collapse、border-spacing、caption-side、empty-cells等表格属性也是可继承属性。
5. 列表属性:
list-style、list-style-type、list-style-position等列表属性也可以被子元素继承,用于设置无序列表和有序列表的样式。
不可以被继承的css属性
1.display:规定元素应该生成的框的类型;
2.文本属性:vertical-align、text-decoration(用于设置文本的修饰线外观包括上/下划线,管穿线,删除线,闪烁 );
3.盒子模型的属性:width、height、margin、border、padding;
4.背景属性:background、background-color、background-image;
5.定位属性:float、clear、position、top、right、bottom、left、min-width、min-height、maxwidth、max-height、overflow、clip;
css层叠性

8.css选择器以及优先级
- id选择器(#box),选择id为box的元素
- 类选择器(.one),选择类名为one的所有元素
- 标签选择器(div),选择标签为div的所有元素
- 后代选择器(#box div),选择id为box元素内部所有的div元素
- ⼦选择器(.one>one_1),选择⽗元素为.one的所有.one_1的元素
- 相邻同胞选择器(.one+.two),选择紧接在.one之后的所有.two元素
- 群组选择器(div,p),选择div、p的所有元素
- 交集选择器(p.highlight) 选择 class 为 "highlight" 的所有
元素s
伪类选择器
:link :选择未被访问的链接
:visited:选取已被访问的链接
:active:选择活动链接
:hover :⿏标指针浮动在上⾯的元素
:focus :选择具有焦点的
:first-child:⽗元素的⾸个⼦元素
伪元素选择器
:first-letter :⽤于选取指定选择器的⾸字⺟
:first-line :选取指定选择器的⾸⾏
:before : 选择器在被选元素的内容前⾯插⼊内容
:after : 选择器在被选元素的内容后⾯插⼊内容
属性选择器
[attribute] 选择带有attribute属性的元素
[attribute=value] 选择所有使⽤attribute=value的元素
[attribute~=value] 选择attribute属性包含value的元素
[attribute|=value]:选择attribute属性以value开头的元素
css3新增
:first-of-type 表示⼀组同级元素中其类型的第⼀个元素
:last-of-type 表示⼀组同级元素中其类型的最后⼀个元素
:only-of-type 表示没有同类型兄弟元素的元素
:only-child 表示没有任何兄弟的元素
:nth-child(n) 根据元素在⼀组同级中的位置匹配元素
:nth-last-of-type(n) 匹配给定类型的元素,基于它们在⼀组兄弟元素中的位置,从末尾开始计
数
:last-child 表示⼀组兄弟元素中的最后⼀个元素
:root 设置HTML⽂档
:empty 指定空的元素
:enabled 选择可⽤元素
:disabled 选择被禁⽤元素
:checked 选择选中的元素
:not(selector) 选择与 <selector> 不匹配的所有元素
[attribute*=value]:选择attribute属性值包含value的所有元素
[attribute^=value]:选择attribute属性开头为value的所有元素
[attribute$=value]:选择attribute属性结尾为value的所有元素
优先级
!important>内联 > ID选择器 > 类选择器 > 标签选择器
到具体的计算层⾯,优先级是由 A 、B、C、D 的值来决定的,其中它们的值计算规则如下:
-
如果存在内联样式,那么 A = 1, 否则 A = 0
-
B的值等于 ID选择器出现的次数
-
C的值等于 类选择器 和 属性选择器 和 伪类 出现的总次数
-
D 的值等于 标签选择器 和 伪元素 出现的总次数
#nav-global > ul > li > a.nav-link
套⽤上⾯的算法,依次求出 A B C D 的值:
- 因为没有内联样式 ,所以 A = 0
- ID选择器总共出现了1次, B = 1
- 类选择器出现了1次, 属性选择器出现了0次,伪类选择器出现0次,所以 C = (1 + 0 + 0) = 1
- 标签选择器出现了3次, 伪元素出现了0次,所以 D = (3 + 0) = 3
上⾯算出的 A 、 B 、 C 、 D 可以简记作: (0, 1, 1, 3)
知道了优先级是如何计算之后,就来看看⽐较规则:
- 从左往右依次进⾏⽐较 ,较⼤者优先级更⾼
- 如果相等,则继续往右移动⼀位进⾏⽐较
- 如果4位全部相等,则后⾯的会覆盖前⾯的
经过上⾯的优先级计算规则,我们知道内联样式的优先级最⾼,如果外部样式需要覆盖内联样式,就需 要使⽤ !important
9. 清除浮动的方法
<style>
.fahter{
width: 400px;
border: 1px solid deeppink;
}
.big {
width: 200px;
height: 200px;
background-color: pink;
float: left;
}
.small{
width: 250px;
height: 250px;
background-color: green;
float: left;
}
</style>
</head>
<body>
<div class="fahter clearfix">
<div class="big">big</div>
<div class="small">small</div>
</div>
</body>
单标签法
.clear {
clear: both;
}
<div class="clear">额外标签法</div>
父级添加overflow方法
.fahter{
width: 400px;
border: 1px solid deeppink;
overflow: hidden;
}
使用after伪元素清除浮动,:after方式为空元素的升级版,好处是不用单独加标签了。IE8以上和非IE浏览器才支持:after
.clearfix:after{/*伪元素是行内元素 正常浏览器清除浮动方法*/
content: "";
display: block;
height: 0;
clear:both;
visibility: hidden;
}
</style>
</head>
<body>
<div class="fahter clearfix">
<div class="big">big</div>
<div class="small">small</div>
</div>
使用before和after双伪元素清除浮动
.clearfix:after,.clearfix:before{
content: "";
display: block;
clear: both;
}
10. 行内元素float:left后是否变为块级元素
行内元素设置成浮动之后变得更加像是 inline-block (行内块级元素,设置 成这个属性的元素会同时拥有行内和块级的特性, 最明显的不同是它的默认宽 度不是 100% ), 这时候给行内元素设置 padding-top 和 padding-bottom 或者 width 、 height 都是有效果的
11.如何实现单⾏/多⾏⽂本溢出的省略样式
理解也很简单,即⽂本在⼀⾏内显示,超出部分以省略号的形式展现 实现⽅式也很简单,涉及的 css 属性有:
-
text-overflow:规定当⽂本溢出时,显示省略符号来代表被修剪的⽂本
-
white-space:设置⽂字在⼀⾏显示,不能换⾏
-
overflow:⽂字⻓度超出限定宽度,则隐藏超出的内容
-
overflow 设为 hidden ,普通情况⽤在块级元素的外层隐藏内部溢出元素,或者配合下⾯两个属性 实现⽂本溢出省略
-
white-space:nowrap ,作⽤是设置⽂本不换⾏,是 overflow:hidden 和 text-overflow: ellipsis ⽣效的基础
-
text-overflow 属性值有如下: clip:当对象内⽂本溢出部分裁切掉 ellipsis:当对象内⽂本溢出时显示省略标记(...)
<style>
p{
overflow: hidden;
line-height: 40px;
width:400px;
height:40px;
border:1px solid red;
text-overflow: ellipsis;
white-space: nowrap;
}
</style>
</head>
<body>
<p>Lorem ipsum dolor, sit amet consectetur adipisicing elit. Expedita adipisci, ratione modi dicta, molestias cum maxime natus, cupiditate sit vel consequuntur quidem? At deserunt neque ab molestias tempora quam a.
Perferendis non, minus quaerat qui libero nostrum sapiente quam repellat, vel obcaecati eveniet. Voluptate voluptatum explicabo ea cum neque nesciunt tempore quaerat amet sit. Neque amet molestias tenetur minus repudiandae!</p>
多行文本移除省略
.demo {
position: relative;
line-height: 20px;
height: 40px;
overflow: hidden;
}
.demo::after {
content: "...";
position: absolute;
bottom: 0;
right: 0;
padding: 0 20px 0 10px;
}
实现原理很好理解,就是通过伪元素绝对定位到⾏尾并遮住⽂字,再通过 overflow: hidden 隐藏 多余⽂字
基于⾏数截断
纯 css 实现也⾮常简单,核⼼的 css 代码如下:
- -webkit-line-clamp: 2:⽤来限制在⼀个块元素显示的⽂本的⾏数,为了实现该效果,它需要组合 其他的WebKit属性)
- display: -webkit-box:和1结合使⽤,将对象作为弹性伸缩盒⼦模型显示
- -webkit-box-orient: vertical:和1结合使⽤ ,设置或检索伸缩盒对象的⼦元素的排列⽅式
- overflow: hidden:⽂本溢出限定的宽度就隐藏内容
- text-overflow: ellipsis:多⾏⽂本的情况下,⽤省略号“…”隐藏溢出范围的⽂本
p {
width: 400px;
border-radius: 1px solid red;
-webkit-line-clamp: 2;
display: -webkit-box;
-webkit-box-orient: vertical;
overflow: hidden;
text-overflow: ellipsis;
}
12.css画一个三角形
CSS 简写属性/复合属性
border
- 使用1个值时表示上、右、下、左的值都一样
- 使用2个值时,第一个值表示上/下,第二个值表示左/右
- 使用3个值时,第一个值表示上,第二个值表示左/右,第三个值表示下
- 使用4个值时,四个值分别代表上、右、下、左的值
border-radius
- 使用1个值时表示四个角的值都一样
- 使用2个值时,第一个值表示左上/右下,第二个值表示右上/左下
- 使用3个值时,第一个值表示左上,第二个值表示右上/左下,第三个值表示右下
- 使用4个值时,四个值分别代表左上、右上、右下、左下的值
background
background-color: #000000;
background-image: url(images/bg.png);
background-repeat: no-repeat;
background-position: top right;
可以简写成:
/*-----zeda--------*/
background: #000000 url(images/bg.png) no-repeat top right;
font
- font-style
- font-variant
- font-weight
- font-size/line-height
- font-family
<style>
.border {
width: 0;
height: 0;
border: 50px solid;
border-color: transparent transparent transparent #d9534f;
}
</style>
</head>
<body>
<div class="border"></div>
clip-path 就是使用它来绘制多边形(或圆形、椭圆形等)并将其定位在元素内。
.border {
width: 80px;
height: 100px;
background-color: skyblue;
clip-path: polygon(0 0, 0% 100%, 100% 50%);
}
linear-gradient
.border {
display: inline-block;
height: 100px;
width: 120px;
/* 线性方向是从左上角到右下角,背景色从蓝色开始变,到50%位置时切换为透明色再开始变,最后到100%位置时以透明色结束 */
background: linear-gradient(to top right, blue, blue 50%, transparent 50%, transparent 100%);
}
13.::before 和::after 中双冒号和单冒号有什么区别、作用?
在 CSS 中伪类一直用 : 表示,如 :hover, :active 等
伪元素在 CSS1 中已存在,当时语法是用 : 表示,如 :before 和 :after
后来在 CSS3 中修订,伪元素用 :: 表示,如 ::before 和 ::after,以此区分伪元素和伪类
由于低版本 IE 对双冒号不兼容,开发者为了兼容性各浏览器,可以继续使用 :after 这种老语法表示伪元素
- 单冒号(:)用于 css3 的伪类
- 双冒号(::)用于 css3 的伪元素
作用:::before 和 ::after 的主要作用是在元素内容前后加上指定内容。
另外,伪类与伪元素的区别有:
- 伪类与伪元素都是用于向选择器加特殊效果
- 伪类与伪元素的本质区别就是是否抽象创造了新元素
- 伪类只要不是互斥可以叠加使用
- 伪元素在一个选择器中只能出现一次,并且只能出现在末尾
- 伪类与伪元素优先级分别与类、标签优先级相同
14.z-index属性在什么情况下会失效?
通常 z-index 的使用是在有两个重叠的标签,在一定的情况下控制其中一个在另一个的上方或者下方出现。z-index值越大就越是在上层。z-index元素的position属性需要是relative,absolute或是fixed。
z-index属性在下列情况下会失效:
- 父元素position为relative时,子元素的z-index失效。解决:父元素position改为absolute或static;
- 元素没有设置position属性为非static属性。解决:设置该元素的position属性为relative,absolute或是fixed中的一种;
- 元素在设置z-index的同时还设置了float浮动。解决:float去除,改为
display:inline-block
15. 说说你对盒子模型的理解
一个盒子由四个部分组成:content、padding、border、margin
标准盒子模型,是浏览器默认的盒子模型
下面看看标准盒子模型的模型图:

预览
从上图可以看到:
- 盒子总宽度 = width + padding + border + margin;
- 盒子总高度 = height + padding + border + margin
也就是,width/height 只是内容高度,不包含 padding 和 border 值
所以上面问题中,设置width为200px,但由于存在padding,但实际上盒子的宽度有240px
同样看看IE 怪异盒子模型的模型图:

预览
从上图可以看到:
- 盒子总宽度 = width + margin;
- 盒子总高度 = height + margin;
也就是,width/height 包含了 padding 和 border 值
Box-sizing
CSS 中的 box-sizing 属性定义了引擎应该如何计算一个元素的总宽度和总高度
语法:
1box-sizing: content-box|border-box|inherit;
- content-box 默认值,元素的 width/height 不包含padding,border,与标准盒子模型表现一致
- border-box 元素的 width/height 包含 padding,border,与怪异盒子模型表现一致
- inherit 指定 box-sizing 属性的值,应该从父元素继承
回到上面的例子里,设置盒子为 border-box 模型
1<style>
2.box {
3 width: 200px;
4 height: 100px;
5 padding: 20px;
6 box-sizing: border-box;
7}
8</style>
9<div class=\"box\">
10盒子模型
11</div>
这时候,就可以发现盒子的所占据的宽度为200px
16.em/px/rem/vh/vw 这些单位有什么区别?
在css单位中,可以分为长度单位、绝对单位,如下表所指示
| CSS单位 | |
|---|---|
| 相对长度单位 | em、ex、ch、rem、vw、vh、vmin、vmax、% |
| 绝对长度单位 | cm、mm、in、px、pt、pc |
这里我们主要讲述px、em、rem、vh、vw
px
px,表示像素,所谓像素就是呈现在我们显示器上的一个个小点,每个像素点都是大小等同的,所以像素为计量单位被分在了绝对长度单位中
有些人会把px认为是相对长度,原因在于在移动端中存在设备像素比,px实际显示的大小是不确定
这里之所以认为px为绝对单位,在于px的大小和元素的其他属性无关
em
em是相对长度单位。相对于当前对象内文本的字体尺寸。如当前对行内文本的字体尺寸未被人为设置,则相对于浏览器的默认字体尺寸(1em = 16px)
为了简化 font-size 的换算,我们需要在css中的 body 选择器中声明font-size= 62.5%,这就使 em 值变为 16px*62.5% = 10px
这样 12px = 1.2em, 10px = 1em, 也就是说只需要将你的原来的 px 数值除以 10,然后换上 em 作为单位就行了
特点:
- em 的值并不是固定的
- em 会继承父级元素的字体大小
- em 是相对长度单位。相对于当前对象内文本的字体尺寸。如当前对行内文本的字体尺寸未被人为设置,则相对于浏览器的默认字体尺寸
- 任意浏览器的默认字体高都是 16px
举个例子
<div class="big">
我是14px=1.4rem
<div class="small">我是12px=1.2rem</div>
4</div>
样式为
<style>
html {font-size: 10px; } /* 公式16px*62.5%=10px */
.big{font-size: 1.4rem}
.small{font-size: 1.2rem}
</style>
这时候.big元素的font-size为14px,而.small元素的font-size为12px
rem
rem,相对单位,相对的只是HTML根元素font-size的值
同理,如果想要简化font-size的转化,我们可以在根元素html中加入font-size: 62.5%
html {font-size: 62.5%; } /* 公式16px*62.5%=10px */
这样页面中1rem=10px、1.2rem=12px、1.4rem=14px、1.6rem=16px;使得视觉、使用、书写都得到了极大的帮助
特点:
- rem单位可谓集相对大小和绝对大小的优点于一身
- 和em不同的是rem总是相对于根元素,而不像em一样使用级联的方式来计算尺寸
vh、vw
vw ,就是根据窗口的宽度,分成100等份,100vw就表示满宽,50vw就表示一半宽。(vw 始终是针对窗口的宽),同理,vh则为窗口的高度
这里的窗口分成几种情况:
- 在桌面端,指的是浏览器的可视区域
- 移动端指的就是布局视口
像vw、vh,比较容易混淆的一个单位是%,不过百分比宽泛的讲是相对于父元素:
对于普通定位元素就是我们理解的父元素
- 对于position: absolute;的元素是相对于已定位的父元素
- 对于position: fixed;的元素是相对于 ViewPort(可视窗口)
三、总结
px:绝对单位,页面按精确像素展示
em:相对单位,基准点为父节点字体的大小,如果自身定义了font-size按自身来计算,整个页面内1em不是一个固定的值
rem:相对单位,可理解为root em, 相对根节点html的字体大小来计算
vh、vw:主要用于页面视口大小布局,在页面布局上更加方便简单
17.IconFont 的原理是什么
conFont 的使用原理来自于 css 的 @font-face 属性。
这个属性用来定义一个新的字体,基本用法如下:
@font-face {
font-family: <YourFontName>;
src: <url> [<format>],[<source> [<format>]], *;
[font-weight: <weight>];
[font-style: <style>];
}
- font-family:为载入的字体取名字。
- src:[url]加载字体,可以是相对路径,可以是绝对路径,也可以是网络地址。[format]定义的字体的格式,用来帮助浏览器识别。主要取值为:【truetype(.ttf)、opentype(.otf)、truetype-aat、embedded-opentype(.eot)、svg(.svg)、woff(.woff)】。
- font-weight:定义加粗样式。
- font-style:定义字体样式。
18.怎么做移动端的样式适配?
19.怎么实现样式隔离
20.常见布局
两栏布局一般指的是页面中一共两栏,左边固定,右边自适应的布局,一共有四种实现的方式。//以左边宽度固定为200px为例/
/*(1)利用浮动,将左边元素宽度设置为200px,并且设置向左浮动。将右边元素的margin-left设置为200px,宽度设置为auto(默认为auto,撑满整个父元素)。*/
.outer {
height: 100px;}
.left {
float: left;
height: 100px;
width: 200px;
background: tomato;}
.right {
margin-left: 200px;
width: auto;
height: 100px;
background: gold;}
/*(2)第二种是利用flex布局,将左边元素的放大和缩小比例设置为0,基础大小设置为200px。将右边的元素的放大比例设置为1,缩小比例设置为1,基础大小设置为auto。*/
.outer {
display: flex;
height: 100px;}
.left {
flex-shrink: 0;
flex-grow: 0;
flex-basis: 200px;
background: tomato;}
.right {
flex: auto; /*11auto*/
background: gold;}
/*(3)第三种是利用绝对定位布局的方式,将父级元素设置相对定位。左边元素设置为absolute定位,并且宽度设置为200px。将右边元素的margin-left的值设置为200px。*/
.outer {
position: relative;
height: 100px;}
.left {
position: absolute;
width: 200px;
height: 100px;
background: tomato;}
.right {
margin-left: 200px;
height: 100px;
background: gold;}
/*(4)第四种还是利用绝对定位的方式,将父级元素设置为相对定位。左边元素宽度设置为200px,右边元素设置为绝对定位,左边定位为200px,其余方向定位为0。*/
.outer {
position: relative;
height: 100px;}
.left {
width: 200px;
height: 100px;
background: tomato;}
.right {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 200px;
background: gold;}
/三栏布局一般指的是页面中一共有三栏,左右两栏宽度固定,中间自适应的布局,一共有五种实现方式。这里以左边宽度固定为100px,右边宽度固定为200px为例。/
/*(1)利用绝对定位的方式,左右两栏设置为绝对定位,中间设置对应方向大小的margin的值。*/
.outer {
position: relative;
height: 100px;}
.left {
position: absolute;
width: 100px;
height: 100px;
background: tomato;}
.right {
position: absolute;
top: 0;
right: 0;
width: 200px;
height: 100px;
background: gold;}
.center {
margin-left: 100px;
margin-right: 200px;
height: 100px;
background: lightgreen;}
/*(2)利用flex布局的方式,左右两栏的放大和缩小比例都设置为0,基础大小设置为固定的大小,中间一栏设置为auto*/
.outer {
display: flex;
height: 100px;}
.left {
flex: 00100px;
background: tomato;}
.right {
flex: 00200px;
background: gold;}
.center {
flex: auto;
background: lightgreen;}
/*(3)利用浮动的方式,左右两栏设置固定大小,并设置对应方向的浮动。中间一栏设置左右两个方向的margin值,注意这种方式,中间一栏必须放到最后。*/
.outer { height: 100px;}
.left {
float: left;
width: 100px;
height: 100px;
background: tomato;}
.right {
float: right;
width: 200px;
height: 100px;
background: gold;}
.center {
height: 100px;
margin-left: 100px;
margin-right: 200px;
background: lightgreen;}
/*(4)圣杯布局,利用浮动和负边距来实现。父级元素设置左右的 padding,三列均设置向左浮动,中间一列放在最前面,宽度设置为父级元素的宽度,因此后面两列都被挤到了下一行,通过设置 margin 负值将其移动到上一行,再利用相对定位,定位到两边。*/
.outer {
height: 100px;
padding-left: 100px;
padding-right: 200px;}
.left {
position: relative;
left: -100px;
float: left;
margin-left: -100%;
width: 100px;
height: 100px;
background: tomato;}
.right {
position: relative;
left: 200px;
float: right;
margin-left: -200px;
width: 200px;
height: 100px;
background: gold;}
.center {
float: left;
width: 100%;
height: 100px;
background: lightgreen;}
/*(5)双飞翼布局,双飞翼布局相对于圣杯布局来说,左右位置的保留是通过中间列的 margin 值来实现的,而不是通过父元素的 padding 来实现的。本质上来说,也是通过浮动和外边距负值来实现的。*/
.outer { height: 100px;}
.left {
float: left;
margin-left: -100%;
width: 100px;
height: 100px;
background: tomato;}
.right {
float: left;
margin-left: -200px;
width: 200px;
height: 100px;
background: gold;}
.wrapper {
float: left;
width: 100%;
height: 100px;
background: lightgreen;}
.center {
margin-left: 100px;
margin-right: 200px;
height: 100px;}
·JavaScript
1. for in和for of 的区别
for in 用它可以遍历数组,对象,集合。遍历数组遍历的值是数组index索引,遍历对象和集合时遍历的是key值。
ad59e3c46d7766249af49e932232a3f4
//遍历数组
var arr = [1,3,5,7]
for (var index in arr) {
console.log(index) //输出0,1,2,3
}
//遍历对象
var obj = {
name: '张三',
gender: '男',
age: 18
}
for (var key in obj) {
console.log(key) //输出name,gender,age
}
//遍历集合
var map = new Map()
map.name = "zs"
map.gender = "男"
map.age = 18 //相当于添加了三个属性,不是以长度没有变化
for (const value in map) {
console.log(value) //name,gender,age
}
for of 是es6 新加加入的语法,适用于遍历数组,字符串,map/set等拥有iterator迭代器的的集合。遍历数组它与for in 遍历的结果不一样,它遍历的是数组的value值,而不是数组索引值index,结果如下:
for of 不能遍历对象,只能遍历带有iterator接口的,例如Set,Map,String,Array
var arr = [1,3,5,7]
for (var value of arr) {
console.log(value) //输出1,3,5,7
}
var str = "你好世界!"
for (var value in str) {
console.log(value) //输出 你,好,世,界!
}
//遍历集合
var map = new Map()
map.set('name','张三')
map.set('gender','男')
map.set('age',18)
//得到key-value的结果:
for (const entries of map) {
console.log(entries) //['name', '张三'],['gender', '男'],['age', 18]
}
//遍历key值
for (const key of map.keys()) {
console.log(key)//name,gender,age
}
//遍历value值
for (const value of map.values()) {
console.log(value)//张三,男,18
}
虽然然Object没有内置迭代器iterator,但我们可以通过Object提供的方法来遍历对象值:
var obj = {
name: '张三',
gender: '男',
age: 18
}
//通过Object()
for (const obj of Object(map)) {
console.log(obj)//['name', '张三'],['gender', '男'],['age', 18]
}
//通过Object.keys()
for (const key of Object.values(obj)) {
console.log(key)//name,gender,age
}
//通过Object.values()
for (const value of Object.values(obj)) {
console.log(value)//张三,男,18
}
2. for循环中var和let的思考
1、for循环有一个特别之处,就是设置循环变量的那部分是一个父作用域,而循环体内部是一个单独的子作用域。
2、只要块级作用域内存在let、const命令,它所声明的变量就“绑定”(binding)这个区域,不再受外部的影响。
3、var 会穿透 for 、if 等语句。
4、let不允许在相同作用域内,重复声明同一个变量。
1、代码一
for (var i = 0; i < 3; i++) {
let i = 'abc';
console.log(i);
}
结果:输出三遍 ‘abc’
原因:父子作用域互不干扰,所以打印三遍‘abc’。其中,由于var会穿透for语句,全局作用域下,多了一个 i 的变量,值为3
2、代码二
for (let i = 0; i < 3; i++) {
let i = 'abc';
console.log(i);
}
结果:输出三遍 ‘abc’
原因:父子作用域互不干扰,所以打印三遍‘abc’。
3、代码三
for (var i = 0; i < 3; i++) {
var i = 'abc';
console.log(i);
}
结果:输出一遍 ‘abc’
原因:执行第一个循环的时候,由于var会穿透for语句,i=‘abc’改变了全局环境的i,i++变成了NaN,跳出循环,所以打印一遍‘abc’。其中,全局作用域下,多了一个 i 的变量,值为NaN
4、代码四
for (let i = 0; i < 3; i++) {
var i = 'abc';
console.log(i);
}
结果:报错:Uncaught SyntaxError: Identifier ‘i’ has already been declared
原因:由于var会穿透for语句,循环体内部的i提升到了设置循环变量那部分的父作用域,由于let不允许在相同作用域内,重复声明同一个变量,所以报错。
这段代码我们应该都见过,想要的效果是我们每隔一秒钟分别打印出来1,2,3,4,5。然而,效果却是打印了5,5,5,5,5。但是我们把声明i的声明语句改成let就可以这是为什么呢?
for (var i = 0; i < 5; i++) {
setTimeout(() => {
console.log(i);
}, i*1000);
}
原因是因为,var声明的变量会被变量提升,上面一段代码相当于
var i;
for (i = 0; i < 5; i++) {
setTimeout(() => {
console.log(i);
}, i*1000);
}
那么为什么使用let就可以解决呢?
因为let声明的变量会存在一个块级作用域的概念,使用let声明迭代变量时,js引擎会在后台为每一个迭代循环声明一个新的迭代变量,因此每次使用的i算是不同的。
如何不使用let来解决var变量作用域相同的问题呢?
-
内部函数引用外部的变量
-
有内部函数
-
有外部函数
for (var i = 0; i < 5; i++) {
//2.a的结果时一个函数
let a = creater();
//3.执行i,此时i的值还未改变,此时另一个函数作用域中引用了i变量的值,因此i并不会就此销毁而是会被延用到a函数这个作用域中
a(i);
}
//1.creater相当于就是一个工厂函数,用来延长i的作用域
function creater() {
//4.此时参数的i就是a中的i。在创建函数时会创建一个作用域链,此时参数中的i指向的是a中的i,而a中的i又是全局中的i,在函数创建过程中会预装载全局变量对象,并保存到内部的scope中。此时就创建了一个新的空间,因此全局变量的修改就不会影响到i的改变。
return function (i) {
setTimeout(() => {
console.log(i);
}, i*1000);
};
}
3. js闭包的理解
闭包(closure)就是能够读取其他函数内部变量的函数。在javascript中,只有函数内部的子函数才能读取局部变量,所以闭包可以理解成 “定义在一个函数内部的函数”。在本质上,闭包是将函数内部和函数外部连接起来的桥梁。(闭包的最典型的应用是实现回调函数(callback) )。
1.让外部访问函数内部变量变成可能
2.变量会常驻在内存中
3.可以避免使用全局变量,防止全局变量污染;
- 闭包的第一个用途是使我们在函数外部能够访问到函数内部的变量。通过使用闭包,可以通过在外部调用闭包函数,从而在外部访问到函数内部的变量,可以使用这种方法来创建私有变量。
- 闭包的另一个用途是使已经运行结束的函数上下文中的变量对象继续留在内存中,因为闭包函数保留了这个变量对象的引用,所以这个变量对象不会被回收。
比如,函数 A 内部有一个函数 B,函数 B 可以访问到函数 A 中的变量,那么函数 B 就是闭包。
function A() {
let a = 1
window.B = function () {
console.log(a)
}
}
A()
B() // 1
在 JS 中,闭包存在的意义就是让我们可以间接访问函数内部的变量。经典面试题:循环中使用闭包解决 var 定义函数的问题
- 第一种是使用闭包的方式
for (var i = 1; i <= 5; i++) {;
(function(j) {
setTimeout(function timer() {
console.log(j)
}, j * 1000)
})(i)
}
- 第二种就是使用
setTimeout的第三个参数,这个参数会被当成timer函数的参数传入。
for (var i = 1; i <= 5; i++) {
setTimeout(
function timer(j) {
console.log(j)
}, i * 1000, i)
}
- 第三种就是使用
let定义i了来解决问题了,这个也是最为推荐的方式
for (let i = 1; i <= 5; i++) {
setTimeout(function timer() {
console.log(i)
}, i * 1000)
}
思考题
代码片段一。
var name = "The Window";
var object = {
name : "My Object",
getNameFunc : function(){
return function(){
return this.name;
};
}
};
alert(object.getNameFunc()()); //The Window
关键字"this"的含义取决于其所在的执行环境。
在这种情况下,"this"关键字在函数内部指向调用它的对象。在 object.getNameFunc()() 这一行代码中,outer函数返回的是 inner 函数的引用,并且inner函数被作为一个函数立即调用了。由于 getNameFunc 函数是作为 object 对象的属性调用的,因此 “this” 关键字在 inner 函数内部将指向 object 对象。
但是,需要注意的是,在inner函数内部的返回函数中,该函数被视为全局上下文的一部分,因此在这个环境中,“this” 关键字将指向全局对象(通常是浏览器中的 “window” 对象)。因此,outer函数通过返回 inner 函数的引用时,返回的函数内部的 “this” 关键字将指向全局对象 “window”,而不是原始的 object 对象。
因此,当执行 alert(object.getNameFunc()()); 时,弹出的警告框中将显示字符串 “The Window”,而不是 “My Object”。
代码片段二。
var name = "The Window";
var object = {
name : "My Object",
getNameFunc : function(){
var that = this;
return function(){
return that.name;
};
}
};
alert(object.getNameFunc()()); //My Object
通过使用一个辅助变量 “that”,可以解决上一个问题中的 “this” 关键字的问题。
在 object.getNameFunc() 函数中,将 “this” 关键字存储在变量 “that” 中。由于上下文的闭包,返回的函数可以访问到外部函数中的变量 “that”。所以,无论返回函数在什么环境中被调用,它将始终通过 “that” 变量来访问原始的 object 对象。
因此,当执行 alert(object.getNameFunc()()); 时,弹出的警告框中将显示字符串 “My Object”,而不是 “The Window”。这是因为在返回函数的上下文中,“that” 变量引用了包含它的 object 对象,而不是全局对象。
4. js中的this指向
一、全局环境下的this指向
在全局作用域下,this始终指向全局对象window,无论是否是严格模式!

congsole.log()完整的写法是window.console.log(),window可以省略,window调用了console.log()方法,所以此时this指向window。
二、函数内的this
- 普通函数内的this分为两种情况,严格模式下和非严格模式下。
1. 严格模式下:

直接test()调用函数,this指向undefined,window.test()调用函数this指向window。因此,在严格模式下, 我们对代码的的调用必须严格的写出被调用的函数的对象,不可以有省略或者说简写。
2. 非严格模式下:

非严格模式下,通过test()和window.test()调用函数对象,this都指向window。
三、对象中的this
对象内部方法的this指向调用这些方法的对象,也就是谁调用就指向谁。
1. 一层对象:

调用obj.skill()方法,返回值为蒙犽,说明此时this指向obj。
2. 二层对象:

调用skill2()方法的顺序为,obj.obj2.skill2() ,返回值为鲁班,说明skill2()方法中的this指向obj2。
总结:
- 函数的定义位置不影响其this指向,this指向只和调用函数的对象有关。
- 多层嵌套的对象,内部方法的this指向离被调用函数最近的对象。
四、箭头函数中的this
箭头函数:this指向于函数作用域所用的对象。
- 箭头函数的重要特征:箭头函数中没有this和arguments,是真的没有!
- 箭头函数没有自己的this指向,它会捕获自己定义所处的外层执行环境,并且继承这个this值,指向当前定义时所在的对象。箭头函数的this指向在被定义的时候就确定了,之后永远都不会改变。即使使用
call()、apply()、bind()等方法改变this指向也不可以。
下面是普通函数的列子:
var name = 'window'; // 其实是window.name = 'window'
var A = {
name: 'A',
sayHello: function(){
console.log(this.name)
}
}
A.sayHello();// 输出A
var B = {
name: 'B'
}
A.sayHello.call(B);//输出B
A.sayHello.call();//不传参数指向全局window对象,输出window.name也就是window
从上面可以看到,sayHello这个方法是定义在A对象中的,当当我们使用call方法,把其指向B对象,最后输出了B;可以得出,sayHello的this只跟使用时的对象有关。
改造一下:
var name = 'window';
var A = {
name: 'A',
sayHello: () => {
console.log(this.name)
}
}
A.sayHello();// 还是以为输出A ? 错啦,其实输出的是window
我相信在这里,大部分同学都会出错,以为sayHello是绑定在A上的,但其实它绑定在window上的,那到底是为什么呢?
一开始,我重点标注了“该函数所在的作用域指向的对象”,作用域是指函数内部,这里的箭头函数,也就是sayHello,所在的作用域其实是最外层的js环境,因为没有其他函数包裹;然后最外层的js环境指向的对象是winodw对象,所以这里的this指向的是window对象。
那如何改造成永远绑定A呢:
var name = 'window';
var A = {
name: 'A',
sayHello: function(){
var s = () => console.log(this.name)
return s//返回箭头函数s
}
}
var sayHello = A.sayHello();
sayHello();// 输出A
var B = {
name: 'B';
}
sayHello.call(B); //还是A
sayHello.call(); //还是A
OK,这样就做到了永远指向A对象了,我们再根据“该函数所在的作用域指向的对象”来分析一下:
- 该函数所在的作用域:箭头函数s 所在的作用域是sayHello,因为sayHello是一个函数。
- 作用域指向的对象:A.sayHello指向的对象是A。
所以箭头函数s 中this就是指向A啦 ~~
z、改变this指向的方法
1. call()
call(a, b, c)方法接收三个参数,第一个是this指向,第二个,三个是传递给函数的实参,可以是数字,字符串,数组等类型的数据类型都可以。
示例:
//定义函数
function fn(n1,n2){
console.log(this);
console.log(n1,n2)
}
//调用call()方法
fn.call();//=>this:window;
let obj = {fn:fn};
fn.call(obj);//=>this:obj;n1,n2:undefined
fn.call(1,2);//=>this: 1;n1=2,n2=undefined;
fn.call(obj,1,2);//=>this: obj;n1=1,n2=2;
//Call方法的几个特殊属性
//非严格模式下
fn.call(undefined);//this=>window
fn.call(null);//this=>window
//严格模式下
"use strict"
fn.call(undefined);//this=>undefined
fn.call(null);//this=>null
1234567891011121314151617181920
2. apply()
apply(a, [b])和call基本上一致,唯一区别在于传参方式,apply把需要传递给fn()的参数放到一个数组(或者类数组)中传递进去,虽然写的是一个数组,但是也相当于给fn()一个个的传递。
//call()的传参方式
fn.call(obj, 1, 2);
//apply()的传参方式
fn.apply(obj, [1, 2]);
1234
示例:
//apply方法的使用和call方法基本相同,唯一的区别是,apply方法传参要求是数组类型的,数组内可以任意形式的数据
function fn (n1,n2){
console.log(this);
console.log(n1,n2)
console.log(arguments)
}
let obj = {fn:fn};
//调用apply()方法
fn.applay(abj,[1,2]);
fn.applay(abj,1,2);//报错
fn.applay(abj,[11,'apply',{a:123}]);//注意第二个参数必须是数组,否则会报错
123456789101112
3. bind()
-
bind(a, b, c):语法和call一模一样,区别在于立即执行还是等待执行,bind不兼容IE6~8 -
bind与call的唯一区别就是call直接改变函数test的指向,而bind是生成了一个新函数
test2(),该函数改变了指向。
//call()方法:改变fn中的this,并且把fn立即执行
fn.call(obj, 1, 2);
//bind()方法:改变fn中的this,fn并不执行
fn.bind(obj, 1, 2);
1234
示例:
//bind和call方法调用形式类似,但是原理完全不同
fn.call(obj,10,20);//=>fn先执行,将fn内的this指向obj,并且把参数10,20传递给fn
fn.bind(obj,10,20)//bind是先将fn中的this指向obj,并且将参数10,20预先传递给fn,但是此时的fn并没有被执行,只有fn执行时this指向和传递参数才有作用
fn.bind(obj,10,20);//=>不会有任何输出
fn.bind(obj,10,20)();//=>调用后才会有输出
//=>需求:点击box这个盒子的时候,需要执行fn,并且让fn中的this指向obj
oBox.onclick=fn; //=>点击的时候执行了fn,但此时fn中的this是oBox
oBox.onclick=fn.call(opp); //=>绑定事件的时候就已经把fn立即执行了(call本身就是立即执行函数),然后把fn执行的返回值绑定给事件
oBox.onclick=fn.bind(opp);
//=>fn.bind(opp):fn调取Function.prototype上的bind方法,执行这个/*
* function(){
* fn.call(opp);
* }
*/
oBox.onclick=function(){
//=>this:oBox
fn.call(opp);
}
5. js判断数据类型的方法
我们先来了解一下JS中数据类型有哪些
基本数据类型(值类型):String、Number、boolean、null、undefined、symbol(es6新增的)
引用数据类型 (引用类型):object。包含 Function、Array、Date、RegExp、Error等都是属于 Object 类型 。
1.typeof
通常用来判断基本数据类型,它返回表示数据类型的字符串(返回结果只能包括number,boolean,string,function,undefined,object); *注意,使用typeof来判断null和引用类型 返回的结果都是 'object'
可以使用typeof判断变量是否存在(如if(typeof a!="undefined"){...});
typeof 1 //number
typeof 'a' //string
typeof true //boolean
typeof undefined //undefined
typeof null //object
typeof {} //object
typeof [1,2,3] //object
function Fn(){}
typeof new Fn() //object
typeof new Array() //object
2.instanceof
使用instanceof,如:a instanceof A 根据instanceof的定义:判断参照对象(大写字母A)的prototype属性所指向的对象是否在被行测对象a的原型链上,instanceof 只能用来判断两个对象是否属于实例关系,而不能判断一个对象实例具体属于哪种类型,例如:
function A(name,age){
this.name = name;
this.age = age;
}
a = new A('张三',18);
console.log(a instanceof A) //true
obj = new Object()//创建一个空对象obj
//或者通过字面量来创建:
obj = {}
console.log(obj instanceof Object); // true
arr = new Array() //创建一个空数组arr 或arr = []
console.log(arr instanceof Array ); // true
date = new Date()
console.log(date instanceof Date ); // true
// 注意:instanceof后面一定要是对象类型,instanceof前面相当于它的实例对象,
// 后面的对象类型大小写不能写错,该方法试用一些条件选择或分支
但是这种方式判断有个弊端:对于number,string,boolean这三种基本数据类型,
只有通过构造函数定义比如:let num =new Number(1);这样定义才能检测出。
let num = 1; 这样定义是检测不出来的
简单实现instanceof:
function my_instanceof(L, R) {
const O = R.prototype;
if (L === null) {
return false;
}
L = L.__proto__;
while (true) {
if (L === null) {
return false;
}
if (L === O) {
return true;
}
L = L.__proto__;
}
}
3.constructor
- 针对于instanceof的弊端,我们使用constructor检测,constructor是原型对象的属性指向构造函数。
console.log('数据类型判断 - constructor');
let num = 23;
let date = new Date();
let str = "biu~";
let reg = new RegExp();
let bool = true;
let fn = function () {
console.log(886);
};
let udf = undefined;
let nul = null;
let array = [1, 2, 3];
console.log(num.constructor); // [Function: Number]
console.log(date.constructor); // [Function: Date]
console.log(str.constructor); // [Function: String]
console.log(bool.constructor); // [Function: Boolean]
console.log(fn.constructor); // [Function: Function]
console.log(reg.constructor); // [Function: RegExp]
console.log(array.constructor); // [Function: Array]
这种方式解决了instanceof的弊端,可以检测出除了undefined和null的9种类型(因为它两没有原生构造函数)
console.log(udf.constructor);//Cannot read property "constructor" of undefined
console.log(nul.constructor);//Cannot read property "constructor" of null
4.toString.call()
在《你不知道的javaScript》(中卷)中讲到:所有typeof返回值为"object"的对象,都包含一个内部属性[[Class]],我们可以把他看作一个内部的分类,而非传统意义上面向对象的类,这个属性无法直接访问,一般通过Object.prototype.toString(…)来查看。并且对于基本数据类类型null,undefined这样没有原生构造函数,内部的[[Class]]属性值仍然是Null和Undefined
Object.prototype.toString.call();
console.log(toString.call(123)); //[object Number]
console.log(toString.call('123')); //[object String]
console.log(toString.call(undefined)); //[object Undefined]
console.log(toString.call(true)); //[object Boolean]
console.log(toString.call({})); //[object Object]
console.log(toString.call([])); //[object Array]
console.log(toString.call(function(){})); //[object Function]