乐趣区

2019前端最全面试题

项目地址

HTML 问题

HTML5 语义化

什么是语义化?就是用合理、正确的标签来展示内容,比如 h1~h6 定义标题。

好处

  • 易于用户阅读,样式丢失的时候能让页面呈现清晰的结构。
  • 有利于 SEO,搜索引擎根据标签来确定上下文和各个关键字的权重。
  • 方便其他设备解析,如盲人阅读器根据语义渲染网页
  • 有利于开发和维护,语义化更具可读性,代码更好维护,与 CSS3 关系更和谐。

http://www.daqianduan.com/654…

为什么最好把 CSS 的 <link> 标签放在 <head></head> 之间?为什么最好把 JS 的 <script> 标签恰好放在 </body> 之前,有例外情况吗?

<link> 放在 <head>

<link> 标签放在 <head></head> 之间是规范要求的内容。此外,这种做法可以让页面逐步呈现,提高了用户体验。将样式表放在文档底部附近,会使许多浏览器(包括 Internet Explorer)不能逐步呈现页面。一些浏览器会阻止渲染,以避免在页面样式发生变化时,重新绘制页面中的元素。这种做法可以防止呈现给用户空白的页面或没有样式的内容。

<script> 标签恰好放在 </body> 之前

脚本在下载和执行期间会阻止 HTML 解析。把 <script> 标签放在底部,保证 HTML 首先完成解析,将页面尽早呈现给用户。

例外情况是当你的脚本里包含 document.write() 时。但是现在,document.write()不推荐使用。同时,将 <script> 标签放在底部,意味着浏览器不能开始下载脚本,直到整个文档(document)被解析。也许,对此比较好的做法是,<script>使用 defer 属性,放在 <head> 中。

什么是渐进式渲染(progressive rendering)?

渐进式渲染是用于提高网页性能(尤其是提高用户感知的加载速度),以尽快呈现页面的技术。

在以前互联网带宽较小的时期,这种技术更为普遍。如今,移动终端的盛行,而移动网络往往不稳定,渐进式渲染在现代前端开发中仍然有用武之地。

一些举例:

  • 图片懒加载——页面上的图片不会一次性全部加载。当用户滚动页面到图片部分时,JavaScript 将加载并显示图像。
  • 确定显示内容的优先级(分层次渲染)——为了尽快将页面呈现给用户,页面只包含基本的最少量的 CSS、脚本和内容,然后可以使用延迟加载脚本或监听 DOMContentLoaded/load 事件加载其他资源和内容。
  • 异步加载 HTML 片段——当页面通过后台渲染时,把 HTML 拆分,通过异步请求,分块发送给浏览器。更多相关细节可以在这里找到。

viewport

Viewport:字面意思为视图窗口,在移动 web 开发中使用。表示将设备浏览器宽度虚拟成一个特定的值(或计算得出),这样利于移动 web 站点跨设备显示效果基本一致。移动版的 Safari 浏览器最新引进了 viewport 这个 meta tag,让网页开发者来控制 viewport 的大小和缩放,其他手机浏览器也基本支持。

在移动端浏览器当中,存在着两种视口,一种是可见视口(也就是我们说的设备大小),另一种是视窗视口(网页的宽度是多少)。
举个例子:如果我们的屏幕是 320 像素 * 480 像素的大小(iPhone4),假设在浏览器中,320 像素的屏幕宽度能够展示 980 像素宽度的内容。那么 320 像素的宽度就是可见视口的宽度,而能够显示的 980 像素的宽度就是视窗视口的宽度。

为了显示更多的内容,大多数的浏览器会把自己的视窗视口扩大,简易的理解,就是让原本 320 像素的屏幕宽度能够容下 980 像素甚至更宽的内容(将网页等比例缩小)。

Viewport 属性值

  • width 设置 layout viewport 的宽度,为一个正整数,或字符串 ”width-device”
  • initial-scale 设置页面的初始缩放值,为一个数字,可以带小数
  • minimum-scale 允许用户的最小缩放值,为一个数字,可以带小数
  • maximum-scale 允许用户的最大缩放值,为一个数字,可以带小数
  • height 设置 layout viewport 的高度,这个属性对我们并不重要,很少使用
  • user-scalable 是否允许用户进行缩放,值为 ”no” 或 ”yes”, no 代表不允许,yes 代表允许这些属性可以同时使用,也可以单独使用或混合使用,多个属性同时使用时用逗号隔开就行了。

Reflow 和 Repaint

Reflow

当涉及到 DOM 节点的布局属性发生变化时,就会重新计算该属性,浏览器会重新描绘相应的元素,此过程叫 Reflow(回流或重排)。

Repaint

当影响 DOM 元素可见性的属性发生变化 (如 color) 时, 浏览器会重新描绘相应的元素, 此过程称为 Repaint(重绘)。因此重排必然会引起重绘。

引起 Repaint 和 Reflow 的一些操作

  • 调整窗口大小
  • 字体大小
  • 样式表变动
  • 元素内容变化,尤其是输入控件
  • CSS 伪类激活,在用户交互过程中发生
  • DOM 操作,DOM 元素增删、修改
  • width, clientWidth, scrollTop 等布局宽高的计算

Repaint 和 Reflow 是不可避免的,只能说对性能的影响减到最小,给出下面几条建议:

  • 避免逐条更改样式。建议集中修改样式,例如操作 className。
  • 避免频繁操作 DOM。创建一个 documentFragment 或 div,在它上面应用所有 DOM 操作,最后添加到文档里。设置 display:none 的元素上操作,最后显示出来。
  • 避免频繁读取元素几何属性(例如 scrollTop)。绝对定位具有复杂动画的元素。
  • 绝对定位使它脱离文档流,避免引起父元素及后续元素大量的回流

https://harttle.land/2015/08/…

http://www.blueidea.com/tech/…

img 中的 alt 和元素的 title 属性作用

  • img 的 alt 属性

如果无法显示图像,浏览器将显示 alt 指定的内容

  • 元素 title 属性

在鼠标移到元素上时显示 title 的内容

href 和 src 区别

  • href

href 标识超文本引用,用在 link 和 a 等元素上,href 是引用和页面关联,是在当前元素和引用资源之间建立联系

若在文档中添加 href,浏览器会识别该文档为 CSS 文件,就会并行下载资源并且不会停止对当前文档的处理。这也是为什么建议使用 link 方式加载 CSS,而不是使用 @import 方式。

  • src

src 表示引用资源,替换当前元素,用在 img,script,iframe 上,src 是页面内容不可缺少的一部分。

当浏览器解析到 src,会暂停其他资源的下载和处理,直到将该资源加载、编译、执行完毕,图片和框架等也如此,类似于将所指向资源应用到当前内容。这也是为什么建议把 js 脚本放在底部而不是头部的原因。

https://blog.csdn.net/lhjueji…

浏览器的渲染过程

  1. 解析 HTML 生成 DOM 树。
  2. 解析 CSS 生成 CSSOM 规则树。
  3. 将 DOM 树与 CSSOM 规则树合并在一起生成渲染树。
  4. 遍历渲染树开始布局,计算每个节点的位置大小信息。
  5. 将渲染树每个节点绘制到屏幕。

https://baijiahao.baidu.com/s…

为何会出现浏览器兼容问题

  • 同一产品,版本越老 bug 越多
  • 同一产品,版本越新,功能越多
  • 不同产品,不同标准,不同实现方式

处理兼容问题的思路

  1. 要不要做
  • 产品的角度(产品的受众、受众的浏览器比例、效果优先还是基本功能优先)
  • 成本的角度 (有无必要做某件事)

2. 做到什么程度

  • 让哪些浏览器支持哪些效果

3.. 如何做

  • 根据兼容需求选择技术框架 / 库(jquery)
  • 根据兼容需求选择兼容工具(html5shiv.js、respond.js、css reset、normalize.css、Modernizr)
  • 条件注释、CSS Hack、js 能力检测做一些修补
  • 渐进增强(progressive enhancement): 针对低版本浏览器进行构建页面,保证最基本的功能,然后再针对高级浏览器进行效果、交互等改进和追加功能达到更好的用户体验
  • 优雅降级 (graceful degradation): 一开始就构建完整的功能,然后再针对低版本浏览器进行兼容。

https://github.com/jirengu/fr…

doctype 有什么用

doctype 是一种标准通用标记语言的文档类型声明,目的是告诉标准通用标记语言解析器要使用什么样的文档类型定义(DTD)来解析文档。

<!DOCTYPE> 声明是用来指示 web 浏览器关于页面使用哪个 HTML 版本进行编写的指令。

<!DOCTYPE> 声明必须是 HTML 文档的第一行,位于 html 标签之前。

浏览器本身分为两种模式,一种是标准模式,一种是怪异模式,浏览器通过 doctype 来区分这两种模式,doctype 在 html 中的作用就是触发浏览器的标准模式,如果 html 中省略了 doctype,浏览器就会进入到 Quirks 模式的怪异状态,在这种模式下,有些样式会和标准模式存在差异,而 html 标准和 dom 标准值规定了标准模式下的行为,没有对怪异模式做出规定,因此不同浏览器在怪异模式下的处理也是不同的,所以一定要在 html 开头使用 doctype。

行内元素和块级元素有哪些

行内元素

一个行内元素只占据它对应标签的边框所包含的空间

一般情况下,行内元素只能包含数据和其他行内元素

b, big, i, small, tt
abbr, acronym, cite, code, dfn, em, kbd, strong, samp, var
a, bdo, br, img, map, object, q, script, span, sub, sup
button, input, label, select, textarea

块级元素

占据一整行,高度、行高、内边距和外边距都可以改变,可以容纳块级标签和其他行内标签

header,form,ul,ol,table,article,div,hr,aside,figure,canvas,video,audio,footer

iframe 框架有那些优缺点

优点:

  • iframe 能够原封不动的把嵌入的网页展现出来。
  • 如果有多个网页引用 iframe,那么你只需要修改 iframe 的内容,就可以实现调用的每一个页面内容的更改,方便快捷。
  • 网页如果为了统一风格,头部和版本都是一样的,就可以写成一个页面,用 iframe 来嵌套,可以增加代码的可重用。
  • 如果遇到加载缓慢的第三方内容如图标和广告,这些问题可以由 iframe 来解决。

缺点:

  • 搜索引擎的爬虫程序无法解读这种页面
  • 框架结构中出现各种滚动条
  • 使用框架结构时,保证设置正确的导航链接。
  • iframe 页面会增加服务器的 http 请求

label 标签有什么作用

label 标签通常是写在表单内,它关联一个控件,使用 label 可以实现点击文字选取对应的控件。

<input type="checkbox" id="test">
<label for="test" >test</label>

HTML5 的 form 如何关闭自动完成功能

将不想要自动完成的 forminput 设置为 autocomplete=off

MDN

DOM 和 BOM 有什么区别

  • DOM

Document Object Model,文档对象模型

DOM 是为了操作文档出现的 API,document 是其的一个对象

DOM 和文档有关,这里的文档指的是网页,也就是 html 文档。DOM 和浏览器无关,他关注的是网页本身的内容。

  • BOM

Browser Object Model,浏览器对象模型

BOM 是为了操作浏览器出现的 API,window 是其的一个对象

window 对象既为 javascript 访问浏览器提供 API,同时在 ECMAScript 中充当 Global 对象

CSS 问题

CSS 选择器的优先级是如何计算的?

浏览器通过优先级规则,判断元素展示哪些样式。优先级通过 4 个维度指标确定,我们假定以 a、b、c、d 命名,分别代表以下含义:

  1. a表示是否使用内联样式(inline style)。如果使用,a为 1,否则为 0。
  2. b表示 ID 选择器的数量。
  3. c表示类选择器、属性选择器和伪类选择器数量之和。
  4. d表示标签(类型)选择器和伪元素选择器之和。

优先级的结果并非通过以上四个值生成一个得分,而是每个值分开比较。a、b、c、d权重从左到右,依次减小。判断优先级时,从左到右,一一比较,直到比较出最大值,即可停止。所以,如果 b 的值不同,那么 cd不管多大,都不会对结果产生影响。比如 0,1,0,0 的优先级高于0,0,10,10

当出现优先级相等的情况时,最晚出现的样式规则会被采纳。如果你在样式表里写了相同的规则(无论是在该文件内部还是其它样式文件中),那么最后出现的(在文件底部的)样式优先级更高,因此会被采纳。

在写样式时,我会使用较低的优先级,这样这些样式可以轻易地覆盖掉。尤其对写 UI 组件的时候更为重要,这样使用者就不需要通过非常复杂的优先级规则或使用 !important 的方式,去覆盖组件的样式了。

参考
  • https://www.smashingmagazine….
  • https://www.sitepoint.com/web…

重置(resetting)CSS 和 标准化(normalizing)CSS 的区别是什么?你会选择哪种方式,为什么?

  • 重置(Resetting):重置意味着除去所有的浏览器默认样式。对于页面所有的元素,像 marginpaddingfont-size 这些样式全部置成一样。你将必须重新定义各种元素的样式。
  • 标准化(Normalizing):标准化没有去掉所有的默认样式,而是保留了有用的一部分,同时还纠正了一些常见错误。

当需要实现非常个性化的网页设计时,我会选择重置的方式,因为我要写很多自定义的样式以满足设计需求,这时候就不再需要标准化的默认样式了。

参考
  • https://stackoverflow.com/que…

请阐述 Float 定位的工作原理。

浮动(float)是 CSS 定位属性。浮动元素从网页的正常流动中移出,但是保持了部分的流动性,会影响其他元素的定位(比如文字会围绕着浮动元素)。这一点与绝对定位不同,绝对定位的元素完全从文档流中脱离。

CSS 的 clear 属性通过使用leftrightboth,让该元素向下移动(清除浮动)到浮动元素下面。

如果父元素只包含浮动元素,那么该父元素的高度将塌缩为 0。我们可以通过清除(clear)从浮动元素后到父元素关闭前之间的浮动来修复这个问题。

有一种 hack 的方法,是自定义一个 .clearfix 类,利用伪元素选择器 ::after 清除浮动。另外还有一些方法,比如添加空的 <div></div> 和设置浮动元素父元素的 overflow 属性。与这些方法不同的是,clearfix方法,只需要给父元素添加一个类,定义如下:

.clearfix::after {
  content: '';
  display: block;
  clear: both;
}

值得一提的是,把父元素属性设置为 overflow: autooverflow: hidden,会使其内部的子元素形成块格式化上下文(Block Formatting Context),并且父元素会扩张自己,使其能够包围它的子元素。

参考
  • https://css-tricks.com/all-ab…

请阐述 z-index 属性,并说明如何形成层叠上下文(stacking context)。

CSS 中的 z-index 属性控制重叠元素的垂直叠加顺序。z-index只能影响 position 值不是 static 的元素。

没有定义 z-index 的值时,元素按照它们出现在 DOM 中的顺序堆叠(层级越低,出现位置越靠上)。非静态定位的元素(及其子元素)将始终覆盖静态定位(static)的元素,而不管 HTML 层次结构如何。

层叠上下文是包含一组图层的元素。在一组层叠上下文中,其子元素的 z-index 值是相对于该父元素而不是 document root 设置的。每个层叠上下文完全独立于它的兄弟元素。如果元素 B 位于元素 A 之上,则即使元素 A 的子元素 C 具有比元素 B 更高的 z-index 值,元素 C 也永远不会在元素 B 之上.

每个层叠上下文是自包含的:当元素的内容发生层叠后,整个该元素将会在父层叠上下文中按顺序进行层叠。少数 CSS 属性会触发一个新的层叠上下文,例如 opacity 小于 1,filter不是 nonetransform 不是none

参考
  • https://css-tricks.com/almana…
  • https://philipwalton.com/arti…
  • https://developer.mozilla.org…

请阐述块格式化上下文(Block Formatting Context)及其工作原理。

块格式上下文(BFC)是 Web 页面的可视化 CSS 渲染的部分,是块级盒布局发生的区域,也是浮动元素与其他元素交互的区域。

一个 HTML 盒(Box)满足以下任意一条,会创建块格式化上下文:

  • float的值不是none.
  • position的值不是 staticrelative.
  • display的值是table-celltable-captioninline-blockflex、或inline-flex
  • overflow的值不是visible

在 BFC 中,每个盒的左外边缘都与其包含的块的左边缘相接。

两个相邻的块级盒在垂直方向上的边距会发生合并(collapse)。更多内容请参考边距合并(margin collapsing)。

参考
  • https://developer.mozilla.org…
  • https://www.sitepoint.com/und…

有哪些清除浮动的技术,都适用哪些情况?

  • div 方法:<div style="clear:both;"></div>
  • Clearfix 方法:上文使用 .clearfix 类已经提到。
  • overflow: autooverflow: hidden 方法:上文已经提到。

在大型项目中,我会使用 Clearfix 方法,在需要的地方使用 .clearfix。设置overflow: hidden 的方法可能使其子元素显示不完整,当子元素的高度大于父元素时。

请解释什么是雪碧图(css sprites),以及如何实现?

雪碧图是把多张图片整合到一张上的图片。它被运用在众多使用了很多小图标的网站上(Gmail 在使用)。实现方法:

  1. 使用生成器将多张图片打包成一张雪碧图,并为其生成合适的 CSS。
  2. 每张图片都有相应的 CSS 类,该类定义了 background-imagebackground-positionbackground-size属性。
  3. 使用图片时,将相应的类添加到你的元素中。

好处:

  • 减少加载多张图片的 HTTP 请求数(一张雪碧图只需要一个请求)。但是对于 HTTP2 而言,加载多张图片不再是问题。
  • 提前加载资源,防止在需要时才在开始下载引发的问题,比如只出现在 :hover 伪类中的图片,不会出现闪烁。
参考
  • https://css-tricks.com/css-sp…

如何解决不同浏览器的样式兼容性问题?

  • 在确定问题原因和有问题的浏览器后,使用单独的样式表,仅供出现问题的浏览器加载。这种方法需要使用服务器端渲染。
  • 使用已经处理好此类问题的库,比如 Bootstrap。
  • 使用 autoprefixer 自动生成 CSS 属性前缀。
  • 使用 Reset CSS 或 Normalize.css。

如何为功能受限的浏览器提供页面?使用什么样的技术和流程?

  • 优雅的降级:为现代浏览器构建应用,同时确保它在旧版浏览器中正常运行。
  • Progressive enhancement – The practice of building an application for a base level of user experience, but adding functional enhancements when a browser supports it.
  • 渐进式增强:构建基于用户体验的应用,但在浏览器支持时添加新增功能。
  • 利用 caniuse.com 检查特性支持。
  • 使用 autoprefixer 自动生成 CSS 属性前缀。
  • 使用 Modernizr 进行特性检测。

有什么不同的方式可以隐藏内容(使其仅适用于屏幕阅读器)?

这些方法与可访问性(a11y)有关。

  • visibility: hidden:元素仍然在页面流中,并占用空间。
  • width: 0; height: 0:使元素不占用屏幕上的任何空间,导致不显示它。
  • position: absolute; left: -99999px:将它置于屏幕之外。
  • text-indent: -9999px:这只适用于 block 元素中的文本。
  • Metadata:例如通过使用 Schema.org,RDF 和 JSON-LD。
  • WAI-ARIA:如何增加网页可访问性的 W3C 技术规范。

即使 WAI-ARIA 是理想的解决方案,我也会采用绝对定位方法,因为它具有最少的注意事项,适用于大多数元素,而且使用起来非常简单。

参考
  • https://www.w3.org/TR/wai-ari…
  • https://developer.mozilla.org…
  • http://a11yproject.com/

除了screen,你还能说出一个 @media 属性的例子吗?

  • all

适用于所有设备。

  • print

为了加载合适的文档到当前使用的可视窗口. 需要提前咨询 paged media(媒体屏幕尺寸), 以满足个别设备网页尺寸不匹配等问题。

  • screen

主要适用于彩色的电脑屏幕

  • speech

解析 speech 这个合成器. 注意: CSS2 已经有一个相似的媒体类型叫 aural.

https://developer.mozilla.org…

编写高效的 CSS 应该注意什么?

首先,浏览器从最右边的选择器,即关键选择器(key selector),向左依次匹配。根据关键选择器,浏览器从 DOM 中筛选出元素,然后向上遍历被选元素的父元素,判断是否匹配。选择器匹配语句链越短,浏览器的匹配速度越快。避免使用标签和通用选择器作为关键选择器,因为它们会匹配大量的元素,浏览器必须要进行大量的工作,去判断这些元素的父元素们是否匹配。

BEM (Block Element Modifier) methodology recommends that everything has a single class, and, where you need hierarchy, that gets baked into the name of the class as well, this naturally makes the selector efficient and easy to override.
BEM (Block Element Modifier)原则上建议为独立的 CSS 类命名,并且在需要层级关系时,将关系也体现在命名中,这自然会使选择器高效且易于覆盖。

搞清楚哪些 CSS 属性会触发重新布局(reflow)、重绘(repaint)和合成(compositing)。在写样式时,避免触发重新布局的可能。

参考
  • https://developers.google.com…
  • https://csstriggers.com/

使用 CSS 预处理的优缺点分别是什么?

优点:

  • 提高 CSS 可维护性。
  • 易于编写嵌套选择器。
  • 引入变量,增添主题功能。可以在不同的项目中共享主题文件。
  • 通过混合(Mixins)生成重复的 CSS。
  • Splitting your code into multiple files. CSS files can be split up too but doing so will require a HTTP request to download each CSS file.
  • 将代码分割成多个文件。不进行预处理的 CSS,虽然也可以分割成多个文件,但需要建立多个 HTTP 请求加载这些文件。

缺点:

  • 需要预处理工具。
  • 重新编译的时间可能会很慢。

对于你使用过的 CSS 预处理,说说喜欢和不喜欢的地方?

喜欢:

  • 绝大部分优点上题以及提过。
  • Less 用 JavaScript 实现,与 NodeJS 高度结合。

Dislikes:

  • 我通过 node-sass 使用 Sass,它用 C ++ 编写的 LibSass 绑定。在 Node 版本切换时,我必须经常重新编译。
  • Less 中,变量名称以 @ 作为前缀,容易与 CSS 关键字混淆,如 @media@import@font-face

如何实现一个使用非标准字体的网页设计?

使用 @font-face 并为不同的 font-weight 定义font-family

解释浏览器如何确定哪些元素与 CSS 选择器匹配。

这部分与上面关于编写高效的 CSS 有关。浏览器从最右边的选择器(关键选择器)根据关键选择器,浏览器从 DOM 中筛选出元素,然后向上遍历被选元素的父元素,判断是否匹配。选择器匹配语句链越短,浏览器的匹配速度越快。

例如,对于形如 p span 的选择器,浏览器首先找到所有 <span> 元素,并遍历它的父元素直到根元素以找到 <p> 元素。对于特定的<span>,只要找到一个<p>,就知道 '<span>` 已经匹配并停止继续匹配。

参考
  • https://stackoverflow.com/que…

描述伪元素及其用途。

CSS 伪元素是添加到选择器的关键字,去选择元素的特定部分。它们可以用于装饰(:first-line:first-letter)或将元素添加到标记中(与 content:… 组合),而不必修改标记(:before:after)。

  • :first-line:first-letter 可以用来修饰文字。
  • 上面提到的 .clearfix 方法中,使用 clear: both 来添加不占空间的元素。
  • 使用 :beforeafter展示提示中的三角箭头。鼓励关注点分离,因为三角被视为样式的一部分,而不是真正的 DOM。如果不使用额外的 HTML 元素,只用 CSS 样式绘制三角形是不太可能的。
参考
  • https://css-tricks.com/almana…

说说你对盒模型的理解,以及如何告知浏览器使用不同的盒模型渲染布局。

CSS 盒模型描述了以文档树中的元素而生成的矩形框,并根据排版模式进行布局。每个盒子都有一个内容区域(例如文本,图像等)以及周围可选的 paddingbordermargin区域。

CSS 盒模型负责计算:

  • 块级元素占用多少空间。
  • 边框是否重叠,边距是否合并。
  • 盒子的尺寸。

盒模型有以下规则:

  • 块级元素的大小由 widthheightpaddingbordermargin决定。
  • 如果没有指定height,则块级元素的高度等于其包含子元素的内容高度加上padding(除非有浮动元素,请参阅下文)。
  • 如果没有指定width,则非浮动块级元素的宽度等于其父元素的宽度减去父元素的padding
  • 元素的 height 是由内容的 height 来计算的。
  • 元素的 width 是由内容的 width 来计算的。
  • 默认情况下,paddingborder 不是元素 widthheight的组成部分。
参考
  • https://www.smashingmagazine….

* {box-sizing: border-box;}会产生怎样的效果?

  • 元素默认应用了box-sizing: content-box,元素的宽高只会决定内容(content)的大小。
  • box-sizing: border-box改变计算元素 widthheight的方式,borderpadding 的大小也将计算在内。
  • 元素的 height = 内容(content)的高度 + 垂直方向的padding + 垂直方向border 的宽度
  • 元素的 width = 内容(content)的宽度 + 水平方向的padding + 水平方向border 的宽度

display的属性值都有哪些?

  • none, block, inline, inline-block, table, table-row, table-cell, list-item.

inlineinline-block 有什么区别?

我把 block 也加入其中,为了获得更好的比较。

block inline-block inline
大小 填充其父容器的宽度。 取决于内容。 取决于内容。
定位 从新的一行开始,并且不允许旁边有 HTML 元素(除非是float 与其他内容一起流动,并允许旁边有其他元素。 与其他内容一起流动,并允许旁边有其他元素。
能否设置 widthheight 不能。设置会被忽略。
可以使用 vertical-align 对齐 不可以 可以 可以
边距(margin)和填充(padding) 各个方向都存在 各个方向都存在 只有水平方向存在。垂直方向会被忽略。尽管 borderpaddingcontent 周围,但垂直方向上的空间取决于 ’line-height’
浮动(float) 就像一个 block 元素,可以设置垂直边距和填充。

relativefixedabsolutestatic 四种定位有什么区别?

经过定位的元素,其 position 属性值必然是 relativeabsolutefixedsticky

  • static:默认定位属性值。该关键字指定元素使用正常的布局行为,即元素在文档常规流中当前的布局位置。此时 top, right, bottom, left 和 z-index 属性无效。
  • relative:该关键字下,元素先放置在未添加定位时的位置,再在不改变页面布局的前提下调整元素位置(因此会在此元素未添加定位时所在位置留下空白)。
  • absolute:不为元素预留空间,通过指定元素相对于最近的非 static 定位祖先元素的偏移,来确定元素位置。绝对定位的元素可以设置外边距(margins),且不会与其他边距合并。
  • fixed:不为元素预留空间,而是通过指定元素相对于屏幕视口(viewport)的位置来指定元素位置。元素的位置在屏幕滚动时不会改变。打印时,元素会出现在的每页的固定位置。fixed 属性会创建新的层叠上下文。当元素祖先的 transform 属性非 none 时,容器由视口改为该祖先。
  • sticky:盒位置根据正常流计算(这称为正常流动中的位置),然后相对于该元素在流中的 flow root(BFC)和 containing block(最近的块级祖先元素)定位。在所有情况下(即便被定位元素为 table 时),该元素定位均不对后续元素造成影响。当元素 B 被粘性定位时,后续元素的位置仍按照 B 未定位时的位置来确定。position: stickytable 元素的效果与 position: relative 相同。
参考
  • https://developer.mozilla.org…

你使用过哪些现有的 CSS 框架?你是如何改进它们的?

  • Bootstrap:更新周期缓慢。Bootstrap 4 已经处于 alpha 版本将近两年了。添加了在页面中广泛使用的微调按钮组件。
  • Semantic UI:源代码结构使得自定义主题很难理解。非常规主题系统的使用体验很差。外部库的路径需要硬编码(hard code)配置。变量重新赋值没有 Bootstrap 设计得好。
  • Bulma:需要很多非语义的类和标记,显得很多余。不向后兼容,以至于升级版本后,会破坏应用的正常运行。

你了解 CSS Flex 和 Grid 吗?

Flex 主要用于一维布局,而 Grid 则用于二维布局。

Flex

flex 容器中存在两条轴,横轴和纵轴,容器中的每个单元称为 flex item。

在容器上可以设置 6 个属性:

  • flex-direction
  • flex-wrap
  • flex-flow
  • justify-content
  • align-items
  • align-content

注意:当设置 flex 布局之后,子元素的 float、clear、vertical-align 的属性将会失效。

Flex 项目属性

有六种属性可运用在 item 项目上:

  1. order
  2. flex-basis
  3. flex-grow
  4. flex-shrink
  5. flex
  6. align-self

Grid

CSS 网格布局用于将页面分割成数个主要区域,或者用来定义组件内部元素间大小、位置和图层之间的关系。

像表格一样,网格布局让我们能够按行或列来对齐元素。但是,使用 CSS 网格可能还是比 CSS 表格更容易布局。例如,网格容器的子元素可以自己定位,以便它们像 CSS 定位的元素一样,真正的有重叠和层次。

响应式设计与自适应设计有何不同?

响应式设计和自适应设计都以提高不同设备间的用户体验为目标,根据视窗大小、分辨率、使用环境和控制方式等参数进行优化调整。

响应式设计的适应性原则:网站应该凭借一份代码,在各种设备上都有良好的显示和使用效果。响应式网站通过使用媒体查询,自适应栅格和响应式图片,基于多种因素进行变化,创造出优良的用户体验。就像一个球通过膨胀和收缩,来适应不同大小的篮圈。

自适应设计更像是渐进式增强的现代解释。与响应式设计单一地去适配不同,自适应设计通过检测设备和其他特征,从早已定义好的一系列视窗大小和其他特性中,选出最恰当的功能和布局。与使用一个球去穿过各种的篮筐不同,自适应设计允许使用多个球,然后根据不同的篮筐大小,去选择最合适的一个。

参考
  • https://developer.mozilla.org…
  • http://mediumwell.com/respons…
  • https://css-tricks.com/the-di…

你有没有使用过视网膜分辨率的图形?当中使用什么技术?

我倾向于使用更高分辨率的图形(显示尺寸的两倍)来处理视网膜显示。更好的方法是使用媒体查询,像@media only screen and (min-device-pixel-ratio: 2) {...},然后改变background-image

对于图标类的图形,我会尽可能使用 svg 和图标字体,因为它们在任何分辨率下,都能被渲染得十分清晰。

还有一种方法是,在检查了 window.devicePixelRatio 的值后,利用 JavaScript 将 <img>src属性修改,用更高分辨率的版本进行替换。

参考
  • https://www.sitepoint.com/css…

什么情况下,用 translate() 而不用绝对定位?什么时候,情况相反。

translate()transform 的一个值。改变 transformopacity不会触发浏览器重新布局(reflow)或重绘(repaint),只会触发复合(compositions)。而改变绝对定位会触发重新布局,进而触发重绘和复合。transform使浏览器为元素创建一个 GPU 图层,但改变绝对定位会使用到 CPU。因此 translate() 更高效,可以缩短平滑动画的绘制时间。

当使用 translate() 时,元素仍然占据其原始空间(有点像position:relative),这与改变绝对定位不同。

参考
  • https://www.paulirish.com/201…

其他答案

  • https://neal.codes/blog/front…
  • https://quizlet.com/28293152/…
  • http://peterdoes.it/2015/12/0…

行内元素、块级元素区别

行内元素:和其他元素都在一行上,高度、行高及外边距和内边距都不可改变,文字图片的宽度不可改变,只能容纳文本或者其他行内元素;其中 img 是行元素

块级元素:总是在新行上开始,高度、行高及外边距和内边距都可控制,可以容纳内敛元素和其他元素;行元素转换为块级元素方式:display:block;

一边固定宽度一边宽度自适应

可以使用 flex 布局 复制下面的 HTML 和 CSS 代码 用浏览器打开可以看到效果

<div class="wrap">
  <div class="div1"></div>
  <div class="div2"></div>
</div>

.wrap {
  display: flex;
  justify-content: space-between;
}
.div1 {min-width: 200px;}
.div2 {
  width: 100%;
  background: #e6e6e6;
}
html,
body,
div {
  height: 100%;
  margin: 0;
}

水平垂直居中的方式

flex

// 父容器
display: flex;
justify-content: center;
align-items: center;

position

// 父容器
position: relative;

// 子容器
position:absolute;
margin:auto;
top:0;
bottom:0;
left:0;
right:0;

position+transform

// 父容器
position: relative;

// 子容器
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);

table-cell

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

html, body {
    height: 100%;
    width: 100%;
    margin: 0;
}
.box {
    display: table;
    height: 100%;
    width: 100%;
}
.content {
    display: table-cell;
    vertical-align: middle;
    text-align: center;
}
.inner {
    background-color: #000;
    display: inline-block;
    width: 200px;
    height: 200px;
}

display:none、visibile:hidden、opacity:0 的区别

是否隐藏 是否在文档中占用空间 是否会触发事件
display: none
visibile: hidden
opacity: 0

CSS 中 link 和 @import 的区别

  • link 属于 HTML 标签,而 @import 是 CSS 提供的
  • 页面被加载的时,link 会同时被加载,而 @import 引用的 CSS 会等到页面被加载完再加载
  • import 只在 IE5 以上才能识别,而 link 是 HTML 标签,无兼容问题
  • link 方式的样式的权重 高于 @import 的权重

如何用 css 实现瀑布流布局

利用 column-count 和 break-inside 这两个 CSS3 属性即可,复制如下代码即可察看效果

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <style>
        body {margin: 0;}
        .waterfall-container {
            /* 分几列 */
            column-count: 2;
            width: 100%;
            /* 列间距 */
            column-gap: 10px;
        }

        .waterfall-item {
            break-inside: avoid;
            width: 100%;
            height: 100px;
            margin-bottom: 10px;
            background: #ddd;
            column-gap: 0;
            text-align: center;
            color: #fff;
            font-size: 40px;
        }
    </style>
</head>
<body>
    <div class="waterfall-container">
        <div class="waterfall-item" style="height: 100px">1</div>
        <div class="waterfall-item" style="height: 300px">2</div>
        <div class="waterfall-item" style="height: 400px">3</div>
        <div class="waterfall-item" style="height: 100px">4</div>
        <div class="waterfall-item" style="height: 300px">5</div>
        <div class="waterfall-item" style="height: 600px">6</div>
        <div class="waterfall-item" style="height: 400px">7</div>
        <div class="waterfall-item" style="height: 300px">8</div>
        <div class="waterfall-item" style="height: 700px">9</div>
        <div class="waterfall-item" style="height: 100px">10</div>
    </div>
</body>
</html>

文本超出部分显示省略号

单行

overflow: hidden;
text-overflow:ellipsis;
white-space: nowrap;

多行

display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 3; // 最多显示几行
overflow: hidden;

利用伪元素画三角

.info-tab {position: relative;}
.info-tab::after {
    content: '';
    border: 4px solid transparent;
    border-top-color: #2c8ac2;
    position: absolute;
    top: 0;
}

已知父级盒子的宽高,子级 img 宽高未知,想让 img 铺满父级盒子且图片不能变形

需要用到 cssobject-fit属性

div {
    width: 200px;
    height: 200px;
}
img {
    object-fit: cover;
    width: 100%;
    height: 100%;
}

MDN

iframe 的作用

iframe 是用来在网页中插入第三方页面,早期的页面使用 iframe 主要是用于导航栏这种很多页面都相同的部分,这样在切换页面的时候避免重复下载。

优点

  1. 便于修改,模拟分离,像一些信息管理系统会用到。
  2. 但现在基本不推荐使用。除非特殊需要,一般不推荐使用。

缺点

  1. iframe 的创建比一般的 DOM 元素慢了 1 - 2 个数量级
  2. iframe 标签会阻塞页面的的加载,如果页面的 onload 事件不能及时触发,会让用户觉得网页加载很慢,用户体验不好,在 Safari 和 Chrome 中可以通过 js 动态设置 iframe 的 src 属性来避免阻塞。
  3. iframe 对于 SEO 不友好,替换方案一般就是动态语言的 Incude 机制和 ajax 动态填充内容等。

css hack 是什么

由于不同的浏览器,比如 Internet Explorer 6,Internet Explorer 7,Mozilla Firefox 等,对 CSS 的解析认识不一样,因此会导致生成的页面效果不一样,得不到我们所需要的页面效果。

这个时候我们就需要针对不同的浏览器去写不同的 CSS,让它能够同时兼容不同的浏览器,能在不同的浏览器中也能得到我们想要的页面效果。

这个针对不同的浏览器写不同的 CSS code 的过程,就叫 CSS hack, 也叫写 CSS hack。

具体请看:

http://www.cnblogs.com/Renyi-…

过渡与动画的区别是什么

  • transition

可以在一定的时间内实现元素的状态过渡为最终状态,用于模拟以一种过渡动画效果,但是功能有限,只能用于制作简单的动画效果而动画属性

  • animation

可以制作类似 Flash 动画,通过关键帧控制动画的每一步,控制更为精确,从而可以制作更为复杂的动画。

什么是外边距合并

外边距合并指的是,当两个垂直外边距相遇时,它们将形成一个外边距。

合并后的外边距的高度等于两个发生合并的外边距的高度中的较大者。

去除 inline-block 元素间间距的方法

  • 移除空格
  • 使用 margin 负值
  • 使用 font-size:0
  • letter-spacing
  • word-spacing

更详细的介绍请看: 去除 inline-block 元素间间距的 N 种方法

JavaScript 问题

同源策略

同源策略可防止 JavaScript 发起跨域请求。源被定义为 URI、主机名和端口号的组合。此策略可防止页面上的恶意脚本通过该页面的文档对象模型,访问另一个网页上的敏感数据。

跨域

  • 原因

浏览器的同源策略导致了跨域

  • 作用

用于隔离潜在恶意文件的重要安全机制

  • 解决
  1. jsonp,允许 script 加载第三方资源
  2. 反向代理(nginx 服务内部配置 Access-Control-Allow-Origin *)
  3. cors 前后端协作设置请求头部,Access-Control-Allow-Origin 等头部信息
  4. iframe 嵌套通讯,postmessage

https://zhuanlan.zhihu.com/p/…

跨域资源共享 CORS 阮一峰

JSONP

这是我认为写得比较通俗易懂的一篇文章 直接转载过来

https://blog.csdn.net/hansexp…

域名收敛

PC 时代为了突破浏览器的域名并发限制。有了域名发散。

浏览器有并发限制,是为了防止 DDOS 攻击。

域名收敛:就是将静态资源放在一个域名下。减少 DNS 解析的开销。

域名发散:是将静态资源放在多个子域名下,就可以多线程下载,提高并行度,使客户端加载静态资源更加迅速。

域名发散是 pc 端为了利用浏览器的多线程并行下载能力。而域名收敛多用与移动端,提高性能,因为 dns 解析是是从后向前迭代解析,如果域名过多性能会下降,增加 DNS 的解析开销。

事件绑定的方式

  • 嵌入 dom
<button onclick="func()"> 按钮 </button>
  • 直接绑定
btn.onclick = function(){}
  • 事件监听
btn.addEventListener('click',function(){})

事件委托

事件委托利用了事件冒泡,只指定一个事件处理程序,就可以管理某一类型的所有事件。所有用到按钮的事件(多数鼠标事件和键盘事件)都适合采用事件委托技术,
使用事件委托可以节省内存。

<ul>
  <li> 苹果 </li>
  <li> 香蕉 </li>
  <li> 凤梨 </li>
</ul>

// good
document.querySelector('ul').onclick = (event) => {
  let target = event.target
  if (target.nodeName === 'LI') {console.log(target.innerHTML)
  }
}

// bad
document.querySelectorAll('li').forEach((e) => {e.onclick = function() {console.log(this.innerHTML)
  }
}) 

事件循环

事件循环是一个单线程循环,用于监视调用堆栈并检查是否有工作即将在任务队列中完成。如果调用堆栈为空并且任务队列中有回调函数,则将回调函数出队并推送到调用堆栈中执行。

事件模型

  • DOM0

直接绑定

<input onclick="sayHi()"/>

btn.onclick = function() {}
btn.onclick = null
  • DOM2

DOM2 级事件可以冒泡和捕获
通过 addEventListener 绑定
通过 removeEventListener 解绑

// 绑定
btn.addEventListener('click', sayHi)
// 解绑
btn.removeEventListener('click', sayHi)
  • DOM3

DOM3 具有更多事件类型
DOM3 级事件在 DOM2 级事件的基础上添加了更多的事件类型,全部类型如下:

UI 事件,当用户与页面上的元素交互时触发,如:load、scroll
焦点事件,当元素获得或失去焦点时触发,如:blur、focus
鼠标事件,当用户通过鼠标在页面执行操作时触发如:dbclick、mouseup
滚轮事件,当使用鼠标滚轮或类似设备时触发,如:mousewheel
文本事件,当在文档中输入文本时触发,如:textInput
键盘事件,当用户通过键盘在页面上执行操作时触发,如:keydown、keypress
合成事件,当为 IME(输入法编辑器)输入字符时触发,如:compositionstart
变动事件,当底层 DOM 结构发生变化时触发,如:DOMsubtreeModified

https://www.jianshu.com/p/3ac…

如何自定义事件

  1. 原生提供了 3 个方法实现自定义事件
  2. createEvent,设置事件类型,是 html 事件还是 鼠标事件
  3. initEvent 初始化事件,事件名称,是否允许冒泡,是否阻止自定义事件
  4. dispatchEvent 触发事件

MDN

target 和 currentTarget 区别

  • event.target

返回触发事件的元素

  • event.currentTarget

返回绑定事件的元素

prototype 和__proto__的关系是什么

所有的对象都拥有__proto__属性,它指向 Object.prototype(Object 是一个原生函数,所有的对象都是 Object 的实例)

let obj = {}
obj.__proto__ === Object.prototype // true

所有的函数都同时拥有__proto__和 protytpe 属性
函数的__proto__指向自己的函数实现 函数的 protytpe 是一个对象 所以函数的 prototype 也有__proto__属性 指向 Object.prototype

function func() {}
func.prototype.__proto__ === Object.prototype // true

Object.prototype.__proto__指向 null

Object.prototype.__proto__ // null

原型继承

所有的 JS 对象都有一个 prototype 属性,指向它的原型对象。当试图访问一个对象的属性时,如果没有在该对象上找到,它还会搜寻该对象的原型,以及该对象的原型的原型,依次层层向上搜索,直到找到一个名字匹配的属性或到达原型链的末尾。

继承

JS 高程第 3 版 第 6 章 继承
寄生组合式继承

function SuperType(name) {
    this.name = name
    this.colors = ['red']
}

SuperType.prototype.sayName = function() {console.log(this.name)
}
// 继承实例属性
function SubType(name, age) {SuperType.call(this, name)
    this.age = age
}

function inheritPrototype(subType, superType) {let prototype = Object.create(superType.prototype)
    prototype.constructor = subType
    subType.prototype = prototype
}
// 继承原型方法
inheritPrototype(SubType, SuperType)

// 定义自己的原型方法
SubType.prototype.sayAge = function() {console.log(this.age)
}

闭包

闭包是指有权访问另一个函数作用域中的变量的函数。

function sayHi(name) {return () => {console.log(`Hi! ${name}`)
    }
}
const test = sayHi('xiaoming')
test() // Hi! xiaoming

虽然 sayHi 函数已经执行完毕,但是其活动对象也不会被销毁,因为 test 函数仍然引用着 sayHi 函数中的变量 name,这就是闭包。

但也因为闭包引用着另一个函数的变量,导致另一个函数已经不使用了也无法销毁,所以闭包使用过多,会占用较多的内存,这也是一个副作用。

有一个函数,参数是一个函数,返回值也是一个函数,返回的函数功能和入参的函数相似,但这个函数只能执行 3 次,再次执行无效,如何实现

这个题目是考察闭包的使用

function sayHi() {console.log('hi')
}

function threeTimes(fn) {
    let times = 0
    return () => {if (times++ < 3) {fn()
        }
    }
}

const newFn = threeTimes(sayHi)
newFn()
newFn()
newFn()
newFn()
newFn() // 后面两次执行都无任何反应

通过闭包变量 times 来控制函数的执行

实现 add 函数, 让 add(a)(b)和 add(a,b)两种调用结果相同

function add(a, b) {if (b === undefined) {return function(x) {return a + x}
    }

    return a + b
}

Ajax

Ajax(asynchronous JavaScript and XML)是使用客户端上的许多 Web 技术,创建异步 Web 应用的一种 Web 开发技术。借助 Ajax,Web 应用可以异步(在后台)向服务器发送数据和从服务器检索数据,而不会干扰现有页面的显示和行为。通过将数据交换层与表示层分离,Ajax 允许网页和扩展 Web 应用程序动态更改内容,而无需重新加载整个页面。实际上,现在通常将 JSON 替换为 XML,因为 JavaScript 对 JSON 有原生支持优势。

XMLHttpRequest API 经常用于异步通信。此外还有最近流行的 fetch API。

let xmlhttp
if (window.XMLHttpRequest) {
    //  IE7+, Firefox, Chrome, Opera, Safari 浏览器执行代码
    xmlhttp = new XMLHttpRequest()} else {
    // IE6, IE5 浏览器执行代码
    xmlhttp = new ActiveXObject("Microsoft.XMLHTTP")
}
xmlhttp.onreadystatechange = () => {if (xmlhttp.readyState === 4 && xmlhttp.status === 200) {document.getElementById("myDiv").innerHTML = xmlhttp.responseText
    }
}
xmlhttp.open("GET", "/ajax/test.txt", true)
xmlhttp.send()

使用 Ajax 的优缺点分别是什么

优点

  • 交互性更好。来自服务器的新内容可以动态更改,无需重新加载整个页面。
  • 减少与服务器的连接,因为脚本和样式只需要被请求一次。
  • 状态可以维护在一个页面上。JavaScript 变量和 DOM 状态将得到保持,因为主容器页面未被重新加载。
  • 基本上包括大部分 SPA 的优点。

缺点

  • 动态网页很难收藏。
  • 如果 JavaScript 已在浏览器中被禁用,则不起作用。
  • 有些网络爬虫不执行 JavaScript,也不会看到 JavaScript 加载的内容。
  • 基本上包括大部分 SPA 的缺点。

https://github.com/yangshun/f…

Ajax 和 Fetch 区别

  • ajax 是使用 XMLHttpRequest 对象发起的,但是用起来很麻烦,所以 ES6 新规范就有了 fetch,fetch 发一个请求不用像 ajax 那样写一大堆代码。
  • 使用 fetch 无法取消一个请求,这是因为 fetch 基于 Promise,而 Promise 无法做到这一点。
  • 在默认情况下,fetch 不会接受或者发送 cookies
  • fetch 没有办法原生监测请求的进度,而 XMLHttpRequest 可以
  • fetch 只对网络请求报错,对 400,500 都当做成功的请求,需要封装去处理
  • fetch 由于是 ES6 规范,兼容性上比不上 XMLHttpRequest

变量提升

var 会使变量提升,这意味着变量可以在声明之前使用。let 和 const 不会使变量提升,提前使用会报错。
变量提升(hoisting)是用于解释代码中变量声明行为的术语。使用 var 关键字声明或初始化的变量,会将声明语句“提升”到当前作用域的顶部。但是,只有声明才会触发提升,赋值语句(如果有的话)将保持原样。

使用 let、var 和 const 创建变量有什么区别

用 var 声明的变量的作用域是它当前的执行上下文,它可以是嵌套的函数,也可以是声明在任何函数外的变量。let 和 const 是块级作用域,意味着它们只能在最近的一组花括号(function、if-else 代码块或 for 循环中)中访问。

function foo() {
  // 所有变量在函数中都可访问
  var bar = 'bar';
  let baz = 'baz';
  const qux = 'qux';

  console.log(bar); // bar
  console.log(baz); // baz
  console.log(qux); // qux
}

console.log(bar); // ReferenceError: bar is not defined
console.log(baz); // ReferenceError: baz is not defined
console.log(qux); // ReferenceError: qux is not defined
if (true) {
  var bar = 'bar';
  let baz = 'baz';
  const qux = 'qux';
}

// 用 var 声明的变量在函数作用域上都可访问
console.log(bar); // bar
// let 和 const 定义的变量在它们被定义的语句块之外不可访问
console.log(baz); // ReferenceError: baz is not defined
console.log(qux); // ReferenceError: qux is not defined

var 会使变量提升,这意味着变量可以在声明之前使用。let 和 const 不会使变量提升,提前使用会报错。

console.log(foo); // undefined

var foo = 'foo';

console.log(baz); // ReferenceError: can't access lexical declaration'baz' before initialization

let baz = 'baz';

console.log(bar); // ReferenceError: can't access lexical declaration'bar' before initialization

const bar = 'bar';

用 var 重复声明不会报错,但 let 和 const 会。

var foo = 'foo';
var foo = 'bar';
console.log(foo); // "bar"

let baz = 'baz';
let baz = 'qux'; // Uncaught SyntaxError: Identifier 'baz' has already been declared

let 和 const 的区别在于:let 允许多次赋值,而 const 只允许一次。

// 这样不会报错。let foo = 'foo';
foo = 'bar';

// 这样会报错。const baz = 'baz';
baz = 'qux';

https://github.com/yangshun/f…

对象浅拷贝和深拷贝有什么区别

JS 中,除了基本数据类型,还存在对象、数组这种引用类型。
基本数据类型,拷贝是直接拷贝变量的值,而引用类型拷贝的其实是变量的地址。

let o1 = {a: 1}
let o2 = o1

在这种情况下,如果改变 o1o2 其中一个值的话,另一个也会变,因为它们都指向同一个地址。

o2.a = 3
console.log(o1.a) // 3

而浅拷贝和深拷贝就是在这个基础之上做的区分,如果在拷贝这个对象的时候,只对基本数据类型进行了拷贝,而对引用数据类型只是进行了引用的传递,而没有重新创建一个新的对象,则认为是浅拷贝。反之,在对引用数据类型进行拷贝的时候,创建了一个新的对象,并且复制其内的成员变量,则认为是深拷贝。

怎么实现对象深拷贝

let o1 = {a:{b:1}
}
let o2 = JSON.parse(JSON.stringify(o1))

另一种方法

function deepCopy(s) {const d = {}
    for (let k in s) {if (typeof s[k] == 'object') {d[k] = deepCopy(s[k])
        } else {d[k] = s[k]
        }
    }

    return d
}

数组去重

ES5

function unique(arry) {const temp = []
    arry.forEach(e => {if (temp.indexOf(e) == -1) {temp.push(e)
        }
    })
    
    return temp
}

ES6

function unique (arr) {return Array.from(new Set(arr))
}

数据类型

  1. Undefined
  2. Null
  3. Boolean
  4. Number
  5. String
  6. Object
  7. symbol(ES6 新增)

内置函数(原生函数)

  • String
  • Number
  • Boolean
  • Object
  • Function
  • Array
  • Date
  • RegExp
  • Error
  • Symbol

原始值 “I am a string” 并不是一个对象,它只是一个字面量,并且是一个不可变的值。
如果要在这个字面量上执行一些操作,比如获取长度、访问其中某个字符等,那需要将其
转换为 String 对象。
幸好,在必要时语言会自动把字符串字面量转换成一个 String 对象,也就是说你并不需要
显式创建一个对象。

如何判断数组与对象

Array.isArray([]) // true
Array.isArray({}) // false

typeof [] // "object"
typeof {} // "object"

Object.prototype == [].__proto__ // false
Object.prototype == {}.__proto__ // true
Array.prototype == [].__proto__ // true
Array.prototype == {}.__proto__ // false

自动分号

有时 JavaScript 会自动为代码行补上缺失的分号,即自动分号插入(Automatic SemicolonInsertion,ASI)。

因为如果缺失了必要的 ;,代码将无法运行,语言的容错性也会降低。ASI 能让我们忽略那些不必要的 ;。

请注意,ASI 只在换行符处起作用,而不会在代码行的中间插入分号。

如果 JavaScript 解析器发现代码行可能因为缺失分号而导致错误,那么它就会自动补上分
号。并且,只有在代码行末尾与换行符之间除了空格和注释之外没有别的内容时,它才会
这样做。

浮点数精度

https://www.css88.com/archive…

cookie、localStorage、sessionStorage 区别

特性 cookie localStorage sessionStorage
由谁初始化 客户端或服务器,服务器可以使用 Set-Cookie 请求头。 客户端 客户端
数据的生命周期 一般由服务器生成,可设置失效时间,如果在浏览器生成,默认是关闭浏览器之后失效 永久保存,可清除 仅在当前会话有效,关闭页面后清除
存放数据大小 4KB 5MB 5MB
与服务器通信 每次都会携带在 HTTP 头中,如果使用 cookie 保存过多数据会带来性能问题 仅在客户端保存 仅在客户端保存
用途 一般由服务器生成,用于标识用户身份 用于浏览器缓存数据 用于浏览器缓存数据
访问权限 任意窗口 任意窗口 当前页面窗口

自执行函数? 用于什么场景?好处?

自执行函数:1、声明一个匿名函数 2、马上调用这个匿名函数。

作用:创建一个独立的作用域。

好处:防止变量弥散到全局,以免各种 js 库冲突。隔离作用域避免污染,或者截断作用域链,避免闭包造成引用变量无法释放。利用立即执行特性,返回需要的业务函数或对象,避免每次通过条件判断来处理

场景:一般用于框架、插件等场景

多个页面之间如何进行通信

有如下几个方式:

  • cookie
  • web worker
  • localeStorage 和 sessionStorage

css 动画和 js 动画的差异

  1. 代码复杂度,js 动画代码相对复杂一些
  2. 动画运行时,对动画的控制程度上,js 能够让动画,暂停,取消,终止,css 动画不能添加事件
  3. 动画性能看,js 动画多了一个 js 解析的过程,性能不如 css 动画好

https://zhuanlan.zhihu.com/p/…

如何实现文件断点续传

断点续传最核心的内容就是把文件“切片”然后再一片一片的传给服务器,但是这看似简单的上传过程却有着无数的坑。

首先是文件的识别,一个文件被分成了若干份之后如何告诉服务器你切了多少块,以及最终服务器应该如何把你上传上去的文件进行合并,这都是要考虑的。

因此在文件开始上传之前,我们和服务器要有一个“握手”的过程,告诉服务器文件信息,然后和服务器约定切片的大小,当和服务器达成共识之后就可以开始后续的文件传输了。

前台要把每一块的文件传给后台,成功之后前端和后端都要标识一下,以便后续的断点。

当文件传输中断之后用户再次选择文件就可以通过标识来判断文件是否已经上传了一部分,如果是的话,那么我们可以接着上次的进度继续传文件,以达到续传的功能。
有了 HTML5 的 File api 之后切割文件比想想的要简单的多的多。

只要用 slice 方法就可以了

var packet = file.slice(start, end);

参数 start 是开始切片的位置,end 是切片结束的位置 单位都是字节。通过控制 start 和 end 就可以是实现文件的分块

file.slice(0,1000);
file.slice(1000,2000);
file.slice(2000,3000);
// ......

在把文件切成片之后,接下来要做的事情就是把这些碎片传到服务器上。
如果中间掉线了,下次再传的时候就得先从服务器获取上一次上传文件的位置,然后以这个位置开始上传接下来的文件内容。

https://www.cnblogs.com/zhwl/…

new 一个对象经历了什么

function Test(){}
const test = new Test()
  1. 创建一个新对象:
const obj = {}
  1. 设置新对象的 constructor 属性为构造函数的名称,设置新对象的__proto__属性指向构造函数的 prototype 对象
obj.constructor = Test
obj.__proto__ = Test.prototype
  1. 使用新对象调用函数,函数中的 this 被指向新实例对象
Test.call(obj)
  1. 将初始化完毕的新对象地址,保存到等号左边的变量中

bind、call、apply 的区别

call 和 apply 其实是一样的,区别就在于传参时参数是一个一个传或者是以一个数组的方式来传。

call 和 apply 都是在调用时生效,改变调用者的 this 指向。

let name = 'Jack'
const obj = {name: 'Tom'}
function sayHi() {console.log('Hi!' + this.name)}

sayHi() // Hi! Jack
sayHi.call(obj) // Hi! Tom

bind 也是改变 this 指向,不过不是在调用时生效,而是返回一个新函数。

const newFunc = sayHi.bind(obj)
newFunc() // Hi! Tom

请简述 JavaScript 中的this

JS 中的 this 是一个相对复杂的概念,不是简单几句能解释清楚的。粗略地讲,函数的调用方式决定了 this 的值。我阅读了网上很多关于 this 的文章,Arnav Aggrawal 写的比较清楚。this取值符合以下规则:

  1. 在调用函数时使用 new 关键字,函数内的 this 是一个全新的对象。
  2. 如果 applycallbind方法用于调用、创建一个函数,函数内的 this 就是作为参数传入这些方法的对象。
  3. 当函数作为对象里的方法被调用时,函数内的 this 是调用该函数的对象。比如当 obj.method() 被调用时,函数内的 this 将绑定到 obj 对象。
  4. 如果调用函数不符合上述规则,那么 this 的值指向全局对象(global object)。浏览器环境下 this 的值指向 window 对象,但是在严格模式下 ('use strict'),this 的值为undefined
  5. 如果符合上述多个规则,则较高的规则(1 号最高,4 号最低)将决定 this 的值。
  6. 如果该函数是 ES2015 中的箭头函数,将忽略上面的所有规则,this被设置为它被创建时的上下文。

想获得更深入的解释,请查看他在 Medium 上的文章。

https://github.com/yangshun/f…

如何确定 this 指向

如果要判断一个运行中函数的 this 绑定,就需要找到这个函数的直接调用位置。找到之后就可以顺序应用下面这四条规则来判断 this 的绑定对象。

  1. 由 new 调用?绑定到新创建的对象。
  2. 由 call 或者 apply(或者 bind)调用?绑定到指定的对象。
  3. 由上下文对象调用?绑定到那个上下文对象。
  4. 默认:在严格模式下绑定到 undefined,否则绑定到全局对象。

一定要注意,有些调用可能在无意中使用默认绑定规则。如果想“更安全”地忽略 this 绑定,你可以使用一个 DMZ 对象,比如 ø = Object.create(null),以保护全局对象。

ES6 中的箭头函数并不会使用四条标准的绑定规则,而是根据当前的词法作用域来决定 this,具体来说,箭头函数会继承外层函数调用的 this 绑定(无论 this 绑定到什么)。这其实和 ES6 之前代码中的 self = this 机制一样

参考:《你不知道的 JavaScript》

== 和 === 的区别是什么

==是抽象相等运算符,而 === 是严格相等运算符。==运算符是在进行必要的类型转换后,再比较。===运算符不会进行类型转换,所以如果两个值不是相同的类型,会直接返回 false。使用== 时,可能发生一些特别的事情,例如:

1 == '1'; // true
1 == [1]; // true
1 == true; // true
0 == ''; // true
0 == '0'; // true
0 == false; // true

如果你对 =====的概念不是特别了解,建议大多数情况下使用===

箭头函数和普通函数有什么区别

  • 函数体内的 this 对象,就是定义时所在的对象,而不是使用时所在的对象,用 call apply bind 也不能改变 this 指向
  • 不可以当作构造函数,也就是说,不可以使用 new 命令,否则会抛出一个错误。
  • 不可以使用 arguments 对象,该对象在函数体内不存在。如果要用,可以用 rest 参数代替。
  • 不可以使用 yield 命令,因此箭头函数不能用作 Generator 函数。
  • 箭头函数没有原型对象prototype

首屏时间、白屏时间

Performance 接口可以获取到当前页面中与性能相关的信息。

该类型的对象可以通过调用只读属性 Window.performance 来获得。

白屏时间:

performance.timing.responseStart - performance.timing.navigationStart

首屏时间

window.onload = () => {new Date() - performance.timing.responseStart
}

https://developer.mozilla.org…

当你在浏览器输入一个地址后发生了什么

https://github.com/skyline754…

页面大量图片,如何优化加载,优化用户体验

  1. 图片懒加载。在页面的未可视区域添加一个滚动事件,判断图片位置与浏览器顶端的距离与页面的距离,如果前者小于后者,优先加载。
  2. 如果为幻灯片、相册等,可以使用图片预加载技术,将当前展示图片的前一张和后一张优先下载。
  3. 如果图片为 css 图片,可以使用 CSSsprite,SVGsprite 等技术。
  4. 如果图片过大,可以使用特殊编码的图片,加载时会先加载一张压缩的特别厉害的缩略图,以提高用户体验。
  5. 如果图片展示区域小于图片的真实大小,应在服务器端根据业务需要先进行图片压缩,图片压缩后大小与展示一致。

https://www.jianshu.com/p/5d8…

js 网络请求性能优化之防抖与节流

  • 防抖(debounce)

在函数需要频繁触发时,只有当有足够空闲的时间时,才执行一次。就好像在百度搜索时,每次输入之后都有联想词弹出,这个控制联想词的方法就不可能是输入框内容一改变就触发的,他一定是当你结束输入一段时间之后才会触发。

  • 节流(thorttle)

预定一个函数只有在大于等于执行周期时才执行,周期内调用不执行。就好像你在淘宝抢购某一件限量热卖商品时,你不断点刷新点购买,可是总有一段时间你点上是没有效果,这里就用到了节流,就是怕点的太快导致系统出现 bug。

  • 区别

在发生持续触发事件时,防抖设置事件延迟并在空闲时间去触发事件,而节流则是隔一定的时间触发一次。

具体请看:

https://blog.csdn.net/jacoox/…

如何做到修改 url 参数页面不刷新

HTML5 引入了 history.pushState()history.replaceState() 方法,它们分别可以添加和修改历史记录条目。

let stateObj = {foo: "bar",};

history.pushState(stateObj, "page 2", "bar.html");

假设当前页面为 foo.html,执行上述代码后会变为 bar.html,点击浏览器后退,会变为 foo.html,但浏览器并不会刷新。
pushState() 需要三个参数: 一个状态对象, 一个标题 (目前被忽略), 和 (可选的) 一个 URL. 让我们来解释下这三个参数详细内容:

  • 状态对象 — 状态对象 state 是一个 JavaScript 对象,通过 pushState () 创建新的历史记录条目。无论什么时候用户导航到新的状态,popstate 事件就会被触发,且该事件的 state 属性包含该历史记录条目状态对象的副本。

状态对象可以是能被序列化的任何东西。原因在于 Firefox 将状态对象保存在用户的磁盘上,以便在用户重启浏览器时使用,我们规定了状态对象在序列化表示后有 640k 的大小限制。如果你给 pushState() 方法传了一个序列化后大于 640k 的状态对象,该方法会抛出异常。如果你需要更大的空间,建议使用 sessionStorage 以及 localStorage.

  • 标题 — Firefox 目前忽略这个参数,但未来可能会用到。传递一个空字符串在这里是安全的,而在将来这是不安全的。二选一的话,你可以为跳转的 state 传递一个短标题。
  • URL — 该参数定义了新的历史 URL 记录。注意,调用 pushState() 后浏览器并不会立即加载这个 URL,但可能会在稍后某些情况下加载这个 URL,比如在用户重新打开浏览器时。新 URL 不必须为绝对路径。如果新 URL 是相对路径,那么它将被作为相对于当前 URL 处理。新 URL 必须与当前 URL 同源,否则 pushState() 会抛出一个异常。该参数是可选的,缺省为当前 URL。

格式化金钱,每千分位加逗号

function format(str) {
    let s = ''
    let count = 0
    for (let i = str.length - 1; i >= 0; i--) {s = str[i] + s
        count++
        if (count % 3 == 0 && i != 0) {s = ',' + s}
    }
    return s
}
function format(str) {return str.replace(/(\d)(?=(?:\d{3})+$)/g, '$1,')
}

请用 js 去除字符串空格

去除所有空格

str.replace(/\s/g, '')

去除两边空格

str.replace(/^\s+|\s+$/g, '')
// 原生方法
str.trim()

HTTP 问题

RESTful

REST 指的是一组架构约束条件和原则。满足这些约束条件和原则的应用程序或设计就是 RESTful。

  • GET

get 方法在 Rest 中主要用于获取资源,能够发送参数,不过有限制,且参数都会以? 开头的形 式附加在 URL 尾部。
规范的 get 方法处理器应该是幂等的,也就是说对一个资源不论发送多少次 get 请求都不会更改数据或造成破坏。

  • POST

post 方法在 Rest 请求中主要用于添加资源,参数信息存放在请求报文的消息体中相对安全,且可发送较大信息

  • PUT

put 方法在 Rest 中主要用于更新资源,因为大多数浏览器不支持 put 和 delete,会自动将 put 和 delete 请求转化为 get 和 post. 因此为了使用 put 和 delete 方法,
需要以 post 发送请求,在表单中使用隐藏域发送真正的请求。
put 方法的参数是同 post 一样是存放在消息中的,同样具有安全性,可发送较大信息。
put 方法是幂等的,对同一 URL 资源做出的同一数据的任意次 put 请求其对数据的改变都是一致的。

  • DELETE

Delete 在 Rest 请求中主要用于删除资源,因为大多数浏览器不支持 put 和 delete,会自动将 put 和 delete 请求转化为 get 和 post。
因此为了使用 put 和 delete 方法, 需要以 post 发送请求,在表单中使用隐藏域发送真正的请求。
Delete 方法的参数同 post 一样存放在消息体中, 具有安全性,可发送较大信息 Delete 方法是幂等的,不论对同一个资源进行多少次 delete 请求都不会破坏数据

https://blog.csdn.net/jnshu_i…

GET 和 POST 的区别

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

Accept 和 Content-Type

Accept 请求头用来告知客户端可以处理的内容类型,这种内容类型用 MIME 类型来表示。
服务器使用 Content-Type 应答头通知客户端它的选择。

Accept: text/html
Accept: image/*
Accept: text/html, application/xhtml+xml, application/xml;q=0.9, */*;q=0.8

1.Accept 属于请求头,Content-Type 属于实体头。

Http 报头分为通用报头,请求报头,响应报头和实体报头。

请求方的 http 报头结构:通用报头 | 请求报头 | 实体报头

响应方的 http 报头结构:通用报头 | 响应报头 | 实体报头

2.Accept 代表发送端(客户端)希望接受的数据类型。

比如:Accept:text/xml;

代表客户端希望接受的数据类型是 xml 类型

Content-Type 代表发送端(客户端 | 服务器)发送的实体数据的数据类型。

比如:Content-Type:text/html;

代表发送端发送的数据格式是 html。

二者合起来,

Accept:text/xml;

Content-Type:text/html

即代表希望接受的数据类型是 xml 格式,本次请求发送的数据的数据格式是 html。

状态码

状态码 类别 描述
1xx Informational(信息状态码) 接受请求正在处理
2xx Success(成功状态码) 请求正常处理完毕
3xx Redirection(重定向状态码) 需要附加操作已完成请求
4xx Client Error(客户端错误状态码) 服务器无法处理请求
5xx Server Error(服务器错误状态码) 服务器处理请求出错

HTTP 缓存

https://segmentfault.com/a/11…

如何处理不让别人盗用你的图片,访问你的服务器资源

  • http header, 对 refer 做判断看来源是不是自己的网站,如果不是就拒绝
  • 通过 session 校验,如果不通过特定服务生成 cookie 和 session 就不能请求得到资源

Http 与 Https 的区别

  • HTTP 的 URL 以 http:// 开头,而 HTTPS 的 URL 以 https:// 开头
  • HTTP 是不安全的,而 HTTPS 是安全的
  • HTTP 标准端口是 80,而 HTTPS 的标准端口是 443
  • 在 OSI 网络模型中,HTTP 工作于应用层,而 HTTPS 的安全传输机制工作在传输层
  • HTTP 无法加密,而 HTTPS 对传输的数据进行加密
  • HTTP 无需证书,而 HTTPS 需要 CA 机构 wosign 的颁发的 SSL 证书

https://zhuanlan.zhihu.com/p/…

什么是 Http 协议无状态协议? 怎么解决 Http 协议无状态协议?

无状态协议对于事务处理没有记忆能力。缺少状态意味着如果后续处理需要前面的信息也就是说,

当客户端一次 HTTP 请求完成以后,客户端再发送一次 HTTP 请求,HTTP 并不知道当前客户端是一个”老用户“。

可以使用 Cookie 来解决无状态的问题,Cookie 就相当于一个通行证,第一次访问的时候给客户端发送一个 Cookie,

当客户端再次来的时候,拿着 Cookie(通行证),那么服务器就知道这个是”老用户“。

https://zhuanlan.zhihu.com/p/…

常用的 HTTP 方法有哪些

  • GET:用于请求访问已经被 URL(统一资源标识符)识别的资源,可以通过 URL 传参给服务器。
  • POST:用于传输信息给服务器,主要功能与 Get 方法类似,但一般推荐 POST 方式。
  • PUT:传输文件,报文主体包含文件内容,保存到对应 URL 位置。
  • HEAD:获取报文首部,与 GET 方法类似,只是不返回报文主体,一般用于验证 URL 是否有效。
  • DELET:删除文件,与 PUT 方法相反,删除对应 URL 位置的文件。OPTIONS:查询相应 URL 支持的 HTTP 方法。

一次完整的 HTTP 请求所经历的 7 个步骤

HTTP 通信机制是在一次完整的 HTTP 通信过程中,Web 浏览器与 Web 服务器之间将完成下列 7 个步骤:

  • 建立 TCP 连接

在 HTTP 工作开始之前,Web 浏览器首先要通过网络与 Web 服务器建立连接,该连接是通过 TCP 来完成的,该协议与 IP 协议共同构建 Internet,即著名的 TCP/IP 协议族,因此 Internet 又被称作是 TCP/IP 网络。HTTP 是比 TCP 更高层次的应用层协议,根据规则,只有低层协议建立之后才能,才能进行更层协议的连接,因此,首先要建立 TCP 连接,一般 TCP 连接的端口号是 80。

  • Web 浏览器向 Web 服务器发送请求行

一旦建立了 TCP 连接,Web 浏览器就会向 Web 服务器发送请求命令。例如:GET /sample/hello.jsp HTTP/1.1。

  • Web 浏览器发送请求头

浏览器发送其请求命令之后,还要以头信息的形式向 Web 服务器发送一些别的信息,之后浏览器发送了一空白行来通知服务器,它已经结束了该头信息的发送。

  • Web 服务器应答

客户机向服务器发出请求后,服务器会客户机回送应答,HTTP/1.1 200 OK,应答的第一部分是协议的版本号和应答状态码。

  • Web 服务器发送应答头

正如客户端会随同请求发送关于自身的信息一样,服务器也会随同应答向用户发送关于它自己的数据及被请求的文档。

  • Web 服务器向浏览器发送数据

Web 服务器向浏览器发送头信息后,它会发送一个空白行来表示头信息的发送到此为结束,接着,它就以 Content-Type 应答头信息所描述的格式发送用户所请求的实际数据。

  • Web 服务器关闭 TCP 连接

一般情况下,一旦 Web 服务器向浏览器发送了请求数据,它就要关闭 TCP 连接,然后如果浏览器或者服务器在其头信息加入了这行代码:

Connection:keep-alive

TCP 连接在发送后将仍然保持打开状态,于是,浏览器可以继续通过相同的连接发送请求。保持连接节省了为每个请求建立新连接所需的时间,还节约了网络带宽。

建立 TCP 连接 -> 发送请求行 -> 发送请求头 ->(到达服务器)发送状态行 -> 发送响应头 -> 发送响应数据 -> 断 TCP 连接

https://juejin.im/post/5a8102…

Vue 问题

什么是 mvvm

MVVM 最早由微软提出来,它借鉴了桌面应用程序的 MVC 思想,在前端页面中,把 Model 用纯 JavaScript 对象表示,View 负责显示,两者做到了最大限度的分离
把 Model 和 View 关联起来的就是 ViewModel。

ViewModel 负责把 Model 的数据同步到 View 显示出来,还负责把 View 的修改同步回 Model

View 和 Model 之间的同步工作完全是自动的,无需人为干涉(由 viewModel 完成,在这里指 VUE)

因此开发者只需关注业务逻辑,不需要手动操作 DOM, 不需要关注数据状态的同步问题,复杂的数据状态维护完全由 MVVM 来统一管理

ViewModel 如何编写?

需要用 JavaScript 编写一个通用的 ViewModel,这样,就可以复用整个 MVVM 模型了

一个 MVVM 框架和 jQuery 操作 DOM 相比有什么区别?
我们先看用 jQuery 实现的修改两个 DOM 节点的例子:

<!-- HTML -->
<p>Hello, <span id="name">Bart</span>!</p>
<p>You are <span id="age">12</span>.</p>

Hello, Bart!

You are 12.

用 jQuery 修改 name 和 age 节点的内容:

var name = 'Homer';
var age = 51;

$('#name').text(name);
$('#age').text(age);

如果我们使用 MVVM 框架来实现同样的功能,我们首先并不关心 DOM 的结构,而是关心数据如何存储。最简单的数据存储方式是使用 JavaScript 对象:

var person = {
    name: 'Bart',
    age: 12
}

我们把变量 person 看作 Model,把 HTML 某些 DOM 节点看作 View,并假定它们之间被关联起来了。

要把显示的 name 从 Bart 改为 Homer,把显示的 age 从 12 改为 51,我们并不操作 DOM,而是直接修改 JavaScript 对象:

person.name = 'Homer';
person.age = 51;

执行上面的代码,我们惊讶地发现,改变 JavaScript 对象的状态,会导致 DOM 结构作出对应的变化!这让我们的关注点从如何操作 DOM 变成了如何更新 JavaScript 对象的状态,而操作 JavaScript 对象比 DOM 简单多了!

这就是 MVVM 的设计思想:关注 Model 的变化,让 MVVM 框架去自动更新 DOM 的状态,从而把开发者从操作 DOM 的繁琐步骤中解脱出来!
下图可以很好的解释 view viewModel model 之间的关系

Vue 的优点是什么

mvvm 的优点即是 vue 的优点,在这里再总结一下:

数据和视频之间的同步工作完全是自动的,无需人为干涉,所以开发者只需关注业务逻辑,不需要手动操作 DOM, 不需要关注数据状态的同步问题,
复杂的数据状态维护完全由 MVVM 来统一管理,节省了很多精力。

对于生命周期的理解

创建一个 Vue 实例,是一个漫长的过程,要经历初始化,数据合并,模板解析,数据渲染等等一系列过程。
所以,为了能实现在这个过程里面插入自己想要提前做的事情,就有了生命周期钩子函数。

举个例子:

一辆公交车,从出发点 A 站到终点站 B,中间有很多站点,公交车每到一个站点,就得停下来,等待客人上车,然后再驶往下一个站点,一直到终点站为止。A 和 B 之间的站点,就像是这个路程的生命周期。每一个站点都是一个不同的生命周期(站点名不同),只要到了站点,就得执行该站点对应的生命周期函数,只不过每个站点的生命周期函数都是一样的(等待客人上车)。

Vue 中的生命周期也是一样,对应了 Vue 实例从创建到结束之间的每一个过程。
例如,Vue 的 beforeCreate 周期,指的就是 Vue 在实例初始化之后,数据观测 (data observer) 和 event/watcher 事件配置之前被调用。

至于 Vue 具体的生命周期函数有哪些,请看官网 API 文档

组件传值

  • 父组件通过 prop 向子组件传值
  • 子组件通过事件向父组件传值
  • 子组件与子组件之间不能直接传值,需要通过父组件来做间接传值,在这种情况下推荐使用 vuex

具体例子请看官方文档

vue 数据绑定原理

Vue 的数据双向绑定都是依据 Object.defineProperty()这一方法来做的

Object.defineProperty 到底有什么作用呢?
MDN

Object.defineProperty(obj, prop, descriptor)

obj
要在其上定义属性的对象。prop
要定义或修改的属性的名称。descriptor
将被定义或修改的属性描述符。

简单来说 这个方法可以定义一个对象某个属性的描述符

我们需要用到的就是描述符当中的 getter 和 setter

const obj = {a:1}
obj.a // 1

obj.a = 2 

像上面代码中的两个操作 读取和赋值 就是在访问 obj.a 的 getter 和 setter

当我们输入 obj.a 时 就是在访问 obj 对象 a 属性的 getter 当输入 obj.a = 2 时就是在访问 obj 对象 a 属性的 setter

Object.defineProperty(obj, 'a', {get : function(){return val},
  set : function(newValue){val = newValue},
  enumerable : true,
  configurable : true
})

getter 和 setter 都是一个函数 我们还可以这样做 例如

get: function() {
  // 每次访问 obj.a 时都会执行这段代码
  console.log('hello, 你在读取 a 的值')
  return val
}
set: function(newValue) {
  val = newValue
  // 每次给 obj.a 赋值时都会执行这段代码
  console.log('你设置了 a 的值')
}

Vue 的双向数据绑定就是根据上面的原理来实现的
只要在读取值时收集观察者 在赋值时触发观察者更新函数 就可以实现数据变更 从而实现 DOM 重新渲染

说到这可能还不是很明白 不要急 慢慢来 先看一下这段代码 复制放到 HTML 文件里自己运行一下
然后打开网页 在控制台里输入 data.user.name 看看 会有惊喜

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title> 动态数据绑定(一)</title>
</head>
<body>
 <script>
    var data = {
        user: {
            name: 'xiaoming',
            age: 18,
            occupation: 'frontend'
        },
        address: {city: 'shaoguan'}
    }; 
    function Observer(data) {
        this.data = data;
        this.walk(data);
    }
    Observer.prototype = {walk: function(obj) {
            var value,
                key;
            for (key in obj) {if (obj.hasOwnProperty(key)) {value = obj[key];
                    if (typeof value === 'object') {new Observer(value);
                    }
                    this.convert(key, value); 
                }   
            }
        },
        convert: function(key, value) {
            Object.defineProperty(this.data, key, {get : function(){console.log("你访问了" + key);
                    return value; 
                },
                set : function(newValue){ 
                    value = newValue; 
                    console.log('你设置了' + key + '=' + value);
                }
            });
        }
    }  
    var example = new Observer(data);
 </script>   
</body>
</html>

vue-router 原理

说简单点,vue-router 的原理就是通过对 URL 地址变化的监听,继而对不同的组件进行渲染。

每当 URL 地址改变时,就对相应的组件进行渲染。原理是很简单,实现方式可能有点复杂,主要有 hash 模式和 history 模式。

如果想了解得详细点,建议百度或者阅读源码。

vuex 原理

vuex 的原理其实非常简单,它为什么能实现所有的组件共享同一份数据?

因为 vuex 生成了一个 store 实例,并且把这个实例挂在了所有的组件上,所有的组件引用的都是同一个 store 实例。

store 实例上有数据,有方法,方法改变的都是 store 实例上的数据。由于其他组件引用的是同样的实例,所以一个组件改变了 store 上的数据,
导致另一个组件上的数据也会改变,就像是一个对象的引用。

如果对 vuex 的实现有兴趣,可以看看我自己造的一个 vue 轮子对应的 vuex 插件。它实现了除 vuex 模块外的所有功能。

v-if 和 v -show 的区别

v-if 是“真正”的条件渲染,因为它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建。

v-if 也是惰性的:如果在初始渲染时条件为假,则什么也不做——直到条件第一次变为真时,才会开始渲染条件块。

相比之下,v-show 就简单得多——不管初始条件是什么,元素总是会被渲染,并且只是简单地基于 CSS 进行切换。

一般来说,v-if 有更高的切换开销,而 v-show 有更高的初始渲染开销。因此,如果需要非常频繁地切换,则使用 v-show 较好;如果在运行时条件很少改变,则使用v-if 较好。

https://cn.vuejs.org/v2/guide…

vue 怎么实现页面的权限控制

利用 vue-routerbeforeEach 事件,可以在跳转页面前判断用户的权限(利用 cookie 或 token),是否能够进入此页面,如果不能则提示错误或重定向到其他页面,在后台管理系统中这种场景经常能遇到。

keep-alive 有什么作用

Vue 中,每次切换组件时,都会重新渲染。如果有多个组件切换,又想让它们保持原来的状态,避免重新渲染,这个时候就可以使用 keep-alive
keep-alive 可以使被包含的组件保留状态,或避免重新渲染。

计算属性有什么作用

先来看一下计算属性的定义:

当其依赖的属性的值发生变化的时,计算属性会重新计算。反之则使用缓存中的属性值。

计算属性和 vue 中的其它数据一样,都是响应式的,只不过它必须依赖某一个数据实现,并且只有它依赖的数据的值改变了,它才会更新。

$route 和 $router 的区别

$route 是路由信息对象,包括pathparamshashqueryfullPathmatchedname 等路由信息参数。

$router 是路由实例对象,包括了路由的跳转方法,钩子函数等

watch 的作用是什么

watch 主要作用是监听某个数据值的变化。和计算属性相比除了没有缓存,作用是一样的。

借助 watch 还可以做一些特别的事情,例如监听页面路由,当页面跳转时,我们可以做相应的权限控制,拒绝没有权限的用户访问页面。

退出移动版