├── images ├── 盒模型.webp ├── 01-冒泡.png ├── 02-捕获.png ├── hybrid.png ├── render.png ├── JS和客户端通讯.png ├── 03-事件流三个阶段.png └── code.render.png ├── README.md ├── 0. 面试准备.md ├── 9. 算法类.md ├── 13. 错误监控类.md ├── 8. 安全问题:CSRF和XSS.md ├── JavaScript高级面试 ├── 2. 原型.md ├── 6. hybrid.md ├── 3. 异步.md ├── 1. ES6.md ├── 4. 虚拟DOM.md └── 5. MVVM和Vue.md ├── 10. 渲染机制.md ├── 12. 页面性能类.md ├── 11. JS运行机制:异步和单线程.md ├── 2. CSS盒模型.md ├── 6. 类与继承.md ├── 3. DOM事件.md ├── 4. http协议类.md ├── 1. 页面布局.md ├── 5. 面向对象和原型链.md └── 7. 跨域通信类.md /images/盒模型.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cocytus1223/imooc-frontend-interview/HEAD/images/盒模型.webp -------------------------------------------------------------------------------- /images/01-冒泡.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cocytus1223/imooc-frontend-interview/HEAD/images/01-冒泡.png -------------------------------------------------------------------------------- /images/02-捕获.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cocytus1223/imooc-frontend-interview/HEAD/images/02-捕获.png -------------------------------------------------------------------------------- /images/hybrid.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cocytus1223/imooc-frontend-interview/HEAD/images/hybrid.png -------------------------------------------------------------------------------- /images/render.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cocytus1223/imooc-frontend-interview/HEAD/images/render.png -------------------------------------------------------------------------------- /images/JS和客户端通讯.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cocytus1223/imooc-frontend-interview/HEAD/images/JS和客户端通讯.png -------------------------------------------------------------------------------- /images/03-事件流三个阶段.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cocytus1223/imooc-frontend-interview/HEAD/images/03-事件流三个阶段.png -------------------------------------------------------------------------------- /images/code.render.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cocytus1223/imooc-frontend-interview/HEAD/images/code.render.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # frontend-interview 2 | 3 | 前端面试题总结 4 | 5 | ## 面试准备 6 | 7 | 1. 职位分析 8 | 2. 业务分析或实战模拟 9 | 3. 技术栈准备 10 | 4. 自我陈述 11 | 12 | ## 一面二面 13 | 14 | 1. 页面布局 15 | 2. CSS 盒模型 16 | 3. DOM 事件 17 | 4. HTTP 协议 18 | 5. 原型链 19 | 6. 面向对象 20 | 7. 通信类 21 | 8. 安全类 22 | 9. 算法类 23 | 24 | ## 二面三面 25 | 26 | 1. 渲染机制 27 | 2. JS 运行机制 28 | 3. 页面性能 29 | 4. 错误监控 30 | 31 | ## 三面四面 32 | 33 | 1. 业务能力 34 | 2. 团队协作能力与带人能力 35 | 36 | ## HR 面 37 | 38 | 1. 职业竞争力 39 | 2. 职业规划 40 | -------------------------------------------------------------------------------- /0. 面试准备.md: -------------------------------------------------------------------------------- 1 | # 面试准备 2 | 3 | ## 如何看待面试 4 | 5 | 面试是测查和评价人员能力素质的一种考试活动。具体地说,面试是一种经过组织者精心设计,在特定场景下,以考官对考生的面对面交谈与观察为主要手段,由表及里测评考生的知识、能力、经验等有关素质的一种考试活动。 6 | 7 | ## 职位描述(JD)分析 8 | 9 | 两个初衷 10 | 11 | 1. 快速识别这个岗位是不是喜欢的,想要的 12 | 2. 通过对 JD 的分析,假定这个岗位就是我需要的,我需要哪些准备,准备到什么程度,能不能快速 hold 住这个岗位 13 | 14 | 职位描述注重的是你的工作职责 15 | 任职要求是看你的技术深度,工作能力 16 | 17 | ## 业务分析或实战模拟 18 | 19 | ## 技术栈准备 20 | 21 | jQuery、Vue、React、Angular 22 | 23 | 前端工程相关:sass、less、webpack、npm 24 | 25 | ## 自我介绍 26 | 27 | 简历 28 | 29 | - 基本信息,姓名-年龄-手机-邮箱-籍贯 30 | - 学历 31 | - 工作经历 32 | - 开源项目,GitHub 33 | 34 | 自我陈述 35 | 36 | - 把握面试的沟通方向 37 | - 豁达、自信的适度发挥 38 | -------------------------------------------------------------------------------- /9. 算法类.md: -------------------------------------------------------------------------------- 1 | # 算法类 2 | 3 | 算法主要包括: 4 | 5 | - 1、排序 6 | 7 | - 2、堆栈、队列、链表 8 | 9 | - 3、递归 10 | 11 | - 4、波兰式和逆波兰式 12 | 13 | ## 排序 14 | 15 | 常用排序算法: 16 | 17 | 18 | - 快速排序: 19 | - 选择排序: 20 | - 希尔排序: 21 | 22 | - 冒泡排序: 23 | 24 | ## 堆栈、队列、链表 25 | 26 | 参考链接: 27 | 28 | - 29 | 30 | 上面这个链接是转载的。原创博主的系列文章是: 31 | 32 | - [数组、队列、链表]() 33 | - [排序]() 34 | 35 | ## 递归 36 | 37 | - [JavaScript 中的递归](https://segmentfault.com/a/1190000009857470) 38 | 39 | ## 波兰式和逆波兰式 40 | 41 | - 理论: 42 | - 源码: 43 | -------------------------------------------------------------------------------- /13. 错误监控类.md: -------------------------------------------------------------------------------- 1 | # 1. 错误监控类 2 | 3 | 面试时,可能有两种问法: 4 | 5 | - 如何监测 js 错误?(开门见山的方式) 6 | 7 | - 如何保证**产品质量**?(其实问的也是错误监控) 8 | 9 | ## 1.1. 前端错误的分类 10 | 11 | - 即时运行错误(代码错误) 12 | - 资源加载错误 13 | 14 | ## 1.2. 错误的捕获方式 15 | 16 | ### 1.2.1. 即时运行错误的捕获方式 17 | 18 | 1. try-catch 19 | 这种方式要部署在代码中 20 | 2. `window.onerror`函数,这个函数是 21 | 22 | ```JavaScript 23 | window.onerror = function(msg, url, row, col, error) { ... } 24 | window.addEventListener("error", fn); 25 | ``` 26 | 27 | 参数解释: 28 | 29 | - msg 为异常基本信息 30 | - source 为发生异常 Javascript 文件的 url 31 | - row 为发生错误的行号 32 | 33 | **问题延伸**: 34 | `window.onerror` 默认无法捕获跨域的 js 运行错误。捕获出来的信息如下:(基本属于无效信息)比如说,我们的代码想引入 B 网站的 `b.js` 文件,怎么捕获它的异常呢? 35 | 36 | **解决方法**: 37 | 38 | 1. 在 script 标签增加 `crossorigin` 属性 39 | 2. 在 b.js 文件里设置资源响应头 `Access-Control-Allow-Origin` 表示允许跨域 40 | 41 | ### 1.2.2. 资源加载错误 42 | 43 | 1. object.onerror。img 标签、script 标签等节点都可以添加 onerror 事件,用来捕获资源加载的错误。 44 | 2. performance.getEntries()。可以获取所有已加载资源的加载时长,通过这种方式,可以间接的拿到没有加载的资源错误。 45 | 3. Error 事件捕获 46 | 47 | ```JavaScript 48 | 49 | 54 | ``` 55 | 56 | ## 1.3. 上报错误的基本原理 57 | 58 | 1. 采用 Ajax 通信的方式上报(此方式虽然可以上报错误,但是我们并不采用这种方式) 59 | 2. 利用 Image 对象上报(推荐。网站的监控体系都是采用的这种方式) 60 | 61 | ```JavaScript 62 | 63 | 64 | 65 | 66 | Title 67 | 68 | 69 | 73 | 74 | 75 | ``` 76 | -------------------------------------------------------------------------------- /8. 安全问题:CSRF和XSS.md: -------------------------------------------------------------------------------- 1 | # 1. 安全类 2 | 3 | ## 1.1. CSRF 4 | 5 | > 跨站请求伪造(英语:Cross-site request forgery),也被称为 one-click attack 或者 session riding,通常缩写为 CSRF 或者 XSRF, 是一种挟制用户在当前已登录的 Web 应用程序上执行非本意的操作的攻击方法。跟 XSS 相比,XSS 利用的是用户对指定网站的信任,CSRF 利用的是网站对用户网页浏览器的信任。 6 | 7 | **攻击原理**: 8 | ![CSRF 攻击原理](https://segmentfault.com/img/bV7gdH?w=383&h=191) 9 | 10 | > 不可缺少的两大因素: 1.用户一定在注册网站登陆过; 2.网站的某一接口有漏洞(引诱链接会自动携带 cookie,不会自动携带 Token)。 11 | 12 | **防御措施**: 13 | 14 | **方法一、Token 验证**:(用的最多) 15 | 16 | (1)服务器发送给客户端一个token; 17 | 18 | (2)客户端提交的表单中带着这个token。 19 | 20 | (3)如果这个 token 不合法,那么服务器拒绝这个请求。 21 | 22 | **方法二:隐藏令牌**: 23 | 24 | 把 token 隐藏在 http 的 head头中。 25 | 26 | 方法二和方法一有点像,本质上没有太大区别,只是使用方式上有区别。 27 | 28 | **方法三、Referer 验证**: 29 | 30 | Referer 指的是页面请求来源。意思是,**只接受本站的请求,服务器才做响应**;如果不是,就拦截。 31 | 32 | ## 1.2. XSS 33 | 34 | > 跨网站指令码(英语:Cross-site scripting,通常简称为:XSS)是一种网站应用程式的安全漏洞攻击,是代码注入的一种。它允许恶意使用者将程式码注入到网页上,其他使用者在观看网页时就会受到影响。这类攻击通常包含了 HTML 以及使用者端脚本语言。 35 | 36 | XSS 分为三种:反射型,存储型和 DOM-based。 37 | 38 | **攻击原理**: 39 | 40 | ```html 41 | 42 |
{{name}}
43 | ``` 44 | 45 | 上述 URL 输入可能会将 HTML 改为 `
` ,这样页面中就凭空多了一段可执行脚本。这种攻击类型是反射型攻击,也可以说是 DOM-based 攻击。 46 | 47 | 也有另一种场景,比如写了一篇包含攻击代码 `` 的文章,那么可能浏览文章的用户都会被攻击到。这种攻击类型是存储型攻击,也可以说是 DOM-based 攻击,并且这种攻击打击面更广。 48 | 49 | **防御措施**: 50 | 最普遍的做法是转义输入输出的内容,对于引号,尖括号,斜杠进行转义 51 | 52 | ```JavaScript 53 | function escape(str) { 54 | str = str.replace(/&/g, "&"); 55 | str = str.replace(//g, ">"); 57 | str = str.replace(/"/g, "&quto;"); 58 | str = str.replace(/'/g, "&##39;"); 59 | str = str.replace(/`/g, "&##96;"); 60 | str = str.replace(/\//g, "&##x2F;"); 61 | return str; 62 | } 63 | ``` 64 | 65 | 通过转义可以将攻击代码 `` 变成 66 | 67 | ```js 68 | // -> <script>alert(1)<&##x2F;script> 69 | escape(""); 70 | ``` 71 | 72 | 对于显示富文本来说,不能通过上面的办法来转义所有字符,因为这样会把需要的格式也过滤掉。这种情况通常采用白名单过滤的办法,当然也可以通过黑名单过滤,但是考虑到需要过滤的标签和标签属性实在太多,更加推荐使用白名单的方式。 73 | 74 | ```js 75 | var xss = require("xss"); 76 | var html = xss('

XSS Demo

'); 77 | // ->

XSS Demo

<script>alert("xss");</script> 78 | console.log(html); 79 | ``` 80 | 81 | ## 1.3. CSRF 和 XSS 区别 82 | 83 | - 区别一: 84 | 85 | - CSRF:需要用户先登录网站A,获取 cookie。 86 | - XSS:不需要登录。 87 | 88 | 区别二:(原理的区别) 89 | 90 | - CSRF:是利用网站A本身的漏洞,去请求网站A的api。 91 | - XSS:是向网站 A 注入 JS代码,然后执行 JS 里的代码,篡改网站A的内容。 92 | -------------------------------------------------------------------------------- /JavaScript高级面试/2. 原型.md: -------------------------------------------------------------------------------- 1 | # 1. 原型 2 | 3 | ## 1.1. 说一个原型的实际应用 4 | 5 | ```html 6 |

jquery test 1

7 |

jquery test 2

8 |

jquery test 3

9 | 10 |

jquery test in div

11 | 12 | 13 | 22 | ``` 23 | 24 | 通过`$`函数传入不同的选择器会实例出不同的对象,不同的对象都会有统一的像 css、remove、append 等 jQuery 自己定义好的方法可以用。 25 | 当多个实例都可以共用一套方法时,说明这些方法都是来自于一个构造函数的原型中的。 26 | 通过常用的 api 作为切入点,是 jQuery 和 zepto 的原型使用的比较好理解的方向。 27 | 28 | ### 1.1.1. jQuery 和 zepto 的简单使用 29 | 30 | ### 1.1.2. zepto 如何使用原型 31 | 32 | ```JavaScript 33 | (function (window){ 34 | 35 | // 空对象 36 | var zepto = {} 37 | 38 | // 这就是构造函数 39 | function Z(dom, selector) { 40 | var i, len = dom ? dom.length : 0 41 | for (i = 0; i < len; i++) { 42 | this[i] = dom[i] 43 | } 44 | this.length = len 45 | this.selector = selector || '' 46 | } 47 | 48 | zepto.Z = function (dom, selector) { 49 | return new Z(dom, selector) 50 | } 51 | 52 | zepto.init = function (selector) { 53 | var slice = Array.prototype.slice 54 | var dom = slice.call(document.querySelectorAll(selector)) 55 | return zepto.Z(dom, selector) 56 | } 57 | 58 | // 即使用 zepto 时候的 $ 59 | var $ = function (selector) { 60 | return zepto.init(selector) 61 | } 62 | window.$ = $ 63 | 64 | $.fn = { 65 | css: function (key, value) { 66 | alert('css') 67 | }, 68 | html: function (value) { 69 | return '这是一个模拟的 html 函数' 70 | } 71 | } 72 | Z.prototype = $.fn 73 | 74 | })(window) 75 | ``` 76 | 77 | ### 1.1.3. jQuery 如何使用原型 78 | 79 | ```JavaScript 80 | (function(window){ 81 | 82 | var jQuery = function (seletor){ 83 | return new jQuery.fn.init(selector) 84 | } 85 | 86 | jQuery.fn = { 87 | css: function (key, value) { 88 | alert('css') 89 | } 90 | html: function (value) { 91 | return 'html' 92 | } 93 | } 94 | 95 | var init = jQuery.fn.init = function (selector) { 96 | var slice = Array.prototype.slice 97 | var dom = slice.call(document.querySelectorAll(selector)) 98 | 99 | var i, len = dom ? dom.length : 0 100 | for (i = 0; i < len; i++) { 101 | this[i] = dom[i] 102 | } 103 | this.length = len 104 | this.selector = selector || '' 105 | } 106 | 107 | init.prototype = jQuery.fn 108 | 109 | 110 | window.$ = jQuery 111 | })(window) 112 | ``` 113 | 114 | ### 1.1.4. 问题解答 115 | 116 | - 描述一下 jQuery 如何使用原型 117 | - 描述一下 zepto 如何使用原型 118 | - 再结合自己的项目经验,说一个自己开发的例子 119 | 120 | ## 1.2. 原型如何实现它的扩展性 121 | 122 | ### 1.2.1. 总结 zepto 和 jQuery 原型的使用 123 | 124 | ### 1.2.2. 插件机制 125 | 126 | 为什么要把原型方法放在\$.fn ? 127 | 因为要扩展插件,做一个简单的插件的例子 128 | 129 | ```JavaScript 130 | $.fn.getNodeName = function () { 131 | return this[0].nodeName 132 | } 133 | ``` 134 | 135 | - 只有`$`会暴露在 window 全局变量 136 | - 将插件扩展统一到`$.fn.xxx` 这一个接口,方便使用 137 | 138 | ### 1.2.3. 问题解答 139 | 140 | - 说一下 jQuery 和 zepto 的插件机制 141 | - 结合自己的开发经验,做过基于原型的插件 142 | -------------------------------------------------------------------------------- /10. 渲染机制.md: -------------------------------------------------------------------------------- 1 | # 1. 渲染机制 2 | 3 | ## 浏览器的组成 4 | 5 | ![浏览器的组成](https://kfwimg.ikafan.com/upload/b9/19/b919dd5130b8694bea56aa44b44b32be.png) 6 | 7 | - 用户界面 - 包括地址栏、后退/前进按钮、书签目录等,也就是你所看到的除了用来显示你所请求页面的主窗口之外的其他部分 8 | - 浏览器引擎 - 用来查询及操作渲染引擎的接口 9 | - 渲染引擎(浏览器内核)- 浏览器有兼容性的根本原因,用来显示请求的内容,例如,如果请求内容为 html,它负责解析 html 及 css,并将解析后的结果显示出来 10 | - 网络 - 用来完成网络调用,例如 http 请求,它具有平台无关的接口,可以在不同平台上工作 11 | - UI 后端 - 用来绘制类似组合选择框及对话框等基本组件,具有不特定于某个平台的通用接口,底层使用操作系统的用户接口 12 | - JS 解释器 - 用来解释执行 JS 代码 13 | - 数据存储 - 属于持久层,浏览器需要在硬盘中保存类似 cookie 的各种数据,HTML5 定义了 Storage 技术,这是一种轻量级完整的客户端存储技术 14 | 15 | ## 1.1. DOCTYPE 16 | 17 | - DTD(document type definition,文档类型定义)是一系列的语法规则,用来定义 XML 或(X)HTML 的文件类型。浏览器会使用它来判断文档类型,决定使用何种协议来解析,以及切换浏览器模式。 18 | 解读:DTD 就是告诉浏览器我是什么文档类型,那么浏览器根据这个来判断用什么引擎来解析渲染他。 19 | 20 | - DOCTYPE 是用来声明文档类型和 DTD 规范的,一个主要的用途便是文件的合法性验证。如果文件代码不合法,那么浏览器解析时便会出一些差错。 21 | 解读:DOCTYPE 通知浏览器当前文档的 DTD。 22 | 23 | - 常见的 DOCTYPE 24 | 25 | 1. HTML 5 26 | `!DOCTYPE html` 27 | 2. HTML 4.01 Strict,严格模式,该 DTD 包含所有 HTML 元素和属性,但不包括展示型的和弃用的元素(比如 font) 28 | `` 29 | 3. HTML 4.01 Transitional,传统模式(宽松模式),该 DTD 包含所有 HTML 元素和属性,包括展示型的和弃用的元素(比如 font) 30 | `` 31 | 32 | ## 1.2. 浏览器渲染过程 33 | 34 | **url 解析**: 35 | 36 | 1. 用户输入 URL 地址 37 | 38 | 2. 浏览器解析 URL 解析出主机名 39 | 40 | 3. 浏览器将主机名转换成服务器 ip 地址(浏览器先查找本地 DNS 缓存列表 没有的话 再向浏览器默认的 DNS 服务器发送查询请求 同时缓存) 41 | 42 | 4. 浏览器将端口号从 URL 中解析出来 43 | 44 | 5. 浏览器建立一条与目标 Web 服务器的 TCP 连接(三次握手) 45 | 46 | 6. 浏览器向服务器发送一条 HTTP 请求报文 47 | 48 | 7. 服务器向浏览器返回一条 HTTP 响应报文 49 | 50 | 8. 关闭连接 浏览器解析文档 51 | 52 | 9. 如果文档中有资源 重复 6 7 8 动作 直至资源全部加载完毕 53 | 54 | **html 解析**: 55 | 56 | ![渲染机制](https://user-gold-cdn.xitu.io/2018/6/23/1642d16f798c0875?imageView2/0/w/1280/h/960/format/webp/ignore-error/1) 57 | 58 | 1. 将 HTML 构建成一个 DOM 树(DOM = Document Object Model 文档对象模型),DOM 树的构建过程是一个深度遍历过程:当前节点的所有子节点都构建好后才会去构建当前节点的下一个兄弟节点。 59 | 2. 解析 CSS 样式,生成样式结构体 60 | 3. 结合 DOM 树和 CSSOM 生成 Rendering Tree(渲染树)。注意:Rendering Tree 渲染树并不等同于 DOM 树,因为一些像 Header 或 display:none 的东西就没必要放在渲染树中了。 61 | 4. 下一步操作称之为布局(Layout),顾名思义就是计算出每个节点在屏幕中的位置 layout render tree。 62 | 5. 再下一步就是绘制(Paint),即遍历 render 树,并使用浏览器 UI 后端层绘制每个节点。 63 | 64 | ## 1.3. 重排(回流) Reflow 65 | 66 | **定义**:DOM 结构中的各个元素都有自己的盒子(模型),这些都需要浏览器根据各种样式来计算,并根据计算结果将元素放到它该出现的位置,这个过程称为 reflow。 67 | 68 | **触发 Reflow**:(一般触发 Reflow 时都会需要 Repaint) 69 | 70 | 1. 当你增加、删除、修改 DOM 节点时; 71 | 2. 当你移动 DOM 的位置,或是搞个动画的时候; 72 | 3. 当你修改 CSS 样式的时候;(边距、填充、边框、宽高等) 73 | 4. 当你 Resize 窗口的时候(移动端没有这个问题),或是滚动的时候; 74 | 5. 当你修改网页的默认字体时;在页面加载完后避免这样做,会影响性能 75 | 76 | **如何减少 Reflow**: 77 | 78 | 1. 尽可能限制 reflow 的影响范围。需要改变元素的样式,不要通过父级元素影响子元素。最好直接加在子元素上。 79 | 2. 通过设置 style 属性改变结点样式的话,每设置一次都会导致一次 reflow。所以最好通过设置 class 的方式。 80 | 3. 减少不必要的 DOM 层级(DOM depth)。改变 DOM 树中的一级会导致所有层级的改变,上至根部,下至被改变节点的子节点。这导致大量时间耗费在执行 reflow 上面。 81 | 4. 避免不必要的复杂的 CSS 选择器,尤其是后代选择器(descendant selectors),因为为了匹配选择器将耗费更多的 CPU。 82 | 83 | ## 1.4. 重绘 Repaint 84 | 85 | **定义**:当各种盒子的位置、大小以及其他属性,例如颜色、字体大小等都确定下来后,浏览器于是便把这些元素按照各自的特性绘制了一遍,于是页面的内容出现了,这个过程称之为 Repaint。(解读:页面要呈现的内容,统统画在屏幕上) 86 | 87 | **触发 Repaint**: 88 | 89 | 1. DOM 改动; 90 | 2. CSS 改动; 91 | 3. 如果有一些不影响布局的样式属性修改(background-color,color,background-image)只会触发重绘,不会触发回流 92 | 93 | **如何尽量减少 Repaint 频率**: 94 | 将需要追加的多个 DOM 节点先存放在一个片段(createDocumentFragment())中,最后一次性放到页面中。 95 | 96 | ## 1.5. 聪明的浏览器 97 | 98 | > 但是假设每句 JS 操作都去回流重绘的话,浏览器可能就会受不了。所以很多浏览器都会优化这些操作,浏览器会维护 1 个队列,把所有会引起回流、重绘的操作放入这个队列,等队列中的操作到了一定的数量或者到了一定的时间间隔,浏览器就会 flush 队列,进行一个批处理。这样就会让多次的回流、重绘变成一次回流重绘。 99 | 100 | 虽然有了浏览器的优化,但有时候我们写的一些代码可能会强制浏览器提前 flush 队列,这样浏览器的优化可能就起不到作用了。当你请求向浏览器请求一些 style 信息的时候,就会让浏览器 flush 队列,比如: 101 | 102 | 1.offsetTop, offsetLeft, offsetWidth, offsetHeight 103 | 104 | 2.scrollTop/Left/Width/Height 105 | 106 | 3.clientTop/Left/Width/Height 107 | 108 | 4.请求了 getComputedStyle(), 或者 IE 的 currentStyle 109 | 110 | 所有获取性操作,都会让浏览器提前 flush 队列(提前批处理),浏览器是为了能够让获取性操作获取的样式保证准确性 111 | 112 | ## 1.6. 渲染优化 113 | 114 | 都是在想办法减少回流和重绘的次数 115 | 116 | 1. HTML 文档结构层次尽量少,最好不深于六层; 117 | 2. 脚本尽量后放,放在前即可; 118 | 3. 少量首屏样式内联放在标签内; 119 | 4. 样式结构层次尽量简单; 120 | 5. 在脚本中尽量减少 DOM 操作,尽量缓存访问 DOM 的样式信息,避免过度触发回流; 121 | 6. 减少通过 JavaScript 代码修改元素样式,尽量使用修改 class 名方式操作样式或动画; 122 | 7. position:fixed/absolute;定位的元素的布局属性更改不会影响到其他元素,动画尽量使用在绝对定位或固定定位的元素上; 123 | 8. css3 的变换(transform 系列)只是一种视觉效果,只会触发重绘,可以减少一次回流的过程 124 | 9. 隐藏在屏幕外,或在页面滚动时,尽量停止动画; 125 | 10. 尽量缓存 DOM 查找,查找器尽量简洁; 126 | 11. 涉及多域名的网站,可以开启域名预解析 127 | -------------------------------------------------------------------------------- /12. 页面性能类.md: -------------------------------------------------------------------------------- 1 | # 1. 页面性能优化 2 | 3 | ## 1.1. 提问:提升页面性能的方法有哪些? 4 | 5 | 1. 资源压缩合并,减少 http 请求 6 | 2. **非核心代码异步加载** --> 异步加载的方式 --> 异步加载的区别 7 | 8 | 3. 利用浏览器缓存 --> 缓存的分类 --> 缓存的原理 9 | 10 | **缓存**是所有性能优化的方式中最重要的一步,这个一定要答好。 11 | 12 | 别说 local storage 和 session storage。浏览器缓存和存储根本不是一回事。 13 | 14 | 4. 使用 CDN 15 | 16 | 浏览器第一次打开页面时,缓存是起不了作用的。CDN 这一条,一定要说出来。 17 | 18 | 5. DNS 预解析 19 | 20 | ### 1.1.1. 资源压缩合并,减少 HTTP 请求 21 | 22 | - 合并图片(css sprites)、CSS 和 JS 文件合并、CSS 和 JS 文件压缩 23 | - 图片较多的页面也可以使用 lazyLoad 等技术进行优化。 24 | - 精灵图等 25 | 26 | ### 1.1.2. 非核心代码异步加载 27 | 28 | #### 1.1.2.1. 异步加载的方式 29 | 30 | 1. 动态脚本加载 31 | 用`document.createElement`等动态创建节点 32 | 2. defer 33 | 在加载 js 的时候在 script 标签上加上这个属性就能完成异步加载。 34 | 3. async 35 | 在加载 js 的时候在 script 标签上加上这个属性就能完成异步加载。 36 | 37 | #### 1.1.2.2. 异步加载的区别 38 | 39 | 1. defer 是在 HTML 解析完之后才会执行,如果是多个,按照加载的顺序依次执行 40 | 2. async 是在加载完之后立即执行,如果是多个,执行顺序和加载顺序无关 41 | 42 | ### 1.1.3. 利用浏览器缓存 43 | 44 | **缓存**:资源文件(比如图片)在**本地的硬盘**里存在的备份,浏览器下次请求的时候,可能直接从本地磁盘里读取,而不会重新请求图片的 url。 45 | 46 | 缓存分为: 47 | 48 | - 强缓存 49 | - 协商缓存 50 | 51 | #### 1.1.3.1. 强缓存 52 | 53 | **强缓存**:用请求服务器,直接使用本地的缓存。 54 | 强缓存是利用 http 响应头中的**Expires**或**Cache-Control**实现的。 55 | 浏览器第一次请求一个资源时,服务器在返回该资源的同时,会把上面这两个属性放在 response header 中。 56 | 57 | **注意**:这两个 response header 属性可以只启用一个,也可以同时启用。当 response header 中,Expires 和 Cache-Control 同时存在时,**Cache-Control 的优先级高于 Expires**。 58 | 59 | 1. Expires:服务器返回的**绝对时间**。 60 | 61 | 是较老的强缓存管理 response header。浏览器再次请求这个资源时,先从缓存中寻找,找到这个资源后,拿出它的 Expires 跟当前的请求时间比较,如果请求时间在 Expires 的时间之前,就能命中缓存,否则就不行。 62 | 63 | 如果缓存没有命中,浏览器直接从服务器请求资源时,Expires Header 在重新请求的时候会被更新。 64 | 65 | **缺点**: 66 | 67 | 由于 Expires 是服务器返回的一个绝对时间,存在的问题是:服务器的事件和客户端的事件可能不一致。在服务器时间与客户端时间相差较大时,缓存管理容易出现问题,比如随意修改客户端时间,就能影响缓存命中的结果。所以,在 http1.1 中,提出了一个新的 response header,就是 Cache-Control。 68 | 69 | 2. Cache-Control:服务器返回的**相对时间**。 70 | 71 | http1.1 中新增的 response header。浏览器第一次请求资源之后,在接下来的相对时间之内,都可以利用本地缓存。超出这个时间之后,则不能命中缓存。重新请求时,Cache-Control 会被更新。 72 | 73 | #### 1.1.3.2. 协商缓存 74 | 75 | **协商缓存**:浏览器发现本地有资源的副本,但是不太确定要不要使用,于是去问问服务器。 76 | 77 | 当浏览器对某个资源的请求没有命中强缓存(也就是说超出时间了),就会发一个请求到服务器,验证协商缓存是否命中。 78 | 79 | 协商缓存是利用的是两对 Header: 80 | 81 | 第一对:`Last-Modified`、`If-Modified-Since` 82 | 83 | 第二对:`ETag`、`If-None-Match` 84 | 85 | ETag(Entity Tag):被请求变量的实体值。 86 | 87 | 1. `Last-Modified`、`If-Modified-Since`。过程如下: 88 | 89 | (1)浏览器第一次请求一个资源,服务器在返回这个资源的同时,会加上 `Last-Modified` 这个 response header,这个 header 表示这该资源在服务器上的最后修改时间; 90 | (2)浏览器再次请求这个资源时,会加上 `If-Modified-Since` 这个 request header,这个 header 的值就是上一次返回的 `Last-Modified` 的值; 91 | (3)服务器收到第二次请求时,会比对浏览器传过来的 `If-Modified-Since` 和资源在服务器上的最后修改时间 `Last-Modified`,判断资源是否有变化。如果没有变化则返回 304 Not Modified,但不返回资源内容(此时,服务器不会返回 Last-Modified 这个 response header);如果有变化,就正常返回资源内容(继续重复整个流程)。这是服务器返回 304 时的 response header; 92 | (4)浏览器如果收到 304 的响应,就会从缓存中加载资源。 93 | 94 | **缺点**: 95 | `Last-Modified`、`If-Modified-Since` 一般来说都是非常可靠的,但面临的问题是: 96 | 97 | - **服务器上的资源变化了,但是最后的修改时间却没有变化。** 98 | 99 | - 如果服务器端在一秒内修改文件两次,但产生的 Last-Modified 却只有一个值。 100 | 101 | 这一对 header 就无法解决这种情况。于是,下面这一对 header 出场了。 102 | 103 | 2. `ETag`、`If-None-Match`。过程如下: 104 | 105 | (1)浏览器第一次请求一个资源,服务器在返回这个资源的同时,会加上 `ETag` 这个 response header,这个 header 是服务器根据当前请求的资源生成的**唯一标识**。这个唯一标识是一个字符串,只要资源有变化这个串就不同,跟最后修改时间无关,所以也就很好地补充了 Last-Modified 的不足; 106 | (2)浏览器再次请求这个资源时,会加上 `If-None-Match` 这个 request header,这个 header 的值就是上一次返回的 `ETag` 的值; 107 | (3)服务器第二次请求时,会对比浏览器传过来的 `If-None-Match` 和服务器重新生成的一个新的 `ETag`,判断资源是否有变化。如果没有变化则返回 304 Not Modified,但不返回资源内容(此时,由于 `ETag` 重新生成过,response header 中还会把这个 `ETag` 返回,即使这个 `ETag` 并无变化)。如果有变化,就正常返回资源内容(继续重复整个流程)。这是服务器返回 304 时的 response header: 108 | (4)浏览器如果收到 304 的响应,就会从缓存中加载资源。 109 | 110 | ### 1.1.4. 使用 CDN 111 | 112 | 怎么最快地让用户请求资源。一方面是让资源在传输的过程中变小,另外就是 CDN。 113 | 114 | 要注意,浏览器第一次打开页面的时候,浏览器缓存是起不了作任何用的,使用 CDN,效果就很明显。 115 | 116 | ### 1.1.5. 预解析 DNS 117 | 118 | 通过 DNS 预解析来告诉浏览器未来我们可能从某个特定的 URL 获取资源,当浏览器真正使用到该域中的某个资源时就可以尽快地完成 DNS 解析。 119 | 120 | **第一步**:打开或关闭 DNS 预解析 121 | 122 | 你可以通过在服务器端发送 X-DNS-Prefetch-Control 报头。或是在文档中使用值为 http-equiv 的 meta 标签: 123 | 124 | ```html 125 | 126 | ``` 127 | 128 | 需要说明的是,在一些高级浏览器中,页面中所有的超链接(``标签),默认打开了 DNS 预解析。但是,如果页面中采用的 https 协议,很多浏览器是默认关闭了超链接的 DNS 预解析。如果加了上面这行代码,则表明强制打开浏览器的预解析。(如果你能在面试中把这句话说出来,则一定是你出彩的地方) 129 | 130 | **第二步**:对指定的域名进行 DNS 预解析 131 | 132 | 如果我们将来可能从 smyhvae.com 获取图片或音频资源,那么可以在文档顶部的 标签中加入以下内容: 133 | 134 | ```html 135 | 136 | ``` 137 | 138 | 当我们从该 URL 请求一个资源时,就不再需要等待 DNS 解析的过程。该技术对使用第三方资源特别有用。 139 | -------------------------------------------------------------------------------- /11. JS运行机制:异步和单线程.md: -------------------------------------------------------------------------------- 1 | # JS 运行机制 2 | 3 | - 面试时,关于**同步和异步**,可能会问以下问题: 4 | 5 | 1. 同步和异步的区别是什么?分别举一个同步和异步的例子 6 | 7 | 2. 一个关于 setTimeout 的笔试题 8 | 9 | 3. 前端使用异步的场景哪些? 10 | 11 | - 面试时,关于 **js 运行机制**,需要注意以下几个问题: 12 | 13 | 1. 如何理解 JS 的**单线程** 14 | 15 | 2. 什么是**任务队列** 16 | 17 | 3. 什么是 `EventLoop` 18 | 19 | 4. 理解哪些语句会放入异步任务队列 20 | 21 | 5. 理解语句放入异步任务队列的**时机** 22 | 23 | ## JS 的异步和单线程 24 | 25 | 因为是单线程,所以必须异步。 26 | 27 | 我们通过题目来解释以下。 28 | 29 | ### 题目一:异步 30 | 31 | 现有如下代码: 32 | 33 | ```JavaScript 34 | console.log(1); 35 | setTimeout(function () { 36 | console.log(2); 37 | }, 1000); 38 | console.log(3); 39 | console.log(4); 40 | ``` 41 | 42 | 上面的代码中,我们很容易知道,打印的顺序是 `1,3,4,2`。因为你会想到,要等一秒之后再打印 `2`。 43 | 44 | 可如果我把延时的时间从 `1000` 改成 `0`: 45 | 46 | ```JavaScript 47 | console.log(1); 48 | setTimeout(function () { 49 | console.log(2); 50 | }, 0); 51 | console.log(3); 52 | console.log(4); 53 | ``` 54 | 55 | 上方代码中,打印的顺序仍然是`1,3,4,2`。这是为什么呢?我们来分析一下。 56 | 57 | **总结**: 58 | 59 | js 是单线程(同一时间只能做一件事),而且有一个**任务队列**:全部的同步任务执行完毕后,再来执行异步任务。第一行代码和最后一行代码是同步任务;但是,**`setTimeout` 是异步任务**。 60 | 61 | 于是,执行的顺序是: 62 | 63 | - 先执行同步任务 `console.log(1)` 64 | 65 | - 遇到异步任务 `setTimeout`,要**挂起** 66 | 67 | - 执行同步任务 `console.log(3)` 68 | 69 | - **全部的同步任务执行完毕后,再来执行异步任务** `console.log(2)`。 70 | 71 | 很多人会把这个题目答错,这是因为他们不懂 js 的运行机制。 72 | 73 | 注意上面那句话:同步任务执行完毕后,再来执行异步任务。也就是说,**如果同步任务没有执行完,异步任务是不会执行的**。为了解释这句话,我们来看下面这个例子。 74 | 75 | ### 题目二:异步 76 | 77 | 现有如下代码: 78 | 79 | ```JavaScript 80 | console.log('A'); 81 | while (1) { 82 | 83 | } 84 | console.log('B'); 85 | ``` 86 | 87 | 我们很容易想到,上方代码的打印结果是 A,因为 while 是同步任务,代码会陷入死循环里出不来,自然也就无法打印 B。可如果我把代码改成下面的样子: 88 | 89 | ```JavaScript 90 | console.log('A'); 91 | 92 | setTimeout(function () { 93 | console.log('B'); 94 | }) 95 | 96 | while (1) { 97 | 98 | } 99 | ``` 100 | 101 | 上方代码的打印结果仍然是 `A`。因为 while 是同步任务,setTimeout 是异步任务,所以还是那句话:**如果同步任务没有执行完,队列里的异步任务是不会执行的。** 102 | 103 | ### 题目三:同步 104 | 105 | ```JavaScript 106 | console.log('A'); 107 | 108 | alert('haha'); //1秒之后点击确认 109 | 110 | console.log('B'); 111 | ``` 112 | 113 | `alert` 函数是同步任务,我只有点击了确认,才会继续打印 `B`。 114 | 115 | ### 同步和异步的对比 ☆ 116 | 117 | 我们在上面列举了异步和同步的例子。现在来描述一下区别: 118 | 119 | 因为 setTimeout 是**异步任务**,所以程序并不会卡在那里,而是继续向下执行(即使 settimeout 设置了倒计时一万秒);但是 alert 函数是**同步任务**,程序会**卡在那里**,如果它没有执行,后面的也不会执行(卡在那里,自然也就造成了**阻塞**)。 120 | 121 | ### 前端使用异步的场景 122 | 123 | 什么时候需要**等待**,就什么时候用异步。 124 | 125 | - 定时任务:setTimeout(定时炸弹)、setInterval(循环执行) 126 | 127 | - 网络请求:ajax 请求、动态``加载 128 | 129 | - DOM 事件绑定(比如说,按钮绑定点击事件之后,用户爱点不点。我们不可能卡在按钮那里,什么都不做。所以,应该用异步) 130 | 131 | - ES6 中的 Promise 132 | 133 | 代码举例: 134 | 135 | ```JavaScript 136 | console.log('start'); 137 | var img = document.createElement('img'); 138 | img.onload = function () { 139 | console.log('loaded'); 140 | } 141 | img.src = '/xxx.png'; 142 | console.log('end'); 143 | ``` 144 | 145 | 先打印 `start`,然后执行 `img.src = '/xxx.png'`,然后打印 `end`,最后打印 `loaded`。 146 | 147 | ## 任务队列和 Event Loop(事件循环) 148 | 149 | ### 任务队列 150 | 151 | 所有任务可以分成两种,一种是同步任务(synchronous),另一种是异步任务(asynchronous)。同步任务指的是,在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务。异步任务指的是,不进入主线程、而进入"任务队列"(task queue)的任务,只有"任务队列"通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行。 152 | 153 | 总结:**只要主线程空了,就会去读取"任务队列",这就是 JavaScript 的运行机制。**☆ 154 | 155 | ### Event Loop 156 | 157 | 主线程从"任务队列"中读取事件,这个过程是循环不断的,所以整个的这种运行机制又称为 Event Loop(事件循环)。 158 | 159 | ![任务队列](https://camo.githubusercontent.com/1b206c2f0184766d1aae57051f6d0d384656b8a5/687474703a2f2f696d672e736d79687661652e636f6d2f32303138303331305f313834302e706e67) 160 | 161 | 在理解 Event Loop 时,要理解两句话: 162 | 163 | - 理解哪些语句会放入异步任务队列 164 | 165 | - 理解语句放入异步任务队列的时机 166 | 167 | ### 容易答错的题目 168 | 169 | ```JavaScript 170 | for (var i = 0; i < 3; i++) { 171 | setTimeout(function () { 172 | console.log(i); 173 | }, 1000); 174 | } 175 | ``` 176 | 177 | 很多人以为上面的题目,答案是 `0,1,2,3`。其实,正确的答案是:`3,3,3,3`。 178 | 179 | 分析:for 循环是同步任务,setTimeout 是异步任务。for 循环每次遍历的时候,遇到 settimeout,就先暂留着,等同步任务全部执行完毕(此时,i 已经等于 3 了),再执行异步任务。 180 | 181 | 我们把上面的题目再加一行代码。最终代码如下: 182 | 183 | ```JavaScript 184 | for (var i = 0; i < 3; i++) { 185 | setTimeout(function () { 186 | console.log(i); 187 | }, 1000); 188 | } 189 | console.log(i); 190 | ``` 191 | 192 | 如果我们约定,用箭头表示其前后的两次输出之间有 1 秒的时间间隔,而逗号表示其前后的两次输出之间的时间间隔可以忽略,代码实际运行的结果该如何描述?可能会有两种答案: 193 | 194 | A. 60% 的人会描述为:3 -> 3 -> 3 -> 3,即每个 3 之间都有 1 秒的时间间隔; 195 | 196 | B. 40% 的人会描述为:3 -> 3,3,3,即第 1 个 3 直接输出,1 秒之后,连续输出 3 个 3。 197 | 198 | 循环执行过程中,几乎同时设置了 3 个定时器,这些定时器都会在 1 秒之后触发,而循环完的输出是立即执行的,显而易见,正确的描述是 B。 199 | -------------------------------------------------------------------------------- /2. CSS盒模型.md: -------------------------------------------------------------------------------- 1 | # 1. CSS 盒模型 2 | 3 | 所有 HTML 元素可以看作盒子,CSS 盒模型本质上是一个盒子,封装周围的 HTML 元素,它包括:边距,边框,填充,和实际内容。 4 | 5 | Margin(外边距) - 清除边框外的区域,外边距是透明的。 6 | Border(边框) - 围绕在内边距和内容外的边框。 7 | Padding(内边距) - 清除内容周围的区域,内边距是透明的。 8 | Content(内容) - 盒子的内容,显示文本和图像。 9 | 10 | ## 1.1. 标准盒子模型 11 | 12 | W3C 标准盒子模式包括内容(content)、填充(padding)、边框(border)、边界(margin),并且 content 部分不包含其他部分。 13 | 14 | ![W3C标准盒子模型](https://upload-images.jianshu.io/upload_images/5217911-4cd25beec9f2be7a.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/795/format/webp) 15 | 16 | ## 1.2. IE 盒子模型 17 | 18 | IE 盒子模型的范围也包括 content、padding、border、margin,所不同的是 IE 盒子模型的 content 包含了 border 和 padding。 19 | ![IE盒子模型](https://upload-images.jianshu.io/upload_images/5217911-24aa8e79ae622d9c.jpeg?imageMogr2/auto-orient/strip%7CimageView2/2/w/791/format/webp) 20 | 21 | ## 1.3. CSS 如何设置这两种模型 22 | 23 | W3C:box-sizing:content-box;(浏览器默认) 24 | IE:box-sizing:border-box; 25 | 26 | ## 1.4. JS 如何设置获取盒模型对应的宽和高 27 | 28 | 1. dom.style.width/height(只适用获取内联元素的宽和高) 29 | 2. dom.currentStyle.width/height (获取渲染后的宽高,只有 IE 支持) 30 | 3. window.getComputedStyle(dom).width/height (与 2 原理相似,但是兼容性,通用性会更好一些) 31 | 4. dom.getBoundingClientRect().width/height (计算一个元素的绝对位置(根据视窗),获取到四个元素 left,top,width,height) 32 | 33 | ## 1.5. BFC(边距重叠解决方案) 34 | 35 | ### 1.5.1. 基本概念 36 | 37 | BFC(Block formatting context)直译为块级格式化上下文。它是一个独立的渲染区域。 38 | 39 | ### 1.5.2. BFC 原理、渲染规则 40 | 41 | 1. BFC 垂直方向的距离由 margin 决定(属于同一个 BFC 的两个相邻 Box 的 margin 会发生重叠,与方向无关) 42 | 2. BFC 的区域不会与 float 的元素区域重叠 43 | 3. BFC 在页面上是一个隔离的独立容器,容器里面的子元素不会影响到外面元素,反之亦然 44 | 4. 计算 BFC 高度时候,浮动子元素也参与计算 45 | 5. 每个元素的 margin box 的左边, 与包含块 border box 的左边相接触(对于从左往右的格式化,否则相反)。即使存在浮动也是如此 46 | 6. 内部的 Box 会在垂直方向上一个接一个的放置 47 | 48 | ### 1.5.3. 如何创建 BFC 49 | 50 | 1. 根元素 51 | 2. float 属性不为 none 52 | 3. position 属性为 absolute 或 fixed 53 | 4. display 的值为 inline-block、table-cell、table-caption、flex、inline-flex 54 | 5. overflow 的值不为 visible 55 | 56 | ### 1.5.4. BFC 的作用 57 | 58 | #### 1.5.4.1. 不和浮动元素重叠 59 | 60 | 如果一个浮动元素后面跟着一个非浮动的元素,那么就会产生一个覆盖的现象,很多自适应的两栏布局就是这么做的。 61 | 62 | ```html 63 | 81 | 82 |
83 |
84 | 85 | ``` 86 | 87 | ![覆盖](https://user-gold-cdn.xitu.io/2018/1/4/160bfae6ea247e92?imageView2/0/w/1280/h/960/format/webp/ignore-error/1) 88 | 89 | 很明显,.aside 和.mian 重叠了。 90 | 91 | 我们可以通过通过触发 main 生成 BFC, 来实现自适应两栏布局 92 | 93 | ```html 94 | .main { overflow: hidden; } 95 | ``` 96 | 97 | 当触发 main 生成 BFC 后,这个新的 BFC 不会与浮动的 aside 重叠。因此会根据包含块的宽度,和 aside 的宽度,自动变窄。效果如下: 98 | ![BFC](https://user-gold-cdn.xitu.io/2018/1/4/160bfae6ebe2f403?imageView2/0/w/1280/h/960/format/webp/ignore-error/1) 99 | 100 | #### 1.5.4.2. 清除元素内部浮动 101 | 102 | ```html 103 | 116 | 117 |
118 |
119 |
120 |
121 | 122 | ``` 123 | 124 | ![浮动](https://user-gold-cdn.xitu.io/2018/1/4/160bfae723e59a6f?imageView2/0/w/1280/h/960/format/webp/ignore-error/1) 125 | 126 | 为达到清除内部浮动,我们可以触发 par 生成 BFC,那么 par 在计算高度时,par 内部的浮动元素 child 也会参与计算。 127 | 128 | ```html 129 | .par{ overflow:hidden } 130 | ``` 131 | 132 | ![清除浮动](https://user-gold-cdn.xitu.io/2018/1/4/160bfae6ea1917ce?imageView2/0/w/1280/h/960/format/webp/ignore-error/1) 133 | 134 | #### 1.5.4.3. 防止垂直 margin 重叠 135 | 136 | ```html 137 | 147 | 148 |

Haha

149 |

Hehe

150 | 151 | ``` 152 | 153 | ![重叠](https://user-gold-cdn.xitu.io/2018/1/4/160bfae6e9f5730f?imageView2/0/w/1280/h/960/format/webp/ignore-error/1) 154 | 155 | 两个 p 之间的距离为 100px,margin 重叠。根据 BFC 布局规则第二条: 156 | 157 | > Box 垂直方向的距离由 margin 决定。属于同一个 BFC 的两个相邻 Box 的 margin 会发生重叠 158 | 159 | 我们可以在 p 外面包裹一层容器,并触发该容器生成一个 BFC。那么两个 P 便不属于同一个 BFC,就不会发生 margin 重叠了。 160 | 161 | ```html 162 | 175 | 176 |

Haha

177 |

Hehe

178 | 179 | ``` 180 | 181 | ![防止重叠](https://user-gold-cdn.xitu.io/2018/1/4/160bfae6ebd2b70d?imageView2/0/w/1280/h/960/format/webp/ignore-error/1) 182 | -------------------------------------------------------------------------------- /6. 类与继承.md: -------------------------------------------------------------------------------- 1 | # 1. 类与继承 2 | 3 | ## 1.1. 类的定义、实例化 4 | 5 | ### 1.1.1. 类的定义/类的声明 6 | 7 | **方式一**:用构造函数模拟类(传统写法) 8 | 9 | ```JavaScript 10 | function Animal1() { 11 | this.name = 'smyhvae'; //通过this,表明这是一个构造函数 12 | } 13 | ``` 14 | 15 | **方式二**:用 class 声明(ES6的写法) 16 | 17 | ```JavaScript 18 | class Animal2 { 19 | constructor() { //可以在构造函数里写属性 20 | this.name = name; 21 | } 22 | } 23 | ``` 24 | 25 | 控制台的效果: 26 | 27 | [![img](https://camo.githubusercontent.com/4d62f65868bc295b44822fdee5ed5c836914de4b/687474703a2f2f696d672e736d79687661652e636f6d2f32303138303330375f303935372e706e67)](https://camo.githubusercontent.com/4d62f65868bc295b44822fdee5ed5c836914de4b/687474703a2f2f696d672e736d79687661652e636f6d2f32303138303330375f303935372e706e67) 28 | 29 | ### 1.1.2. 实例化 30 | 31 | 类的实例化很简单,直接 new 出来即可。 32 | 33 | ```JavaScript 34 | console.log(new Animal1(),new Animal2()); //实例化。如果括号里没有参数,则括号可以省略 35 | ``` 36 | 37 | [![img](https://camo.githubusercontent.com/237f5959fd14aa9b28d5153b314be8e23efcba59/687474703a2f2f696d672e736d79687661652e636f6d2f32303138303330375f313030302e706e67)](https://camo.githubusercontent.com/237f5959fd14aa9b28d5153b314be8e23efcba59/687474703a2f2f696d672e736d79687661652e636f6d2f32303138303330375f313030302e706e67) 38 | 39 | ## 1.2. 继承的几种方式 40 | 41 | 继承的本质就是原型链。 42 | 43 | **继承的方式有几种?每种形式的优缺点是**?这些问题必问的。其实就是考察你对原型链的掌握程度。 44 | 45 | ### 1.2.1. 方式一:借助构造函数 46 | 47 | ```JavaScript 48 | function Parent1() { 49 | this.name = 'parent1 的属性'; 50 | } 51 | 52 | function Child1() { 53 | Parent1.call(this); //【重要】此处用 call 或 apply 都行:改变 this 的指向 54 | this.type = 'child1 的属性'; 55 | } 56 | 57 | console.log(new Child1); 58 | ``` 59 | 60 | 【重要】上方代码中,最重要的那行代码:在子类的构造函数里写了`Parent1.call(this);`,意思是:**让Parent的构造函数在child的构造函数中执行**。发生的变化是:**改变this的指向**,parent的实例 --> 改为指向child的实例。导致 parent的实例的属性挂在到了child的实例上,这就实现了继承。 61 | 62 | 打印结果: 63 | 64 | [![img](https://camo.githubusercontent.com/bf51068315e7795528674c2cbab3e5cf0f830288/687474703a2f2f696d672e736d79687661652e636f6d2f32303138303330375f313031352e706e67)](https://camo.githubusercontent.com/bf51068315e7795528674c2cbab3e5cf0f830288/687474703a2f2f696d672e736d79687661652e636f6d2f32303138303330375f313031352e706e67) 65 | 66 | 上方结果表明:child先有了 parent 实例的属性(继承得以实现),再有了child 实例的属性。 67 | 68 | **分析**: 69 | 70 | 这种方式,虽然改变了 this 的指向,但是,**Child1 无法继承 Parent1 的原型**。也就是说,如果我给 Parent1 的原型增加一个方法: 71 | 72 | ```JavaScript 73 | Parent1.prototype.say = function () { 74 | }; 75 | ``` 76 | 77 | 上面这个方法是无法被 Child1 继承的。如下: 78 | 79 | [![img](https://camo.githubusercontent.com/af560b5f5f86c53c49c07f383ea4b491131d6d28/687474703a2f2f696d672e736d79687661652e636f6d2f32303138303330375f313033302e706e67)](https://camo.githubusercontent.com/af560b5f5f86c53c49c07f383ea4b491131d6d28/687474703a2f2f696d672e736d79687661652e636f6d2f32303138303330375f313033302e706e67) 80 | 81 | ### 1.2.2. 方法二:通过原型链实现继承 82 | 83 | ```JavaScript 84 | /* 85 | 通过原型链实现继承 86 | */ 87 | function Parent() { 88 | this.name = 'Parent 的属性'; 89 | } 90 | 91 | function Child() { 92 | this.type = 'Child 的属性'; 93 | } 94 | 95 | Child.prototype = new Parent(); //【重要】 96 | 97 | console.log(new Child()); 98 | ``` 99 | 100 | 打印结果: 101 | 102 | [![img](https://camo.githubusercontent.com/b6ac94e16b1d99af84e707a06989e9cd1ad4d277/687474703a2f2f696d672e736d79687661652e636f6d2f32303138303330375f313130392e706e67)](https://camo.githubusercontent.com/b6ac94e16b1d99af84e707a06989e9cd1ad4d277/687474703a2f2f696d672e736d79687661652e636f6d2f32303138303330375f313130392e706e67) 103 | 104 | 【重要】上方代码中,最重要的那行:每个函数都有`prototype`属性,于是,构造函数也有这个属性,这个属性是一个对象。现在,**我们把Parent的实例赋值给了Child的prototye**,从而实现**继承**。此时,`Child`构造函数、`Parent`的实例、`Child`的实例构成一个三角关系。于是: 105 | 106 | - `new Child.__proto__ === new Parent()`的结果为true 107 | 108 | **分析:**: 109 | 110 | 这种继承方式,**Child 可以继承 Parent 的原型**,但有个缺点: 111 | 112 | 缺点是:**如果修改 child1实例的name属性,child2实例中的name属性也会跟着改变**。 113 | 114 | 如下: 115 | 116 | [![img](https://camo.githubusercontent.com/0d3066ad49084589204a496ec889b335aa131b7d/687474703a2f2f696d672e736d79687661652e636f6d2f32303138303330375f313132332e706e67)](https://camo.githubusercontent.com/0d3066ad49084589204a496ec889b335aa131b7d/687474703a2f2f696d672e736d79687661652e636f6d2f32303138303330375f313132332e706e67) 117 | 118 | 上面的代码中, child1修改了arr属性,却发现,child2的arr属性也跟着改变了。这显然不太好,在业务中,两个子模块应该隔离才对。如果改了一个对象,另一个对象却发生了改变,就不太好。 119 | 120 | 造成这种缺点的原因是:child1和child2共用原型。即:`chi1d1.__proto__ === child2__proto__`是严格相同。而 arr方法是在 Parent 的实例上(即 Child实例的原型)的。 121 | 122 | ## 1.3. 方式三:组合的方式:构造函数 + 原型链 123 | 124 | 就是把上面的两种方式组合起来: 125 | 126 | ```JavaScript 127 | /* 128 | 组合方式实现继承:构造函数、原型链 129 | */ 130 | function Parent3() { 131 | this.name = 'Parent 的属性'; 132 | this.arr = [1, 2, 3]; 133 | } 134 | 135 | function Child3() { 136 | Parent3.call(this); //【重要1】执行 parent方法 137 | this.type = 'Child 的属性'; 138 | } 139 | Child3.prototype = new Parent3(); //【重要2】第二次执行parent方法 140 | 141 | var child = new Child3(); 142 | ``` 143 | 144 | 这种方式,能解决之前两种方式的问题:既可以继承父类原型的内容,也不会造成原型里属性的修改。 145 | 146 | 这种方式的缺点是:让父亲Parent的构造方法执行了两次。 147 | 148 | ES6中的继承方式,一带而过即可,重点是要掌握ES5中的继承。 -------------------------------------------------------------------------------- /JavaScript高级面试/6. hybrid.md: -------------------------------------------------------------------------------- 1 | # 1. hybrid 2 | 3 | > - 移动端占大部分流量,已经远超过 PC 4 | > - 一线互联网公司都有自己的 App 5 | > - 这些 App 中有很大比例的前端代码 6 | > - 拿微信举例,你每天浏览微信的内容,多少是前端? 7 | 8 | ## 1.1. hybrid 是什么?为何用 hybrid? 9 | 10 | - hybrid 是客户端和前端的混合开发 11 | - hybrid 存在的核心意义在于快速迭代,无需审核 12 | 13 | ### 1.1.1. hybrid 文字解释 14 | 15 | - hybrid 即“混合”,即前端和客户端的混合开发 16 | - 需前端开发人员和客户端开发人员配合完成 17 | - 某些环节也可能涉及到 server 端 18 | - 不要以为自己是前端就可以不理会客户端的知识 19 | 20 | ### 1.1.2. 存在价值,为何会用 hybrid 21 | 22 | - 可以快速迭代更新,无需 App 审核(关键) 23 | - app 的代码有权利访问到手机的地理位置声音视频等 api,用这些功能开发的 app 肯定需要严格审核 24 | - hybrid 的代码是纯前端开发的,无需这种审核机制 25 | - 体验流畅(和 NA 的体验基本一致) 26 | - 减少开发和沟通成本,双端公用一套代码 27 | 28 | ### 1.1.3. webview 29 | 30 | - 是 app 中的一个组件(app 中可以有 webview,也可以没有) 31 | - 用于加载 h5 页面,即一个小型的浏览器内核 32 | 33 | ### 1.1.4. file://协议 34 | 35 | - 其实在一开始接触 html 开发,就已经使用了 file 协议 36 | - 只不过你当时没有“协议”、“标准”等这些概念 37 | - 再次强调“协议”、“标准”的重要性 38 | 39 | http 协议和 file 协议的区别 40 | 41 | - file 协议:本地文件,快 42 | - http 协议:网络加载,慢 43 | 44 | ### 1.1.5. hybrid 实现流程 45 | 46 | - 不是所有场景都适合使用 hybrid 47 | - 使用 NA:体验要求极致,变化不频繁(头条首页) 48 | - 使用 hybrid:体验要求高,变化频繁(头条的新闻详情页) 49 | - 使用 h5:体验无要求,不常用(举报、反馈等页面) 50 | 51 | ![hybrid](../images/hybrid.png) 52 | 具体流程图 53 | 54 | - 前端做好静态页面(html js css),将文件交给客户端 55 | - 客户端拿到前端静态页面,以文件形式存储在 app 中 56 | - 客户端在一个 webview 中 57 | - 使用 file 协议加载静态页面 58 | 59 | ## 1.2. 介绍一下 hybrid 更新和上线的流程? 60 | 61 | - 服务端的版本和 zip 包维护 62 | - 更新 zip 包之前,先对比版本号 63 | - zip 下载解压和覆盖 64 | 65 | ### 1.2.1. 目的,可行途径 66 | 67 | - 要替换每个客户端的静态文件 68 | - 只能客户端来做(客户端是我们开发的) 69 | - app 去 server 端下载最新的静态文件 70 | - 我们维护 server 的静态文件 71 | 72 | ### 1.2.2. 完整流程 73 | 74 | - 分版本,有版本号,如 201803211015 75 | - 将静态文件压缩成 zip 包,上传到服务端 76 | - 客户端每次启动,都去服务端检查版本号 77 | - 如果服务端版本号大于客户端版本号,就去下载最新的 zip 包 78 | - 下载完之后解压包,然后将现有文件覆盖 79 | 80 | ## 1.3. hybrid 和 h5 的主要区别 81 | 82 | ### 1.3.1. 优点 83 | 84 | - 体验更好,跟 NA 体验基本一致 85 | - 可快速迭代,无需 app 审核 86 | 87 | ### 1.3.2. 缺点 88 | 89 | - 开发成本高。联调、测试、查 bug 都比较麻烦 90 | - 运维成本高,上线很麻烦 91 | 92 | ### 1.3.3. 适用的场景 93 | 94 | - hybrid:产品型,产品的稳定功能,体验要求高,迭代频繁 95 | - h5:运营型,单次的运营活动(红包)或不常用的功能 96 | 97 | ## 1.4. 前端 JS 和客户端如何通讯? 98 | 99 | - 通讯的基本形式:调用能力、传递参数、监听回调 100 | - 对 schema 协议的理解和使用 101 | - 调用 schema 代码的封装 102 | - 内置上线的好处:更快,更安全 103 | 104 | ### 1.4.1. 新闻详情页适用 hybrid,前端如何获取新闻内容? 105 | 106 | - 不能用 ajax。跨域问题,速度慢 107 | - 客户端获取新闻内容,然后 JS 通讯拿到内容,再渲染 108 | 109 | ### 1.4.2. JS 和客户端通讯的基本形式 110 | 111 | ![JS和客户端通讯](../images/JS和客户端通讯.png) 112 | 113 | - JS 访问客户端能力,传递参数和回调函数 114 | - 客户端通过回调函数返回内容 115 | 116 | ### 1.4.3. schema 协议简介和使用 117 | 118 | - 之前介绍了 http 和 file 协议 119 | - schema 协议 —— 前端和客户端通讯的约定 120 | 121 | 微信的部分 schema 协议 122 | 123 | ```schema 124 | weixin://dl/scan 扫一扫 125 | weixin://dl/feedback 反馈 126 | weixin://dl/moments 朋友圈 127 | weixin://dl/settings 设置 128 | ``` 129 | 130 | ```js 131 | function invokeScan() { 132 | window['_invoke_scan_callback_'] = function(result) { 133 | alert(result) 134 | } 135 | 136 | var iframe = document.createElement('iframe') 137 | iframe.style.display = 'none' 138 | // iframe.src = 'weixin://dl/scan' // 重要! 139 | iframe.src = 140 | 'weixin://dl/scan?k1=v1&k2=v2&k3=v3&callback=_invoke_scan_callback_' 141 | var body = document.body 142 | body.appendChild(iframe) 143 | setTimeout(function() { 144 | body.removeChild(iframe) 145 | iframe = null 146 | }) 147 | } 148 | ``` 149 | 150 | ### 1.4.4. schema 使用的封装 151 | 152 | ```js 153 | // 封装效果,傻瓜式调用,而且不用再自己定义全局函数 154 | window.invoke.share( 155 | { 156 | title: 'xxx', 157 | content: 'yyy' 158 | }, 159 | function(result) { 160 | if (result.errno === 0) { 161 | alert('分享成功') 162 | } else { 163 | alert(result.message) 164 | } 165 | } 166 | ) 167 | ``` 168 | 169 | schema 封装 170 | 171 | ```js 172 | ;(function(window, undefined) { 173 | // 调用 schema 的封装 174 | function _invoke(action, data, callback) { 175 | // 拼装 schema 协议 176 | var schema = 'myapp://utils/' + action 177 | 178 | // 拼接参数 179 | schema += '?a=a' 180 | var key 181 | for (key in data) { 182 | if (data.hasOwnProperty(key)) { 183 | schema += '&' + key + data[key] 184 | } 185 | } 186 | 187 | // 处理 callback 188 | var callbackName = '' 189 | if (typeof callback === 'string') { 190 | callbackName = callback 191 | } else { 192 | callbackName = action + Date.now() 193 | window[callbackName] = callback 194 | } 195 | schema += 'callback=callbackName' 196 | 197 | // 触发 198 | var iframe = document.createElement('iframe') 199 | iframe.style.display = 'none' 200 | iframe.src = schema // 重要! 201 | var body = document.body 202 | body.appendChild(iframe) 203 | setTimeout(function() { 204 | body.removeChild(iframe) 205 | iframe = null 206 | }) 207 | } 208 | 209 | // 暴露到全局变量 210 | window.invoke = { 211 | share: function(data, callback) { 212 | _invoke('share', data, callback) 213 | }, 214 | scan: function(data, callback) { 215 | _invoke('scan', data, callback) 216 | }, 217 | login: function(data, callback) { 218 | _invoke('login', data, callback) 219 | } 220 | } 221 | })(window) 222 | ``` 223 | 224 | ### 1.4.5. 内置上线 225 | 226 | - 将以上封装的代码打包,叫做 invoke.js,内置到客户端 227 | - 客户端每次启动 webview,都默认执行 invoke.js 228 | - 本地加载,免去网络加载的时间,更快 229 | - 本地加载,没有网络请求,黑客看不到 schema 协议,更安全 230 | -------------------------------------------------------------------------------- /3. DOM事件.md: -------------------------------------------------------------------------------- 1 | # 1. DOM 事件类 2 | 3 | 4 | 5 | - [1. DOM 事件类](#1-dom-事件类) 6 | - [1.1. DOM 事件的级别](#11-dom-事件的级别) 7 | - [1.1.1. DOM 0 级事件](#111-dom-0-级事件) 8 | - [1.1.1.1. 定义事件处理程序](#1111-定义事件处理程序) 9 | - [1.1.2. DOM2 级事件](#112-dom2-级事件) 10 | - [1.2. DOM 事件模型](#12-dom-事件模型) 11 | - [1.3. DOM 事件流](#13-dom-事件流) 12 | - [1.3.1. 事件冒泡](#131-事件冒泡) 13 | - [1.3.2. 事件捕获](#132-事件捕获) 14 | - [1.3.3. DOM 事件流](#133-dom-事件流) 15 | - [1.4. event 对象](#14-event-对象) 16 | - [1.4.1. DOM 中的事件对象](#141-dom-中的事件对象) 17 | - [1.4.2. IE 中的事件对象](#142-ie-中的事件对象) 18 | - [1.4.3. 跨浏览器的事件对象](#143-跨浏览器的事件对象) 19 | - [1.5. 自定义事件](#15-自定义事件) 20 | 21 | 22 | 23 | ## 1.1. DOM 事件的级别 24 | 25 | ### 1.1.1. DOM 0 级事件 26 | 27 | > 通过 `JavaScript` 指定事件处理程序的传统方式,就是将一个函数赋值给一个事件处理程序属性。这种为事件处理程序赋值的方法是在第四代 Web 浏览器中出现的,而且至今仍然为所有现代浏览器所支持。原因一是简单,二是具有跨浏览器的优势。 28 | 29 | #### 1.1.1.1. 定义事件处理程序 30 | 31 | - 使用 JS 代码来给事件指定事件处理程序,方法是:将一个函数赋值给一个事件处理程序属性。 32 | - 每个元素都有自己的事件处理程序属性,这些属性的名字与事件处理程序的名字相同,如 `onclick`。 33 | 34 | ```JavaScript 35 | var btn = document.getElementById("myBtn"); 36 | //为按钮指定onclick事件处理程序 37 | btn.onclick = function(){ 38 | alert("Clicked"); 39 | } 40 | ``` 41 | 42 | **注意**: 43 | 44 | - 使用 DOM0 级方法指定的事件处理程序被认为是元素的方法。因此,这时候的事件处理程序是在元素的作用域中运行;换句话说,程序中的 this 引用当前元素。 45 | - 单击按钮显示的是元素的 ID,这个 ID 是通过 `this.id` 取得的。不仅仅是 ID,实际上可以在事件处理程序中通过 `this` 访问元素的任何属性和方法。以这种方式添加的事件处理程序会在事件流的冒泡阶段被处理 46 | 47 | ### 1.1.2. DOM2 级事件 48 | 49 | “DOM2 级事件”定义了两个方法,用于处理指定和删除事件处理程序的操作: 50 | 51 | - addEventListener(要处理的事件名,事件处理程序函数,布尔值) 52 | - 布尔值取值为 false:在冒泡阶段调用事件处理程序 53 | - 布尔值取值为 true:在捕获阶段调用事件处理程序 54 | - removeEventListener(要处理的事件名,事件处理程序函数,布尔值) 55 | 56 | 大多数都是将事件处理程序添加到冒泡阶段,从而保证跨浏览器的兼容性,故通常都是将最后一个参数设为 false 57 | 所有的 DOM 节点都包含上述两个方法 58 | DOM2 级添加的事件处理程序也是在其依附的元素的作用域中运行的 59 | 使用 DOM2 级方法可以为同一个元素添加多个事件处理程序,这些事件处理程序会按照添加它们的顺序从上到下执行(DOM0 级只能为同一个元素添加一个事件处理程序) 60 | 61 | ## 1.2. DOM 事件模型 62 | 63 | ## 1.3. DOM 事件流 64 | 65 | DOM 事件流指从页面中接收事件的顺序,有冒泡流和捕获流。 66 |   当页面中发生某种事件(比如鼠标点击,鼠标滑过等)时,毫无疑问子元素和父元素都会接收到该事件,可具体顺序是怎样的呢?IE 和 Netscape 开发团队提出了几乎是完全相反的事件流概念。 67 | 68 | ### 1.3.1. 事件冒泡 69 | 70 | > IE 的事件流叫做**事件冒泡**(event bubbling),即事件开始时由文档中嵌套层次最深的那个节点触发,然后逐级向上传播到其父元素依次被触发。 71 | 72 | ![bubble](images/01-冒泡.png) 73 | 74 | **拓展** : 阻止事件冒泡:`event.stopPropagation()` 75 | 76 | ```JavaScript 77 | //1. 因为是事件的冒泡,因事件引起,也要因事件停止 78 | father/son/sun.onclick = function (event) { 79 | 80 | //stop :停止 propagation:传播 81 | event.stopPropagation(); 82 | } 83 | 84 | // onclick : 默认就是 第三个参数为 false: 85 | father.addEventListener('click',function(){ 86 | alert('father'); 87 | },false); 88 | ``` 89 | 90 | ### 1.3.2. 事件捕获 91 | 92 | > Netscape Communicator 团队提出的另一种事件流叫做**事件捕获**(event capturing),事件的处理将从 DOM 层次的根开始,而不是从触发事件的目标元素开始,事件被从目标元素的所有祖先元素依次往下传递。 93 | 94 | ![bubble](images/02-捕获.png) 95 | 96 | ```JavaScript 97 | //当addEventListener第三个参数为true时,表示事件捕获 98 | // 参数3 : 是否捕获 99 | arr[i].addEventListener("click",function() { 100 | console.log(this); 101 | },true); 102 | ``` 103 | 104 | ### 1.3.3. DOM 事件流 105 | 106 | “DOM2 级事件”规定的时间流包括三个阶段: 107 | 108 | 1. 事件的捕获阶段 109 | 2. 事件的目标阶段(触发自己的事件) 110 | 3. 事件的冒泡阶段 111 | 112 | ![事件的三个阶段](images/03-事件流三个阶段.png) 113 | 114 | - 若捕获事件和冒泡事件都存在,首先发生的是捕获阶段,然后是目标阶段,最后才是冒泡阶段。 115 | - `addEventListener()` 第三个参数为是否捕获: 116 | - 如果为 `true` 时, 表示该事件在捕获阶段发生, 117 | - 如果为 `false` 时 , 表示该事件在冒泡阶段发生, 118 | 119 | **兼容 :** IE8 及更早版本不支持 DOM 事件流。 120 | 121 | ## 1.4. event 对象 122 | 123 | ### 1.4.1. DOM 中的事件对象 124 | 125 | 在触发 DOM 上的某个事件时,会产生一个事件对象 event,这个对象中包含着所有与事件有关的信息。包括导致事件的元素、事件的类型以及与特定事件相关的信息。鼠标事件触发时,事件对象中会包含鼠标的位置信息。键盘事件触发时,事件对象中会包含按下的键相关的信息。 126 | 127 | 现代浏览器获取 : 128 | 129 | ```JavaScript 130 | // 给一个形参即可 131 | btn.onclick = function(event){ 132 | //event 就是事件对象,里面包含了事件触发时的一些信息。 133 | console.log(event); 134 | } 135 | ``` 136 | 137 | 低版本浏览器 (ie678): 138 | 139 | ```JavaScript 140 | btn.onclick = function(){ 141 | //IE678 通过 window.event 获取事件对象 142 | console.log(window.event); 143 | } 144 | ``` 145 | 146 | 兼容性 : 147 | 148 | ```JavaScript 149 | btn.onclick = function(event){ 150 | //只要用到了事件对象,就要记得处理浏览器兼容性 151 | event = event || window.event; 152 | } 153 | ``` 154 | 155 | event 对象包含与创建它的特定事件有关的属性和方法。出发的事件类型不一样,可用的属性和方法也不一样。不过,所有事件都会有下表列出的成员。 156 | | 属性/方法 | 类型 | 读/写 | 说明 | 157 | | -------------------------- | -------- | ----- | ---------------------------------------------------------------------- | 158 | | bubbles | Boolean | 只读 | 表明事件是否冒泡 | 159 | | cancelable | Boolean | 只读 | 表明是否可以取消事件的默认行为 | 160 | | currentTarget | Element | 只读 | 表明事件处理程序当前正在处理的元素 | 161 | | defaultPervented | Boolean | 只读 | 为 true 表示已经调用了 preventDefault()方法 | 162 | | detail | Integer | 只读 | 表示与事件相关的细节信息 | 163 | | eventPhase | Integer | 只读 | 调用事件处理程序的阶段:1 表示捕获阶段,2 表示目标阶段,3 表示冒泡阶段 | 164 | | preventDefault() | Function | 只读 | 取消事件的默认行为 | 165 | | stopImmediatePropagation() | Function | 只读 | 取消事件的进一步捕获或冒泡,同时阻止任何事件处理程序被调用 | 166 | | stopPropagation() | Function | 只读 | 阻止事件进一步捕获或冒泡 | 167 | | target | Element | 只读 | 目标元素 | 168 | | type | String | 只读 | 被触发的事件类型 | 169 | 170 | --- 171 | 172 | ### 1.4.2. IE 中的事件对象 173 | 174 | | 属性/方法 | 类型 | 读/写 | 说明 | 175 | | ------------ | ------- | ----- | ------------------------------------------------------------------- | 176 | | cancelBubble | Boolean | 读写 | 默认为 false。用于取消事件冒泡,与 DOM 中的 stopPropagation()相同 | 177 | | returnValue | Boolean | 读写 | 默认为 true,用于取消默认行为,与 DOM 中的 preventDeafault()相同 | 178 | | srcElement | Element | 只读 | 事件目标元素,与 DOM 中的 target 相同 | 179 | | type | String | 只读 | 被触发的事件类型 | 180 | 181 | ### 1.4.3. 跨浏览器的事件对象 182 | 183 | ```JavaScript 184 | var eventUtil = { 185 | addHandler: function(ele, type, handler) { 186 | // 代码在上文, 187 | }, 188 | getEvent: function(event) { 189 | return event || window.event; 190 | }, 191 | getTarget: function(event) { 192 | // 该语句可以不用,关键是传入的event是否已经被处理 193 | // var event = event || window.event; 194 | return event.target || event.srcElement; 195 | }, 196 | preventDefault: function(event) { 197 | if(event.preventDefault) { 198 | event.preventDefault(); 199 | } else { 200 | event.returnValue = false; 201 | } 202 | }, 203 | removeHandler: function(ele, type, handler) { 204 | // 省略代码 205 | }, 206 | stopPropagation: function(event) { 207 | if(event.stopPropagation) { 208 | event.stopPropagation((); 209 | } else { 210 | event.cancelBubble = true; 211 | } 212 | }, 213 | }; 214 | ``` 215 | 216 | ## 1.5. 自定义事件 217 | 218 | 自定义事件的写法 219 | 220 | ```JavaScript 221 | //1、创建事件 222 | var eve = new Event('custome'); 223 | //2、注册事件监听器 224 | ev.addEventListener('custome', function() { 225 | console.log('custome'); 226 | }); 227 | //3、触发事件 228 | ev.dispatchEvent(eve); 229 | ``` 230 | -------------------------------------------------------------------------------- /JavaScript高级面试/3. 异步.md: -------------------------------------------------------------------------------- 1 | # 1. 异步 2 | 3 | ## 1.1. 什么是单线程,和异步有什么关系 4 | 5 | ### 1.1.1. 单线程:只有一个线程,同一时间只能做一件事情 6 | 7 | ```JavaScript 8 | // 循环运行期间,JS执行和DOM渲染暂时卡顿 9 | console.log('start'); 10 | var i, sum = 0; 11 | for (i = 0; i < 1000000000; i++){ 12 | sum += i; 13 | } 14 | console.log(sum); // 上面循环很长,导致打印卡顿 15 | 16 | // alert不处理,JS执行和DOM渲染暂时卡顿 17 | console.log(1); 18 | alert('hello') // 弹框出来,不点确认的话下面的2是打印不出来的 19 | console.log(2); 20 | ``` 21 | 22 | ### 1.1.2. 原因:避免 DOM 渲染的冲突 23 | 24 | - 浏览器需要渲染 DOM 25 | - JS 可以修改 DOM 结构 26 | - JS 执行的时候,浏览器 DOM 渲染就会停止 27 | - 两段 JS 也不能同时执行(同时修改 DOM 就冲突了) 28 | - webworker 支持多线程,但是不能访问 DOM 29 | 30 | ### 1.1.3. 解决方案:异步 31 | 32 | 异步是单线程的唯一解决方案 33 | 34 | ```JavaScript 35 | console.log(100); 36 | setTimeout(() => { 37 | console.log(200); 38 | }, 1000); 39 | console.log(300); 40 | console.log(400); 41 | 42 | // 100 43 | // 300 44 | // 400 45 | // 200 46 | ``` 47 | 48 | ```JavaScript 49 | console.log(100); 50 | $.ajax({ 51 | url: 'xxx', 52 | success: function(result) { 53 | console.log(result); 54 | } 55 | }) 56 | console.log(300); 57 | console.log(400); 58 | ``` 59 | 60 | - 问题一:没按照书写方式执行,可读性差 61 | - 问题二:callback 中不容易模块化 62 | 63 | ### 1.1.4. 问题解答 64 | 65 | - 单线程就是同一时间只能做一件事,两段 JS 不能同时执行 66 | - 原因就是为了避免 DOM 渲染的冲突 67 | - 异步算是一种“无奈”的解决方案,虽然很高效,但是还是有很多问题存在 68 | 69 | ## 1.2. 什么是 event-loop 事件轮询 70 | 71 | - 事件轮询:JS 实现异步的具体解决方案 72 | - 同步代码,直接执行 73 | - 异步函数先放在异步队列中 74 | - 待同步函数执行完毕,轮询执行异步队列的函数 75 | 76 | ## 1.3. 是否用过 jQuery 的 Deferred 77 | 78 | ### 1.3.1. jQuery 1.5 的变化 79 | 80 | jQuery1.5 之前 81 | 82 | ```JavaScript 83 | var ajax = $.ajax({ 84 | url: 'data.json', 85 | success: function () { 86 | console.log('success1'); 87 | console.log('success2'); 88 | console.log('success3'); 89 | }, 90 | error: function () { 91 | console.log('error'); 92 | } 93 | }) 94 | console.log(ajax); // 返回一个 XHR 对象 95 | ``` 96 | 97 | jQuery1.5 之后 98 | 99 | ```JavaScript 100 | var ajax = $.ajax('data.json') 101 | ajax.done(function () { 102 | console.log('success1'); 103 | }) 104 | .fail(function () { 105 | console.log('error'); 106 | }) 107 | .done(function () { 108 | console.log('success2'); 109 | }) 110 | console.log(ajax); // 返回一个deferred 对象 111 | ``` 112 | 113 | ```JavaScript 114 | // 很像 Promise 的写法 115 | var ajax = $.ajax('data.json') 116 | ajax.then(function () { 117 | console.log('success1') 118 | }, function () { 119 | console.log('error1') 120 | }) 121 | .then(function () { 122 | console.log('success2') 123 | }, function () { 124 | console.log('error2') 125 | }) 126 | ``` 127 | 128 | - 无法改变 JS 异步和单线程的本质 129 | - 只能从写法上杜绝 callback 这种形式 130 | - 它是一种语法糖形式,但是解耦了代码 131 | - 很好的体现了开放封闭原则(对扩展开放,对修改封闭) 132 | 133 | ### 1.3.2. 使用 jQuery Deferred 134 | 135 | 给出一段非常简单的异步操作代码,使用 setTimeout 函数 136 | 137 | ```JavaScript 138 | var wait = function () { 139 | var task = function () { 140 | console.log('执行完成'); 141 | } 142 | setTimeout(task, 2000) 143 | } 144 | wait() 145 | 146 | ``` 147 | 148 | 新增需求:要在执行完成之后进行一些特别复杂的操作,代码可能会很多,而且分好几个步骤 149 | 150 | ```JavaScript 151 | function waitHandle() { 152 | var dtd = $.Deferred() // 创建一个deferred对象 153 | 154 | var wait = function (dtd) { // 要求传入一个deferred对象 155 | var task = function () { 156 | console.log('执行完成') 157 | dtd.resolve() // 表示异步任务已经完成 158 | dtd.reject() // 失败 159 | } 160 | setTimeout(task, 2000) 161 | return dtd // 要求返回deferred对象 162 | } 163 | 164 | // 注意,这里一定要有返回值 165 | return wait(dtd) 166 | } 167 | 168 | var w = waitHandle() 169 | w.then(function () { 170 | console.log('ok 1') 171 | }, function () { 172 | console.log('err 1') 173 | }).then(function () { 174 | console.log('ok 2') 175 | }, function () { 176 | console.log('err 2') 177 | }) 178 | 179 | // 执行dtd.resolve()则打印ok1 ok2 180 | // 执行dtd.reject()则打印err1 err2 181 | ``` 182 | 183 | - 总结:dtd 的 API 可分成两类,用意不同 184 | - 第一类:dtd.resolve dtd.reject 185 | - 第二类:dtd.then dtd.done dtd.fail 186 | - 这两类应该分开,否则后果很严重 187 | - 可以在上面代码最后执行 dtd.reject()试一下后果 188 | 189 | ### 1.3.3. 问题解答 190 | 191 | - 可以 jQuery1.5 对 ajax 的改变来举例 192 | - 说明如何简单的封装、使用 Deferred 193 | - 说明 promise 和 Deferred 的区别 194 | 195 | ## 1.4. Promise 的基本使用和原理 196 | 197 | ### 1.4.1. 基本语法回顾 198 | 199 | ```JavaScript 200 | function loadImg(src) { 201 | var promise = new Promise(function (resolve, reject) { 202 | var img = document.createElement('img') 203 | // throw new Error('自定义错误') 204 | img.onload = function () { 205 | resolve(img) 206 | } 207 | img.onerror = function () { 208 | reject('图片加载失败') 209 | } 210 | img.src = src 211 | }) 212 | return promise 213 | } 214 | 215 | var src = 'https://www.imooc.com/static/img/index/logo_new.png' 216 | var result = loadImg(src) 217 | result.then(function (img) { 218 | console.log(1,img.width) 219 | return img 220 | }, function () { 221 | console.log('error 1') 222 | }).then(function (img) { 223 | console.log(1, img.height) 224 | }) 225 | ``` 226 | 227 | ### 1.4.2. 异常捕获 228 | 229 | then 只接受一个参数,最后统一用 catch 捕获异常 230 | 231 | ```JavaScript 232 | var src = 'https://www.imooc.com/static/img/index/logo_new.png' 233 | var result = loadImg(src) 234 | result.then(function (img) { 235 | console.log(1, img.width) 236 | return img 237 | }).then(function (img) { 238 | console.log(2, img.height) 239 | }).catch(function (ex) { 240 | // 统一捕获异常 241 | console.log(ex) 242 | }) 243 | ``` 244 | 245 | ### 1.4.3. 多个串联 246 | 247 | ```JavaScript 248 | var src1 = 'https://www.imooc.com/static/img/index/logo_new.png' 249 | var result1 = loadImg(src1) 250 | var src2 = 'https://img1.mukewang.com/545862fe00017c2602200220-100-100.jpg' 251 | var result2 = loadImg(src2) 252 | 253 | // 链式操作 254 | result1.then(function (img1) { 255 | console.log('第一个图片加载完成', img1.width) 256 | return result2 // 重要!!! 257 | }).then(function (img2) { 258 | console.log('第二个图片加载完成', img2.width) 259 | }).catch(function (ex) { 260 | // 最后统一catch 261 | console.log(ex) 262 | }) 263 | ``` 264 | 265 | ### 1.4.4. Promise.all 和 Promise.race 266 | 267 | ```JavaScript 268 | // Promise.all 接收一个 promise 对象的数组 269 | // 待全部完成之后,统一执行 success 270 | Promise.all([result1, result2]).then((datas) => { 271 | // 接收到的datas是一个数组,依次包含了多个promise返回的内容 272 | console.log(datas[0]) 273 | console.log(datas[1]) 274 | }) 275 | 276 | // Promise.race 接收一个包含多个promise对象的数组 277 | // 只要有一个完成,就执行seccess 278 | Promise.race([result1, result2]).then((data) => { 279 | // data即最先执行完成的promise的返回值 280 | console.log(data) 281 | }) 282 | ``` 283 | 284 | ### 1.4.5. Promise 标准 285 | 286 | - “标准” 287 | - 任何技术推广使用都需要一套标准来支撑 288 | - 如 html js css http 等,无规矩不方圆 289 | - 任何不符合标准的东西,终将会被用户抛弃 290 | - 不要挑战标准,不要自造标准 291 | - 状态变化 292 | - 三种状态:pending、fulfilled、rejected 293 | - 初始状态:pending 294 | - pending 变为 fulfilled,或者 pending 变为 rejected 295 | - 状态变化不可逆 296 | - then 297 | - Promise 实例必须实现 then 这个方法 298 | - then()必须可以接收两个函数作为参数 299 | - then()返回的必须是个 Promise 实例 300 | 301 | ### 1.4.6. 问题解答 302 | 303 | - 基础语法 304 | - 如何捕获异常(Error 和 reject 都要考虑) 305 | - 多个串联,链式执行的好处 306 | - Promise.all 和 Promise.race 307 | - Promise 标准 308 | 309 | ## 1.5. 介绍一下 async/await (和 Promise 的区别、联系) 310 | 311 | - then 只是将 callback 拆分了 312 | - async/await 是最直接的同步写法 313 | 314 | 用法: 315 | 316 | - 使用 await,函数必须用 async 标识 317 | - await 后面跟的是一个 Promise 实例 318 | - 需要 babel-polyfill 319 | 320 | ```JavaScript 321 | const load = async function () { 322 | const result1 = await loadImg(src1) 323 | console.log(result1) 324 | const result2 = await loadImg(src2) 325 | console.log(result2) 326 | } 327 | 328 | load() 329 | ``` 330 | 331 | ### 1.5.1. 问题解答 332 | 333 | - 基本语法 334 | - async/await 使用了 Promise,并没有和 Promise 冲突 335 | - promise 是对异步回调的一个封装,promise 标准中,如果封装完了之后要使用的话要有一个.then .then .catch 的写法 336 | - async/await 使用了 promise 的封装,但是是完全同步的写法。并不是取代了 promise,而是和 promise 进行了完美的兼容 337 | - 但是,任何写法的改变都改变不了 JS 单线程、异步的本质 338 | 339 | ## 1.6. 总结一下当前 JS 解决异步的方案 340 | 341 | - jQuery Deferred 342 | - Promise 343 | - Async/Await 344 | - Generator(不推荐) 345 | - 原理比较复杂 346 | - 不是异步的直接替代方式 347 | - 有更好更简洁的解决方案 async/await 348 | - koa 也弃用了 349 | -------------------------------------------------------------------------------- /4. http协议类.md: -------------------------------------------------------------------------------- 1 | # 1. HTTP 协议类 2 | 3 | 4 | 5 | - [1. HTTP 协议类](#1-http-协议类) 6 | - [1.1. HTTP 协议的主要特点](#11-http-协议的主要特点) 7 | - [1.2. HTTP 之 URL](#12-http-之-url) 8 | - [1.3. URI 、 URL 和 URN 的区别](#13-uri--url-和-urn-的区别) 9 | - [1.4. HTTP 报文的组成部分](#14-http-报文的组成部分) 10 | - [1.5. HTTP 方法](#15-http-方法) 11 | - [1.6. HTTP 工作原理](#16-http-工作原理) 12 | - [1.7. POST 和 GET 的区别](#17-post-和-get-的区别) 13 | - [1.8. HTTP 状态码](#18-http-状态码) 14 | - [1.9. 什么是持久连接](#19-什么是持久连接) 15 | - [1.10. 什么是管线化](#110-什么是管线化) 16 | 17 | 18 | 19 | HTTP 协议是 Hyper Text Transfer Protocol(超文本传输协议)的缩写,是用于从万维网(WWW:World Wide Web )服务器传输超文本到本地浏览器的传送协议。 20 | 21 | HTTP 是一个基于 TCP/IP 通信协议来传递数据(HTML 文件, 图片文件, 查询结果等)。 22 | 23 | HTTP 是一个属于应用层的面向对象的协议,由于其简捷、快速的方式,适用于分布式超媒体信息系统。它于 1990 年提出,经过几年的使用与发展,得到不断地完善和扩展。目前在 WWW 中使用的是 HTTP/1.0 的第六版,HTTP/1.1 的规范化工作正在进行之中,而且 HTTP-NG(Next Generation of HTTP)的建议已经提出。 24 | 25 | HTTP 协议工作于客户端-服务端架构为上。浏览器作为 HTTP 客户端通过 URL 向 HTTP 服务端即 WEB 服务器发送所有请求。Web 服务器根据接收到的请求后,向客户端发送响应信息。 26 | 27 | ## 1.1. HTTP 协议的主要特点 28 | 29 | 1. 简单快速 30 | 客户向服务器请求服务时,只需传送请求方法和路径。请求方法常用的有 GET、HEAD、POST。每种方法规定了客户与服务器联系的类型不同。由于 HTTP 协议简单,使得 HTTP 服务器的程序规模小,因而通信速度很快。 31 | 2. 灵活 32 | HTTP 允许传输任意类型的数据对象。正在传输的类型由 Content-Type 加以标记。 33 | 3. 无连接 34 | 无连接的含义是限制每次连接只处理一个请求。服务器处理完客户的请求,并收到客户的应答后,即断开连接。采用这种方式可以节省传输时间。 35 | 4. 无状态 36 | HTTP 协议是无状态协议。无状态是指协议对于事务处理没有记忆能力。缺少状态意味着如果后续处理需要前面的信息,则它必须重传,这样可能导致每次连接传送的数据量增大。另一方面,在服务器不需要先前信息时它的应答就较快。 37 | 38 | ## 1.2. HTTP 之 URL 39 | 40 | HTTP 使用统一资源标识符(Uniform Resource Identifiers, URI)来传输数据和建立连接。URL 是一种特殊类型的 URI,包含了用于查找某个资源的足够的信息 URL,全称是 UniformResourceLocator, 中文叫统一资源定位符,是互联网上用来标识某一处资源的地址。 41 | 42 | 协议://域名:端口号/路径?查询字符串#哈希值 43 | 44 | 协议: 规定了两台电脑交互方式 http https ftp.. 45 | 46 | 域名: 找到对应的电脑 47 | 48 | 端口号:找电脑上对应的应用程序 web 服务器一般默认端口号都是 80 49 | 50 | ## 1.3. URI 、 URL 和 URN 的区别 51 | 52 | 1. URI(uniform resource identifier)统一资源标识符,用来唯一的标识一个资源。 53 | Web 上可用的每种资源如 HTML 文档、图像、视频片段、程序等都是一个来 URI 来定位的 54 | URI 一般由三部组成: 55 | 56 | - 访问资源的命名机制 57 | - 存放资源的主机名 58 | - 资源自身的名称,由路径表示,着重强调于资源。 59 | 60 | 2. URL(uniform resource locator)统一资源定位器,它是一种具体的 URI,即 URL 可以用来标识一个资源,而且还指明了如何 locate 这个资源。 61 | URL 是 Internet 上用来描述信息资源的字符串,主要用在各种 WWW 客户程序和服务器程序上,特别是著名的 Mosaic。 62 | 采用 URL 可以用一种统一的格式来描述各种信息资源,包括文件、服务器的地址和目录等。URL 一般由三部组成: 63 | 64 | - 协议(或称为服务方式) 65 | - 存有该资源的主机 IP 地址(有时也包括端口号) 66 | - 主机资源的具体地址。如目录和文件名等 67 | 68 | 3. URN(uniform resource name)统一资源命名,是通过名字来标识资源,比如mailto:java-net@java.sun.com。 69 | URI 是以一种抽象的,高层次概念定义统一资源标识,而 URL 和 URN 则是具体的资源标识的方式。URL 和 URN 都是一种 URI。笼统地说,每个 URL 都是 URI,但不一定每个 URI 都是 URL。这是因为 URI 还包括一个子类,即统一资源名称 (URN),它命名资源但不指定如何定位资源。上面的 mailto、news 和 isbn URI 都是 URN 的示例。 70 | 71 | ## 1.4. HTTP 报文的组成部分 72 | 73 | 1. 请求报文: 74 | 75 | ```html 76 | GET /562f25980001b1b106000338.jpg HTTP/1.1 77 | Host img.mukewang.com 78 | User-Agent Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.106 Safari/537.36 79 | Accept image/webp,image/*,*/*;q=0.8 80 | Referer http://www.imooc.com/ 81 | Accept-Encoding gzip, deflate, sdch 82 | Accept-Language zh-CN,zh;q=0.8 83 | ``` 84 | 85 | - 请求行:用来说明请求类型,要访问的资源以及所使用的 HTTP 版本 86 | - 请求头:紧接着请求行(即第一行)之后的部分,用来说明服务器要使用的附加信息 87 | - 空行:消息报头后面的空行是必须的 88 | - 请求体:可以添加任意的其他数据 89 | 90 | 2. 响应报文: 91 | 92 | ```html 93 | HTTP/1.1 200 OK //状态行 94 | Date: Fri, 22 May 2009 06:07:21 GMT 95 | Content-Type: text/html; charset=UTF-8 响应头 96 | //空行 97 | 98 | 99 | 100 | 101 | 102 | 103 | ``` 104 | 105 | - 状态行:由 HTTP 协议版本号, 状态码, 状态消息 三部分组成。 106 | - 响应头:用来说明客户端要使用的一些附加信息 107 | - 空行:消息报头后面的空行是必须的 108 | - 响应体:服务器返回给客户端的文本信息。 109 | 110 | ## 1.5. HTTP 方法 111 | 112 | 根据 HTTP 标准,HTTP 请求可以使用多种请求方法。 113 | HTTP1.0 定义了三种请求方法: GET, POST 和 HEAD 方法。 114 | HTTP1.1 新增了五种请求方法:OPTIONS, PUT, DELETE, TRACE 和 CONNECT 方法。 115 | 116 | 1. GET——请求指定的页面信息,并返回实体主体。 117 | 2. POST——向指定资源提交数据进行处理请求(例如提交表单或者上传文件)。数据被包含在请求体中。POST 请求可能会导致新的资源的建立和/或已有资源的修改。 118 | 3. HEAD——类似于 get 请求,只不过返回的响应中没有具体的内容,用于获取报头 119 | 4. PUT——从客户端向服务器传送的数据取代指定的文档的内容。 120 | 5. DELETE——请求服务器删除指定的页面。 121 | 6. CONNECT——HTTP/1.1 协议中预留给能够将连接改为管道方式的代理服务器。 122 | 7. OPTIONS——允许客户端查看服务器的性能。 123 | 8. TRACE——回显服务器收到的请求,主要用于测试或诊断。 124 | 125 | ## 1.6. HTTP 工作原理 126 | 127 | HTTP 协议定义 Web 客户端如何从 Web 服务器请求 Web 页面,以及服务器如何把 Web 页面传送给客户端。HTTP 协议采用了请求/响应模型。客户端向服务器发送一个请求报文,请求报文包含请求的方法、URL、协议版本、请求头部和请求数据。服务器以一个状态行作为响应,响应的内容包括协议的版本、成功或者错误代码、服务器信息、响应头部和响应数据。 128 | 129 | 以下是 HTTP 请求/响应的步骤: 130 | 131 | 1. 客户端连接到 Web 服务器 132 | 一个 HTTP 客户端,通常是浏览器,与 Web 服务器的 HTTP 端口(默认为 80)建立一个 TCP 套接字连接。例如,`http://www.oakcms.cn。` 133 | 134 | 2. 发送 HTTP 请求 135 | 通过 TCP 套接字,客户端向 Web 服务器发送一个文本的请求报文,一个请求报文由请求行、请求头部、空行和请求数据 4 部分组成。 136 | 137 | 3. 服务器接受请求并返回 HTTP 响应 138 | Web 服务器解析请求,定位请求资源。服务器将资源复本写到 TCP 套接字,由客户端读取。一个响应由状态行、响应头部、空行和响应数据 4 部分组成。 139 | 140 | 4. 释放连接 TCP 连接 141 | 若 connection 模式为 close,则服务器主动关闭 TCP 连接,客户端被动关闭连接,释放 TCP 连接;若 connection 模式为 keepalive,则该连接会保持一段时间,在该时间内可以继续接收请求; 142 | 143 | 5. 客户端浏览器解析 HTML 内容 144 | 客户端浏览器首先解析状态行,查看表明请求是否成功的状态代码。然后解析每一个响应头,响应头告知以下为若干字节的 HTML 文档和文档的字符集。客户端浏览器读取响应数据 HTML,根据 HTML 的语法对其进行格式化,并在浏览器窗口中显示。 145 | 146 | 例如:在浏览器地址栏键入 URL,按下回车之后会经历以下流程: 147 | 148 | 1. 浏览器向 DNS 服务器请求解析该 URL 中的域名所对应的 IP 地址; 149 | 2. 解析出 IP 地址后,根据该 IP 地址和默认端口 80,和服务器建立 TCP 连接; 150 | 3. 浏览器发出读取文件(URL 中域名后面部分对应的文件)的 HTTP 请求,该请求报文作为 TCP 三次握手的第三个报文的数据发送给服务器; 151 | 4. 服务器对浏览器请求作出响应,并把对应的 html 文本发送给浏览器; 152 | 5. 释放 TCP 连接; 153 | 6. 浏览器将该 html 文本并显示内容; 154 | 155 | ## 1.7. POST 和 GET 的区别 156 | 157 | GET 请求 158 | 159 | ```html 160 | GET /books/?sex=man&name=Professional HTTP/1.1 161 | Host: www.wrox.com 162 | User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.7.6) 163 | Gecko/20050225 Firefox/1.0.1 164 | Connection: Keep-Alive 165 | 注意最后一行是空行 166 | ``` 167 | 168 | POST 请求 169 | 170 | ```html 171 | POST / HTTP/1.1 172 | Host: www.wrox.com 173 | User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.7.6) 174 | Gecko/20050225 Firefox/1.0.1 175 | Content-Type: application/x-www-form-urlencoded 176 | Content-Length: 40 177 | Connection: Keep-Alive 178 | 179 | name=Professional%20Ajax&publisher=Wiley 180 | ``` 181 | 182 | 1. GET 在浏览器回退时是无害的,而 POST 会再次提交请求 ☆ 183 | 2. GET 产生的 URL 地址是可以被收藏的,而 POST 不可能 184 | 3. GET 请求会被浏览器自动缓存,而 POST 不可以,除非手动设置 ☆ 185 | 4. GET 请求只能进行 URL 编码,而 POST 支持多种编码方式 186 | 5. GET 请求参数会被完整保留在浏览器历史记录中,而 POST 中的参数不会被保留 ☆ 187 | 6. GET 请求在 URL 中传送的参数是有长度限制的(2KB,不同浏览器不同,URL 拼接的时候要注意长度),而 POST 没有限制 ☆ 188 | 7. 对参数的数据类型,GET 只接受 ASCII 字符,而 POST 没有限制 189 | 8. GET 比 POST 更不安全,因为参数直接暴露在 URL 上,所以不能用来传递敏感信息 190 | 9. GET 参数通过 URL 传递,POST 是放在 Request body 中 ☆ 191 | 192 | ## 1.8. HTTP 状态码 193 | 194 | - 1xx:指示信息——表示请求已接受,继续处理 195 | - 2xx:成功——表示请求已被成功接收 196 | - 3xx:重定向——要完成请求必须进行更进一步的操作 197 | - 4xx:客户端错误——请求有语法错误或请求无法实现 198 | - 5xx:服务器错误——服务器未能实现合法的请求 199 | 200 | 200 OK:客户端请求成功 201 | 206 Partial Content:客户发送了一个带有 Range 头的 GET 请求,服务器完成了它 202 | 301 Moved Permanently:永久重定向,所请求的页面已经转移到新的 url 203 | 302 Found:临时重定向,所请求的页面已经临时转移至新的 url 204 | 304 Not MOdified:缓存,客户端有缓冲的文档并发出了一个条件性的请求,服务器告诉客户,原来缓冲的文档还可以继续使用 205 | 400 Bad Request:客户端有语法错误,不能被服务器所理解 206 | 401 Unauthorized:请求未经授权,这个状态码必须和 WWW-Authenticate 报头域一起使用 207 | 403 Forbidden:对被请求页面的访问被禁止 208 | 404 Not Found:请求资源不存在 209 | 500 Internal Server Error:服务器发生不可预期的错误原来缓冲的文档还可以继续使用 210 | 503 Server Unavailable:请求未完成,服务器临时过载或宕机,一段时间后可能恢复正常 211 | 212 | ## 1.9. 什么是持久连接 213 | 214 | HTTP 协议采用“请求-应答”模式,当使用普通模式,即非 Keep-Alive 模式时,每个请求/应答客户和服务器都要新建一个连接,完成之后立即断开连接(HTTP 协议为无连接的协议) 215 | 216 | 当使用 Keep-Alive 模式(又称持久连接、连接重用)时,Keep-Alive 功能使客户端到服务器端的连接持续有效,当出现对服务器的后继请求时,Keep-Alive 功能避免了建立或者重新建立连接 217 | 218 | HTTP/1.1 版本支持的,1.0 版本不支持 219 | 220 | ## 1.10. 什么是管线化 221 | 222 | 在使用持久连接的情况下,某个连接上消息的传递类似于 223 | 请求 1 ->响应 1 -> 请求 2 -> 响应 2 -> 请求 3 -> 响应 3 224 | 225 | 管线化,某个连接上的消息就变成了类似这样 226 | 请求 1 ->请求 2 -> 请求 3 -> 响应 1 -> 响应 2 -> 响应 3 227 | 228 | - 管线化机制通过持久连接完成,仅 HTTP/1.1 支持此技术 229 | - 只有 GET 和 HEAD 请求可以进行管线化,而 POST 则有所限制 230 | - 初次创建连接时不应启动管线机制,因为对方(服务器)不一定支持 HTTP/1.1 版本的协议 231 | - 管线化不会影响响应到来的顺序,如上面的例子所示,响应返回的顺序并未改变 232 | - HTTP/1.1 要求服务器端支持管线化,但并不要求服务器端也对响应进行管线化处理,只是要求对于管线化的请求不失败即可 233 | - 由于上面提到的服务器端的问题,开启管线化很可能并不会带来大幅度的性能提升,而且很多服务器端和代理程序对管线化的支持并不是很友好,因此现代浏览器默认并未开始管线化支持 234 | -------------------------------------------------------------------------------- /JavaScript高级面试/1. ES6.md: -------------------------------------------------------------------------------- 1 | # 1. ES6 2 | 3 | ## 1.1. ES6 模块化 4 | 5 | ### 1.1.1. 模块化的基本语法 6 | 7 | (1)util1.js: 8 | 9 | ```JavaScript 10 | export default var a = 100; 11 | 12 | export function foo { 13 | console.log('util1-foo'); 14 | } 15 | ``` 16 | 17 | `export default`命令用于指定模块的默认输出。显然,一个模块只能有一个默认输出。 18 | 19 | 有了默认输出之后,其他模块加载该模块时,import 命令可以为该匿名变量/函数,起**任意的名字**。 20 | 21 | 上面的代码中,默认输出是一个变量。当然,我们也可以换成**默认输出一个匿名函数**: 22 | 23 | ```JavaScript 24 | export default function() { 25 | console.log('util1-function'); 26 | } 27 | ``` 28 | 29 | (2)util2.js: 30 | 31 | ```JavaScript 32 | export var myUtil2 = 'this is util2'; 33 | 34 | export function fn1() { 35 | console.log('util2-fn1'); 36 | } 37 | 38 | export function fn2() { 39 | console.log('util2-fn2'); 40 | } 41 | ``` 42 | 43 | 上方代码中,我把一个变量和两个函数作为了导出。 44 | 45 | (3)index.js: 46 | 47 | ### 1.1.2. 开发环境 48 | 49 | #### 1.1.2.1. babel 50 | 51 | - 电脑有 node 环境,运行 npm init 52 | - npm install --save-dev babel-core babel-preset-es2015 babel-preset-latest 53 | - 目录下创建.babelrc 文件 54 | - npm install --g babel-cli 55 | - babel-version 56 | - 创建./src/index.js 57 | - 内容:[1,2,3].map(item => item + 1) 58 | - babel src/index.js 59 | 60 | ### 1.1.3. webpack 61 | 62 | - npm install webpack babel-loader --save-dev 63 | - 配置 webpack.config.js 64 | - 配置 package.json 中的 scripts 65 | - 运行 npm start 66 | 67 | ### 1.1.4. rollup 68 | 69 | - vue、react 都是通过 rollup 来打包的,能尽量简化代码,优化冗余内容 70 | 71 | - npm init 72 | - npm i rollup rollup-plugin-node-resolve rollup-plugin-babel 73 | babel-plugin-external-helpers babel-preset-latest --save-dev 74 | - 配置 .babelrc 75 | - 配置 rollup.config.js 76 | - 将 webpack 环境的 JS 代码拷贝过来 77 | - 修改 package.json 的 scripts 78 | - 运行 npm start 79 | 80 | - rollup 功能单一,webpack 功能强大,学习成本高 81 | 82 | - 参考设计原则和《Linux/Unix 设计思想》 83 | 84 | - 工具要尽量功能单一,可集成,可扩展 85 | 86 | ### 1.1.5. JS 众多模块化标准 87 | 88 | - 没有模块化 89 | - AMD 成为标准,require.js(CMD) 90 | - 前端打包工具,使得 nodejs 模块化可以被使用 91 | - ES6 出现,想统一现在所有模块化标准 92 | - nodejs 积极支持,浏览器尚未统一 93 | - 可以自造 lib,但不要自造标准 94 | 95 | ### 1.1.6. 一般面试问题解答 96 | 97 | - 语法:import export (注意有无 default) 98 | - 环境:babel 编译 ES6 语法,模块化可用 webpack 和 rollup 99 | - 扩展:说一下自己对模块化统一的期待 100 | 101 | ## 1.2. Class 和普通构造函数的区别 102 | 103 | ### 1.2.1. JS 构造函数 104 | 105 | ```JavaScript 106 | function MathHandle (x,y) { 107 | this.x = x; 108 | this.y = y; 109 | } 110 | 111 | MathHandle.prototype.add = function () { 112 | return this.x + this.y; 113 | } 114 | 115 | var m = new MathHandle(1, 2); 116 | console.log(m.add()) 117 | ``` 118 | 119 | ### 1.2.2. Class 基本语法 120 | 121 | ```JavaScript 122 | class MathHandle { 123 | constructor(x, y) { 124 | this.x = x; 125 | this.y = y; 126 | } 127 | 128 | add() { 129 | return this.x + this.y; 130 | } 131 | } 132 | const m = new MathHandle(1, 2); 133 | console.log(m.add()) 134 | ``` 135 | 136 | ### 1.2.3. 语法糖 137 | 138 | ```JavaScript 139 | class MathHandle { 140 | // ... 141 | } 142 | 143 | typeof MathHandle // "function" 144 | MathHandle === MathHandle.prototype.constructor // true 145 | m.__proto__ === MathHandle.prototype // ture 146 | ``` 147 | 148 | 构造函数的显式原型有个 constructor 属性等于构造函数本身 149 | 150 | new 出来的实例有个隐式原型等于构造函数的显式原型 151 | 152 | ### 1.2.4. 继承 153 | 154 | #### 1.2.4.1. 继承-JS 155 | 156 | ```JavaScript 157 | function Animal() { 158 | this.eat = function () { 159 | console.log('animal eat') 160 | } 161 | } 162 | 163 | function Dog() { 164 | this.bark = function () { 165 | console.log('dog bark') 166 | } 167 | } 168 | 169 | // 绑定原型,实现继承 170 | Dog.prototype = new Animal() 171 | var hashiqi = new Dog() 172 | hashiqi.bark() // dog bark 173 | hashiqi.eat() // animal eat 174 | ``` 175 | 176 | #### 1.2.4.2. 继承-class 177 | 178 | ```JavaScript 179 | class Animal { 180 | constructor(name) { 181 | this.name = name 182 | } 183 | eat() { 184 | console.log(this.name + `eat`) 185 | } 186 | } 187 | 188 | class Dog extends Animal { 189 | constructor(name) { 190 | super(name) // super就是被继承的class的constructor 191 | this.name = name 192 | } 193 | say() { 194 | console.log(this.name + 'say') 195 | } 196 | } 197 | const Dog = new Dog('哈士奇') 198 | dag.say() // 哈士奇say 199 | dog.eat() // 哈士奇eat 200 | ``` 201 | 202 | ### 1.2.5. 面试问题解答 203 | 204 | - Class 在语法上更加贴合面向对象的写法 205 | - Class 实现继承更加易读、易理解 206 | - 更易于写 java 等后端语言的使用 207 | - 本质还是语法糖,使用 prototype 208 | 209 | ## 1.3. Promise 的基本语法 210 | 211 | ### 1.3.1. Callback Hell 212 | 213 | ```JavaScript 214 | function loadImg(src, callback, fail) { 215 | var img = document.createElement('img') 216 | img.onload = function () { 217 | callback(img) 218 | } 219 | img.onerror = function () { 220 | fail() 221 | } 222 | img.src = src 223 | } 224 | 225 | var src = 'http://www.imooc.com/static/img/index/logo_new.png' 226 | loadImg(src, function (img) { 227 | console.log(img.width) 228 | }, function () { 229 | console.log('failed') 230 | }) 231 | ``` 232 | 233 | ### 1.3.2. Promise 的语法 234 | 235 | ```JavaScript 236 | function loadImg(src) { 237 | const promise = new Promise(function (resolve, reject { 238 | var img = document.createElement('img') 239 | img.onload = function () { 240 | resolve(img) 241 | } 242 | img.onerror = function () { 243 | reject() 244 | } 245 | img.src = src 246 | }) 247 | return promise 248 | } 249 | 250 | var src = 'http://www.imooc.com/static/img/index/logo_new.png' 251 | var result = loadImg(src) 252 | 253 | result.then(function (img) { 254 | console.log(img.width) 255 | }, function () { 256 | console.log('failed') 257 | }) 258 | 259 | result.then(function (img) { 260 | console.log(img.height) 261 | }) 262 | ``` 263 | 264 | ### 1.3.3. 问题解答 265 | 266 | - new Promise 实例,而且要 return 267 | - new Promise 时要传入函数,函数有 resolve reject 两个参数 268 | - 成功时执行 reslove()失败时执行 reject() 269 | - then 监听结果 270 | 271 | ## 1.4. ES6 其他常用功能 272 | 273 | ### 1.4.1. let/const 274 | 275 | ```JavaScript 276 | // JS 277 | var i = 10; 278 | i = 100; 279 | var j = 20; 280 | j = 200; 281 | 282 | // ES6 283 | let i = 10; 284 | i = 100; // 正确 285 | const j = 20; 286 | j = 200; // 报错 287 | ``` 288 | 289 | ### 1.4.2. 多行字符串/模板变量 290 | 291 | ```JavaScript 292 | // JS 293 | var name = 'zhangsan', age = 20, html = ''; 294 | html += '
'; 295 | html += '

' + name + '

'; 296 | html += '

' + age + '

'; 297 | html += '
'; 298 | 299 | // ES6 300 | const name = 'zhangsan', age = 20; 301 | const html = `
302 |

${name}

303 |

${age}

304 |
`; 305 | console.log(html); 306 | ``` 307 | 308 | ### 1.4.3. 解构赋值 309 | 310 | ```JavaScript 311 | // JS 312 | var obj = {a: 100, b: 200} 313 | var a = obj.a 314 | var b = obj.b 315 | 316 | var arr = ['xxx', 'yyy', 'zzz'] 317 | var x = arr[0] 318 | 319 | //ES6 320 | const obj = {a: 10, b: 20, c: 30} 321 | const {a, c} = obj 322 | console.log(a) 323 | console.log(c) 324 | 325 | const arr = ['xxx', 'yyy', 'zzz'] 326 | const [x, y, z] = arr 327 | console.log(x) 328 | console.log(y) 329 | console.log(z) 330 | ``` 331 | 332 | ### 1.4.4. 块级作用域 333 | 334 | ```JavaScript 335 | // JS 336 | var obj = {a: 100, b:200} 337 | for (var item in obj) { 338 | console.log(item) 339 | } 340 | console.log(item) // 'b' 341 | 342 | // ES6 343 | const obj = {a:100, b:200} 344 | for (let item in obj) { 345 | console.log(item) 346 | } 347 | console.log(item) // undefined 348 | ``` 349 | 350 | ### 1.4.5. 函数默认参数 351 | 352 | ```JavaScript 353 | // JS 354 | function (a, b) { 355 | if (b == null) { 356 | b = 0 357 | } 358 | } 359 | 360 | // ES6 361 | function (a, b=0) { 362 | 363 | } 364 | ``` 365 | 366 | ### 1.4.6. 箭头函数(this) 367 | 368 | ```JavaScript 369 | // JS 370 | var arr = [1, 2, 3] 371 | arr.map(function (item) { 372 | return item + 1 373 | }) 374 | 375 | // ES6 376 | const arr = [1, 2, 3] 377 | arr.map(item => item + 1); 378 | arr.map((item, index) => { 379 | console.log(index) 380 | return item + 1 381 | }) 382 | ``` 383 | 384 | ```JavaScript 385 | function fn() { 386 | console.log('real', this) 387 | var arr = [1, 2, 3] 388 | 389 | // JS 390 | arr.map(function (item) { 391 | console.log('js', this) 392 | return item + 1 393 | }) 394 | 395 | // ES6 396 | arr.map(item => { 397 | console.log('es6', this) 398 | return item + 1 399 | }) 400 | } 401 | fn.call({a: 100}) 402 | ``` 403 | -------------------------------------------------------------------------------- /1. 页面布局.md: -------------------------------------------------------------------------------- 1 | # 页面布局 2 | 3 | ## 问题:假设高度已知,请写出三栏布局,其中左栏、右栏宽度各为 300px,中间自适应 4 | 5 | ### 方法一:浮动布局 6 | 7 | ```html 8 |
9 | 26 |

三栏布局

27 |
28 |
29 |
30 |
31 |

浮动解决方案

32 | 1.这是三栏布局的浮动解决方案; 33 | 2.这是三栏布局的浮动解决方案; 34 | 3.这是三栏布局的浮动解决方案; 35 | 4.这是三栏布局的浮动解决方案; 36 | 5.这是三栏布局的浮动解决方案; 37 | 6.这是三栏布局的浮动解决方案; 38 |
39 |
40 |
41 | ``` 42 | 43 | ### 方法二:绝对定位布局 44 | 45 | ```html 46 |
47 | 70 |

三栏布局

71 |
72 |
73 |
74 |

绝对定位解决方案

75 | 1.这是三栏布局的浮动解决方案; 76 | 2.这是三栏布局的浮动解决方案; 77 | 3.这是三栏布局的浮动解决方案; 78 | 4.这是三栏布局的浮动解决方案; 79 | 5.这是三栏布局的浮动解决方案; 80 | 6.这是三栏布局的浮动解决方案; 81 |
82 |
83 |
84 |
85 | ``` 86 | 87 | ### 方法三:flexbox 布局 88 | 89 | ```html 90 |
91 | 115 |

三栏布局

116 |
117 |
118 |
119 |

flexbox解决方案

120 | 1.这是三栏布局的浮动解决方案; 121 | 2.这是三栏布局的浮动解决方案; 122 | 3.这是三栏布局的浮动解决方案; 123 | 4.这是三栏布局的浮动解决方案; 124 | 5.这是三栏布局的浮动解决方案; 125 | 6.这是三栏布局的浮动解决方案; 126 |
127 |
128 |
129 |
130 | ``` 131 | 132 | ### 方法四:表格布局 133 | 134 | ```html 135 |
136 | 161 |

三栏布局

162 |
163 |
164 |
165 |

表格布局解决方案

166 | 1.这是三栏布局的浮动解决方案; 167 | 2.这是三栏布局的浮动解决方案; 168 | 3.这是三栏布局的浮动解决方案; 169 | 4.这是三栏布局的浮动解决方案; 170 | 5.这是三栏布局的浮动解决方案; 171 | 6.这是三栏布局的浮动解决方案; 172 |
173 |
174 |
175 |
176 | ``` 177 | 178 | ### 方法五:网格布局 179 | 180 | ```html 181 |
182 | 206 |

三栏布局

207 |
208 |
209 |
210 |

网格布局解决方案

211 | 1.这是三栏布局的浮动解决方案; 212 | 2.这是三栏布局的浮动解决方案; 213 | 3.这是三栏布局的浮动解决方案; 214 | 4.这是三栏布局的浮动解决方案; 215 | 5.这是三栏布局的浮动解决方案; 216 | 6.这是三栏布局的浮动解决方案; 217 |
218 |
219 |
220 |
221 | ``` 222 | 223 | ### 方法六:圣杯布局 224 | 225 | ```html 226 |
227 |
Main
228 |
Left
229 |
Right
230 |
231 | 277 | ``` 278 | 279 | ### 方法七:双飞翼布局 280 | 281 | ```html 282 |
283 |
284 |
Main
285 |
286 |
Left
287 |
Right
288 |
289 | 290 | 329 | ``` 330 | 331 | ### 问题延伸: 332 | 333 | #### 分析每个解决方案的优缺点 334 | 335 | 1. 浮动:浮动后脱标,需要清除浮动,需要处理好其他浮动周边元素的关系;优点兼容性好 336 | 2. 绝对定位:下面所有的子元素都要脱标,有效性差;优点快捷 337 | 3. flex 布局:CSS3 中新出现的布局方式,就是为了解决上述两个的解决方案的不足而产生,基本上是比较完美的 338 | 4. 表格布局:当其中一个单元格高度超出时,两侧的单元格都要调整高度;在很多场景中都是非常适用的,兼容性很好 339 | 5. 网格布局:新技术,可以做很多复杂的事情,代码量少 340 | 6. 圣杯布局:结构简单,无多余 DOM 层;中间部分宽度小于左侧时布局混乱,当浏览器无限放大时圣杯会破碎 341 | 7. 双飞翼布局:主要的内容先加载的优化;兼容目前所有的主流浏览器,包括 IE6 在内。缺点:会多加一层 DOM 节点,增加渲染树生成的计算量 342 | 343 | #### 假设把高度已知去掉,哪个方案不适用了 344 | 345 | 在不改动的情况下,flex 布局和表格布局是可以通用的,其他的都不适用了 346 | 347 | ### 页面布局小结 348 | 349 | - 语义化掌握到位 article section 不要通篇 div 350 | - 页面布局理解深刻 351 | - CSS 基础知识扎实 352 | - 思维灵活积极向上 知道每个方案的优缺点、对比 353 | - 代码书写规范 354 | 355 | ### 其他布局 356 | 357 | 三栏布局 358 | 359 | - 左右宽度固定,中间自适应 360 | - 上下高度固定,中间自适应 361 | 362 | 两栏布局 363 | 364 | - 左宽度固定,右自适应 365 | - 右宽度固定,左自适应 366 | - 上高度固定,下自适应 367 | - 下高度固定,上自适应 368 | -------------------------------------------------------------------------------- /JavaScript高级面试/4. 虚拟DOM.md: -------------------------------------------------------------------------------- 1 | # 1. 虚拟 DOM 2 | 3 | - vdom 是 Vue 和 React 的核心 4 | - vdom 比较独立,使用也比较简单 5 | - 如果面试问到 Vue 和 React 和实现,免不了问 vdom,考察基础能力和自学能力 6 | 7 | ## 1.1. vdom 是什么?为什么会存在 vdom? 8 | 9 | ### 1.1.1. 什么是 vdom 10 | 11 | - virtual dom,虚拟 DOM 12 | - 用 JS 模拟 DOM 结构 13 | - DOM 变化的对比,放在 JS 层来做 14 | - 前端的 HTML、CSS、JS 只有 JS 是图灵完备语言 15 | - 图灵完备语言:能实现各种逻辑的语言,判断、循环、递归,高复杂逻辑的语言 16 | - 提高重绘性能 17 | 18 | ```html 19 |
    20 |
  • Item 1
  • 21 |
  • Item 2
  • 22 |
23 | ``` 24 | 25 | 用 JS 来模拟上面的 DOM 结构 26 | 27 | ```js 28 | { 29 | tag: 'ul', 30 | attrs: { 31 | id: 'list' 32 | }, 33 | children: [ 34 | { 35 | tag: 'li', 36 | attrs: { className: 'item' }, 37 | children: ['Item 1'] 38 | }, { 39 | tag: 'li', 40 | attrs: { className: 'item' }, 41 | children: ['Item 2'] 42 | } 43 | ] 44 | } 45 | ``` 46 | 47 | ### 1.1.2. 设计一个需求场景 48 | 49 | ```json 50 | // 1. 将该数据展示成一个表格 51 | // 2, 随便修改一个信息,表格也跟着修改 52 | [ 53 | { 54 | "name": "张三", 55 | "age": "20", 56 | "address": "北京" 57 | }, 58 | { 59 | "name": "李四", 60 | "age": "21", 61 | "address": "上海" 62 | }, 63 | { 64 | "name": "王五", 65 | "age": "22", 66 | "address": "广州" 67 | } 68 | ] 69 | ``` 70 | 71 | ### 1.1.3. 用 jQuery 实现 72 | 73 | ```html 74 |
75 | 76 | 77 | 78 | 134 | ``` 135 | 136 | 容器需要清空之后再重新加上 table,操作 DOM 137 | 138 | ### 1.1.4. 遇到的问题 139 | 140 | - DOM 的操作是非常昂贵的,js 的运行效率高 141 | - 尽量减少 DOM 操作,而不是推倒重来 142 | - 项目越复杂,影响就越严重 143 | - vdom 即可解决这个问题 144 | 145 | ### 1.1.5. 问题解答 146 | 147 | - virtual DOM,虚拟 DOM 148 | - 用 JS 模拟 DOM 结构 149 | - DOM 操作非常昂贵 150 | - 将 DOM 对比操作放在 JS 层,提高效率 151 | 152 | ## 1.2. vdom 如何应用,核心 API 是什么? 153 | 154 | ### 1.2.1. 介绍 snabbdom 155 | 156 | ```js 157 | // snabbdom在./snabbdom.js 158 | var snabbdom = require('snabbdom') 159 | // 初始化snabbdom,得到patch。随后,我们可以看到snabbdom设计的精妙之处 160 | var patch = snabbdom.init([ 161 | require('snabbdom/modules/class'), 162 | require('snabbdom/modules/props'), 163 | require('snabbdom/modules/style'), 164 | require('snabbdom/modules/eventlisteners') 165 | ]) 166 | // h是一个生成vnode的包装函数,factory模式?对生成vnode更精细的包装就是使用jsx 167 | // 在工程里,我们通常使用webpack或者browserify对jsx编译 168 | var h = require('snabbdom/h') 169 | // 构造一个virtual dom,在实际中,我们通常希望一个无状态的vnode 170 | // 并且我们通过state来创造vnode 171 | // react使用具有render方法的对象来作为组件,这个组件可以接受props和state 172 | // 在snabbdom里面,我们同样可以实现类似效果 173 | // function component(state){return h(...)} 174 | var vnode = 175 | h( 176 | 'div#container.two.classes', 177 | {on: {click: someFn}}, 178 | [ 179 | h('span', {style: {fontWeight: 'bold'}}, 'This is bold'), 180 | ' and this is just normal text', 181 | h('a', {props: {href: '/foo'}}, 182 | 'I\'ll take you places!') 183 | ] 184 | ) 185 | // 得到初始的容器,注意container是一个dom element 186 | var container = document.getElementById('container') 187 | // 将vnode patch到container中 188 | // patch函数会对第一个参数做处理,如果第一个参数不是vnode,那么就把它包装成vnode 189 | // patch过后,vnode发生变化,代表了现在virtual dom的状态 190 | patch(container, vnode) 191 | // 创建一个新的vnode 192 | var newVnode = 193 | h( 194 | 'div#container.two.classes', 195 | {on: {click: anotherEventHandler}}, 196 | [ 197 | h('span', {style: {fontWeight: 'normal', fontStyle: 'italics'}}, 198 | 'This is now italics'), 199 | ' and this is still just normal text', 200 | h('a', {props: {href: '/bar'}}, 'I\'ll take you places!') 201 | ] 202 | ) 203 | // 将新的vnode patch到vnode上,现在newVnode代表vdom的状态 204 | patch(vnode, newVnode 205 | ``` 206 | 207 | ### 1.2.2. 重做之前的 demo 208 | 209 | ```html 210 |
211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 300 | ``` 301 | 302 | ### 1.2.3. 核心 API 303 | 304 | - h('<标签名>', {…属性…}, […子元素…]) 305 | - h('<标签名>', {…属性…}, '…') 306 | - patch(container, vnode) 307 | - patch(vnode, newVnode) 308 | 309 | ### 1.2.4. 问题解答 310 | 311 | - 如何使用?可用 snabbdom 的用法来举例 312 | - 核心 API:h 函数,patch 函数 313 | 314 | ## 1.3. 介绍一下 diff 算法 315 | 316 | ### 1.3.1. 什么是 diff 算法 317 | 318 | - Linux 中最基本的 diff 命令能找出两个文本文件之间的异常 319 | - git diff xxx 查看修改的内容 320 | 321 | ### 1.3.2. 去繁就简 322 | 323 | - diff 算法非常复杂,实现难度很大,源码量很大 324 | - 去繁就简,讲明白核心流程,不关心细节 325 | - 面试官也大多不清楚细节,但是很关心核心流程 326 | - 去繁就简之后,依然具有很大挑战性,并不简单 327 | 328 | ### 1.3.3. vdom 为何用 diff 算法 329 | 330 | - DOM 操作是昂贵的,因此需要尽量减少 DOM 操作 331 | - 找出本次 DOM 必须更新的节点来更新,其他的不更新 332 | - 这个“找出”的过程,就需要 diff 算法 333 | 334 | ### 1.3.4. diff 实现过程 335 | 336 | - patch(container, vnode) 337 | createElement() 338 | 页面初次加载的时候,直接将 vnode 打包渲染到 container 中 339 | - patch(vnode, newVnode) 340 | updateChildren() 341 | 数据改变了,生成新的 vnode,将新的 vnode 和旧的 vnode 进行对比,把差异更新到 vnode 中 342 | 343 | #### 1.3.4.1. patch(container, vnode) 344 | 345 | ```js 346 | function createElement(vnode) { 347 | var tag = vnode.tag // 'ul' 348 | var attrs = vnode.attrs || {} 349 | var children = vnode.children || [] 350 | if (!tag) { 351 | return null 352 | } 353 | 354 | // 创建真实的 DOM 元素 355 | var elem = document.createElement(tag) 356 | // 属性 357 | var attrName 358 | for (attrName in attrs) { 359 | if (attrs.hasOwnProperty(attrName)) { 360 | // 给 elem 添加属性 361 | elem.setAttribute(attrName, attrs[attrName]) 362 | } 363 | } 364 | // 子元素 365 | children.forEach(function(childVnode) { 366 | // 给 elem 添加子元素 367 | elem.appendChild(createElement(childVnode)) // 递归 368 | }) 369 | 370 | // 返回真实的 DOM 元素 371 | return elem 372 | } 373 | ``` 374 | 375 | #### 1.3.4.2. patch(vnode, newVnode) 376 | 377 | ```js 378 | function updateChildren(vnode, newVnode) { 379 | var children = vnode.children || [] 380 | var newChildren = newVnode.children || [] 381 | 382 | children.forEach(function(childVnode, index) { 383 | var newChildVnode = newChildren[index] 384 | if (childVnode.tag === newChildVnode.tag) { 385 | // 深层次对比,递归 386 | updateChildren(childVnode, newChildVnode) 387 | } else { 388 | // 替换 389 | replaceNode(childVnode, newChildVnode) 390 | } 391 | }) 392 | } 393 | 394 | function replaceNode(vnode, newVnode) { 395 | var elem = vnode.elem // 真实的 DOM 节点 396 | var newElem = createElement(newVnode) 397 | 398 | // 替换 399 | } 400 | ``` 401 | 402 | ### 1.3.5. 问题解答 403 | 404 | - 知道什么是 diff 算法,是 Linux 的基础命令 405 | - vdom 中应用 diff 算法是为了找出需要更新的节点 406 | - diff 算法的实现:patch(container, vnode)、patch(vnode, newVnode) 407 | - 核心逻辑:createElement()、updateChildren() 408 | -------------------------------------------------------------------------------- /5. 面向对象和原型链.md: -------------------------------------------------------------------------------- 1 | # 1. 面向对象和原型链 2 | 3 | ## 1.1. 面向对象编程 4 | 5 | 面向对象编程 —— Object Oriented Programming,简称 OOP ,是一种编程开发思想。 6 | 在面向对象程序开发思想中,每一个对象都是功能中心,具有明确分工,可以完成接受信息、处理数据、发出信息等任务。 7 | 因此,面向对象编程具有灵活、代码可复用、高度模块化等特点,容易维护和开发,比起由一系列函数或指令组成的传统的过程式编程(procedural programming),更适合多人合作的大型软件项目 8 | 9 | ### 1.1.1. 面向对象三大特性 10 | 11 | #### 1.1.1.1. 封装性 12 | 13 | 将功能的具体实现,全部封装到对象的内部,外界使用对象时,只需要关注对象提供的方法如何使用,而不需要关心对象的内部具体实现,这就是封装。 14 | 15 | #### 1.1.1.2. 继承性 16 | 17 | 在 js 中,继承的概念很简单,一个对象没有的一些属性和方法,另外一个对象有,拿过来用,就实现了继承。 18 | 注意:在其他语言里面,继承是类与类之间的关系,在 js 中,是对象与对象之间的关系。 19 | 20 | #### 1.1.1.3. 多态性 21 | 22 | 多态是在强类型的语言中才有的。js 是弱类型语言,所以 JS 不支持多态 23 | 24 | ## 1.2. 创建对象的方法 25 | 26 | ### 1.2.1. 字面量创建 27 | 28 | 字面量创建是由若干名/值对组成的映射表,名/值对中间用冒号分隔,名/值对之间用逗号分隔,整个映射表用花括号括起来。 29 | 30 | **基本写法**: 31 | 32 | ```JavaScript 33 | var student1 = { 34 | name : 'cocytus', 35 | age : 23, 36 | sayName: function() { 37 | console.log(this.name); 38 | } 39 | }; 40 | 41 | console.log(student1.name); 42 | console.log(student1.age); 43 | console.log(student1.sayName); 44 | ``` 45 | 46 | **优点**:简单,直观,易懂。 47 | 48 | **缺点**:① 代码复用性差;② 无法批量生成多个对象,代码冗余度高 49 | 50 | ### 1.2.2. 内置构造函数 51 | 52 | `ECMAScript` 核心语法自带内置构造函数:`Function`、 `Object`、 `Array`、 `String`、 `Number`、 `Boolean`、 `RegExp`、 `Error`、 `Date` 53 | 54 | **基本写法**: 55 | 56 | ```JavaScript 57 | var obj1 = new Object(); 58 | obj1.name = 'cocytus'; 59 | obj1.age = 23; 60 | obj1.hobby = ['JavaScript','HTML']; 61 | obj1.height = 175; 62 | console.log(obj1); 63 | ``` 64 | 65 | **弊端**:① 创建的对象无法复用;② 代码冗余度高 66 | 67 | ### 1.2.3. 工厂模式 68 | 69 | 工厂模式抽象了创建具体对象的过程。因为 ECMAScript 中无法创建类,开发人员就发明了一种函数,用函数来封装以待定接口创建对象的细节。 70 | 71 | **基本写法**: 72 | 73 | ```JavaScript 74 | /** 75 | * 功能:批量创建同一个类型的对象 76 | * 参数:对象的不同属性值 77 | * 返回值:当前创建出来的对象 78 | */ 79 | function createPerson(age,gender,name) { 80 | var person = new Object(); 81 | person.age = age; 82 | person.gender = gender; 83 | person.name = name; 84 | person.sayName = function(){ 85 | }; 86 | 87 | return person; 88 | } 89 | 90 | var person1 = create{Person}(18,"male","zs"); 91 | console.log(person1); 92 | var person2 = createPerson(20,"male","ls"); 93 | console.log(person2); 94 | var person3 = createPerson(19,"male","ww"); 95 | console.log(person3); 96 | ``` 97 | 98 | **优点**:可以批量创建同一种类型的对象 99 | 100 | **缺点**:没有解决对象识别的问题(看不到对象类型),创建出来的对象都是 Object 类型的。 101 | 102 | ### 1.2.4. 自定义构造函数模式 103 | 104 | 为了解决从原型对象生成实例的问题, JavaScript 提供了一个构造函数模式。 105 | 106 | 所谓“构造函数”,其实就是一个普通函数,但是内部使用了 this 变量,对构造函数使用 new 运算符,就能生成实例,并且 this 变量会绑定在实例对象上。 107 | 108 | 用构造函数模式将前面的例子重写: 109 | 110 | ```JavaScript 111 | function Person(age,gender,name){//首字母大写 112 | this.age = age; 113 | this.gender = gender; 114 | this.name = name; 115 | this.sayName = function(){ 116 | alert(this.name); 117 | }; 118 | } 119 | 120 | var person1 = new Person(18,"male","zs"); 121 | console.log(person1); 122 | var person2 = new Person(20,"male","ls"); 123 | console.log(person2); 124 | var person3 = new Person(19,"male","ww"); 125 | console.log(person3); 126 | ``` 127 | 128 | **注意**:必须使用 new 操作符,这种方式调用构造函数实际上经历了以下 4 个步骤: 129 | 130 | 1. 创建了一个新的空对象 131 | 2. 将构造函数的作用域赋给新对象(因此 this 就指向了这个新对象) 132 | 3. 执行了这个构造函数的函数体 133 | 4. 返回新对象 134 | 135 | **优点**:可以批量创建,只要需要每一次 new 一个即可,并且可以看到具象化的类型 136 | 137 | ### 1.2.5. 构造函数的存在的问题 138 | 139 | 使用构造函数带来的最大的好处就是创建对象更方便了,但是其本身也存在一个浪费内存的问题: 140 | 141 | ```JavaScript 142 | function Person(name, age){ 143 | this.name = name; 144 | this.age = age; 145 | this.sayHi = function(){ 146 | console.log("你今天吃了吗"); 147 | } 148 | } 149 | 150 | var person1 = new Person("zs", 20); 151 | console.log(p); 152 | person1.sayHi(); 153 | var person2 = new Person("ls", 16); 154 | console.log(p2); 155 | person2.sayHi(); 156 | // 因为是两个新对象,内存地址不相同 157 | console.log(person1 == person2); // false 158 | console.log(person1.sayHi == person2.sayHi); // false 159 | ``` 160 | 161 | 因为每个对象上都有属于自己的 sayHi 方法,然而两个 sayHi 方法并不是同一个 Function 实例。不同实例上的同名函数是不相等的。 162 | 假如创建了 100 个对象,那么在内存里面就会创建 100 个 sayHi 函数,这样会造成内存浪费的问题。其实在内存里面只需要一份 sayHi 方法就行了。 163 |   所以我们把函数定义转移到构造函数之外来解决这个问题。 164 | 165 | ```JavaScript 166 | function Person(name, age){ 167 | this.name = name; 168 | this.age = age; 169 | this.sayHi = sayHi(); 170 | } 171 | } 172 | function sayHi(){ 173 | console.log("你今天吃了吗"); 174 | } 175 | 176 | var person1 = new Person("zs", 20); 177 | var person2 = new Person("ls", 16); 178 | console.log(person1.sayHi == person2.sayHi); // true 179 | ``` 180 | 181 | 我们把 sayHi()函数的定义转移到了构造函数外部,而在构造函数内部,我们将 sayHi 属性设置成等于全局的 sayHi 函数。这样由于 sayHi 包含的是一个指向函数的指针,因此 person1 和 person2 对象就共享在全局作用域中定义的同一个 sayHi()函数。 182 | 183 | 但是这样全局作用域中定义的函数实际上只能被某个对象调用,而且如果对象需要定义很多方法,那么就要定义很多个全局函数。就会暴漏很多的函数,容易造成全局变量污染。 184 | 185 | ### 1.2.6. 原型模式 186 | 187 | `Javascript` 规定,每一个构造函数都有一个 `prototype` 属性,指向另一个对象。这个对象的所有属性和方法,都会被构造函数的实例继承。这也就意味着,我们可以把所有对象实例需要共享的属性和方法直接定义在 `prototype` 对象上。 188 | 189 | ```JavaScript 190 | // 构造函数 191 | function Person(){ 192 | } 193 | // console.dir(Person); 194 | // console.log(Person.prototype); 195 | 196 | // 原型对象 197 | // 这是给原型对象添加color属性 198 | Person.prototype.color = "lime"; 199 | Person.prototype.gender = "male"; 200 | // 把sayHi方法添加给原型,谁可以访问 201 | // 构造函数创建的实例就可以访问到了 202 | Person.prototype.sayHi = function(){ 203 | console.log("hello"); 204 | } 205 | 206 | // console.log(Person.prototype); 207 | 208 | // 实例对象:person1 209 | var person1 = new Person(); 210 | // console.log(p.color); // lime 211 | // console.log(p.gender); // male 212 | // p.sayHi(); 213 | 214 | // 实例对象:person2 215 | var person2 = new Person(); 216 | console.log(person2); // 是个空对象 217 | p2.sayHi(); 218 | 219 | // person1的sayHi和person2的sayHi是同一个函数 220 | // 来源于原型上的sayHi方法 221 | // 这样就解决了内存浪费问题 222 | console.log(p.sayHi == p2.sayHi); // true 223 | ``` 224 | 225 | ## 1.3. 原型、构造函数、实例、原型链 226 | 227 | ![关系图](https://camo.githubusercontent.com/803a654869fa6a06847ee34b4ef91aed4daee13e/687474703a2f2f696d672e736d79687661652e636f6d2f32303138303330365f313534302e706e67) 228 | 229 | **构造函数**:构造函数就是一个函数,配合 new 可以新建对象。构造函数中有一个 `prototype` 属性,这个属性是一个指针,指向它的原型对象。 230 | 231 | **实例**:通过**构造函数**创建出来的对象我们把它叫做构造函数的实例。一个构造函数可以有很多实例。实例一创造出来就具有 `constructor` 属性(指向构造函数)和`__proto__`属性(指向原型对象)。 232 | 233 | **原型**:每一个构造函数都有一个属性`prototype`,函数的 `prototype` 属性值就是原型。通过构造函数创建出来的实例能够直接使用原型上的属性和方法。 234 | 235 | ### 1.3.1. `__proto__`属性 236 | 237 | 任意一个对象,都会有`__proto__`属性,这个属性指向了构造函数的 `prototype` 属性,也就是原型对象。 238 | 239 | 获取原型对象: 240 | 241 | - 通过 `构造函数.prototype` 可以获取 242 | - 通过 `实例.__proto__` 可以获取(隐式原型) 243 | - 它们指向了同一个对象 `构造函数.prototype === 实例.__proto__` 244 | 245 | **注意**:`__proto__` 是浏览器的一个隐藏(私有)属性,早期的 IE 浏览器不支持,所以说不要在线上项目中去使用它。本地开发中可以通过实例知道实例对象可以访问原型上的哪些成员。不要通过它来修改原型里的内容,如果要修改原型中的内容,使用 `构造函数.prototype` 去修改. 246 | 247 | ### 1.3.2. constructor 属性 248 | 249 | 默认情况下,原型对象中只包含了一个属性:constructor,constructor 属性指向了当前的构造函数。 250 | 251 | ## 1.4. 原型链 252 | 253 | ### 1.4.1. 原型链概念 254 | 255 | 任何一个对象,都有原型对象,原型对象本身又是一个对象,所以原型对象也有自己的原型对象,这样一环扣一环就形成了一个链式结构,我们把这个链式结构称为:**原型链**。 256 | 257 | 绘制对象的原型链结构: 258 | 259 | ```javascript 260 | //1. var p = new Person(); 261 | //2. var o = new Object(); 262 | //3. var arr = new Array(); 263 | //4. var date = new Date(); 264 | //5. Math 265 | //6. 查看一个div的原型链结构 266 | ``` 267 | 268 | 总结:在访问一个实例的时候,如果实例本身没找到此方法或属性,就往原型上找。如果还是找不到,继续往上一级的原型上找。Object.prototype 是原型链的尽头,Object.prototype 的原型是 null。 269 | 270 | ### 1.4.2. 属性查找原则 271 | 272 | 如果是获取操作 273 | 274 | 1. 会先在自身上查找,如果没有 275 | 2. 则根据`__proto__`对应的原型去找,如果没有 276 | 3. 一直找到`Object.prototype`,如果没有,那就找不到了。 277 | 278 | 如果是修改操作: 279 | 280 | 只会修改对象自身的属性,如果自身没有这个属性,那么就会添加这个属性,并不会修改原型中的属性。 281 | 282 | ### 1.4.3. Object.prototype 对象的方法 283 | 284 | #### 1.4.3.1. `hasOwnProperty()` 285 | 286 | **语法**:对象.hasOwnProperty("属性"); 287 | 288 | **作用**:该方法会返回一个布尔值,判断对象是否具有指定的属性是自身的属性。 289 | 290 | ```javascript 291 | var obj = { 292 | name: "zs" 293 | }; 294 | //判断name属性是不是obj自己提供的 295 | console.log(obj.hasOwnProperty("name")); //true 296 | console.log(obj.hasOwnProperty("toString")); //false 297 | ``` 298 | 299 | in 运算符: 300 | **语法**: "属性" in 对象 301 | **作用**: 判断该属性能否被对象访问到,也就是说,不管这个属性是对象自身的,还是来源于原型链上,只要能够访问到,就返回 true 302 | 303 | ```JavaScript 304 | function Person(name){ 305 | this.name = name; 306 | } 307 | var p = new Person("zs"); 308 | 309 | console.log("name" in p); // true 310 | console.log("hasOwnProperty" in p); // true 311 | console.log("toString" in p); // true 312 | console.log("push" in []); // true 313 | console.log(p.hasOwnProperty("hasOwnProperty")); // false 314 | ``` 315 | 316 | `hasOwnProperty()` 与 in 的区别 317 | in 操作符:如果属性不是自己提供的,是从原型上继承来的,也会返回 true 318 | 319 | `hasOwnProperty():` 该属性必须是自己提供,才返回 true,否则返回 false。 320 | 321 | #### 1.4.3.2. `isPrototypeOf()` 322 | 323 | **语法**:对象 A.isPrototypeOf(对象 B); 324 | **作用**:该方法用于测试一个对象是否存在于另一个对象的原型链上。 325 | 326 | ```javascript 327 | //判断A对象是否在B对象的原型链上。 328 | //返回值:true,在原型链上 false:不在原型链上。 329 | A.isPrototetypeOf(B); 330 | ``` 331 | 332 | #### 1.4.3.3. `propertyIsEnumerable()` 333 | 334 | 该方法返回一个布尔值,表明指定的属性名是否是当前对象可枚举的自身属性。 335 | 336 | 该方法可以判断出指定对象里的属性是否可枚举,也就是说该属性是否可以通过 for...in 循环等遍历到,不过有些属性虽然可以通过 for...in 循环遍历到,但因为它们不是自身属性,而是从原型链上继承的属性,所以该方法也会返回 false。如果对象没有指定的属性,该方法返回 false。 337 | 338 | ```javascript 339 | //1. 判断这个属性能不能被遍历(必须是自己的属性)。 340 | obj.propertyIsEnumerable(prop); 341 | ``` 342 | 343 | #### 1.4.3.4. toLocaleString()/toString() 344 | 345 | `toString()` 方法返回一个表示该对象的字符串。格式:[object Object],内置对象都重写了 toString 方法。 346 | 347 | `toLocaleString()` 方法返回一个该对象的字符串表示。该方法主要用于被本地化相关对象覆盖。 348 | 349 | #### 1.4.3.5. `valueOf()` 350 | 351 | `valueOf()` 方法返回指定对象的原始值。 352 | 353 | **语法**: 354 | 355 | **作用**: 356 |   `valueOf()`把对象转换成原始类型的值(数值、字符串和布尔值),很少需要显式的调用此函数;当遇到需要转换成一个原始值的情况时, JavaScript 会自动调用此方法。默认情况下, `valueOf()` 会被每个对象 Object 继承。每一个内置对象都会覆盖这个方法为了返回一个合理的值,如果对象没有原始值,`valueOf()` 就会返回对象自身。 357 | 358 | ### 1.4.4. `instanceof()` 359 | 360 | ![instanceof](https://camo.githubusercontent.com/9996f26d7a4e9df5ab87a962689d42de99943855/687474703a2f2f696d672e736d79687661652e636f6d2f32303138303330365f323230392e706e67) 361 | 362 | `instanceof` 运算符用来测试一个对象的原型链中是否存在一个构造函数的 `prototype` 属性。作用和 `isPrototypeOf` 类似 363 | 364 | **语法**: 实例对象 `instanceof` 构造函数 365 | 366 | **作用**:检测构造函数的 `prototype` 属性是否在实例对象的原型链上。 367 | 368 | - A.isPrototypeOf(B) 判断 A 是否在 B 的原型链上 A: 是一个原型对象 369 | - B instanceof A 判断 A 的 prototype 是否在 B 的原型链上 A:是一个构造函数 370 | -------------------------------------------------------------------------------- /JavaScript高级面试/5. MVVM和Vue.md: -------------------------------------------------------------------------------- 1 | # 1. MVVM 和 Vue 2 | 3 | > 如何理解 MVVM 4 | > 如何实现 MVVM 5 | > 是否解读过 vue 的源码 6 | 7 | ## 1.1. 说一下使用 jQuery 和使用框架的区别 8 | 9 | ### 1.1.1. jQuery 实现 todo-list 10 | 11 | ```html 12 |
13 | 14 | 15 |
16 |
    17 | 18 | 19 | 33 | ``` 34 | 35 | ### 1.1.2. vue 实现 todo-list 36 | 37 | ```html 38 |
    39 |
    40 |
    41 |
      42 |
    • {{item}}
    • 43 |
    44 |
    45 |
    46 | 47 | 65 | ``` 66 | 67 | ### 1.1.3. jQuery 和框架的区别 68 | 69 | - 数据和视图的分离,解耦(开放封闭原则) 70 | 71 | - 以数据驱动视图,只关心数据变化,DOM 操作被封装 72 | 73 | ## 1.2. 说一下对 MVVM 的理解 74 | 75 | - MVVM - Model View ViewModel 76 | - 三者之间的联系 77 | - ViewModel 的理解,联系 View 和 Model 78 | 79 | ## 1.3. MVVM 框架的三大要素 80 | 81 | - 响应式:vue 如何监听到 data 的每个属性变化? 82 | - 模板引擎:vue 的模板如何被解析,指令如何处理? 83 | - 渲染:vue 的模板如何被渲染成 html?以及渲染过程 84 | 85 | ## 1.4. vue 如何实现响应式 86 | 87 | ### 1.4.1. 什么是响应式 88 | 89 | - 修改 data 属性之后,vue 立刻监听到,立刻做出修改 90 | - data 属性被代理到 vm 上 91 | 92 | ```html 93 |
    94 |

    {{name}}

    95 |

    {{age}}

    96 |
    97 | 98 | 107 | ``` 108 | 109 | ### 1.4.2. Object.defineProperty 110 | 111 | ```js 112 | var obj = {} 113 | var _name = 'shangsan' 114 | Object.defineProperty(obj, 'name', { 115 | get: function () { 116 | console.log('get', _name) // 监听 117 | return _name 118 | }, 119 | set: function (newVal) { 120 | console.log('set', newVal) // 监听 121 | _name = newVal 122 | } 123 | } 124 | ``` 125 | 126 | ### 1.4.3. 模拟 127 | 128 | ```js 129 | var vm = {} 130 | var data = { 131 | name: 'zhangsan', 132 | age: 20 133 | } 134 | var key, value 135 | for (key in data) { 136 | // 命中闭包。新建一个函数保证key的独立作用域 137 | ;(function(key) { 138 | Object.defineProperty(vm, key, { 139 | get: function() { 140 | console.log('get', data[key]) // 监听 141 | return data[key] 142 | }, 143 | set: function(newVal) { 144 | console.log('set', newVal) // 监听 145 | data[key] = newVal 146 | } 147 | }) 148 | })(key) 149 | } 150 | ``` 151 | 152 | ### 1.4.4. 问题解答 153 | 154 | - 关键是理解 Object.defineProperty 155 | - 将 data 的属性代理到 vm 上 156 | 157 | ## 1.5. vue 如何解析模板 158 | 159 | ### 1.5.1. 模板是什么 160 | 161 | ```js 162 |
    163 |
    164 | 165 | 166 |
    167 |
      168 |
    • {{ item }}
    • 169 |
    170 |
    171 | ``` 172 | 173 | - 本质:对 vue 来说,模板本质就是一个字符串 174 | - 有逻辑:如 v-if、v-for 等 175 | - 与 html 格式很像,但有很大的区别 176 | - 最终还要转换为 html 来显示 177 | - 模板最终必须转换成 JS 代码,因为: 178 | - 有逻辑,必须用 JS 才能实现(图灵完备,判断循环递归等) 179 | - 转换为 html 渲染页面,必须用 JS 才能实现 180 | - 因此,模板最重要转换成一个 JS 函数(render 函数) 181 | 182 | ### 1.5.2. render 函数 183 | 184 | ```js 185 | ;
    186 |

    {{ price }}

    187 |
    188 | 189 | with (this) { 190 | return _c( 191 | 'div', 192 | { 193 | attrs: { id: 'app' } 194 | }, 195 | [_c('p', [_v(_s(price))])] 196 | ) 197 | } 198 | ``` 199 | 200 | - 模板中所有的信息都包含在 render 函数中 201 | - this 即 vm 202 | - price 即 this.price 即 vm.price,即 data 中的 price 203 | - \_c 即 this.\_c 即 vm.c 204 | 205 | ```html 206 |

    {{price}}

    207 | 208 | 245 | ``` 246 | 247 | - 从哪里可以看到 render 函数? 248 | - 复杂一点的例子,render 函数是什么样子的? 249 | - v-if v-for v-on 都是怎么处理的? 250 | 251 | ```html 252 |
    253 |
    254 |
    255 |
      256 |
    • {{item}}
    • 257 |
    258 |
    259 |
    260 | ``` 261 | 262 | 在 vue 源码中搜索`code.render`,然后加上`alert(code.render)` 263 | 264 | ![code.render](../images/code.render.png) 265 | 266 | 复制浏览器中弹出的代码 267 | 268 | ![render](../images/render.png) 269 | 270 | vue2.0 开始支持预编译,在开发环境下写模板经过编译打包之后放在生产环境下是 JS 代码 271 | 272 | ```js 273 | with (this) { 274 | // this 就是 vm 275 | return _c( 276 | 'div', 277 | { 278 | attrs: { 279 | id: 'app' 280 | } 281 | }, 282 | [ 283 | _c('div', [ 284 | _c('input', { 285 | directives: [ 286 | { 287 | name: 'model', 288 | rawName: 'v-model', 289 | value: title, 290 | expression: 'title' 291 | } 292 | ], 293 | domProps: { 294 | value: title 295 | }, 296 | on: { 297 | input: function($event) { 298 | if ($event.target.composing) return 299 | title = $event.target.value 300 | } 301 | } 302 | }), 303 | _v(' '), 304 | _c( 305 | 'button', 306 | { 307 | on: { 308 | click: add 309 | } 310 | }, 311 | [_v('submit')] 312 | ) 313 | ]), 314 | _v(' '), 315 | _c('div', [ 316 | _c( 317 | 'ul', 318 | _l(list, function(item) { 319 | return _c('li', [_v(_s(item))]) 320 | }) 321 | ) 322 | ]) 323 | ] 324 | ) 325 | } 326 | ``` 327 | 328 | - 根据 todo-list demo 的 render 函数: 329 | - v-model 是怎么实现的? 330 | - 一个 get 一个 set 331 | - v-on:click 怎么实现? 332 | - 渲染 button 的时候绑定上 click 事件,定义 add 函数 333 | - v-for 怎么实现的? 334 | - list 数组遍历,每个元素封装成 li 标签汇成一个数组,一起返回作为 ul 标签的子元素 335 | 336 | ### 1.5.3. render 函数与 vdom 337 | 338 | ```js 339 | // render函数 340 | with (this) { 341 | return _c( 342 | 'div', 343 | { 344 | attrs: { id: 'app' } 345 | }, 346 | [_c('p', [_v(_s(price))])] 347 | ) 348 | } 349 | 350 | // snabbdom中的h函数 351 | var vnode = h('div#container.two.classer', { on: { click: someFn } }, [ 352 | h('span', { style: { fontWeight: 'bold' } }, 'This is bold'), 353 | 'and this is just normal text', 354 | h('a', { props: { href: '/foo' } }, "I'll take you places!") 355 | ]) 356 | ``` 357 | 358 | - vm.\_c 其实就相当于 snabbdom 中的 h 函数 359 | - render 函数执行之后,返回的是 vnode 360 | 361 | ```js 362 | vm._update(vnode) { 363 | const prevVnode = vm._vnode 364 | vm._vnode = vnode 365 | if (!prevVnode) { 366 | vm.$el = vm.__patch__(vm.$el, vnode) 367 | } else { 368 | vm.$el = vm.__patch__(prevVnode, vnode) 369 | } 370 | } 371 | 372 | function updateComponent() { 373 | // vm._redner即上面的render函数,返回vnode 374 | vm._update(vm._render()) 375 | } 376 | ``` 377 | 378 | - updateComponent 中实现了 vdom 中的 patch 379 | - 页面首次渲染执行 updateComponent 380 | - data 中每次修改属性,执行 updateComponent 381 | 382 | ### 1.5.4. 问题解答 383 | 384 | - 模板:本质字符串,有逻辑,嵌入 JS 变量 385 | - 模板必须转换为 JS 代码 386 | - render 函数 387 | - render 函数执行是返回 vnode 388 | - updateComponent 389 | 390 | ## 1.6. vue 的整个实现流程 391 | 392 | > vue 整个的实现流程是怎么样的? 393 | 394 | ### 1.6.1. 第一步:解析模板成 render 函数 395 | 396 | - with 的用法 397 | - 模板中的所有信息都被 render 函数包含 398 | - 模板中用到的 data 中的属性,都变成了 JS 变量 399 | - 模板中的 v-model v-for v-on 都变成了 JS 逻辑 400 | - render 函数返回 vnode 401 | 402 | ```js 403 |
    404 |
    405 | 406 |
    407 |
    408 |
      409 |
    • {{ item }}
    • 410 |
    411 |
    412 |
    413 | ``` 414 | 415 | ### 1.6.2. 第二步:响应式开始监听 416 | 417 | - Object.defineProperty 418 | - 将 data 的属性代理到了 vm 上 419 | 420 | ### 1.6.3. 第三步:首次渲染,显示页面,绑定依赖 421 | 422 | - 初次渲染,执行 updateComponent,执行 vm.\_render() 423 | - 执行 render()函数,会访问到 vm.list 和 vm.title 424 | - 会被响应式的 get 方法监听到 425 | - 执行 updateComponent,会走到 vdom 的 patch 方法 426 | - patch 将 vnode 渲染成 DOM,初次渲染完成 427 | 428 | ```js 429 | vm._update(vnode) { 430 | const prevVnode = vm._vnode 431 | vm._vnode = vnode 432 | if (!prevVnode) { 433 | vm.$el = vm.__patch__(vm.$el, vnode) 434 | } else { 435 | vm.$el = vm.__patch__(prevVnode, vnode) 436 | } 437 | } 438 | 439 | function updateComponent() { 440 | // vm._redner即上面的render函数,返回vnode 441 | vm._update(vm._render()) 442 | } 443 | ``` 444 | 445 | > 为什么要监听 get,直接监听 set 不行吗? 446 | 447 | - data 中有很多属性,有些被用到,有些可能不被用到 448 | - 被用到的会走到 get,不被用到的不会走到 get 449 | - 未走到 get 中的属性,set 的时候我们也无需关心 450 | - 避免不必要的重复渲染 451 | 452 | ### 1.6.4. 第四步:data 属性变化,触发 rerender 函数 453 | 454 | - 修改属性,被响应式的 set 监听到 455 | - set 中执行 updateComponent 456 | - updateComponent 重新执行 vm.\_render() 457 | - 生成 vnode 和 prevVnode,通过 patch 进行对比 458 | - 渲染到 HTML 中 459 | 460 | ### 1.6.5. 问题解答 461 | 462 | - 第一步:解析模板成 render 函数 463 | - 第二步:响应式开始监听 464 | - 第三步:首次渲染,显示页面,绑定依赖 465 | - 第四步:data 属性变化,触发 rerender 函数 466 | 467 | ## 1.7. 总结 468 | 469 | 1. 说一下使用 jQuery 和使用框架的区别 470 | 471 | - 数据和视图的分离,解耦 472 | - 以数据驱动视图,只关心数据变化,DOM 操作被封装 473 | 474 | 2. 什么是 MVVM 475 | 476 | - MVVM :Model View VIewModel 477 | - 三者之间的联系,以及如何对应到各段代码 478 | - ViewModel 的理解,联系 View 和 Model 479 | 480 | 3. MVVM 的三要素 481 | 482 | - 响应式:vue 如何监听到 data 的每个属性变化? 483 | - 模板引擎:vue 的模板如何被解析,指令如何处理? 484 | - 渲染:vue 的模板如何被渲染成 html?以及渲染过程 485 | 486 | 4. Vue 如何实现响应式 487 | 488 | - 关键是理解 Object.defineProperty 489 | - 将 data 的属性代理到 vm 上 490 | 491 | 5. vue 如何解析模板 492 | 493 | - 模板:本质字符串,有逻辑,嵌入 JS 变量 494 | - 模板必须转换为 JS 代码(图灵完备语言,有逻辑、渲染 html、JS 变量) 495 | - render 函数是什么样子的 496 | - render 函数执行是返回 vnode 497 | - updateComponent 498 | 499 | 6. vue 实现的整体流程 500 | 501 | - 第一步:解析模板成 render 函数 502 | - 第二步:响应式开始监听 503 | - 第三步:首次渲染,显示页面,绑定依赖 504 | - 第四步:data 属性变化,触发 rerender 函数 505 | -------------------------------------------------------------------------------- /7. 跨域通信类.md: -------------------------------------------------------------------------------- 1 | # 1. 通信类 2 | 3 | 4 | 5 | - [1. 通信类](#1-通信类) 6 | - [1.1. 同源策略及限制](#11-同源策略及限制) 7 | - [1.2. 前后端如何通信](#12-前后端如何通信) 8 | - [1.3. 如何创建 Ajax](#13-如何创建-ajax) 9 | - [1.3.1. `XMLHTTPRequest` 对象的工作流程](#131-xmlhttprequest-对象的工作流程) 10 | - [1.3.2. 发送 get 请求和 post 请求](#132-发送-get-请求和-post-请求) 11 | - [1.3.3. `onreadystatechange` 事件](#133-onreadystatechange-事件) 12 | - [1.3.4. 兼容性处理](#134-兼容性处理) 13 | - [1.3.5. 事件的触发条件](#135-事件的触发条件) 14 | - [1.3.6. 事件的触发顺序](#136-事件的触发顺序) 15 | - [1.3.7. 原生 Ajax 请求](#137-原生-ajax-请求) 16 | - [1.4. 跨域通信的几种方式](#14-跨域通信的几种方式) 17 | - [1.4.1. JSONP](#141-jsonp) 18 | - [1.4.2. Hash](#142-hash) 19 | - [1.4.3. postMessage()](#143-postmessage) 20 | - [1.4.4. WebSocket](#144-websocket) 21 | - [1.4.5. CORS](#145-cors) 22 | 23 | 24 | 25 | ## 1.1. 同源策略及限制 26 | 27 | **同源策略**:限制从一个源加载的文档或脚本如何与来自另一个源的资源进行交互。这是一个用于隔离潜在恶意文件的关键的安全机制。 28 | 29 | **源**:包括三个部分:协议、域名、端口(http 协议的默认端口是 80)。如果有任何一个部分不同,则源不同,那就是跨域了。 30 | 31 | **限制**: 32 | 33 | - Cookie、LocalStorage 和 IndexDB 无法读取 34 | - DOM 无法获取 35 | - AJAX 请求不能发送 36 | 37 | ## 1.2. 前后端如何通信 38 | 39 | 1. Ajax:同源通信,不支持跨域 40 | 2. WebSocket:不受同源策略的限制,支持跨域。 41 | 3. CORS:不受同源策略的限制,支持跨域。一种新的通信协议标准。可以理解成是:同时支持同源和跨域的 Ajax。 42 | 43 | ## 1.3. 如何创建 Ajax 44 | 45 | ### 1.3.1. `XMLHTTPRequest` 对象的工作流程 46 | 47 | (1)创建 XMLHttpRequest 对象。 48 | 49 | (2)使用 open 方法设置请求的参数。open(method, url, 是否异步)。 50 | 51 | (3)发送请求。 52 | 53 | (4)注册事件。 注册 onreadystatechange 事件,状态改变时就会调用。如果要在数据完整请求回来的时候才调用,我们需要手动写一些判断的逻辑。 54 | 55 | (5)获取返回的数据,更新 UI。 56 | 57 | ### 1.3.2. 发送 get 请求和 post 请求 58 | 59 | **get 请求**: 60 | 61 | ```html 62 | 63 | 64 | 65 | 66 | Document 67 | 68 | 69 |

    Ajax 发送 get 请求

    70 | 71 | 72 | 104 | 105 | 106 | ``` 107 | 108 | **post 请求**: 109 | 110 | ```html 111 | 112 | 113 | 114 | 115 | Document 116 | 117 | 118 |

    Ajax 发送 get 请求

    119 | 120 | 142 | 143 | 144 | ``` 145 | 146 | ### 1.3.3. `onreadystatechange` 事件 147 | 148 | 注册 `onreadystatechange` 事件后,每当 `readyState` 属性改变时,就会调用 `onreadystatechange` 函数。 149 | 150 | `readyState`:(存有 `XMLHttpRequest` 的状态。从 0 到 4 发生变化) 151 | 152 | 0: 请求未初始化。尚未调用 open()方法 153 | 154 | 1: 服务器连接已建立。已经调用 open()方法,未调用 send()方法 155 | 156 | 2: 请求已接收。已经调用 send()方法,但尚未接受到响应 157 | 158 | 3: 请求处理中 159 | 160 | 4: 请求已完成,且响应已就绪 161 | 162 | ### 1.3.4. 兼容性处理 163 | 164 | IE8/IE9、Opera Mini 完全不支持 xhr 对象 165 | 166 | IE10/IE11 部分支持,不支持 xhr.responseType 为 json 167 | 168 | 部分浏览器不支持设置请求超时,即无法使用 xhr.timeout 169 | 170 | 部分浏览器不支持 xhr.responseType 为 blob 171 | 172 | ### 1.3.5. 事件的触发条件 173 | 174 | | 事件 | 触发条件 | 175 | | -------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | 176 | | `onreadystatechange` | 每当 `xhr.readyState` 改变时触发;但 `xhr.readyState`由非 0 值变为 0 时不触发。 | 177 | | `onloadstart` | 调用 `xhr.send()`方法后立即触发,若 `xhr.send()`未被调用则不会触发此事件。 | 178 | | `onprogress` | `xhr.upload.onprogress` 在上传阶段(即 `xhr.send()`之后,`xhr.readystate=2` 之前)触发,每 50ms 触发一次;`xhr.onprogress` 在下载阶段(即 `xhr.readystate=3` 时)触发,每 50ms 触发一次。 | 179 | | `onload` | 当请求成功完成时触发,此时 `xhr.readystate=4` | 180 | | `onloadend` | 当请求结束(包括请求成功和请求失败)时触发 | 181 | | `onabort` | 当调用 `xhr.abort()`后触发 | 182 | | `ontimeout` | `xhr.timeout` 不等于 0,由请求开始即 `onloadstart` 开始算起,当到达 `xhr.timeout` 所设置时间请求还未结束即 `onloadend`,则触发此事件。 | 183 | | `onerror` | 在请求过程中,若发生 `Network error` 则会触发此事件(若发生 `Network error` 时,上传还没有结束,则会先触发 `xhr.upload.onerror`,再触发 `xhr.onerror`;若发生 `Network error` 时,上传已经结束,则只会触发 `xhr.onerror`)。注意,只有发生了网络层级别的异常才会触发此事件,对于应用层级别的异常,如响应返回的 `xhr.statusCode` 是 4xx 时,并不属于 `Network error`,所以不会触发 `onerror` 事件,而是会触发 `onload` 事件。 | 184 | 185 | ### 1.3.6. 事件的触发顺序 186 | 187 | 当请求一切正常时,相关的事件触发顺序如下: 188 | 189 | 1. 触发 xhr.onreadystatechange(之后每次 readyState 变化时,都会触发一次) 190 | 191 | 2. 触发 xhr.onloadstart 192 | //上传阶段开始: 193 | 194 | 3. 触发 xhr.upload.onloadstart 195 | 196 | 4. 触发 xhr.upload.onprogress 197 | 198 | 5. 触发 xhr.upload.onload 199 | 200 | 6. 触发 xhr.upload.onloadend 201 | //上传结束,下载阶段开始: 202 | 203 | 7. 触发 xhr.onprogress 204 | 205 | 8. 触发 xhr.onload 206 | 207 | 9. 触发 xhr.onloadend 208 | 209 | ### 1.3.7. 原生 Ajax 请求 210 | 211 | ```JavaScript 212 | var util = {}; 213 | 214 | //获取 ajax 请求之后的json 215 | util.json = function (options) { 216 | 217 | var opt = { 218 | url: '', 219 | type: 'get', 220 | data: {}, 221 | success: function () { 222 | }, 223 | error: function () { 224 | }, 225 | 226 | }; 227 | util.extend(opt, options); 228 | if (opt.url) { 229 | //IE兼容性处理:浏览器特征检查。检查该浏览器是否存在XMLHttpRequest这个api,没有的话,就用IE的api 230 | var xhr = XMLHttpRequest ? new XMLHttpRequest() : new window.ActiveXObject('Microsoft.XMLHTTP'); 231 | 232 | var data = opt.data, 233 | url = opt.url, 234 | type = opt.type.toUpperCase(); 235 | dataArr = []; 236 | } 237 | 238 | for (var key in data) { 239 | dataArr.push(key + '=' + data[key]); 240 | } 241 | 242 | if (type === 'GET') { 243 | url = url + '?' + dataArr.join('&'); 244 | xhr.open(type, url.replace(/\?$/g, ''), true); 245 | xhr.send(); 246 | } 247 | 248 | if (type === 'POST') { 249 | xhr.open(type, url, true); 250 | // 如果想要使用post提交数据,必须添加此行 251 | xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); 252 | xhr.send(dataArr.join('&')); 253 | } 254 | 255 | xhr.onload = function () { 256 | if (xhr.status === 200 || xhr.status === 304) { //304表示:用缓存即可。206表示获取媒体资源的前面一部分 257 | var res; 258 | if (opt.success && opt.success instanceof Function) { 259 | res = xhr.responseText; 260 | if (typeof res === 'string') { 261 | res = JSON.parse(res); //将字符串转成json 262 | opt.success.call(xhr, res); 263 | } 264 | } 265 | } else { 266 | if (opt.error && opt.error instanceof Function) { 267 | opt.error.call(xhr, res); 268 | } 269 | } 270 | }; 271 | } 272 | ``` 273 | 274 | ## 1.4. 跨域通信的几种方式 275 | 276 | ### 1.4.1. JSONP 277 | 278 | 在 CORS 和 postMessage 以前,我们一直都是通过 JSONP 来做跨域通信的。 279 | 280 | **JSONP 的原理**: 281 | 282 | 通过` 294 | ``` 295 | 296 | 上面的 src 中,data=name 是 get 请求的参数,myjsonp 是和后台约定好的函数名。 297 | 服务器端这样写: 298 | 299 | ```JavaScript 300 | myjsonp({ 301 | data: {} 302 | 303 | }) 304 | ``` 305 | 306 | 于是,本地要求创建一个 myjsonp 的全局函数,才能将返回的数据执行出来。 307 | 308 | **实际开发中,前端的 JSONP 是这样实现的**: 309 | 310 | ```JavaScript 311 | 368 | ``` 369 | 370 | ### 1.4.2. Hash 371 | 372 | url 地址中`#`后面的内容叫 Hash,**Hash 的改变页面是不刷新的**,这就是 Hash 做跨域通信的基本原理。 373 | url 地址中`?`后面的内容叫 Search。Search 的改变,会导致页面刷新,因此不能做跨域通信。 374 | 375 | **使用场景**:我的页面 A 通过 iframe 或 frame 嵌入了跨域的页面 B。现在,我这个 A 页面想给 B 页面发消息,怎么操作呢? 376 | 377 | (1)首先,在我的 A 页面中: 378 | 379 | ```JavaScript 380 | //伪代码 381 | var B = document.getElementsByTagName('iframe'); 382 | B.src = B.src + '#' + 'jsonString'; //我们可以把JS 对象,通过 JSON.stringify()方法转成 json字符串,发给 B 383 | ``` 384 | 385 | (2)然后,在 B 页面中: 386 | 387 | ```JavaScript 388 | // B中的伪代码 389 | window.onhashchange = function () { //通过onhashchange方法监听,url中的 hash 是否发生变化 390 | var data = window.location.hash; 391 | }; 392 | ``` 393 | 394 | ### 1.4.3. postMessage() 395 | 396 | html5 中新增的跨域通信方式,可解决 iframe & window.open 的跨域问题 397 | 398 | **使用场景**:窗口 A (http:A.com)向跨域的窗口 B (http:B.com)发送信息。步骤如下。 399 | 400 | (1)在 A 窗口中操作如下:向 B 窗口发送数据: 401 | 402 | ```JavaScript 403 | // 窗口A(http:A.com)向跨域的窗口B(http:B.com)发送信息 404 | Bwindow.postMessage('data', 'http://B.com'); //这里强调的是B窗口里的window对象 405 | ``` 406 | 407 | (2)在 B 窗口中操作如下: 408 | 409 | ```JavaScript 410 | // 在窗口B中监听 message 事件 411 | Awindow.addEventListener('message', function (event) { //这里强调的是A窗口里的window对象 412 | console.log(event.origin); //获取 :url。这里指:http://A.com 413 | console.log(event.source); //获取:A window对象 414 | console.log(event.data); //获取传过来的数据 415 | }, false); 416 | ``` 417 | 418 | ### 1.4.4. WebSocket 419 | 420 | ```JavaScript 421 | var ws = new WebSocket('wss://echo.websocket.org');// 创建WebSocket的对象。参数可以是 ws 或 wss,后者表示加密。 422 | 423 | // 把请求发出去 424 | ws.onopen = function (evt) { 425 | console.log('Connection open ...'); 426 | ws.send('Hello WebSockets!'); 427 | }; 428 | 429 | // 对方发消息过来时,我接收 430 | ws.onmessage = function (evt) { 431 | console.log('Received Message: ', evt.data); 432 | ws.close(); 433 | }; 434 | 435 | // 关闭连接 436 | ws.onclose = function (evt) { 437 | console.log('Connection closed.'); 438 | }; 439 | ``` 440 | 441 | ### 1.4.5. CORS 442 | 443 | CORS 可以理解成是既可以同步、也可以异步的 Ajax。 444 | 445 | fetch 是一个比较新的 API,用来实现 CORS 通信。用法如下: 446 | 447 | ```JavaScript 448 | // url(必选),options(可选) 449 | fetch('/some/url/', { 450 | method: 'get', 451 | }).then(function (response) { //类似于 ES6中的promise 452 | 453 | }).catch(function (err) { 454 | // 出错了,等价于 then 的第二个参数,但这样更好用更直观 455 | }); 456 | ``` 457 | 458 | **CORS 为什么支持跨域的通信**? 459 | 跨域时,浏览器会拦截 Ajax 请求,并在 http 头中加 Origin。 460 | 461 | **与 JSONP 的比较**: 462 | 463 | - CORS 与 JSONP 的使用目的相同,但是比 JSONP 更强大。 464 | - JSONP 只支持 GET 请求,CORS 支持所有类型的 HTTP 请求。JSONP 的优势在于支持老式浏览器,以及可以向不支持 CORS 的网站请求数据。 465 | --------------------------------------------------------------------------------