diff --git a/README.md b/README.md index 710b198..375ae44 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,10 @@ 技术输出存放在 [issue](https://github.com/webproblem/Blog/issues) 中。 +* [从underscore源码看如何判断两个对象相等](https://github.com/webproblem/Blog/issues/10) + +* [从underscore源码看数组乱序](https://github.com/webproblem/Blog/issues/7) + * [从一道面试题认识函数柯里化](https://github.com/webproblem/Blog/issues/4) * [移动端手势库AlloyFinger源码分析](https://github.com/webproblem/Blog/issues/3) diff --git "a/\345\211\215\347\253\257/Browser/CSS\345\212\250\347\224\273\346\200\247\350\203\275.md" "b/\345\211\215\347\253\257/Browser/CSS\345\212\250\347\224\273\346\200\247\350\203\275.md" new file mode 100644 index 0000000..4e6cd14 --- /dev/null +++ "b/\345\211\215\347\253\257/Browser/CSS\345\212\250\347\224\273\346\200\247\350\203\275.md" @@ -0,0 +1,5 @@ +### 参考 + +* [CSS Animation性能优化](https://www.w3cplus.com/animation/animation-performance.html) + +* [[译]CSS animations 与 transitions 在浏览器方面的性能研究](https://segmentfault.com/a/1190000006923260) \ No newline at end of file diff --git "a/\345\211\215\347\253\257/Browser/\344\273\216\350\276\223\345\205\245URL\345\210\260\351\241\265\351\235\242\345\212\240\350\275\275\345\217\221\347\224\237\344\272\206\344\273\200\344\271\210.md" "b/\345\211\215\347\253\257/Browser/\344\273\216\350\276\223\345\205\245URL\345\210\260\351\241\265\351\235\242\345\212\240\350\275\275\345\217\221\347\224\237\344\272\206\344\273\200\344\271\210.md" new file mode 100644 index 0000000..b0cca7a --- /dev/null +++ "b/\345\211\215\347\253\257/Browser/\344\273\216\350\276\223\345\205\245URL\345\210\260\351\241\265\351\235\242\345\212\240\350\275\275\345\217\221\347\224\237\344\272\206\344\273\200\344\271\210.md" @@ -0,0 +1,79 @@ +## 从输入URL到页面加载发生了什么 + +1. DNS 解析 +2. TCP/IP 连接 +3. 浏览器发送 HTTP 请求 +4. 服务端响应请求 +5. 浏览器解析渲染页面 + +浏览器是多进程的,每一个 tab 标签页就是一个进程,也就是浏览器渲染进程,这个进程里面包含了多个线程: + +* GUI 线程 +* JS 引擎线程 +* 事件触发线程 +* 定时器线程 +* 网络请求线程 + +在浏览器输入 url 后,浏览器首先会开启一条网络请求线程去请求加载资源。从开启网络线程到发出 HTTP 请求的过程中会经过 DNS 解析,TCP/IP 连接等步骤。 + +### DNS 解析 + +简单来说,DNS 解析就是将 url 域名解析查询得到对应的 IP 地址的过程。 + +#### 为什么需要 DNS 解析 + +因为网络通讯都是基于 TCP/IP 的,而 TCP/IP 是基于 IP 地址识别访问的,而计算机只能识别 IP 地址,而 IP 地址并不方便记忆,所以互联网设计者在用户的方便性和可用性之间做一个权衡,也就是用网址来代替 IP 地址的使用,但实际还是需要 DNS 解析出 IP 地址访问。 + +### TCP/IP 连接 + +这一步就会经过网络的三次握手和服务器建立连接,之后客户端就会发起 HTTP 请求。 + +### HTTP 请求 + +客户端发送 HTTP 请求就是构建 HTTP 请求报文并通过 TCP 协议将请求发送给服务端。请求报文由请求行,请求头,请求正文组成。 + +这里涉及到一个优化知识点,HTTP 缓存也就是浏览器缓存,合理使用浏览器缓存对页面性能更好。在请求头中携带缓存标识的字段发送给服务端,服务端设置好响应的缓存规则后再响应头中携带过来,浏览器会根据缓存规则作出相应的缓存。浏览器缓存分为强缓存和协商缓存,强缓存如果缓存未失效就使用本地资源,不需要发起请求;而协商缓存会像服务端发起请求验证缓存是否失效,未失效就返回304状态码继续使用本地缓存。 + +[浏览器缓存机制](https://github.com/webproblem/Blog/blob/master/%E5%89%8D%E7%AB%AF/Performance/%E6%B5%8F%E8%A7%88%E5%99%A8%E7%BC%93%E5%AD%98%E6%9C%BA%E5%88%B6.md) + +#### http, https 的区别 + +* http 协议传输的数据都是明文未加密的,是无状态的,存在安全问题 +* https 本质是 http + SSL,使用 SSL 对 http 协议传输的数据进行加密 + +### 服务端响应请求 + +服务端接收到了客户端发起的请求后,后端会进行一系列的处理: + +* 会先进行验证,安全拦截,跨域验证等 +* 验证通过后,才进入到后端代码并执行相关的处理逻辑代码 +* 后端程序执行完毕后会返回一个 http 响应包 +* 将响应包返回给客户端,完成响应 + +响应报文由状态码,响应头,响应报文组成。 + +### 浏览器解析渲染页面 + +浏览器获取到 html 文件后就开始解析渲染页面。大致流程如下: + +* 解析 HTML 生成 DOM tree +* 解析 CSS 生成 CSSOM tree +* 合并 DOM tree 和 CSSOM tree,构建 render 树 +* 布局 render 树,计算元素的尺寸大小,位置 +* 绘制 render 树,绘制页面像素信息 +* 合并图层,显示在屏幕上 + +这个过程中也涉及到了很多优化点:回流重绘,css阻塞DOM渲染,js阻塞DOM解析渲染等 + +### 推荐阅读 + +[从输入URL到页面加载发生了什么](https://segmentfault.com/a/1190000006879700) + +[从输入URL到页面加载的过程?如何由一道题完善自己的前端知识体系!](https://zhuanlan.zhihu.com/p/34453198) + +[详细解析 HTTP 与 HTTPS 的区别](https://juejin.im/entry/58d7635e5c497d0057fae036) + +[深入浅出浏览器渲染原理](https://juejin.im/post/5c24d736f265da614b120d4a) + + + diff --git "a/\345\211\215\347\253\257/Browser/\345\205\263\344\272\216\351\230\273\345\241\236DOM\346\270\262\346\237\223.md" "b/\345\211\215\347\253\257/Browser/\345\205\263\344\272\216\351\230\273\345\241\236DOM\346\270\262\346\237\223.md" new file mode 100644 index 0000000..33586a6 --- /dev/null +++ "b/\345\211\215\347\253\257/Browser/\345\205\263\344\272\216\351\230\273\345\241\236DOM\346\270\262\346\237\223.md" @@ -0,0 +1,87 @@ +## 前言 + +前段时间,有个小姐姐问了我一道面试题,题目大致是:```css 和 js 是如何阻塞浏览器渲染 DOM 的,并给出一些优化方案。``` 后来发现自己给出的答案并不正确,所以这里就来总结下。 + +### CSS 不会阻塞 DOM 解析,但是会阻塞 DOM 渲染 + +首先需要知道的是,浏览器在渲染整个页面的过程中,遇到 css(外链表或者内联样式),都会阻塞后面的 DOM 渲染,但是对于后面的 DOM 解析会正常进行。这里写个 Demo 测试下。 + +```html + + + + css阻塞 + + + + + + + +

这是红色的

+ + + +``` + +模拟浏览器教慢网络的情况下测试,测试的结果是:浏览器会先打印出获取 h1 的节点(没有阻塞 DOM 解析),而后再等样式文件下载解析完成后再渲染出 h1 节点元素(阻塞了 DOM 渲染)。 + +![image](https://user-images.githubusercontent.com/20440496/49925989-7d740c00-fef5-11e8-827b-2f7603f327dc.png) + +同时也可以看到,css 文件的下载也延迟了后面 js 脚本的执行,脚本在等待 css 文件下载完成后才执行的。这里就有两个问题需要明白: + +1. 为什么 css 只阻塞 DOM 渲染,而不阻塞 DOM 解析? +2. 后面的 js 脚本为什么要等待前面的 css 文件下载? + +关于第一个问题,首先要明白浏览器的渲染流程,不同内核的浏览器渲染过程可能会有些不一样,以 webkit 为例。 + +![image](https://user-images.githubusercontent.com/20440496/49929503-997bab80-fefd-11e8-8f40-3d73f58bffb8.png) + +* 解析 HTML 文件,生成 DOM Tree +* 解析 CSS 文件,生成 CSSOM Tree +* 合并 DOM Tree 和 CSSOM Tree 生成渲染树 +* 根据 Render Tree 机制绘制页面 + +可以看到,DOM 解析和 CSS 解析是独立开来不相互影响的,所以 CSS 并不会阻塞 DOM 的解析。然而 Render Tree 是依赖于 DOM Tree 和 CSSOM Tree 的,页面渲染必须等待 CSSOM Tree 构建完成之后才会继续进行,所以 CSS 会阻塞 DOM 的渲染。 + +关于第二个问题,其实是因为浏览器内核(浏览器渲染进程)是多线程工作的,浏览器内核包含了两个线程:GUI 渲染线程,JS 引擎线程。GUI 渲染线程负责渲染页面,解析 HTML,CSS 构成 Render Tree,布局和绘制等,JS 引擎线程负责解析 Javascript 脚本,执行代码 。这两个线程之间是互斥的,也就是不能并行执行,当遇到 CSS 文件需要下载解析时,JS 引擎线程必须等待 GUI 渲染线程执行完前面的任务后才会执行,所以后面的 js 脚本必须等待前面的 css 文件下载解析完成后再执行。 + +### JS 会阻塞 DOM 解析,也会阻塞 DOM 渲染 + +浏览器在下载解析及执行 JS 脚本(外链和内联)的时候,都会阻塞 DOM 的解析和渲染。这也是因为 GUI 渲染线程 和 JS 引擎线程是互斥的。JS 引擎在执行过程中,GUI 线程会被挂起,GUI更新会被保存在一个队列中等到JS引擎空闲时立即被执行。 + +其实也可以这样理解:浏览器在没下载解析 JS 脚本之前并不知道要进行怎样的操作,如果不阻塞后面 DOM 的解析和渲染,等到后面 DOM 解析渲染完成后,JS 脚本下载完成再执行发现里面有删除 DOM 的操作,那么前面的 DOM 解析渲染流程就变得无意义且会引发回流重绘导致性能下降。所以浏览器需要停止解析渲染,下载并执行 JS 脚本后再继续解析渲染后面的内容。 + +### 小结 + +* css 文件在下载解析时会阻塞后面 DOM 的渲染,但不会阻塞 DOM 的解析,且会延迟后面 JS 脚本的执行 +* js 文件在下载执行时不但会阻塞 DOM 的渲染还会阻塞 DOM 的解析,如果 JS 执行时间过长,会导致页面的渲染不连贯,甚至出现页面空白加载 + +### 优化方案 + +* css 文件放置在 head 标签中,如果涉及到页面加载的样式,就内联样式 +* js 文件放置在 body 尾部,减少 js 解析执行对页面渲染造成的阻塞 +* 合并压缩 css/js 文件,减少 http 请求数和请求体积 +* 使用浏览器缓存,图片资源优化等 + +### 推荐阅读 + +* [css加载会造成阻塞吗](https://github.com/chenjigeng/blog/blob/master/css%E5%8A%A0%E8%BD%BD%E4%BC%9A%E9%80%A0%E6%88%90%E9%98%BB%E5%A1%9E%E5%90%97%EF%BC%9F.md) +* [CSS/JS 阻塞 DOM 解析和渲染](https://harttle.land/2016/11/26/static-dom-render-blocking.html) +* [原来 CSS 与 JS 是这样阻塞 DOM 解析和渲染的](https://juejin.im/post/59c60691518825396f4f71a1) +* [关键渲染路径](https://developers.google.com/web/fundamentals/performance/critical-rendering-path/?hl=zh-cn) +* [CSS 与网络性能](https://mp.weixin.qq.com/s/0fSUaT-Y1b5hmXJ3Q0bmJA) +* [从浏览器多进程到JS单线程,JS运行机制最全面的一次梳理](https://segmentfault.com/a/1190000012925872) \ No newline at end of file diff --git "a/\345\211\215\347\253\257/Browser/\351\207\215\347\273\230\345\233\236\346\265\201.md" "b/\345\211\215\347\253\257/Browser/\351\207\215\347\273\230\345\233\236\346\265\201.md" new file mode 100644 index 0000000..cad20e4 --- /dev/null +++ "b/\345\211\215\347\253\257/Browser/\351\207\215\347\273\230\345\233\236\346\265\201.md" @@ -0,0 +1,7 @@ +## 重绘回流 + +### 参考 + +* [页面重绘和回流以及优化](https://www.css88.com/archives/4996) +* [回流与重绘:CSS性能让JavaScript变慢?](https://www.zhangxinxu.com/wordpress/2010/01/%E5%9B%9E%E6%B5%81%E4%B8%8E%E9%87%8D%E7%BB%98%EF%BC%9Acss%E6%80%A7%E8%83%BD%E8%AE%A9javascript%E5%8F%98%E6%85%A2%EF%BC%9F/) + diff --git "a/\345\211\215\347\253\257/CSS/BFC.md" "b/\345\211\215\347\253\257/CSS/BFC.md" index 2d6b1ca..2ac3cc2 100644 --- "a/\345\211\215\347\253\257/CSS/BFC.md" +++ "b/\345\211\215\347\253\257/CSS/BFC.md" @@ -1,6 +1,28 @@ ## BFC +* BFC 是 web 页面中盒模型布局的 CSS 渲染模式。 +* BFC 相当于创建了一个隔离容器,容器内部元素与外部元素相互不影响。 +* 一个元素不能同时存在于两个 BFC 中。 + +#### 创建 BFC 方式如下,只要满足一个条件即可: + +* 根元素(html 元素) +* 浮动(float 值不为 none) +* 绝对定位(position 值不为 static 和 relative) +* overflow 值不为 visible +* display 值为 table-cell, table-caption, inline-block, flex, inline-flex 中的一个 + +####BFC 的布局规则: + +* 内部的盒模型会在垂直方向按照顺序一个接一个的排列 +* 同一个 BFC 内部的相邻元素的外边距会重叠 +* BFC 相当于页面上的独立隔离容器,里面的子元素和外部元素相互不影响 +* 计算 BFC 容器的高度时,浮动元素也参与计算 +* 同一个 BFC 内部每个盒子的左外边框紧挨着包含块的左边框(从右到左的格式,则为紧挨右边框),即使存在也是这样 + ### 参考 * https://juejin.im/post/59b73d5bf265da064618731d +* https://juejin.im/post/5909db2fda2f60005d2093db +* [理解CSS中BFC](https://www.w3cplus.com/css/understanding-block-formatting-contexts-in-css.html) diff --git "a/\345\211\215\347\253\257/CSS/Demo/BFC.html" "b/\345\211\215\347\253\257/CSS/Demo/BFC.html" new file mode 100644 index 0000000..24cdf9c --- /dev/null +++ "b/\345\211\215\347\253\257/CSS/Demo/BFC.html" @@ -0,0 +1,39 @@ + + + + + + + BFC + + + + +
+

Sibling 1

+

Sibling 2

+ +
+ + + \ No newline at end of file diff --git "a/\345\211\215\347\253\257/CSS/Demo/index.html" "b/\345\211\215\347\253\257/CSS/Demo/index.html" index c22194e..128925a 100644 --- "a/\345\211\215\347\253\257/CSS/Demo/index.html" +++ "b/\345\211\215\347\253\257/CSS/Demo/index.html" @@ -4,7 +4,7 @@ - Document + 多栏布局 + +
header
+
+
+
main
+
+ + +
+ + \ No newline at end of file diff --git "a/\345\211\215\347\253\257/CSS/Demo/layout.html" "b/\345\211\215\347\253\257/CSS/Demo/layout.html" new file mode 100644 index 0000000..34a5231 --- /dev/null +++ "b/\345\211\215\347\253\257/CSS/Demo/layout.html" @@ -0,0 +1,176 @@ + + + + + + + Document + + + + +

水平居中

+
+
DEMO1
+
DEMO2
+
+ +
+
DEMO
+
+ +
+
DEMO
+
+ +
+
DEMO
+
+ + +

垂直居中

+
+
DEMO
+
+ +
+
DEMO
+
+ +
+
DEMO
+
+ + +
+
DEMO
+
+ +
+
DEMO
+
+ +
+
DEMO
+
+ + + \ No newline at end of file diff --git "a/\345\211\215\347\253\257/CSS/Demo/shape.html" "b/\345\211\215\347\253\257/CSS/Demo/shape.html" new file mode 100644 index 0000000..76f858c --- /dev/null +++ "b/\345\211\215\347\253\257/CSS/Demo/shape.html" @@ -0,0 +1,38 @@ + + + + + + + css画几何形状 + + + +
+ +
+
+ + + \ No newline at end of file diff --git "a/\345\211\215\347\253\257/CSS/assets/a5.png" "b/\345\211\215\347\253\257/CSS/assets/a5.png" deleted file mode 100644 index d6267ed..0000000 Binary files "a/\345\211\215\347\253\257/CSS/assets/a5.png" and /dev/null differ diff --git "a/\345\211\215\347\253\257/CSS/assets/holy-grail.png" "b/\345\211\215\347\253\257/CSS/assets/holy-grail.png" new file mode 100644 index 0000000..d27bcfe Binary files /dev/null and "b/\345\211\215\347\253\257/CSS/assets/holy-grail.png" differ diff --git "a/\345\211\215\347\253\257/CSS/\345\234\243\346\235\257\345\270\203\345\261\200&\345\217\214\351\243\236\347\277\274\345\270\203\345\261\200.md" "b/\345\211\215\347\253\257/CSS/\345\234\243\346\235\257\345\270\203\345\261\200&\345\217\214\351\243\236\347\277\274\345\270\203\345\261\200.md" index 0d9c58d..c93b51f 100644 --- "a/\345\211\215\347\253\257/CSS/\345\234\243\346\235\257\345\270\203\345\261\200&\345\217\214\351\243\236\347\277\274\345\270\203\345\261\200.md" +++ "b/\345\211\215\347\253\257/CSS/\345\234\243\346\235\257\345\270\203\345\261\200&\345\217\214\351\243\236\347\277\274\345\270\203\345\261\200.md" @@ -6,8 +6,114 @@ ### 圣杯布局 +圣杯布局实现思路大致是: + +* 三块内容都要设置向左浮动 +* 中间内容宽度设置成 100%,左右两边固定宽度 +* 左右两块内容设置负边距,将左右两侧内容和中间内容放置在同一行显示 +* 左右两侧内容出现遮挡了中间内容的现象,通过设置内容块的父级元素的 padding 值使中间内容宽度变小 +* 左右两侧内容通过相对定位,分别设置 left 和 right 值将内容设置到左右两侧的位置 + +实现代码如下: + +```html +
header
+
+
main
+ + +
+ + + +``` + +![](./assets/holy-grail.png) + +### 双飞翼布局 + +双飞翼布局的实现思路不同于圣杯布局的思路在于左右两侧内容遮挡中间内容的处理。圣杯布局的处理方式是通过设置内边距预留出左右两侧的位置,并使用相对定位配合 left 和 right 将两侧内容放置到左右两侧;双飞翼的处理方式是多创建一层 DOM 将中间内容包裹起来,通过设置包裹元素的外边距空留出左右两侧的位置。 + +实现代码如下: + +```html +
header
+
+
+
main
+
+ + +
+ + + +``` + ### 参考 * https://alistapart.com/article/holygrail -* https://www.jianshu.com/p/f9bcddb0e8b4 -* https://github.com/zwwill/blog/issues/11 \ No newline at end of file +* [圣杯布局和双飞翼布局](https://www.jianshu.com/p/f9bcddb0e8b4) +* [聊聊为什么淘宝要提出「双飞翼」布局](https://github.com/zwwill/blog/issues/11) +* [圣杯布局的实现原理](https://www.zybuluo.com/xudongh/note/557250) +* [CSS布局中圣杯布局与双飞翼布局的实现思路差异在哪里?](https://www.zhihu.com/question/21504052) +* [双飞翼布局介绍-始于淘宝UED](http://www.imooc.com/wenda/detail/254035) \ No newline at end of file diff --git "a/\345\211\215\347\253\257/CSS/\345\270\203\345\261\200.md" "b/\345\211\215\347\253\257/CSS/\345\270\203\345\261\200.md" new file mode 100644 index 0000000..50f060d --- /dev/null +++ "b/\345\211\215\347\253\257/CSS/\345\270\203\345\261\200.md" @@ -0,0 +1,31 @@ +## 布局 + +### 水平居中 + +1. 将子元素设置为行内元素,再设置父元素居中,子元素的宽度不超过父元素的宽度有效。 +2. 子元素是块级元素,设置margin: 0 auto;子元素的宽必须固定子元素的宽度不超过父元素的宽度有效。 +3. 使用绝对定位+transform。父元素设置相对定位,子元素使用绝对定位,子元素相对父元素设置left向左偏移50%,再设置transform使子元素向左偏移自身尺寸的一半。 +4. 使用 flex 布局。将父元素设置为 display:flex,再将主轴上的对齐方式设置为居中。 + +### 垂直居中 + +1. 给父元素设置行高。前提是必须知道父元素的高度,且子元素只能是单行行内内容。 +2. 使用绝对定位+transform。元素设置相对定位,子元素使用绝对定位,子元素相对父元素设置top向上偏移50%,再设置transform使子元素向上偏移自身尺寸的一半。 +3. 使用flex布局,将父元素设置为display:flex,再将交叉轴上的对齐方式设置为居中。 + +### 水平垂直居中 + +1. 绝对定位,父元素设置相对定位,子元素设置绝对定位,子元素相对父元素设置top向上偏移50%,left向左偏移50%,再设置transform使子元素向左,向上偏移自身尺寸的一半。 +2. 绝对居中,父元素设置相对定位,子元素设置绝对定位,子元素的top,bottom,left,right设置为0,再设置margin:auto,让浏览器自动平分空间。 +3. flex,父元素设置为flex布局,子元素设置margin:auto,让浏览器自动平分空间。 + +### 推荐阅读 + +[Flex 布局教程:语法篇](http://www.ruanyifeng.com/blog/2015/07/flex-grammar.html) + +[各种常见布局实现+知名网站实例分析](https://juejin.im/post/5aa252ac518825558001d5de) + +[CSS布局解决方案(终结版)](https://segmentfault.com/a/1190000013565024) + + + diff --git "a/\345\211\215\347\253\257/ECMAScript/DEMO/you-may-not-know/index.html" "b/\345\211\215\347\253\257/ECMAScript/DEMO/you-may-not-know/index.html" index 127f255..e0bc6f0 100644 --- "a/\345\211\215\347\253\257/ECMAScript/DEMO/you-may-not-know/index.html" +++ "b/\345\211\215\347\253\257/ECMAScript/DEMO/you-may-not-know/index.html" @@ -7,6 +7,8 @@ 可能不知道的知识点 + 跳转百度 + + + + + \ No newline at end of file diff --git "a/\345\211\215\347\253\257/ECMAScript/\344\270\200\344\272\233\344\270\215\345\270\270\344\275\277\347\224\250\347\232\204API.md" "b/\345\211\215\347\253\257/ECMAScript/\344\270\200\344\272\233\344\270\215\345\270\270\344\275\277\347\224\250\347\232\204API.md" index 52c89a2..3692b41 100644 --- "a/\345\211\215\347\253\257/ECMAScript/\344\270\200\344\272\233\344\270\215\345\270\270\344\275\277\347\224\250\347\232\204API.md" +++ "b/\345\211\215\347\253\257/ECMAScript/\344\270\200\344\272\233\344\270\215\345\270\270\344\275\277\347\224\250\347\232\204API.md" @@ -1,6 +1,6 @@ ## 记录一些不常使用的 API 或知识点 -### console +### 1. console 总结下 console 一些常用的 API。 @@ -108,4 +108,70 @@ console.timeEnd('timer'); #### 参考 -https://segmentfault.com/a/1190000002511877 \ No newline at end of file +https://segmentfault.com/a/1190000002511877 + +------------------------------------------------------------------------------------------------------- + +### 2. visibilitychange + +visibilitychange 用于监听浏览器标签页显示或隐藏操作,用户切换页面的时候或者窗口最小化和非最小化的时候触发该事件,从 a 页面切换到 b 页面表示 a 页面处于隐藏状态,再切换回 a 页面处于显示状态。 + +visibilitychange 事件只是用于监听页面切换,那么如何判断页面切换是显示状态还是隐藏状态呢? + +通过 ```document.hidden``` 来判断页面的状态(显示还是隐藏) + +```javascript +document.addEventListener('visibilitychange', function() { + if(document.hidden) { + document.title = '页面隐藏起来了'; + }else { + document.title = '页面显示出来了'; + } +}) +``` + +#### 参考 + +https://developer.mozilla.org/zh-CN/docs/Web/API/Page_Visibility_API + +----------------------------------------------------------------------------------------------------------- + +### 3. 关于浏览器关闭刷新状态 + +有一些应用场景,比如表单提交,用户在填写过程中无意刷新或者关闭页面,导致填写的内容丢失,如果只是一些简单的表单填写还好,要是遇到复杂的表单填写,不得不重新填写,这样用户体验很不好。那么有没有办法在用户刷新或者关闭页面的时候,给个提示让用户确认操作正确呢,从而避免无效的失误。 + +#### onbeforeunload + +MDN: 当窗口即将被卸载(关闭)时,会触发该事件.此时页面文档依然可见,且该事件的默认动作可以被取消。 + +简单来说就是刷新重新加载页面或者关闭页面窗口的时候,会先触发 onbeforeunload 事件,让用户再次确认是否关闭或者刷新页面,如果是失误操作可以动作取消。 + +onbeforeunload 事件需要返回一个字符串才能有效,字符串将作用在确认框上显示(从Firefox 4、 Chrome 51、Opera 38 和Safari 9.1开始,通用确认信息代替事件返回的字符串,不过返回字符串步骤不能缺失) + +```javascript +window.onbeforeunload = function (e) { + // Chrome, Safari, Firefox 4+, Opera 12+ , IE 9+ + return '关闭提示'; +}; +``` + +需要注意的是,经过测试,在 Chrome 下必须要存在 input 这样的表单输入框且必须输入内容,在关闭窗口的时候才会弹出确认窗口,否则不需要用户确认直接关闭窗口;而 IE 下便没有这样的限制。测试环境: Chrome/70,IE9。 + +#### onunload + +查阅资料的时候,网上很多文章都会顺带提到 onunload 事件,经过测试,该事件在 Chrmoe 下无效。 + +onunload 事件类似于 onbeforeunload 事件,当窗口即将被卸载(关闭)时,会触发。不同的是,onunload 更像是提示用户页面被关闭了或者页面刷新了,只是一个提醒的作用,并不会让用户进行确认操作是否正确。 + +```javascript +window.onunload = function(e) { + alert("unload event detected!"); +} +``` + +#### 参考 + +https://developer.mozilla.org/zh-CN/docs/Web/API/Window/onbeforeunload + +---------------------------------------------------------------------------------------------------------------- + diff --git "a/\345\211\215\347\253\257/Frame/Vue/vue\345\217\214\345\220\221\346\225\260\346\215\256\347\273\221\345\256\232.md" "b/\345\211\215\347\253\257/Frame/Vue/vue\345\217\214\345\220\221\346\225\260\346\215\256\347\273\221\345\256\232.md" index 7a1e159..6589b76 100644 --- "a/\345\211\215\347\253\257/Frame/Vue/vue\345\217\214\345\220\221\346\225\260\346\215\256\347\273\221\345\256\232.md" +++ "b/\345\211\215\347\253\257/Frame/Vue/vue\345\217\214\345\220\221\346\225\260\346\215\256\347\273\221\345\256\232.md" @@ -1,13 +1,87 @@ ## Vue 双向数据绑定 -Vue 双向数据绑定实现的核心是 Object.defineProperty 对属性的劫持,当数据变动时,Object.defineProperty 会劫持到数据的变化并通知到订阅者,触发相应的操作。 +### 什么是双向数据绑定 + +双向数据绑定是对 MVVM 中的 viewModel 的实现,简单说就是数据驱动视图,视图修改数据。视图与数据分离解耦,视图的显示状态和数据保持一致,视图的修改会同步更新数据,数据的变化也会同步更新视图的显示状态。 + +### 双向数据绑定的实现方式 + +1. 观察者模式 + + 观察者模式其实是定义的一种一对多的依赖关系,由主体和订阅者组成,主体发布订阅消息,所有依赖于该主体的订阅者都将得到通知,当然订阅者也可以取消对主体的订阅。这就类似于微信公众号,主体是公众号,用户是订阅者,当公众号推送消息时,所有关注了公众号的用户都将看到推送的消息。 + + 观察者模式的实现大致是这样:定义一个主体类,然后在主体类上添加收集订阅者,删除订阅者,发布消息等方法,通过维护一个数组来依赖收集订阅者,当发布消息的时候,遍历订阅者数组一一通知。 + + 在开源的移动端手势库AlloyFinger中,就使用到了观察者模式来收集所有的手势操作。 + + 关于[观察者模式](https://github.com/webproblem/Blog/blob/master/%E5%89%8D%E7%AB%AF/ECMAScript/%E8%A7%82%E5%AF%9F%E8%80%85%E6%A8%A1%E5%BC%8F.md) + +2. AngularJs 脏值检查 + + AngularJs 就是通过脏值检查的机制来对比前后数据是否一致,以此来觉得是否更新视图,大致就是记录下所有变量的当前值,当发生某些操作的时候,进入到脏值检查的环节,对比最近一次的值和现在的值是否一致,不一致就更新视图。 + + angular只有在指定的事件触发时进入脏值检测,大致如下: + + DOM事件、XHR响应事件、延迟事件等 + +3. VueJs 观察者模式+数据劫持 + + 数据劫持主要是通过ES5的Object.defineProperty进行属性劫持,监听数据变动,通过存取描述符(另一种是数据描述符)getter和setter可以观察到对象属性的读取和赋值。当数据变动时,Object.defineProperty 会劫持到数据的变化并通知到订阅者,触发相应的操作。 ## 实现思路 +首先思考几个问题: + +1. 如何知道Model的数据变化 +2. Model数据变化如何通知视图更新,也就是model => view 的变化 +3. 视图的修改如何通知Model数据更新,也就是view => model的变化 + * 实现一个 Observer 数据监听器,通过使用 Object.defineProperty 对所有属性进行劫持监听,数据变动时通知订阅者。 * 实现一个 Compile 指令解析器,对每个元素上的指令进行解析,对数据进行初始化绑定。 * 实现一个 Watcher 订阅器,作为 Observer 和 Compile 之间的桥梁, -![image](https://user-images.githubusercontent.com/20440496/42452395-6eb0219a-83bc-11e8-90e8-6571ef756f27.png) \ No newline at end of file +![image](https://user-images.githubusercontent.com/20440496/42452395-6eb0219a-83bc-11e8-90e8-6571ef756f27.png) + +第一步:可以通过Object.defineProperty对属性进行劫持监听,可以观察到数据的变化,也就是实现一个数据监听器Observer函数,Observer函数接受的参数就是new Vue构造函数时传入的data选项。函数内部通过递归遍历,取出所有的属性,然后通过对所有属性的劫持来观察数据的变化,可以再封装一个defineReactive函数来劫持属性监听数据。 + +第二步:实现一个消息订阅主体Dep,主要用来收集订阅者。这里涉及到了一个知识点:依赖收集,为什么需要依赖收集:因为可能存在data选项中的属性并没有在视图中使用到,比如存在a,b属性,视图中只用到了a属性,b属性没有使用到,那么在逻辑代码中如果修改了b属性的值,其实是没必要去触发视图更新函数的,所以依赖收集只涉及到视图中使用到了的属性。回到Dep主体对象,分别定义收集订阅者,删除订阅者和发布订阅消息的方法,订阅者就是Watcher,后面会讲到,当数据变动时,在属性劫持的setter中发布消息通知watcher订阅者进行视图更新。 + +依赖收集的前提条件有两个:触发getter,新建一个watcher对象。在指令解析编译模板阶段会读取对象属性,所有会触发getter,同时在解析指令时给相关的节点作为订阅者绑定更新函数,数据变动就通知更新视图。 + +第三步:实现指令解析器Compile,用来编译模板,解析指令以及语法糖,然后初始化渲染视图,并将指令相关的节点绑定更新函数,添加监听数据的订阅者,数据一变动收到通知更新视图。出于操作DOM性能的考虑,使用DocumentFragment性能更好,在编译模板过程中,使用DocumentFragment作为容器对子节点进行递归遍历处理,可以将所有的子节点一次性插入到DOM中。 + +第四步:实现订阅者Watcher,将自身添加到Dep的依赖收集中,定义一个update函数,用于在数据修改之后调用更新视图。 + +## ES5环境中实现const + +Object.defineProperty属性描述符可以用来简单模拟实现const的功能,属性描述符分为存取描述符和数据描述符,数据描述符包括是否可枚举,是否可修改删除的属性。比如简单实现全局下的const a常量,可以这样实现,定义一个函数,接受赋值的参数,然后在window下添加a属性,将是否可枚举是否可修改删除属性都设置为false,读取的时候触发getter的时候返回结果,修改的时候触发setter,判断前后的值是否一致,不一致就抛出错误,提示不能修改值。 + +## vue3.0使用proxy实现双向绑定 + +vue3.0中将使用ES6的proxy代替Object.defineProperty实现双向数据绑定,主要是因为Object.defineProperty存在一些缺陷: + +1. 对数组的监测存在一定的局限性 +2. 只能劫持对象的属性,如果对象的属性是嵌套对象的,只能递归遍历一层层的对属性劫持,如果能劫持一个完整的对象,性能上能有很大的提升 + +proxy的优势: + +1. 可以劫持完整的对象 +2. 有13种劫持的方法 + +## 推荐参考 + +https://zhuanlan.zhihu.com/p/25464162 + +[Object.defineProperty-MDN](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty) + +[createDocumentFragment-MDN](https://developer.mozilla.org/zh-CN/docs/Web/API/Document/createDocumentFragment) + +https://www.cnblogs.com/kidney/p/6052935.html + +https://github.com/DMQ/mvvm + +https://juejin.im/post/5c2042e95188255e9b620964 + +https://juejin.im/post/5bf3e632e51d452baa5f7375 \ No newline at end of file diff --git "a/\345\211\215\347\253\257/Frame/Vue/vue\347\224\237\345\221\275\345\221\250\346\234\237.md" "b/\345\211\215\347\253\257/Frame/Vue/vue\347\224\237\345\221\275\345\221\250\346\234\237.md" new file mode 100644 index 0000000..2d0e09a --- /dev/null +++ "b/\345\211\215\347\253\257/Frame/Vue/vue\347\224\237\345\221\275\345\221\250\346\234\237.md" @@ -0,0 +1,40 @@ +## Vue 生命周期 + +Vue 的生命周期主要是这些钩子函数:beforeCreated, created, beforeMount, mounted, beforeUpdata, updated, beforeDestory, destoryed。实例创建前后,挂载前后,更新前后,销毁前后。 + +### 生命周期详解 + +在实例创建阶段,主要是进行事件初始化,数据监测等,new vue 的时候,内部会触发 init 函数进行初始化。 + +在beforeCreate阶段也就是实例初始化但是还没创建完成的时候,数据监测和事件初始化还没完成,不能对任何数据访问操作。 + +created阶段,实例创建完成,数据监测和事件初始化也都完成,可以访问和操作data数据,但是在这一步,$el还获取不到,这一步可以开始一些数据请求的操作。 + +实例创建完成后,就进入到挂载阶段,在挂载阶段就可以访问 $el 的值了。要说明的是进入挂载阶段前,会先判断传入的配置项中是否有 el 属性,如果有才会继续初始化,如果没有初始化会暂停,等到执行了 vm.$mount(el) 后才会继续。在 beforeMount 阶段,内部会找到 template 配置项作为模板进行编译成 render 函数,如果没有template 配置项,则使用外部 HTML 作为模板,如果有直接配置 render 函数,则直接使用配置的 render 函数,优先级是:render 函数选项-template选项-外部HTML。并且在 beforeMount 阶段,$el 获取的值还是双大括号的 vue 语法,只是个占位符,内容还没有被替换掉。 + +当执行完 render function之后,到了mounted阶段,el被创建的vm.$el替换掉,挂载完成。 + +到这里为止,初始化过程中会触发上面这些钩子函数,更新和销毁钩子函数都需要手动触发的。 + +当修改 data 选项中的数据时,会触发 beforeUpdate 和 updated,虚拟 DOM 按照 DIFF 算法重新渲染和打补丁,最后 DOM 更新完成后进入到 updated 阶段。 + +beforeDestory 是在vue实例销毁之前调用,在这里,实例仍然可以访问操作,一般在这一步中进行:销毁定时器、解绑全局事件、销毁插件对象等操作。 + +destoryed 在实例销毁后调用,所有指令都将会解绑,事件监听移除,子实例也会销毁。 + +![image](https://user-images.githubusercontent.com/20440496/51070161-87fdfe80-1677-11e9-8d1d-67f1b86bad0a.png) + +### 推荐阅读 + +[Vue2.0生命周期(组件钩子函数与路由守卫)](https://segmentfault.com/a/1190000013956945) + +[详解vue生命周期](https://segmentfault.com/a/1190000011381906) + +[如何解释vue的生命周期才能令面试官满意?](https://juejin.im/post/5ad10800f265da23826e681e) + +[vue 生命周期深入](https://juejin.im/entry/5aee8fbb518825671952308c) + +[vue文档-生命周期](https://cn.vuejs.org/v2/api/#%E9%80%89%E9%A1%B9-%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F%E9%92%A9%E5%AD%90) + +[Vue 实例中的生命周期钩子详解](https://github.com/koucxz/blog/issues/3) + diff --git "a/\345\211\215\347\253\257/Interview/Demo/interview.html" "b/\345\211\215\347\253\257/Interview/Demo/interview.html" new file mode 100644 index 0000000..f186606 --- /dev/null +++ "b/\345\211\215\347\253\257/Interview/Demo/interview.html" @@ -0,0 +1,19 @@ + + + + + + + Document + + + + + \ No newline at end of file diff --git "a/\345\211\215\347\253\257/Interview/JavaScript.md" "b/\345\211\215\347\253\257/Interview/JavaScript.md" index 6dfeef6..b7ebdd6 100644 --- "a/\345\211\215\347\253\257/Interview/JavaScript.md" +++ "b/\345\211\215\347\253\257/Interview/JavaScript.md" @@ -43,7 +43,7 @@ * [https://segmentfault.com/a/1190000000411840](https://segmentfault.com/a/1190000000411840) ------------------------------------------------------------------------ +------------------------------------------------------------------ ### 2. var arr = ['a', 'b', 'c', 'd', 'c']; 实现数组乱序 @@ -147,6 +147,8 @@ function shuffle(arr) { * https://github.com/hanzichi/underscore-analysis/issues/15 * https://blog.oldj.net/2017/01/23/shuffle-an-array-in-javascript/ +-------------------------------------------------------------------------------------------------- + ### 3. var arr = ['a', 'b', 'c', 'd', 'c']; 实现数组倒序(非 reverse) > ### 解答 @@ -191,9 +193,200 @@ function reverse(arr) { return arr; } ``` ------------------------------------------------------------------------ +-------------------------------------------------------------- + +### 4. 如何判断两个对象相等 + +> ### 解答 + +首先要清楚 JavaScript 中的相等分为宽松相等(==)和严格相等(===)。宽松相等在比较值的时候会先进行类型的隐式转换,严格相等下如果比较值的类型不一致,那么就判定比较值不全等。如果比较值是引用类型,宽松相等和严格相等就不能判断出值是否相等了(引用类型浅拷贝比较值除外,也就是比较值指向的是同一引用地址),原因是对于任意两个不同的非原始对象,即便他们有相同的结构,都会计算得到 false 。 + +```javascript +var num = 1; +var str = '1'; +console.log(num == str); // true +console.log(num === str); // false + +var obj1 = {name: '白展堂'}; +var obj2 = {name: '白展堂'}; +var obj3 = obj1; +console.log(obj1 == obj2); // false +console.log(obj1 === obj2); // false +console.log(obj1 == obj3); // true +console.log(obj1 === obj3); // true + +var arr1 = [1]; +var arr2 = [1]; +console.log(arr1 == arr2); // false +console.log(arr1 === arr2); // false +``` + +#### JSON.stringify + +如何判断对象是否相等? + +一种解决方案就是使用 JSON.stringify 序列化成字符串再做比较。 + +```javascript +var obj1 = {name: '白展堂', age: 25}; +var obj2 = {name: '白展堂', age: 25}; +JSON.stringify(obj1) === JSON.stringify(obj2); // true + +var arr1 = ['a', 'b', 'c', 'd']; +var arr2 = ['a', 'b', 'c', 'd']; +JSON.stringify(arr1) === JSON.stringify(arr2); // true +``` + +这种方案看似可以判断出对象是否相等,但是会不会存在问题呢?看过 underscore 源码的都知道,isEqual 函数的实现有多复杂,很多种情况显然不是通过 JSON.stringify 序列化就能解决的。 + +先来分析下 JSON.stringify 方案存在的问题,假设比较对象中的属性值存在 RegExp 对象,判定结果是怎样的呢? + +```javascript +function eq(a, b) { + return JSON.stringify(a) === JSON.stringify(b); +} +var obj1 = {name: '白展堂', reg: /test1/i}; +var obj2 = {name: '白展堂', reg: /test2/i}; +eq(obj1, obj2); // true +``` + +结果为 true,也就是说 obj1 和 obj2 序列化的字符串是一致的。 + +```javascript +var obj1 = {name: '白展堂', reg: /test1/i}; +var obj2 = {name: '白展堂', reg: /test2/i}; +JSON.stringify(obj1); // "{"name":"白展堂","reg":{}}" +JSON.stringify(obj2); // "{"name":"白展堂","reg":{}}" +``` + +可以看到,JSON.stringify 将 RegExp 对象序列化成了 '{}',也就是说 JSON.stringify 序列化对于某些情况会存在问题,比如 undefined 和 Function 函数在序列化过程中会被忽略。 + +```javascript +function test() {} +JSON.stringify(undefined) === JSON.stringify(test); // true +``` + +#### 实现 + +那么如何完美的判断对象或值相等,可以读下 underscore 中的 isEqual 函数源码,这里说下大致思路。 + +**区分 0 与 -0 之间的差异** + +0 与 -0 看似相等吗,其实不然。 + +```javascript +1 / 0 // Infinity +1 / -0 // -Infinity +1 / 0 === 1 / -0 // false +``` + +区分方式: + +```javascript +function eq(a, b) { + if(a === b) { + return a !== 0 || 1 / a === 1 / b; + } + return false; +} +eq(0, 0); // true +eq(0, -0); // false +``` +**判断值是否为 NaN** +判断某个值是否为 NaN 时不能直接比较这个值是否等于 NaN,因为 NaN 不等于自身,可以使用原生函数 Number.isNaN() 或 isNaN()。 + +```javascript +var a = NaN; +a === NaN; // false +isNaN(a); // true +``` + +那么自己如何实现判断 NaN 值的方法?利用 NaN 不等于自身的原理。 + +```javascript +function eq(a, b) { + if(a !== a) return b !== b; +} +eq(NaN, NaN); //true +eq(NaN, 'test'); // false +``` + +**隐式类型转换** + +对于 RegExp,String,Number,Boolean 等值的比较先进行隐式类型转换。 + +**递归遍历** + +对于数组和对象的比较值,采用递归遍历比较,需要注意的是可能存在循环引用的问题。 + +#### 参考 + +* https://blog.csdn.net/qq_30100043/article/details/53419801 +* https://github.com/mqyqingfeng/Blog/issues/41 +* https://github.com/hanzichi/underscore-analysis/issues/5 +* https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Equality_comparisons_and_sameness +* https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify + +------------------------------------------------------------------------------------------------ + +### 5. 如何判断一个变量是数组还是对象 + +> ### 解答 + +这里主要考察的是对类型判断的掌握。 + +#### typeof + +typeof 操作符会返回字符串形式的数据类型。JavaScript 有 6 中基本数据类型(Undefined, Null, String, Number, Boolean, Symbol)和 1 种引用类型(Object),使用 typeof 操作符对这些数据类型操作时,返回的结果分别是:undefined, object, string, number, boolean, symbol, object。Null 类型的变量通过 typeof 操作符得到的结果是 object,也就是说 typeof 操作符判断类型并不准确,同样的如: Date, RegExp, Array 等类型。 + +```javascript +var a = null; +typeof a; // object + +var arr = []; +typeof arr; // object + +var date = new Date(); +typeof date; // object +``` + +#### Object.prototype.toString + +另一种完美的解决方案是 Object.prototype.toString!通过 toString 来获取数据的类型,需要以 call 或者 apply 的方式来调用对数据进行检测,返回的结果是由 [object, class] 组成的。关于该方法的具体描述,参考: 。 + +```javascript +var toString = Object.prototype.toString; +toString.call(null); // [object Null] +toString.call([]); // [object Array] +toString.call(new Date()); // [object Date] +``` + +那么如何来封装一个判断变量类型的函数 + +```javascript +/** + * @param obj 需要判断类型的变量 + * @param type 判断的类型 +*/ +function type(obj, type) { + var classType = {}; + var result = null; + ['Function', 'Array', 'Date', 'RegExp', 'Null', 'Object', 'Error'].map(function(item) { + classType['[object '+item+']'] = item.toLowerCase(); + }) + result = typeof obj === 'function' || typeof obj === 'object' ? classType[Object.prototype.toString.call(obj)] : typeof obj; + return result === type; +} + +type([], 'array'); // true +type([], 'object'); // false +``` + +#### 参考 +* https://github.com/mqyqingfeng/Blog/issues/28 +-------------------------------------------------------------------------------- diff --git "a/\345\211\215\347\253\257/Interview/\344\270\200\344\272\233\345\237\272\347\241\200\347\232\204\351\242\230.md" "b/\345\211\215\347\253\257/Interview/\344\270\200\344\272\233\345\237\272\347\241\200\347\232\204\351\242\230.md" new file mode 100644 index 0000000..4d834ee --- /dev/null +++ "b/\345\211\215\347\253\257/Interview/\344\270\200\344\272\233\345\237\272\347\241\200\347\232\204\351\242\230.md" @@ -0,0 +1,92 @@ +### css3 新特性有哪些?css 选择器和优先级?哪些 css 属性可以继承,哪些不可以继承? + +css3新特性主要有:动画,2D/3D旋转,圆角,阴影,文字特效等。 + +css选择器:class,id,伪类,属性选择器,关系选择器 + +优先级:!import,内联,id,class,属性,元素,通配符 + +### css 画圆,画三角形,画扇形 + +圆形:元素宽高一致,设置元素的边框圆角为宽高的一半 + +```css +#circle { + width: 100px; + height: 100px; + border-radius: 50px; + background-color: red; +} +``` + +三角形:元素宽高为0,使用border边框实现 + +```css +#triangle { + width: 0; + height: 0; + border-width: 100px; + border-style: solid; + border-color: transparent transparent transparent red; +} +``` + +### 行内元素、块元素有哪些? + +行内元素:a,span,img,label,input,i + +块元素:div,p,h1-h6,ul,ol,table,form + +行内元素,块元素,行内块元素的区别: + +块元素独占一行,可以设置宽高,内边距和外边距 + +行元素不会独占一行,相邻行内元素可以排在同一行,设置宽高无效 + + 行内块元素实际就是把块元素以行的形式展现,保留了块元素可以设置的对应CSS属性 + +### HTML5 新特性有哪些? + +video,radio音频视频,canvas,article,header,footer,nav,section新标签,存储localStorage ,sessionStorage + +### jpg, png 的区别 + +### href,src的区别 + +href用于在当前文档和引用资源之间确立关系,src用于替换当前元素。 + +在link标签中使用href属性引入css资源,浏览器在解析的时候会并行下载css文件资源 + +src指向的是外部资源位置,指向的内容会嵌套到文档中当前标签所在的位置 + +### Vue 组件传参(父子,兄弟) + +子组件通过props方法接受父组件的数据 ,子组件通过$emit方法向父组件传参 + +### MVVM 模式 + +### GET 和 POST 的区别 + +https://zhuanlan.zhihu.com/p/25028045 + +### DOM 事件流,事件委托 + +DOM事件流的三个阶段: + +1. 事件捕获阶段 +2. 处于目标阶段 +3. 事件冒泡阶段 + +https://segmentfault.com/a/1190000003497939 + +https://segmentfault.com/a/1190000004463384 + +### js继承 + +https://www.cnblogs.com/humin/p/4556820.html + +### js排序 + +https://www.cnblogs.com/dushao/p/6004883.html + +https://www.cnblogs.com/liyongshuai/p/7197962.html \ No newline at end of file diff --git "a/\345\211\215\347\253\257/Other/\344\273\243\347\240\201\347\211\207\346\256\265 - JS.md" "b/\345\211\215\347\253\257/Other/\344\273\243\347\240\201\347\211\207\346\256\265 - JS.md" index a707b5b..4978ea2 100644 --- "a/\345\211\215\347\253\257/Other/\344\273\243\347\240\201\347\211\207\346\256\265 - JS.md" +++ "b/\345\211\215\347\253\257/Other/\344\273\243\347\240\201\347\211\207\346\256\265 - JS.md" @@ -85,5 +85,51 @@ function getOneStyle(element, cssParam) { } ``` +### 倒计时 + +#### 验证码倒计时 + +```javascript +function limitTime() { + var codeText = ''; + var defaultTime = 60; + var time = setInterval(function(){ + if(defaultTime <= 0){ + clearInterval(time); + // 一些操作... + return; + } + codeText = (defaultTime--) + "s"; + }, 1000) +} +``` + +#### 时分秒倒计时 + +```html + +``` + +```javascript +const timeWrap = document.getElementById("time"); +let curDate = new Date().getTime(); +function countdown(){ + let getDate = new Date().getTime(); + const LIMIT_TIME = 2 * 3600 * 1000; + let limitDate = (LIMIT_TIME + curDate) - getDate; + const hours = parseInt(limitDate / 1000 / 60 / 60 % 24 , 10); + const minutes = parseInt(limitDate / 1000 / 60 % 60, 10); + const seconds = parseInt(limitDate / 1000 % 60, 10); + timeWrap.innerHTML = hours + ":" + minutes + ":" + seconds; + let times = setTimeout(function(){ + if(limitDate < 1){ + clearTimeout(times); + return; + } + countdown(); + },1000) +} +``` +