diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..9e662a32 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +node_modules/ +build/ +.idea diff --git a/.idea/Leo-JavaScript.iml b/.idea/Leo-JavaScript.iml new file mode 100644 index 00000000..24643cc3 --- /dev/null +++ b/.idea/Leo-JavaScript.iml @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 00000000..9c694110 --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,7 @@ + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 00000000..28a804d8 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 00000000..af6a5e30 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 00000000..94a25f7f --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/Cute-Algorithms/Cute-Algorithms-Dictionary&&HashTable.md b/Cute-Algorithms/Cute-Algorithms-Dictionary&&HashTable.md new file mode 100644 index 00000000..5c7546e9 --- /dev/null +++ b/Cute-Algorithms/Cute-Algorithms-Dictionary&&HashTable.md @@ -0,0 +1,549 @@ +![封面](https://user-gold-cdn.xitu.io/2019/4/27/16a5cd3f25400f0f?imageView2/1/w/1304/h/734/q/85/format/webp/interlace/1) + +这是第五周的练习题,上周忘记发啦,这周是复习 **Dictionary 和 HashTable**。 + +下面是之前分享的链接: +* 1.[每周一练 之 数据结构与算法(Stack)](https://juejin.im/post/5cb2df0c5188251aca7340a0) +* 2.[每周一练 之 数据结构与算法(LinkedList)](https://juejin.im/post/5cbdbb1af265da036d79bb35) +* 3.[每周一练 之 数据结构与算法(Queue)](https://juejin.im/post/5cc3cbaaf265da03a85ac7f8) +* 4.[每周一练 之 数据结构与算法(Set)](https://juejin.im/post/5cceee526fb9a0323a01c72e) + +> 欢迎关注我的 [个人主页](https://github.com/pingan8787) && [个人博客](http://www.pingan8787.com/) && [个人知识库](http://js.pingan8787.com/) && 微信公众号“前端自习课” + + +**本周练习内容:数据结构与算法 —— Dictionary 和 HashTable** + +这些都是数据结构与算法,一部分方法是团队其他成员实现的,一部分我自己做的,有什么其他实现方法或错误,欢迎各位大佬指点,感谢。 + + +## 一、字典和散列表的概念 + +1. 字典是什么? + +2. 字典和集合有什么异同? + +3. 什么是散列表和散列函数? + +4. 散列表的特点是什么? + +--- +解析: + +1. 字典是什么? + +字典是一种以 **键-值对** 形式存储数据的数据格式,其中键名用来查询特定元素。 + +2. 字典和集合有什么异同? + +相同:都是用来存储不同元素的数据格式; +区别:集合是以 **值-值** 的数据格式存储,而字典是以 **键-值** 的数据格式存储。 + +3. 什么是散列表和散列函数? + +哈希表(`Hash table`,也叫散列表),是根据关键码值(·Key value·)而直接进行访问的数据结构。也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。这个映射函数叫做**散列函数**,存放记录的数组叫做**散列表**。 + +4. 散列表的特点是什么? + +特点:数组和链接优点的结合,查询速度非常的快,几乎是O(1)的时间复杂度,并且插入和删除也容易。 + + +## 二、请实现一个字典 + +`set(key,value)`:向字典中添加新元素。 +`delete(key)`:通过使用键值从字典中移除键值对应的值。 +`has(key)`:如果某个键值存在于这个字典中,则返回 true,否则返回 false。 +`get(key)`:使用键值查找对应的值并返回。 +`clear()`:删除字典中的所有元素。 +`size()`:返回字典包含的元素数量,与数组的 length 属性类似。 +`keys()`:将字典的所有键名以数组的形式返回。 +`values()`:将字典包含的所有数值以数组形式返回。 + + +使用示例: + +```js +let dictionary = new Dictionary(); + +dictionary.set("Gandalf", "gandalf@email.com"); +dictionary.set("John", "johnsnow@email.com"); +dictionary.set("Tyrion", "tyrion@email.com"); + +console.log(dictionary.has("Gandalf")); +console.log(dictionary.size()); + +console.log(dictionary.keys()); +console.log(dictionary.values()); +console.log(dictionary.get("Tyrion")); + +dictionary.delete("John"); + +console.log(dictionary.keys()); +console.log(dictionary.values()); + +``` + +**提示:Web 端优先使用 ES6 以上的语法实现。** + +--- +解析: +```js +// 二、请实现一个字典 +class Dictionary { + constructor(){ + this.items = [] + } + /** + * 向字典中添加新元素 + * @param {*} key 添加的键名 + * @param {*} value 添加的值 + */ + set (key, value) { + if ( !key ) return new Error('请指定插入的key') + this.items[key] = value + } + + /** + * 查询某个键值存在于这个字典 + * @param {*} key 查询的键名 + * @return {Boolean} 是否存在 + */ + has (key) { + return key in this.items + } + + /** + * 通过使用键值从字典中移除键值对应的值 + * @param {*} key 移除的键名 + * @return {Boolean} 是否移除成功 + */ + delete (key) { + if(!key || !this.has(key)) return false + delete this.items[key] + return true + } + + /** + * 使用键值查找对应的值并返回 + * @param {*} key 查找的键名 + * @return {*} 查找的结果 + */ + get (key) { + return this.has(key) ? this.items[key] : undefined + } + + /** + * 删除字典中的所有元素 + */ + clear () { + this.items = {} + } + + /** + * 将字典的所有键名以数组的形式返回 + * @return {Array} 所有键名的数组 + */ + keys () { + return Object.keys(this.items) + } + + /** + * 将字典的所有键值以数组的形式返回 + * @return {Array} 所有键值的数组 + */ + values () { + let result = [] + for(let k in this.items){ + if(this.has[k]){ + result.push(this.items[k]) + } + } + return result + } + + /** + * 返回字典包含的元素数量 + * @return {Number} 元素数量 + */ + size () { + const values = this.values() + return values.length + } +} +``` + +## 三、请实现一个散列表 + +`put(key,value)`:向散列表增加/更新一个新的项。 +`remove(key)`:根据键值从散列表中移除值。 +`get(key)`:根据键值检索到特定的值。 +`print()`:打印散列表中已保存的值。 + + +散列表内部的散列算法: + +```js +function hashCode(key) { + let hash = 0; + for (let i = 0; i < key.length; i++) { + hash += key.charCodeAt(i); + } + return hash % 37; +} +``` + +使用示例: + +```js +const hashTable = new HashTable(); + +hashTable.put("Gandalf", "gandalf@email.com"); +hashTable.put("John", "johnsnow@email.com"); +hashTable.put("Tyrion", "tyrion@email.com"); +hashTable.print(); +``` + +--- +解析: +```js +// 三、请实现一个散列表 +class HashTable { + constructor(){ + this.table = [] + } + /** + * 散列函数 + * @param {*} key 键名 + */ + hashCode(key) { + let hash = 0; + for (let i = 0; i < key.length; i++) { + hash += key.charCodeAt(i); + } + return hash % 37; + } + /** + * 向散列表增加/更新一个新的项 + * @param {*} key 添加的键名 + * @param {*} value 添加的值 + */ + put (key, value) { + let position = this.hashCode(key) + this.table[position] = value + } + + /** + * 根据键值从散列表中移除值 + * @param {*} key 移除的键名 + * @return {Boolean} 是否成功移除 + */ + remove (key) { + if ( !key ) return false + let position = this.hashCode(key) + this.table[position] = undefined + return true + } + + /** + * 根据键值检索到特定的值 + * @param {*} key 查找的键名 + * @return {*} 查找的值 + */ + get (key) { + let position = this.hashCode(key) + return this.table[position] + } + + /** + * 打印散列表中已保存的值 + * @return {*} 散列表的值 + */ + print () { + return this.table + } +} +``` + +## 四、请利用之前已实现的链表,实现一个分离链接的散列表 + +分离链接是为散列表的每一个位置创建一个链表储存元素的方式来处理散列表中的冲突: + +![separate-chaining.png](https://cdn.nlark.com/yuque/0/2019/png/102778/1556970090828-8bc73588-76b8-49c3-b39e-34928db5c5ae.png) + + +请实现新的散列表方法: + +`put(key,value)`:将 `key 和 `value` 存在一个 `ValuePair` 对象中(即可定义一个包含 `key` 和 `value` 属性的 `ValuePair` 类),并将其加入对应位置的链表中。 + +`get(key)`:返回键值对应的值,没有则返回 `undefined`。 + +`remove(key)`:从散列表中移除键值对应的元素。 + +`print()`:打印散列表中已保存的值。 + + +**提示:先找到元素储存位置所对应的链表,再找到对应的值。** + +```js +const hashTable = new HashTable(); + +hashTable.put("Gandalf", "gandalf@email.com"); +hashTable.put("Tyrion", "tyrion@email.com"); +hashTable.put("Aaron", "aaron@email.com"); +hashTable.put("Ana", "ana@email.com"); +hashTable.put("Mindy", "mindy@email.com"); +hashTable.put("Paul", "paul@email.com"); + +hashTable.print(); + +console.log(hashTable.get("Tyrion")); +console.log(hashTable.get("Aaron")); + +hashTable.remove("Tyrion"); + +hashTable.print(); +``` + +--- +解析: +```js +// 链表的实现代码省略 可以查看之前的代码 +let ValuePair = function (key, value){ + this.key = key + this.value = value + this.toString = function(){ + return `[${this.key} - ${this.value}]` + } +} +class HashTable { + constructor(){ + this.table = [] + } + /** + * 散列函数 + * @param {*} key 键名 + */ + hashCode(key) { + let hash = 0; + for (let i = 0; i < key.length; i++) { + hash += key.charCodeAt(i); + } + return hash % 37; + } + /** + * 向散列表增加/更新一个新的项 + * @param {*} key 添加的键名 + * @param {*} value 添加的值 + */ + put (key, value) { + let position = this.hashCode(key) + if(this.table[position] == undefined){ + this.table[position] = new LinkedList() + } + this.table[position].append(new ValuePair(key, value)) + } + + /** + * 根据键值从散列表中移除值 + * @param {*} key 移除的键名 + * @return {Boolean} 是否成功移除 + */ + remove (key) { + let position = this.hashCode(key) + if ( !key || this.table[position] === undefined ) return false + let current = this.table[position].getHead() + while(current.next){ + if(current.element.key === key){ + this.table[position].remove(current.element) + if(this.table[position].isEmpty){ + this.table[position] = undefined + } + return true + } + current = current.next + } + } + + /** + * 根据键值检索到特定的值 + * @param {*} key 查找的键名 + * @return {*} 查找的值 + */ + get (key) { + let position = this.hashCode(key) + if(!key || this.table[position] === undefined) return undefined + let current = this.table[position].getHead() + while(current.next()){ + if(current.element.key === key){ + return current.element.value + } + current = current.next + } + } + + /** + * 打印散列表中已保存的值 + * @return {*} 散列表的值 + */ + print () { + return this.table + } +} + +``` + + +## 五、实现一个线性探查的散列表 + + +线性探查是解决散列表中冲突的另一种方法,当向表中某一个位置加入新元素的时候,如果索引为 `index` 的位置已经被占据了,就尝试 `index+1` 的位置。如果 `index+1` 的位置也被占据,就尝试 `index+2`,以此类推。 + +![separate-chaining.png](https://cdn.nlark.com/yuque/0/2019/png/102778/1556970188250-d9f8542a-5084-42e7-8bc7-ac57f52e537b.png) + + + +请实现散列表: + +`put(key,value)`:将 `key` 和 `value` 存在一个 `ValuePair` 对象中(即可定义一个包含 `key` 和 `value` 属性的 `ValuePair` 类)并分配到散列表。 + +`get(key)`:返回键值对应的值,没有则返回 `undefined`。 + +`remove(key)`:从散列表中移除键值对应的元素。 + + +**提示:移除一个元素,只需要将其赋值为 undefined。** + + +使用示例: +```js +const hashTable = new HashTable(); + +hashTable.put("Gandalf", "gandalf@email.com"); +hashTable.put("Tyrion", "tyrion@email.com"); +hashTable.put("Aaron", "aaron@email.com"); +hashTable.put("Ana", "ana@email.com"); +hashTable.put("Mindy", "mindy@email.com"); +hashTable.put("Paul", "paul@email.com"); + +hashTable.print(); + +console.log(hashTable.get("Tyrion")); +console.log(hashTable.get("Aaron")); + +hashTable.remove("Tyrion"); + +hashTable.print(); +``` +--- +解析: +```js +let ValuePair = function (key, value){ + this.key = key + this.value = value + this.toString = function(){ + return `[${this.key} - ${this.value}]` + } +} +class HashTable { + constructor(){ + this.table = [] + } + /** + * 散列函数 + * @param {*} key 键名 + */ + hashCode(key) { + let hash = 0; + for (let i = 0; i < key.length; i++) { + hash += key.charCodeAt(i); + } + return hash % 37; + } + /** + * 向散列表增加/更新一个新的项 + * @param {*} key 添加的键名 + * @param {*} value 添加的值 + */ + put (key, value) { + let position = this.hashCode(key) + if(this.table[position] == undefined){ + this.table[position] = new ValuePair(key, value) + }else{ + let index = ++position + while(this.table[index] !== undefined){ + index ++ + } + this.table[index] = new ValuePair(key, value) + } + } + + /** + * 根据键值从散列表中移除值 + * @param {*} key 移除的键名 + * @return {Boolean} 是否成功移除 + */ + remove (key) { + let position = this.hashCode(key) + if( !key || this.table[position] === undefined ) return undefined + if(this.table[position].key === key){ + this.table[index] = undefined + }else{ + let index = ++position + while( + this.table[index] === undefined || + this.table[index].key !== key + ){ + index ++ + } + if(this.table[index].key === key){ + this.table[index] = undefined + } + } + } + + /** + * 根据键值检索到特定的值 + * @param {*} key 查找的键名 + * @return {*} 查找的值 + */ + get (key) { + let position = this.hashCode(key) + if( !key || this.table[position] === undefined ) return undefined + if(this.table[position].key === key){ + return this.table[position].value + }else{ + let index = ++position + while( + this.table[index] === undefined || + this.table[index].key !== key + ){ + index ++ + } + if(this.table[index].key === key){ + return this.table[index].value + } + } + } + + /** + * 打印散列表中已保存的值 + * @return {*} 散列表的值 + */ + print () { + return this.table + } +} +``` + +## 下周预告 +下周将练习 **Tree** 的题目。 + +|Author|王平安| +|---|---| +|E-mail|pingan8787@qq.com| +|博 客|www.pingan8787.com| +|微 信|pingan8787| +|每日文章推荐|https://github.com/pingan8787/Leo_Reading/issues| +|ES小册|js.pingan8787.com| + +## 微信公众号 +![bg](http://images.pingan8787.com/2019_07_12guild_page.png) \ No newline at end of file diff --git a/Cute-Algorithms/Cute-Algorithms-LinkedList.md b/Cute-Algorithms/Cute-Algorithms-LinkedList.md new file mode 100644 index 00000000..f9e99c2b --- /dev/null +++ b/Cute-Algorithms/Cute-Algorithms-LinkedList.md @@ -0,0 +1,550 @@ +这是第三周的练习题,原本应该先发第二周的,因为周末的时候,我的母亲大人来看望她的宝贝儿子,哈哈,我得带她看看厦门这座美丽的城市呀。 + +这两天我抓紧整理下第二周的题目和答案,下面我把之前的也列出来: +* 1.[每周一练 之 数据结构与算法(Stack)](https://juejin.im/post/5cb2df0c5188251aca7340a0) + +> 欢迎关注我的 [个人主页](https://github.com/pingan8787) && [个人博客](http://www.pingan8787.com/) && [个人知识库](http://js.pingan8787.com/) && 微信公众号“前端自习课” + + + +**本周练习内容:数据结构与算法 —— LinkedList** + +这些都是数据结构与算法,一部分方法是团队其他成员实现的,一部分我自己做的,有什么其他实现方法或错误,欢迎各位大佬指点,感谢。 + + +## 一、链表是什么?与数组有什么区别?生活中有什么案例? +---- +解析: +概念参考阅读 [链表 —— 维基百科](https://zh.wikipedia.org/wiki/%E9%93%BE%E8%A1%A8) + +**1.概念:** + +链表(Linked list)是一种上一个元素的引用指向下一个元素的存储结构,链表通过指针来连接元素与元素; + +链表是线性表的一种,所谓的线性表包含顺序线性表和链表,顺序线性表是用数组实现的,在内存中有顺序排列,通过改变数组大小实现。而链表不是用顺序实现的,用指针实现,在内存中不连续。意思就是说,链表就是**将一系列不连续的内存联系起来**,将那种碎片内存进行合理的利用,解决空间的问题。 + +所以,链表允许插入和删除表上任意位置上的节点,但是不允许随即存取。链表有很多种不同的类型:**单向链表**、**双向链表**及**循环链表**。 + +**2.与数组的区别:** +* 相同: +两种结构均**可实现数据的顺序存储**,构造出来的模型呈**线性结构**。 + +* 不同: +链表是**链式的存储结构**;数组是**顺序的存储结构**。 +链表通过**指针**来连接元素与元素,数组则是把所有元素按**次序**依次存储。 + +链表的插入删除元素相对数组较为简单,不需要移动元素,且较为容易实现长度扩充,但是寻找某个元素较为困难。 + +数组寻找某个元素较为简单,但插入与删除比较复杂,由于最大长度需要再编程一开始时指定,故当达到最大长度时,扩充长度不如链表方便。 + + +**数组和链表一些操作的时间复杂度对比:** +数组: +* 查找复杂度:O(1) +* 添加/删除复杂度:O(n) + +链表: +* 查找复杂度:O(n) +* 添加/删除复杂度:O(1) + +**3.生活中的案例:** +火车,是由一些列车厢连接起来; +寻宝游戏,每个线索都是下一个线索地点的指针。 + +## 二、请实现一个链表,并实现以下方法 +* `append(element)`:向列表尾部添加一个新的元素。 +* `insert(position, element)`:向列表指定位置插入一个新的元素。 +* `remove(element)`:从列表中移除并返回特定元素(若有多个相同元素则取第一次出现的情况)。 +* `indexOf(element)`:返回元素在列表的索引(若有多个相同元素则取第一次出现的情况),如果列表中没有该元素则返回 `-1`。 +* `removeAt(position)`:从列表中,移除并返回特定位置的一项。 +* `isEmpty()`:如果列表不含任何元素,返回 `true`,否则返回 `false`。 +* `size()`:返回列表中元素个数,与数组的 `length` 属性类似。 +* `toString()`:由于列表项使用 `Node` 类,需要重写继承自 JavaScript 对象默认的 `toString()` 方法,让其只输出元素的值。 +**提示:Web 端优先使用 ES6 以上的语法实现。** + +---- +解析: + +```js +class Node { + constructor(element){ + this.element = element + this.next = null + } +} +class LinkedList { + constructor(){ + this.length = 0 + this.head = null + } + /** + * 添加元素(末尾添加) + * @param {*} element 添加的元素 + */ + append(element){ + let node = new Node(element) + if(!this.head){ + this.head = node + }else{ + let current = this.head + // 查找最后一项 + while(current.next){ + current = current.next + } + // 将最后一下的next赋值为node,实现追加元素 + current.next = node + } + this.length ++ + } + /** + * 添加元素(指定位置) + * @param {Number} position 添加的位置 + * @param {*} element 添加的元素 + */ + insert(position, element){ + if(position >= 0 && position <= this.length){ + let node = new Node(element), + index = 0, + previous = null + if(position === 0){ + node.next = this.head + this.head = node + }else{ + let current = this.head + while(index++ < position){ + previous = current + current = current.next + } + previous.next = node + node.next = current + } + this.length ++ + } + } + /** + * 删除元素 + * @param {*} element 删除的元素 + * @return {*} 被删除的元素 + */ + remove(element){ + let current = this.head, + previous = null + if(element === this.head.element){ + this.head = current.next + }else{ + while(current.next && current.element !== element){ + previous = current + current = current.next + } + previous.next = current.next + this.length -- + return current.element + } + } + /** + * 删除元素(指定位置) + * @param {Number} position 删除元素的位置 + * @return {*} 被删除的元素 + */ + removeAt(position){ + if(position >= 0 && position <= this.length){ + let current = this.head, + index = 0, + previous = null + if(position === 0){ // 删除第一项 + this.head = current.next + }else{ + while(index++ < position){ + previous = current + current = current.next + } + previous.next = current.next + } + this.length -- + return current.element + } + } + /** + * 查找指定元素的位置 + * @param {*} element 查找的元素 + * @return {Number} 查找的元素的下标 + */ + indexOf(element){ + let current = this.head, + index = 0 + while(current.next && current.element !== element){ + current = current.next + index ++ + } + return index === 0 ? -1 : index + } + /** + * 链表是否为空 + * @return {Boolean} + */ + isEmpty(){ + return this.length === 0 + } + /** + * 链表的长度 + * @return {Number} + */ + size(){ + return this.length + } + /** + * 将链表转成字符串 + * @return {String} + */ + toString(){ + let current = this.head, + arr = new Array() + while(current.next){ + arr.push(current.element) + current = current.next + } + arr.push(current.element) + return arr.toString() + } +} + +let leo = new LinkedList() +leo.append(3) +leo.append(6) +leo.append(9) +console.log(leo.length) +console.log(leo.head) +leo.remove(6) +console.log(leo.length) +console.log(leo.head) +console.log(leo.toString()) +``` + +## 三、实现反转链表 +用链表的方式,输出一个反转后的单链表。 + +示例: +```js +输入: 1->2->3->4->5->NULL +输出: 5->4->3->2->1->NULL +// input +let head = { + 'val': 1,'next': { + 'val': 2,'next': { + 'val': 3,'next': { + 'val': 4,'next': { + 'val': 5, + 'next': null + } + } + } + } +}; +reverseList(head) + +// output +head = { + 'val': 5,'next': { + 'val': 4,'next': { + 'val': 3,'next': { + 'val': 2,'next': { + 'val': 1, + 'next': null + } + } + } + } +}; +``` + +**解题思路1.使用迭代:** +在遍历列表时,将当前节点的 `next` 指针改为**指向前一个元素**。由于节点没有引用其上一个节点,因此必须**先存储其前一个元素**。在更改引用之前,还需要另一个指针来存储下一个节点。**不要忘记在最后返回新的头引用**! + +**解题思路2.使用递归:** +通过递归修改 `head.next.next` 和 `head.next` 指针来实现。 + + +---- +解析: +题目出自:[Leetcode 206. 反转链表](https://leetcode-cn.com/problems/reverse-linked-list/) + +介绍两种常用方法: + +**1.使用迭代:** +在遍历列表时,将当前节点的 `next` 指针改为**指向前一个元素**。由于节点没有引用其上一个节点,因此必须**先存储其前一个元素**。在更改引用之前,还需要另一个指针来存储下一个节点。**不要忘记在最后返回新的头引用**! +```js +/** + * Definition for singly-linked list. + * function ListNode(val) { + * this.val = val; + * this.next = null; + * } + */ +/** + * @param {ListNode} head + * @return {ListNode} + */ +let reverseList = function(head) { + let pre = null, curr = head + while (curr) { + next = curr.next + curr.next = pre + pre = curr + curr = next + } + return pre +}; +``` +**复杂度分析** + +**时间复杂度**:`O(n)`。 假设 `n` 是列表的长度,时间复杂度是 `O(n)`。 +**空间复杂度**:`O(1)`。 + + +**2.使用递归:** + +```js +/** + * Definition for singly-linked list. + * function ListNode(val) { + * this.val = val; + * this.next = null; + * } + */ +/** + * @param {ListNode} head + * @return {ListNode} + */ +let reverseList = function(head) { + if(head == null || head.next == null) return head + let pre = reverseList(head.next) + head.next.next = head + head.next = null + return pre +}; +``` +**复杂度分析** + +**时间复杂度**:`O(n)`。 假设 `n` 是列表的长度,那么时间复杂度为 `O(n)`。 +**空间复杂度**:`O(n)`。 由于使用递归,将会使用隐式栈空间。递归深度可能会达到 `n` 层。 + + +## 四、判断链表是否有环 +设计一个函数 `hasCycle`,接收一个链表作为参数,判断链表中是否有环。 +为了表示给定链表中的环,我们使用整数 `pos` 来表示**链表尾**连接到**链表中的位置**(索引从 `0` 开始)。 如果 `pos` 是 `-1`,则在该链表中没有环。 + +![linkedlist-cycle](http://images.pingan8787.com/linkedlist-cycle.png) + +需要注意的是,不可能存在多个环,最多只有一个。 + +**示例 1:** +``` +输入:head = [3,2,0,-4], pos = 1 +输出:true +解释:链表中有一个环,其尾部连接到第二个节点。 +``` +![示例 1](http://images.pingan8787.com/20190702add1.png) + +**示例 2:** +``` +输入:head = [1,2], pos = 0 +输出:true +解释:链表中有一个环,其尾部连接到第一个节点。 +``` +![示例 2](http://images.pingan8787.com/20190702add2.png) + +**示例 3:** +``` +输入:head = [1], pos = -1 +输出:false +解释:链表中没有环。 +``` +![示例 3](http://images.pingan8787.com/20190702add3.png) + + + +**解题思路1.判断是否有 null:** +一直遍历下去,如果遍历到 `null` 则表示没有环,否则有环,但是考虑到性能问题,最好给定一段时间作为限制,超过时间就不要继续遍历。 + +**解题思路2.标记法:** +也是要遍历每个节点,并在遍历的节点添加标记,如果后面遍历过程中,遇到有这个标记的节点,即表示有环,反之没有环。 + +**解题思路3.使用双指针(龟兔赛跑式):** +设置2个指针,一个 `快指针` 每次走 2 步,`慢指针` 每次走 1 步,如果没有环的情况,最后这两个指针不会相遇,如果有环,会相遇。 + +---- +解析: +题目出自:[Leetcode 141. 环形链表](https://leetcode-cn.com/problems/linked-list-cycle/) + +**1.断是否有 null** +```js +/** + * Definition for singly-linked list. + * function ListNode(val) { + * this.val = val; + * this.next = null; + * } + */ + +/** + * @param {ListNode} head + * @return {boolean} + */ +let hasCycle = function(head) { + while(head){ + if(head.value == null) return true + head.value = null + head = head.next + } + return false +} +``` + +**2.标记法** +```js +/** + * Definition for singly-linked list. + * function ListNode(val) { + * this.val = val; + * this.next = null; + * } + */ + +/** + * @param {ListNode} head + * @return {boolean} + */ +let hasCycle = function(head) { + let node = head + while(node){ + if(node.isVisit){ + return true + }else{ + node.isVisit = true + } + node = node.next + } + return false +}; +``` + +**3.使用双指针** +```js +/** + * Definition for singly-linked list. + * function ListNode(val) { + * this.val = val; + * this.next = null; + * } + */ + +/** + * @param {ListNode} head + * @return {boolean} + */ +let hasCycle = function(head) { + if(head == null || head.next == null) return false + let slow = head, fast = head.next + while(slow != fast){ + if(fast == null || fast.next == null) return false + slow = slow.next // 慢指针每次走1步 + fast = fast.next.next // 快指针每次走1补 + } + return true +}; +``` + + +## 五、实现两两交换链表中的节点 +给定一个链表,两两交换其中相邻的节点,并返回交换后的链表。 + +**你不能只是单纯的改变节点内部的值**,而是需要实际的进行节点交换。 + +**示例:** +``` +给定 1->2->3->4, 你应该返回 2->1->4->3. +给定 1->2->3->4->5, 你应该返回 2->1->4->3->5. +``` + +**解题思路1.使用迭代:** +和**反转链表**类似,关键在于有三个指针,分别指向前后和当前节点,而不同在于两两交换后,移动节点的步长为2,需要注意。 + +**解题思路2.使用递归:** +这里也可以使用递归,也可以参考**反转链表**的问题,终止条件是递归到链表为空,或者只剩下一个元素没得交换了,才终止。 + +---- +解析: +题目出自:[Leetcode 24. 两两交换链表中的节点](https://leetcode-cn.com/problems/swap-nodes-in-pairs/) + +介绍两种常用方法: + +**1.使用迭代:** +```js +/** + * Definition for singly-linked list. + * function ListNode(val) { + * this.val = val; + * this.next = null; + * } + */ +/** + * @param {ListNode} head + * @return {ListNode} + */ +let swapPairs = function (head){ + if(!head) return null + let arr = [] + while(head){ + let next = head.next + head.next = null + arr.push(head) + head = next + } + + for(let i = 0; i < arr.length; i += 2){ + let [a, b] = [arr[i], arr[i + 1]] + if(!b) continue + [arr[i], arr[i + 1]] = [b, a] + } + + for(let i = 0; i < arr.length - 1; i ++){ + arr[i].next = arr[i + 1] + } + return arr[0] +} +``` + +**2.使用递归:** +```js +/** + * Definition for singly-linked list. + * function ListNode(val) { + * this.val = val; + * this.next = null; + * } + */ +/** + * @param {ListNode} head + * @return {ListNode} + */ + +let swapPairs = function (head){ + if(head == null || head.next ==null) return head + let next = head.next + head.next = swapPairs(next.next) + next.next = head + return next +} +``` + + +|Author|王平安| +|---|---| +|E-mail|pingan8787@qq.com| +|博 客|www.pingan8787.com| +|微 信|pingan8787| +|每日文章推荐|https://github.com/pingan8787/Leo_Reading/issues| +|ES小册|js.pingan8787.com| + +## 微信公众号 +![bg](http://images.pingan8787.com/2019_07_12guild_page.png) \ No newline at end of file diff --git a/Cute-Algorithms/Cute-Algorithms-Queue.md b/Cute-Algorithms/Cute-Algorithms-Queue.md new file mode 100644 index 00000000..1a6ce1f4 --- /dev/null +++ b/Cute-Algorithms/Cute-Algorithms-Queue.md @@ -0,0 +1,363 @@ +![封面](https://user-gold-cdn.xitu.io/2019/4/27/16a5cd3f25400f0f?imageView2/1/w/1304/h/734/q/85/format/webp/interlace/1) + +这是第二周的练习题,这里补充下咯,五一节马上就要到了,自己的计划先安排上了,开发一个有趣的玩意儿。 + +下面是之前分享的链接: +* 1.[每周一练 之 数据结构与算法(Stack)](https://juejin.im/post/5cb2df0c5188251aca7340a0) +* 2.[每周一练 之 数据结构与算法(LinkedList)](https://juejin.im/post/5cbdbb1af265da036d79bb35) + +> 欢迎关注我的 [个人主页](https://github.com/pingan8787) && [个人博客](http://www.pingan8787.com/) && [个人知识库](http://js.pingan8787.com/) && 微信公众号“前端自习课” + + +**本周练习内容:数据结构与算法 —— Queue** + +这些都是数据结构与算法,一部分方法是团队其他成员实现的,一部分我自己做的,有什么其他实现方法或错误,欢迎各位大佬指点,感谢。 + + +## 一、队列有什么特点,生活中有什么例子? + +---- +解题: +**1.概念介绍** +> 队列,又称为伫列(queue),是先进先出(FIFO, First-In-First-Out)的线性表。在具体应用中通常用链表或者数组来实现。队列只允许在后端(称为rear)进行插入操作,在前端(称为front)进行删除操作。 ——《维基百科》 + +队列特点:**先进先出**操作。 +生活中的案例:常见的排队,在电影院也好,排队结账也是,排在第一位的人会先接受服务。 + +**2.与堆栈区别** +队列的操作方式和堆栈类似,唯一的区别在于**队列只允许新数据在后端进行添加。** + + +## 二、请实现一个队列,并实现以下方法: + +* `enqueue(element)`:向队列尾部添加一个新的项。 +* `dequeue()`:移除队列的第一项,并返回被移除的元素。 +* `front()`:返回队列中第一个元素 —— 最先被添加,也将是最先被移除的元素。队列不做任何变动 (不移除元素,只返回元素信息 —— 与 `Stack` 类的 `peek` 方法类似)。 +* `tail()`:返回队列中的最后一个元素,队列不做任何变动。 +* `isEmpty()`:如果栈没有任何元素就返回 `true`,否则返回 `false`。 +* `size()`:返回队列包含的的元素个数,与数组的 `length` 属性类似。 +* `print()`:打印队列中的元素。 + + +**提示:Web 端优先使用 ES6 以上的语法实现。** + +---- +解题: + +```js + /** + * 2. 实现一个队列 + */ +class Queue { + constructor (){ + this.items = [] + } + // enqueue(element):向队列尾部添加一个新的项。 + enqueue( element ){ + this.items.push(element) + } + // dequeue():移除队列的第一项,并返回被移除的元素。 + dequeue (){ + return this.items.shift() + } + // front():返回队列中第一个元素 —— 最先被添加,也将是最先被移除的元素。队列不做任何变动 (不移除元素,只返回元素信息 —— 与 Stack 类的 peek 方法类似)。 + front (){ + return this.items[0] + } + // tail():返回队列中的最后一个元素,队列不做任何变动。 + tail (){ + return this.items[this.items.length-1] + } + // isEmpty():如果栈没有任何元素就返回 true,否则返回 false。 + isEmpty (){ + return this.items.length === 0 + } + // size():返回队列包含的的元素个数,与数组的 length 属性类似。 + size (){ + return this.items.length + } + // print():打印队列中的元素。 + print (){ + console.log(this.items.toString()) + } +} +``` + + +## 三、使用队列计算斐波那契数列的第 n 项。 + +斐波那契数列(Fibonacci sequence),又称黄金分割数列、因数学家列昂纳多·斐波那契(Leonardoda Fibonacci)以兔子繁殖为例子而引入,故又称为“兔子数列”,指的是这样一个数列: +``` +1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233,377,610... +``` + +在数学上,斐波那契数列以如下被以递推的方法定义:**F(1)=1,F(2)=1, F(n)=F(n-1)+F(n-2)(n>=3,n∈N*)**,即**前两项固定为 1**,**后面的项为前两项之和**,依次向后。在现代物理、准晶体结构、化学等领域,斐波纳契数列都有直接的应用。 + +使用示例如下: +```js +fibonacci(5); --> 5 +fibonacci(9); --> 34 +fibonacci(14); --> 377 +``` + +--- +解题: + +**解题方法1:** +```js +/** + * 3. 使用队列计算斐波那契数列的第 n 项。 + * 前两项固定为 1,后面的项为前两项之和,依次向后。 + * @param {Number} num + */ + +function fibonacci (num){ + if(isNaN(num) || num < 0 || num === 0) return 0 + // // 1. 直接 + // let n1 = 1, n2 = 1, sum + // for(let i = 3; i <= num; i++){ + // sum = n1 + n2 + // n1 = n2 + // n2 = sum + // } + // // 2. 队列 考虑小于等于2 + // let arr = [], sum + // num === 1 && (arr = [1]) + // num >= 2 && (arr = [1, 1]) + // for(let i = 3; i <= num; i ++){ + // sum = arr[i-2] + arr[i-3] + // arr.push(sum) + // } + // // 3.队列 进出队列 + let queue = [], sum; + for(let i = 1; i <= num; i ++){ + if(i <=2 ){ + queue.push(1) + }else{ + sum = queue[0] + queue[1] + queue.push(sum) + queue.shift() + } + } + return sum +} +``` + +**解题方法2:** +```js +function fibonacci(n) { + const queue = new Queue(); + queue.enqueue(1); + queue.enqueue(1); + + let index = 0; + while(index < n - 2) { + index += 1; + // 出队列一个元素 + const delItem = queue.dequeue(); + // 获取头部值 + const headItem = queue.front(); + const nextItem = delItem + headItem; + queue.enqueue(nextItem); + } + return queue.tail(); +} +console.log(fibonacci(9)); // 34 +``` + + +## 四、实现优先队列 PriorityQueue。 + +现实中优先队列的例子很多,比如机场登机的顺序,头等舱和商务舱乘客优先级高于经济舱乘客。又如在银行中办理业务时,VIP 客户的优先级高于普通客户。要实现一个优先队列,有两种方式: + +1. 设置优先级,然后在正确的位置添加元素。 +2. 用入列操作添加元素,然后按照优先级移除它们。 + + +**本题要求使用第一种方式来实现优先队列,数值越小优先级越高,若优先级相同时,先入队的元素,排在前面。** + +使用示例如下: +```js +let priorityQueue = new PriorityQueue(); +priorityQueue.enqueue("leo", 2); +priorityQueue.enqueue("pingan", 1); +priorityQueue.enqueue("robin", 1); +priorityQueue.print(); +// pingan - 1 +// robin - 1 +// leo - 2 +``` + +--- +解题: + +**解题方法1:** +```js +class PriorityQueue { + constructor() { + this._items = []; + } + + enqueue(element, priority) { + let queueElement = { + element + priority + }; + + if (this.isEmpty()) { + this._items.push(queueElement); + } else { + let added = false; + for (var i = 0; i < this.size(); i++) { + if (queueElement.priority < this._items[i].priority) { + this.items.splice(i, 0, queueElement); + added = true; + break ; + } + } + + if (!added) { + this._items.push(queueElement); + } + } + } + + print() { + var strArr = []; + strArr = this._items.map(function (item) { + return `${item.element}->${item.priority}`; + }); + + console.log(strArr.toString()); + } +} +``` + +**解题方法2:** +```js +/** + * 4. 实现优先队列 + */ + +class PriorityQueue { + constructor (){ + this.items = [] + } + enqueue (element, priority){ + let ele = {element, priority} + let isAdded = false + for(let i = 0; i < this.items.length; i++){ + if(ele.priority < this.items[i].priority){ + this.items.splice(i, 0, ele) + isAdded = true + break + } + } + !isAdded && this.items.push(ele) + } + print (){ + for(let i = 0; i < this.items.length; i++){ + let {element, priority} = this.items[i] + console.log(`${element} - ${priority}`) + } + } +} +let leo = new PriorityQueue() +leo.enqueue("leo", 2); +leo.enqueue("leo1", 1); +leo.enqueue("leo2", 1); +console.log(leo) +``` + +## 五、用队列实现栈。 + +利用两个队列实现栈,栈的特点是后进先出,可以让元素入队 `q1`,留下队尾元素让其他元素出队,暂存到 `q2` 中,再让 `q1` 中剩下的元素出队,即最后进的最先出来。 + + +**提示:入栈和出栈都在 q1 中完成,q2 只作为临时中转空间。** + +--- +解题: +```js +/** + * 5. 队列实现栈 + */ +class Myqueue { + constructor (){ + this.items = [] + } + enqueue (element){ + this.items.push(element) + } + dequeue (){ + return this.items.shift() + } +} +class Mystack { + constructor (){ + this.q1 = new myQueue() + this.q2 = new myQueue() + } + push (element){ + this.q1.enqueue(element) + this.q2.items = [] + let len = this.q1.items.length + while(len > 0){ + this.q2.enqueue(this.q1.items[len-1]) + len -- + } + } + pop (){ + let result = this.q2.dequeue() + let len = this.q2.items.length + this.q1.items = [] + while(len > 0){ + this.q1.enqueue(this.q2.items[len-1]) + len -- + } + return result + } + print (){ + console.log(this.q1.items.toString()) + } +} +``` + +**这里也可以直接使用第二题定义的Queue来实现:** +```js +class QueueStack { + constructor() { + this.queue = new Queue(); + } + + push(item) { + this.queue.enqueue(item); + } + + pop() { + // 向队列末尾追加 队列长度-1 次,后弹出队列头部 + for(let i = 1; i < this.queue.size(); i += 1) { + this.queue.enqueue(this.queue.dequeue()); + } + return this.queue.dequeue(); + } + + peek() { + return this.queue.tail(); + } +} +``` + +## 下周预告 +下周将练习**集合(Set)** 的题目,五一要到咯,也要好好做自己一个项目了。 + +|Author|王平安| +|---|---| +|E-mail|pingan8787@qq.com| +|博 客|www.pingan8787.com| +|微 信|pingan8787| +|每日文章推荐|https://github.com/pingan8787/Leo_Reading/issues| +|ES小册|js.pingan8787.com| + +## 微信公众号 +![bg](http://images.pingan8787.com/2019_07_12guild_page.png) \ No newline at end of file diff --git a/Cute-Algorithms/Cute-Algorithms-Set.md b/Cute-Algorithms/Cute-Algorithms-Set.md new file mode 100644 index 00000000..4fedf7a0 --- /dev/null +++ b/Cute-Algorithms/Cute-Algorithms-Set.md @@ -0,0 +1,268 @@ +这是第四周的练习题,五一放假结束,该收拾好状态啦。 + +下面是之前分享的链接: +* 1.[每周一练 之 数据结构与算法(Stack)](https://juejin.im/post/5cb2df0c5188251aca7340a0) +* 2.[每周一练 之 数据结构与算法(LinkedList)](https://juejin.im/post/5cbdbb1af265da036d79bb35) +* 3.[每周一练 之 数据结构与算法(Queue)](https://juejin.im/post/5cc3cbaaf265da03a85ac7f8) + +> 欢迎关注我的 [个人主页](https://github.com/pingan8787) && [个人博客](http://www.pingan8787.com/) && [个人知识库](http://js.pingan8787.com/) && 微信公众号“前端自习课” + + +**本周练习内容:数据结构与算法 —— Set** + +这些都是数据结构与算法,一部分方法是团队其他成员实现的,一部分我自己做的,有什么其他实现方法或错误,欢迎各位大佬指点,感谢。 + +## 一、集合是什么?与它相关数学概念有哪些 + +--- +解题: +**1.集合定义:** +**集合(Set)**是一种包含不同元素的数据结构。集合中的元素称为**成员**,集合最重要的两个特点: +* 集合中的成员是无序; +* 集合中不存在相同成员; + +即:无序且唯一。 + +**2.集合相关的数学概念:** +集合的概念,如数学中一个由大于或等于0的整数组成的自然数集合, `N = { 0, 1, 2, ...}`。 +还有如**空集**,表示不包含任何元素的集合。 +并且也有并集,交集,差集等操作。 + + +## 二、请实现一个集合,并实现以下方法 + +`add(value)`:向集合添加一个新的项。 +`delete(value)`:从集合移除一个值。 +`has(value)`:如果值在集合中,返回 true,否则返回 false。 +`clear()`:移除集合中的所有项。 +`size()`:返回集合所包含元素的数量。与数组的 length 属性类似。 +`values()`:返回一个包含集合中所有值的数组。 + +--- +解题: +```js +class Sets { + constructor(){ + this.items = {} + } + has(value){ + // return value in this.items + return this.items.hasOwnProperty(value) + } + add(value){ + if(!this.has(value)) { + this.items[value] = value + return true + } + return false + } + delete(value){ + if(!this.has(value)){ + delete this.items[value] + return true + } + return false + } + clear(){ + this.items = {} + } + size(){ + const values = this.values() + return values.length + } + values(){ + return Object.keys(this.items) + } +} +``` + +## 三、请实现集合的并集、交集、差集、子集操作 +* **并集(union)**:对于给定的两个集合,返回一个包含两个集合中所有元素的新集合。 +* **交集(intersection)**:对于给定的两个集合,返回一个包含两个集合中共用元素的新集合。 +* **差集(difference)**:对于给定的两个集合,返回一个包含所有存在于第一个集合且不存在于第二个集合的元素的新集合。 +* **子集(subset)**:验证一个给定集合是否是另一个集合的子集。 + +--- +解题: +```js +/** + * union 并集 + * @param {Object} otherSet 其他集合 + */ +Sets.prototype.union = function(otherSet){ + let result = new Sets(), + current = this.values(), + other = otherSet.values() + for(let i = 0; i < current.length; i++){ + result.add(current[i]) + } + for(let i = 0; i < other.length; i++){ + result.add(other[i]) + } + return result +} + + +/** + * intersection 交集 + * @param {Object} otherSet 其他集合 + */ +Sets.prototype.intersection = function(otherSet){ + let result = new Sets(), + current = this.values() + for(let i = 0; i < current.length; i++){ + if(otherSet.has(current[i])){ + result.add(current[i]) + } + } + return result +} + + +/** + * difference 差集 + * @param {Object} otherSet 其他集合 + */ +Sets.prototype.difference = function(otherSet){ + let result = new Sets(), + current = this.values() + for(let i = 0; i < current.length; i++){ + if(!otherSet.has(current[i])){ + result.add(current[i]) + } + } + return result +} + + + +/** + * subset 子集 + * @param {Object} otherSet 其他集合 + */ +Sets.prototype.subset = function(otherSet){ + let result = new Sets(), + current = this.values() + + if(this.size() > otherSet.size()) return false + for(let i = 0; i < current.length; i++){ + if(!otherSet.has(current[i])){ + return false + } + } + return true +} +``` + +## 四、给定两个数组,编写一个 intersection() 函数来计算它们的交集 + + +使用示例如下: +```js +const nums1 = [1, 2, 2, 1]; +const nums2 = [2, 2]; +const nums3 = [4, 9, 5]; +const nums4 = [9, 4, 9, 8, 4]; + +intersection(nums1, nums2); // [2] +intersection(nums3, nums4); // [9, 4] +``` + +**提示:输出结果中的每个元素是唯一的,可以不考虑输出结果的顺序。** + +--- +解题: +```js +function intersection(arr1, arr2){ + if(!Array.isArray(arr1) || !Array.isArray(arr2)) return [] + let create = function(arr){ + let sets = new Sets() + arr.map(item => sets.add(item)) + return sets + } + let Sets1 = create(arr1) + let Sets2 = create(arr2) + let result = Sets1.intersection(Sets2) + return result.values() +} +``` + +## 五、给定一组不含重复元素的整数数组 nums,返回该数组所有可能的子集 + +使用示例如下: +```js +const nums = [1, 2, 3]; +subsets(nums); +// 输出以下结果: +[ + [3], + [1], + [2], + [1, 2, 3], + [1, 3], + [2, 3], + [1, 2], + [] +] +``` +来源:[leetcode 78.集合](https://leetcode-cn.com/problems/subsets/) + +--- +解题: + +**目前网络上的最优解:** +```js +function subsets(nums){ + if(!nums || !Array.isArray(nums)) return [] + + function diff (num, vec) { + let tmp = vec.slice(0) + result.push(tmp) + for (let i = num; i < len; i++) { + vec.push(nums[i]) + diff(i + 1, vec) + vec.splice(-1) + } + } + + const len = nums.length + let arr = [], result = [] + diff(0, arr) + return result +} +``` + +**穷举法:** +```js +function subsets(nums){ + if(!nums || !Array.isArray(nums)) return [] + + let result = [[]], + len = nums.length + if(len === 0) return result + for(let i = 0; i < len; i++){ + let l = result.length + let num = nums[i] + let array = [num] + for(let j = 0; j < l; j++){ + let tmparray = result[j].concat(array) + result.push(tmparray) + } + } + return result +} +``` + +## 下周预告 +下周将练习**Dictionary 和 HashTable** 的题目。 + +|Author|王平安| +|---|---| +|E-mail|pingan8787@qq.com| +|博 客|www.pingan8787.com| +|微 信|pingan8787| +|每日文章推荐|https://github.com/pingan8787/Leo_Reading/issues| +|ES小册|js.pingan8787.com| + +## 微信公众号 +![bg](http://images.pingan8787.com/2019_07_12guild_page.png) \ No newline at end of file diff --git a/Cute-Algorithms/Cute-Algorithms-Stack.md b/Cute-Algorithms/Cute-Algorithms-Stack.md new file mode 100644 index 00000000..a847572a --- /dev/null +++ b/Cute-Algorithms/Cute-Algorithms-Stack.md @@ -0,0 +1,313 @@ +最近公司内部在开始做前端技术的技术分享,每周一个主题的 **每周一练**,以**基础知识**为主,感觉挺棒的,跟着团队的大佬们学习和复习一些知识,新人也可以多学习一些知识,也把团队内部学习氛围营造起来。 + +我接下来会开始把每周一练的题目和知识整理一下,便于思考和巩固,就像今天这篇开始。 + +学习的道路,很漫长,要坚持,希望大家都能掌握自己喜欢的技术,和自己需要的技术。 + +> 欢迎查看我的 [个人主页](https://github.com/pingan8787) && [个人博客](http://www.pingan8787.com/) && [个人知识库](http://js.pingan8787.com/) && 微信公众号“前端自习课” + +**本周练习内容:数据结构与算法 —— Stack** + +这些都是数据结构与算法,一部分方法是团队其他成员实现的,一部分我自己做的,有什么其他实现方法或错误,欢迎各位大佬指点,感谢。 + +## 一、栈有什么特点,生活中有什么例子? +* 栈( stack )又称**堆栈**,是一种后进先出的**有序集合**,其中一端为栈顶,另一端为栈底,添加元素(称为压栈/入栈或进栈)时,将新元素压入栈顶,删除元素(称为出栈或退栈)时,将栈底元素删除并返回被删除元素。 +* 特点:**先进后出,后进先出**。 +* 例子:一叠书、一叠盘子。 + +![栈](http://images.pingan8787.com/20190702add.png) + +## 二、实现一个栈,并实现下面方法 +* `push(element)`:添加一个新元素到栈顶。 +* `pop()`:移除栈顶的元素,同时返回被移除的元素。 +* `peek()`:返回栈顶的元素,不对栈做任何修改 (这个方法不会移除栈顶的元素,仅仅返回它)。 +* `isEmpty()`:如果栈没有任何元素就返回 `true`,否则返回 `false`。 +* `clear()`:移除栈里面的所有元素。 +* `size()`:返回栈里的元素个数。这个方法与数组的 `length` 属性类似。 + +**方法1:ES6实现** +```js +class Stack { + constructor (){ + this.items = [] + } + push( element ){ + this.items.push(element) + } + pop(){ + return this.items.pop() + } + peek(){ + return this.items[this.items.length - 1] + } + isEmpty(){ + return this.items.length === 0 + } + clear(){ + this.items = [] + } + size(){ + return this.items.length + } +} +``` +上面实现的方式虽然简单,但是内部 `items` 属性是公共的,为了满足面向对象变成私有性的原则,我们应该让 `items` 作为私有属性,因此我们可以使用 ES6 中 `Symbol` 或 `WeakMap` 来实现: + +**方法2:使用 ES6 的 Symbol 基本数据类型实现** +知识点复习:[ES6 中的 Symbol 介绍](http://es6.ruanyifeng.com/#docs/symbol) +```js +const _items = Symbol() +class Stack { + constructor (){ + this[_items] = [] + } + push (element){ + this[_items].push(element) + } + // 剩下方法和第一种实现的差不多,这里省略 + // 只要把前面方法中的 this.items 更改为 this[_items] +} +``` + +**方法3:使用 ES6 的 WeakMap 实现** +知识点复习:[ES6 中的 WeakMap 介绍](http://es6.ruanyifeng.com/#docs/set-map#WeakMap) +```js +const items = new WeakMap() +class Stack { + constructor (){ + items.set(this, []) + } + push (element){ + let item = items.get(this) + item.push(element) + } + // 剩下方法和第一种实现的差不多,这里省略 + // 只要把前面方法中的获取 this.items 的方式,更改为 items.get(this) 获取 +} +``` + +## 三、编写一个函数,实现十进制转二进制 +题目意思很简单,就是十进制转二进制,但是在实际工作开发中,我们更愿意实现的是任意进制转任意进制,不过呢,我们还是以解决问题为首要目标呀。 + +当然,业务需求可以直接使用 `toString(2)` 方法,但是为了练习,咱还是不这么用咯。 + +**方法1:使用前面定义的 Stack 类** +这里使用前面题目中定义的 `Stack` 类。 +```js +/** + * 十进制转换为二进制 + * @param {Number} bit + */ +function bitset (bit){ + if(bit == 0) return '0' + if(!/^[0-9]+.?[0-9]*$/.test(bit)){ + return new Error('请输入正确的数值!') + } + + let stack = new Stack(), result = '' + while (bit > 0){ + stack.push(bit % 2) + bit = Math.floor(bit / 2) + } + while (!stack.isEmpty()){ + result += stack.pop().toString() + } + return result + +} +``` + +**方法2:简单实现** +下面这个方法,其实不太好,因为没有怎么用到这次要练习的**栈**方法,哈哈。 +```js +/** + * 十进制转换为二进制 + * @param {Number} bit + */ +function bitset (bit){ + if(bit == 0) return '0' + if(!/^[0-9]+.?[0-9]*$/.test(bit)){ + return new Error('请输入正确的数值!') + } + + let arr = [] + while(bit > 0){ + arr.push(bit % 2) + bit = Math.floor(bit / 2) + } + return arr.reverse().join('') +} +``` +另外可以参考:[wikiHow - 从十进制转换为二进制](https://zh.wikihow.com/%E4%BB%8E%E5%8D%81%E8%BF%9B%E5%88%B6%E8%BD%AC%E6%8D%A2%E4%B8%BA%E4%BA%8C%E8%BF%9B%E5%88%B6)。 + + +## 四、编写一个函数,实现检验圆括号顺序的有效性 +主要目的就是:该函数接收一个**圆括号字符串**,判断里面的括号顺序是否有效,如果有效则返回 `true` 反之 `false`。 +如: +* `(` -> `false` +* `()` -> `true` +* `(()` -> `false` +* `())` -> `false` +* `())` -> `false` +* `(((()()))())` -> `true` + +这个题目实现的主要方法是:遍历字符串,先排除错误情况,然后将 `(` 入栈保存,将 `)` 入栈匹配前一个元素是否是 `(` ,如果是,则 `pop()` 前一个元素 `(`,如果不是,则 `push()` 这个 `)` 入栈,最终查看栈是否为空,若是则检验成功,否则失败。 + +**方法1:使用前面定义的 Stack 类** +这里使用前面题目中定义的 `Stack` 类。 +```js +/** + * 检验圆括号顺序的有效性 + * @param {String} str + */ +function validParentheses (str){ + if(!str || str.length === 0 || str[0] === ')') return false + + let stack = new Stack() + str.split('').forEach(char => { + let status = stack.peek() === '(' && char === ')' + status ? stack.pop() : stack.push(char) + }) + return stack.isEmpty() +} +``` + + +**方法2:出入栈操作** +```js +/** + * 检验圆括号顺序的有效性 + * @param {String} str + */ +function validParentheses (str){ + if(!str || str.length === 0 || str[0] === ')') return false + + let arr = [] + for(let i = 0; i < str.length ; i++){ + str[i] === '(' ? arr.push(str[i]) : arr.pop() + } + return arr.length === 0 +} +``` + +## 五、改造题二,添加一个 min 函数来获得栈中最小元素 + +|步骤|数据栈|辅助栈|最小值| +|---|---|---|---| +|1.push 3|3|0|3| +|2.push 4|3, 4|0, 0|3| +|3.push 2|3, 4, 2|0, 0, 2|2| +|4.push 1|3, 4, 2 ,1|0, 0, 2, 3|1| +|5.pop |3, 4, 2|0, 0, 2|2| +|6.pop |3, 4|0, 0|3| +|7.push |3, 4 ,0|0, 0, 2|0| + +使用示例如下: +```js +let stack = new Stack(); +stack.push(3); +console.log('After push 3, Min item is', stack.min()); +stack.push(4); +console.log('After push 4, Min item is', stack.min()); +stack.push(2); +console.log('After push 2, Min item is', stack.min()); +stack.push(1); +console.log('After push 1, Min item is', stack.min()); +stack.pop(); +console.log('After pop, Min item is', stack.min()); +stack.pop(); +console.log('After pop, Min item is', stack.min()); +stack.push(0); +console.log('After push 0, Min item is', stack.min()); +``` + +**提示:利用辅助栈(Web 端可利用数组),每次对栈 push/pop 元素时,也同时更新辅助栈(存储最小元素的位置)** + +**方法1:小操作** +```js +class Stack { + constructor() { + this.items = []; + this.minIndexStack = []; + } + + push(element) { + this.items.push(element); + let minLen = this.minIndexStack.length; + let minItemIndex = this.minIndexStack[minLen - 1]; + if(minLen === 0 || this.items[minItemIndex] > item) { + this.minIndexStack.push(this.items.length - 1); + } else { + this.minIndexStack.push(minItemIndex); + } + } + + pop() { + this.minIndexStack.pop(); + return this.items.pop(); + } + + min() { + let len = this.minIndexStack.length; + return (len > 0 && this.items[this.minIndexStack[len - 1]]) || 0; + } + + peek() { + return this.items[this.items.length - 1]; + } + + // 省略其它方法 +} +``` + + +**方法2:与方法1中push实现的差异** +```js +class Stack { + constructor (){ + this.items = [] // 数据栈 + this.arr = [] // 辅助栈 + } + push( element ){ + this.items.push(element) + let min = Math.min(...this.items) + this.arr.push( min === element ? this.size() - 1 : 0) + } + pop(){ + this.arr.pop() + return this.items.pop() + } + peek(){ + return this.items[this.items.length - 1] + } + isEmpty(){ + return this.items.length === 1 + } + clear(){ + this.items = [] + } + size(){ + return this.items.length + } + min (){ + let last = this.arr[this.arr.length - 1] + return this.items[last] + } +} +``` + +## 下周预告 +下周将练习**队列(Queue)** 的题目,开始翻起算法书籍学习咯。 + + + +|Author|王平安| +|---|---| +|E-mail|pingan8787@qq.com| +|博 客|www.pingan8787.com| +|微 信|pingan8787| +|每日文章推荐|https://github.com/pingan8787/Leo_Reading/issues| +|ES小册|js.pingan8787.com| + +## 微信公众号 +![bg](http://images.pingan8787.com/2019_07_12guild_page.png) \ No newline at end of file diff --git a/Cute-Algorithms/Cute-Algorithms-Tree.md b/Cute-Algorithms/Cute-Algorithms-Tree.md new file mode 100644 index 00000000..24a18190 --- /dev/null +++ b/Cute-Algorithms/Cute-Algorithms-Tree.md @@ -0,0 +1,387 @@ +![封面](https://user-gold-cdn.xitu.io/2019/4/27/16a5cd3f25400f0f?imageView2/1/w/1304/h/734/q/85/format/webp/interlace/1) + +这是第六周的练习题,最近加班比较多,上周主要完成一篇 [GraphQL入门教程](https://juejin.im/post/5cd56b1f6fb9a0321e16bde3) ,有兴趣的小伙伴可以看下哈。 + +下面是之前分享的链接: +* 1.[每周一练 之 数据结构与算法(Stack)](https://juejin.im/post/5cb2df0c5188251aca7340a0) +* 2.[每周一练 之 数据结构与算法(LinkedList)](https://juejin.im/post/5cbdbb1af265da036d79bb35) +* 3.[每周一练 之 数据结构与算法(Queue)](https://juejin.im/post/5cc3cbaaf265da03a85ac7f8) +* 4.[每周一练 之 数据结构与算法(Set)](https://juejin.im/post/5cceee526fb9a0323a01c72e) +* 5.[每周一练 之 数据结构与算法(Dictionary 和 HashTable)](https://juejin.im/post/5ce2a196f265da1b7638738b) + +> 欢迎关注我的 [个人主页](https://github.com/pingan8787) && [个人博客](http://www.pingan8787.com/) && [个人知识库](http://js.pingan8787.com/) && 微信公众号“前端自习课” + + +**本周练习内容:数据结构与算法 —— Tree** + +这些都是数据结构与算法,一部分方法是团队其他成员实现的,一部分我自己做的,有什么其他实现方法或错误,欢迎各位大佬指点,感谢。 + + +## 一、什么是树? +1.树有什么特点,什么是二叉树和二叉搜索树(BST: Binary Search Tree)? +2.生活中常见的例子有哪些? + +--- +解析: +1. 树有什么特点,什么是二叉树和二叉搜索树: + +* **树**是一种**非线性的数据结构**,以**分层方式存储数据**,用来表示**有层级关系的数据**。 + +* 每棵树至多只有一个**根结点**,**根结点**会有很多**子节点**,每个**子节点只有一个父结点**。 + +* **父结点**和**子节点**是相对的。 + +2. 生活中的例子: +如:家谱、公司组织架构图。 + +## 二、请实现二叉搜索树(BST),并实现以下方法: +* `insert(key)`:向树中插入一个新的键; +* `search(key)`:树中查找一个键,如果节点存在返回true,不存在返回false; +* `min()`:返回树中最小的值/键; +* `max()`:返回树中最大的值/键; +* `remove(key)`:移除某个键; + + +> 提示:所谓的键对应于之前章节所学的节点(Node) + +```js +class Node { + constructor(key){ + this.key = key + this.left = null + this.right = null + } +} +class BST { + constructor(){ + this.root = null + } + /** + * 插入一个节点 + * @param {*} node 插入的位置节点 + * @param {*} newNode 插入的节点 + */ + insertNode (node, newNode){ + if(newNode.key < node.key){ + if(node.left === null && node.right === null){ + node.left = newNode + }else if(node.left !== null && node.right === null){ + node.right = newNode + }else{ + this.insertNode(node.left, newNode) + } + }else{ + if(node.left === null && node.right === null){ + node.left = newNode + }else if(node.left !== null && node.right === null){ + node.right = newNode + }else{ + this.insertNode(node.right, newNode) + } + } + } + /** + * 插入操作 + * @param {*} key + */ + insert (key){ + let newNode = new Node(key) + if(this.root === null){ + this.root = newNode + }else{ + this.insertNode(this.root, newNode) + } + } + searchNode (node, key){ + if(node === null) return false + if(key < node.key){ + return this.searchNode(node.left, key) + }else if(key > node.key){ + return this.searchNode(node.right, key) + }else{ + return true + } + } + /** + * 搜索操作 + * @param {*} key + */ + search (key){ + return this.searchNode(this.root, key) + } + /** + * 最小值的节点 + */ + min (){ + let node = this.root + if(node === null) return null + while(node && node.left !== null){ + node = node.left + } + return node.key + } + /** + * 最大值的节点 + */ + max (){ + let node = this.root + if(node === null) return null + while(node && node.right !== null){ + node = node.right + } + return node.key + } + /** + * 找到最小节点 + * @param {*} node + */ + findMinNode (node){ + if(node === null) return null + while(node && node.left !== null){ + node = node.left + } + return node + } + /** + * 删除一个节点 + * @param {*} node + * @param {*} key + */ + removeNode (node, key){ + if(node === null) return null + if(key < node.key){ + node.left = this.removeNode(node.left, key) + return node + }else if(key > node.key){ + node.right = this.removeNode(node.right, key) + return node + }else{ + // 1.叶节点 + if(node.left === null && node.right === null){ + node = null + return node + } + // 2.只有一个子节点 + if(node.left === null){ + node = node.right + return node + }else if(node.right === null){ + node = node.left + } + // 3.有两个子节点 + let curNode = this.findMinNode(node.right) + node.key = curNode.key + node.right = this.removeNode(node.right, curNode.key) + return node + } + } + /** + * 删除一个节点 + * @param {*} key + */ + remove (key){ + if(this.root === null) return null + this.root = this.removeNode(this.root, key) + } +} +``` + + +## 三、基于题二实现二叉搜索树扩展以下方法: +* `preOrderTraverse()`: 通过先序遍历方式遍历所有节点; +* `inOrderTraverse()`: 通过中序遍历方式遍历所有节点; +* `postOrderTraverse()`: 通过后序遍历方式遍历所有节点; + + +提示: + +* 先序:先访问根节点,然后以同样方式访问左子树和右子树;(根==>左==>右) + +输出 =》 11 7 5 3 6 9 8 10 15 13 12 14 20 18 25 +![tree_pre](http://images.pingan8787.com/20190520tree_pre.png) + +* 中序:先访问左子树,再访问根节点,最后访问右字数;以升序访问所有节点;(左==>根==>右) + +输出 =》 3 5 6 7 8 9 10 11 12 13 14 15 18 20 25 + +![tree_in](http://images.pingan8787.com/20190520tree_in.png) + +* 后序:先访问叶子节点,从左子树到右子树,再到根节点。(左==>右==>根) + +输出 =》 3 6 5 8 10 9 7 12 14 13 18 25 20 15 11 + +![tree_post](http://images.pingan8787.com/20190520tree_post.png) + + +--- +解析: +```js +// 1. 先序 +BST.prototype.preOrderTraverseNode = function(node, callback){ + if(node !== null){ + callback(node.key) + this.preOrderTraverseNode(node.left, callback) + this.preOrderTraverseNode(node.right, callback) + } +} +BST.prototype.preOrderTraverse = function(callback){ + this.preOrderTraverseNode(this.root, callback) +} + +// 2. 中序 +BST.prototype.inOrderTraverseNode = function(node, callback){ + if(node !== null){ + this.inOrderTraverseNode(node.left, callback) + callback(node.key) + this.inOrderTraverseNode(node.right, callback) + } +} +BST.prototype.inOrderTraverse = function(callback){ + this.inOrderTraverseNode(this.root, callback) +} + +// 3. 后序 +BST.prototype.postOrderTraverseNode = function(node, callback){ + if(node !== null){ + this.postOrderTraverseNode(node.left, callback) + this.postOrderTraverseNode(node.right, callback) + callback(node.key) + } +} +BST.prototype.postOrderTraverse = function(callback){ + this.postOrderTraverseNode(this.root, callback) +} +``` + +## 四、请实现从上往下打印二叉树 +给定的二叉树为:[3, 9 , 20, null, null, 15, 7] +``` + 3 + / \ + 9 20 + / \ + 15 7 +``` + +请实现一个 `printLevelOrder` 方法,输出以下结果: +``` +[ + [3], + [9, 20], + [15, 7] +] +``` +--- +来源:[102.二叉树的层次遍历](https://leetcode-cn.com/problems/binary-tree-level-order-traversal/) +解析: +* 方法一: +```js +BST.prototype.printLevelOrder = function (root, arr = [], i = 0){ + if (root && (root.key || root.key === 0)) { + !arr[i] && (arr[i] = []) + arr[i].push(root.key) + i++ + root.left && this.printLevelOrder(root.left, arr, i) + root.right && this.printLevelOrder(root.right, arr, i) + } + return arr +} +``` + +* 方法二: +```js +BST.prototype.printLevelOrder = function (){ + if(this.root === null) return [] + let result = [], queue = [this.root] + while(true){ + let len = queue.length, arr = [] + while(len > 0){ + console.log(queue) + let node = queue.shift() + len -= 1 + arr.push(node.key) + if(node.left !== null) queue.push(node.left) + if(node.right !== null) queue.push(node.right) + } + if(arr.length === 0) return result + result.push([...arr]) + } +} +``` + +## 五、给定一个二叉树,判断其是否是一个有效的二叉搜索树。 +假设一个二叉搜索树具有如下特征: + +* 节点的左子树只包含**小于**当前节点的数。 +* 节点的右子树只包含**大于**当前节点的数。 +* 所有左子树和右子树自身必须也是二叉搜索树。 + +示例 1: +``` +输入: + 2 + / \ + 1 3 +输出: true +``` + +示例 2: +``` +输入: + 5 + / \ + 1 4 + / \ + 3 6 +输出: false +解释: 输入为: [5,1,4,null,null,3,6]。 +根节点的值为 5 ,但是其右子节点值为 4 。 +``` + +代码实现: +```js +/** + * 二叉树节点定义 + */ +function TreeNode(val) { + this.val = val; + this.left = this.right = null; +} + +/** +- @param {TreeNode} root +- @return {boolean} +*/ +function isValidBST(root) {}; +``` + +--- +来源:[99.验证二叉搜索树](https://leetcode-cn.com/problems/validate-binary-search-tree/) +解析: +```js +function isValidBST(root) { + let arr = [] + function inOrderTraverse(node){ + if(node === null) return; + node.left && inOrderTraverse(node.left); + arr.push(node.val); + node.right && inOrderTraverse(node.right); + } + inOrderTraverse(root) + for(let i = 0; i < arr.length - 1; i++){ + if(arr[i] >= arr[i+1]) return false + } + return true +}; +``` + +|Author|王平安| +|---|---| +|E-mail|pingan8787@qq.com| +|博 客|www.pingan8787.com| +|微 信|pingan8787| +|每日文章推荐|https://github.com/pingan8787/Leo_Reading/issues| +|ES小册|js.pingan8787.com| + +## 微信公众号 +![bg](http://images.pingan8787.com/2019_07_12guild_page.png) \ No newline at end of file diff --git a/Cute-Algorithms/README.md b/Cute-Algorithms/README.md new file mode 100644 index 00000000..d56e5fda --- /dev/null +++ b/Cute-Algorithms/README.md @@ -0,0 +1,25 @@ +## 💌仓库介绍 +**Cute-Algorithms 系列**主要分享我学习**数据结构与算法**的相关笔记,每种算法都会有 5 道题目来练习,喜欢的朋友欢迎 👉star。 + +### 关于作者 +[![博客](http://images.pingan8787.com/icon_my1.png)](http://www.pingan8787.com) +[![知乎](http://images.pingan8787.com/icon_zhihu1.png)](https://zhuanlan.zhihu.com/cute-javascript) +[![掘金](http://images.pingan8787.com/icon_juejin2.png)](https://juejin.im/user/586fc337a22b9d0058807d53/posts) +[![思否](http://images.pingan8787.com/icon_sf1.png)](https://segmentfault.com/blog/pingan8787) +[![CSDN](http://images.pingan8787.com/icon_csdn1.png)](https://blog.csdn.net/qq_36380426) +[![简书](http://images.pingan8787.com/icon_jianshu1.png)](https://www.jianshu.com/u/2ec5d94afd60) + + +## 💌文章目录 + +1. [《数据结构与算法 - Tree》](https://github.com/pingan8787/Leo-JavaScript/blob/master/Cute-Algorithms/Cute-Algorithms-Tree.md) + +2. [《数据结构与算法 - Stack》](https://github.com/pingan8787/Leo-JavaScript/blob/master/Cute-Algorithms/Cute-Algorithms-Stack.md) + +3. [《数据结构与算法 - Queue》](https://github.com/pingan8787/Leo-JavaScript/blob/master/Cute-Algorithms/Cute-Algorithms-Queue.md) + +4. [《数据结构与算法 - Set》](https://github.com/pingan8787/Leo-JavaScript/blob/master/Cute-Algorithms/Cute-Algorithms-Set.md) + +5. [《数据结构与算法 - LinkedList》](https://github.com/pingan8787/Leo-JavaScript/blob/master/Cute-Algorithms/Cute-Algorithms-Tree.md) + +6. [《数据结构与算法 - Dictionary&&HashTable》](https://github.com/pingan8787/Leo-JavaScript/blob/master/Cute-Algorithms/Cute-Algorithms-Dictionary%26%26HashTable.md) diff --git a/Cute-Angular/README.md b/Cute-Angular/README.md new file mode 100644 index 00000000..5524b158 --- /dev/null +++ b/Cute-Angular/README.md @@ -0,0 +1,17 @@ +## 💌仓库介绍 +**Cute-GraphQL 系列**主要分享我学习 `Angular` 的一些学习笔记和资料,后面也有实战的案例等,喜欢的朋友欢迎 👉star。 + +### 关于作者 +[![博客](http://images.pingan8787.com/icon_my1.png)](http://www.pingan8787.com) +[![知乎](http://images.pingan8787.com/icon_zhihu1.png)](https://zhuanlan.zhihu.com/cute-javascript) +[![掘金](http://images.pingan8787.com/icon_juejin2.png)](https://juejin.im/user/586fc337a22b9d0058807d53/posts) +[![思否](http://images.pingan8787.com/icon_sf1.png)](https://segmentfault.com/blog/pingan8787) +[![CSDN](http://images.pingan8787.com/icon_csdn1.png)](https://blog.csdn.net/qq_36380426) +[![简书](http://images.pingan8787.com/icon_jianshu1.png)](https://www.jianshu.com/u/2ec5d94afd60) + +## 💌文章目录 + +1. [《Angular 入门教程》](https://github.com/pingan8787/Leo-JavaScript/blob/master/Cute-Angular/books%E9%A1%B9%E7%9B%AEdemo/README.md) +2. [《Angular 官网 demo》](https://github.com/pingan8787/Leo-JavaScript/blob/master/Cute-Angular/angualr%E5%AE%98%E7%BD%91demo/README.md) +3. [《Angular 知识点整理》](https://github.com/pingan8787/Leo-JavaScript/blob/master/Cute-Angular/%E7%9F%A5%E8%AF%86%E7%82%B9%E6%95%B4%E7%90%86/README.md) + diff --git "a/Cute-Angular/angualr\345\256\230\347\275\221demo/.editorconfig" "b/Cute-Angular/angualr\345\256\230\347\275\221demo/.editorconfig" new file mode 100644 index 00000000..6e87a003 --- /dev/null +++ "b/Cute-Angular/angualr\345\256\230\347\275\221demo/.editorconfig" @@ -0,0 +1,13 @@ +# Editor configuration, see http://editorconfig.org +root = true + +[*] +charset = utf-8 +indent_style = space +indent_size = 2 +insert_final_newline = true +trim_trailing_whitespace = true + +[*.md] +max_line_length = off +trim_trailing_whitespace = false diff --git "a/Cute-Angular/angualr\345\256\230\347\275\221demo/.gitignore" "b/Cute-Angular/angualr\345\256\230\347\275\221demo/.gitignore" new file mode 100644 index 00000000..ee5c9d83 --- /dev/null +++ "b/Cute-Angular/angualr\345\256\230\347\275\221demo/.gitignore" @@ -0,0 +1,39 @@ +# See http://help.github.com/ignore-files/ for more about ignoring files. + +# compiled output +/dist +/tmp +/out-tsc + +# dependencies +/node_modules + +# IDEs and editors +/.idea +.project +.classpath +.c9/ +*.launch +.settings/ +*.sublime-workspace + +# IDE - VSCode +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json + +# misc +/.sass-cache +/connect.lock +/coverage +/libpeerconnection.log +npm-debug.log +yarn-error.log +testem.log +/typings + +# System Files +.DS_Store +Thumbs.db diff --git "a/Cute-Angular/angualr\345\256\230\347\275\221demo/README.md" "b/Cute-Angular/angualr\345\256\230\347\275\221demo/README.md" new file mode 100644 index 00000000..e41787b6 --- /dev/null +++ "b/Cute-Angular/angualr\345\256\230\347\275\221demo/README.md" @@ -0,0 +1,27 @@ +# AngularDemo + +This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 6.2.4. + +## Development server + +Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files. + +## Code scaffolding + +Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`. + +## Build + +Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `--prod` flag for a production build. + +## Running unit tests + +Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io). + +## Running end-to-end tests + +Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/). + +## Further help + +To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md). diff --git "a/Cute-Angular/angualr\345\256\230\347\275\221demo/angular.json" "b/Cute-Angular/angualr\345\256\230\347\275\221demo/angular.json" new file mode 100644 index 00000000..1bb00277 --- /dev/null +++ "b/Cute-Angular/angualr\345\256\230\347\275\221demo/angular.json" @@ -0,0 +1,127 @@ +{ + "$schema": "./node_modules/@angular/cli/lib/config/schema.json", + "version": 1, + "newProjectRoot": "projects", + "projects": { + "angular-demo": { + "root": "", + "sourceRoot": "src", + "projectType": "application", + "prefix": "app", + "schematics": {}, + "architect": { + "build": { + "builder": "@angular-devkit/build-angular:browser", + "options": { + "outputPath": "dist/angular-demo", + "index": "src/index.html", + "main": "src/main.ts", + "polyfills": "src/polyfills.ts", + "tsConfig": "src/tsconfig.app.json", + "assets": [ + "src/favicon.ico", + "src/assets" + ], + "styles": [ + "src/styles.css" + ], + "scripts": [] + }, + "configurations": { + "production": { + "fileReplacements": [ + { + "replace": "src/environments/environment.ts", + "with": "src/environments/environment.prod.ts" + } + ], + "optimization": true, + "outputHashing": "all", + "sourceMap": false, + "extractCss": true, + "namedChunks": false, + "aot": true, + "extractLicenses": true, + "vendorChunk": false, + "buildOptimizer": true + } + } + }, + "serve": { + "builder": "@angular-devkit/build-angular:dev-server", + "options": { + "browserTarget": "angular-demo:build" + }, + "configurations": { + "production": { + "browserTarget": "angular-demo:build:production" + } + } + }, + "extract-i18n": { + "builder": "@angular-devkit/build-angular:extract-i18n", + "options": { + "browserTarget": "angular-demo:build" + } + }, + "test": { + "builder": "@angular-devkit/build-angular:karma", + "options": { + "main": "src/test.ts", + "polyfills": "src/polyfills.ts", + "tsConfig": "src/tsconfig.spec.json", + "karmaConfig": "src/karma.conf.js", + "styles": [ + "src/styles.css" + ], + "scripts": [], + "assets": [ + "src/favicon.ico", + "src/assets" + ] + } + }, + "lint": { + "builder": "@angular-devkit/build-angular:tslint", + "options": { + "tsConfig": [ + "src/tsconfig.app.json", + "src/tsconfig.spec.json" + ], + "exclude": [ + "**/node_modules/**" + ] + } + } + } + }, + "angular-demo-e2e": { + "root": "e2e/", + "projectType": "application", + "architect": { + "e2e": { + "builder": "@angular-devkit/build-angular:protractor", + "options": { + "protractorConfig": "e2e/protractor.conf.js", + "devServerTarget": "angular-demo:serve" + }, + "configurations": { + "production": { + "devServerTarget": "angular-demo:serve:production" + } + } + }, + "lint": { + "builder": "@angular-devkit/build-angular:tslint", + "options": { + "tsConfig": "e2e/tsconfig.e2e.json", + "exclude": [ + "**/node_modules/**" + ] + } + } + } + } + }, + "defaultProject": "angular-demo" +} \ No newline at end of file diff --git "a/Cute-Angular/angualr\345\256\230\347\275\221demo/e2e/protractor.conf.js" "b/Cute-Angular/angualr\345\256\230\347\275\221demo/e2e/protractor.conf.js" new file mode 100644 index 00000000..86776a39 --- /dev/null +++ "b/Cute-Angular/angualr\345\256\230\347\275\221demo/e2e/protractor.conf.js" @@ -0,0 +1,28 @@ +// Protractor configuration file, see link for more information +// https://github.com/angular/protractor/blob/master/lib/config.ts + +const { SpecReporter } = require('jasmine-spec-reporter'); + +exports.config = { + allScriptsTimeout: 11000, + specs: [ + './src/**/*.e2e-spec.ts' + ], + capabilities: { + 'browserName': 'chrome' + }, + directConnect: true, + baseUrl: 'http://localhost:4200/', + framework: 'jasmine', + jasmineNodeOpts: { + showColors: true, + defaultTimeoutInterval: 30000, + print: function() {} + }, + onPrepare() { + require('ts-node').register({ + project: require('path').join(__dirname, './tsconfig.e2e.json') + }); + jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } })); + } +}; \ No newline at end of file diff --git "a/Cute-Angular/angualr\345\256\230\347\275\221demo/e2e/src/app.e2e-spec.ts" "b/Cute-Angular/angualr\345\256\230\347\275\221demo/e2e/src/app.e2e-spec.ts" new file mode 100644 index 00000000..820fb057 --- /dev/null +++ "b/Cute-Angular/angualr\345\256\230\347\275\221demo/e2e/src/app.e2e-spec.ts" @@ -0,0 +1,14 @@ +import { AppPage } from './app.po'; + +describe('workspace-project App', () => { + let page: AppPage; + + beforeEach(() => { + page = new AppPage(); + }); + + it('should display welcome message', () => { + page.navigateTo(); + expect(page.getParagraphText()).toEqual('Welcome to angular-demo!'); + }); +}); diff --git "a/Cute-Angular/angualr\345\256\230\347\275\221demo/e2e/src/app.po.ts" "b/Cute-Angular/angualr\345\256\230\347\275\221demo/e2e/src/app.po.ts" new file mode 100644 index 00000000..82ea75ba --- /dev/null +++ "b/Cute-Angular/angualr\345\256\230\347\275\221demo/e2e/src/app.po.ts" @@ -0,0 +1,11 @@ +import { browser, by, element } from 'protractor'; + +export class AppPage { + navigateTo() { + return browser.get('/'); + } + + getParagraphText() { + return element(by.css('app-root h1')).getText(); + } +} diff --git "a/Cute-Angular/angualr\345\256\230\347\275\221demo/e2e/tsconfig.e2e.json" "b/Cute-Angular/angualr\345\256\230\347\275\221demo/e2e/tsconfig.e2e.json" new file mode 100644 index 00000000..a6dd6220 --- /dev/null +++ "b/Cute-Angular/angualr\345\256\230\347\275\221demo/e2e/tsconfig.e2e.json" @@ -0,0 +1,13 @@ +{ + "extends": "../tsconfig.json", + "compilerOptions": { + "outDir": "../out-tsc/app", + "module": "commonjs", + "target": "es5", + "types": [ + "jasmine", + "jasminewd2", + "node" + ] + } +} \ No newline at end of file diff --git "a/Cute-Angular/angualr\345\256\230\347\275\221demo/package-lock.json" "b/Cute-Angular/angualr\345\256\230\347\275\221demo/package-lock.json" new file mode 100644 index 00000000..6a946795 --- /dev/null +++ "b/Cute-Angular/angualr\345\256\230\347\275\221demo/package-lock.json" @@ -0,0 +1,10968 @@ +{ + "name": "angular-demo", + "version": "0.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@angular-devkit/architect": { + "version": "0.8.9", + "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.8.9.tgz", + "integrity": "sha512-2tiGPkvJyFY/G3a27uC8r6Jj3H5m8SxjMqhjNUQ5AtNumweTBPt3YIYMNAvHUmxG0nA9upDolVXFmoQGK9AhKQ==", + "dev": true, + "requires": { + "@angular-devkit/core": "0.8.9", + "rxjs": "6.2.2" + } + }, + "@angular-devkit/build-angular": { + "version": "0.8.9", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-0.8.9.tgz", + "integrity": "sha512-J6o0MwIG1cJT29p87c7uUn7NY3QLEoQOVw4VXWM9cqG9bv99VK7f7eOSDhHJbXn7Snm4XYrye0zRa3RFXhMG+A==", + "dev": true, + "requires": { + "@angular-devkit/architect": "0.8.9", + "@angular-devkit/build-optimizer": "0.8.9", + "@angular-devkit/build-webpack": "0.8.9", + "@angular-devkit/core": "0.8.9", + "@ngtools/webpack": "6.2.9", + "ajv": "6.4.0", + "autoprefixer": "8.6.5", + "circular-dependency-plugin": "5.0.2", + "clean-css": "4.2.1", + "copy-webpack-plugin": "4.5.2", + "file-loader": "1.1.11", + "glob": "7.1.3", + "html-webpack-plugin": "3.2.0", + "istanbul": "0.4.5", + "istanbul-instrumenter-loader": "3.0.1", + "karma-source-map-support": "1.3.0", + "less": "3.8.1", + "less-loader": "4.1.0", + "license-webpack-plugin": "1.5.0", + "loader-utils": "1.1.0", + "mini-css-extract-plugin": "0.4.4", + "minimatch": "3.0.4", + "node-sass": "^4.9.3", + "opn": "5.4.0", + "parse5": "4.0.0", + "portfinder": "1.0.19", + "postcss": "6.0.23", + "postcss-import": "11.1.0", + "postcss-loader": "2.1.6", + "postcss-url": "7.3.2", + "raw-loader": "0.5.1", + "rxjs": "6.2.2", + "sass-loader": "7.1.0", + "semver": "5.6.0", + "source-map-loader": "0.2.4", + "source-map-support": "0.5.9", + "stats-webpack-plugin": "0.6.2", + "style-loader": "0.21.0", + "stylus": "0.54.5", + "stylus-loader": "3.0.2", + "tree-kill": "1.2.1", + "uglifyjs-webpack-plugin": "1.3.0", + "url-loader": "1.1.2", + "webpack": "4.16.4", + "webpack-dev-middleware": "3.4.0", + "webpack-dev-server": "3.1.14", + "webpack-merge": "4.1.4", + "webpack-sources": "1.3.0", + "webpack-subresource-integrity": "1.1.0-rc.4" + } + }, + "@angular-devkit/build-optimizer": { + "version": "0.8.9", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-optimizer/-/build-optimizer-0.8.9.tgz", + "integrity": "sha512-h8u5iAhSmt0TsLDZXZCmOkXZDMgP2itLkgZvOIsGInyMAESJuWK4P1qegMSv2R5ELOsinJiuhe218M4K2enEdA==", + "dev": true, + "requires": { + "loader-utils": "1.1.0", + "source-map": "0.5.7", + "typescript": "2.9.2", + "webpack-sources": "1.3.0" + } + }, + "@angular-devkit/build-webpack": { + "version": "0.8.9", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.8.9.tgz", + "integrity": "sha512-2csJ6utodPSLABTXfBLymYLrndJURF3xVqVjEDzUFl9zLqK1YOkKH4XPr12vfH8SfAtvzIutNLRxBtAuWJmDlw==", + "dev": true, + "requires": { + "@angular-devkit/architect": "0.8.9", + "@angular-devkit/core": "0.8.9", + "rxjs": "6.2.2" + } + }, + "@angular-devkit/core": { + "version": "0.8.9", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-0.8.9.tgz", + "integrity": "sha512-Umax3YKBPTQy360TeoSNaIIOJOKoXvN/S2WNTV8wDjSWWNiWLTIlckWMb9DVsafAifjUi0mtOLRFuM4YatKgTw==", + "dev": true, + "requires": { + "ajv": "6.4.0", + "chokidar": "2.0.4", + "rxjs": "6.2.2", + "source-map": "0.5.7" + } + }, + "@angular-devkit/schematics": { + "version": "0.8.9", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-0.8.9.tgz", + "integrity": "sha512-JZiK1aHJUFV6xDtUMBLoH3cLgi7EtR1bXjNqqa11MAjnHMqzm2GBazPvzGkMwVbCxC1sdYgswwGX9GS2tpHawA==", + "dev": true, + "requires": { + "@angular-devkit/core": "0.8.9", + "rxjs": "6.2.2" + } + }, + "@angular/animations": { + "version": "6.1.10", + "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-6.1.10.tgz", + "integrity": "sha512-dd/lq7kw3uwfHPICan8psu2nthuUpp7PvMLuNIm0XxObZ4oNs0ls6uxKEDPnEkRKoGdiJpvmsyzZZN9ACMPEAA==", + "requires": { + "tslib": "^1.9.0" + } + }, + "@angular/cli": { + "version": "6.2.9", + "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-6.2.9.tgz", + "integrity": "sha512-4xuTbmMKGx1bMi0KA3Xmtx/emy10wlSwTXoUijlhd2tcWmlI2wRjAYjR7efSbFo8dVskiq0CyAVFWr1IanYQZw==", + "dev": true, + "requires": { + "@angular-devkit/architect": "0.8.9", + "@angular-devkit/core": "0.8.9", + "@angular-devkit/schematics": "0.8.9", + "@schematics/angular": "0.8.9", + "@schematics/update": "0.8.9", + "@yarnpkg/lockfile": "1.1.0", + "ini": "1.3.5", + "json-schema-traverse": "0.4.1", + "npm-package-arg": "6.1.0", + "opn": "5.4.0", + "pacote": "9.2.3", + "rxjs": "6.2.2", + "semver": "5.6.0", + "symbol-observable": "1.2.0", + "yargs-parser": "10.1.0" + }, + "dependencies": { + "camelcase": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", + "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", + "dev": true + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "yargs-parser": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-10.1.0.tgz", + "integrity": "sha512-VCIyR1wJoEBZUqk5PA+oOBF6ypbwh5aNB3I50guxAL/quggdfs4TtNHQrSazFA3fYZ+tEqfs0zIGlv0c/rgjbQ==", + "dev": true, + "requires": { + "camelcase": "^4.1.0" + } + } + } + }, + "@angular/common": { + "version": "6.1.10", + "resolved": "https://registry.npmjs.org/@angular/common/-/common-6.1.10.tgz", + "integrity": "sha512-73xxTSYJNKfiJ7C1Ajg+sz5l8y+blb/vNgHYg7O3yem5zLBnfPpidJ1UGg4W4d2Y+jwUVJbZKh8SKJarqAJVUQ==", + "requires": { + "tslib": "^1.9.0" + } + }, + "@angular/compiler": { + "version": "6.1.10", + "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-6.1.10.tgz", + "integrity": "sha512-FPIb2j3zfoBwb6vo/u0gQeu70h8InGlSisBr3xMACs/35/pwB6kbQR+JQiUr0D7k6QApg7AuMkvq8aFNelg0aw==", + "requires": { + "tslib": "^1.9.0" + } + }, + "@angular/compiler-cli": { + "version": "6.1.10", + "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-6.1.10.tgz", + "integrity": "sha512-GCWdyeNQSnF4RfzO4A0+WHsNEgxKpl5arg4ldLSWMNkj/DrhMD4TnmxhR+IVY+7ieMkUBwpcuWRnjdOdnbmV+w==", + "dev": true, + "requires": { + "chokidar": "^1.4.2", + "minimist": "^1.2.0", + "reflect-metadata": "^0.1.2", + "tsickle": "^0.32.1" + }, + "dependencies": { + "anymatch": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-1.3.2.tgz", + "integrity": "sha512-0XNayC8lTHQ2OI8aljNCN3sSx6hsr/1+rlcDAotXJR7C1oZZHCNsfpbKwMjRA3Uqb5tF1Rae2oloTr4xpq+WjA==", + "dev": true, + "requires": { + "micromatch": "^2.1.5", + "normalize-path": "^2.0.0" + } + }, + "arr-diff": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz", + "integrity": "sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=", + "dev": true, + "requires": { + "arr-flatten": "^1.0.1" + } + }, + "array-unique": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz", + "integrity": "sha1-odl8yvy8JiXMcPrc6zalDFiwGlM=", + "dev": true + }, + "braces": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz", + "integrity": "sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=", + "dev": true, + "requires": { + "expand-range": "^1.8.1", + "preserve": "^0.2.0", + "repeat-element": "^1.1.2" + } + }, + "chokidar": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-1.7.0.tgz", + "integrity": "sha1-eY5ol3gVHIB2tLNg5e3SjNortGg=", + "dev": true, + "requires": { + "anymatch": "^1.3.0", + "async-each": "^1.0.0", + "fsevents": "^1.0.0", + "glob-parent": "^2.0.0", + "inherits": "^2.0.1", + "is-binary-path": "^1.0.0", + "is-glob": "^2.0.0", + "path-is-absolute": "^1.0.0", + "readdirp": "^2.0.0" + } + }, + "expand-brackets": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz", + "integrity": "sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s=", + "dev": true, + "requires": { + "is-posix-bracket": "^0.1.0" + } + }, + "extglob": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz", + "integrity": "sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE=", + "dev": true, + "requires": { + "is-extglob": "^1.0.0" + } + }, + "glob-parent": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz", + "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=", + "dev": true, + "requires": { + "is-glob": "^2.0.0" + } + }, + "is-extglob": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", + "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", + "dev": true + }, + "is-glob": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", + "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", + "dev": true, + "requires": { + "is-extglob": "^1.0.0" + } + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + }, + "micromatch": { + "version": "2.3.11", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz", + "integrity": "sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU=", + "dev": true, + "requires": { + "arr-diff": "^2.0.0", + "array-unique": "^0.2.1", + "braces": "^1.8.2", + "expand-brackets": "^0.1.4", + "extglob": "^0.3.1", + "filename-regex": "^2.0.0", + "is-extglob": "^1.0.0", + "is-glob": "^2.0.1", + "kind-of": "^3.0.2", + "normalize-path": "^2.0.1", + "object.omit": "^2.0.0", + "parse-glob": "^3.0.4", + "regex-cache": "^0.4.2" + } + }, + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "dev": true + } + } + }, + "@angular/core": { + "version": "6.1.10", + "resolved": "https://registry.npmjs.org/@angular/core/-/core-6.1.10.tgz", + "integrity": "sha512-61l3rIQTVdT45eOf6/fBJIeVmV10mcrxqS4N/1OWkuDT29YSJTZSxGcv8QjAyyutuhcqWWpO6gVRkN07rWmkPg==", + "requires": { + "tslib": "^1.9.0" + } + }, + "@angular/forms": { + "version": "6.1.10", + "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-6.1.10.tgz", + "integrity": "sha512-zAPx2kMV1/FbP5DrY472Sd/ze1m+GS6T5ullZCtP392r62p2RkwzDCXieR51YiRJjZj3M6c3AcRND7PWBdXT7A==", + "requires": { + "tslib": "^1.9.0" + } + }, + "@angular/http": { + "version": "6.1.10", + "resolved": "https://registry.npmjs.org/@angular/http/-/http-6.1.10.tgz", + "integrity": "sha512-LDsSqyexh8fj23y+G2oSGLWSZVhbxBBo2ehYHnRgH/jlp0pmZVLRaGgUMNSCVtZc1rxLzpEjZjtw+P+qlutAtw==", + "requires": { + "tslib": "^1.9.0" + } + }, + "@angular/language-service": { + "version": "6.1.10", + "resolved": "https://registry.npmjs.org/@angular/language-service/-/language-service-6.1.10.tgz", + "integrity": "sha512-nN29Ovomg21eL8acwOSUFAYwWFI1TuFwUgUu37ZssfVQrYdaV+BFx3yv3P0nKU90h3Hp+oIkWHd8U34UYrvBCg==", + "dev": true + }, + "@angular/platform-browser": { + "version": "6.1.10", + "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-6.1.10.tgz", + "integrity": "sha512-CB7pqMwtgb7KjdHDAJlsXcs0rrU+2xQVaoOaqEfJtUrKhtGMLaZh8Qoic5l92SoGattkOw7SYarAOsWlAsVfvw==", + "requires": { + "tslib": "^1.9.0" + } + }, + "@angular/platform-browser-dynamic": { + "version": "6.1.10", + "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-6.1.10.tgz", + "integrity": "sha512-DmBSUyFPoyKqkmBXyJ2CrP1oXDioeoBlPA8lmWUDUv2yBuoHIzIkdY/OkTZbdyu/QYa1hK2Jl9OlfoeoenKddg==", + "requires": { + "tslib": "^1.9.0" + } + }, + "@angular/router": { + "version": "6.1.10", + "resolved": "https://registry.npmjs.org/@angular/router/-/router-6.1.10.tgz", + "integrity": "sha512-tekI3dkdvd65oMoxjjgRA+16uDgPUBWHhYxids6pgO8vobZNtCo8VaVlcDyLUhdmtS5kONELx0iL5E2M0Y2Bag==", + "requires": { + "tslib": "^1.9.0" + } + }, + "@babel/code-frame": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.0.0.tgz", + "integrity": "sha512-OfC2uemaknXr87bdLUkWog7nYuliM9Ij5HUcajsVcMCpQrcLmtxRbVFTIqmcSkSeYRBFBRxs2FiUqFJDLdiebA==", + "dev": true, + "requires": { + "@babel/highlight": "^7.0.0" + } + }, + "@babel/generator": { + "version": "7.3.3", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.3.3.tgz", + "integrity": "sha512-aEADYwRRZjJyMnKN7llGIlircxTCofm3dtV5pmY6ob18MSIuipHpA2yZWkPlycwu5HJcx/pADS3zssd8eY7/6A==", + "dev": true, + "requires": { + "@babel/types": "^7.3.3", + "jsesc": "^2.5.1", + "lodash": "^4.17.11", + "source-map": "^0.5.0", + "trim-right": "^1.0.1" + }, + "dependencies": { + "jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true + } + } + }, + "@babel/helper-function-name": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.1.0.tgz", + "integrity": "sha512-A95XEoCpb3TO+KZzJ4S/5uW5fNe26DjBGqf1o9ucyLyCmi1dXq/B3c8iaWTfBk3VvetUxl16e8tIrd5teOCfGw==", + "dev": true, + "requires": { + "@babel/helper-get-function-arity": "^7.0.0", + "@babel/template": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "@babel/helper-get-function-arity": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.0.0.tgz", + "integrity": "sha512-r2DbJeg4svYvt3HOS74U4eWKsUAMRH01Z1ds1zx8KNTPtpTL5JAsdFv8BNyOpVqdFhHkkRDIg5B4AsxmkjAlmQ==", + "dev": true, + "requires": { + "@babel/types": "^7.0.0" + } + }, + "@babel/helper-split-export-declaration": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.0.0.tgz", + "integrity": "sha512-MXkOJqva62dfC0w85mEf/LucPPS/1+04nmmRMPEBUB++hiiThQ2zPtX/mEWQ3mtzCEjIJvPY8nuwxXtQeQwUag==", + "dev": true, + "requires": { + "@babel/types": "^7.0.0" + } + }, + "@babel/highlight": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.0.0.tgz", + "integrity": "sha512-UFMC4ZeFC48Tpvj7C8UgLvtkaUuovQX+5xNWrsIoMG8o2z+XFKjKaN9iVmS84dPwVN00W4wPmqvYoZF3EGAsfw==", + "dev": true, + "requires": { + "chalk": "^2.0.0", + "esutils": "^2.0.2", + "js-tokens": "^4.0.0" + }, + "dependencies": { + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + } + } + }, + "@babel/parser": { + "version": "7.3.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.3.3.tgz", + "integrity": "sha512-xsH1CJoln2r74hR+y7cg2B5JCPaTh+Hd+EbBRk9nWGSNspuo6krjhX0Om6RnRQuIvFq8wVXCLKH3kwKDYhanSg==", + "dev": true + }, + "@babel/template": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.2.2.tgz", + "integrity": "sha512-zRL0IMM02AUDwghf5LMSSDEz7sBCO2YnNmpg3uWTZj/v1rcG2BmQUvaGU8GhU8BvfMh1k2KIAYZ7Ji9KXPUg7g==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "@babel/parser": "^7.2.2", + "@babel/types": "^7.2.2" + } + }, + "@babel/traverse": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.2.3.tgz", + "integrity": "sha512-Z31oUD/fJvEWVR0lNZtfgvVt512ForCTNKYcJBGbPb1QZfve4WGH8Wsy7+Mev33/45fhP/hwQtvgusNdcCMgSw==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "@babel/generator": "^7.2.2", + "@babel/helper-function-name": "^7.1.0", + "@babel/helper-split-export-declaration": "^7.0.0", + "@babel/parser": "^7.2.3", + "@babel/types": "^7.2.2", + "debug": "^4.1.0", + "globals": "^11.1.0", + "lodash": "^4.17.10" + }, + "dependencies": { + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "globals": { + "version": "11.11.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.11.0.tgz", + "integrity": "sha512-WHq43gS+6ufNOEqlrDBxVEbb8ntfXrfAUU2ZOpCxrBdGKW3gyv8mCxAfIBD0DroPKGrJ2eSsXsLtY9MPntsyTw==", + "dev": true + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "dev": true + } + } + }, + "@babel/types": { + "version": "7.3.3", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.3.3.tgz", + "integrity": "sha512-2tACZ80Wg09UnPg5uGAOUvvInaqLk3l/IAhQzlxLQOIXacr6bMsra5SH6AWw/hIDRCSbCdHP2KzSOD+cT7TzMQ==", + "dev": true, + "requires": { + "esutils": "^2.0.2", + "lodash": "^4.17.11", + "to-fast-properties": "^2.0.0" + }, + "dependencies": { + "to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", + "dev": true + } + } + }, + "@ngtools/webpack": { + "version": "6.2.9", + "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-6.2.9.tgz", + "integrity": "sha512-wZ9ROI4FdA9gnx21ULtA8u6Gmtig3BbT6EfUh2uYisjyL4P9k2p22sfY/Txwu/InnF4LDHOs5xZutOnGvC87vw==", + "dev": true, + "requires": { + "@angular-devkit/core": "0.8.9", + "rxjs": "6.2.2", + "tree-kill": "1.2.1", + "webpack-sources": "1.3.0" + } + }, + "@schematics/angular": { + "version": "0.8.9", + "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-0.8.9.tgz", + "integrity": "sha512-QTCcvBr+HLk8oJuPvonc8myYVU4ko5qsIhTon0gyWQg8McQnjCf8dTpnLM/VSzrmR71wOQ9fqvl+qS+eujrtnA==", + "dev": true, + "requires": { + "@angular-devkit/core": "0.8.9", + "@angular-devkit/schematics": "0.8.9", + "typescript": ">=2.6.2 <2.10" + } + }, + "@schematics/update": { + "version": "0.8.9", + "resolved": "https://registry.npmjs.org/@schematics/update/-/update-0.8.9.tgz", + "integrity": "sha512-duefusf5YdKGcNfNlHM/eMpkuvR2o4rqLDX0wo59Pu1KKXoDEUy5lj0mOA+hXbzjHtrtOxpffxKU0+AzovEMPA==", + "dev": true, + "requires": { + "@angular-devkit/core": "0.8.9", + "@angular-devkit/schematics": "0.8.9", + "npm-registry-client": "8.6.0", + "rxjs": "6.2.2", + "semver": "5.6.0", + "semver-intersect": "1.4.0" + } + }, + "@types/jasmine": { + "version": "2.8.16", + "resolved": "https://registry.npmjs.org/@types/jasmine/-/jasmine-2.8.16.tgz", + "integrity": "sha512-056oRlBBp7MDzr+HoU5su099s/s7wjZ3KcHxLfv+Byqb9MwdLUvsfLgw1VS97hsh3ddxSPyQu+olHMnoVTUY6g==", + "dev": true + }, + "@types/jasminewd2": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/jasminewd2/-/jasminewd2-2.0.6.tgz", + "integrity": "sha512-2ZOKrxb8bKRmP/po5ObYnRDgFE4i+lQiEB27bAMmtMWLgJSqlIDqlLx6S0IRorpOmOPRQ6O80NujTmQAtBkeNw==", + "dev": true, + "requires": { + "@types/jasmine": "*" + } + }, + "@types/node": { + "version": "8.9.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-8.9.5.tgz", + "integrity": "sha512-jRHfWsvyMtXdbhnz5CVHxaBgnV6duZnPlQuRSo/dm/GnmikNcmZhxIES4E9OZjUmQ8C+HCl4KJux+cXN/ErGDQ==", + "dev": true + }, + "@types/q": { + "version": "0.0.32", + "resolved": "https://registry.npmjs.org/@types/q/-/q-0.0.32.tgz", + "integrity": "sha1-vShOV8hPEyXacCur/IKlMoGQwMU=", + "dev": true + }, + "@types/selenium-webdriver": { + "version": "3.0.15", + "resolved": "https://registry.npmjs.org/@types/selenium-webdriver/-/selenium-webdriver-3.0.15.tgz", + "integrity": "sha512-5nh8/K2u9p4bk95GGCJB7KBvewaB0TUziZ9DTr+mR2I6RoO4OJVqx7rxK83hs2J1tomwtCGkhiW+Dy8EUnfB+Q==", + "dev": true + }, + "@webassemblyjs/ast": { + "version": "1.5.13", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.5.13.tgz", + "integrity": "sha512-49nwvW/Hx9i+OYHg+mRhKZfAlqThr11Dqz8TsrvqGKMhdI2ijy3KBJOun2Z4770TPjrIJhR6KxChQIDaz8clDA==", + "dev": true, + "requires": { + "@webassemblyjs/helper-module-context": "1.5.13", + "@webassemblyjs/helper-wasm-bytecode": "1.5.13", + "@webassemblyjs/wast-parser": "1.5.13", + "debug": "^3.1.0", + "mamacro": "^0.0.3" + }, + "dependencies": { + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "dev": true + } + } + }, + "@webassemblyjs/floating-point-hex-parser": { + "version": "1.5.13", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.5.13.tgz", + "integrity": "sha512-vrvvB18Kh4uyghSKb0NTv+2WZx871WL2NzwMj61jcq2bXkyhRC+8Q0oD7JGVf0+5i/fKQYQSBCNMMsDMRVAMqA==", + "dev": true + }, + "@webassemblyjs/helper-api-error": { + "version": "1.5.13", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.5.13.tgz", + "integrity": "sha512-dBh2CWYqjaDlvMmRP/kudxpdh30uXjIbpkLj9HQe+qtYlwvYjPRjdQXrq1cTAAOUSMTtzqbXIxEdEZmyKfcwsg==", + "dev": true + }, + "@webassemblyjs/helper-buffer": { + "version": "1.5.13", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.5.13.tgz", + "integrity": "sha512-v7igWf1mHcpJNbn4m7e77XOAWXCDT76Xe7Is1VQFXc4K5jRcFrl9D0NrqM4XifQ0bXiuTSkTKMYqDxu5MhNljA==", + "dev": true, + "requires": { + "debug": "^3.1.0" + }, + "dependencies": { + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "dev": true + } + } + }, + "@webassemblyjs/helper-code-frame": { + "version": "1.5.13", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.5.13.tgz", + "integrity": "sha512-yN6ScQQDFCiAXnVctdVO/J5NQRbwyTbQzsGzEgXsAnrxhjp0xihh+nNHQTMrq5UhOqTb5LykpJAvEv9AT0jnAQ==", + "dev": true, + "requires": { + "@webassemblyjs/wast-printer": "1.5.13" + } + }, + "@webassemblyjs/helper-fsm": { + "version": "1.5.13", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-fsm/-/helper-fsm-1.5.13.tgz", + "integrity": "sha512-hSIKzbXjVMRvy3Jzhgu+vDd/aswJ+UMEnLRCkZDdknZO3Z9e6rp1DAs0tdLItjCFqkz9+0BeOPK/mk3eYvVzZg==", + "dev": true + }, + "@webassemblyjs/helper-module-context": { + "version": "1.5.13", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-module-context/-/helper-module-context-1.5.13.tgz", + "integrity": "sha512-zxJXULGPLB7r+k+wIlvGlXpT4CYppRz8fLUM/xobGHc9Z3T6qlmJD9ySJ2jknuktuuiR9AjnNpKYDECyaiX+QQ==", + "dev": true, + "requires": { + "debug": "^3.1.0", + "mamacro": "^0.0.3" + }, + "dependencies": { + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "dev": true + } + } + }, + "@webassemblyjs/helper-wasm-bytecode": { + "version": "1.5.13", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.5.13.tgz", + "integrity": "sha512-0n3SoNGLvbJIZPhtMFq0XmmnA/YmQBXaZKQZcW8maGKwLpVcgjNrxpFZHEOLKjXJYVN5Il8vSfG7nRX50Zn+aw==", + "dev": true + }, + "@webassemblyjs/helper-wasm-section": { + "version": "1.5.13", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.5.13.tgz", + "integrity": "sha512-IJ/goicOZ5TT1axZFSnlAtz4m8KEjYr12BNOANAwGFPKXM4byEDaMNXYowHMG0yKV9a397eU/NlibFaLwr1fbw==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.5.13", + "@webassemblyjs/helper-buffer": "1.5.13", + "@webassemblyjs/helper-wasm-bytecode": "1.5.13", + "@webassemblyjs/wasm-gen": "1.5.13", + "debug": "^3.1.0" + }, + "dependencies": { + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "dev": true + } + } + }, + "@webassemblyjs/ieee754": { + "version": "1.5.13", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.5.13.tgz", + "integrity": "sha512-TseswvXEPpG5TCBKoLx9tT7+/GMACjC1ruo09j46ULRZWYm8XHpDWaosOjTnI7kr4SRJFzA6MWoUkAB+YCGKKg==", + "dev": true, + "requires": { + "ieee754": "^1.1.11" + } + }, + "@webassemblyjs/leb128": { + "version": "1.5.13", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.5.13.tgz", + "integrity": "sha512-0NRMxrL+GG3eISGZBmLBLAVjphbN8Si15s7jzThaw1UE9e5BY1oH49/+MA1xBzxpf1OW5sf9OrPDOclk9wj2yg==", + "dev": true, + "requires": { + "long": "4.0.0" + }, + "dependencies": { + "long": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", + "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==", + "dev": true + } + } + }, + "@webassemblyjs/utf8": { + "version": "1.5.13", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.5.13.tgz", + "integrity": "sha512-Ve1ilU2N48Ew0lVGB8FqY7V7hXjaC4+PeZM+vDYxEd+R2iQ0q+Wb3Rw8v0Ri0+rxhoz6gVGsnQNb4FjRiEH/Ng==", + "dev": true + }, + "@webassemblyjs/wasm-edit": { + "version": "1.5.13", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.5.13.tgz", + "integrity": "sha512-X7ZNW4+Hga4f2NmqENnHke2V/mGYK/xnybJSIXImt1ulxbCOEs/A+ZK/Km2jgihjyVxp/0z0hwIcxC6PrkWtgw==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.5.13", + "@webassemblyjs/helper-buffer": "1.5.13", + "@webassemblyjs/helper-wasm-bytecode": "1.5.13", + "@webassemblyjs/helper-wasm-section": "1.5.13", + "@webassemblyjs/wasm-gen": "1.5.13", + "@webassemblyjs/wasm-opt": "1.5.13", + "@webassemblyjs/wasm-parser": "1.5.13", + "@webassemblyjs/wast-printer": "1.5.13", + "debug": "^3.1.0" + }, + "dependencies": { + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "dev": true + } + } + }, + "@webassemblyjs/wasm-gen": { + "version": "1.5.13", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.5.13.tgz", + "integrity": "sha512-yfv94Se8R73zmr8GAYzezFHc3lDwE/lBXQddSiIZEKZFuqy7yWtm3KMwA1uGbv5G1WphimJxboXHR80IgX1hQA==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.5.13", + "@webassemblyjs/helper-wasm-bytecode": "1.5.13", + "@webassemblyjs/ieee754": "1.5.13", + "@webassemblyjs/leb128": "1.5.13", + "@webassemblyjs/utf8": "1.5.13" + } + }, + "@webassemblyjs/wasm-opt": { + "version": "1.5.13", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.5.13.tgz", + "integrity": "sha512-IkXSkgzVhQ0QYAdIayuCWMmXSYx0dHGU8Ah/AxJf1gBvstMWVnzJnBwLsXLyD87VSBIcsqkmZ28dVb0mOC3oBg==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.5.13", + "@webassemblyjs/helper-buffer": "1.5.13", + "@webassemblyjs/wasm-gen": "1.5.13", + "@webassemblyjs/wasm-parser": "1.5.13", + "debug": "^3.1.0" + }, + "dependencies": { + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "dev": true + } + } + }, + "@webassemblyjs/wasm-parser": { + "version": "1.5.13", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.5.13.tgz", + "integrity": "sha512-XnYoIcu2iqq8/LrtmdnN3T+bRjqYFjRHqWbqK3osD/0r/Fcv4d9ecRzjVtC29ENEuNTK4mQ9yyxCBCbK8S/cpg==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.5.13", + "@webassemblyjs/helper-api-error": "1.5.13", + "@webassemblyjs/helper-wasm-bytecode": "1.5.13", + "@webassemblyjs/ieee754": "1.5.13", + "@webassemblyjs/leb128": "1.5.13", + "@webassemblyjs/utf8": "1.5.13" + } + }, + "@webassemblyjs/wast-parser": { + "version": "1.5.13", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-parser/-/wast-parser-1.5.13.tgz", + "integrity": "sha512-Lbz65T0LQ1LgzKiUytl34CwuhMNhaCLgrh0JW4rJBN6INnBB8NMwUfQM+FxTnLY9qJ+lHJL/gCM5xYhB9oWi4A==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.5.13", + "@webassemblyjs/floating-point-hex-parser": "1.5.13", + "@webassemblyjs/helper-api-error": "1.5.13", + "@webassemblyjs/helper-code-frame": "1.5.13", + "@webassemblyjs/helper-fsm": "1.5.13", + "long": "^3.2.0", + "mamacro": "^0.0.3" + } + }, + "@webassemblyjs/wast-printer": { + "version": "1.5.13", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.5.13.tgz", + "integrity": "sha512-QcwogrdqcBh8Z+eUF8SG+ag5iwQSXxQJELBEHmLkk790wgQgnIMmntT2sMAMw53GiFNckArf5X0bsCA44j3lWQ==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.5.13", + "@webassemblyjs/wast-parser": "1.5.13", + "long": "^3.2.0" + } + }, + "@yarnpkg/lockfile": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz", + "integrity": "sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==", + "dev": true + }, + "JSONStream": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz", + "integrity": "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==", + "dev": true, + "requires": { + "jsonparse": "^1.2.0", + "through": ">=2.2.7 <3" + } + }, + "abbrev": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.0.9.tgz", + "integrity": "sha1-kbR5JYinc4wl813W9jdSovh3YTU=", + "dev": true + }, + "accepts": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.5.tgz", + "integrity": "sha1-63d99gEXI6OxTopywIBcjoZ0a9I=", + "dev": true, + "requires": { + "mime-types": "~2.1.18", + "negotiator": "0.6.1" + } + }, + "acorn": { + "version": "5.7.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.3.tgz", + "integrity": "sha512-T/zvzYRfbVojPWahDsE5evJdHb3oJoQfFbsrKM7w5Zcs++Tr257tia3BmMP8XYVjp1S9RZXQMh7gao96BlqZOw==", + "dev": true + }, + "acorn-dynamic-import": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/acorn-dynamic-import/-/acorn-dynamic-import-3.0.0.tgz", + "integrity": "sha512-zVWV8Z8lislJoOKKqdNMOB+s6+XV5WERty8MnKBeFgwA+19XJjJHs2RP5dzM57FftIs+jQnRToLiWazKr6sSWg==", + "dev": true, + "requires": { + "acorn": "^5.0.0" + } + }, + "adm-zip": { + "version": "0.4.13", + "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.4.13.tgz", + "integrity": "sha512-fERNJX8sOXfel6qCBCMPvZLzENBEhZTzKqg6vrOW5pvoEaQuJhRU4ndTAh6lHOxn1I6jnz2NHra56ZODM751uw==", + "dev": true + }, + "after": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/after/-/after-0.8.2.tgz", + "integrity": "sha1-/ts5T58OAqqXaOcCvaI7UF+ufh8=", + "dev": true + }, + "agent-base": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.2.1.tgz", + "integrity": "sha512-JVwXMr9nHYTUXsBFKUqhJwvlcYU/blreOEUkhNR2eXZIvwd+c+o5V4MgDPKWnMS/56awN3TRzIP+KoPn+roQtg==", + "dev": true, + "requires": { + "es6-promisify": "^5.0.0" + } + }, + "agentkeepalive": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-3.5.2.tgz", + "integrity": "sha512-e0L/HNe6qkQ7H19kTlRRqUibEAwDK5AFk6y3PtMsuut2VAH6+Q4xZml1tNDJD7kSAyqmbG/K08K5WEJYtUrSlQ==", + "dev": true, + "requires": { + "humanize-ms": "^1.2.1" + } + }, + "ajv": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.4.0.tgz", + "integrity": "sha1-06/3jpJ3VJdx2vAWTP9ISCt1T8Y=", + "dev": true, + "requires": { + "fast-deep-equal": "^1.0.0", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.3.0", + "uri-js": "^3.0.2" + } + }, + "ajv-errors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ajv-errors/-/ajv-errors-1.0.1.tgz", + "integrity": "sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ==", + "dev": true + }, + "ajv-keywords": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.4.0.tgz", + "integrity": "sha512-aUjdRFISbuFOl0EIZc+9e4FfZp0bDZgAdOOf30bJmw8VM9v84SHyVyxDfbWxpGYbdZD/9XoKxfHVNmxPkhwyGw==", + "dev": true + }, + "amdefine": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", + "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=", + "dev": true + }, + "angular-in-memory-web-api": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/angular-in-memory-web-api/-/angular-in-memory-web-api-0.8.0.tgz", + "integrity": "sha512-2n0YtCLFxZo4JePHvH6q8b7JmBmhZq44Ic8VaBPRSXE4vAmlKXHU+kI2quNa612EAETDRkZcvLOU8K8CkhIZgQ==" + }, + "ansi-colors": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.3.tgz", + "integrity": "sha512-LEHHyuhlPY3TmuUYMh2oz89lTShfvgbmzaBcxve9t/9Wuy7Dwf4yoAKcND7KFT1HAQfqZ12qtc+DUrBMeKF9nw==", + "dev": true + }, + "ansi-html": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/ansi-html/-/ansi-html-0.0.7.tgz", + "integrity": "sha1-gTWEAhliqenm/QOflA0S9WynhZ4=", + "dev": true + }, + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "anymatch": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", + "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", + "dev": true, + "requires": { + "micromatch": "^3.1.4", + "normalize-path": "^2.1.1" + } + }, + "app-root-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/app-root-path/-/app-root-path-2.1.0.tgz", + "integrity": "sha1-mL9lmTJ+zqGZMJhm6BQDaP0uZGo=", + "dev": true + }, + "append-transform": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-1.0.0.tgz", + "integrity": "sha512-P009oYkeHyU742iSZJzZZywj4QRJdnTWffaKuJQLablCZ1uz6/cW4yaRgcDaoQ+uwOxxnt0gRUcwfsNP2ri0gw==", + "dev": true, + "requires": { + "default-require-extensions": "^2.0.0" + } + }, + "aproba": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", + "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", + "dev": true + }, + "are-we-there-yet": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz", + "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", + "dev": true, + "requires": { + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" + } + }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "arr-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", + "dev": true + }, + "arr-flatten": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", + "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", + "dev": true + }, + "arr-union": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", + "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", + "dev": true + }, + "array-find-index": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", + "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=", + "dev": true, + "optional": true + }, + "array-flatten": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz", + "integrity": "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==", + "dev": true + }, + "array-slice": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-0.2.3.tgz", + "integrity": "sha1-3Tz7gO15c6dRF82sabC5nshhhvU=", + "dev": true + }, + "array-union": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", + "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", + "dev": true, + "requires": { + "array-uniq": "^1.0.1" + } + }, + "array-uniq": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", + "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=", + "dev": true + }, + "array-unique": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", + "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", + "dev": true + }, + "arraybuffer.slice": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/arraybuffer.slice/-/arraybuffer.slice-0.0.7.tgz", + "integrity": "sha512-wGUIVQXuehL5TCqQun8OW81jGzAWycqzFF8lFp+GOM5BXLYj3bKNsYC4daB7n6XjCqxQA/qgTJ+8ANR3acjrog==", + "dev": true + }, + "arrify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", + "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", + "dev": true + }, + "asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=", + "dev": true, + "optional": true + }, + "asn1": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", + "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", + "dev": true, + "requires": { + "safer-buffer": "~2.1.0" + } + }, + "asn1.js": { + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.10.1.tgz", + "integrity": "sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==", + "dev": true, + "requires": { + "bn.js": "^4.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, + "assert": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/assert/-/assert-1.4.1.tgz", + "integrity": "sha1-mZEtWRg2tab1s0XA8H7vwI/GXZE=", + "dev": true, + "requires": { + "util": "0.10.3" + }, + "dependencies": { + "inherits": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", + "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=", + "dev": true + }, + "util": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", + "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", + "dev": true, + "requires": { + "inherits": "2.0.1" + } + } + } + }, + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "dev": true + }, + "assign-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", + "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", + "dev": true + }, + "async": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", + "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=", + "dev": true + }, + "async-each": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.1.tgz", + "integrity": "sha1-GdOGodntxufByF04iu28xW0zYC0=", + "dev": true + }, + "async-foreach": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/async-foreach/-/async-foreach-0.1.3.tgz", + "integrity": "sha1-NhIfhFwFeBct5Bmpfb6x0W7DRUI=", + "dev": true, + "optional": true + }, + "async-limiter": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz", + "integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg==", + "dev": true + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", + "dev": true + }, + "atob": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", + "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", + "dev": true + }, + "autoprefixer": { + "version": "8.6.5", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-8.6.5.tgz", + "integrity": "sha512-PLWJN3Xo/rycNkx+mp8iBDMTm3FeWe4VmYaZDSqL5QQB9sLsQkG5k8n+LNDFnhh9kdq2K+egL/icpctOmDHwig==", + "dev": true, + "requires": { + "browserslist": "^3.2.8", + "caniuse-lite": "^1.0.30000864", + "normalize-range": "^0.1.2", + "num2fraction": "^1.2.2", + "postcss": "^6.0.23", + "postcss-value-parser": "^3.2.3" + } + }, + "aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", + "dev": true + }, + "aws4": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz", + "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==", + "dev": true + }, + "babel-code-frame": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", + "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=", + "dev": true, + "requires": { + "chalk": "^1.1.3", + "esutils": "^2.0.2", + "js-tokens": "^3.0.2" + }, + "dependencies": { + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + } + } + }, + "babel-generator": { + "version": "6.26.1", + "resolved": "https://registry.npmjs.org/babel-generator/-/babel-generator-6.26.1.tgz", + "integrity": "sha512-HyfwY6ApZj7BYTcJURpM5tznulaBvyio7/0d4zFOeMPUmfxkCjHocCuoLa2SAGzBI8AREcH3eP3758F672DppA==", + "dev": true, + "requires": { + "babel-messages": "^6.23.0", + "babel-runtime": "^6.26.0", + "babel-types": "^6.26.0", + "detect-indent": "^4.0.0", + "jsesc": "^1.3.0", + "lodash": "^4.17.4", + "source-map": "^0.5.7", + "trim-right": "^1.0.1" + } + }, + "babel-messages": { + "version": "6.23.0", + "resolved": "https://registry.npmjs.org/babel-messages/-/babel-messages-6.23.0.tgz", + "integrity": "sha1-8830cDhYA1sqKVHG7F7fbGLyYw4=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-runtime": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", + "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", + "dev": true, + "requires": { + "core-js": "^2.4.0", + "regenerator-runtime": "^0.11.0" + } + }, + "babel-template": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-template/-/babel-template-6.26.0.tgz", + "integrity": "sha1-3gPi0WOWsGn0bdn/+FIfsaDjXgI=", + "dev": true, + "requires": { + "babel-runtime": "^6.26.0", + "babel-traverse": "^6.26.0", + "babel-types": "^6.26.0", + "babylon": "^6.18.0", + "lodash": "^4.17.4" + } + }, + "babel-traverse": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.26.0.tgz", + "integrity": "sha1-RqnL1+3MYsjlwGTi0tjQ9ANXZu4=", + "dev": true, + "requires": { + "babel-code-frame": "^6.26.0", + "babel-messages": "^6.23.0", + "babel-runtime": "^6.26.0", + "babel-types": "^6.26.0", + "babylon": "^6.18.0", + "debug": "^2.6.8", + "globals": "^9.18.0", + "invariant": "^2.2.2", + "lodash": "^4.17.4" + } + }, + "babel-types": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-types/-/babel-types-6.26.0.tgz", + "integrity": "sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc=", + "dev": true, + "requires": { + "babel-runtime": "^6.26.0", + "esutils": "^2.0.2", + "lodash": "^4.17.4", + "to-fast-properties": "^1.0.3" + } + }, + "babylon": { + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz", + "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==", + "dev": true + }, + "backo2": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz", + "integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc=", + "dev": true + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true + }, + "base": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", + "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", + "dev": true, + "requires": { + "cache-base": "^1.0.1", + "class-utils": "^0.3.5", + "component-emitter": "^1.2.1", + "define-property": "^1.0.0", + "isobject": "^3.0.1", + "mixin-deep": "^1.2.0", + "pascalcase": "^0.1.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "base64-arraybuffer": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz", + "integrity": "sha1-c5JncZI7Whl0etZmqlzUv5xunOg=", + "dev": true + }, + "base64-js": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.0.tgz", + "integrity": "sha512-ccav/yGvoa80BQDljCxsmmQ3Xvx60/UpBIij5QN21W3wBi/hhIC9OoO+KLpu9IJTS9j4DRVJ3aDDF9cMSoa2lw==", + "dev": true + }, + "base64id": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/base64id/-/base64id-1.0.0.tgz", + "integrity": "sha1-R2iMuZu2gE8OBtPnY7HDLlfY5rY=", + "dev": true + }, + "batch": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", + "integrity": "sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY=", + "dev": true + }, + "bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", + "dev": true, + "requires": { + "tweetnacl": "^0.14.3" + } + }, + "better-assert": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/better-assert/-/better-assert-1.0.2.tgz", + "integrity": "sha1-QIZrnhueC1W0gYlDEeaPr/rrxSI=", + "dev": true, + "requires": { + "callsite": "1.0.0" + } + }, + "big.js": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-3.2.0.tgz", + "integrity": "sha512-+hN/Zh2D08Mx65pZ/4g5bsmNiZUuChDiQfTUQ7qJr4/kuopCr88xZsAXv6mBoZEsUI4OuGHlX59qE94K2mMW8Q==", + "dev": true + }, + "binary-extensions": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.0.tgz", + "integrity": "sha512-EgmjVLMn22z7eGGv3kcnHwSnJXmFHjISTY9E/S5lIcTD3Oxw05QTcBLNkJFzcb3cNueUdF/IN4U+d78V0zO8Hw==", + "dev": true + }, + "blob": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/blob/-/blob-0.0.5.tgz", + "integrity": "sha512-gaqbzQPqOoamawKg0LGVd7SzLgXS+JH61oWprSLH+P+abTczqJbhTR8CmJ2u9/bUYNmHTGJx/UEmn6doAvvuig==", + "dev": true + }, + "block-stream": { + "version": "0.0.9", + "resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz", + "integrity": "sha1-E+v+d4oDIFz+A3UUgeu0szAMEmo=", + "dev": true, + "optional": true, + "requires": { + "inherits": "~2.0.0" + } + }, + "blocking-proxy": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/blocking-proxy/-/blocking-proxy-1.0.1.tgz", + "integrity": "sha512-KE8NFMZr3mN2E0HcvCgRtX7DjhiIQrwle+nSVJVC/yqFb9+xznHl2ZcoBp2L9qzkI4t4cBFJ1efXF8Dwi132RA==", + "dev": true, + "requires": { + "minimist": "^1.2.0" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "dev": true + } + } + }, + "bluebird": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.3.tgz", + "integrity": "sha512-/qKPUQlaW1OyR51WeCPBvRnAlnZFUJkCSG5HzGnuIqhgyJtF+T94lFnn33eiazjRm2LAHVy2guNnaq48X9SJuw==", + "dev": true + }, + "bn.js": { + "version": "4.11.8", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", + "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==", + "dev": true + }, + "body-parser": { + "version": "1.18.3", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.3.tgz", + "integrity": "sha1-WykhmP/dVTs6DyDe0FkrlWlVyLQ=", + "dev": true, + "requires": { + "bytes": "3.0.0", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "~1.1.2", + "http-errors": "~1.6.3", + "iconv-lite": "0.4.23", + "on-finished": "~2.3.0", + "qs": "6.5.2", + "raw-body": "2.3.3", + "type-is": "~1.6.16" + } + }, + "bonjour": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/bonjour/-/bonjour-3.5.0.tgz", + "integrity": "sha1-jokKGD2O6aI5OzhExpGkK897yfU=", + "dev": true, + "requires": { + "array-flatten": "^2.1.0", + "deep-equal": "^1.0.1", + "dns-equal": "^1.0.0", + "dns-txt": "^2.0.2", + "multicast-dns": "^6.0.1", + "multicast-dns-service-types": "^1.1.0" + } + }, + "boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=", + "dev": true + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "dev": true, + "requires": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "brorand": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=", + "dev": true + }, + "browserify-aes": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", + "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", + "dev": true, + "requires": { + "buffer-xor": "^1.0.3", + "cipher-base": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.3", + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "browserify-cipher": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz", + "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==", + "dev": true, + "requires": { + "browserify-aes": "^1.0.4", + "browserify-des": "^1.0.0", + "evp_bytestokey": "^1.0.0" + } + }, + "browserify-des": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz", + "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==", + "dev": true, + "requires": { + "cipher-base": "^1.0.1", + "des.js": "^1.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "browserify-rsa": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz", + "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=", + "dev": true, + "requires": { + "bn.js": "^4.1.0", + "randombytes": "^2.0.1" + } + }, + "browserify-sign": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.0.4.tgz", + "integrity": "sha1-qk62jl17ZYuqa/alfmMMvXqT0pg=", + "dev": true, + "requires": { + "bn.js": "^4.1.1", + "browserify-rsa": "^4.0.0", + "create-hash": "^1.1.0", + "create-hmac": "^1.1.2", + "elliptic": "^6.0.0", + "inherits": "^2.0.1", + "parse-asn1": "^5.0.0" + } + }, + "browserify-zlib": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz", + "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==", + "dev": true, + "requires": { + "pako": "~1.0.5" + } + }, + "browserslist": { + "version": "3.2.8", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-3.2.8.tgz", + "integrity": "sha512-WHVocJYavUwVgVViC0ORikPHQquXwVh939TaelZ4WDqpWgTX/FsGhl/+P4qBUAGcRvtOgDgC+xftNWWp2RUTAQ==", + "dev": true, + "requires": { + "caniuse-lite": "^1.0.30000844", + "electron-to-chromium": "^1.3.47" + } + }, + "browserstack": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/browserstack/-/browserstack-1.5.2.tgz", + "integrity": "sha512-+6AFt9HzhKykcPF79W6yjEUJcdvZOV0lIXdkORXMJftGrDl0OKWqRF4GHqpDNkxiceDT/uB7Fb/aDwktvXX7dg==", + "dev": true, + "requires": { + "https-proxy-agent": "^2.2.1" + } + }, + "buffer": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", + "integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=", + "dev": true, + "requires": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4", + "isarray": "^1.0.0" + } + }, + "buffer-alloc": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.2.0.tgz", + "integrity": "sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==", + "dev": true, + "requires": { + "buffer-alloc-unsafe": "^1.1.0", + "buffer-fill": "^1.0.0" + } + }, + "buffer-alloc-unsafe": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz", + "integrity": "sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==", + "dev": true + }, + "buffer-fill": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz", + "integrity": "sha1-+PeLdniYiO858gXNY39o5wISKyw=", + "dev": true + }, + "buffer-from": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", + "dev": true + }, + "buffer-indexof": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-indexof/-/buffer-indexof-1.1.1.tgz", + "integrity": "sha512-4/rOEg86jivtPTeOUUT61jJO1Ya1TrR/OkqCSZDyq84WJh3LuuiphBYJN+fm5xufIk4XAFcEwte/8WzC8If/1g==", + "dev": true + }, + "buffer-xor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", + "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=", + "dev": true + }, + "builtin-modules": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", + "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=", + "dev": true + }, + "builtin-status-codes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", + "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=", + "dev": true + }, + "builtins": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/builtins/-/builtins-1.0.3.tgz", + "integrity": "sha1-y5T662HIaWRR2zZTThQi+U8K7og=", + "dev": true + }, + "bytes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=", + "dev": true + }, + "cacache": { + "version": "10.0.4", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-10.0.4.tgz", + "integrity": "sha512-Dph0MzuH+rTQzGPNT9fAnrPmMmjKfST6trxJeK7NQuHRaVw24VzPRWTmg9MpcwOVQZO0E1FBICUlFeNaKPIfHA==", + "dev": true, + "requires": { + "bluebird": "^3.5.1", + "chownr": "^1.0.1", + "glob": "^7.1.2", + "graceful-fs": "^4.1.11", + "lru-cache": "^4.1.1", + "mississippi": "^2.0.0", + "mkdirp": "^0.5.1", + "move-concurrently": "^1.0.1", + "promise-inflight": "^1.0.1", + "rimraf": "^2.6.2", + "ssri": "^5.2.4", + "unique-filename": "^1.1.0", + "y18n": "^4.0.0" + } + }, + "cache-base": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", + "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", + "dev": true, + "requires": { + "collection-visit": "^1.0.0", + "component-emitter": "^1.2.1", + "get-value": "^2.0.6", + "has-value": "^1.0.0", + "isobject": "^3.0.1", + "set-value": "^2.0.0", + "to-object-path": "^0.3.0", + "union-value": "^1.0.0", + "unset-value": "^1.0.0" + } + }, + "callsite": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/callsite/-/callsite-1.0.0.tgz", + "integrity": "sha1-KAOY5dZkvXQDi28JBRU+borxvCA=", + "dev": true + }, + "camel-case": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-3.0.0.tgz", + "integrity": "sha1-yjw2iKTpzzpM2nd9xNy8cTJJz3M=", + "dev": true, + "requires": { + "no-case": "^2.2.0", + "upper-case": "^1.1.1" + } + }, + "camelcase": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", + "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=", + "dev": true, + "optional": true + }, + "camelcase-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz", + "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=", + "dev": true, + "optional": true, + "requires": { + "camelcase": "^2.0.0", + "map-obj": "^1.0.0" + } + }, + "caniuse-lite": { + "version": "1.0.30000938", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30000938.tgz", + "integrity": "sha512-ekW8NQ3/FvokviDxhdKLZZAx7PptXNwxKgXtnR5y+PR3hckwuP3yJ1Ir+4/c97dsHNqtAyfKUGdw8P4EYzBNgw==", + "dev": true + }, + "caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", + "dev": true + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "chokidar": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.0.4.tgz", + "integrity": "sha512-z9n7yt9rOvIJrMhvDtDictKrkFHeihkNl6uWMmZlmL6tJtX9Cs+87oK+teBx+JIgzvbX3yZHT3eF8vpbDxHJXQ==", + "dev": true, + "requires": { + "anymatch": "^2.0.0", + "async-each": "^1.0.0", + "braces": "^2.3.0", + "fsevents": "^1.2.2", + "glob-parent": "^3.1.0", + "inherits": "^2.0.1", + "is-binary-path": "^1.0.0", + "is-glob": "^4.0.0", + "lodash.debounce": "^4.0.8", + "normalize-path": "^2.1.1", + "path-is-absolute": "^1.0.0", + "readdirp": "^2.0.0", + "upath": "^1.0.5" + } + }, + "chownr": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.1.tgz", + "integrity": "sha512-j38EvO5+LHX84jlo6h4UzmOwi0UgW61WRyPtJz4qaadK5eY3BTS5TY/S1Stc3Uk2lIM6TPevAlULiEJwie860g==", + "dev": true + }, + "chrome-trace-event": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.0.tgz", + "integrity": "sha512-xDbVgyfDTT2piup/h8dK/y4QZfJRSa73bw1WZ8b4XM1o7fsFubUVGYcE+1ANtOzJJELGpYoG2961z0Z6OAld9A==", + "dev": true, + "requires": { + "tslib": "^1.9.0" + } + }, + "cipher-base": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", + "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "circular-dependency-plugin": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/circular-dependency-plugin/-/circular-dependency-plugin-5.0.2.tgz", + "integrity": "sha512-oC7/DVAyfcY3UWKm0sN/oVoDedQDQiw/vIiAnuTWTpE5s0zWf7l3WY417Xw/Fbi/QbAjctAkxgMiS9P0s3zkmA==", + "dev": true + }, + "circular-json": { + "version": "0.5.9", + "resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.5.9.tgz", + "integrity": "sha512-4ivwqHpIFJZBuhN3g/pEcdbnGUywkBblloGbkglyloVjjR3uT6tieI89MVOfbP2tHX5sgb01FuLgAOzebNlJNQ==", + "dev": true + }, + "class-utils": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", + "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", + "dev": true, + "requires": { + "arr-union": "^3.1.0", + "define-property": "^0.2.5", + "isobject": "^3.0.0", + "static-extend": "^0.1.1" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + } + } + }, + "clean-css": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.1.tgz", + "integrity": "sha512-4ZxI6dy4lrY6FHzfiy1aEOXgu4LIsW2MhwG0VBKdcoGoH/XLFgaHSdLTGr4O8Be6A8r3MOphEiI8Gc1n0ecf3g==", + "dev": true, + "requires": { + "source-map": "~0.6.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "cliui": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", + "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", + "dev": true, + "optional": true, + "requires": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wrap-ansi": "^2.0.0" + } + }, + "clone": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", + "integrity": "sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18=", + "dev": true + }, + "clone-deep": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-2.0.2.tgz", + "integrity": "sha512-SZegPTKjCgpQH63E+eN6mVEEPdQBOUzjyJm5Pora4lrwWRFS8I0QAxV/KD6vV/i0WuijHZWQC1fMsPEdxfdVCQ==", + "dev": true, + "requires": { + "for-own": "^1.0.0", + "is-plain-object": "^2.0.4", + "kind-of": "^6.0.0", + "shallow-clone": "^1.0.0" + } + }, + "co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", + "dev": true + }, + "code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", + "dev": true + }, + "codelyzer": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/codelyzer/-/codelyzer-4.3.0.tgz", + "integrity": "sha512-RLMrtLwrBS0dfo2/KTP+2NHofCpzcuh0bEp/A/naqvQonbUL4AW/qWQdbpn8dMNudtpmzEx9eS8KEpGdVPg1BA==", + "dev": true, + "requires": { + "app-root-path": "^2.0.1", + "css-selector-tokenizer": "^0.7.0", + "cssauron": "^1.4.0", + "semver-dsl": "^1.0.1", + "source-map": "^0.5.7", + "sprintf-js": "^1.0.3" + } + }, + "collection-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", + "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", + "dev": true, + "requires": { + "map-visit": "^1.0.0", + "object-visit": "^1.0.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "colors": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz", + "integrity": "sha1-FopHAXVran9RoSzgyXv6KMCE7WM=", + "dev": true + }, + "combine-lists": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/combine-lists/-/combine-lists-1.0.1.tgz", + "integrity": "sha1-RYwH4J4NkA/Ci3Cj/sLazR0st/Y=", + "dev": true, + "requires": { + "lodash": "^4.5.0" + } + }, + "combined-stream": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.7.tgz", + "integrity": "sha512-brWl9y6vOB1xYPZcpZde3N9zDByXTosAeMDo4p1wzo6UMOX4vumB+TP1RZ76sfE6Md68Q0NJSrE/gbezd4Ul+w==", + "dev": true, + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "commander": { + "version": "2.17.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.17.1.tgz", + "integrity": "sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg==", + "dev": true + }, + "commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", + "dev": true + }, + "compare-versions": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-3.4.0.tgz", + "integrity": "sha512-tK69D7oNXXqUW3ZNo/z7NXTEz22TCF0pTE+YF9cxvaAM9XnkLo1fV621xCLrRR6aevJlKxExkss0vWqUCUpqdg==", + "dev": true + }, + "component-bind": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/component-bind/-/component-bind-1.0.0.tgz", + "integrity": "sha1-AMYIq33Nk4l8AAllGx06jh5zu9E=", + "dev": true + }, + "component-emitter": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", + "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=", + "dev": true + }, + "component-inherit": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/component-inherit/-/component-inherit-0.0.3.tgz", + "integrity": "sha1-ZF/ErfWLcrZJ1crmUTVhnbJv8UM=", + "dev": true + }, + "compressible": { + "version": "2.0.15", + "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.15.tgz", + "integrity": "sha512-4aE67DL33dSW9gw4CI2H/yTxqHLNcxp0yS6jB+4h+wr3e43+1z7vm0HU9qXOH8j+qjKuL8+UtkOxYQSMq60Ylw==", + "dev": true, + "requires": { + "mime-db": ">= 1.36.0 < 2" + } + }, + "compression": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.3.tgz", + "integrity": "sha512-HSjyBG5N1Nnz7tF2+O7A9XUhyjru71/fwgNb7oIsEVHR0WShfs2tIS/EySLgiTe98aOK18YDlMXpzjCXY/n9mg==", + "dev": true, + "requires": { + "accepts": "~1.3.5", + "bytes": "3.0.0", + "compressible": "~2.0.14", + "debug": "2.6.9", + "on-headers": "~1.0.1", + "safe-buffer": "5.1.2", + "vary": "~1.1.2" + } + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + } + }, + "connect": { + "version": "3.6.6", + "resolved": "https://registry.npmjs.org/connect/-/connect-3.6.6.tgz", + "integrity": "sha1-Ce/2xVr3I24TcTWnJXSFi2eG9SQ=", + "dev": true, + "requires": { + "debug": "2.6.9", + "finalhandler": "1.1.0", + "parseurl": "~1.3.2", + "utils-merge": "1.0.1" + }, + "dependencies": { + "finalhandler": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.0.tgz", + "integrity": "sha1-zgtoVbRYU+eRsvzGgARtiCU91/U=", + "dev": true, + "requires": { + "debug": "2.6.9", + "encodeurl": "~1.0.1", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.2", + "statuses": "~1.3.1", + "unpipe": "~1.0.0" + } + }, + "statuses": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.3.1.tgz", + "integrity": "sha1-+vUbnrdKrvOzrPStX2Gr8ky3uT4=", + "dev": true + } + } + }, + "connect-history-api-fallback": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz", + "integrity": "sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg==", + "dev": true + }, + "console-browserify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.1.0.tgz", + "integrity": "sha1-8CQcRXMKn8YyOyBtvzjtx0HQuxA=", + "dev": true, + "requires": { + "date-now": "^0.1.4" + } + }, + "console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", + "dev": true + }, + "constants-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", + "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=", + "dev": true + }, + "content-disposition": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", + "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ=", + "dev": true + }, + "content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", + "dev": true + }, + "convert-source-map": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.6.0.tgz", + "integrity": "sha512-eFu7XigvxdZ1ETfbgPBohgyQ/Z++C0eEhTor0qRwBw9unw+L0/6V8wkSuGgzdThkiS5lSpdptOQPD8Ak40a+7A==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.1" + } + }, + "cookie": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", + "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=", + "dev": true + }, + "cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=", + "dev": true + }, + "copy-concurrently": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/copy-concurrently/-/copy-concurrently-1.0.5.tgz", + "integrity": "sha512-f2domd9fsVDFtaFcbaRZuYXwtdmnzqbADSwhSWYxYB/Q8zsdUUFMXVRwXGDMWmbEzAn1kdRrtI1T/KTFOL4X2A==", + "dev": true, + "requires": { + "aproba": "^1.1.1", + "fs-write-stream-atomic": "^1.0.8", + "iferr": "^0.1.5", + "mkdirp": "^0.5.1", + "rimraf": "^2.5.4", + "run-queue": "^1.0.0" + } + }, + "copy-descriptor": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", + "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", + "dev": true + }, + "copy-webpack-plugin": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-4.5.2.tgz", + "integrity": "sha512-zmC33E8FFSq3AbflTvqvPvBo621H36Afsxlui91d+QyZxPIuXghfnTsa1CuqiAaCPgJoSUWfTFbKJnadZpKEbQ==", + "dev": true, + "requires": { + "cacache": "^10.0.4", + "find-cache-dir": "^1.0.0", + "globby": "^7.1.1", + "is-glob": "^4.0.0", + "loader-utils": "^1.1.0", + "minimatch": "^3.0.4", + "p-limit": "^1.0.0", + "serialize-javascript": "^1.4.0" + } + }, + "core-js": { + "version": "2.6.5", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.5.tgz", + "integrity": "sha512-klh/kDpwX8hryYL14M9w/xei6vrv6sE8gTHDG7/T/+SEovB/G4ejwcfE/CBzO6Edsu+OETZMZ3wcX/EjUkrl5A==" + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", + "dev": true + }, + "cosmiconfig": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-4.0.0.tgz", + "integrity": "sha512-6e5vDdrXZD+t5v0L8CrurPeybg4Fmf+FCSYxXKYVAqLUtyCSbuyqE059d0kDthTNRzKVjL7QMgNpEUlsoYH3iQ==", + "dev": true, + "requires": { + "is-directory": "^0.3.1", + "js-yaml": "^3.9.0", + "parse-json": "^4.0.0", + "require-from-string": "^2.0.1" + }, + "dependencies": { + "parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", + "dev": true, + "requires": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + } + } + } + }, + "create-ecdh": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.3.tgz", + "integrity": "sha512-GbEHQPMOswGpKXM9kCWVrremUcBmjteUaQ01T9rkKCPDXfUHX0IoP9LpHYo2NPFampa4e+/pFDc3jQdxrxQLaw==", + "dev": true, + "requires": { + "bn.js": "^4.1.0", + "elliptic": "^6.0.0" + } + }, + "create-hash": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", + "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", + "dev": true, + "requires": { + "cipher-base": "^1.0.1", + "inherits": "^2.0.1", + "md5.js": "^1.3.4", + "ripemd160": "^2.0.1", + "sha.js": "^2.4.0" + } + }, + "create-hmac": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", + "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", + "dev": true, + "requires": { + "cipher-base": "^1.0.3", + "create-hash": "^1.1.0", + "inherits": "^2.0.1", + "ripemd160": "^2.0.0", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "cross-spawn": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-3.0.1.tgz", + "integrity": "sha1-ElYDfsufDF9549bvE14wdwGEuYI=", + "dev": true, + "optional": true, + "requires": { + "lru-cache": "^4.0.1", + "which": "^1.2.9" + } + }, + "crypto-browserify": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", + "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==", + "dev": true, + "requires": { + "browserify-cipher": "^1.0.0", + "browserify-sign": "^4.0.0", + "create-ecdh": "^4.0.0", + "create-hash": "^1.1.0", + "create-hmac": "^1.1.0", + "diffie-hellman": "^5.0.0", + "inherits": "^2.0.1", + "pbkdf2": "^3.0.3", + "public-encrypt": "^4.0.0", + "randombytes": "^2.0.0", + "randomfill": "^1.0.3" + } + }, + "css-parse": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/css-parse/-/css-parse-1.7.0.tgz", + "integrity": "sha1-Mh9s9zeCpv91ERE5D8BeLGV9jJs=", + "dev": true + }, + "css-select": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz", + "integrity": "sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg=", + "dev": true, + "requires": { + "boolbase": "~1.0.0", + "css-what": "2.1", + "domutils": "1.5.1", + "nth-check": "~1.0.1" + } + }, + "css-selector-tokenizer": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/css-selector-tokenizer/-/css-selector-tokenizer-0.7.1.tgz", + "integrity": "sha512-xYL0AMZJ4gFzJQsHUKa5jiWWi2vH77WVNg7JYRyewwj6oPh4yb/y6Y9ZCw9dsj/9UauMhtuxR+ogQd//EdEVNA==", + "dev": true, + "requires": { + "cssesc": "^0.1.0", + "fastparse": "^1.1.1", + "regexpu-core": "^1.0.0" + } + }, + "css-what": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-2.1.3.tgz", + "integrity": "sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg==", + "dev": true + }, + "cssauron": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/cssauron/-/cssauron-1.4.0.tgz", + "integrity": "sha1-pmAt/34EqDBtwNuaVR6S6LVmKtg=", + "dev": true, + "requires": { + "through": "X.X.X" + } + }, + "cssesc": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-0.1.0.tgz", + "integrity": "sha1-yBSQPkViM3GgR3tAEJqq++6t27Q=", + "dev": true + }, + "cuint": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/cuint/-/cuint-0.2.2.tgz", + "integrity": "sha1-QICG1AlVDCYxFVYZ6fp7ytw7mRs=", + "dev": true + }, + "currently-unhandled": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", + "integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=", + "dev": true, + "optional": true, + "requires": { + "array-find-index": "^1.0.1" + } + }, + "custom-event": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/custom-event/-/custom-event-1.0.1.tgz", + "integrity": "sha1-XQKkaFCt8bSjF5RqOSj8y1v9BCU=", + "dev": true + }, + "cyclist": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/cyclist/-/cyclist-0.2.2.tgz", + "integrity": "sha1-GzN5LhHpFKL9bW7WRHRkRE5fpkA=", + "dev": true + }, + "dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "dev": true, + "requires": { + "assert-plus": "^1.0.0" + } + }, + "date-format": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/date-format/-/date-format-1.2.0.tgz", + "integrity": "sha1-YV6CjiM90aubua4JUODOzPpuytg=", + "dev": true + }, + "date-now": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz", + "integrity": "sha1-6vQ5/U1ISK105cx9vvIAZyueNFs=", + "dev": true + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "dev": true + }, + "decode-uri-component": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", + "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", + "dev": true + }, + "deep-equal": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.0.1.tgz", + "integrity": "sha1-9dJgKStmDghO/0zbyfCK0yR0SLU=", + "dev": true + }, + "deep-is": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", + "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", + "dev": true + }, + "default-gateway": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-2.7.2.tgz", + "integrity": "sha512-lAc4i9QJR0YHSDFdzeBQKfZ1SRDG3hsJNEkrpcZa8QhBfidLAilT60BDEIVUUGqosFp425KOgB3uYqcnQrWafQ==", + "dev": true, + "requires": { + "execa": "^0.10.0", + "ip-regex": "^2.1.0" + } + }, + "default-require-extensions": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-2.0.0.tgz", + "integrity": "sha1-9fj7sYp9bVCyH2QfZJ67Uiz+JPc=", + "dev": true, + "requires": { + "strip-bom": "^3.0.0" + }, + "dependencies": { + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "dev": true + } + } + }, + "define-properties": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", + "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "dev": true, + "requires": { + "object-keys": "^1.0.12" + } + }, + "define-property": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", + "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", + "dev": true, + "requires": { + "is-descriptor": "^1.0.2", + "isobject": "^3.0.1" + }, + "dependencies": { + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "del": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/del/-/del-3.0.0.tgz", + "integrity": "sha1-U+z2mf/LyzljdpGrE7rxYIGXZuU=", + "dev": true, + "requires": { + "globby": "^6.1.0", + "is-path-cwd": "^1.0.0", + "is-path-in-cwd": "^1.0.0", + "p-map": "^1.1.1", + "pify": "^3.0.0", + "rimraf": "^2.2.8" + }, + "dependencies": { + "globby": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", + "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=", + "dev": true, + "requires": { + "array-union": "^1.0.1", + "glob": "^7.0.3", + "object-assign": "^4.0.1", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + }, + "dependencies": { + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + } + } + } + } + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", + "dev": true + }, + "delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", + "dev": true + }, + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", + "dev": true + }, + "des.js": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.0.tgz", + "integrity": "sha1-wHTS4qpqipoH29YfmhXCzYPsjsw=", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, + "destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=", + "dev": true + }, + "detect-indent": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-4.0.0.tgz", + "integrity": "sha1-920GQ1LN9Docts5hnE7jqUdd4gg=", + "dev": true, + "requires": { + "repeating": "^2.0.0" + } + }, + "detect-node": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.0.4.tgz", + "integrity": "sha512-ZIzRpLJrOj7jjP2miAtgqIfmzbxa4ZOr5jJc601zklsfEx9oTzmmj2nVpIPRpNlRTIh8lc1kyViIY7BWSGNmKw==", + "dev": true + }, + "di": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/di/-/di-0.0.1.tgz", + "integrity": "sha1-gGZJMmzqp8qjMG112YXqJ0i6kTw=", + "dev": true + }, + "diff": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", + "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", + "dev": true + }, + "diffie-hellman": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", + "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", + "dev": true, + "requires": { + "bn.js": "^4.1.0", + "miller-rabin": "^4.0.0", + "randombytes": "^2.0.0" + } + }, + "dir-glob": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-2.2.2.tgz", + "integrity": "sha512-f9LBi5QWzIW3I6e//uxZoLBlUt9kcp66qo0sSCxL6YZKc75R1c4MFCoe/LaZiBGmgujvQdxc5Bn3QhfyvK5Hsw==", + "dev": true, + "requires": { + "path-type": "^3.0.0" + } + }, + "dns-equal": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz", + "integrity": "sha1-s55/HabrCnW6nBcySzR1PEfgZU0=", + "dev": true + }, + "dns-packet": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-1.3.1.tgz", + "integrity": "sha512-0UxfQkMhYAUaZI+xrNZOz/as5KgDU0M/fQ9b6SpkyLbk3GEswDi6PADJVaYJradtRVsRIlF1zLyOodbcTCDzUg==", + "dev": true, + "requires": { + "ip": "^1.1.0", + "safe-buffer": "^5.0.1" + } + }, + "dns-txt": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/dns-txt/-/dns-txt-2.0.2.tgz", + "integrity": "sha1-uR2Ab10nGI5Ks+fRB9iBocxGQrY=", + "dev": true, + "requires": { + "buffer-indexof": "^1.0.0" + } + }, + "dom-converter": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/dom-converter/-/dom-converter-0.2.0.tgz", + "integrity": "sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA==", + "dev": true, + "requires": { + "utila": "~0.4" + } + }, + "dom-serialize": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/dom-serialize/-/dom-serialize-2.2.1.tgz", + "integrity": "sha1-ViromZ9Evl6jB29UGdzVnrQ6yVs=", + "dev": true, + "requires": { + "custom-event": "~1.0.0", + "ent": "~2.2.0", + "extend": "^3.0.0", + "void-elements": "^2.0.0" + } + }, + "dom-serializer": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.1.tgz", + "integrity": "sha512-l0IU0pPzLWSHBcieZbpOKgkIn3ts3vAh7ZuFyXNwJxJXk/c4Gwj9xaTJwIDVQCXawWD0qb3IzMGH5rglQaO0XA==", + "dev": true, + "requires": { + "domelementtype": "^1.3.0", + "entities": "^1.1.1" + } + }, + "domain-browser": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz", + "integrity": "sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==", + "dev": true + }, + "domelementtype": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz", + "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==", + "dev": true + }, + "domhandler": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.1.0.tgz", + "integrity": "sha1-0mRvXlf2w7qxHPbLBdPArPdBJZQ=", + "dev": true, + "requires": { + "domelementtype": "1" + } + }, + "domutils": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz", + "integrity": "sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=", + "dev": true, + "requires": { + "dom-serializer": "0", + "domelementtype": "1" + } + }, + "duplexify": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz", + "integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==", + "dev": true, + "requires": { + "end-of-stream": "^1.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.0.0", + "stream-shift": "^1.0.0" + } + }, + "ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", + "dev": true, + "requires": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, + "ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=", + "dev": true + }, + "ejs": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-2.6.1.tgz", + "integrity": "sha512-0xy4A/twfrRCnkhfk8ErDi5DqdAsAqeGxht4xkCUrsvhhbQNs7E+4jV0CN7+NKIY0aHE72+XvqtBIXzD31ZbXQ==", + "dev": true + }, + "electron-to-chromium": { + "version": "1.3.113", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.113.tgz", + "integrity": "sha512-De+lPAxEcpxvqPTyZAXELNpRZXABRxf+uL/rSykstQhzj/B0l1150G/ExIIxKc16lI89Hgz81J0BHAcbTqK49g==", + "dev": true + }, + "elliptic": { + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.4.1.tgz", + "integrity": "sha512-BsXLz5sqX8OHcsh7CqBMztyXARmGQ3LWPtGjJi6DiJHq5C/qvi9P3OqgswKSDftbu8+IoI/QDTAm2fFnQ9SZSQ==", + "dev": true, + "requires": { + "bn.js": "^4.4.0", + "brorand": "^1.0.1", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.0" + } + }, + "emojis-list": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz", + "integrity": "sha1-TapNnbAPmBmIDHn6RXrlsJof04k=", + "dev": true + }, + "encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", + "dev": true + }, + "encoding": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.12.tgz", + "integrity": "sha1-U4tm8+5izRq1HsMjgp0flIDHS+s=", + "dev": true, + "requires": { + "iconv-lite": "~0.4.13" + } + }, + "end-of-stream": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", + "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==", + "dev": true, + "requires": { + "once": "^1.4.0" + } + }, + "engine.io": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-3.2.1.tgz", + "integrity": "sha512-+VlKzHzMhaU+GsCIg4AoXF1UdDFjHHwMmMKqMJNDNLlUlejz58FCy4LBqB2YVJskHGYl06BatYWKP2TVdVXE5w==", + "dev": true, + "requires": { + "accepts": "~1.3.4", + "base64id": "1.0.0", + "cookie": "0.3.1", + "debug": "~3.1.0", + "engine.io-parser": "~2.1.0", + "ws": "~3.3.1" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + } + } + }, + "engine.io-client": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-3.2.1.tgz", + "integrity": "sha512-y5AbkytWeM4jQr7m/koQLc5AxpRKC1hEVUb/s1FUAWEJq5AzJJ4NLvzuKPuxtDi5Mq755WuDvZ6Iv2rXj4PTzw==", + "dev": true, + "requires": { + "component-emitter": "1.2.1", + "component-inherit": "0.0.3", + "debug": "~3.1.0", + "engine.io-parser": "~2.1.1", + "has-cors": "1.1.0", + "indexof": "0.0.1", + "parseqs": "0.0.5", + "parseuri": "0.0.5", + "ws": "~3.3.1", + "xmlhttprequest-ssl": "~1.5.4", + "yeast": "0.1.2" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + } + } + }, + "engine.io-parser": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-2.1.3.tgz", + "integrity": "sha512-6HXPre2O4Houl7c4g7Ic/XzPnHBvaEmN90vtRO9uLmwtRqQmTOw0QMevL1TOfL2Cpu1VzsaTmMotQgMdkzGkVA==", + "dev": true, + "requires": { + "after": "0.8.2", + "arraybuffer.slice": "~0.0.7", + "base64-arraybuffer": "0.1.5", + "blob": "0.0.5", + "has-binary2": "~1.0.2" + } + }, + "enhanced-resolve": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.1.0.tgz", + "integrity": "sha512-F/7vkyTtyc/llOIn8oWclcB25KdRaiPBpZYDgJHgh/UHtpgT2p2eldQgtQnLtUvfMKPKxbRaQM/hHkvLHt1Vng==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "memory-fs": "^0.4.0", + "tapable": "^1.0.0" + } + }, + "ent": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/ent/-/ent-2.2.0.tgz", + "integrity": "sha1-6WQhkyWiHQX0RGai9obtbOX13R0=", + "dev": true + }, + "entities": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", + "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==", + "dev": true + }, + "err-code": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/err-code/-/err-code-1.1.2.tgz", + "integrity": "sha1-BuARbTAo9q70gGhJ6w6mp0iuaWA=", + "dev": true + }, + "errno": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.7.tgz", + "integrity": "sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg==", + "dev": true, + "requires": { + "prr": "~1.0.1" + } + }, + "error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "requires": { + "is-arrayish": "^0.2.1" + } + }, + "es-abstract": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.13.0.tgz", + "integrity": "sha512-vDZfg/ykNxQVwup/8E1BZhVzFfBxs9NqMzGcvIJrqg5k2/5Za2bWo40dK2J1pgLngZ7c+Shh8lwYtLGyrwPutg==", + "dev": true, + "requires": { + "es-to-primitive": "^1.2.0", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "is-callable": "^1.1.4", + "is-regex": "^1.0.4", + "object-keys": "^1.0.12" + } + }, + "es-to-primitive": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.0.tgz", + "integrity": "sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg==", + "dev": true, + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, + "es6-promise": { + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.5.tgz", + "integrity": "sha512-n6wvpdE43VFtJq+lUDYDBFUwV8TZbuGXLV4D6wKafg13ldznKsyEvatubnmUe31zcvelSzOHF+XbaT+Bl9ObDg==", + "dev": true + }, + "es6-promisify": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz", + "integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=", + "dev": true, + "requires": { + "es6-promise": "^4.0.3" + } + }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=", + "dev": true + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true + }, + "escodegen": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.8.1.tgz", + "integrity": "sha1-WltTr0aTEQvrsIZ6o0MN07cKEBg=", + "dev": true, + "requires": { + "esprima": "^2.7.1", + "estraverse": "^1.9.1", + "esutils": "^2.0.2", + "optionator": "^0.8.1", + "source-map": "~0.2.0" + }, + "dependencies": { + "source-map": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.2.0.tgz", + "integrity": "sha1-2rc/vPwrqBm03gO9b26qSBZLP50=", + "dev": true, + "optional": true, + "requires": { + "amdefine": ">=0.0.4" + } + } + } + }, + "eslint-scope": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.0.tgz", + "integrity": "sha512-1G6UTDi7Jc1ELFwnR58HV4fK9OQK4S6N985f166xqXxpjU6plxFISJa2Ba9KCQuFa8RCnj/lSFJbHo7UFDBnUA==", + "dev": true, + "requires": { + "esrecurse": "^4.1.0", + "estraverse": "^4.1.1" + }, + "dependencies": { + "estraverse": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz", + "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=", + "dev": true + } + } + }, + "esprima": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz", + "integrity": "sha1-luO3DVd59q1JzQMmc9HDEnZ7pYE=", + "dev": true + }, + "esrecurse": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz", + "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==", + "dev": true, + "requires": { + "estraverse": "^4.1.0" + }, + "dependencies": { + "estraverse": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz", + "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=", + "dev": true + } + } + }, + "estraverse": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-1.9.3.tgz", + "integrity": "sha1-r2fy3JIlgkFZUJJgkaQAXSnJu0Q=", + "dev": true + }, + "esutils": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", + "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", + "dev": true + }, + "etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=", + "dev": true + }, + "eventemitter3": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-3.1.0.tgz", + "integrity": "sha512-ivIvhpq/Y0uSjcHDcOIccjmYjGLcP09MFGE7ysAwkAvkXfpZlC985pH2/ui64DKazbTW/4kN3yqozUxlXzI6cA==", + "dev": true + }, + "events": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.0.0.tgz", + "integrity": "sha512-Dc381HFWJzEOhQ+d8pkNon++bk9h6cdAoAj4iE6Q4y6xgTzySWXlKn05/TVNpjnfRqi/X0EpJEJohPjNI3zpVA==", + "dev": true + }, + "eventsource": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-1.0.7.tgz", + "integrity": "sha512-4Ln17+vVT0k8aWq+t/bF5arcS3EpT9gYtW66EPacdj/mAFevznsnyoHLPy2BA8gbIQeIHoPsvwmfBftfcG//BQ==", + "dev": true, + "requires": { + "original": "^1.0.0" + } + }, + "evp_bytestokey": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", + "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", + "dev": true, + "requires": { + "md5.js": "^1.3.4", + "safe-buffer": "^5.1.1" + } + }, + "execa": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-0.10.0.tgz", + "integrity": "sha512-7XOMnz8Ynx1gGo/3hyV9loYNPWM94jG3+3T3Y8tsfSstFmETmENCMU/A/zj8Lyaj1lkgEepKepvd6240tBRvlw==", + "dev": true, + "requires": { + "cross-spawn": "^6.0.0", + "get-stream": "^3.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + }, + "dependencies": { + "cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "dev": true, + "requires": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + } + } + }, + "exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=", + "dev": true + }, + "expand-braces": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/expand-braces/-/expand-braces-0.1.2.tgz", + "integrity": "sha1-SIsdHSRRyz06axks/AMPRMWFX+o=", + "dev": true, + "requires": { + "array-slice": "^0.2.3", + "array-unique": "^0.2.1", + "braces": "^0.1.2" + }, + "dependencies": { + "array-unique": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz", + "integrity": "sha1-odl8yvy8JiXMcPrc6zalDFiwGlM=", + "dev": true + }, + "braces": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/braces/-/braces-0.1.5.tgz", + "integrity": "sha1-wIVxEIUpHYt1/ddOqw+FlygHEeY=", + "dev": true, + "requires": { + "expand-range": "^0.1.0" + } + }, + "expand-range": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/expand-range/-/expand-range-0.1.1.tgz", + "integrity": "sha1-TLjtoJk8pW+k9B/ELzy7TMrf8EQ=", + "dev": true, + "requires": { + "is-number": "^0.1.1", + "repeat-string": "^0.2.2" + } + }, + "is-number": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-0.1.1.tgz", + "integrity": "sha1-aaevEWlj1HIG7JvZtIoUIW8eOAY=", + "dev": true + }, + "repeat-string": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-0.2.2.tgz", + "integrity": "sha1-x6jTI2BoNiBZp+RlH8aITosftK4=", + "dev": true + } + } + }, + "expand-brackets": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", + "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", + "dev": true, + "requires": { + "debug": "^2.3.3", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "posix-character-classes": "^0.1.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "expand-range": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/expand-range/-/expand-range-1.8.2.tgz", + "integrity": "sha1-opnv/TNf4nIeuujiV+x5ZE/IUzc=", + "dev": true, + "requires": { + "fill-range": "^2.1.0" + }, + "dependencies": { + "fill-range": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-2.2.4.tgz", + "integrity": "sha512-cnrcCbj01+j2gTG921VZPnHbjmdAf8oQV/iGeV2kZxGSyfYjjTyY79ErsK1WJWMpw6DaApEX72binqJE+/d+5Q==", + "dev": true, + "requires": { + "is-number": "^2.1.0", + "isobject": "^2.0.0", + "randomatic": "^3.0.0", + "repeat-element": "^1.1.2", + "repeat-string": "^1.5.2" + } + }, + "is-number": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-2.1.0.tgz", + "integrity": "sha1-Afy7s5NGOlSPL0ZszhbezknbkI8=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + } + }, + "isobject": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", + "dev": true, + "requires": { + "isarray": "1.0.0" + } + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "express": { + "version": "4.16.4", + "resolved": "https://registry.npmjs.org/express/-/express-4.16.4.tgz", + "integrity": "sha512-j12Uuyb4FMrd/qQAm6uCHAkPtO8FDTRJZBDd5D2KOL2eLaz1yUNdUB/NOIyq0iU4q4cFarsUCrnFDPBcnksuOg==", + "dev": true, + "requires": { + "accepts": "~1.3.5", + "array-flatten": "1.1.1", + "body-parser": "1.18.3", + "content-disposition": "0.5.2", + "content-type": "~1.0.4", + "cookie": "0.3.1", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "~1.1.2", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.1.1", + "fresh": "0.5.2", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "~2.3.0", + "parseurl": "~1.3.2", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.4", + "qs": "6.5.2", + "range-parser": "~1.2.0", + "safe-buffer": "5.1.2", + "send": "0.16.2", + "serve-static": "1.13.2", + "setprototypeof": "1.1.0", + "statuses": "~1.4.0", + "type-is": "~1.6.16", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "dependencies": { + "array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=", + "dev": true + } + } + }, + "extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "dev": true + }, + "extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", + "dev": true, + "requires": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "dependencies": { + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dev": true, + "requires": { + "is-plain-object": "^2.0.4" + } + } + } + }, + "extglob": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", + "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", + "dev": true, + "requires": { + "array-unique": "^0.3.2", + "define-property": "^1.0.0", + "expand-brackets": "^2.1.4", + "extend-shallow": "^2.0.1", + "fragment-cache": "^0.2.1", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", + "dev": true + }, + "fast-deep-equal": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", + "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=", + "dev": true + }, + "fast-json-stable-stringify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", + "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=", + "dev": true + }, + "fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", + "dev": true + }, + "fastparse": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/fastparse/-/fastparse-1.1.2.tgz", + "integrity": "sha512-483XLLxTVIwWK3QTrMGRqUfUpoOs/0hbQrl2oz4J0pAcm3A3bu84wxTFqGqkJzewCLdME38xJLJAxBABfQT8sQ==", + "dev": true + }, + "faye-websocket": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.10.0.tgz", + "integrity": "sha1-TkkvjQTftviQA1B/btvy1QHnxvQ=", + "dev": true, + "requires": { + "websocket-driver": ">=0.5.1" + } + }, + "figgy-pudding": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/figgy-pudding/-/figgy-pudding-3.5.1.tgz", + "integrity": "sha512-vNKxJHTEKNThjfrdJwHc7brvM6eVevuO5nTj6ez8ZQ1qbXTvGthucRF7S4vf2cr71QVnT70V34v0S1DyQsti0w==", + "dev": true + }, + "file-loader": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-1.1.11.tgz", + "integrity": "sha512-TGR4HU7HUsGg6GCOPJnFk06RhWgEWFLAGWiT6rcD+GRC2keU3s9RGJ+b3Z6/U73jwwNb2gKLJ7YCrp+jvU4ALg==", + "dev": true, + "requires": { + "loader-utils": "^1.0.2", + "schema-utils": "^0.4.5" + } + }, + "filename-regex": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/filename-regex/-/filename-regex-2.0.1.tgz", + "integrity": "sha1-wcS5vuPglyXdsQa3XB4wH+LxiyY=", + "dev": true + }, + "fileset": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/fileset/-/fileset-2.0.3.tgz", + "integrity": "sha1-jnVIqW08wjJ+5eZ0FocjozO7oqA=", + "dev": true, + "requires": { + "glob": "^7.0.3", + "minimatch": "^3.0.3" + } + }, + "fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "finalhandler": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz", + "integrity": "sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg==", + "dev": true, + "requires": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.2", + "statuses": "~1.4.0", + "unpipe": "~1.0.0" + } + }, + "find-cache-dir": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-1.0.0.tgz", + "integrity": "sha1-kojj6ePMN0hxfTnq3hfPcfww7m8=", + "dev": true, + "requires": { + "commondir": "^1.0.1", + "make-dir": "^1.0.0", + "pkg-dir": "^2.0.0" + } + }, + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "dev": true, + "requires": { + "locate-path": "^2.0.0" + } + }, + "flush-write-stream": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.1.1.tgz", + "integrity": "sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "readable-stream": "^2.3.6" + } + }, + "follow-redirects": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.7.0.tgz", + "integrity": "sha512-m/pZQy4Gj287eNy94nivy5wchN3Kp+Q5WgUPNy5lJSZ3sgkVKSYV/ZChMAQVIgx1SqfZ2zBZtPA2YlXIWxxJOQ==", + "dev": true, + "requires": { + "debug": "^3.2.6" + }, + "dependencies": { + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "dev": true + } + } + }, + "for-in": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", + "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", + "dev": true + }, + "for-own": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/for-own/-/for-own-1.0.0.tgz", + "integrity": "sha1-xjMy9BXO3EsE2/5wz4NklMU8tEs=", + "dev": true, + "requires": { + "for-in": "^1.0.1" + } + }, + "forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", + "dev": true + }, + "form-data": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "dev": true, + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + } + }, + "forwarded": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", + "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=", + "dev": true + }, + "fragment-cache": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", + "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", + "dev": true, + "requires": { + "map-cache": "^0.2.2" + } + }, + "fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=", + "dev": true + }, + "from2": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz", + "integrity": "sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8=", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "readable-stream": "^2.0.0" + } + }, + "fs-access": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/fs-access/-/fs-access-1.0.1.tgz", + "integrity": "sha1-1qh/JiJxzv6+wwxVNAf7mV2od3o=", + "dev": true, + "requires": { + "null-check": "^1.0.0" + } + }, + "fs-minipass": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.5.tgz", + "integrity": "sha512-JhBl0skXjUPCFH7x6x61gQxrKyXsxB5gcgePLZCwfyCGGsTISMoIeObbrvVeP6Xmyaudw4TT43qV2Gz+iyd2oQ==", + "dev": true, + "requires": { + "minipass": "^2.2.1" + } + }, + "fs-write-stream-atomic": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz", + "integrity": "sha1-tH31NJPvkR33VzHnCp3tAYnbQMk=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "iferr": "^0.1.5", + "imurmurhash": "^0.1.4", + "readable-stream": "1 || 2" + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "fsevents": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.7.tgz", + "integrity": "sha512-Pxm6sI2MeBD7RdD12RYsqaP0nMiwx8eZBXCa6z2L+mRHm2DYrOYwihmhjpkdjUHwQhslWQjRpEgNq4XvBmaAuw==", + "dev": true, + "optional": true, + "requires": { + "nan": "^2.9.2", + "node-pre-gyp": "^0.10.0" + }, + "dependencies": { + "abbrev": { + "version": "1.1.1", + "bundled": true, + "dev": true, + "optional": true + }, + "ansi-regex": { + "version": "2.1.1", + "bundled": true, + "dev": true + }, + "aproba": { + "version": "1.2.0", + "bundled": true, + "dev": true, + "optional": true + }, + "are-we-there-yet": { + "version": "1.1.5", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" + } + }, + "balanced-match": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "brace-expansion": { + "version": "1.1.11", + "bundled": true, + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "chownr": { + "version": "1.1.1", + "bundled": true, + "dev": true, + "optional": true + }, + "code-point-at": { + "version": "1.1.0", + "bundled": true, + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "bundled": true, + "dev": true + }, + "console-control-strings": { + "version": "1.1.0", + "bundled": true, + "dev": true + }, + "core-util-is": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "debug": { + "version": "2.6.9", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "ms": "2.0.0" + } + }, + "deep-extend": { + "version": "0.6.0", + "bundled": true, + "dev": true, + "optional": true + }, + "delegates": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "detect-libc": { + "version": "1.0.3", + "bundled": true, + "dev": true, + "optional": true + }, + "fs-minipass": { + "version": "1.2.5", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "minipass": "^2.2.1" + } + }, + "fs.realpath": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "gauge": { + "version": "2.7.4", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "aproba": "^1.0.3", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.0", + "object-assign": "^4.1.0", + "signal-exit": "^3.0.0", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wide-align": "^1.1.0" + } + }, + "glob": { + "version": "7.1.3", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "has-unicode": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "iconv-lite": { + "version": "0.4.24", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "ignore-walk": { + "version": "3.0.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "minimatch": "^3.0.4" + } + }, + "inflight": { + "version": "1.0.6", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.3", + "bundled": true, + "dev": true + }, + "ini": { + "version": "1.3.5", + "bundled": true, + "dev": true, + "optional": true + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "isarray": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "minimatch": { + "version": "3.0.4", + "bundled": true, + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "0.0.8", + "bundled": true, + "dev": true + }, + "minipass": { + "version": "2.3.5", + "bundled": true, + "dev": true, + "requires": { + "safe-buffer": "^5.1.2", + "yallist": "^3.0.0" + } + }, + "minizlib": { + "version": "1.2.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "minipass": "^2.2.1" + } + }, + "mkdirp": { + "version": "0.5.1", + "bundled": true, + "dev": true, + "requires": { + "minimist": "0.0.8" + } + }, + "ms": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "needle": { + "version": "2.2.4", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "debug": "^2.1.2", + "iconv-lite": "^0.4.4", + "sax": "^1.2.4" + } + }, + "node-pre-gyp": { + "version": "0.10.3", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "detect-libc": "^1.0.2", + "mkdirp": "^0.5.1", + "needle": "^2.2.1", + "nopt": "^4.0.1", + "npm-packlist": "^1.1.6", + "npmlog": "^4.0.2", + "rc": "^1.2.7", + "rimraf": "^2.6.1", + "semver": "^5.3.0", + "tar": "^4" + } + }, + "nopt": { + "version": "4.0.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "abbrev": "1", + "osenv": "^0.1.4" + } + }, + "npm-bundled": { + "version": "1.0.5", + "bundled": true, + "dev": true, + "optional": true + }, + "npm-packlist": { + "version": "1.2.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "ignore-walk": "^3.0.1", + "npm-bundled": "^1.0.1" + } + }, + "npmlog": { + "version": "4.1.2", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "are-we-there-yet": "~1.1.2", + "console-control-strings": "~1.1.0", + "gauge": "~2.7.3", + "set-blocking": "~2.0.0" + } + }, + "number-is-nan": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "object-assign": { + "version": "4.1.1", + "bundled": true, + "dev": true, + "optional": true + }, + "once": { + "version": "1.4.0", + "bundled": true, + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "os-homedir": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "os-tmpdir": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "osenv": { + "version": "0.1.5", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "os-homedir": "^1.0.0", + "os-tmpdir": "^1.0.0" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "process-nextick-args": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "rc": { + "version": "1.2.8", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "bundled": true, + "dev": true, + "optional": true + } + } + }, + "readable-stream": { + "version": "2.3.6", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "rimraf": { + "version": "2.6.3", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "glob": "^7.1.3" + } + }, + "safe-buffer": { + "version": "5.1.2", + "bundled": true, + "dev": true + }, + "safer-buffer": { + "version": "2.1.2", + "bundled": true, + "dev": true, + "optional": true + }, + "sax": { + "version": "1.2.4", + "bundled": true, + "dev": true, + "optional": true + }, + "semver": { + "version": "5.6.0", + "bundled": true, + "dev": true, + "optional": true + }, + "set-blocking": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "signal-exit": { + "version": "3.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "string-width": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "string_decoder": { + "version": "1.1.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "bundled": true, + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "strip-json-comments": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "tar": { + "version": "4.4.8", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "chownr": "^1.1.1", + "fs-minipass": "^1.2.5", + "minipass": "^2.3.4", + "minizlib": "^1.1.1", + "mkdirp": "^0.5.0", + "safe-buffer": "^5.1.2", + "yallist": "^3.0.2" + } + }, + "util-deprecate": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "wide-align": { + "version": "1.1.3", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "string-width": "^1.0.2 || 2" + } + }, + "wrappy": { + "version": "1.0.2", + "bundled": true, + "dev": true + }, + "yallist": { + "version": "3.0.3", + "bundled": true, + "dev": true + } + } + }, + "fstream": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.11.tgz", + "integrity": "sha1-XB+x8RdHcRTwYyoOtLcbPLD9MXE=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "inherits": "~2.0.0", + "mkdirp": ">=0.5 0", + "rimraf": "2" + } + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "gauge": { + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", + "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", + "dev": true, + "requires": { + "aproba": "^1.0.3", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.0", + "object-assign": "^4.1.0", + "signal-exit": "^3.0.0", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wide-align": "^1.1.0" + } + }, + "gaze": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/gaze/-/gaze-1.1.3.tgz", + "integrity": "sha512-BRdNm8hbWzFzWHERTrejLqwHDfS4GibPoq5wjTPIoJHoBtKGPg3xAFfxmM+9ztbXelxcf2hwQcaz1PtmFeue8g==", + "dev": true, + "optional": true, + "requires": { + "globule": "^1.0.0" + } + }, + "genfun": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/genfun/-/genfun-5.0.0.tgz", + "integrity": "sha512-KGDOARWVga7+rnB3z9Sd2Letx515owfk0hSxHGuqjANb1M+x2bGZGqHLiozPsYMdM2OubeMni/Hpwmjq6qIUhA==", + "dev": true + }, + "get-caller-file": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", + "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==", + "dev": true + }, + "get-stdin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", + "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=", + "dev": true + }, + "get-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", + "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", + "dev": true + }, + "get-value": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", + "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", + "dev": true + }, + "getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "dev": true, + "requires": { + "assert-plus": "^1.0.0" + } + }, + "glob": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", + "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "glob-base": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/glob-base/-/glob-base-0.3.0.tgz", + "integrity": "sha1-27Fk9iIbHAscz4Kuoyi0l98Oo8Q=", + "dev": true, + "requires": { + "glob-parent": "^2.0.0", + "is-glob": "^2.0.0" + }, + "dependencies": { + "glob-parent": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz", + "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=", + "dev": true, + "requires": { + "is-glob": "^2.0.0" + } + }, + "is-extglob": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", + "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", + "dev": true + }, + "is-glob": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", + "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", + "dev": true, + "requires": { + "is-extglob": "^1.0.0" + } + } + } + }, + "glob-parent": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", + "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", + "dev": true, + "requires": { + "is-glob": "^3.1.0", + "path-dirname": "^1.0.0" + }, + "dependencies": { + "is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "dev": true, + "requires": { + "is-extglob": "^2.1.0" + } + } + } + }, + "globals": { + "version": "9.18.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz", + "integrity": "sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==", + "dev": true + }, + "globby": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/globby/-/globby-7.1.1.tgz", + "integrity": "sha1-+yzP+UAfhgCUXfral0QMypcrhoA=", + "dev": true, + "requires": { + "array-union": "^1.0.1", + "dir-glob": "^2.0.0", + "glob": "^7.1.2", + "ignore": "^3.3.5", + "pify": "^3.0.0", + "slash": "^1.0.0" + } + }, + "globule": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/globule/-/globule-1.2.1.tgz", + "integrity": "sha512-g7QtgWF4uYSL5/dn71WxubOrS7JVGCnFPEnoeChJmBnyR9Mw8nGoEwOgJL/RC2Te0WhbsEUCejfH8SZNJ+adYQ==", + "dev": true, + "optional": true, + "requires": { + "glob": "~7.1.1", + "lodash": "~4.17.10", + "minimatch": "~3.0.2" + } + }, + "graceful-fs": { + "version": "4.1.15", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz", + "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==", + "dev": true + }, + "handle-thing": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.0.tgz", + "integrity": "sha512-d4sze1JNC454Wdo2fkuyzCr6aHcbL6PGGuFAz0Li/NcOm1tCHGnWDRmJP85dh9IhQErTc2svWFEX5xHIOo//kQ==", + "dev": true + }, + "handlebars": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.1.0.tgz", + "integrity": "sha512-l2jRuU1NAWK6AW5qqcTATWQJvNPEwkM7NEKSiv/gqOsoSQbVoWyqVEY5GS+XPQ88zLNmqASRpzfdm8d79hJS+w==", + "dev": true, + "requires": { + "async": "^2.5.0", + "optimist": "^0.6.1", + "source-map": "^0.6.1", + "uglify-js": "^3.1.4" + }, + "dependencies": { + "async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.2.tgz", + "integrity": "sha512-H1qVYh1MYhEEFLsP97cVKqCGo7KfCyTt6uEWqsTBr9SO84oK9Uwbyd/yCW+6rKJLHksBNUVWZDAjfS+Ccx0Bbg==", + "dev": true, + "requires": { + "lodash": "^4.17.11" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", + "dev": true + }, + "har-validator": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz", + "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==", + "dev": true, + "requires": { + "ajv": "^6.5.5", + "har-schema": "^2.0.0" + }, + "dependencies": { + "ajv": { + "version": "6.9.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.9.1.tgz", + "integrity": "sha512-XDN92U311aINL77ieWHmqCcNlwjoP5cHXDxIxbf2MaPYuCXOHS7gHH8jktxeK5omgd52XbSTX6a4Piwd1pQmzA==", + "dev": true, + "requires": { + "fast-deep-equal": "^2.0.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "fast-deep-equal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", + "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=", + "dev": true + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "uri-js": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", + "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", + "dev": true, + "requires": { + "punycode": "^2.1.0" + } + } + } + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-ansi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "has-binary2": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-binary2/-/has-binary2-1.0.3.tgz", + "integrity": "sha512-G1LWKhDSvhGeAQ8mPVQlqNcOB2sJdwATtZKl2pDKKHfpf/rYj24lkinxf69blJbnsvtqqNU+L3SL50vzZhXOnw==", + "dev": true, + "requires": { + "isarray": "2.0.1" + }, + "dependencies": { + "isarray": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz", + "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4=", + "dev": true + } + } + }, + "has-cors": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-cors/-/has-cors-1.1.0.tgz", + "integrity": "sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk=", + "dev": true + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "has-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.0.tgz", + "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=", + "dev": true + }, + "has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", + "dev": true + }, + "has-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", + "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", + "dev": true, + "requires": { + "get-value": "^2.0.6", + "has-values": "^1.0.0", + "isobject": "^3.0.0" + } + }, + "has-values": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", + "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", + "dev": true, + "requires": { + "is-number": "^3.0.0", + "kind-of": "^4.0.0" + }, + "dependencies": { + "kind-of": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", + "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "hash-base": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.4.tgz", + "integrity": "sha1-X8hoaEfs1zSZQDMZprCj8/auSRg=", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "hash.js": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", + "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, + "he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true + }, + "hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", + "dev": true, + "requires": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "hosted-git-info": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.7.1.tgz", + "integrity": "sha512-7T/BxH19zbcCTa8XkMlbK5lTo1WtgkFi3GvdWEyNuc4Vex7/9Dqbnpsf4JMydcfj9HCg4zUWFTL3Za6lapg5/w==", + "dev": true + }, + "hpack.js": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", + "integrity": "sha1-h3dMCUnlE/QuhFdbPEVoH63ioLI=", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "obuf": "^1.0.0", + "readable-stream": "^2.0.1", + "wbuf": "^1.1.0" + } + }, + "html-entities": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-1.2.1.tgz", + "integrity": "sha1-DfKTUfByEWNRXfueVUPl9u7VFi8=", + "dev": true + }, + "html-minifier": { + "version": "3.5.21", + "resolved": "https://registry.npmjs.org/html-minifier/-/html-minifier-3.5.21.tgz", + "integrity": "sha512-LKUKwuJDhxNa3uf/LPR/KVjm/l3rBqtYeCOAekvG8F1vItxMUpueGd94i/asDDr8/1u7InxzFA5EeGjhhG5mMA==", + "dev": true, + "requires": { + "camel-case": "3.0.x", + "clean-css": "4.2.x", + "commander": "2.17.x", + "he": "1.2.x", + "param-case": "2.1.x", + "relateurl": "0.2.x", + "uglify-js": "3.4.x" + } + }, + "html-webpack-plugin": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-3.2.0.tgz", + "integrity": "sha1-sBq71yOsqqeze2r0SS69oD2d03s=", + "dev": true, + "requires": { + "html-minifier": "^3.2.3", + "loader-utils": "^0.2.16", + "lodash": "^4.17.3", + "pretty-error": "^2.0.2", + "tapable": "^1.0.0", + "toposort": "^1.0.0", + "util.promisify": "1.0.0" + }, + "dependencies": { + "loader-utils": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-0.2.17.tgz", + "integrity": "sha1-+G5jdNQyBabmxg6RlvF8Apm/s0g=", + "dev": true, + "requires": { + "big.js": "^3.1.3", + "emojis-list": "^2.0.0", + "json5": "^0.5.0", + "object-assign": "^4.0.1" + } + } + } + }, + "htmlparser2": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.3.0.tgz", + "integrity": "sha1-zHDQWln2VC5D8OaFyYLhTJJKnv4=", + "dev": true, + "requires": { + "domelementtype": "1", + "domhandler": "2.1", + "domutils": "1.1", + "readable-stream": "1.0" + }, + "dependencies": { + "domutils": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.1.6.tgz", + "integrity": "sha1-vdw94Jm5ou+sxRxiPyj0FuzFdIU=", + "dev": true, + "requires": { + "domelementtype": "1" + } + }, + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", + "dev": true + }, + "readable-stream": { + "version": "1.0.34", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", + "dev": true + } + } + }, + "http-cache-semantics": { + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-3.8.1.tgz", + "integrity": "sha512-5ai2iksyV8ZXmnZhHH4rWPoxxistEexSi5936zIQ1bnNTW5VnA85B6P/VpXiRM017IgRvb2kKo1a//y+0wSp3w==", + "dev": true + }, + "http-deceiver": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", + "integrity": "sha1-+nFolEq5pRnTN8sL7HKE3D5yPYc=", + "dev": true + }, + "http-errors": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", + "dev": true, + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.0", + "statuses": ">= 1.4.0 < 2" + } + }, + "http-parser-js": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.0.tgz", + "integrity": "sha512-cZdEF7r4gfRIq7ezX9J0T+kQmJNOub71dWbgAXVHDct80TKP4MCETtZQ31xyv38UwgzkWPYF/Xc0ge55dW9Z9w==", + "dev": true + }, + "http-proxy": { + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.17.0.tgz", + "integrity": "sha512-Taqn+3nNvYRfJ3bGvKfBSRwy1v6eePlm3oc/aWVxZp57DQr5Eq3xhKJi7Z4hZpS8PC3H4qI+Yly5EmFacGuA/g==", + "dev": true, + "requires": { + "eventemitter3": "^3.0.0", + "follow-redirects": "^1.0.0", + "requires-port": "^1.0.0" + } + }, + "http-proxy-agent": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-2.1.0.tgz", + "integrity": "sha512-qwHbBLV7WviBl0rQsOzH6o5lwyOIvwp/BdFnvVxXORldu5TmjFfjzBcWUWS5kWAZhmv+JtiDhSuQCp4sBfbIgg==", + "dev": true, + "requires": { + "agent-base": "4", + "debug": "3.1.0" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + } + } + }, + "http-proxy-middleware": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-0.18.0.tgz", + "integrity": "sha512-Fs25KVMPAIIcgjMZkVHJoKg9VcXcC1C8yb9JUgeDvVXY0S/zgVIhMb+qVswDIgtJe2DfckMSY2d6TuTEutlk6Q==", + "dev": true, + "requires": { + "http-proxy": "^1.16.2", + "is-glob": "^4.0.0", + "lodash": "^4.17.5", + "micromatch": "^3.1.9" + } + }, + "http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", + "dev": true, + "requires": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + } + }, + "https-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", + "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=", + "dev": true + }, + "https-proxy-agent": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.1.tgz", + "integrity": "sha512-HPCTS1LW51bcyMYbxUIOO4HEOlQ1/1qRaFWcyxvwaqUS9TY88aoEuHUY33kuAh1YhVVaDQhLZsnPd+XNARWZlQ==", + "dev": true, + "requires": { + "agent-base": "^4.1.0", + "debug": "^3.1.0" + }, + "dependencies": { + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "dev": true + } + } + }, + "humanize-ms": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", + "integrity": "sha1-xG4xWaKT9riW2ikxbYtv6Lt5u+0=", + "dev": true, + "requires": { + "ms": "^2.0.0" + } + }, + "iconv-lite": { + "version": "0.4.23", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz", + "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==", + "dev": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "ieee754": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.12.tgz", + "integrity": "sha512-GguP+DRY+pJ3soyIiGPTvdiVXjZ+DbXOxGpXn3eMvNW4x4irjqXm4wHKscC+TfxSJ0yw/S1F24tqdMNsMZTiLA==", + "dev": true + }, + "iferr": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/iferr/-/iferr-0.1.5.tgz", + "integrity": "sha1-xg7taebY/bazEEofy8ocGS3FtQE=", + "dev": true + }, + "ignore": { + "version": "3.3.10", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.10.tgz", + "integrity": "sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug==", + "dev": true + }, + "ignore-walk": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.1.tgz", + "integrity": "sha512-DTVlMx3IYPe0/JJcYP7Gxg7ttZZu3IInhuEhbchuqneY9wWe5Ojy2mXLBaQFUQmo0AW2r3qG7m1mg86js+gnlQ==", + "dev": true, + "requires": { + "minimatch": "^3.0.4" + } + }, + "image-size": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/image-size/-/image-size-0.5.5.tgz", + "integrity": "sha1-Cd/Uq50g4p6xw+gLiZA3jfnjy5w=", + "dev": true, + "optional": true + }, + "immediate": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", + "integrity": "sha1-nbHb0Pr43m++D13V5Wu2BigN5ps=", + "dev": true + }, + "import-cwd": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/import-cwd/-/import-cwd-2.1.0.tgz", + "integrity": "sha1-qmzzbnInYShcs3HsZRn1PiQ1sKk=", + "dev": true, + "requires": { + "import-from": "^2.1.0" + } + }, + "import-from": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/import-from/-/import-from-2.1.0.tgz", + "integrity": "sha1-M1238qev/VOqpHHUuAId7ja387E=", + "dev": true, + "requires": { + "resolve-from": "^3.0.0" + } + }, + "import-local": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-2.0.0.tgz", + "integrity": "sha512-b6s04m3O+s3CGSbqDIyP4R6aAwAeYlVq9+WUWep6iHa8ETRf9yei1U48C5MmfJmV9AiLYYBKPMq/W+/WRpQmCQ==", + "dev": true, + "requires": { + "pkg-dir": "^3.0.0", + "resolve-cwd": "^2.0.0" + }, + "dependencies": { + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "requires": { + "locate-path": "^3.0.0" + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "p-limit": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.1.0.tgz", + "integrity": "sha512-NhURkNcrVB+8hNfLuysU8enY5xn2KXphsHBaC2YmRNTZRc7RWusw6apSpdEj3jo4CMb6W9nrF6tTnsJsJeyu6g==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "requires": { + "p-limit": "^2.0.0" + } + }, + "p-try": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.0.0.tgz", + "integrity": "sha512-hMp0onDKIajHfIkdRk3P4CdCmErkYAxxDtP3Wx/4nZ3aGlau2VKh3mZpcuFkH27WQkL/3WBCPOktzA9ZOAnMQQ==", + "dev": true + }, + "pkg-dir": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", + "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", + "dev": true, + "requires": { + "find-up": "^3.0.0" + } + } + } + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "dev": true + }, + "in-publish": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/in-publish/-/in-publish-2.0.0.tgz", + "integrity": "sha1-4g/146KvwmkDILbcVSaCqcf631E=", + "dev": true, + "optional": true + }, + "indent-string": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz", + "integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=", + "dev": true, + "optional": true, + "requires": { + "repeating": "^2.0.0" + } + }, + "indexof": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz", + "integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10=", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true + }, + "ini": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", + "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", + "dev": true + }, + "internal-ip": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/internal-ip/-/internal-ip-3.0.1.tgz", + "integrity": "sha512-NXXgESC2nNVtU+pqmC9e6R8B1GpKxzsAQhffvh5AL79qKnodd+L7tnEQmTiUAVngqLalPbSqRA7XGIEL5nCd0Q==", + "dev": true, + "requires": { + "default-gateway": "^2.6.0", + "ipaddr.js": "^1.5.2" + } + }, + "invariant": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", + "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", + "dev": true, + "requires": { + "loose-envify": "^1.0.0" + } + }, + "invert-kv": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", + "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=", + "dev": true, + "optional": true + }, + "ip": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz", + "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=", + "dev": true + }, + "ip-regex": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz", + "integrity": "sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk=", + "dev": true + }, + "ipaddr.js": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.8.0.tgz", + "integrity": "sha1-6qM9bd16zo9/b+DJygRA5wZzix4=", + "dev": true + }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", + "dev": true + }, + "is-binary-path": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", + "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", + "dev": true, + "requires": { + "binary-extensions": "^1.0.0" + } + }, + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true + }, + "is-callable": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.4.tgz", + "integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==", + "dev": true + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-date-object": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz", + "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=", + "dev": true + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + }, + "dependencies": { + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true + } + } + }, + "is-directory": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/is-directory/-/is-directory-0.3.1.tgz", + "integrity": "sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE=", + "dev": true + }, + "is-dotfile": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-dotfile/-/is-dotfile-1.0.3.tgz", + "integrity": "sha1-pqLzL/0t+wT1yiXs0Pa4PPeYoeE=", + "dev": true + }, + "is-equal-shallow": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz", + "integrity": "sha1-IjgJj8Ih3gvPpdnqxMRdY4qhxTQ=", + "dev": true, + "requires": { + "is-primitive": "^2.0.0" + } + }, + "is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "dev": true + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "dev": true + }, + "is-finite": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.2.tgz", + "integrity": "sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko=", + "dev": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "dev": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "is-glob": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.0.tgz", + "integrity": "sha1-lSHHaEXMJhCoUgPd8ICpWML/q8A=", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-path-cwd": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-1.0.0.tgz", + "integrity": "sha1-0iXsIxMuie3Tj9p2dHLmLmXxEG0=", + "dev": true + }, + "is-path-in-cwd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-1.0.1.tgz", + "integrity": "sha512-FjV1RTW48E7CWM7eE/J2NJvAEEVektecDBVBE5Hh3nM1Jd0kvhHtX68Pr3xsDf857xt3Y4AkwVULK1Vku62aaQ==", + "dev": true, + "requires": { + "is-path-inside": "^1.0.0" + } + }, + "is-path-inside": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.1.tgz", + "integrity": "sha1-jvW33lBDej/cprToZe96pVy0gDY=", + "dev": true, + "requires": { + "path-is-inside": "^1.0.1" + } + }, + "is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dev": true, + "requires": { + "isobject": "^3.0.1" + } + }, + "is-posix-bracket": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz", + "integrity": "sha1-MzTceXdDaOkvAW5vvAqI9c1ua8Q=", + "dev": true + }, + "is-primitive": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-primitive/-/is-primitive-2.0.0.tgz", + "integrity": "sha1-IHurkWOEmcB7Kt8kCkGochADRXU=", + "dev": true + }, + "is-regex": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz", + "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=", + "dev": true, + "requires": { + "has": "^1.0.1" + } + }, + "is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", + "dev": true + }, + "is-symbol": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.2.tgz", + "integrity": "sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw==", + "dev": true, + "requires": { + "has-symbols": "^1.0.0" + } + }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", + "dev": true + }, + "is-utf8": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", + "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=", + "dev": true + }, + "is-windows": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", + "dev": true + }, + "is-wsl": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", + "integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=", + "dev": true + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "isbinaryfile": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-3.0.3.tgz", + "integrity": "sha512-8cJBL5tTd2OS0dM4jz07wQd5g0dCCqIhUxPIGtZfa5L6hWlvV5MHTITy/DBAsF+Oe2LS1X3krBUhNwaGUWpWxw==", + "dev": true, + "requires": { + "buffer-alloc": "^1.2.0" + } + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true + }, + "isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", + "dev": true + }, + "istanbul": { + "version": "0.4.5", + "resolved": "https://registry.npmjs.org/istanbul/-/istanbul-0.4.5.tgz", + "integrity": "sha1-ZcfXPUxNqE1POsMQuRj7C4Azczs=", + "dev": true, + "requires": { + "abbrev": "1.0.x", + "async": "1.x", + "escodegen": "1.8.x", + "esprima": "2.7.x", + "glob": "^5.0.15", + "handlebars": "^4.0.1", + "js-yaml": "3.x", + "mkdirp": "0.5.x", + "nopt": "3.x", + "once": "1.x", + "resolve": "1.1.x", + "supports-color": "^3.1.0", + "which": "^1.1.1", + "wordwrap": "^1.0.0" + }, + "dependencies": { + "glob": { + "version": "5.0.15", + "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz", + "integrity": "sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E=", + "dev": true, + "requires": { + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "2 || 3", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", + "dev": true + }, + "supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "dev": true, + "requires": { + "has-flag": "^1.0.0" + } + } + } + }, + "istanbul-api": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/istanbul-api/-/istanbul-api-2.1.1.tgz", + "integrity": "sha512-kVmYrehiwyeBAk/wE71tW6emzLiHGjYIiDrc8sfyty4F8M02/lrgXSm+R1kXysmF20zArvmZXjlE/mg24TVPJw==", + "dev": true, + "requires": { + "async": "^2.6.1", + "compare-versions": "^3.2.1", + "fileset": "^2.0.3", + "istanbul-lib-coverage": "^2.0.3", + "istanbul-lib-hook": "^2.0.3", + "istanbul-lib-instrument": "^3.1.0", + "istanbul-lib-report": "^2.0.4", + "istanbul-lib-source-maps": "^3.0.2", + "istanbul-reports": "^2.1.1", + "js-yaml": "^3.12.0", + "make-dir": "^1.3.0", + "minimatch": "^3.0.4", + "once": "^1.4.0" + }, + "dependencies": { + "async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.2.tgz", + "integrity": "sha512-H1qVYh1MYhEEFLsP97cVKqCGo7KfCyTt6uEWqsTBr9SO84oK9Uwbyd/yCW+6rKJLHksBNUVWZDAjfS+Ccx0Bbg==", + "dev": true, + "requires": { + "lodash": "^4.17.11" + } + }, + "istanbul-lib-coverage": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz", + "integrity": "sha512-dKWuzRGCs4G+67VfW9pBFFz2Jpi4vSp/k7zBcJ888ofV5Mi1g5CUML5GvMvV6u9Cjybftu+E8Cgp+k0dI1E5lw==", + "dev": true + }, + "istanbul-lib-instrument": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-3.1.0.tgz", + "integrity": "sha512-ooVllVGT38HIk8MxDj/OIHXSYvH+1tq/Vb38s8ixt9GoJadXska4WkGY+0wkmtYCZNYtaARniH/DixUGGLZ0uA==", + "dev": true, + "requires": { + "@babel/generator": "^7.0.0", + "@babel/parser": "^7.0.0", + "@babel/template": "^7.0.0", + "@babel/traverse": "^7.0.0", + "@babel/types": "^7.0.0", + "istanbul-lib-coverage": "^2.0.3", + "semver": "^5.5.0" + } + } + } + }, + "istanbul-instrumenter-loader": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-instrumenter-loader/-/istanbul-instrumenter-loader-3.0.1.tgz", + "integrity": "sha512-a5SPObZgS0jB/ixaKSMdn6n/gXSrK2S6q/UfRJBT3e6gQmVjwZROTODQsYW5ZNwOu78hG62Y3fWlebaVOL0C+w==", + "dev": true, + "requires": { + "convert-source-map": "^1.5.0", + "istanbul-lib-instrument": "^1.7.3", + "loader-utils": "^1.1.0", + "schema-utils": "^0.3.0" + }, + "dependencies": { + "ajv": { + "version": "5.5.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", + "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=", + "dev": true, + "requires": { + "co": "^4.6.0", + "fast-deep-equal": "^1.0.0", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.3.0" + } + }, + "schema-utils": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-0.3.0.tgz", + "integrity": "sha1-9YdyIs4+kx7a4DnxfrNxbnE3+M8=", + "dev": true, + "requires": { + "ajv": "^5.0.0" + } + } + } + }, + "istanbul-lib-coverage": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-1.2.1.tgz", + "integrity": "sha512-PzITeunAgyGbtY1ibVIUiV679EFChHjoMNRibEIobvmrCRaIgwLxNucOSimtNWUhEib/oO7QY2imD75JVgCJWQ==", + "dev": true + }, + "istanbul-lib-hook": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-2.0.3.tgz", + "integrity": "sha512-CLmEqwEhuCYtGcpNVJjLV1DQyVnIqavMLFHV/DP+np/g3qvdxu3gsPqYoJMXm15sN84xOlckFB3VNvRbf5yEgA==", + "dev": true, + "requires": { + "append-transform": "^1.0.0" + } + }, + "istanbul-lib-instrument": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-1.10.2.tgz", + "integrity": "sha512-aWHxfxDqvh/ZlxR8BBaEPVSWDPUkGD63VjGQn3jcw8jCp7sHEMKcrj4xfJn/ABzdMEHiQNyvDQhqm5o8+SQg7A==", + "dev": true, + "requires": { + "babel-generator": "^6.18.0", + "babel-template": "^6.16.0", + "babel-traverse": "^6.18.0", + "babel-types": "^6.18.0", + "babylon": "^6.18.0", + "istanbul-lib-coverage": "^1.2.1", + "semver": "^5.3.0" + } + }, + "istanbul-lib-report": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-2.0.4.tgz", + "integrity": "sha512-sOiLZLAWpA0+3b5w5/dq0cjm2rrNdAfHWaGhmn7XEFW6X++IV9Ohn+pnELAl9K3rfpaeBfbmH9JU5sejacdLeA==", + "dev": true, + "requires": { + "istanbul-lib-coverage": "^2.0.3", + "make-dir": "^1.3.0", + "supports-color": "^6.0.0" + }, + "dependencies": { + "istanbul-lib-coverage": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz", + "integrity": "sha512-dKWuzRGCs4G+67VfW9pBFFz2Jpi4vSp/k7zBcJ888ofV5Mi1g5CUML5GvMvV6u9Cjybftu+E8Cgp+k0dI1E5lw==", + "dev": true + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "istanbul-lib-source-maps": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-3.0.2.tgz", + "integrity": "sha512-JX4v0CiKTGp9fZPmoxpu9YEkPbEqCqBbO3403VabKjH+NRXo72HafD5UgnjTEqHL2SAjaZK1XDuDOkn6I5QVfQ==", + "dev": true, + "requires": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^2.0.3", + "make-dir": "^1.3.0", + "rimraf": "^2.6.2", + "source-map": "^0.6.1" + }, + "dependencies": { + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "istanbul-lib-coverage": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz", + "integrity": "sha512-dKWuzRGCs4G+67VfW9pBFFz2Jpi4vSp/k7zBcJ888ofV5Mi1g5CUML5GvMvV6u9Cjybftu+E8Cgp+k0dI1E5lw==", + "dev": true + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "istanbul-reports": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-2.1.1.tgz", + "integrity": "sha512-FzNahnidyEPBCI0HcufJoSEoKykesRlFcSzQqjH9x0+LC8tnnE/p/90PBLu8iZTxr8yYZNyTtiAujUqyN+CIxw==", + "dev": true, + "requires": { + "handlebars": "^4.1.0" + } + }, + "jasmine": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/jasmine/-/jasmine-2.8.0.tgz", + "integrity": "sha1-awicChFXax8W3xG4AUbZHU6Lij4=", + "dev": true, + "requires": { + "exit": "^0.1.2", + "glob": "^7.0.6", + "jasmine-core": "~2.8.0" + }, + "dependencies": { + "jasmine-core": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-2.8.0.tgz", + "integrity": "sha1-vMl5rh+f0FcB5F5S5l06XWPxok4=", + "dev": true + } + } + }, + "jasmine-core": { + "version": "2.99.1", + "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-2.99.1.tgz", + "integrity": "sha1-5kAN8ea1bhMLYcS80JPap/boyhU=", + "dev": true + }, + "jasmine-diff": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/jasmine-diff/-/jasmine-diff-0.1.3.tgz", + "integrity": "sha1-k8zC3MQQKMXd1GBlWAdIOfLe6qg=", + "dev": true, + "requires": { + "diff": "^3.2.0" + } + }, + "jasmine-spec-reporter": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/jasmine-spec-reporter/-/jasmine-spec-reporter-4.2.1.tgz", + "integrity": "sha512-FZBoZu7VE5nR7Nilzy+Np8KuVIOxF4oXDPDknehCYBDE080EnlPu0afdZNmpGDBRCUBv3mj5qgqCRmk6W/K8vg==", + "dev": true, + "requires": { + "colors": "1.1.2" + } + }, + "jasminewd2": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/jasminewd2/-/jasminewd2-2.2.0.tgz", + "integrity": "sha1-43zwsX8ZnM4jvqcbIDk5Uka07E4=", + "dev": true + }, + "js-base64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.5.1.tgz", + "integrity": "sha512-M7kLczedRMYX4L8Mdh4MzyAMM9O5osx+4FcOQuTvr3A9F2D9S5JXheN0ewNbrvK2UatkTRhL5ejGmGSjNMiZuw==", + "dev": true, + "optional": true + }, + "js-tokens": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", + "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=", + "dev": true + }, + "js-yaml": { + "version": "3.12.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.12.1.tgz", + "integrity": "sha512-um46hB9wNOKlwkHgiuyEVAybXBjwFUV0Z/RaHJblRd9DXltue9FTYvzCr9ErQrK9Adz5MU4gHWVaNUfdmrC8qA==", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "dependencies": { + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true + } + } + }, + "jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", + "dev": true + }, + "jsesc": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-1.3.0.tgz", + "integrity": "sha1-RsP+yMGJKxKwgz25vHYiF226s0s=", + "dev": true + }, + "json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", + "dev": true + }, + "json-schema": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", + "dev": true + }, + "json-schema-traverse": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", + "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=", + "dev": true + }, + "json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", + "dev": true + }, + "json3": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/json3/-/json3-3.3.2.tgz", + "integrity": "sha1-PAQ0dD35Pi9cQq7nsZvLSDV19OE=", + "dev": true + }, + "json5": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", + "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=", + "dev": true + }, + "jsonparse": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", + "integrity": "sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA=", + "dev": true + }, + "jsprim": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", + "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", + "dev": true, + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.2.3", + "verror": "1.10.0" + } + }, + "jszip": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.1.5.tgz", + "integrity": "sha512-5W8NUaFRFRqTOL7ZDDrx5qWHJyBXy6velVudIzQUSoqAAYqzSh2Z7/m0Rf1QbmQJccegD0r+YZxBjzqoBiEeJQ==", + "dev": true, + "requires": { + "core-js": "~2.3.0", + "es6-promise": "~3.0.2", + "lie": "~3.1.0", + "pako": "~1.0.2", + "readable-stream": "~2.0.6" + }, + "dependencies": { + "core-js": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.3.0.tgz", + "integrity": "sha1-+rg/uwstjchfpjbEudNMdUIMbWU=", + "dev": true + }, + "es6-promise": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.0.2.tgz", + "integrity": "sha1-AQ1YWEI6XxGJeWZfRkhqlcbuK7Y=", + "dev": true + }, + "process-nextick-args": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", + "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=", + "dev": true + }, + "readable-stream": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.6.tgz", + "integrity": "sha1-j5A0HmilPMySh4jaz80Rs265t44=", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "~1.0.0", + "process-nextick-args": "~1.0.6", + "string_decoder": "~0.10.x", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", + "dev": true + } + } + }, + "karma": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/karma/-/karma-3.0.0.tgz", + "integrity": "sha512-ZTjyuDXVXhXsvJ1E4CnZzbCjSxD6sEdzEsFYogLuZM0yqvg/mgz+O+R1jb0J7uAQeuzdY8kJgx6hSNXLwFuHIQ==", + "dev": true, + "requires": { + "bluebird": "^3.3.0", + "body-parser": "^1.16.1", + "chokidar": "^2.0.3", + "colors": "^1.1.0", + "combine-lists": "^1.0.0", + "connect": "^3.6.0", + "core-js": "^2.2.0", + "di": "^0.0.1", + "dom-serialize": "^2.2.0", + "expand-braces": "^0.1.1", + "glob": "^7.1.1", + "graceful-fs": "^4.1.2", + "http-proxy": "^1.13.0", + "isbinaryfile": "^3.0.0", + "lodash": "^4.17.4", + "log4js": "^3.0.0", + "mime": "^2.3.1", + "minimatch": "^3.0.2", + "optimist": "^0.6.1", + "qjobs": "^1.1.4", + "range-parser": "^1.2.0", + "rimraf": "^2.6.0", + "safe-buffer": "^5.0.1", + "socket.io": "2.1.1", + "source-map": "^0.6.1", + "tmp": "0.0.33", + "useragent": "2.2.1" + }, + "dependencies": { + "mime": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.0.tgz", + "integrity": "sha512-ikBcWwyqXQSHKtciCcctu9YfPbFYZ4+gbHEmE0Q8jzcTYQg5dHCr3g2wwAZjPoJfQVXZq6KXAjpXOTf5/cjT7w==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "karma-chrome-launcher": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/karma-chrome-launcher/-/karma-chrome-launcher-2.2.0.tgz", + "integrity": "sha512-uf/ZVpAabDBPvdPdveyk1EPgbnloPvFFGgmRhYLTDH7gEB4nZdSBk8yTU47w1g/drLSx5uMOkjKk7IWKfWg/+w==", + "dev": true, + "requires": { + "fs-access": "^1.0.0", + "which": "^1.2.1" + } + }, + "karma-coverage-istanbul-reporter": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/karma-coverage-istanbul-reporter/-/karma-coverage-istanbul-reporter-2.0.4.tgz", + "integrity": "sha512-xJS7QSQIVU6VK9HuJ/ieE5yynxKhjCCkd96NLY/BX/HXsx0CskU9JJiMQbd4cHALiddMwI4OWh1IIzeWrsavJw==", + "dev": true, + "requires": { + "istanbul-api": "^2.0.5", + "minimatch": "^3.0.4" + } + }, + "karma-jasmine": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/karma-jasmine/-/karma-jasmine-1.1.2.tgz", + "integrity": "sha1-OU8rJf+0pkS5rabyLUQ+L9CIhsM=", + "dev": true + }, + "karma-jasmine-html-reporter": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/karma-jasmine-html-reporter/-/karma-jasmine-html-reporter-0.2.2.tgz", + "integrity": "sha1-SKjl7xiAdhfuK14zwRlMNbQ5Ukw=", + "dev": true, + "requires": { + "karma-jasmine": "^1.0.2" + } + }, + "karma-source-map-support": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/karma-source-map-support/-/karma-source-map-support-1.3.0.tgz", + "integrity": "sha512-HcPqdAusNez/ywa+biN4EphGz62MmQyPggUsDfsHqa7tSe4jdsxgvTKuDfIazjL+IOxpVWyT7Pr4dhAV+sxX5Q==", + "dev": true, + "requires": { + "source-map-support": "^0.5.5" + } + }, + "killable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/killable/-/killable-1.0.1.tgz", + "integrity": "sha512-LzqtLKlUwirEUyl/nicirVmNiPvYs7l5n8wOPP7fyJVpUPkvCnW/vuiXGpylGUlnPDnB7311rARzAt3Mhswpjg==", + "dev": true + }, + "kind-of": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", + "dev": true + }, + "lcid": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", + "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", + "dev": true, + "optional": true, + "requires": { + "invert-kv": "^1.0.0" + } + }, + "less": { + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/less/-/less-3.8.1.tgz", + "integrity": "sha512-8HFGuWmL3FhQR0aH89escFNBQH/nEiYPP2ltDFdQw2chE28Yx2E3lhAIq9Y2saYwLSwa699s4dBVEfCY8Drf7Q==", + "dev": true, + "requires": { + "clone": "^2.1.2", + "errno": "^0.1.1", + "graceful-fs": "^4.1.2", + "image-size": "~0.5.0", + "mime": "^1.4.1", + "mkdirp": "^0.5.0", + "promise": "^7.1.1", + "request": "^2.83.0", + "source-map": "~0.6.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "optional": true + } + } + }, + "less-loader": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/less-loader/-/less-loader-4.1.0.tgz", + "integrity": "sha512-KNTsgCE9tMOM70+ddxp9yyt9iHqgmSs0yTZc5XH5Wo+g80RWRIYNqE58QJKm/yMud5wZEvz50ugRDuzVIkyahg==", + "dev": true, + "requires": { + "clone": "^2.1.1", + "loader-utils": "^1.1.0", + "pify": "^3.0.0" + } + }, + "levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", + "dev": true, + "requires": { + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" + } + }, + "license-webpack-plugin": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/license-webpack-plugin/-/license-webpack-plugin-1.5.0.tgz", + "integrity": "sha512-Of/H79rZqm2aeg4RnP9SMSh19qkKemoLT5VaJV58uH5AxeYWEcBgGFs753JEJ/Hm6BPvQVfIlrrjoBwYj8p7Tw==", + "dev": true, + "requires": { + "ejs": "^2.5.7" + } + }, + "lie": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.1.1.tgz", + "integrity": "sha1-mkNrLMd0bKWd56QfpGmz77dr2H4=", + "dev": true, + "requires": { + "immediate": "~3.0.5" + } + }, + "load-json-file": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", + "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^2.2.0", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0", + "strip-bom": "^2.0.0" + }, + "dependencies": { + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + } + } + }, + "loader-runner": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-2.4.0.tgz", + "integrity": "sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw==", + "dev": true + }, + "loader-utils": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.1.0.tgz", + "integrity": "sha1-yYrvSIvM7aL/teLeZG1qdUQp9c0=", + "dev": true, + "requires": { + "big.js": "^3.1.3", + "emojis-list": "^2.0.0", + "json5": "^0.5.0" + } + }, + "locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "dev": true, + "requires": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + } + }, + "lodash": { + "version": "4.17.11", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", + "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==", + "dev": true + }, + "lodash.assign": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.assign/-/lodash.assign-4.2.0.tgz", + "integrity": "sha1-DZnzzNem0mHRm9rrkkUAXShYCOc=", + "dev": true, + "optional": true + }, + "lodash.clonedeep": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", + "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=", + "dev": true + }, + "lodash.debounce": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168=", + "dev": true + }, + "lodash.mergewith": { + "version": "4.6.1", + "resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.1.tgz", + "integrity": "sha512-eWw5r+PYICtEBgrBE5hhlT6aAa75f411bgDz/ZL2KZqYV03USvucsxcHUIlGTDTECs1eunpI7HOV7U+WLDvNdQ==", + "dev": true, + "optional": true + }, + "lodash.tail": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.tail/-/lodash.tail-4.1.1.tgz", + "integrity": "sha1-0jM6NtnncXyK0vfKyv7HwytERmQ=", + "dev": true + }, + "log4js": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/log4js/-/log4js-3.0.6.tgz", + "integrity": "sha512-ezXZk6oPJCWL483zj64pNkMuY/NcRX5MPiB0zE6tjZM137aeusrOnW1ecxgF9cmwMWkBMhjteQxBPoZBh9FDxQ==", + "dev": true, + "requires": { + "circular-json": "^0.5.5", + "date-format": "^1.2.0", + "debug": "^3.1.0", + "rfdc": "^1.1.2", + "streamroller": "0.7.0" + }, + "dependencies": { + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "dev": true + } + } + }, + "loglevel": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.6.1.tgz", + "integrity": "sha1-4PyVEztu8nbNyIh82vJKpvFW+Po=", + "dev": true + }, + "long": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/long/-/long-3.2.0.tgz", + "integrity": "sha1-2CG3E4yhy1gcFymQ7xTbIAtcR0s=", + "dev": true + }, + "loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "dev": true, + "requires": { + "js-tokens": "^3.0.0 || ^4.0.0" + } + }, + "loud-rejection": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz", + "integrity": "sha1-W0b4AUft7leIcPCG0Eghz5mOVR8=", + "dev": true, + "optional": true, + "requires": { + "currently-unhandled": "^0.4.1", + "signal-exit": "^3.0.0" + } + }, + "lower-case": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-1.1.4.tgz", + "integrity": "sha1-miyr0bno4K6ZOkv31YdcOcQujqw=", + "dev": true + }, + "lru-cache": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", + "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", + "dev": true, + "requires": { + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" + } + }, + "make-dir": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz", + "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==", + "dev": true, + "requires": { + "pify": "^3.0.0" + } + }, + "make-error": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.5.tgz", + "integrity": "sha512-c3sIjNUow0+8swNwVpqoH4YCShKNFkMaw6oH1mNS2haDZQqkeZFlHS3dhoeEbKKmJB4vXpJucU6oH75aDYeE9g==", + "dev": true + }, + "make-fetch-happen": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-4.0.1.tgz", + "integrity": "sha512-7R5ivfy9ilRJ1EMKIOziwrns9fGeAD4bAha8EB7BIiBBLHm2KeTUGCrICFt2rbHfzheTLynv50GnNTK1zDTrcQ==", + "dev": true, + "requires": { + "agentkeepalive": "^3.4.1", + "cacache": "^11.0.1", + "http-cache-semantics": "^3.8.1", + "http-proxy-agent": "^2.1.0", + "https-proxy-agent": "^2.2.1", + "lru-cache": "^4.1.2", + "mississippi": "^3.0.0", + "node-fetch-npm": "^2.0.2", + "promise-retry": "^1.1.1", + "socks-proxy-agent": "^4.0.0", + "ssri": "^6.0.0" + }, + "dependencies": { + "cacache": { + "version": "11.3.2", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-11.3.2.tgz", + "integrity": "sha512-E0zP4EPGDOaT2chM08Als91eYnf8Z+eH1awwwVsngUmgppfM5jjJ8l3z5vO5p5w/I3LsiXawb1sW0VY65pQABg==", + "dev": true, + "requires": { + "bluebird": "^3.5.3", + "chownr": "^1.1.1", + "figgy-pudding": "^3.5.1", + "glob": "^7.1.3", + "graceful-fs": "^4.1.15", + "lru-cache": "^5.1.1", + "mississippi": "^3.0.0", + "mkdirp": "^0.5.1", + "move-concurrently": "^1.0.1", + "promise-inflight": "^1.0.1", + "rimraf": "^2.6.2", + "ssri": "^6.0.1", + "unique-filename": "^1.1.1", + "y18n": "^4.0.0" + }, + "dependencies": { + "lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "requires": { + "yallist": "^3.0.2" + } + } + } + }, + "mississippi": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-3.0.0.tgz", + "integrity": "sha512-x471SsVjUtBRtcvd4BzKE9kFC+/2TeWgKCgw0bZcw1b9l2X3QX5vCWgF+KaZaYm87Ss//rHnWryupDrgLvmSkA==", + "dev": true, + "requires": { + "concat-stream": "^1.5.0", + "duplexify": "^3.4.2", + "end-of-stream": "^1.1.0", + "flush-write-stream": "^1.0.0", + "from2": "^2.1.0", + "parallel-transform": "^1.1.0", + "pump": "^3.0.0", + "pumpify": "^1.3.3", + "stream-each": "^1.1.0", + "through2": "^2.0.0" + } + }, + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "ssri": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.1.tgz", + "integrity": "sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA==", + "dev": true, + "requires": { + "figgy-pudding": "^3.5.1" + } + }, + "yallist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.3.tgz", + "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==", + "dev": true + } + } + }, + "mamacro": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/mamacro/-/mamacro-0.0.3.tgz", + "integrity": "sha512-qMEwh+UujcQ+kbz3T6V+wAmO2U8veoq2w+3wY8MquqwVA3jChfwY+Tk52GZKDfACEPjuZ7r2oJLejwpt8jtwTA==", + "dev": true + }, + "map-age-cleaner": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz", + "integrity": "sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==", + "dev": true, + "requires": { + "p-defer": "^1.0.0" + } + }, + "map-cache": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", + "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", + "dev": true + }, + "map-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", + "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=", + "dev": true + }, + "map-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", + "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", + "dev": true, + "requires": { + "object-visit": "^1.0.0" + } + }, + "math-random": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/math-random/-/math-random-1.0.4.tgz", + "integrity": "sha512-rUxjysqif/BZQH2yhd5Aaq7vXMSx9NdEsQcyA07uEzIvxgI7zIr33gGsh+RU0/XjmQpCW7RsVof1vlkvQVCK5A==", + "dev": true + }, + "md5.js": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", + "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", + "dev": true, + "requires": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", + "dev": true + }, + "mem": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/mem/-/mem-4.1.0.tgz", + "integrity": "sha512-I5u6Q1x7wxO0kdOpYBB28xueHADYps5uty/zg936CiG8NTe5sJL8EjrCuLneuDW3PlMdZBGDIn8BirEVdovZvg==", + "dev": true, + "requires": { + "map-age-cleaner": "^0.1.1", + "mimic-fn": "^1.0.0", + "p-is-promise": "^2.0.0" + } + }, + "memory-fs": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz", + "integrity": "sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=", + "dev": true, + "requires": { + "errno": "^0.1.3", + "readable-stream": "^2.0.1" + } + }, + "meow": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz", + "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=", + "dev": true, + "optional": true, + "requires": { + "camelcase-keys": "^2.0.0", + "decamelize": "^1.1.2", + "loud-rejection": "^1.0.0", + "map-obj": "^1.0.1", + "minimist": "^1.1.3", + "normalize-package-data": "^2.3.4", + "object-assign": "^4.0.1", + "read-pkg-up": "^1.0.1", + "redent": "^1.0.0", + "trim-newlines": "^1.0.0" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "dev": true, + "optional": true + } + } + }, + "merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=", + "dev": true + }, + "methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", + "dev": true + }, + "micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "dev": true, + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + } + }, + "miller-rabin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", + "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", + "dev": true, + "requires": { + "bn.js": "^4.0.0", + "brorand": "^1.0.1" + } + }, + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true + }, + "mime-db": { + "version": "1.38.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.38.0.tgz", + "integrity": "sha512-bqVioMFFzc2awcdJZIzR3HjZFX20QhilVS7hytkKrv7xFAn8bM1gzc/FOX2awLISvWe0PV8ptFKcon+wZ5qYkg==", + "dev": true + }, + "mime-types": { + "version": "2.1.22", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.22.tgz", + "integrity": "sha512-aGl6TZGnhm/li6F7yx82bJiBZwgiEa4Hf6CNr8YO+r5UHr53tSTYZb102zyU50DOWWKeOv0uQLRL0/9EiKWCog==", + "dev": true, + "requires": { + "mime-db": "~1.38.0" + } + }, + "mimic-fn": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", + "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", + "dev": true + }, + "mini-css-extract-plugin": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-0.4.4.tgz", + "integrity": "sha512-o+Jm+ocb0asEngdM6FsZWtZsRzA8koFUudIDwYUfl94M3PejPHG7Vopw5hN9V8WsMkSFpm3tZP3Fesz89EyrfQ==", + "dev": true, + "requires": { + "loader-utils": "^1.1.0", + "schema-utils": "^1.0.0", + "webpack-sources": "^1.1.0" + }, + "dependencies": { + "schema-utils": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", + "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", + "dev": true, + "requires": { + "ajv": "^6.1.0", + "ajv-errors": "^1.0.0", + "ajv-keywords": "^3.1.0" + } + } + } + }, + "minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", + "dev": true + }, + "minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=", + "dev": true + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", + "dev": true + }, + "minipass": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.3.5.tgz", + "integrity": "sha512-Gi1W4k059gyRbyVUZQ4mEqLm0YIUiGYfvxhF6SIlk3ui1WVxMTGfGdQ2SInh3PDrRTVvPKgULkpJtT4RH10+VA==", + "dev": true, + "requires": { + "safe-buffer": "^5.1.2", + "yallist": "^3.0.0" + }, + "dependencies": { + "yallist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.3.tgz", + "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==", + "dev": true + } + } + }, + "minizlib": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.2.1.tgz", + "integrity": "sha512-7+4oTUOWKg7AuL3vloEWekXY2/D20cevzsrNT2kGWm+39J9hGTCBv8VI5Pm5lXZ/o3/mdR4f8rflAPhnQb8mPA==", + "dev": true, + "requires": { + "minipass": "^2.2.1" + } + }, + "mississippi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-2.0.0.tgz", + "integrity": "sha512-zHo8v+otD1J10j/tC+VNoGK9keCuByhKovAvdn74dmxJl9+mWHnx6EMsDN4lgRoMI/eYo2nchAxniIbUPb5onw==", + "dev": true, + "requires": { + "concat-stream": "^1.5.0", + "duplexify": "^3.4.2", + "end-of-stream": "^1.1.0", + "flush-write-stream": "^1.0.0", + "from2": "^2.1.0", + "parallel-transform": "^1.1.0", + "pump": "^2.0.1", + "pumpify": "^1.3.3", + "stream-each": "^1.1.0", + "through2": "^2.0.0" + } + }, + "mixin-deep": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.1.tgz", + "integrity": "sha512-8ZItLHeEgaqEvd5lYBXfm4EZSFCX29Jb9K+lAHhDKzReKBQKj3R+7NOF6tjqYi9t4oI8VUfaWITJQm86wnXGNQ==", + "dev": true, + "requires": { + "for-in": "^1.0.2", + "is-extendable": "^1.0.1" + }, + "dependencies": { + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dev": true, + "requires": { + "is-plain-object": "^2.0.4" + } + } + } + }, + "mixin-object": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mixin-object/-/mixin-object-2.0.1.tgz", + "integrity": "sha1-T7lJRB2rGCVA8f4DW6YOGUel5X4=", + "dev": true, + "requires": { + "for-in": "^0.1.3", + "is-extendable": "^0.1.1" + }, + "dependencies": { + "for-in": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/for-in/-/for-in-0.1.8.tgz", + "integrity": "sha1-2Hc5COMSVhCZUrH9ubP6hn0ndeE=", + "dev": true + } + } + }, + "mkdirp": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "dev": true, + "requires": { + "minimist": "0.0.8" + } + }, + "move-concurrently": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz", + "integrity": "sha1-viwAX9oy4LKa8fBdfEszIUxwH5I=", + "dev": true, + "requires": { + "aproba": "^1.1.1", + "copy-concurrently": "^1.0.0", + "fs-write-stream-atomic": "^1.0.8", + "mkdirp": "^0.5.1", + "rimraf": "^2.5.4", + "run-queue": "^1.0.3" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "multicast-dns": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-6.2.3.tgz", + "integrity": "sha512-ji6J5enbMyGRHIAkAOu3WdV8nggqviKCEKtXcOqfphZZtQrmHKycfynJ2V7eVPUA4NhJ6V7Wf4TmGbTwKE9B6g==", + "dev": true, + "requires": { + "dns-packet": "^1.3.1", + "thunky": "^1.0.2" + } + }, + "multicast-dns-service-types": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz", + "integrity": "sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE=", + "dev": true + }, + "nan": { + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.12.1.tgz", + "integrity": "sha512-JY7V6lRkStKcKTvHO5NVSQRv+RV+FIL5pvDoLiAtSL9pKlC5x9PKQcZDsq7m4FO4d57mkhC6Z+QhAh3Jdk5JFw==", + "dev": true, + "optional": true + }, + "nanomatch": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", + "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", + "dev": true, + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "fragment-cache": "^0.2.1", + "is-windows": "^1.0.2", + "kind-of": "^6.0.2", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + } + }, + "negotiator": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz", + "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=", + "dev": true + }, + "neo-async": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.0.tgz", + "integrity": "sha512-MFh0d/Wa7vkKO3Y3LlacqAEeHK0mckVqzDieUKTT+KGxi+zIpeVsFxymkIiRpbpDziHc290Xr9A1O4Om7otoRA==", + "dev": true + }, + "nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", + "dev": true + }, + "no-case": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/no-case/-/no-case-2.3.2.tgz", + "integrity": "sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ==", + "dev": true, + "requires": { + "lower-case": "^1.1.1" + } + }, + "node-fetch-npm": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/node-fetch-npm/-/node-fetch-npm-2.0.2.tgz", + "integrity": "sha512-nJIxm1QmAj4v3nfCvEeCrYSoVwXyxLnaPBK5W1W5DGEJwjlKuC2VEUycGw5oxk+4zZahRrB84PUJJgEmhFTDFw==", + "dev": true, + "requires": { + "encoding": "^0.1.11", + "json-parse-better-errors": "^1.0.0", + "safe-buffer": "^5.1.1" + } + }, + "node-forge": { + "version": "0.7.5", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.7.5.tgz", + "integrity": "sha512-MmbQJ2MTESTjt3Gi/3yG1wGpIMhUfcIypUCGtTizFR9IiccFwxSpfp0vtIZlkFclEqERemxfnSdZEMR9VqqEFQ==", + "dev": true + }, + "node-gyp": { + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-3.8.0.tgz", + "integrity": "sha512-3g8lYefrRRzvGeSowdJKAKyks8oUpLEd/DyPV4eMhVlhJ0aNaZqIrNUIPuEWWTAoPqyFkfGrM67MC69baqn6vA==", + "dev": true, + "optional": true, + "requires": { + "fstream": "^1.0.0", + "glob": "^7.0.3", + "graceful-fs": "^4.1.2", + "mkdirp": "^0.5.0", + "nopt": "2 || 3", + "npmlog": "0 || 1 || 2 || 3 || 4", + "osenv": "0", + "request": "^2.87.0", + "rimraf": "2", + "semver": "~5.3.0", + "tar": "^2.0.0", + "which": "1" + }, + "dependencies": { + "semver": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz", + "integrity": "sha1-myzl094C0XxgEq0yaqa00M9U+U8=", + "dev": true, + "optional": true + } + } + }, + "node-libs-browser": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.2.0.tgz", + "integrity": "sha512-5MQunG/oyOaBdttrL40dA7bUfPORLRWMUJLQtMg7nluxUvk5XwnLdL9twQHFAjRx/y7mIMkLKT9++qPbbk6BZA==", + "dev": true, + "requires": { + "assert": "^1.1.1", + "browserify-zlib": "^0.2.0", + "buffer": "^4.3.0", + "console-browserify": "^1.1.0", + "constants-browserify": "^1.0.0", + "crypto-browserify": "^3.11.0", + "domain-browser": "^1.1.1", + "events": "^3.0.0", + "https-browserify": "^1.0.0", + "os-browserify": "^0.3.0", + "path-browserify": "0.0.0", + "process": "^0.11.10", + "punycode": "^1.2.4", + "querystring-es3": "^0.2.0", + "readable-stream": "^2.3.3", + "stream-browserify": "^2.0.1", + "stream-http": "^2.7.2", + "string_decoder": "^1.0.0", + "timers-browserify": "^2.0.4", + "tty-browserify": "0.0.0", + "url": "^0.11.0", + "util": "^0.11.0", + "vm-browserify": "0.0.4" + }, + "dependencies": { + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", + "dev": true + } + } + }, + "node-sass": { + "version": "4.11.0", + "resolved": "https://registry.npmjs.org/node-sass/-/node-sass-4.11.0.tgz", + "integrity": "sha512-bHUdHTphgQJZaF1LASx0kAviPH7sGlcyNhWade4eVIpFp6tsn7SV8xNMTbsQFpEV9VXpnwTTnNYlfsZXgGgmkA==", + "dev": true, + "optional": true, + "requires": { + "async-foreach": "^0.1.3", + "chalk": "^1.1.1", + "cross-spawn": "^3.0.0", + "gaze": "^1.0.0", + "get-stdin": "^4.0.1", + "glob": "^7.0.3", + "in-publish": "^2.0.0", + "lodash.assign": "^4.2.0", + "lodash.clonedeep": "^4.3.2", + "lodash.mergewith": "^4.6.0", + "meow": "^3.7.0", + "mkdirp": "^0.5.1", + "nan": "^2.10.0", + "node-gyp": "^3.8.0", + "npmlog": "^4.0.0", + "request": "^2.88.0", + "sass-graph": "^2.2.4", + "stdout-stream": "^1.4.0", + "true-case-path": "^1.0.2" + }, + "dependencies": { + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true, + "optional": true + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "optional": true, + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true, + "optional": true + } + } + }, + "nopt": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz", + "integrity": "sha1-xkZdvwirzU2zWTF/eaxopkayj/k=", + "dev": true, + "requires": { + "abbrev": "1" + } + }, + "normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "requires": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + }, + "dependencies": { + "resolve": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.10.0.tgz", + "integrity": "sha512-3sUr9aq5OfSg2S9pNtPA9hL1FVEAjvfOC4leW0SNf/mpnaakz2a9femSd6LqAww2RaFctwyf1lCqnTHuF1rxDg==", + "dev": true, + "requires": { + "path-parse": "^1.0.6" + } + } + } + }, + "normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", + "dev": true, + "requires": { + "remove-trailing-separator": "^1.0.1" + } + }, + "normalize-range": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", + "integrity": "sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=", + "dev": true + }, + "npm-bundled": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.0.6.tgz", + "integrity": "sha512-8/JCaftHwbd//k6y2rEWp6k1wxVfpFzB6t1p825+cUb7Ym2XQfhwIC5KwhrvzZRJu+LtDE585zVaS32+CGtf0g==", + "dev": true + }, + "npm-package-arg": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-6.1.0.tgz", + "integrity": "sha512-zYbhP2k9DbJhA0Z3HKUePUgdB1x7MfIfKssC+WLPFMKTBZKpZh5m13PgexJjCq6KW7j17r0jHWcCpxEqnnncSA==", + "dev": true, + "requires": { + "hosted-git-info": "^2.6.0", + "osenv": "^0.1.5", + "semver": "^5.5.0", + "validate-npm-package-name": "^3.0.0" + } + }, + "npm-packlist": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.3.0.tgz", + "integrity": "sha512-qPBc6CnxEzpOcc4bjoIBJbYdy0D/LFFPUdxvfwor4/w3vxeE0h6TiOVurCEPpQ6trjN77u/ShyfeJGsbAfB3dA==", + "dev": true, + "requires": { + "ignore-walk": "^3.0.1", + "npm-bundled": "^1.0.1" + } + }, + "npm-pick-manifest": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-2.2.3.tgz", + "integrity": "sha512-+IluBC5K201+gRU85vFlUwX3PFShZAbAgDNp2ewJdWMVSppdo/Zih0ul2Ecky/X7b51J7LrrUAP+XOmOCvYZqA==", + "dev": true, + "requires": { + "figgy-pudding": "^3.5.1", + "npm-package-arg": "^6.0.0", + "semver": "^5.4.1" + } + }, + "npm-registry-client": { + "version": "8.6.0", + "resolved": "https://registry.npmjs.org/npm-registry-client/-/npm-registry-client-8.6.0.tgz", + "integrity": "sha512-Qs6P6nnopig+Y8gbzpeN/dkt+n7IyVd8f45NTMotGk6Qo7GfBmzwYx6jRLoOOgKiMnaQfYxsuyQlD8Mc3guBhg==", + "dev": true, + "requires": { + "concat-stream": "^1.5.2", + "graceful-fs": "^4.1.6", + "normalize-package-data": "~1.0.1 || ^2.0.0", + "npm-package-arg": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0", + "npmlog": "2 || ^3.1.0 || ^4.0.0", + "once": "^1.3.3", + "request": "^2.74.0", + "retry": "^0.10.0", + "safe-buffer": "^5.1.1", + "semver": "2 >=2.2.1 || 3.x || 4 || 5", + "slide": "^1.1.3", + "ssri": "^5.2.4" + } + }, + "npm-registry-fetch": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-3.9.0.tgz", + "integrity": "sha512-srwmt8YhNajAoSAaDWndmZgx89lJwIZ1GWxOuckH4Coek4uHv5S+o/l9FLQe/awA+JwTnj4FJHldxhlXdZEBmw==", + "dev": true, + "requires": { + "JSONStream": "^1.3.4", + "bluebird": "^3.5.1", + "figgy-pudding": "^3.4.1", + "lru-cache": "^4.1.3", + "make-fetch-happen": "^4.0.1", + "npm-package-arg": "^6.1.0" + } + }, + "npm-run-path": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", + "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", + "dev": true, + "requires": { + "path-key": "^2.0.0" + } + }, + "npmlog": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", + "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", + "dev": true, + "requires": { + "are-we-there-yet": "~1.1.2", + "console-control-strings": "~1.1.0", + "gauge": "~2.7.3", + "set-blocking": "~2.0.0" + } + }, + "nth-check": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz", + "integrity": "sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==", + "dev": true, + "requires": { + "boolbase": "~1.0.0" + } + }, + "null-check": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/null-check/-/null-check-1.0.0.tgz", + "integrity": "sha1-l33/1xdgErnsMNKjnbXPcqBDnt0=", + "dev": true + }, + "num2fraction": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/num2fraction/-/num2fraction-1.2.2.tgz", + "integrity": "sha1-b2gragJ6Tp3fpFZM0lidHU5mnt4=", + "dev": true + }, + "number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", + "dev": true + }, + "oauth-sign": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", + "dev": true + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "dev": true + }, + "object-component": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/object-component/-/object-component-0.0.3.tgz", + "integrity": "sha1-8MaapQ78lbhmwYb0AKM3acsvEpE=", + "dev": true + }, + "object-copy": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", + "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", + "dev": true, + "requires": { + "copy-descriptor": "^0.1.0", + "define-property": "^0.2.5", + "kind-of": "^3.0.3" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "object-keys": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.0.tgz", + "integrity": "sha512-6OO5X1+2tYkNyNEx6TsCxEqFfRWaqx6EtMiSbGrw8Ob8v9Ne+Hl8rBAgLBZn5wjEz3s/s6U1WXFUFOcxxAwUpg==", + "dev": true + }, + "object-visit": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", + "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", + "dev": true, + "requires": { + "isobject": "^3.0.0" + } + }, + "object.getownpropertydescriptors": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz", + "integrity": "sha1-h1jIRvW0B62rDyNuCYbxSwUcqhY=", + "dev": true, + "requires": { + "define-properties": "^1.1.2", + "es-abstract": "^1.5.1" + } + }, + "object.omit": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/object.omit/-/object.omit-2.0.1.tgz", + "integrity": "sha1-Gpx0SCnznbuFjHbKNXmuKlTr0fo=", + "dev": true, + "requires": { + "for-own": "^0.1.4", + "is-extendable": "^0.1.1" + }, + "dependencies": { + "for-own": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/for-own/-/for-own-0.1.5.tgz", + "integrity": "sha1-UmXGgaTylNq78XyVCbZ2OqhFEM4=", + "dev": true, + "requires": { + "for-in": "^1.0.1" + } + } + } + }, + "object.pick": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", + "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", + "dev": true, + "requires": { + "isobject": "^3.0.1" + } + }, + "obuf": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", + "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==", + "dev": true + }, + "on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "dev": true, + "requires": { + "ee-first": "1.1.1" + } + }, + "on-headers": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.1.tgz", + "integrity": "sha1-ko9dD0cNSTQmUepnlLCFfBAGk/c=", + "dev": true + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "opn": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/opn/-/opn-5.4.0.tgz", + "integrity": "sha512-YF9MNdVy/0qvJvDtunAOzFw9iasOQHpVthTCvGzxt61Il64AYSGdK+rYwld7NAfk9qJ7dt+hymBNSc9LNYS+Sw==", + "dev": true, + "requires": { + "is-wsl": "^1.1.0" + } + }, + "optimist": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", + "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=", + "dev": true, + "requires": { + "minimist": "~0.0.1", + "wordwrap": "~0.0.2" + }, + "dependencies": { + "wordwrap": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz", + "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc=", + "dev": true + } + } + }, + "optionator": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz", + "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=", + "dev": true, + "requires": { + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.4", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "wordwrap": "~1.0.0" + } + }, + "original": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/original/-/original-1.0.2.tgz", + "integrity": "sha512-hyBVl6iqqUOJ8FqRe+l/gS8H+kKYjrEndd5Pm1MfBtsEKA038HkkdbAl/72EAXGyonD/PFsvmVG+EvcIpliMBg==", + "dev": true, + "requires": { + "url-parse": "^1.4.3" + } + }, + "os-browserify": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", + "integrity": "sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=", + "dev": true + }, + "os-homedir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", + "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", + "dev": true + }, + "os-locale": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", + "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=", + "dev": true, + "optional": true, + "requires": { + "lcid": "^1.0.0" + } + }, + "os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", + "dev": true + }, + "osenv": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", + "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", + "dev": true, + "requires": { + "os-homedir": "^1.0.0", + "os-tmpdir": "^1.0.0" + } + }, + "p-defer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz", + "integrity": "sha1-n26xgvbJqozXQwBKfU+WsZaw+ww=", + "dev": true + }, + "p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", + "dev": true + }, + "p-is-promise": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-2.0.0.tgz", + "integrity": "sha512-pzQPhYMCAgLAKPWD2jC3Se9fEfrD9npNos0y150EeqZll7akhEgGhTW/slB6lHku8AvYGiJ+YJ5hfHKePPgFWg==", + "dev": true + }, + "p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "requires": { + "p-try": "^1.0.0" + } + }, + "p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "dev": true, + "requires": { + "p-limit": "^1.1.0" + } + }, + "p-map": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-1.2.0.tgz", + "integrity": "sha512-r6zKACMNhjPJMTl8KcFH4li//gkrXWfbD6feV8l6doRHlzljFWGJ2AP6iKaCJXyZmAUMOPtvbW7EXkbWO/pLEA==", + "dev": true + }, + "p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", + "dev": true + }, + "pacote": { + "version": "9.2.3", + "resolved": "https://registry.npmjs.org/pacote/-/pacote-9.2.3.tgz", + "integrity": "sha512-Y3+yY3nBRAxMlZWvr62XLJxOwCmG9UmkGZkFurWHoCjqF0cZL72cTOCRJTvWw8T4OhJS2RTg13x4oYYriauvEw==", + "dev": true, + "requires": { + "bluebird": "^3.5.2", + "cacache": "^11.2.0", + "figgy-pudding": "^3.5.1", + "get-stream": "^4.1.0", + "glob": "^7.1.3", + "lru-cache": "^4.1.3", + "make-fetch-happen": "^4.0.1", + "minimatch": "^3.0.4", + "minipass": "^2.3.5", + "mississippi": "^3.0.0", + "mkdirp": "^0.5.1", + "normalize-package-data": "^2.4.0", + "npm-package-arg": "^6.1.0", + "npm-packlist": "^1.1.12", + "npm-pick-manifest": "^2.2.3", + "npm-registry-fetch": "^3.8.0", + "osenv": "^0.1.5", + "promise-inflight": "^1.0.1", + "promise-retry": "^1.1.1", + "protoduck": "^5.0.1", + "rimraf": "^2.6.2", + "safe-buffer": "^5.1.2", + "semver": "^5.6.0", + "ssri": "^6.0.1", + "tar": "^4.4.6", + "unique-filename": "^1.1.1", + "which": "^1.3.1" + }, + "dependencies": { + "cacache": { + "version": "11.3.2", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-11.3.2.tgz", + "integrity": "sha512-E0zP4EPGDOaT2chM08Als91eYnf8Z+eH1awwwVsngUmgppfM5jjJ8l3z5vO5p5w/I3LsiXawb1sW0VY65pQABg==", + "dev": true, + "requires": { + "bluebird": "^3.5.3", + "chownr": "^1.1.1", + "figgy-pudding": "^3.5.1", + "glob": "^7.1.3", + "graceful-fs": "^4.1.15", + "lru-cache": "^5.1.1", + "mississippi": "^3.0.0", + "mkdirp": "^0.5.1", + "move-concurrently": "^1.0.1", + "promise-inflight": "^1.0.1", + "rimraf": "^2.6.2", + "ssri": "^6.0.1", + "unique-filename": "^1.1.1", + "y18n": "^4.0.0" + }, + "dependencies": { + "lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "requires": { + "yallist": "^3.0.2" + } + } + } + }, + "get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "dev": true, + "requires": { + "pump": "^3.0.0" + } + }, + "mississippi": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-3.0.0.tgz", + "integrity": "sha512-x471SsVjUtBRtcvd4BzKE9kFC+/2TeWgKCgw0bZcw1b9l2X3QX5vCWgF+KaZaYm87Ss//rHnWryupDrgLvmSkA==", + "dev": true, + "requires": { + "concat-stream": "^1.5.0", + "duplexify": "^3.4.2", + "end-of-stream": "^1.1.0", + "flush-write-stream": "^1.0.0", + "from2": "^2.1.0", + "parallel-transform": "^1.1.0", + "pump": "^3.0.0", + "pumpify": "^1.3.3", + "stream-each": "^1.1.0", + "through2": "^2.0.0" + } + }, + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "ssri": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.1.tgz", + "integrity": "sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA==", + "dev": true, + "requires": { + "figgy-pudding": "^3.5.1" + } + }, + "tar": { + "version": "4.4.8", + "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.8.tgz", + "integrity": "sha512-LzHF64s5chPQQS0IYBn9IN5h3i98c12bo4NCO7e0sGM2llXQ3p2FGC5sdENN4cTW48O915Sh+x+EXx7XW96xYQ==", + "dev": true, + "requires": { + "chownr": "^1.1.1", + "fs-minipass": "^1.2.5", + "minipass": "^2.3.4", + "minizlib": "^1.1.1", + "mkdirp": "^0.5.0", + "safe-buffer": "^5.1.2", + "yallist": "^3.0.2" + } + }, + "yallist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.3.tgz", + "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==", + "dev": true + } + } + }, + "pako": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.8.tgz", + "integrity": "sha512-6i0HVbUfcKaTv+EG8ZTr75az7GFXcLYk9UyLEg7Notv/Ma+z/UG3TCoz6GiNeOrn1E/e63I0X/Hpw18jHOTUnA==", + "dev": true + }, + "parallel-transform": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/parallel-transform/-/parallel-transform-1.1.0.tgz", + "integrity": "sha1-1BDwZbBdojCB/NEPKIVMKb2jOwY=", + "dev": true, + "requires": { + "cyclist": "~0.2.2", + "inherits": "^2.0.3", + "readable-stream": "^2.1.5" + } + }, + "param-case": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/param-case/-/param-case-2.1.1.tgz", + "integrity": "sha1-35T9jPZTHs915r75oIWPvHK+Ikc=", + "dev": true, + "requires": { + "no-case": "^2.2.0" + } + }, + "parse-asn1": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.4.tgz", + "integrity": "sha512-Qs5duJcuvNExRfFZ99HDD3z4mAi3r9Wl/FOjEOijlxwCZs7E7mW2vjTpgQ4J8LpTF8x5v+1Vn5UQFejmWT11aw==", + "dev": true, + "requires": { + "asn1.js": "^4.0.0", + "browserify-aes": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.0", + "pbkdf2": "^3.0.3", + "safe-buffer": "^5.1.1" + } + }, + "parse-glob": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/parse-glob/-/parse-glob-3.0.4.tgz", + "integrity": "sha1-ssN2z7EfNVE7rdFz7wu246OIORw=", + "dev": true, + "requires": { + "glob-base": "^0.3.0", + "is-dotfile": "^1.0.0", + "is-extglob": "^1.0.0", + "is-glob": "^2.0.0" + }, + "dependencies": { + "is-extglob": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", + "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", + "dev": true + }, + "is-glob": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", + "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", + "dev": true, + "requires": { + "is-extglob": "^1.0.0" + } + } + } + }, + "parse-json": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", + "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", + "dev": true, + "requires": { + "error-ex": "^1.2.0" + } + }, + "parse5": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-4.0.0.tgz", + "integrity": "sha512-VrZ7eOd3T1Fk4XWNXMgiGBK/z0MG48BWG2uQNU4I72fkQuKUTZpl+u9k+CxEG0twMVzSmXEEz12z5Fnw1jIQFA==", + "dev": true + }, + "parseqs": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.5.tgz", + "integrity": "sha1-1SCKNzjkZ2bikbouoXNoSSGouJ0=", + "dev": true, + "requires": { + "better-assert": "~1.0.0" + } + }, + "parseuri": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.5.tgz", + "integrity": "sha1-gCBKUNTbt3m/3G6+J3jZDkvOMgo=", + "dev": true, + "requires": { + "better-assert": "~1.0.0" + } + }, + "parseurl": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz", + "integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M=", + "dev": true + }, + "pascalcase": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", + "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", + "dev": true + }, + "path-browserify": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.0.tgz", + "integrity": "sha1-oLhwcpquIUAFt9UDLsLLuw+0RRo=", + "dev": true + }, + "path-dirname": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", + "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=", + "dev": true + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, + "path-is-inside": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", + "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=", + "dev": true + }, + "path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", + "dev": true + }, + "path-parse": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", + "dev": true + }, + "path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=", + "dev": true + }, + "path-type": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", + "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", + "dev": true, + "requires": { + "pify": "^3.0.0" + } + }, + "pbkdf2": { + "version": "3.0.17", + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.17.tgz", + "integrity": "sha512-U/il5MsrZp7mGg3mSQfn742na2T+1/vHDCG5/iTI3X9MKUuYUZVLQhyRsg06mCgDBTd57TxzgZt7P+fYfjRLtA==", + "dev": true, + "requires": { + "create-hash": "^1.1.2", + "create-hmac": "^1.1.4", + "ripemd160": "^2.0.1", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", + "dev": true + }, + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "dev": true + }, + "pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", + "dev": true + }, + "pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", + "dev": true, + "requires": { + "pinkie": "^2.0.0" + } + }, + "pkg-dir": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz", + "integrity": "sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=", + "dev": true, + "requires": { + "find-up": "^2.1.0" + } + }, + "portfinder": { + "version": "1.0.19", + "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.19.tgz", + "integrity": "sha512-23aeQKW9KgHe6citUrG3r9HjeX6vls0h713TAa+CwTKZwNIr/pD2ApaxYF4Um3ZZyq4ar+Siv3+fhoHaIwSOSw==", + "dev": true, + "requires": { + "async": "^1.5.2", + "debug": "^2.2.0", + "mkdirp": "0.5.x" + } + }, + "posix-character-classes": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", + "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", + "dev": true + }, + "postcss": { + "version": "6.0.23", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.23.tgz", + "integrity": "sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag==", + "dev": true, + "requires": { + "chalk": "^2.4.1", + "source-map": "^0.6.1", + "supports-color": "^5.4.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "postcss-import": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-11.1.0.tgz", + "integrity": "sha512-5l327iI75POonjxkXgdRCUS+AlzAdBx4pOvMEhTKTCjb1p8IEeVR9yx3cPbmN7LIWJLbfnIXxAhoB4jpD0c/Cw==", + "dev": true, + "requires": { + "postcss": "^6.0.1", + "postcss-value-parser": "^3.2.3", + "read-cache": "^1.0.0", + "resolve": "^1.1.7" + } + }, + "postcss-load-config": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-2.0.0.tgz", + "integrity": "sha512-V5JBLzw406BB8UIfsAWSK2KSwIJ5yoEIVFb4gVkXci0QdKgA24jLmHZ/ghe/GgX0lJ0/D1uUK1ejhzEY94MChQ==", + "dev": true, + "requires": { + "cosmiconfig": "^4.0.0", + "import-cwd": "^2.0.0" + } + }, + "postcss-loader": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-2.1.6.tgz", + "integrity": "sha512-hgiWSc13xVQAq25cVw80CH0l49ZKlAnU1hKPOdRrNj89bokRr/bZF2nT+hebPPF9c9xs8c3gw3Fr2nxtmXYnNg==", + "dev": true, + "requires": { + "loader-utils": "^1.1.0", + "postcss": "^6.0.0", + "postcss-load-config": "^2.0.0", + "schema-utils": "^0.4.0" + } + }, + "postcss-url": { + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/postcss-url/-/postcss-url-7.3.2.tgz", + "integrity": "sha512-QMV5mA+pCYZQcUEPQkmor9vcPQ2MT+Ipuu8qdi1gVxbNiIiErEGft+eny1ak19qALoBkccS5AHaCaCDzh7b9MA==", + "dev": true, + "requires": { + "mime": "^1.4.1", + "minimatch": "^3.0.4", + "mkdirp": "^0.5.0", + "postcss": "^6.0.1", + "xxhashjs": "^0.2.1" + } + }, + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + }, + "prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", + "dev": true + }, + "preserve": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/preserve/-/preserve-0.2.0.tgz", + "integrity": "sha1-gV7R9uvGWSb4ZbMQwHE7yzMVzks=", + "dev": true + }, + "pretty-error": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-2.1.1.tgz", + "integrity": "sha1-X0+HyPkeWuPzuoerTPXgOxoX8aM=", + "dev": true, + "requires": { + "renderkid": "^2.0.1", + "utila": "~0.4" + } + }, + "process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=", + "dev": true + }, + "process-nextick-args": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", + "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", + "dev": true + }, + "promise": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", + "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", + "dev": true, + "optional": true, + "requires": { + "asap": "~2.0.3" + } + }, + "promise-inflight": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", + "integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=", + "dev": true + }, + "promise-retry": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-1.1.1.tgz", + "integrity": "sha1-ZznpaOMFHaIM5kl/srUPaRHfPW0=", + "dev": true, + "requires": { + "err-code": "^1.0.0", + "retry": "^0.10.0" + } + }, + "protoduck": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/protoduck/-/protoduck-5.0.1.tgz", + "integrity": "sha512-WxoCeDCoCBY55BMvj4cAEjdVUFGRWed9ZxPlqTKYyw1nDDTQ4pqmnIMAGfJlg7Dx35uB/M+PHJPTmGOvaCaPTg==", + "dev": true, + "requires": { + "genfun": "^5.0.0" + } + }, + "protractor": { + "version": "5.4.2", + "resolved": "https://registry.npmjs.org/protractor/-/protractor-5.4.2.tgz", + "integrity": "sha512-zlIj64Cr6IOWP7RwxVeD8O4UskLYPoyIcg0HboWJL9T79F1F0VWtKkGTr/9GN6BKL+/Q/GmM7C9kFVCfDbP5sA==", + "dev": true, + "requires": { + "@types/q": "^0.0.32", + "@types/selenium-webdriver": "^3.0.0", + "blocking-proxy": "^1.0.0", + "browserstack": "^1.5.1", + "chalk": "^1.1.3", + "glob": "^7.0.3", + "jasmine": "2.8.0", + "jasminewd2": "^2.1.0", + "optimist": "~0.6.0", + "q": "1.4.1", + "saucelabs": "^1.5.0", + "selenium-webdriver": "3.6.0", + "source-map-support": "~0.4.0", + "webdriver-js-extender": "2.1.0", + "webdriver-manager": "^12.0.6" + }, + "dependencies": { + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + } + }, + "del": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/del/-/del-2.2.2.tgz", + "integrity": "sha1-wSyYHQZ4RshLyvhiz/kw2Qf/0ag=", + "dev": true, + "requires": { + "globby": "^5.0.0", + "is-path-cwd": "^1.0.0", + "is-path-in-cwd": "^1.0.0", + "object-assign": "^4.0.1", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0", + "rimraf": "^2.2.8" + } + }, + "globby": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-5.0.0.tgz", + "integrity": "sha1-69hGZ8oNuzMLmbz8aOrCvFQ3Dg0=", + "dev": true, + "requires": { + "array-union": "^1.0.1", + "arrify": "^1.0.0", + "glob": "^7.0.3", + "object-assign": "^4.0.1", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + }, + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "dev": true + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + }, + "source-map-support": { + "version": "0.4.18", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.18.tgz", + "integrity": "sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA==", + "dev": true, + "requires": { + "source-map": "^0.5.6" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + }, + "webdriver-manager": { + "version": "12.1.1", + "resolved": "https://registry.npmjs.org/webdriver-manager/-/webdriver-manager-12.1.1.tgz", + "integrity": "sha512-L9TEQmZs6JbMMRQI1w60mfps265/NCr0toYJl7p/R2OAk6oXAfwI6jqYP7EWae+d7Ad2S2Aj4+rzxoSjqk3ZuA==", + "dev": true, + "requires": { + "adm-zip": "^0.4.9", + "chalk": "^1.1.1", + "del": "^2.2.0", + "glob": "^7.0.3", + "ini": "^1.3.4", + "minimist": "^1.2.0", + "q": "^1.4.1", + "request": "^2.87.0", + "rimraf": "^2.5.2", + "semver": "^5.3.0", + "xml2js": "^0.4.17" + } + } + } + }, + "proxy-addr": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.4.tgz", + "integrity": "sha512-5erio2h9jp5CHGwcybmxmVqHmnCBZeewlfJ0pex+UW7Qny7OOZXTtH56TGNyBizkgiOwhJtMKrVzDTeKcySZwA==", + "dev": true, + "requires": { + "forwarded": "~0.1.2", + "ipaddr.js": "1.8.0" + } + }, + "prr": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", + "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=", + "dev": true + }, + "pseudomap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", + "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", + "dev": true + }, + "psl": { + "version": "1.1.31", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.1.31.tgz", + "integrity": "sha512-/6pt4+C+T+wZUieKR620OpzN/LlnNKuWjy1iFLQ/UG35JqHlR/89MP1d96dUfkf6Dne3TuLQzOYEYshJ+Hx8mw==", + "dev": true + }, + "public-encrypt": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", + "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==", + "dev": true, + "requires": { + "bn.js": "^4.1.0", + "browserify-rsa": "^4.0.0", + "create-hash": "^1.1.0", + "parse-asn1": "^5.0.0", + "randombytes": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "pump": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz", + "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "pumpify": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.5.1.tgz", + "integrity": "sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==", + "dev": true, + "requires": { + "duplexify": "^3.6.0", + "inherits": "^2.0.3", + "pump": "^2.0.0" + } + }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true + }, + "q": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/q/-/q-1.4.1.tgz", + "integrity": "sha1-VXBbzZPF82c1MMLCy8DCs63cKG4=", + "dev": true + }, + "qjobs": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/qjobs/-/qjobs-1.2.0.tgz", + "integrity": "sha512-8YOJEHtxpySA3fFDyCRxA+UUV+fA+rTWnuWvylOK/NCjhY+b4ocCtmu8TtsWb+mYeU+GCHf/S66KZF/AsteKHg==", + "dev": true + }, + "qs": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", + "dev": true + }, + "querystring": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", + "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=", + "dev": true + }, + "querystring-es3": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz", + "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=", + "dev": true + }, + "querystringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.1.0.tgz", + "integrity": "sha512-sluvZZ1YiTLD5jsqZcDmFyV2EwToyXZBfpoVOmktMmW+VEnhgakFHnasVph65fOjGPTWN0Nw3+XQaSeMayr0kg==", + "dev": true + }, + "randomatic": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/randomatic/-/randomatic-3.1.1.tgz", + "integrity": "sha512-TuDE5KxZ0J461RVjrJZCJc+J+zCkTb1MbH9AQUq68sMhOMcy9jLcb3BrZKgp9q9Ncltdg4QVqWrH02W2EFFVYw==", + "dev": true, + "requires": { + "is-number": "^4.0.0", + "kind-of": "^6.0.0", + "math-random": "^1.0.1" + }, + "dependencies": { + "is-number": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz", + "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==", + "dev": true + } + } + }, + "randombytes": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.0.6.tgz", + "integrity": "sha512-CIQ5OFxf4Jou6uOKe9t1AOgqpeU5fd70A8NPdHSGeYXqXsPe6peOwI0cUl88RWZ6sP1vPMV3avd/R6cZ5/sP1A==", + "dev": true, + "requires": { + "safe-buffer": "^5.1.0" + } + }, + "randomfill": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz", + "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", + "dev": true, + "requires": { + "randombytes": "^2.0.5", + "safe-buffer": "^5.1.0" + } + }, + "range-parser": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", + "integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4=", + "dev": true + }, + "raw-body": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.3.tgz", + "integrity": "sha512-9esiElv1BrZoI3rCDuOuKCBRbuApGGaDPQfjSflGxdy4oyzqghxu6klEkkVIvBje+FF0BX9coEv8KqW6X/7njw==", + "dev": true, + "requires": { + "bytes": "3.0.0", + "http-errors": "1.6.3", + "iconv-lite": "0.4.23", + "unpipe": "1.0.0" + } + }, + "raw-loader": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/raw-loader/-/raw-loader-0.5.1.tgz", + "integrity": "sha1-DD0L6u2KAclm2Xh793goElKpeao=", + "dev": true + }, + "read-cache": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", + "integrity": "sha1-5mTvMRYRZsl1HNvo28+GtftY93Q=", + "dev": true, + "requires": { + "pify": "^2.3.0" + }, + "dependencies": { + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + } + } + }, + "read-pkg": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", + "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", + "dev": true, + "requires": { + "load-json-file": "^1.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^1.0.0" + }, + "dependencies": { + "path-type": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", + "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + } + } + }, + "read-pkg-up": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", + "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", + "dev": true, + "requires": { + "find-up": "^1.0.0", + "read-pkg": "^1.0.0" + }, + "dependencies": { + "find-up": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", + "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", + "dev": true, + "requires": { + "path-exists": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + }, + "path-exists": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", + "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", + "dev": true, + "requires": { + "pinkie-promise": "^2.0.0" + } + } + } + }, + "readable-stream": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "readdirp": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", + "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.11", + "micromatch": "^3.1.10", + "readable-stream": "^2.0.2" + } + }, + "redent": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz", + "integrity": "sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94=", + "dev": true, + "optional": true, + "requires": { + "indent-string": "^2.1.0", + "strip-indent": "^1.0.1" + } + }, + "reflect-metadata": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz", + "integrity": "sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==", + "dev": true + }, + "regenerate": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.0.tgz", + "integrity": "sha512-1G6jJVDWrt0rK99kBjvEtziZNCICAuvIPkSiUFIQxVP06RCVpq3dmDo2oi6ABpYaDYaTRr67BEhL8r1wgEZZKg==", + "dev": true + }, + "regenerator-runtime": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", + "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==", + "dev": true + }, + "regex-cache": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/regex-cache/-/regex-cache-0.4.4.tgz", + "integrity": "sha512-nVIZwtCjkC9YgvWkpM55B5rBhBYRZhAaJbgcFYXXsHnbZ9UZI9nnVWYZpBlCqv9ho2eZryPnWrZGsOdPwVWXWQ==", + "dev": true, + "requires": { + "is-equal-shallow": "^0.1.3" + } + }, + "regex-not": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", + "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", + "dev": true, + "requires": { + "extend-shallow": "^3.0.2", + "safe-regex": "^1.1.0" + } + }, + "regexpu-core": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-1.0.0.tgz", + "integrity": "sha1-hqdj9Y7k18L2sQLkdkBQ3n7ZDGs=", + "dev": true, + "requires": { + "regenerate": "^1.2.1", + "regjsgen": "^0.2.0", + "regjsparser": "^0.1.4" + } + }, + "regjsgen": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.2.0.tgz", + "integrity": "sha1-bAFq3qxVT3WCP+N6wFuS1aTtsfc=", + "dev": true + }, + "regjsparser": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.1.5.tgz", + "integrity": "sha1-fuj4Tcb6eS0/0K4ijSS9lJ6tIFw=", + "dev": true, + "requires": { + "jsesc": "~0.5.0" + }, + "dependencies": { + "jsesc": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", + "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=", + "dev": true + } + } + }, + "relateurl": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", + "integrity": "sha1-VNvzd+UUQKypCkzSdGANP/LYiKk=", + "dev": true + }, + "remove-trailing-separator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", + "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=", + "dev": true + }, + "renderkid": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-2.0.2.tgz", + "integrity": "sha512-FsygIxevi1jSiPY9h7vZmBFUbAOcbYm9UwyiLNdVsLRs/5We9Ob5NMPbGYUTWiLq5L+ezlVdE0A8bbME5CWTpg==", + "dev": true, + "requires": { + "css-select": "^1.1.0", + "dom-converter": "~0.2", + "htmlparser2": "~3.3.0", + "strip-ansi": "^3.0.0", + "utila": "^0.4.0" + } + }, + "repeat-element": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz", + "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==", + "dev": true + }, + "repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", + "dev": true + }, + "repeating": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz", + "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=", + "dev": true, + "requires": { + "is-finite": "^1.0.0" + } + }, + "request": { + "version": "2.88.0", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz", + "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==", + "dev": true, + "requires": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.0", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.4.3", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" + } + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "dev": true + }, + "require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true + }, + "require-main-filename": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", + "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=", + "dev": true + }, + "requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=", + "dev": true + }, + "resolve": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", + "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=", + "dev": true + }, + "resolve-cwd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-2.0.0.tgz", + "integrity": "sha1-AKn3OHVW4nA46uIyyqNypqWbZlo=", + "dev": true, + "requires": { + "resolve-from": "^3.0.0" + } + }, + "resolve-from": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", + "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=", + "dev": true + }, + "resolve-url": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", + "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", + "dev": true + }, + "ret": { + "version": "0.1.15", + "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", + "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", + "dev": true + }, + "retry": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.10.1.tgz", + "integrity": "sha1-52OI0heZLCUnUCQdPTlW/tmNj/Q=", + "dev": true + }, + "rfdc": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.1.2.tgz", + "integrity": "sha512-92ktAgvZhBzYTIK0Mja9uen5q5J3NRVMoDkJL2VMwq6SXjVCgqvQeVP2XAaUY6HT+XpQYeLSjb3UoitBryKmdA==", + "dev": true + }, + "rimraf": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", + "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "ripemd160": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", + "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", + "dev": true, + "requires": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1" + } + }, + "run-queue": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/run-queue/-/run-queue-1.0.3.tgz", + "integrity": "sha1-6Eg5bwV9Ij8kOGkkYY4laUFh7Ec=", + "dev": true, + "requires": { + "aproba": "^1.1.1" + } + }, + "rxjs": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.2.2.tgz", + "integrity": "sha512-0MI8+mkKAXZUF9vMrEoPnaoHkfzBPP4IGwUYRJhIRJF6/w3uByO1e91bEHn8zd43RdkTMKiooYKmwz7RH6zfOQ==", + "requires": { + "tslib": "^1.9.0" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "safe-regex": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", + "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", + "dev": true, + "requires": { + "ret": "~0.1.10" + } + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true + }, + "sass-graph": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/sass-graph/-/sass-graph-2.2.4.tgz", + "integrity": "sha1-E/vWPNHK8JCLn9k0dq1DpR0eC0k=", + "dev": true, + "optional": true, + "requires": { + "glob": "^7.0.0", + "lodash": "^4.0.0", + "scss-tokenizer": "^0.2.3", + "yargs": "^7.0.0" + } + }, + "sass-loader": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-7.1.0.tgz", + "integrity": "sha512-+G+BKGglmZM2GUSfT9TLuEp6tzehHPjAMoRRItOojWIqIGPloVCMhNIQuG639eJ+y033PaGTSjLaTHts8Kw79w==", + "dev": true, + "requires": { + "clone-deep": "^2.0.1", + "loader-utils": "^1.0.1", + "lodash.tail": "^4.1.1", + "neo-async": "^2.5.0", + "pify": "^3.0.0", + "semver": "^5.5.0" + } + }, + "saucelabs": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/saucelabs/-/saucelabs-1.5.0.tgz", + "integrity": "sha512-jlX3FGdWvYf4Q3LFfFWS1QvPg3IGCGWxIc8QBFdPTbpTJnt/v17FHXYVAn7C8sHf1yUXo2c7yIM0isDryfYtHQ==", + "dev": true, + "requires": { + "https-proxy-agent": "^2.2.1" + } + }, + "sax": { + "version": "0.5.8", + "resolved": "https://registry.npmjs.org/sax/-/sax-0.5.8.tgz", + "integrity": "sha1-1HLbIo6zMcJQaw6MFVJK25OdEsE=", + "dev": true + }, + "schema-utils": { + "version": "0.4.7", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-0.4.7.tgz", + "integrity": "sha512-v/iwU6wvwGK8HbU9yi3/nhGzP0yGSuhQMzL6ySiec1FSrZZDkhm4noOSWzrNFo/jEc+SJY6jRTwuwbSXJPDUnQ==", + "dev": true, + "requires": { + "ajv": "^6.1.0", + "ajv-keywords": "^3.1.0" + } + }, + "scss-tokenizer": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/scss-tokenizer/-/scss-tokenizer-0.2.3.tgz", + "integrity": "sha1-jrBtualyMzOCTT9VMGQRSYR85dE=", + "dev": true, + "optional": true, + "requires": { + "js-base64": "^2.1.8", + "source-map": "^0.4.2" + }, + "dependencies": { + "source-map": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", + "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=", + "dev": true, + "optional": true, + "requires": { + "amdefine": ">=0.0.4" + } + } + } + }, + "select-hose": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", + "integrity": "sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo=", + "dev": true + }, + "selenium-webdriver": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/selenium-webdriver/-/selenium-webdriver-3.6.0.tgz", + "integrity": "sha512-WH7Aldse+2P5bbFBO4Gle/nuQOdVwpHMTL6raL3uuBj/vPG07k6uzt3aiahu352ONBr5xXh0hDlM3LhtXPOC4Q==", + "dev": true, + "requires": { + "jszip": "^3.1.3", + "rimraf": "^2.5.4", + "tmp": "0.0.30", + "xml2js": "^0.4.17" + }, + "dependencies": { + "tmp": { + "version": "0.0.30", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.30.tgz", + "integrity": "sha1-ckGdSovn1s51FI/YsyTlk6cRwu0=", + "dev": true, + "requires": { + "os-tmpdir": "~1.0.1" + } + } + } + }, + "selfsigned": { + "version": "1.10.4", + "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-1.10.4.tgz", + "integrity": "sha512-9AukTiDmHXGXWtWjembZ5NDmVvP2695EtpgbCsxCa68w3c88B+alqbmZ4O3hZ4VWGXeGWzEVdvqgAJD8DQPCDw==", + "dev": true, + "requires": { + "node-forge": "0.7.5" + } + }, + "semver": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz", + "integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==", + "dev": true + }, + "semver-dsl": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/semver-dsl/-/semver-dsl-1.0.1.tgz", + "integrity": "sha1-02eN5VVeimH2Ke7QJTZq5fJzQKA=", + "dev": true, + "requires": { + "semver": "^5.3.0" + } + }, + "semver-intersect": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/semver-intersect/-/semver-intersect-1.4.0.tgz", + "integrity": "sha512-d8fvGg5ycKAq0+I6nfWeCx6ffaWJCsBYU0H2Rq56+/zFePYfT8mXkB3tWBSjR5BerkHNZ5eTPIk1/LBYas35xQ==", + "dev": true, + "requires": { + "semver": "^5.0.0" + } + }, + "send": { + "version": "0.16.2", + "resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz", + "integrity": "sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw==", + "dev": true, + "requires": { + "debug": "2.6.9", + "depd": "~1.1.2", + "destroy": "~1.0.4", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "~1.6.2", + "mime": "1.4.1", + "ms": "2.0.0", + "on-finished": "~2.3.0", + "range-parser": "~1.2.0", + "statuses": "~1.4.0" + }, + "dependencies": { + "mime": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz", + "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==", + "dev": true + } + } + }, + "serialize-javascript": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-1.6.1.tgz", + "integrity": "sha512-A5MOagrPFga4YaKQSWHryl7AXvbQkEqpw4NNYMTNYUNV51bA8ABHgYFpqKx+YFFrw59xMV1qGH1R4AgoNIVgCw==", + "dev": true + }, + "serve-index": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", + "integrity": "sha1-03aNabHn2C5c4FD/9bRTvqEqkjk=", + "dev": true, + "requires": { + "accepts": "~1.3.4", + "batch": "0.6.1", + "debug": "2.6.9", + "escape-html": "~1.0.3", + "http-errors": "~1.6.2", + "mime-types": "~2.1.17", + "parseurl": "~1.3.2" + } + }, + "serve-static": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.2.tgz", + "integrity": "sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw==", + "dev": true, + "requires": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.2", + "send": "0.16.2" + } + }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", + "dev": true + }, + "set-value": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.0.tgz", + "integrity": "sha512-hw0yxk9GT/Hr5yJEYnHNKYXkIA8mVJgd9ditYZCe16ZczcaELYYcfvaXesNACk2O8O0nTiPQcQhGUQj8JLzeeg==", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-extendable": "^0.1.1", + "is-plain-object": "^2.0.3", + "split-string": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=", + "dev": true + }, + "setprototypeof": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==", + "dev": true + }, + "sha.js": { + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "shallow-clone": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-1.0.0.tgz", + "integrity": "sha512-oeXreoKR/SyNJtRJMAKPDSvd28OqEwG4eR/xc856cRGBII7gX9lvAqDxusPm0846z/w/hWYjI1NpKwJ00NHzRA==", + "dev": true, + "requires": { + "is-extendable": "^0.1.1", + "kind-of": "^5.0.0", + "mixin-object": "^2.0.1" + }, + "dependencies": { + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true + } + } + }, + "shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "dev": true, + "requires": { + "shebang-regex": "^1.0.0" + } + }, + "shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", + "dev": true + }, + "signal-exit": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", + "dev": true + }, + "slash": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz", + "integrity": "sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=", + "dev": true + }, + "slide": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/slide/-/slide-1.1.6.tgz", + "integrity": "sha1-VusCfWW00tzmyy4tMsTUr8nh1wc=", + "dev": true + }, + "smart-buffer": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.0.2.tgz", + "integrity": "sha512-JDhEpTKzXusOqXZ0BUIdH+CjFdO/CR3tLlf5CN34IypI+xMmXW1uB16OOY8z3cICbJlDAVJzNbwBhNO0wt9OAw==", + "dev": true + }, + "snapdragon": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", + "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", + "dev": true, + "requires": { + "base": "^0.11.1", + "debug": "^2.2.0", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "map-cache": "^0.2.2", + "source-map": "^0.5.6", + "source-map-resolve": "^0.5.0", + "use": "^3.1.0" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "snapdragon-node": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", + "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", + "dev": true, + "requires": { + "define-property": "^1.0.0", + "isobject": "^3.0.0", + "snapdragon-util": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "snapdragon-util": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", + "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", + "dev": true, + "requires": { + "kind-of": "^3.2.0" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "socket.io": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-2.1.1.tgz", + "integrity": "sha512-rORqq9c+7W0DAK3cleWNSyfv/qKXV99hV4tZe+gGLfBECw3XEhBy7x85F3wypA9688LKjtwO9pX9L33/xQI8yA==", + "dev": true, + "requires": { + "debug": "~3.1.0", + "engine.io": "~3.2.0", + "has-binary2": "~1.0.2", + "socket.io-adapter": "~1.1.0", + "socket.io-client": "2.1.1", + "socket.io-parser": "~3.2.0" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + } + } + }, + "socket.io-adapter": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-1.1.1.tgz", + "integrity": "sha1-KoBeihTWNyEk3ZFZrUUC+MsH8Gs=", + "dev": true + }, + "socket.io-client": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-2.1.1.tgz", + "integrity": "sha512-jxnFyhAuFxYfjqIgduQlhzqTcOEQSn+OHKVfAxWaNWa7ecP7xSNk2Dx/3UEsDcY7NcFafxvNvKPmmO7HTwTxGQ==", + "dev": true, + "requires": { + "backo2": "1.0.2", + "base64-arraybuffer": "0.1.5", + "component-bind": "1.0.0", + "component-emitter": "1.2.1", + "debug": "~3.1.0", + "engine.io-client": "~3.2.0", + "has-binary2": "~1.0.2", + "has-cors": "1.1.0", + "indexof": "0.0.1", + "object-component": "0.0.3", + "parseqs": "0.0.5", + "parseuri": "0.0.5", + "socket.io-parser": "~3.2.0", + "to-array": "0.1.4" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + } + } + }, + "socket.io-parser": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.2.0.tgz", + "integrity": "sha512-FYiBx7rc/KORMJlgsXysflWx/RIvtqZbyGLlHZvjfmPTPeuD/I8MaW7cfFrj5tRltICJdgwflhfZ3NVVbVLFQA==", + "dev": true, + "requires": { + "component-emitter": "1.2.1", + "debug": "~3.1.0", + "isarray": "2.0.1" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "isarray": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz", + "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4=", + "dev": true + } + } + }, + "sockjs": { + "version": "0.3.19", + "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.19.tgz", + "integrity": "sha512-V48klKZl8T6MzatbLlzzRNhMepEys9Y4oGFpypBFFn1gLI/QQ9HtLLyWJNbPlwGLelOVOEijUbTTJeLLI59jLw==", + "dev": true, + "requires": { + "faye-websocket": "^0.10.0", + "uuid": "^3.0.1" + } + }, + "sockjs-client": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/sockjs-client/-/sockjs-client-1.3.0.tgz", + "integrity": "sha512-R9jxEzhnnrdxLCNln0xg5uGHqMnkhPSTzUZH2eXcR03S/On9Yvoq2wyUZILRUhZCNVu2PmwWVoyuiPz8th8zbg==", + "dev": true, + "requires": { + "debug": "^3.2.5", + "eventsource": "^1.0.7", + "faye-websocket": "~0.11.1", + "inherits": "^2.0.3", + "json3": "^3.3.2", + "url-parse": "^1.4.3" + }, + "dependencies": { + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "faye-websocket": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.1.tgz", + "integrity": "sha1-8O/hjE9W5PQK/H4Gxxn9XuYYjzg=", + "dev": true, + "requires": { + "websocket-driver": ">=0.5.1" + } + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "dev": true + } + } + }, + "socks": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.2.3.tgz", + "integrity": "sha512-+2r83WaRT3PXYoO/1z+RDEBE7Z2f9YcdQnJ0K/ncXXbV5gJ6wYfNAebYFYiiUjM6E4JyXnPY8cimwyvFYHVUUA==", + "dev": true, + "requires": { + "ip": "^1.1.5", + "smart-buffer": "4.0.2" + } + }, + "socks-proxy-agent": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-4.0.1.tgz", + "integrity": "sha512-Kezx6/VBguXOsEe5oU3lXYyKMi4+gva72TwJ7pQY5JfqUx2nMk7NXA6z/mpNqIlfQjWYVfeuNvQjexiTaTn6Nw==", + "dev": true, + "requires": { + "agent-base": "~4.2.0", + "socks": "~2.2.0" + } + }, + "source-list-map": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz", + "integrity": "sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==", + "dev": true + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + }, + "source-map-loader": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/source-map-loader/-/source-map-loader-0.2.4.tgz", + "integrity": "sha512-OU6UJUty+i2JDpTItnizPrlpOIBLmQbWMuBg9q5bVtnHACqw1tn9nNwqJLbv0/00JjnJb/Ee5g5WS5vrRv7zIQ==", + "dev": true, + "requires": { + "async": "^2.5.0", + "loader-utils": "^1.1.0" + }, + "dependencies": { + "async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.2.tgz", + "integrity": "sha512-H1qVYh1MYhEEFLsP97cVKqCGo7KfCyTt6uEWqsTBr9SO84oK9Uwbyd/yCW+6rKJLHksBNUVWZDAjfS+Ccx0Bbg==", + "dev": true, + "requires": { + "lodash": "^4.17.11" + } + } + } + }, + "source-map-resolve": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.2.tgz", + "integrity": "sha512-MjqsvNwyz1s0k81Goz/9vRBe9SZdB09Bdw+/zYyO+3CuPk6fouTaxscHkgtE8jKvf01kVfl8riHzERQ/kefaSA==", + "dev": true, + "requires": { + "atob": "^2.1.1", + "decode-uri-component": "^0.2.0", + "resolve-url": "^0.2.1", + "source-map-url": "^0.4.0", + "urix": "^0.1.0" + } + }, + "source-map-support": { + "version": "0.5.9", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.9.tgz", + "integrity": "sha512-gR6Rw4MvUlYy83vP0vxoVNzM6t8MUXqNuRsuBmBHQDu1Fh6X015FrLdgoDKcNdkwGubozq0P4N0Q37UyFVr1EA==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "source-map-url": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", + "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=", + "dev": true + }, + "spdx-correct": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz", + "integrity": "sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==", + "dev": true, + "requires": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-exceptions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz", + "integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==", + "dev": true + }, + "spdx-expression-parse": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz", + "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", + "dev": true, + "requires": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-license-ids": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.3.tgz", + "integrity": "sha512-uBIcIl3Ih6Phe3XHK1NqboJLdGfwr1UN3k6wSD1dZpmPsIkb8AGNbZYJ1fOBk834+Gxy8rpfDxrS6XLEMZMY2g==", + "dev": true + }, + "spdy": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.0.tgz", + "integrity": "sha512-ot0oEGT/PGUpzf/6uk4AWLqkq+irlqHXkrdbk51oWONh3bxQmBuljxPNl66zlRRcIJStWq0QkLUCPOPjgjvU0Q==", + "dev": true, + "requires": { + "debug": "^4.1.0", + "handle-thing": "^2.0.0", + "http-deceiver": "^1.2.7", + "select-hose": "^2.0.0", + "spdy-transport": "^3.0.0" + }, + "dependencies": { + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "dev": true + } + } + }, + "spdy-transport": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-3.0.0.tgz", + "integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==", + "dev": true, + "requires": { + "debug": "^4.1.0", + "detect-node": "^2.0.4", + "hpack.js": "^2.1.6", + "obuf": "^1.1.2", + "readable-stream": "^3.0.6", + "wbuf": "^1.7.3" + }, + "dependencies": { + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "dev": true + }, + "readable-stream": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.1.1.tgz", + "integrity": "sha512-DkN66hPyqDhnIQ6Jcsvx9bFjhw214O4poMBcIMgPVpQvNy9a0e0Uhg5SqySyDKAmUlwt8LonTBz1ezOnM8pUdA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + } + } + }, + "split-string": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", + "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", + "dev": true, + "requires": { + "extend-shallow": "^3.0.0" + } + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "dev": true + }, + "sshpk": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", + "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", + "dev": true, + "requires": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + } + }, + "ssri": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-5.3.0.tgz", + "integrity": "sha512-XRSIPqLij52MtgoQavH/x/dU1qVKtWUAAZeOHsR9c2Ddi4XerFy3mc1alf+dLJKl9EUIm/Ht+EowFkTUOA6GAQ==", + "dev": true, + "requires": { + "safe-buffer": "^5.1.1" + } + }, + "static-extend": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", + "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", + "dev": true, + "requires": { + "define-property": "^0.2.5", + "object-copy": "^0.1.0" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + } + } + }, + "stats-webpack-plugin": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/stats-webpack-plugin/-/stats-webpack-plugin-0.6.2.tgz", + "integrity": "sha1-LFlJtTHgf4eojm6k3PrFOqjHWis=", + "dev": true, + "requires": { + "lodash": "^4.17.4" + } + }, + "statuses": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", + "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==", + "dev": true + }, + "stdout-stream": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/stdout-stream/-/stdout-stream-1.4.1.tgz", + "integrity": "sha512-j4emi03KXqJWcIeF8eIXkjMFN1Cmb8gUlDYGeBALLPo5qdyTfA9bOtl8m33lRoC+vFMkP3gl0WsDr6+gzxbbTA==", + "dev": true, + "optional": true, + "requires": { + "readable-stream": "^2.0.1" + } + }, + "stream-browserify": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.2.tgz", + "integrity": "sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg==", + "dev": true, + "requires": { + "inherits": "~2.0.1", + "readable-stream": "^2.0.2" + } + }, + "stream-each": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/stream-each/-/stream-each-1.2.3.tgz", + "integrity": "sha512-vlMC2f8I2u/bZGqkdfLQW/13Zihpej/7PmSiMQsbYddxuTsJp8vRe2x2FvVExZg7FaOds43ROAuFJwPR4MTZLw==", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "stream-shift": "^1.0.0" + } + }, + "stream-http": { + "version": "2.8.3", + "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-2.8.3.tgz", + "integrity": "sha512-+TSkfINHDo4J+ZobQLWiMouQYB+UVYFttRA94FpEzzJ7ZdqcL4uUUQ7WkdkI4DSozGmgBUE/a47L+38PenXhUw==", + "dev": true, + "requires": { + "builtin-status-codes": "^3.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.3.6", + "to-arraybuffer": "^1.0.0", + "xtend": "^4.0.0" + } + }, + "stream-shift": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.0.tgz", + "integrity": "sha1-1cdSgl5TZ+eG944Y5EXqIjoVWVI=", + "dev": true + }, + "streamroller": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/streamroller/-/streamroller-0.7.0.tgz", + "integrity": "sha512-WREzfy0r0zUqp3lGO096wRuUp7ho1X6uo/7DJfTlEi0Iv/4gT7YHqXDjKC2ioVGBZtE8QzsQD9nx1nIuoZ57jQ==", + "dev": true, + "requires": { + "date-format": "^1.2.0", + "debug": "^3.1.0", + "mkdirp": "^0.5.1", + "readable-stream": "^2.3.0" + }, + "dependencies": { + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "dev": true + } + } + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "dev": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "strip-bom": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", + "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", + "dev": true, + "requires": { + "is-utf8": "^0.2.0" + } + }, + "strip-eof": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", + "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", + "dev": true + }, + "strip-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz", + "integrity": "sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI=", + "dev": true, + "optional": true, + "requires": { + "get-stdin": "^4.0.1" + } + }, + "style-loader": { + "version": "0.21.0", + "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-0.21.0.tgz", + "integrity": "sha512-T+UNsAcl3Yg+BsPKs1vd22Fr8sVT+CJMtzqc6LEw9bbJZb43lm9GoeIfUcDEefBSWC0BhYbcdupV1GtI4DGzxg==", + "dev": true, + "requires": { + "loader-utils": "^1.1.0", + "schema-utils": "^0.4.5" + } + }, + "stylus": { + "version": "0.54.5", + "resolved": "https://registry.npmjs.org/stylus/-/stylus-0.54.5.tgz", + "integrity": "sha1-QrlWCTHKcJDOhRWnmLqeaqPW3Hk=", + "dev": true, + "requires": { + "css-parse": "1.7.x", + "debug": "*", + "glob": "7.0.x", + "mkdirp": "0.5.x", + "sax": "0.5.x", + "source-map": "0.1.x" + }, + "dependencies": { + "glob": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.0.6.tgz", + "integrity": "sha1-IRuvr0nlJbjNkyYNFKsTYVKz9Xo=", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.2", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "source-map": { + "version": "0.1.43", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.1.43.tgz", + "integrity": "sha1-wkvBRspRfBRx9drL4lcbK3+eM0Y=", + "dev": true, + "requires": { + "amdefine": ">=0.0.4" + } + } + } + }, + "stylus-loader": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/stylus-loader/-/stylus-loader-3.0.2.tgz", + "integrity": "sha512-+VomPdZ6a0razP+zinir61yZgpw2NfljeSsdUF5kJuEzlo3khXhY19Fn6l8QQz1GRJGtMCo8nG5C04ePyV7SUA==", + "dev": true, + "requires": { + "loader-utils": "^1.0.2", + "lodash.clonedeep": "^4.5.0", + "when": "~3.6.x" + } + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + }, + "symbol-observable": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz", + "integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==", + "dev": true + }, + "tapable": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.1.tgz", + "integrity": "sha512-9I2ydhj8Z9veORCw5PRm4u9uebCn0mcCa6scWoNcbZ6dAtoo2618u9UUzxgmsCOreJpqDDuv61LvwofW7hLcBA==", + "dev": true + }, + "tar": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tar/-/tar-2.2.1.tgz", + "integrity": "sha1-jk0qJWwOIYXGsYrWlK7JaLg8sdE=", + "dev": true, + "optional": true, + "requires": { + "block-stream": "*", + "fstream": "^1.0.2", + "inherits": "2" + } + }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", + "dev": true + }, + "through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "dev": true, + "requires": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, + "thunky": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.0.3.tgz", + "integrity": "sha512-YwT8pjmNcAXBZqrubu22P4FYsh2D4dxRmnWBOL8Jk8bUcRUtc5326kx32tuTmFDAZtLOGEVNl8POAR8j896Iow==", + "dev": true + }, + "timers-browserify": { + "version": "2.0.10", + "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.10.tgz", + "integrity": "sha512-YvC1SV1XdOUaL6gx5CoGroT3Gu49pK9+TZ38ErPldOWW4j49GI1HKs9DV+KGq/w6y+LZ72W1c8cKz2vzY+qpzg==", + "dev": true, + "requires": { + "setimmediate": "^1.0.4" + } + }, + "tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dev": true, + "requires": { + "os-tmpdir": "~1.0.2" + } + }, + "to-array": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/to-array/-/to-array-0.1.4.tgz", + "integrity": "sha1-F+bBH3PdTz10zaek/zI46a2b+JA=", + "dev": true + }, + "to-arraybuffer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz", + "integrity": "sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M=", + "dev": true + }, + "to-fast-properties": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz", + "integrity": "sha1-uDVx+k2MJbguIxsG46MFXeTKGkc=", + "dev": true + }, + "to-object-path": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", + "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "to-regex": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", + "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", + "dev": true, + "requires": { + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "regex-not": "^1.0.2", + "safe-regex": "^1.1.0" + } + }, + "to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "dev": true, + "requires": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + } + }, + "toposort": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/toposort/-/toposort-1.0.7.tgz", + "integrity": "sha1-LmhELZ9k7HILjMieZEOsbKqVACk=", + "dev": true + }, + "tough-cookie": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz", + "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==", + "dev": true, + "requires": { + "psl": "^1.1.24", + "punycode": "^1.4.1" + }, + "dependencies": { + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", + "dev": true + } + } + }, + "tree-kill": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.1.tgz", + "integrity": "sha512-4hjqbObwlh2dLyW4tcz0Ymw0ggoaVDMveUB9w8kFSQScdRLo0gxO9J7WFcUBo+W3C1TLdFIEwNOWebgZZ0RH9Q==", + "dev": true + }, + "trim-newlines": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz", + "integrity": "sha1-WIeWa7WCpFA6QetST301ARgVphM=", + "dev": true, + "optional": true + }, + "trim-right": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz", + "integrity": "sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM=", + "dev": true + }, + "true-case-path": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/true-case-path/-/true-case-path-1.0.3.tgz", + "integrity": "sha512-m6s2OdQe5wgpFMC+pAJ+q9djG82O2jcHPOI6RNg1yy9rCYR+WD6Nbpl32fDpfC56nirdRy+opFa/Vk7HYhqaew==", + "dev": true, + "optional": true, + "requires": { + "glob": "^7.1.2" + } + }, + "ts-node": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-7.0.1.tgz", + "integrity": "sha512-BVwVbPJRspzNh2yfslyT1PSbl5uIk03EZlb493RKHN4qej/D06n1cEhjlOJG69oFsE7OT8XjpTUcYf6pKTLMhw==", + "dev": true, + "requires": { + "arrify": "^1.0.0", + "buffer-from": "^1.1.0", + "diff": "^3.1.0", + "make-error": "^1.1.1", + "minimist": "^1.2.0", + "mkdirp": "^0.5.1", + "source-map-support": "^0.5.6", + "yn": "^2.0.0" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "dev": true + } + } + }, + "tsickle": { + "version": "0.32.1", + "resolved": "https://registry.npmjs.org/tsickle/-/tsickle-0.32.1.tgz", + "integrity": "sha512-JW9j+W0SaMSZGejIFZBk0AiPfnhljK3oLx5SaqxrJhjlvzFyPml5zqG1/PuScUj6yTe1muEqwk5CnDK0cOZmKw==", + "dev": true, + "requires": { + "jasmine-diff": "^0.1.3", + "minimist": "^1.2.0", + "mkdirp": "^0.5.1", + "source-map": "^0.6.0", + "source-map-support": "^0.5.0" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "tslib": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz", + "integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ==" + }, + "tslint": { + "version": "5.11.0", + "resolved": "https://registry.npmjs.org/tslint/-/tslint-5.11.0.tgz", + "integrity": "sha1-mPMMAurjzecAYgHkwzywi0hYHu0=", + "dev": true, + "requires": { + "babel-code-frame": "^6.22.0", + "builtin-modules": "^1.1.1", + "chalk": "^2.3.0", + "commander": "^2.12.1", + "diff": "^3.2.0", + "glob": "^7.1.1", + "js-yaml": "^3.7.0", + "minimatch": "^3.0.4", + "resolve": "^1.3.2", + "semver": "^5.3.0", + "tslib": "^1.8.0", + "tsutils": "^2.27.2" + }, + "dependencies": { + "resolve": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.10.0.tgz", + "integrity": "sha512-3sUr9aq5OfSg2S9pNtPA9hL1FVEAjvfOC4leW0SNf/mpnaakz2a9femSd6LqAww2RaFctwyf1lCqnTHuF1rxDg==", + "dev": true, + "requires": { + "path-parse": "^1.0.6" + } + } + } + }, + "tsutils": { + "version": "2.29.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.29.0.tgz", + "integrity": "sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA==", + "dev": true, + "requires": { + "tslib": "^1.8.1" + } + }, + "tty-browserify": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", + "integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=", + "dev": true + }, + "tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "dev": true, + "requires": { + "safe-buffer": "^5.0.1" + } + }, + "tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", + "dev": true + }, + "type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", + "dev": true, + "requires": { + "prelude-ls": "~1.1.2" + } + }, + "type-is": { + "version": "1.6.16", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.16.tgz", + "integrity": "sha512-HRkVv/5qY2G6I8iab9cI7v1bOIdhm94dVjQCPFElW9W+3GeDOSHmy2EBYe4VTApuzolPcmgFTN3ftVJRKR2J9Q==", + "dev": true, + "requires": { + "media-typer": "0.3.0", + "mime-types": "~2.1.18" + } + }, + "typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", + "dev": true + }, + "typescript": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-2.9.2.tgz", + "integrity": "sha512-Gr4p6nFNaoufRIY4NMdpQRNmgxVIGMs4Fcu/ujdYk3nAZqk7supzBE9idmvfZIlH/Cuj//dvi+019qEue9lV0w==", + "dev": true + }, + "uglify-js": { + "version": "3.4.9", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.4.9.tgz", + "integrity": "sha512-8CJsbKOtEbnJsTyv6LE6m6ZKniqMiFWmm9sRbopbkGs3gMPPfd3Fh8iIA4Ykv5MgaTbqHr4BaoGLJLZNhsrW1Q==", + "dev": true, + "requires": { + "commander": "~2.17.1", + "source-map": "~0.6.1" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "uglifyjs-webpack-plugin": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/uglifyjs-webpack-plugin/-/uglifyjs-webpack-plugin-1.3.0.tgz", + "integrity": "sha512-ovHIch0AMlxjD/97j9AYovZxG5wnHOPkL7T1GKochBADp/Zwc44pEWNqpKl1Loupp1WhFg7SlYmHZRUfdAacgw==", + "dev": true, + "requires": { + "cacache": "^10.0.4", + "find-cache-dir": "^1.0.0", + "schema-utils": "^0.4.5", + "serialize-javascript": "^1.4.0", + "source-map": "^0.6.1", + "uglify-es": "^3.3.4", + "webpack-sources": "^1.1.0", + "worker-farm": "^1.5.2" + }, + "dependencies": { + "commander": { + "version": "2.13.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.13.0.tgz", + "integrity": "sha512-MVuS359B+YzaWqjCL/c+22gfryv+mCBPHAv3zyVI2GN8EY6IRP8VwtasXn8jyyhvvq84R4ImN1OKRtcbIasjYA==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "uglify-es": { + "version": "3.3.9", + "resolved": "https://registry.npmjs.org/uglify-es/-/uglify-es-3.3.9.tgz", + "integrity": "sha512-r+MU0rfv4L/0eeW3xZrd16t4NZfK8Ld4SWVglYBb7ez5uXFWHuVRs6xCTrf1yirs9a4j4Y27nn7SRfO6v67XsQ==", + "dev": true, + "requires": { + "commander": "~2.13.0", + "source-map": "~0.6.1" + } + } + } + }, + "ultron": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.1.1.tgz", + "integrity": "sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og==", + "dev": true + }, + "union-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.0.tgz", + "integrity": "sha1-XHHDTLW61dzr4+oM0IIHulqhrqQ=", + "dev": true, + "requires": { + "arr-union": "^3.1.0", + "get-value": "^2.0.6", + "is-extendable": "^0.1.1", + "set-value": "^0.4.3" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + }, + "set-value": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-0.4.3.tgz", + "integrity": "sha1-fbCPnT0i3H945Trzw79GZuzfzPE=", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-extendable": "^0.1.1", + "is-plain-object": "^2.0.1", + "to-object-path": "^0.3.0" + } + } + } + }, + "unique-filename": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", + "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==", + "dev": true, + "requires": { + "unique-slug": "^2.0.0" + } + }, + "unique-slug": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.1.tgz", + "integrity": "sha512-n9cU6+gITaVu7VGj1Z8feKMmfAjEAQGhwD9fE3zvpRRa0wEIx8ODYkVGfSc94M2OX00tUFV8wH3zYbm1I8mxFg==", + "dev": true, + "requires": { + "imurmurhash": "^0.1.4" + } + }, + "unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", + "dev": true + }, + "unset-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", + "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", + "dev": true, + "requires": { + "has-value": "^0.3.1", + "isobject": "^3.0.0" + }, + "dependencies": { + "has-value": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", + "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", + "dev": true, + "requires": { + "get-value": "^2.0.3", + "has-values": "^0.1.4", + "isobject": "^2.0.0" + }, + "dependencies": { + "isobject": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", + "dev": true, + "requires": { + "isarray": "1.0.0" + } + } + } + }, + "has-values": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", + "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=", + "dev": true + } + } + }, + "upath": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/upath/-/upath-1.1.0.tgz", + "integrity": "sha512-bzpH/oBhoS/QI/YtbkqCg6VEiPYjSZtrHQM6/QnJS6OL9pKUFLqb3aFh4Scvwm45+7iAgiMkLhSbaZxUqmrprw==", + "dev": true + }, + "upper-case": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/upper-case/-/upper-case-1.1.3.tgz", + "integrity": "sha1-9rRQHC7EzdJrp4vnIilh3ndiFZg=", + "dev": true + }, + "uri-js": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-3.0.2.tgz", + "integrity": "sha1-+QuFhQf4HepNz7s8TD2/orVX+qo=", + "dev": true, + "requires": { + "punycode": "^2.1.0" + } + }, + "urix": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", + "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", + "dev": true + }, + "url": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", + "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", + "dev": true, + "requires": { + "punycode": "1.3.2", + "querystring": "0.2.0" + }, + "dependencies": { + "punycode": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", + "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=", + "dev": true + } + } + }, + "url-loader": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/url-loader/-/url-loader-1.1.2.tgz", + "integrity": "sha512-dXHkKmw8FhPqu8asTc1puBfe3TehOCo2+RmOOev5suNCIYBcT626kxiWg1NBVkwc4rO8BGa7gP70W7VXuqHrjg==", + "dev": true, + "requires": { + "loader-utils": "^1.1.0", + "mime": "^2.0.3", + "schema-utils": "^1.0.0" + }, + "dependencies": { + "mime": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.0.tgz", + "integrity": "sha512-ikBcWwyqXQSHKtciCcctu9YfPbFYZ4+gbHEmE0Q8jzcTYQg5dHCr3g2wwAZjPoJfQVXZq6KXAjpXOTf5/cjT7w==", + "dev": true + }, + "schema-utils": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", + "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", + "dev": true, + "requires": { + "ajv": "^6.1.0", + "ajv-errors": "^1.0.0", + "ajv-keywords": "^3.1.0" + } + } + } + }, + "url-parse": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.4.4.tgz", + "integrity": "sha512-/92DTTorg4JjktLNLe6GPS2/RvAd/RGr6LuktmWSMLEOa6rjnlrFXNgSbSmkNvCoL2T028A0a1JaJLzRMlFoHg==", + "dev": true, + "requires": { + "querystringify": "^2.0.0", + "requires-port": "^1.0.0" + } + }, + "use": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", + "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", + "dev": true + }, + "useragent": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/useragent/-/useragent-2.2.1.tgz", + "integrity": "sha1-z1k+9PLRdYdei7ZY6pLhik/QbY4=", + "dev": true, + "requires": { + "lru-cache": "2.2.x", + "tmp": "0.0.x" + }, + "dependencies": { + "lru-cache": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.2.4.tgz", + "integrity": "sha1-bGWGGb7PFAMdDQtZSxYELOTcBj0=", + "dev": true + } + } + }, + "util": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/util/-/util-0.11.1.tgz", + "integrity": "sha512-HShAsny+zS2TZfaXxD9tYj4HQGlBezXZMZuM/S5PKLLoZkShZiGk9o5CzukI1LVHZvjdvZ2Sj1aW/Ndn2NB/HQ==", + "dev": true, + "requires": { + "inherits": "2.0.3" + } + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "dev": true + }, + "util.promisify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.0.tgz", + "integrity": "sha512-i+6qA2MPhvoKLuxnJNpXAGhg7HphQOSUq2LKMZD0m15EiskXUkMvKdF4Uui0WYeCUGea+o2cw/ZuwehtfsrNkA==", + "dev": true, + "requires": { + "define-properties": "^1.1.2", + "object.getownpropertydescriptors": "^2.0.3" + } + }, + "utila": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/utila/-/utila-0.4.0.tgz", + "integrity": "sha1-ihagXURWV6Oupe7MWxKk+lN5dyw=", + "dev": true + }, + "utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=", + "dev": true + }, + "uuid": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", + "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==", + "dev": true + }, + "validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "requires": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "validate-npm-package-name": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-3.0.0.tgz", + "integrity": "sha1-X6kS2B630MdK/BQN5zF/DKffQ34=", + "dev": true, + "requires": { + "builtins": "^1.0.3" + } + }, + "vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=", + "dev": true + }, + "verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "dev": true, + "requires": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + }, + "vm-browserify": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-0.0.4.tgz", + "integrity": "sha1-XX6kW7755Kb/ZflUOOCofDV9WnM=", + "dev": true, + "requires": { + "indexof": "0.0.1" + } + }, + "void-elements": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-2.0.1.tgz", + "integrity": "sha1-wGavtYK7HLQSjWDqkjkulNXp2+w=", + "dev": true + }, + "watchpack": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.6.0.tgz", + "integrity": "sha512-i6dHe3EyLjMmDlU1/bGQpEw25XSjkJULPuAVKCbNRefQVq48yXKUpwg538F7AZTf9kyr57zj++pQFltUa5H7yA==", + "dev": true, + "requires": { + "chokidar": "^2.0.2", + "graceful-fs": "^4.1.2", + "neo-async": "^2.5.0" + } + }, + "wbuf": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz", + "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==", + "dev": true, + "requires": { + "minimalistic-assert": "^1.0.0" + } + }, + "webdriver-js-extender": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/webdriver-js-extender/-/webdriver-js-extender-2.1.0.tgz", + "integrity": "sha512-lcUKrjbBfCK6MNsh7xaY2UAUmZwe+/ib03AjVOpFobX4O7+83BUveSrLfU0Qsyb1DaKJdQRbuU+kM9aZ6QUhiQ==", + "dev": true, + "requires": { + "@types/selenium-webdriver": "^3.0.0", + "selenium-webdriver": "^3.0.1" + } + }, + "webpack": { + "version": "4.16.4", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.16.4.tgz", + "integrity": "sha512-RqUfwp4qMqv3oFwBQQOoK69C2tdu2FHJEqPABPqgjGDvOIOLqkTOhmmdJjpiRabzNAAH1ahmkA3z4xowlHN+VA==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.5.13", + "@webassemblyjs/helper-module-context": "1.5.13", + "@webassemblyjs/wasm-edit": "1.5.13", + "@webassemblyjs/wasm-opt": "1.5.13", + "@webassemblyjs/wasm-parser": "1.5.13", + "acorn": "^5.6.2", + "acorn-dynamic-import": "^3.0.0", + "ajv": "^6.1.0", + "ajv-keywords": "^3.1.0", + "chrome-trace-event": "^1.0.0", + "enhanced-resolve": "^4.1.0", + "eslint-scope": "^4.0.0", + "json-parse-better-errors": "^1.0.2", + "loader-runner": "^2.3.0", + "loader-utils": "^1.1.0", + "memory-fs": "~0.4.1", + "micromatch": "^3.1.8", + "mkdirp": "~0.5.0", + "neo-async": "^2.5.0", + "node-libs-browser": "^2.0.0", + "schema-utils": "^0.4.4", + "tapable": "^1.0.0", + "uglifyjs-webpack-plugin": "^1.2.4", + "watchpack": "^1.5.0", + "webpack-sources": "^1.0.1" + } + }, + "webpack-core": { + "version": "0.6.9", + "resolved": "https://registry.npmjs.org/webpack-core/-/webpack-core-0.6.9.tgz", + "integrity": "sha1-/FcViMhVjad76e+23r3Fo7FyvcI=", + "dev": true, + "requires": { + "source-list-map": "~0.1.7", + "source-map": "~0.4.1" + }, + "dependencies": { + "source-list-map": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-0.1.8.tgz", + "integrity": "sha1-xVCyq1Qn9rPyH1r+rYjE9Vh7IQY=", + "dev": true + }, + "source-map": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", + "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=", + "dev": true, + "requires": { + "amdefine": ">=0.0.4" + } + } + } + }, + "webpack-dev-middleware": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-3.4.0.tgz", + "integrity": "sha512-Q9Iyc0X9dP9bAsYskAVJ/hmIZZQwf/3Sy4xCAZgL5cUkjZmUZLt4l5HpbST/Pdgjn3u6pE7u5OdGd1apgzRujA==", + "dev": true, + "requires": { + "memory-fs": "~0.4.1", + "mime": "^2.3.1", + "range-parser": "^1.0.3", + "webpack-log": "^2.0.0" + }, + "dependencies": { + "mime": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.0.tgz", + "integrity": "sha512-ikBcWwyqXQSHKtciCcctu9YfPbFYZ4+gbHEmE0Q8jzcTYQg5dHCr3g2wwAZjPoJfQVXZq6KXAjpXOTf5/cjT7w==", + "dev": true + } + } + }, + "webpack-dev-server": { + "version": "3.1.14", + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-3.1.14.tgz", + "integrity": "sha512-mGXDgz5SlTxcF3hUpfC8hrQ11yhAttuUQWf1Wmb+6zo3x6rb7b9mIfuQvAPLdfDRCGRGvakBWHdHOa0I9p/EVQ==", + "dev": true, + "requires": { + "ansi-html": "0.0.7", + "bonjour": "^3.5.0", + "chokidar": "^2.0.0", + "compression": "^1.5.2", + "connect-history-api-fallback": "^1.3.0", + "debug": "^3.1.0", + "del": "^3.0.0", + "express": "^4.16.2", + "html-entities": "^1.2.0", + "http-proxy-middleware": "~0.18.0", + "import-local": "^2.0.0", + "internal-ip": "^3.0.1", + "ip": "^1.1.5", + "killable": "^1.0.0", + "loglevel": "^1.4.1", + "opn": "^5.1.0", + "portfinder": "^1.0.9", + "schema-utils": "^1.0.0", + "selfsigned": "^1.9.1", + "semver": "^5.6.0", + "serve-index": "^1.7.2", + "sockjs": "0.3.19", + "sockjs-client": "1.3.0", + "spdy": "^4.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^5.1.0", + "url": "^0.11.0", + "webpack-dev-middleware": "3.4.0", + "webpack-log": "^2.0.0", + "yargs": "12.0.2" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + }, + "camelcase": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", + "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", + "dev": true + }, + "cliui": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz", + "integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==", + "dev": true, + "requires": { + "string-width": "^2.1.1", + "strip-ansi": "^4.0.0", + "wrap-ansi": "^2.0.0" + }, + "dependencies": { + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + } + } + } + }, + "cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "dev": true, + "requires": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "decamelize": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-2.0.0.tgz", + "integrity": "sha512-Ikpp5scV3MSYxY39ymh45ZLEecsTdv/Xj2CaQfI8RLMuwi7XvjX9H/fhraiSuU+C5w5NTDu4ZU72xNiZnurBPg==", + "dev": true, + "requires": { + "xregexp": "4.0.0" + } + }, + "execa": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", + "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", + "dev": true, + "requires": { + "cross-spawn": "^6.0.0", + "get-stream": "^4.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + } + }, + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "requires": { + "locate-path": "^3.0.0" + } + }, + "get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "dev": true, + "requires": { + "pump": "^3.0.0" + } + }, + "invert-kv": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-2.0.0.tgz", + "integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "lcid": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lcid/-/lcid-2.0.0.tgz", + "integrity": "sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==", + "dev": true, + "requires": { + "invert-kv": "^2.0.0" + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "dev": true + }, + "os-locale": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.1.0.tgz", + "integrity": "sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==", + "dev": true, + "requires": { + "execa": "^1.0.0", + "lcid": "^2.0.0", + "mem": "^4.0.0" + } + }, + "p-limit": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.1.0.tgz", + "integrity": "sha512-NhURkNcrVB+8hNfLuysU8enY5xn2KXphsHBaC2YmRNTZRc7RWusw6apSpdEj3jo4CMb6W9nrF6tTnsJsJeyu6g==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "requires": { + "p-limit": "^2.0.0" + } + }, + "p-try": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.0.0.tgz", + "integrity": "sha512-hMp0onDKIajHfIkdRk3P4CdCmErkYAxxDtP3Wx/4nZ3aGlau2VKh3mZpcuFkH27WQkL/3WBCPOktzA9ZOAnMQQ==", + "dev": true + }, + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "schema-utils": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", + "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", + "dev": true, + "requires": { + "ajv": "^6.1.0", + "ajv-errors": "^1.0.0", + "ajv-keywords": "^3.1.0" + } + }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "requires": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + }, + "dependencies": { + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + } + } + } + }, + "which-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", + "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", + "dev": true + }, + "yargs": { + "version": "12.0.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-12.0.2.tgz", + "integrity": "sha512-e7SkEx6N6SIZ5c5H22RTZae61qtn3PYUE8JYbBFlK9sYmh3DMQ6E5ygtaG/2BW0JZi4WGgTR2IV5ChqlqrDGVQ==", + "dev": true, + "requires": { + "cliui": "^4.0.0", + "decamelize": "^2.0.0", + "find-up": "^3.0.0", + "get-caller-file": "^1.0.1", + "os-locale": "^3.0.0", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^2.0.0", + "which-module": "^2.0.0", + "y18n": "^3.2.1 || ^4.0.0", + "yargs-parser": "^10.1.0" + } + }, + "yargs-parser": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-10.1.0.tgz", + "integrity": "sha512-VCIyR1wJoEBZUqk5PA+oOBF6ypbwh5aNB3I50guxAL/quggdfs4TtNHQrSazFA3fYZ+tEqfs0zIGlv0c/rgjbQ==", + "dev": true, + "requires": { + "camelcase": "^4.1.0" + } + } + } + }, + "webpack-log": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/webpack-log/-/webpack-log-2.0.0.tgz", + "integrity": "sha512-cX8G2vR/85UYG59FgkoMamwHUIkSSlV3bBMRsbxVXVUk2j6NleCKjQ/WE9eYg9WY4w25O9w8wKP4rzNZFmUcUg==", + "dev": true, + "requires": { + "ansi-colors": "^3.0.0", + "uuid": "^3.3.2" + } + }, + "webpack-merge": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-4.1.4.tgz", + "integrity": "sha512-TmSe1HZKeOPey3oy1Ov2iS3guIZjWvMT2BBJDzzT5jScHTjVC3mpjJofgueEzaEd6ibhxRDD6MIblDr8tzh8iQ==", + "dev": true, + "requires": { + "lodash": "^4.17.5" + } + }, + "webpack-sources": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.3.0.tgz", + "integrity": "sha512-OiVgSrbGu7NEnEvQJJgdSFPl2qWKkWq5lHMhgiToIiN9w34EBnjYzSYs+VbL5KoYiLNtFFa7BZIKxRED3I32pA==", + "dev": true, + "requires": { + "source-list-map": "^2.0.0", + "source-map": "~0.6.1" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "webpack-subresource-integrity": { + "version": "1.1.0-rc.4", + "resolved": "https://registry.npmjs.org/webpack-subresource-integrity/-/webpack-subresource-integrity-1.1.0-rc.4.tgz", + "integrity": "sha1-xcTj1pD50vZKlVDgeodn+Xlqpdg=", + "dev": true, + "requires": { + "webpack-core": "^0.6.8" + } + }, + "websocket-driver": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.0.tgz", + "integrity": "sha1-DK+dLXVdk67gSdS90NP+LMoqJOs=", + "dev": true, + "requires": { + "http-parser-js": ">=0.4.0", + "websocket-extensions": ">=0.1.1" + } + }, + "websocket-extensions": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.3.tgz", + "integrity": "sha512-nqHUnMXmBzT0w570r2JpJxfiSD1IzoI+HGVdd3aZ0yNi3ngvQ4jv1dtHt5VGxfI2yj5yqImPhOK4vmIh2xMbGg==", + "dev": true + }, + "when": { + "version": "3.6.4", + "resolved": "https://registry.npmjs.org/when/-/when-3.6.4.tgz", + "integrity": "sha1-RztRfsFZ4rhQBUl6E5g/CVQS404=", + "dev": true + }, + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "which-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-1.0.0.tgz", + "integrity": "sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8=", + "dev": true, + "optional": true + }, + "wide-align": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", + "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", + "dev": true, + "requires": { + "string-width": "^1.0.2 || 2" + } + }, + "wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", + "dev": true + }, + "worker-farm": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/worker-farm/-/worker-farm-1.6.0.tgz", + "integrity": "sha512-6w+3tHbM87WnSWnENBUvA2pxJPLhQUg5LKwUQHq3r+XPhIM+Gh2R5ycbwPCyuGbNg+lPgdcnQUhuC02kJCvffQ==", + "dev": true, + "requires": { + "errno": "~0.1.7" + } + }, + "wrap-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", + "dev": true, + "requires": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1" + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "ws": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-3.3.3.tgz", + "integrity": "sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA==", + "dev": true, + "requires": { + "async-limiter": "~1.0.0", + "safe-buffer": "~5.1.0", + "ultron": "~1.1.0" + } + }, + "xml2js": { + "version": "0.4.19", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.19.tgz", + "integrity": "sha512-esZnJZJOiJR9wWKMyuvSE1y6Dq5LCuJanqhxslH2bxM6duahNZ+HMpCLhBQGZkbX6xRf8x1Y2eJlgt2q3qo49Q==", + "dev": true, + "requires": { + "sax": ">=0.6.0", + "xmlbuilder": "~9.0.1" + }, + "dependencies": { + "sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", + "dev": true + } + } + }, + "xmlbuilder": { + "version": "9.0.7", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz", + "integrity": "sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0=", + "dev": true + }, + "xmlhttprequest-ssl": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.5.tgz", + "integrity": "sha1-wodrBhaKrcQOV9l+gRkayPQ5iz4=", + "dev": true + }, + "xregexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/xregexp/-/xregexp-4.0.0.tgz", + "integrity": "sha512-PHyM+sQouu7xspQQwELlGwwd05mXUFqwFYfqPO0cC7x4fxyHnnuetmQr6CjJiafIDoH4MogHb9dOoJzR/Y4rFg==", + "dev": true + }, + "xtend": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", + "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=", + "dev": true + }, + "xxhashjs": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/xxhashjs/-/xxhashjs-0.2.2.tgz", + "integrity": "sha512-AkTuIuVTET12tpsVIQo+ZU6f/qDmKuRUcjaqR+OIvm+aCBsZ95i7UVY5WJ9TMsSaZ0DA2WxoZ4acu0sPH+OKAw==", + "dev": true, + "requires": { + "cuint": "^0.2.2" + } + }, + "y18n": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", + "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", + "dev": true + }, + "yallist": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", + "dev": true + }, + "yargs": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-7.1.0.tgz", + "integrity": "sha1-a6MY6xaWFyf10oT46gA+jWFU0Mg=", + "dev": true, + "optional": true, + "requires": { + "camelcase": "^3.0.0", + "cliui": "^3.2.0", + "decamelize": "^1.1.1", + "get-caller-file": "^1.0.1", + "os-locale": "^1.4.0", + "read-pkg-up": "^1.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^1.0.2", + "which-module": "^1.0.0", + "y18n": "^3.2.1", + "yargs-parser": "^5.0.0" + }, + "dependencies": { + "camelcase": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", + "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=", + "dev": true, + "optional": true + }, + "y18n": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", + "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=", + "dev": true, + "optional": true + } + } + }, + "yargs-parser": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-5.0.0.tgz", + "integrity": "sha1-J17PDX/+Bcd+ZOfIbkzZS/DhIoo=", + "dev": true, + "optional": true, + "requires": { + "camelcase": "^3.0.0" + }, + "dependencies": { + "camelcase": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", + "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=", + "dev": true, + "optional": true + } + } + }, + "yeast": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz", + "integrity": "sha1-AI4G2AlDIMNy28L47XagymyKxBk=", + "dev": true + }, + "yn": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yn/-/yn-2.0.0.tgz", + "integrity": "sha1-5a2ryKz0CPY4X8dklWhMiOavaJo=", + "dev": true + }, + "zone.js": { + "version": "0.8.29", + "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.8.29.tgz", + "integrity": "sha512-mla2acNCMkWXBD+c+yeUrBUrzOxYMNFdQ6FGfigGGtEVBPJx07BQeJekjt9DmH1FtZek4E9rE1eRR9qQpxACOQ==" + } + } +} diff --git "a/Cute-Angular/angualr\345\256\230\347\275\221demo/package.json" "b/Cute-Angular/angualr\345\256\230\347\275\221demo/package.json" new file mode 100644 index 00000000..9680352b --- /dev/null +++ "b/Cute-Angular/angualr\345\256\230\347\275\221demo/package.json" @@ -0,0 +1,49 @@ +{ + "name": "angular-demo", + "version": "0.0.0", + "scripts": { + "ng": "ng", + "start": "ng serve", + "build": "ng build", + "test": "ng test", + "lint": "ng lint", + "e2e": "ng e2e" + }, + "private": true, + "dependencies": { + "@angular/animations": "^6.1.0", + "@angular/common": "^6.1.0", + "@angular/compiler": "^6.1.0", + "@angular/core": "^6.1.0", + "@angular/forms": "^6.1.0", + "@angular/http": "^6.1.0", + "@angular/platform-browser": "^6.1.0", + "@angular/platform-browser-dynamic": "^6.1.0", + "@angular/router": "^6.1.0", + "angular-in-memory-web-api": "^0.8.0", + "core-js": "^2.5.4", + "rxjs": "~6.2.0", + "zone.js": "~0.8.26" + }, + "devDependencies": { + "@angular-devkit/build-angular": "~0.8.0", + "@angular/cli": "~6.2.4", + "@angular/compiler-cli": "^6.1.0", + "@angular/language-service": "^6.1.0", + "@types/jasmine": "~2.8.8", + "@types/jasminewd2": "~2.0.3", + "@types/node": "~8.9.4", + "codelyzer": "~4.3.0", + "jasmine-core": "~2.99.1", + "jasmine-spec-reporter": "~4.2.1", + "karma": "~3.0.0", + "karma-chrome-launcher": "~2.2.0", + "karma-coverage-istanbul-reporter": "~2.0.1", + "karma-jasmine": "~1.1.2", + "karma-jasmine-html-reporter": "^0.2.2", + "protractor": "~5.4.0", + "ts-node": "~7.0.0", + "tslint": "~5.11.0", + "typescript": "~2.9.2" + } +} diff --git "a/Cute-Angular/angualr\345\256\230\347\275\221demo/src/app/app-routing.module.spec.ts" "b/Cute-Angular/angualr\345\256\230\347\275\221demo/src/app/app-routing.module.spec.ts" new file mode 100644 index 00000000..d68ef067 --- /dev/null +++ "b/Cute-Angular/angualr\345\256\230\347\275\221demo/src/app/app-routing.module.spec.ts" @@ -0,0 +1,13 @@ +import { AppRoutingModule } from './app-routing.module'; + +describe('AppRoutingModule', () => { + let appRoutingModule: AppRoutingModule; + + beforeEach(() => { + appRoutingModule = new AppRoutingModule(); + }); + + it('should create an instance', () => { + expect(appRoutingModule).toBeTruthy(); + }); +}); diff --git "a/Cute-Angular/angualr\345\256\230\347\275\221demo/src/app/app-routing.module.ts" "b/Cute-Angular/angualr\345\256\230\347\275\221demo/src/app/app-routing.module.ts" new file mode 100644 index 00000000..2bbdb5fd --- /dev/null +++ "b/Cute-Angular/angualr\345\256\230\347\275\221demo/src/app/app-routing.module.ts" @@ -0,0 +1,26 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { RouterModule, Routes } from '@angular/router'; +import { HeroesComponent } from './heroes/heroes.component'; +import { DashboardComponent } from './dashboard/dashboard.component'; +import { HeroDetailComponent } from './hero-detail/hero-detail.component'; + +const routes: Routes = [ + //把一个与空路径“完全匹配”的 URL 重定向到路径为 '/dashboard' 的路由 + { path: '', redirectTo:'/dashboard', pathMatch:'full' }, + { path: 'heroes', component: HeroesComponent }, + { path: 'dashboard', component: DashboardComponent }, + { path: 'detail/:id', component: HeroDetailComponent } +]; + +@NgModule({ + imports: [ + CommonModule, + RouterModule.forRoot(routes), // 监听浏览器地址变化 + ], + declarations: [], + exports: [ + RouterModule + ] +}) +export class AppRoutingModule { } diff --git "a/Cute-Angular/angualr\345\256\230\347\275\221demo/src/app/app.component.css" "b/Cute-Angular/angualr\345\256\230\347\275\221demo/src/app/app.component.css" new file mode 100644 index 00000000..b8ab4642 --- /dev/null +++ "b/Cute-Angular/angualr\345\256\230\347\275\221demo/src/app/app.component.css" @@ -0,0 +1,22 @@ +/* Application-wide Styles */ +h1 { + color: #369; + font-family: Arial, Helvetica, sans-serif; + font-size: 250%; + } + h2, h3 { + color: #444; + font-family: Arial, Helvetica, sans-serif; + font-weight: lighter; + } + body { + margin: 2em; + } + body, input[type="text"], button { + color: #888; + font-family: Cambria, Georgia; + } + /* everywhere else */ + * { + font-family: Arial, Helvetica, sans-serif; + } \ No newline at end of file diff --git "a/Cute-Angular/angualr\345\256\230\347\275\221demo/src/app/app.component.html" "b/Cute-Angular/angualr\345\256\230\347\275\221demo/src/app/app.component.html" new file mode 100644 index 00000000..757ab484 --- /dev/null +++ "b/Cute-Angular/angualr\345\256\230\347\275\221demo/src/app/app.component.html" @@ -0,0 +1,14 @@ + +
+

+ Welcome to {{ title }}! +

+ + + + +
diff --git "a/Cute-Angular/angualr\345\256\230\347\275\221demo/src/app/app.component.spec.ts" "b/Cute-Angular/angualr\345\256\230\347\275\221demo/src/app/app.component.spec.ts" new file mode 100644 index 00000000..5148b0a2 --- /dev/null +++ "b/Cute-Angular/angualr\345\256\230\347\275\221demo/src/app/app.component.spec.ts" @@ -0,0 +1,31 @@ +import { TestBed, async } from '@angular/core/testing'; +import { AppComponent } from './app.component'; + +describe('AppComponent', () => { + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ + AppComponent + ], + }).compileComponents(); + })); + + it('should create the app', () => { + const fixture = TestBed.createComponent(AppComponent); + const app = fixture.debugElement.componentInstance; + expect(app).toBeTruthy(); + }); + + it(`should have as title 'angular-demo'`, () => { + const fixture = TestBed.createComponent(AppComponent); + const app = fixture.debugElement.componentInstance; + expect(app.title).toEqual('angular-demo'); + }); + + it('should render title in a h1 tag', () => { + const fixture = TestBed.createComponent(AppComponent); + fixture.detectChanges(); + const compiled = fixture.debugElement.nativeElement; + expect(compiled.querySelector('h1').textContent).toContain('Welcome to angular-demo!'); + }); +}); diff --git "a/Cute-Angular/angualr\345\256\230\347\275\221demo/src/app/app.component.ts" "b/Cute-Angular/angualr\345\256\230\347\275\221demo/src/app/app.component.ts" new file mode 100644 index 00000000..eba78e65 --- /dev/null +++ "b/Cute-Angular/angualr\345\256\230\347\275\221demo/src/app/app.component.ts" @@ -0,0 +1,10 @@ +import { Component } from '@angular/core'; + +@Component({ + selector: 'app-root', + templateUrl: './app.component.html', + styleUrls: ['./app.component.css'] +}) +export class AppComponent { + title = 'leo-angular-demo'; +} diff --git "a/Cute-Angular/angualr\345\256\230\347\275\221demo/src/app/app.module.ts" "b/Cute-Angular/angualr\345\256\230\347\275\221demo/src/app/app.module.ts" new file mode 100644 index 00000000..556af361 --- /dev/null +++ "b/Cute-Angular/angualr\345\256\230\347\275\221demo/src/app/app.module.ts" @@ -0,0 +1,35 @@ +import { BrowserModule } from '@angular/platform-browser'; +import { NgModule } from '@angular/core'; +import { FormsModule } from '@angular/forms'; +import { HttpClientModule } from '@angular/common/http'; +import { HttpClientInMemoryWebApiModule } from 'angular-in-memory-web-api'; +import { InMemoryDataService } from './in-memory-data.service'; + +import { AppComponent } from './app.component'; +import { HeroesComponent } from './heroes/heroes.component'; +import { HeroDetailComponent } from './hero-detail/hero-detail.component'; +import { MessagesComponent } from './messages/messages.component'; +import { AppRoutingModule } from './app-routing.module'; +import { DashboardComponent } from './dashboard/dashboard.component'; +import { HeroSearchComponent } from './hero-search/hero-search.component'; + +// 存放关键性的元数据在@NgModule +@NgModule({ + declarations: [ + AppComponent, + HeroesComponent, + HeroDetailComponent, + MessagesComponent, + DashboardComponent, + HeroSearchComponent + ], + imports: [ + BrowserModule,FormsModule, AppRoutingModule,HttpClientModule, + HttpClientInMemoryWebApiModule.forRoot( + InMemoryDataService, {dataEncapsulation:false} + ) + ], + providers: [], + bootstrap: [AppComponent] +}) +export class AppModule { } diff --git "a/Cute-Angular/angualr\345\256\230\347\275\221demo/src/app/dashboard/dashboard.component.css" "b/Cute-Angular/angualr\345\256\230\347\275\221demo/src/app/dashboard/dashboard.component.css" new file mode 100644 index 00000000..122d4e37 --- /dev/null +++ "b/Cute-Angular/angualr\345\256\230\347\275\221demo/src/app/dashboard/dashboard.component.css" @@ -0,0 +1,63 @@ +/* DashboardComponent's private CSS styles */ +[class*='col-'] { + float: left; + padding-right: 20px; + padding-bottom: 20px; + } + [class*='col-']:last-of-type { + padding-right: 0; + } + a { + text-decoration: none; + } + *, *:after, *:before { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + } + h3 { + text-align: center; + margin-bottom: 0; + } + h4 { + position: relative; + } + .grid { + margin: 0; + } + .col-1-4 { + width: 25%; + } + .module { + padding: 20px; + text-align: center; + color: #eee; + max-height: 120px; + min-width: 120px; + background-color: #607d8b; + border-radius: 2px; + } + .module:hover { + background-color: #eee; + cursor: pointer; + color: #607d8b; + } + .grid-pad { + padding: 10px 0; + } + .grid-pad > [class*='col-']:last-of-type { + padding-right: 20px; + } + @media (max-width: 600px) { + .module { + font-size: 10px; + max-height: 75px; } + } + @media (max-width: 1024px) { + .grid { + margin: 0; + } + .module { + min-width: 60px; + } + } \ No newline at end of file diff --git "a/Cute-Angular/angualr\345\256\230\347\275\221demo/src/app/dashboard/dashboard.component.html" "b/Cute-Angular/angualr\345\256\230\347\275\221demo/src/app/dashboard/dashboard.component.html" new file mode 100644 index 00000000..54711c92 --- /dev/null +++ "b/Cute-Angular/angualr\345\256\230\347\275\221demo/src/app/dashboard/dashboard.component.html" @@ -0,0 +1,12 @@ +

Top Heroes

+
+ +
+

{{hero.name}}

+
+
+
+ + \ No newline at end of file diff --git "a/Cute-Angular/angualr\345\256\230\347\275\221demo/src/app/dashboard/dashboard.component.spec.ts" "b/Cute-Angular/angualr\345\256\230\347\275\221demo/src/app/dashboard/dashboard.component.spec.ts" new file mode 100644 index 00000000..9c996c37 --- /dev/null +++ "b/Cute-Angular/angualr\345\256\230\347\275\221demo/src/app/dashboard/dashboard.component.spec.ts" @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { DashboardComponent } from './dashboard.component'; + +describe('DashboardComponent', () => { + let component: DashboardComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ DashboardComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(DashboardComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git "a/Cute-Angular/angualr\345\256\230\347\275\221demo/src/app/dashboard/dashboard.component.ts" "b/Cute-Angular/angualr\345\256\230\347\275\221demo/src/app/dashboard/dashboard.component.ts" new file mode 100644 index 00000000..f9f93f6c --- /dev/null +++ "b/Cute-Angular/angualr\345\256\230\347\275\221demo/src/app/dashboard/dashboard.component.ts" @@ -0,0 +1,24 @@ +import { Component, OnInit } from '@angular/core'; +import { Hero } from '../hero'; +import { HeroService } from '../hero.service'; + +@Component({ + selector: 'app-dashboard', + templateUrl: './dashboard.component.html', + styleUrls: ['./dashboard.component.css'] +}) +export class DashboardComponent implements OnInit { + heroes: Hero[] = []; + constructor(private heroService: HeroService) { } + + ngOnInit() { + this.getHeroes(); + } + + getHeroes(): void{ + this.heroService.getHeroes().subscribe( + heroes => this.heroes = heroes.slice(1, 5) + ); + } + +} diff --git "a/Cute-Angular/angualr\345\256\230\347\275\221demo/src/app/hero-detail/hero-detail.component.css" "b/Cute-Angular/angualr\345\256\230\347\275\221demo/src/app/hero-detail/hero-detail.component.css" new file mode 100644 index 00000000..e69de29b diff --git "a/Cute-Angular/angualr\345\256\230\347\275\221demo/src/app/hero-detail/hero-detail.component.html" "b/Cute-Angular/angualr\345\256\230\347\275\221demo/src/app/hero-detail/hero-detail.component.html" new file mode 100644 index 00000000..84051a78 --- /dev/null +++ "b/Cute-Angular/angualr\345\256\230\347\275\221demo/src/app/hero-detail/hero-detail.component.html" @@ -0,0 +1,12 @@ +
+

{{hero.name | uppercase}} Details

+
id: {{hero.id}}
+
name: {{hero.name}}
+
+ +
+ + +
\ No newline at end of file diff --git "a/Cute-Angular/angualr\345\256\230\347\275\221demo/src/app/hero-detail/hero-detail.component.spec.ts" "b/Cute-Angular/angualr\345\256\230\347\275\221demo/src/app/hero-detail/hero-detail.component.spec.ts" new file mode 100644 index 00000000..5e34497b --- /dev/null +++ "b/Cute-Angular/angualr\345\256\230\347\275\221demo/src/app/hero-detail/hero-detail.component.spec.ts" @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { HeroDetailComponent } from './hero-detail.component'; + +describe('HeroDetailComponent', () => { + let component: HeroDetailComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ HeroDetailComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(HeroDetailComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git "a/Cute-Angular/angualr\345\256\230\347\275\221demo/src/app/hero-detail/hero-detail.component.ts" "b/Cute-Angular/angualr\345\256\230\347\275\221demo/src/app/hero-detail/hero-detail.component.ts" new file mode 100644 index 00000000..4d6b9733 --- /dev/null +++ "b/Cute-Angular/angualr\345\256\230\347\275\221demo/src/app/hero-detail/hero-detail.component.ts" @@ -0,0 +1,40 @@ +import { Component, OnInit, Input } from '@angular/core'; +import { Hero } from '../hero'; +import { ActivatedRoute } from '@angular/router'; +import { Location } from '@angular/common'; +import { HeroService } from '../hero.service'; + +@Component({ + selector: 'app-hero-detail', + templateUrl: './hero-detail.component.html', + styleUrls: ['./hero-detail.component.css'] +}) +export class HeroDetailComponent implements OnInit { + @Input() hero: Hero; // 外部数据通过@Input绑定数据 + constructor( + private route: ActivatedRoute, + private heroService: HeroService, + private location: Location + ) { } + + ngOnInit(): void { + this.getHero(); + } + goBack(): void { + this.location.back(); + } + getHero(): void{ + const id = + this.route.snapshot.paramMap.get('id'); + this.heroService.getHero(id).subscribe( + hero => this.hero = hero + ); + // route.snapshot 是一个路由信息的静态快照,抓取自组件刚刚创建完毕之后。 + // paramMap 是一个从 URL 中提取的路由参数值的字典。 "id" 对应的值就是要获取的英雄的 id。 + }; + save(): void{ + this.heroService.updateHero(this.hero).subscribe( + () => this.goBack() + ) + }; + +} diff --git "a/Cute-Angular/angualr\345\256\230\347\275\221demo/src/app/hero-search/hero-search.component.css" "b/Cute-Angular/angualr\345\256\230\347\275\221demo/src/app/hero-search/hero-search.component.css" new file mode 100644 index 00000000..3faebe3a --- /dev/null +++ "b/Cute-Angular/angualr\345\256\230\347\275\221demo/src/app/hero-search/hero-search.component.css" @@ -0,0 +1,40 @@ +/* HeroSearch private styles */ +.search-result li { + border-bottom: 1px solid gray; + border-left: 1px solid gray; + border-right: 1px solid gray; + width: 195px; + height: 16px; + padding: 5px; + background-color: white; + cursor: pointer; + list-style-type: none; + margin: 0 auto; + } + + .search-result li:hover { + background-color: #607D8B; + } + + .search-result li a { + color: #888; + display: block; + text-decoration: none; + } + + .search-result li a:hover { + color: white; + } + .search-result li a:active { + color: white; + } + #search-box { + width: 200px; + height: 20px; + } + + + ul.search-result { + margin-top: 0; + padding-left: 0; + } \ No newline at end of file diff --git "a/Cute-Angular/angualr\345\256\230\347\275\221demo/src/app/hero-search/hero-search.component.html" "b/Cute-Angular/angualr\345\256\230\347\275\221demo/src/app/hero-search/hero-search.component.html" new file mode 100644 index 00000000..c111268e --- /dev/null +++ "b/Cute-Angular/angualr\345\256\230\347\275\221demo/src/app/hero-search/hero-search.component.html" @@ -0,0 +1,14 @@ +
+

Hero Search

+ + + +
\ No newline at end of file diff --git "a/Cute-Angular/angualr\345\256\230\347\275\221demo/src/app/hero-search/hero-search.component.spec.ts" "b/Cute-Angular/angualr\345\256\230\347\275\221demo/src/app/hero-search/hero-search.component.spec.ts" new file mode 100644 index 00000000..901bb7f2 --- /dev/null +++ "b/Cute-Angular/angualr\345\256\230\347\275\221demo/src/app/hero-search/hero-search.component.spec.ts" @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { HeroSearchComponent } from './hero-search.component'; + +describe('HeroSearchComponent', () => { + let component: HeroSearchComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ HeroSearchComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(HeroSearchComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git "a/Cute-Angular/angualr\345\256\230\347\275\221demo/src/app/hero-search/hero-search.component.ts" "b/Cute-Angular/angualr\345\256\230\347\275\221demo/src/app/hero-search/hero-search.component.ts" new file mode 100644 index 00000000..a7d8a06d --- /dev/null +++ "b/Cute-Angular/angualr\345\256\230\347\275\221demo/src/app/hero-search/hero-search.component.ts" @@ -0,0 +1,39 @@ +import { Component, OnInit } from '@angular/core'; +import { Observable, Subject } from 'rxjs'; +import { debounceTime, distinctUntilChanged, switchMap } from 'rxjs/operators'; + +import { Hero } from '../hero'; +import { HeroService } from '../hero.service'; + +@Component({ + selector: 'app-hero-search', + templateUrl: './hero-search.component.html', + styleUrls: ['./hero-search.component.css'] +}) +export class HeroSearchComponent implements OnInit { + heroes$: Observable; + private searchTerms = new Subject(); + + constructor( + private heroSerive: HeroService + ) { } + + search(term: string): void{ + // Push a search term into the observable stream. + this.searchTerms.next(term); + // 通过调用next(value) 方法往 Observable 中推送一些值, + } + + ngOnInit() : void{ + this.heroes$ = this.searchTerms.pipe( + // 在传出最终字符串之前,debounceTime(300) 将会等待,直到新增字符串的事件暂停了 300 毫秒。 你实际发起请求的间隔永远不会小于 300ms。 + debounceTime(300), + + // distinctUntilChanged() 会确保只在过滤条件变化时才发送请求。 + distinctUntilChanged(), + + // switchMap() 会为每个从 debounce 和 distinctUntilChanged 中通过的搜索词调用搜索服务。 它会取消并丢弃以前的搜索可观察对象,只保留最近的。 + switchMap((term: string) => this.heroSerive.searchHeroes(term)) + ); + } +} diff --git "a/Cute-Angular/angualr\345\256\230\347\275\221demo/src/app/hero.service.spec.ts" "b/Cute-Angular/angualr\345\256\230\347\275\221demo/src/app/hero.service.spec.ts" new file mode 100644 index 00000000..082791a7 --- /dev/null +++ "b/Cute-Angular/angualr\345\256\230\347\275\221demo/src/app/hero.service.spec.ts" @@ -0,0 +1,12 @@ +import { TestBed } from '@angular/core/testing'; + +import { HeroService } from './hero.service'; + +describe('HeroService', () => { + beforeEach(() => TestBed.configureTestingModule({})); + + it('should be created', () => { + const service: HeroService = TestBed.get(HeroService); + expect(service).toBeTruthy(); + }); +}); diff --git "a/Cute-Angular/angualr\345\256\230\347\275\221demo/src/app/hero.service.ts" "b/Cute-Angular/angualr\345\256\230\347\275\221demo/src/app/hero.service.ts" new file mode 100644 index 00000000..bbe21fda --- /dev/null +++ "b/Cute-Angular/angualr\345\256\230\347\275\221demo/src/app/hero.service.ts" @@ -0,0 +1,99 @@ +import { Injectable } from '@angular/core'; +import { Hero } from './hero'; +import { HEROES } from './mock-heroes'; +import { Observable, of } from 'rxjs'; +import { MessageService } from './message.service'; +import { HttpClient, HttpHeaders } from '@angular/common/http'; +import { catchError, map, tap } from 'rxjs/operators'; + +const httpOptions = { + headers: new HttpHeaders({ + 'Content-Type': 'application/json' + }) +}; + + +// @Injectable把这个类标记为依赖注入系统的参与者之一 注册服务的提供商 +@Injectable({ + providedIn: 'root'//单一的、共享的,可以把它注入到任何想要它的类上 +}) + +export class HeroService { + + constructor( + private http: HttpClient, + private messageService: MessageService// 注入 MessageService + + ) { + + } + private heroesUrl = 'api/heroes'; + private log(message: string){ + this.messageService.add(`HeroService: ${message}`); + }; + private handleError (operation = 'opration', result?:T){ + return (error: any): Observable => { + console.error(error); + this.log(`${operation} failed:${error.message}`); + return of(result as T);// 返回一个空结果 让程序继续运行 + } + }; + getHeroes(): Observable{ + // this.messageService.add('HeroService: fetched heroes!'); + // return of(HEROES); + return this.http.get(this.heroesUrl).pipe( + tap(_ => this.log('fetched heroes')), + catchError(this.handleError('getHeroes', [])) + ); + } + + getHero(id: number): Observable{ + // this.messageService.add(`HeroService: fetched heroes!id = ${id}`); + // return of(HEROES.find(hero => hero.id === id)); + const url = `${this.heroesUrl}/${id}`; + return this.http.get(url).pipe( + tap(_ => this.log(`fetched hero id=${id}`)), + catchError(this.handleError(`getHero id=${id}`)) + ); + } + + updateHero (hero: Hero): Observable{ + return this.http.put(this.heroesUrl, hero, httpOptions).pipe( + tap(_ => this.log(`updated hero id = ${hero.id}`)), + catchError(this.handleError('updateHero')) + ) + }; + + addHero (hero: Hero): Observable{ + return this.http.post(this.heroesUrl, hero, httpOptions).pipe( + tap((hero:Hero) => this.log(`added hero w/ id=${hero.id}`)), + catchError(this.handleError('addHero')) + ); + } + + deleteHero(hero: Hero): Observable{ + const id = typeof hero === 'number' ? hero : hero.id; + const url = `${this.heroesUrl}/${id}`; + return this.http.delete(url, httpOptions).pipe( + tap( _ => this.log(`deleted hero id=${id}`)), + catchError(this.handleError('deleteHero')) + ); + }; + + searchHeroes(term: string): Observable{ + //如果没有搜索词,该方法立即返回一个空数组 + if(!term.trim()){ + return of([]) + } + return this.http.get(`${this.heroesUrl}/?name=${term}`).pipe( + tap(_ => this.log(`found heroes matching "${term}"`)), + catchError(this.handleError('serchHeroes', [])) + ) + } +} + + +// tap操作符会查看 Observable 中的值,使用那些值做一些事情,并且把它们传出来。这种 tap 回调不会改变这些值本身。 + +// HttpClient.put() 方法接受三个参数 +// URL 地址, 要修改的数据(这里就是修改后的英雄),选项 \ No newline at end of file diff --git "a/Cute-Angular/angualr\345\256\230\347\275\221demo/src/app/hero.ts" "b/Cute-Angular/angualr\345\256\230\347\275\221demo/src/app/hero.ts" new file mode 100644 index 00000000..08a76ef9 --- /dev/null +++ "b/Cute-Angular/angualr\345\256\230\347\275\221demo/src/app/hero.ts" @@ -0,0 +1,4 @@ +export class Hero { + id: number; + name: string; +} \ No newline at end of file diff --git "a/Cute-Angular/angualr\345\256\230\347\275\221demo/src/app/heroes/heroes.component.css" "b/Cute-Angular/angualr\345\256\230\347\275\221demo/src/app/heroes/heroes.component.css" new file mode 100644 index 00000000..0f74618f --- /dev/null +++ "b/Cute-Angular/angualr\345\256\230\347\275\221demo/src/app/heroes/heroes.component.css" @@ -0,0 +1,73 @@ +/* HeroesComponent's private CSS styles */ +.heroes { + margin: 0 0 2em 0; + list-style-type: none; + padding: 0; + width: 15em; +} +.heroes li { + position: relative; + cursor: pointer; + background-color: #EEE; + margin: .5em; + padding: .3em 0; + height: 1.6em; + border-radius: 4px; +} + +.heroes li:hover { + color: #607D8B; + background-color: #DDD; + left: .1em; +} + +.heroes a { + color: #888; + text-decoration: none; + position: relative; + display: block; + width: 250px; +} + +.heroes a:hover { + color:#607D8B; +} + +.heroes .badge { + display: inline-block; + font-size: small; + color: white; + padding: 0.8em 0.7em 0 0.7em; + background-color: #607D8B; + line-height: 1em; + position: relative; + left: -1px; + top: -4px; + height: 1.8em; + min-width: 16px; + text-align: right; + margin-right: .8em; + border-radius: 4px 0 0 4px; +} + +button { + background-color: #eee; + border: none; + padding: 5px 10px; + border-radius: 4px; + cursor: pointer; + cursor: hand; + font-family: Arial; +} + +button:hover { + background-color: #cfd8dc; +} + +button.delete { + position: relative; + left: 194px; + top: -32px; + background-color: gray !important; + color: white; +} \ No newline at end of file diff --git "a/Cute-Angular/angualr\345\256\230\347\275\221demo/src/app/heroes/heroes.component.html" "b/Cute-Angular/angualr\345\256\230\347\275\221demo/src/app/heroes/heroes.component.html" new file mode 100644 index 00000000..1a510250 --- /dev/null +++ "b/Cute-Angular/angualr\345\256\230\347\275\221demo/src/app/heroes/heroes.component.html" @@ -0,0 +1,30 @@ +

My Heroes

+
+ + +
+ + diff --git "a/Cute-Angular/angualr\345\256\230\347\275\221demo/src/app/heroes/heroes.component.spec.ts" "b/Cute-Angular/angualr\345\256\230\347\275\221demo/src/app/heroes/heroes.component.spec.ts" new file mode 100644 index 00000000..66518e44 --- /dev/null +++ "b/Cute-Angular/angualr\345\256\230\347\275\221demo/src/app/heroes/heroes.component.spec.ts" @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { HeroesComponent } from './heroes.component'; + +describe('HeroesComponent', () => { + let component: HeroesComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ HeroesComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(HeroesComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git "a/Cute-Angular/angualr\345\256\230\347\275\221demo/src/app/heroes/heroes.component.ts" "b/Cute-Angular/angualr\345\256\230\347\275\221demo/src/app/heroes/heroes.component.ts" new file mode 100644 index 00000000..82bcc39f --- /dev/null +++ "b/Cute-Angular/angualr\345\256\230\347\275\221demo/src/app/heroes/heroes.component.ts" @@ -0,0 +1,55 @@ +import { Component, OnInit } from '@angular/core'; +import { Hero } from '../hero'; +import { HeroService } from '../hero.service'; + +@Component({ + selector: 'app-heroes', + templateUrl: './heroes.component.html', + styleUrls: ['./heroes.component.css'] + // selector — 组件的选择器(CSS 元素选择器) + // templateUrl — 组件模板文件的位置。 + // styleUrls — 组件私有 CSS 样式表文件的位置。 +}) +export class HeroesComponent implements OnInit { + hero: Hero = { + id: 1, + name:'Windstorm' + }; + heroes: Hero[]; + + // selectedHero : Hero; + // onSelect(hero : Hero): void{ + // this.selectedHero = hero; + // }; + + constructor(private heroService: HeroService) { //标记为一个 HeroService 的注入点 + + } + + getHeroes(): void{ + this.heroService.getHeroes().subscribe( // 订阅服务 + heroes => this.heroes = heroes + ); + + }; + + ngOnInit() { + this.getHeroes(); + } + + add(name: string): void{ + name = name.trim(); + if(!name)return; + this.heroService.addHero({name} as Hero).subscribe( + hero => { + this.heroes.push(hero); + } + ) + } + + delete(hero: Hero):void{ + this.heroes = this.heroes.filter(h => h !== hero); + this.heroService.deleteHero(hero).subscribe(); + } + +} diff --git "a/Cute-Angular/angualr\345\256\230\347\275\221demo/src/app/in-memory-data.service.spec.ts" "b/Cute-Angular/angualr\345\256\230\347\275\221demo/src/app/in-memory-data.service.spec.ts" new file mode 100644 index 00000000..a75ef029 --- /dev/null +++ "b/Cute-Angular/angualr\345\256\230\347\275\221demo/src/app/in-memory-data.service.spec.ts" @@ -0,0 +1,12 @@ +import { TestBed } from '@angular/core/testing'; + +import { InMemoryDataService } from './in-memory-data.service'; + +describe('InMemoryDataService', () => { + beforeEach(() => TestBed.configureTestingModule({})); + + it('should be created', () => { + const service: InMemoryDataService = TestBed.get(InMemoryDataService); + expect(service).toBeTruthy(); + }); +}); diff --git "a/Cute-Angular/angualr\345\256\230\347\275\221demo/src/app/in-memory-data.service.ts" "b/Cute-Angular/angualr\345\256\230\347\275\221demo/src/app/in-memory-data.service.ts" new file mode 100644 index 00000000..91250c2a --- /dev/null +++ "b/Cute-Angular/angualr\345\256\230\347\275\221demo/src/app/in-memory-data.service.ts" @@ -0,0 +1,29 @@ +import { Injectable } from '@angular/core'; +import { Hero } from './hero'; +import { InMemoryDbService } from 'angular-in-memory-web-api'; + +@Injectable({ + providedIn: 'root' +}) +export class InMemoryDataService implements InMemoryDbService { + createDb() { + const heroes = [ + { id: 11, name: 'Mr. Nice' }, + { id: 12, name: 'Narco' }, + { id: 13, name: 'Bombasto' }, + { id: 14, name: 'Celeritas' }, + { id: 15, name: 'Magneta' }, + { id: 16, name: 'RubberMan' }, + { id: 17, name: 'Dynama' }, + { id: 18, name: 'Dr IQ' }, + { id: 19, name: 'Magma' }, + { id: 20, name: 'Tornado' } + ] + return {heroes}; + } + genId(heroes: Hero[]): number { + return heroes.length > 0? Math.max(...heroes.map(hero => hero.id)) + 1 : 11; + } + + constructor() { } +} diff --git "a/Cute-Angular/angualr\345\256\230\347\275\221demo/src/app/message.service.spec.ts" "b/Cute-Angular/angualr\345\256\230\347\275\221demo/src/app/message.service.spec.ts" new file mode 100644 index 00000000..24d2d1d3 --- /dev/null +++ "b/Cute-Angular/angualr\345\256\230\347\275\221demo/src/app/message.service.spec.ts" @@ -0,0 +1,12 @@ +import { TestBed } from '@angular/core/testing'; + +import { MessageService } from './message.service'; + +describe('MessageService', () => { + beforeEach(() => TestBed.configureTestingModule({})); + + it('should be created', () => { + const service: MessageService = TestBed.get(MessageService); + expect(service).toBeTruthy(); + }); +}); diff --git "a/Cute-Angular/angualr\345\256\230\347\275\221demo/src/app/message.service.ts" "b/Cute-Angular/angualr\345\256\230\347\275\221demo/src/app/message.service.ts" new file mode 100644 index 00000000..bae6d133 --- /dev/null +++ "b/Cute-Angular/angualr\345\256\230\347\275\221demo/src/app/message.service.ts" @@ -0,0 +1,18 @@ +import { Injectable } from '@angular/core'; + +@Injectable({ + providedIn: 'root' +}) +export class MessageService { + messages: string[] = []; + + add(message: string) { + this.messages.push(message); + }; + + clear(){ + this.messages = []; + }; + + constructor() { } +} diff --git "a/Cute-Angular/angualr\345\256\230\347\275\221demo/src/app/messages/messages.component.css" "b/Cute-Angular/angualr\345\256\230\347\275\221demo/src/app/messages/messages.component.css" new file mode 100644 index 00000000..e69de29b diff --git "a/Cute-Angular/angualr\345\256\230\347\275\221demo/src/app/messages/messages.component.html" "b/Cute-Angular/angualr\345\256\230\347\275\221demo/src/app/messages/messages.component.html" new file mode 100644 index 00000000..586527c2 --- /dev/null +++ "b/Cute-Angular/angualr\345\256\230\347\275\221demo/src/app/messages/messages.component.html" @@ -0,0 +1,12 @@ +
+

Message

+ +
+ {{message}} +
+
\ No newline at end of file diff --git "a/Cute-Angular/angualr\345\256\230\347\275\221demo/src/app/messages/messages.component.spec.ts" "b/Cute-Angular/angualr\345\256\230\347\275\221demo/src/app/messages/messages.component.spec.ts" new file mode 100644 index 00000000..66109cc1 --- /dev/null +++ "b/Cute-Angular/angualr\345\256\230\347\275\221demo/src/app/messages/messages.component.spec.ts" @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { MessagesComponent } from './messages.component'; + +describe('MessagesComponent', () => { + let component: MessagesComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ MessagesComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(MessagesComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git "a/Cute-Angular/angualr\345\256\230\347\275\221demo/src/app/messages/messages.component.ts" "b/Cute-Angular/angualr\345\256\230\347\275\221demo/src/app/messages/messages.component.ts" new file mode 100644 index 00000000..95165a52 --- /dev/null +++ "b/Cute-Angular/angualr\345\256\230\347\275\221demo/src/app/messages/messages.component.ts" @@ -0,0 +1,16 @@ +import { Component, OnInit } from '@angular/core'; +import { MessageService } from '../message.service'; + +@Component({ + selector: 'app-messages', + templateUrl: './messages.component.html', + styleUrls: ['./messages.component.css'] +}) +export class MessagesComponent implements OnInit { + + constructor(public messageService: MessageService) { } + + ngOnInit() { + } + +} diff --git "a/Cute-Angular/angualr\345\256\230\347\275\221demo/src/app/mock-heroes.ts" "b/Cute-Angular/angualr\345\256\230\347\275\221demo/src/app/mock-heroes.ts" new file mode 100644 index 00000000..317e0224 --- /dev/null +++ "b/Cute-Angular/angualr\345\256\230\347\275\221demo/src/app/mock-heroes.ts" @@ -0,0 +1,14 @@ +import { Hero } from './hero'; + +export const HEROES: Hero[] = [ + { id: 11, name: 'Mr. Nice' }, + { id: 12, name: 'Narco' }, + { id: 13, name: 'Bombasto' }, + { id: 14, name: 'Celeritas' }, + { id: 15, name: 'Magneta' }, + { id: 16, name: 'RubberMan' }, + { id: 17, name: 'Dynama' }, + { id: 18, name: 'Dr IQ' }, + { id: 19, name: 'Magma' }, + { id: 20, name: 'Tornado' } +]; \ No newline at end of file diff --git "a/Cute-Angular/angualr\345\256\230\347\275\221demo/src/assets/.gitkeep" "b/Cute-Angular/angualr\345\256\230\347\275\221demo/src/assets/.gitkeep" new file mode 100644 index 00000000..e69de29b diff --git "a/Cute-Angular/angualr\345\256\230\347\275\221demo/src/browserslist" "b/Cute-Angular/angualr\345\256\230\347\275\221demo/src/browserslist" new file mode 100644 index 00000000..37371cb0 --- /dev/null +++ "b/Cute-Angular/angualr\345\256\230\347\275\221demo/src/browserslist" @@ -0,0 +1,11 @@ +# This file is currently used by autoprefixer to adjust CSS to support the below specified browsers +# For additional information regarding the format and rule options, please see: +# https://github.com/browserslist/browserslist#queries +# +# For IE 9-11 support, please remove 'not' from the last line of the file and adjust as needed + +> 0.5% +last 2 versions +Firefox ESR +not dead +not IE 9-11 \ No newline at end of file diff --git "a/Cute-Angular/angualr\345\256\230\347\275\221demo/src/environments/environment.prod.ts" "b/Cute-Angular/angualr\345\256\230\347\275\221demo/src/environments/environment.prod.ts" new file mode 100644 index 00000000..3612073b --- /dev/null +++ "b/Cute-Angular/angualr\345\256\230\347\275\221demo/src/environments/environment.prod.ts" @@ -0,0 +1,3 @@ +export const environment = { + production: true +}; diff --git "a/Cute-Angular/angualr\345\256\230\347\275\221demo/src/environments/environment.ts" "b/Cute-Angular/angualr\345\256\230\347\275\221demo/src/environments/environment.ts" new file mode 100644 index 00000000..7b4f817a --- /dev/null +++ "b/Cute-Angular/angualr\345\256\230\347\275\221demo/src/environments/environment.ts" @@ -0,0 +1,16 @@ +// This file can be replaced during build by using the `fileReplacements` array. +// `ng build --prod` replaces `environment.ts` with `environment.prod.ts`. +// The list of file replacements can be found in `angular.json`. + +export const environment = { + production: false +}; + +/* + * For easier debugging in development mode, you can import the following file + * to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`. + * + * This import should be commented out in production mode because it will have a negative impact + * on performance if an error is thrown. + */ +// import 'zone.js/dist/zone-error'; // Included with Angular CLI. diff --git "a/Cute-Angular/angualr\345\256\230\347\275\221demo/src/favicon.ico" "b/Cute-Angular/angualr\345\256\230\347\275\221demo/src/favicon.ico" new file mode 100644 index 00000000..8081c7ce Binary files /dev/null and "b/Cute-Angular/angualr\345\256\230\347\275\221demo/src/favicon.ico" differ diff --git "a/Cute-Angular/angualr\345\256\230\347\275\221demo/src/index.html" "b/Cute-Angular/angualr\345\256\230\347\275\221demo/src/index.html" new file mode 100644 index 00000000..565f4a57 --- /dev/null +++ "b/Cute-Angular/angualr\345\256\230\347\275\221demo/src/index.html" @@ -0,0 +1,14 @@ + + + + + AngularDemo + + + + + + + + + diff --git "a/Cute-Angular/angualr\345\256\230\347\275\221demo/src/karma.conf.js" "b/Cute-Angular/angualr\345\256\230\347\275\221demo/src/karma.conf.js" new file mode 100644 index 00000000..b6e00421 --- /dev/null +++ "b/Cute-Angular/angualr\345\256\230\347\275\221demo/src/karma.conf.js" @@ -0,0 +1,31 @@ +// Karma configuration file, see link for more information +// https://karma-runner.github.io/1.0/config/configuration-file.html + +module.exports = function (config) { + config.set({ + basePath: '', + frameworks: ['jasmine', '@angular-devkit/build-angular'], + plugins: [ + require('karma-jasmine'), + require('karma-chrome-launcher'), + require('karma-jasmine-html-reporter'), + require('karma-coverage-istanbul-reporter'), + require('@angular-devkit/build-angular/plugins/karma') + ], + client: { + clearContext: false // leave Jasmine Spec Runner output visible in browser + }, + coverageIstanbulReporter: { + dir: require('path').join(__dirname, '../coverage'), + reports: ['html', 'lcovonly'], + fixWebpackSourcePaths: true + }, + reporters: ['progress', 'kjhtml'], + port: 9876, + colors: true, + logLevel: config.LOG_INFO, + autoWatch: true, + browsers: ['Chrome'], + singleRun: false + }); +}; \ No newline at end of file diff --git "a/Cute-Angular/angualr\345\256\230\347\275\221demo/src/main.ts" "b/Cute-Angular/angualr\345\256\230\347\275\221demo/src/main.ts" new file mode 100644 index 00000000..28bfa9e1 --- /dev/null +++ "b/Cute-Angular/angualr\345\256\230\347\275\221demo/src/main.ts" @@ -0,0 +1,13 @@ +import { enableProdMode } from '@angular/core'; +import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; + +import { AppModule } from './app/app.module'; +import { environment } from './environments/environment'; + +if (environment.production) { + enableProdMode(); +} + +platformBrowserDynamic().bootstrapModule(AppModule) + .catch(err => console.error(err)); + diff --git "a/Cute-Angular/angualr\345\256\230\347\275\221demo/src/polyfills.ts" "b/Cute-Angular/angualr\345\256\230\347\275\221demo/src/polyfills.ts" new file mode 100644 index 00000000..d310405a --- /dev/null +++ "b/Cute-Angular/angualr\345\256\230\347\275\221demo/src/polyfills.ts" @@ -0,0 +1,80 @@ +/** + * This file includes polyfills needed by Angular and is loaded before the app. + * You can add your own extra polyfills to this file. + * + * This file is divided into 2 sections: + * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers. + * 2. Application imports. Files imported after ZoneJS that should be loaded before your main + * file. + * + * The current setup is for so-called "evergreen" browsers; the last versions of browsers that + * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera), + * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile. + * + * Learn more in https://angular.io/docs/ts/latest/guide/browser-support.html + */ + +/*************************************************************************************************** + * BROWSER POLYFILLS + */ + +/** IE9, IE10 and IE11 requires all of the following polyfills. **/ +// import 'core-js/es6/symbol'; +// import 'core-js/es6/object'; +// import 'core-js/es6/function'; +// import 'core-js/es6/parse-int'; +// import 'core-js/es6/parse-float'; +// import 'core-js/es6/number'; +// import 'core-js/es6/math'; +// import 'core-js/es6/string'; +// import 'core-js/es6/date'; +// import 'core-js/es6/array'; +// import 'core-js/es6/regexp'; +// import 'core-js/es6/map'; +// import 'core-js/es6/weak-map'; +// import 'core-js/es6/set'; + +/** IE10 and IE11 requires the following for NgClass support on SVG elements */ +// import 'classlist.js'; // Run `npm install --save classlist.js`. + +/** IE10 and IE11 requires the following for the Reflect API. */ +// import 'core-js/es6/reflect'; + + +/** Evergreen browsers require these. **/ +// Used for reflect-metadata in JIT. If you use AOT (and only Angular decorators), you can remove. +import 'core-js/es7/reflect'; + + +/** + * Web Animations `@angular/platform-browser/animations` + * Only required if AnimationBuilder is used within the application and using IE/Edge or Safari. + * Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0). + **/ +// import 'web-animations-js'; // Run `npm install --save web-animations-js`. + +/** + * By default, zone.js will patch all possible macroTask and DomEvents + * user can disable parts of macroTask/DomEvents patch by setting following flags + */ + + // (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame + // (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick + // (window as any).__zone_symbol__BLACK_LISTED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames + + /* + * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js + * with the following flag, it will bypass `zone.js` patch for IE/Edge + */ +// (window as any).__Zone_enable_cross_context_check = true; + +/*************************************************************************************************** + * Zone JS is required by default for Angular itself. + */ +import 'zone.js/dist/zone'; // Included with Angular CLI. + + + +/*************************************************************************************************** + * APPLICATION IMPORTS + */ diff --git "a/Cute-Angular/angualr\345\256\230\347\275\221demo/src/styles.css" "b/Cute-Angular/angualr\345\256\230\347\275\221demo/src/styles.css" new file mode 100644 index 00000000..90d4ee00 --- /dev/null +++ "b/Cute-Angular/angualr\345\256\230\347\275\221demo/src/styles.css" @@ -0,0 +1 @@ +/* You can add global styles to this file, and also import other style files */ diff --git "a/Cute-Angular/angualr\345\256\230\347\275\221demo/src/test.ts" "b/Cute-Angular/angualr\345\256\230\347\275\221demo/src/test.ts" new file mode 100644 index 00000000..16317897 --- /dev/null +++ "b/Cute-Angular/angualr\345\256\230\347\275\221demo/src/test.ts" @@ -0,0 +1,20 @@ +// This file is required by karma.conf.js and loads recursively all the .spec and framework files + +import 'zone.js/dist/zone-testing'; +import { getTestBed } from '@angular/core/testing'; +import { + BrowserDynamicTestingModule, + platformBrowserDynamicTesting +} from '@angular/platform-browser-dynamic/testing'; + +declare const require: any; + +// First, initialize the Angular testing environment. +getTestBed().initTestEnvironment( + BrowserDynamicTestingModule, + platformBrowserDynamicTesting() +); +// Then we find all the tests. +const context = require.context('./', true, /\.spec\.ts$/); +// And load the modules. +context.keys().map(context); diff --git "a/Cute-Angular/angualr\345\256\230\347\275\221demo/src/tsconfig.app.json" "b/Cute-Angular/angualr\345\256\230\347\275\221demo/src/tsconfig.app.json" new file mode 100644 index 00000000..190fd300 --- /dev/null +++ "b/Cute-Angular/angualr\345\256\230\347\275\221demo/src/tsconfig.app.json" @@ -0,0 +1,11 @@ +{ + "extends": "../tsconfig.json", + "compilerOptions": { + "outDir": "../out-tsc/app", + "types": [] + }, + "exclude": [ + "test.ts", + "**/*.spec.ts" + ] +} diff --git "a/Cute-Angular/angualr\345\256\230\347\275\221demo/src/tsconfig.spec.json" "b/Cute-Angular/angualr\345\256\230\347\275\221demo/src/tsconfig.spec.json" new file mode 100644 index 00000000..de773363 --- /dev/null +++ "b/Cute-Angular/angualr\345\256\230\347\275\221demo/src/tsconfig.spec.json" @@ -0,0 +1,18 @@ +{ + "extends": "../tsconfig.json", + "compilerOptions": { + "outDir": "../out-tsc/spec", + "types": [ + "jasmine", + "node" + ] + }, + "files": [ + "test.ts", + "polyfills.ts" + ], + "include": [ + "**/*.spec.ts", + "**/*.d.ts" + ] +} diff --git "a/Cute-Angular/angualr\345\256\230\347\275\221demo/src/tslint.json" "b/Cute-Angular/angualr\345\256\230\347\275\221demo/src/tslint.json" new file mode 100644 index 00000000..52e2c1a5 --- /dev/null +++ "b/Cute-Angular/angualr\345\256\230\347\275\221demo/src/tslint.json" @@ -0,0 +1,17 @@ +{ + "extends": "../tslint.json", + "rules": { + "directive-selector": [ + true, + "attribute", + "app", + "camelCase" + ], + "component-selector": [ + true, + "element", + "app", + "kebab-case" + ] + } +} diff --git "a/Cute-Angular/angualr\345\256\230\347\275\221demo/tsconfig.json" "b/Cute-Angular/angualr\345\256\230\347\275\221demo/tsconfig.json" new file mode 100644 index 00000000..916247e4 --- /dev/null +++ "b/Cute-Angular/angualr\345\256\230\347\275\221demo/tsconfig.json" @@ -0,0 +1,21 @@ +{ + "compileOnSave": false, + "compilerOptions": { + "baseUrl": "./", + "outDir": "./dist/out-tsc", + "sourceMap": true, + "declaration": false, + "module": "es2015", + "moduleResolution": "node", + "emitDecoratorMetadata": true, + "experimentalDecorators": true, + "target": "es5", + "typeRoots": [ + "node_modules/@types" + ], + "lib": [ + "es2017", + "dom" + ] + } +} diff --git "a/Cute-Angular/angualr\345\256\230\347\275\221demo/tslint.json" "b/Cute-Angular/angualr\345\256\230\347\275\221demo/tslint.json" new file mode 100644 index 00000000..6ddb6b29 --- /dev/null +++ "b/Cute-Angular/angualr\345\256\230\347\275\221demo/tslint.json" @@ -0,0 +1,131 @@ +{ + "rulesDirectory": [ + "node_modules/codelyzer" + ], + "rules": { + "arrow-return-shorthand": true, + "callable-types": true, + "class-name": true, + "comment-format": [ + true, + "check-space" + ], + "curly": true, + "deprecation": { + "severity": "warn" + }, + "eofline": true, + "forin": true, + "import-blacklist": [ + true, + "rxjs/Rx" + ], + "import-spacing": true, + "indent": [ + true, + "spaces" + ], + "interface-over-type-literal": true, + "label-position": true, + "max-line-length": [ + true, + 140 + ], + "member-access": false, + "member-ordering": [ + true, + { + "order": [ + "static-field", + "instance-field", + "static-method", + "instance-method" + ] + } + ], + "no-arg": true, + "no-bitwise": true, + "no-console": [ + true, + "debug", + "info", + "time", + "timeEnd", + "trace" + ], + "no-construct": true, + "no-debugger": true, + "no-duplicate-super": true, + "no-empty": false, + "no-empty-interface": true, + "no-eval": true, + "no-inferrable-types": [ + true, + "ignore-params" + ], + "no-misused-new": true, + "no-non-null-assertion": true, + "no-redundant-jsdoc": true, + "no-shadowed-variable": true, + "no-string-literal": false, + "no-string-throw": true, + "no-switch-case-fall-through": true, + "no-trailing-whitespace": true, + "no-unnecessary-initializer": true, + "no-unused-expression": true, + "no-use-before-declare": true, + "no-var-keyword": true, + "object-literal-sort-keys": false, + "one-line": [ + true, + "check-open-brace", + "check-catch", + "check-else", + "check-whitespace" + ], + "prefer-const": true, + "quotemark": [ + true, + "single" + ], + "radix": true, + "semicolon": [ + true, + "always" + ], + "triple-equals": [ + true, + "allow-null-check" + ], + "typedef-whitespace": [ + true, + { + "call-signature": "nospace", + "index-signature": "nospace", + "parameter": "nospace", + "property-declaration": "nospace", + "variable-declaration": "nospace" + } + ], + "unified-signatures": true, + "variable-name": false, + "whitespace": [ + true, + "check-branch", + "check-decl", + "check-operator", + "check-separator", + "check-type" + ], + "no-output-on-prefix": true, + "use-input-property-decorator": true, + "use-output-property-decorator": true, + "use-host-property-decorator": true, + "no-input-rename": true, + "no-output-rename": true, + "use-life-cycle-interface": true, + "use-pipe-transform-interface": true, + "component-class-suffix": true, + "directive-class-suffix": true + } +} diff --git "a/Cute-Angular/books\351\241\271\347\233\256demo/README.md" "b/Cute-Angular/books\351\241\271\347\233\256demo/README.md" new file mode 100644 index 00000000..0eb45f08 --- /dev/null +++ "b/Cute-Angular/books\351\241\271\347\233\256demo/README.md" @@ -0,0 +1,46 @@ +## 本文目录 +* 一、[项目起步](https://github.com/pingan8787/Leo-JavaScript/blob/master/Angular/books%E9%A1%B9%E7%9B%AEdemo/angular_books_1.md) +* 二、[编写路由组件](https://github.com/pingan8787/Leo-JavaScript/blob/master/Angular/books%E9%A1%B9%E7%9B%AEdemo/angular_books_1.md) +* 三、[编写页面组件](https://github.com/pingan8787/Leo-JavaScript/blob/master/Angular/books%E9%A1%B9%E7%9B%AEdemo/angular_books_2.md) + * 1.[编写单一组件](https://github.com/pingan8787/Leo-JavaScript/blob/master/Angular/books%E9%A1%B9%E7%9B%AEdemo/angular_books_2.md) + * 2.[模拟数据](https://github.com/pingan8787/Leo-JavaScript/blob/master/Angular/books%E9%A1%B9%E7%9B%AEdemo/angular_books_2.md) + * 3.[编写主从组件](https://github.com/pingan8787/Leo-JavaScript/blob/master/Angular/books%E9%A1%B9%E7%9B%AEdemo/angular_books_2.md) +* 四、[编写服务](https://github.com/pingan8787/Leo-JavaScript/blob/master/Angular/books%E9%A1%B9%E7%9B%AEdemo/angular_books_3.md) + * 1.[为什么需要服务](https://github.com/pingan8787/Leo-JavaScript/blob/master/Angular/books%E9%A1%B9%E7%9B%AEdemo/angular_books_3.md) + * 2.[编写服务](https://github.com/pingan8787/Leo-JavaScript/blob/master/Angular/books%E9%A1%B9%E7%9B%AEdemo/angular_books_3.md) +* 五、[引入RxJS](https://github.com/pingan8787/Leo-JavaScript/blob/master/Angular/books%E9%A1%B9%E7%9B%AEdemo/angular_books_3.md) + * 1.[关于RxJS](https://github.com/pingan8787/Leo-JavaScript/blob/master/Angular/books%E9%A1%B9%E7%9B%AEdemo/angular_books_3.md) + * 2.[引入RxJS](https://github.com/pingan8787/Leo-JavaScript/blob/master/Angular/books%E9%A1%B9%E7%9B%AEdemo/angular_books_3.md) + * 3.[改造数据获取方式](https://github.com/pingan8787/Leo-JavaScript/blob/master/Angular/books%E9%A1%B9%E7%9B%AEdemo/angular_books_3.md) +* 六、[改造组件](https://github.com/pingan8787/Leo-JavaScript/blob/master/Angular/books%E9%A1%B9%E7%9B%AEdemo/angular_books_4.md) + * 1.[添加历史记录组件](https://github.com/pingan8787/Leo-JavaScript/blob/master/Angular/books%E9%A1%B9%E7%9B%AEdemo/angular_books_4.md) + * 2.[添加和删除历史记录](https://github.com/pingan8787/Leo-JavaScript/blob/master/Angular/books%E9%A1%B9%E7%9B%AEdemo/angular_books_4.md) +* 七、[HTTP改造](https://github.com/pingan8787/Leo-JavaScript/blob/master/Angular/books%E9%A1%B9%E7%9B%AEdemo/angular_books_4.md) + * 1.[引入HTTP](https://github.com/pingan8787/Leo-JavaScript/blob/master/Angular/books%E9%A1%B9%E7%9B%AEdemo/angular_books_4.md) + * 2.[通过HTTP请求数据](https://github.com/pingan8787/Leo-JavaScript/blob/master/Angular/books%E9%A1%B9%E7%9B%AEdemo/angular_books_4.md) + * 3.[通过HTTP修改数据](https://github.com/pingan8787/Leo-JavaScript/blob/master/Angular/books%E9%A1%B9%E7%9B%AEdemo/angular_books_4.md) + * 4.[通过HTTP增加数据](https://github.com/pingan8787/Leo-JavaScript/blob/master/Angular/books%E9%A1%B9%E7%9B%AEdemo/angular_books_4.md) + * 5.[通过HTTP删除数据](https://github.com/pingan8787/Leo-JavaScript/blob/master/Angular/books%E9%A1%B9%E7%9B%AEdemo/angular_books_4.md) + * 6.[通过HTTP查找数据](https://github.com/pingan8787/Leo-JavaScript/blob/master/Angular/books%E9%A1%B9%E7%9B%AEdemo/angular_books_4.md) +* 八、结语 + +## 项目介绍 + +这个入门项目是我学习完[Angular 英雄指南教程](https://angular.cn/tutorial)后,自己手写的一个练习项目,一步一步来,最终的[项目源码可以这里查看](https://github.com/pingan8787/Leo-JavaScript/tree/master/Cute-Angular/books%E9%A1%B9%E7%9B%AEdemo),大佬们请指点啦。 + +推荐两个Angular学习网站: +1. [Angular 中文网](https://angular.cn/) +2. [Angular 修仙之路](http://www.semlinker.com/) + +还有呢,我没怎么关注到样式,所以样式会有点丑,主要都放在核心逻辑中了。 +**最终实现:** +* 首页书本列表数据展示 +* 各个页面静态/动态路由跳转 +* 本地模拟数据服务 +* 书本数据的增删改查 +* 父子组件通信 +* 常用指令使用和介绍 + +![图片结果](https://camo.githubusercontent.com/ded9be340188be8d3cb8ee2fd6adfe614f07e043/68747470733a2f2f757365722d676f6c642d63646e2e786974752e696f2f323031392f322f32332f313639313832386637643766663866373f773d38393526683d38343726663d706e6726733d313031303632) + +后面我将把这个系列的文章,收录到我的[【CuteJavaScript】](http://js.pingan8787.com)中,里面有整理了**ES6/7/8/9知识点**和**重温JS基础系列**文章。 \ No newline at end of file diff --git "a/Cute-Angular/books\351\241\271\347\233\256demo/angular_books_1.md" "b/Cute-Angular/books\351\241\271\347\233\256demo/angular_books_1.md" new file mode 100644 index 00000000..1e904465 --- /dev/null +++ "b/Cute-Angular/books\351\241\271\347\233\256demo/angular_books_1.md" @@ -0,0 +1,275 @@ +## 本文目录 +* **一、项目起步** +* **二、编写路由组件** +* 三、编写页面组件 + * 1.编写单一组件 + * 2.模拟数据 + * 3.编写主从组件 +* 四、编写服务 + * 1.为什么需要服务 + * 2.编写服务 +* 五、引入RxJS + * 1.关于RxJS + * 2.引入RxJS + * 3.改造数据获取方式 +* 六、改造组件 + * 1.添加历史记录组件 + * 2.添加和删除历史记录 +* 七、HTTP改造 + * 1.引入HTTP + * 2.通过HTTP请求数据 + * 3.通过HTTP修改数据 + * 4.通过HTTP增加数据 + * 5.通过HTTP删除数据 + * 6.通过HTTP查找数据 +* 八、结语 + + + +这个入门项目是我学习完[Angular 英雄指南教程](https://angular.cn/tutorial)后,自己手写的一个练习项目,一步一步来,最终的[项目源码可以这里查看](https://github.com/pingan8787/Leo-JavaScript/tree/master/Cute-Angular/books%E9%A1%B9%E7%9B%AEdemo),大佬们请指点啦。 + +推荐两个Angular学习网站: +1. [Angular 中文网](https://angular.cn/) +2. [Angular 修仙之路](http://www.semlinker.com/) + +还有呢,我没怎么关注到样式,所以样式会有点丑,主要都放在核心逻辑中了。 +**最终实现:** +* 首页书本列表数据展示 +* 各个页面静态/动态路由跳转 +* 本地模拟数据服务 +* 书本数据的增删改查 +* 父子组件通信 +* 常用指令使用和介绍 + +![图片结果](https://camo.githubusercontent.com/ded9be340188be8d3cb8ee2fd6adfe614f07e043/68747470733a2f2f757365722d676f6c642d63646e2e786974752e696f2f323031392f322f32332f313639313832386637643766663866373f773d38393526683d38343726663d706e6726733d313031303632) + +后面我将把这个系列的文章,收录到我的[【CuteJavaScript】](http://js.pingan8787.com)中,里面有整理了**ES6/7/8/9知识点**和**重温JS基础系列**文章。 + +那么,快跟我一步步来完成这个入门项目吧。 + +## 零、Angular安装 +Angular 需要 `Node.js` 的 `8.x` 或 `10.x` 版本。 +检查你的`Node.js`版本,请在终端/控制台窗口中运行 `node -v` 命令。 +要想安装` Node.js`,请访问 nodejs.org。 + +1. 安装Angular CLI + +```sh +npm install -g @angular/cli +``` + +2. 常用命令 + +后续用到会详细介绍这些命令。 + +* 启动服务,并打开新窗口 +```sh +ng serve --open +# --open 可简写 -o +``` + +* 创建新组件 +```sh +ng generate component books +# generate 可简写 g +``` + +* 创建新服务 +```sh +ng generate service books +``` + +* 创建路由模块 +```sh +ng generate module app-routing --flat --module=app +``` + +* 其他 +另外Angular CLI还有很多的命令提供,详细可以查阅官方文档 [Angular CLI 命令](https://angular.cn/cli)。 + +最后搭建完是这样: + +![图片创建](https://camo.githubusercontent.com/9c67d00f8b707eae15fd6c5d1a907942641909ad/68747470733a2f2f757365722d676f6c642d63646e2e786974752e696f2f323031392f322f32332f313639313832386637633431386237393f773d35373926683d36333126663d706e6726733d3239313137) + +## 一、项目起步 +1. 创建项目 +```sh +ng new books +cd books +``` + +2. 创建所需的两个页面组件 +```sh +ng g component index +ng g component detail +``` +`g`是`generate`的简写。 + +然后运行项目: + +```sh +ng serve --open +``` + +## 二、编写路由组件 +这里为了项目结构先起来,所以先简单配置一下路由,后面路由会调整,如果遇到什么不懂,可以查看[Angular 路由与导航](https://angular.cn/guide/router)。 + +1. 安装**路由模块** +```sh +ng g module app-routing --flat --module=app +``` +**知识点:** +`--flat` 把这个文件放进了 `src/app` 中,而不是单独的目录中。 +`--module=app` 告诉 CLI 把它注册到 `AppModule` 的 `imports` 数组中。 + +2. 引入**路由模块** +```js +// app-routing.module.ts +import { RouterModule, Routes } from '@angular/router'; +``` +3. 导出**路由模块**的指令 + +这里需要添加一个 `@NgModule.exports` 数组,并传入`RouterModule`,导出 `RouterModule` 让路由器的相关指令可以在 `AppModule` 中的组件中使用。 +```js +// app-routing.module.ts +@NgModule({ + imports: [CommonModule], + declarations: [], + exports: [RouterModule] +}) +``` + +4. 添加定义路由 + +这里添加路由的时候,记得将所需要指向的组件也引入进来,这里我们需要引入两个页面的组件: +```js +// app-routing.module.ts +import { IndexComponent } from './index/index.component'; +import { DetailComponent } from './detail/detail.component'; +``` +然后将我们所需要的路由定义在`routes`变量中,类型是我们引入的`Routes`: +```js +// app-routing.module.ts +const routes: Routes = [ + { path: '', redirectTo:'/index', pathMatch:'full' }, // 1 + { path: 'index', component: IndexComponent}, // 2 + { path: 'detail/:id', component: DetailComponent}, // 3 +] +``` +**知识点**: +`angular`的路由接收两个参数: +* `path`:用于匹配浏览器地址栏中 `URL` 的字符串。 +* `component`:当导航到此路由时,路由器展示的组件名称。 + +**第1行代码**: +作为路由系统的默认路由,当所有路由都不匹配的话,就会重定向到这个路由,并展示对应的组件。 +**第2行代码**: +正常情况下的路由配置。 +**第3行代码**: +配置的是携带参数的路由,在路由`/`后,用 `:` 拼接参数名来实现,**获取这个参数的值的方法后面会介绍**。 + +另外,我们还可以这么传递参数,直接将数据通过路由传入,后面还会介绍: +```js +{ path: 'pathname', component: DemoComponent, data: { title: 'pingan8787' } }, +``` + +5. 添加路由监视 + +配置好路由还不能使用,需要一个监视路由变化的工具,这时候需要把`RouterModule`添加到 `@NgModule.imports` 数组中,并用 `routes` 来配置它。 +这里只需要调用` imports `数组中的 `RouterModule.forRoot()` 函数就行了,就像这样: +```js +// app-routing.module.ts +imports: [ RouterModule.forRoot(routes) ], +``` + +6. 添加路由出口 + +所谓的路由出口,就是路由所对应的组件展示的地方,接下来我们在`app.component.html`内容中,添加``: +```html + +
+

欢迎来到我的个人书屋!

+ +
+``` +这里的``就是我们路由输出的地方,也是组件展示的地方,简单理解就是,它会告诉路由器要在哪里显示路由的视图。 + +7. 添加路由链接 + +所谓的路由链接,就是出发路由跳转事件的地方,比如一个按钮,一张图片等,我们还是在`app.component.html`中,使用``添加3个按钮: +```html + +
+

欢迎来到我的个人书屋!

+ 重定向 | + 打开首页 | + 打开书本详情 + +
+``` +这边3个按钮的路由,我们将上面定义的3种路由,传入到`routerLink`参数中,现在就项目就可以实现页面跳转了。 + +另外,这里还可以传入一个可选参数`routerLinkActive="className"`,表示当这个``标签激活的时候显示的样式,值是一个字符串,为样式的类名: +```html +打开首页 | +``` + +8. 获取带参数路由的参数 + +在第7步中,我们点击 **打开书本详情** 按钮中,在路由中带了参数,这时候我们需要这么来获取这个参数: +* 先导出模块`ActivatedRoute`和`Location`: +```js +// detail.component.ts +import { ActivatedRoute } from '@angular/router'; +import { Location } from '@angular/common'; +``` +* 再注入到构造函数中,并将值作为私有变量: +```js +// detail.component.ts +export class DetailComponent implements OnInit { + constructor( + private route: ActivatedRoute, + private location: Location + ) { } + ngOnInit() {} +} +``` +**知识点:** +`ActivatedRoute` 保存该 `DetailComponent` 实例的路由信息。可以从这个组件获取URL中的路由参数和其他数据。 +`Location` 是一个 `Angular` 的服务,用来与浏览器打交道。后续会使用它来导航回上一个视图。 + +* 提取路由参数: + +这里声明`getDetail`方法,提取路由参数,并`ngOnInit`**生命周期钩子方法**在中执行。 +```js +// detail.component.ts +ngOnInit() { + this.getDetail() +} +getDetail(): void{ + const id = +this.route.snapshot.paramMap.get('id'); + console.log(`此课本的id是${id}`) +} +``` +**知识点**: +`route.snapshot` 是一个路由信息的**静态快照**,抓取自组件刚刚创建完毕之后。 +`paramMap` 是一个URL中路由所携带的参数值的对象。"id"对应的值就是要获取的书本的 id。 +**注意**: +路由参数总会是字符串。这里我们使用 (+) 操作符,将字符串转换成数字。 + +现在在浏览器上刷新下页面,再点击 **打开书本详情** 按钮,可以看到控制台输出了` 此课本的id是1 `的结果。 +到这一步,我们算是把路由配置完成了,接下来可以开始做页面的逻辑了。 + +**本部分内容到这结束** + +|Author|王平安| +|---|---| +|E-mail|pingan8787@qq.com| +|博 客|www.pingan8787.com| +|微 信|pingan8787| +|每日文章推荐|https://github.com/pingan8787/Leo_Reading/issues| +|JS小册|js.pingan8787.com| +|微信公众号|前端自习课| + + +![前端自习课](https://camo.githubusercontent.com/7d890fb10cccf99c03dcf144e0e290357195ac44/68747470733a2f2f757365722d676f6c642d63646e2e786974752e696f2f323031392f322f31362f313638663439663032333831393163613f773d3130373826683d36343726663d706e6726733d323832353135) diff --git "a/Cute-Angular/books\351\241\271\347\233\256demo/angular_books_2.md" "b/Cute-Angular/books\351\241\271\347\233\256demo/angular_books_2.md" new file mode 100644 index 00000000..1ab75c51 --- /dev/null +++ "b/Cute-Angular/books\351\241\271\347\233\256demo/angular_books_2.md" @@ -0,0 +1,227 @@ +## 本文目录 +* 一、[项目起步](https://juejin.im/post/5c70ae586fb9a049c64476a0) +* 二、[编写路由组件](https://juejin.im/post/5c70ae586fb9a049c64476a0) +* 三、**编写页面组件** + * 1.**编写单一组件** + * 2.**模拟数据** + * 3.**编写主从组件** +* 四、编写服务 + * 1.为什么需要服务 + * 2.编写服务 +* 五、引入RxJS + * 1.关于RxJS + * 2.引入RxJS + * 3.改造数据获取方式 +* 六、改造组件 + * 1.添加历史记录组件 + * 2.添加和删除历史记录 +* 七、HTTP改造 + * 1.引入HTTP + * 2.通过HTTP请求数据 + * 3.通过HTTP修改数据 + * 4.通过HTTP增加数据 + * 5.通过HTTP删除数据 + * 6.通过HTTP查找数据 + + +## 三、编写页面组件 +接下来开始编写页面组件,这里我们挑重点来写,一些布局的样式,后面可以看源码。 + +### 1.编写单一组件 +我们首先写一个书本信息的组件,代码如下: +```html + +
+
+ +
+ + +
程姬
+
+
+
+``` +**知识点**: +`*ngFor` 是一个 Angular 的复写器(repeater)指令,就像**angular1**中的`ng-for`和**vuejs**中的`v-for`。 它会为列表中的每项数据复写它的宿主元素。 +这时候可以看到页面变成下面这个样子: +![图片3-1](http://images.pingan8787.com/angular_books_3_1.png) + + +接下来我们要把写死在HTML上面的数据,抽到JS中: + +现在先新建一个`books.ts`文件来定义一个`Book`类,并添加`id`,`url`,`title`和`author`四个属性: +```js +// src/app/books.ts +export class Book { + id: number; + url: string; + title: string; + author: string; +} +``` +然后回到`index.component.ts`文件去引入它,并定义一个`books`属性,使用导入进来的`Book`类作为类型: +```js +// index.component.ts +import { Book } from '../books'; +export class IndexComponent implements OnInit { + books: Book = { + id: 1, + url: 'https://img3.doubanio.com/view/subject/m/public/s29988481.jpg', + title: '像火焰像灰烬', + author: '程姬', + } +} +``` +然后再改造前面的组件文件`index.component.html`: +```html + +
+ {{books.id}} + +
{{books.author}}
+
+``` + +接着,我们再为每个课本添加一个点击事件,来实现点击封面图能查看大图的效果,现在`index.component.ts`中定义一个`getDetailImage`方法,并在`index.component.html`中绑定该方法: +```js +// index.component.ts +export class IndexComponent implements OnInit { + getDetailImage(books){ + alert(`正在查看id为${books.id}的大图!`); + } +} +``` +这边方法的具体实现,不写,不是本文重点。下面是增加点击事件的绑定: +```html + +{{books.id}} +``` +**知识点**: +`(click)`是Angular用来绑定事件,它会让 Angular 监听这个` ` 元素的 `click` 事件。 当用户点击 `` 时,Angular 就会执行表达式 `getDetailImage(books)`。 + +再来,我们引入前面学到的**路由链接**指令来改造HTML: +```html + +{{books.title}} +``` +这时候,我们在点击书本的标题,发现页面跳转到URL地址为`http://localhost:4200/detail/1`的页面,这就说明,我们页面的路由跳转也成功了~ + +改造完成后,可以看到,页面显示的还是一样,接下来我们先这样放着,因为我们后面会进行数据模拟,和模拟服务器请求。 + +我们就这样写好第一个单一组件,并且数据是从JS中读取的。 + +### 2.模拟数据 +这时候为了方便后面数据渲染,我们这里需要模拟一些本地数据,我们创建一个本地` mock-books.ts`文件来存放模拟的数据: +```js +// app/mock-books.ts +import { Books } from './books'; +export const BookList: Books[] = [ + { + id: 1, + url: 'https://img3.doubanio.com/view/subject/m/public/s29988481.jpg', + title: '像火焰像灰烬', + author: '程姬', + }, + // 省略其他9条 +] +``` +然后在`index.component.ts`中导入模拟的数据,并将原有的`books`值修改成导入的模拟数据`BookList`: +```js +// index.component.ts +import { BookList } from '../mock-books'; +books = BookList; +``` +并将原本的`*ngFor`中修改成这样,绑定真正的数据: +```html + +
+ {{item.id}} + +
{{item.author}}
+
+``` + +### 3.编写主从组件 +当我们写完一个单一组件后,我们会发现,如果我们把每个组件都写到同一个HTML文件中,这是很糟糕的事情,这样做有缺点: +* 代码复用性差;(导致每次相同功能要重新写) +* 代码难维护;(因为一个文件会非常长) +* 影响性能;(打开每个页面都要重复加载很多) + +为了解决这个问题,我们这里就要开始使用真正的**组件化思维**,将通用常用组件抽离出来,通过参数传递来控制组件的不同业务形态。 +这便是我们接下来要写的主从组件。 + +思考一下,我们这里现在能抽成组件作为公共代码的,就是这个单个书本的内容,因为每个书本的内容都一致,只是里面数据的差异,于是我们再新建一个组件: +```sh +ng g component books +``` +并将前面`index.component.html`中关于课本的代码剪切到`books.component.html`中来,然后删除掉`*ngFor`的内容,并将原本本地的变量`books`替换成`list`,这个变量我们等会会取到: +```html + +
+ {{list.id}} + +
{{list.author}}
+
+``` +再将这个组件,引用到它的父组件中,这里是要引用到`index.component.html`的组件中,并将前面的`*ngFor`再次传入``: +```html +
+
+ +
+
+``` + +接下来要做的就是获取到`list`变量的值,显然这个值是要从外面组件传进来的,我们需要在`books.component.ts`引入前面定义的 `Books`类 和 `@Input() 装饰器`,还要添加一个带有 `@Input() 装饰器`的 `list` 属性,另外还要记得将`getDetailImage`方法也剪切过来: +```js +// books.component.ts +import { Component, OnInit, Input } from '@angular/core'; +import { Books } from '../books'; + +export class BooksComponent implements OnInit { + @Input() list: Books; + constructor() { } + ngOnInit() {} + getDetailImage(books){ + alert(`正在查看id为${books.id}的大图!`); + } +} +``` +`@Input() 装饰器`介绍具体可以查看 [手册](https://angular.cn/guide/template-syntax#inputs-outputs) + +我们要获取的 `list` 属性必须是一个带有` @Input() `装饰器的输入属性,因为外部的 `IndexComponent` 组件将会绑定到它。就像这样: +```html + +``` + +**知识点**: +`[list]="item"` 是 `Angular` 的**属性绑定**语法。这是一种**单向数据绑定**。从 `IndexComponent` 的 `item` 属性绑定到目标元素的 `list` 属性,并映射到了 `BooksComponent` 的 `list` 属性。 + +做到这里,我们已经将`BooksComponent`作为`IndexComponent`的子组件来引用了,在实际开发过程中,这样的父子组件关系,会用的非常多。 + +写到这里,看看我们项目,还是一样正常在运行,只是现在项目中组件分工更加明确了。 + +现在的效果图: +![图片3-2](http://images.pingan8787.com/angular_books_3_2.png) + + +**本部分内容到这结束** + +|Author|王平安| +|---|---| +|E-mail|pingan8787@qq.com| +|博 客|www.pingan8787.com| +|微 信|pingan8787| +|每日文章推荐|https://github.com/pingan8787/Leo_Reading/issues| +|JS小册|js.pingan8787.com| +|微信公众号|前端自习课| + + +![前端自习课](https://camo.githubusercontent.com/7d890fb10cccf99c03dcf144e0e290357195ac44/68747470733a2f2f757365722d676f6c642d63646e2e786974752e696f2f323031392f322f31362f313638663439663032333831393163613f773d3130373826683d36343726663d706e6726733d323832353135) diff --git "a/Cute-Angular/books\351\241\271\347\233\256demo/angular_books_3.md" "b/Cute-Angular/books\351\241\271\347\233\256demo/angular_books_3.md" new file mode 100644 index 00000000..b98ff92e --- /dev/null +++ "b/Cute-Angular/books\351\241\271\347\233\256demo/angular_books_3.md" @@ -0,0 +1,251 @@ +## 本文目录 +* 一、[项目起步](https://juejin.im/post/5c70ae586fb9a049c64476a0) +* 二、[编写路由组件](https://juejin.im/post/5c70ae586fb9a049c64476a0) +* 三、[编写页面组件](https://juejin.im/post/5c70b48d6fb9a04a0b22cbce) + * 1.[编写单一组件](https://juejin.im/post/5c70b48d6fb9a04a0b22cbce) + * 2.[模拟数据](https://juejin.im/post/5c70b48d6fb9a04a0b22cbce) + * 3.[编写主从组件](https://juejin.im/post/5c70b48d6fb9a04a0b22cbce) +* 四、**编写服务** + * 1.**为什么需要服务** + * 2.**编写服务** +* 五、**引入RxJS** + * 1.**关于RxJS** + * 2.**引入RxJS** + * 3.**改造数据获取方式** +* 六、改造组件 + * 1.添加历史记录组件 + * 2.添加和删除历史记录 +* 七、HTTP改造 + * 1.引入HTTP + * 2.通过HTTP请求数据 + * 3.通过HTTP修改数据 + * 4.通过HTTP增加数据 + * 5.通过HTTP删除数据 + * 6.通过HTTP查找数据 + +## 四、编写服务 +截止到这部分,我们的`BooksComponent`组件获取和显示的都是本地模拟的数据。 +接下来我们要开始对这些进行重构,让聚焦于为它的视图提供支持,这也让它更容易使用模拟服务进行单元测试。 + +### 1.为什么需要服务 +我们不应该让组件来直接获取或保存数据,它们应该聚焦于展示数据,而数据访问的工作交给其他服务来做。 +这里我们需要创建一个名为`BooksService`的服务,让我们应用中所有的类都使用它来获取书本列表的数据,使用的时候,只需要将它通过Angular的**依赖注入机制**注入到需要用的组件的构造函数中。 + +**知识点:** +服务可以实现多个不同组件之间信息共享,后面我们还会将它注入到两个地方: +`BooksService`中,使用该服务发送消息。 +`IndexService`中,使用该服务来展示消息。 + +接下来我们使用命令行,创建`BooksService `: +```sh +ng g service books +``` +在生成的`books.service.ts`文件中: +```js +// books.service.ts +import { Injectable } from '@angular/core'; +@Injectable({ + providedIn: 'root' +}) +``` +新导入了`@Injectable`装饰器,是为了让`BooksService`提供一个可注入的服务,并且它还可以拥有自己的待注入的依赖,简单理解就是**如果你的服务需要依赖,那么你就需要导入它**。 +并且它接收该服务的元数据对象。 + +### 2.编写服务 +接下来我们开始编写`books.service.ts`服务。 + +* 导入服务所需组件 + +这里我们导入`Books`和`BookList`,并添加一个`getBooks`方法来返回所有书本的数据,并且还需要添加一个`getBooks`方法来返回指定id的书本信息: +```js +// index.component.ts +import { Books } from './books'; +import { BookList } from './mock-books'; +@Injectable({ + providedIn: 'root' +}) +export class BooksService { + constructor() { } + getBookList(): Books[] { + return BookList; + } + getBook(id: number): Books{ + return BookList.find(book => book.id === id) + } +} +``` +在我们使用这个服务之前,需要先注册该服务,因为我们在使用`ng g service books`命令创建服务时,CLI已经默认为我们添加了注册了,这是方法就是上面代码中的: +```js +providedIn: 'root' +``` +表示将我们的服务注册在**根注入器**上,这样我们就可以把这个服务注入到任何享用的类上了。 + +* 修改`IndexComponent` + +先删除`BookList`的引入,并修改`books`属性的定义: + +```js +// index.component.ts +import { BooksService } from '../books.service'; +export class IndexComponent implements OnInit { + books : Books[]; + ngOnInit() {} +} +``` +然后注入我们的`BooksService`服务,需要先往构造函数中添加一个私有的`booksservice`,使用注入的`BooksService`作为类型,理解成一个注入点: +```js +// index.component.ts +constructor(private booksservice: BooksService) { } +``` + +之后我们需要添加一个`getBooks`方法来获取这些书本数据,并在生命周期函数`ngOnInit`中调用: +```js +export class IndexComponent implements OnInit { + ngOnInit() { + this.getBooks(); + } + getBooks(): void{ + this.books = this.booksservice.getBookList(); + } +} +``` + +* 修改`DetailComponent` +我们先改造书本详情页的HTML结构: +```html + +
+

《{{books.title}}》介绍

+
+ +
+

书本标题: {{books.title}}

+

书本作者: {{books.author}}

+

书本id: {{books.id}}

+
+
+

暂无信息

+
+``` +**知识点**: +这里使用了`*ngIf`指令,当条件为`true`则显示其HTML内容。 + +```js +// detail.component.ts +import { Books } from '../books'; +import { BooksService } from '../books.service'; +export class DetailComponent implements OnInit { + constructor( + private route: ActivatedRoute, + private location: Location, + private booksservice: BooksService // 引入BooksService服务 + ) { } + + books: Books; // 定义books类型 + ngOnInit() { + this.getDetail() + } + getDetail(): void{ + const id = +this.route.snapshot.paramMap.get('id'); + this.getBooks(id); + } + getBooks(id: number): void { + this.books = this.booksservice.getBook(id); + } +} +``` +这段代码,主要定义了`getBooks`方法,当刚进入页面时,将书本`id`传入`getBooks`方法,去`BooksService`去获取对应id的书本信息,并复制给变量`books`,然后展示到页面。 + +改造之后,我们的页面显示依旧正常。 + +![图片3-2](http://images.pingan8787.com/angular_books_3_2.png) + +但是我们要知道,这背后的逻辑已经改变了。 + +## 五、引入RxJS改造项目 +### 1.关于RxJS +这里简单介绍关键概念,具体可以查看 [RxJS 官网](https://RxJS.dev/),也可以参考 [浅析Angular之RxJS](https://www.jianshu.com/p/36d85f8cafdd)。 + +#### 什么是RxJS + +RxJS全称`Reactive Extensions for JavaScript`,中文意思: JavaScript的响应式扩展。 +RxJS主要是提供一种更加强大和优雅的方式,来利用响应式编程的模式,实现JavaScript的异步编程。 + +#### RxJS优点 + +* 纯净性; +* 流动性; + +#### RxJS核心概念 + +RxJS 是基于观察者模式和迭代器模式以函数式编程思维来实现的。RxJS 中含有两个基本概念:`Observables` 与 `Observer`。 +`Observables` 作为被观察者,是一个值或事件的流集合;而 `Observer` 则作为观察者,根据 `Observables` 进行处理。它们之间的订阅发布关系(观察者模式) 如下: +**订阅**:`Observer` 通过 `Observable` 提供的 `subscribe()` 方法订阅 `Observable`。 +**发布**:`Observable` 通过回调 `next` 方法向 `Observer` 发布事件。 + +———— 来源[Angular修仙之路 RxJS Observable](http://www.semlinker.com/rxjs-observable/) + +另外这里列出来一些核心,具体还是看官网咯,并且下面使用到的时候会具体介绍。 +* `Observable` (可观察对象): 表示一个概念,这个概念是一个可调用的未来值或事件的集合。 +* `Observer`(观察者): 一个回调函数的集合,它知道如何去监听由 `Observable` 提供的值。 +* `Subscription` (订阅): 表示 `Observable` 的执行,主要用于取消 `Observable` 的执行。 +* `Operators` (操作符): 采用函数式编程风格的纯函数 (`pure function`),使用像 `map`、`filter`、`concat`、`flatMap` 等这样的操作符来处理集合。 +* `Subject` (主体): 相当于 `EventEmitter`,并且是将值或事件多路推送给多个 `Observer` 的唯一方式。 +* `Schedulers` (调度器): 用来控制并发并且是中央集权的调度员,允许我们在发生计算时进行协调,例如 `setTimeout` 或` requestAnimationFrame `或其他。 + +### 2.引入RxJS +在我们的真实应用中,我们必须要等到服务器响应后,我们才能获取到数据,因此这天生就需要用异步思维来操作。 + +由于Angular中已经自带RxJS,所以我们只要在需要使用的时候,引入即可使用: + +### 3.改造数据获取方式 +了解完RxJS的一些概念后,我们开始改造下这些书本的数据获取方式。 + +* 改造`BooksService` + +首先我们从 RxJS 中导入 `Observable` 和 `of` 符号: +```js +// books.service.ts +import { Observable, of } from 'rxjs'; +``` +**知识点**: +`Observable`: 观察者模式中的观察者,具体可以参考 [Angular修仙之路 RxJS Observable](http://www.semlinker.com/rxjs-observable/) +`of`: 用来获取观察者拿到的数据,通常是一个`Observable`。 + +然后修改`getBookList`方法 +```js +// books.service.ts +getBookList(): Observable { + return of(BookList); +} +``` +这里 `of(BookList)` 返回一个` Observable`,它会发出单个值,这个值就是这些模拟书本的数组。 + +* 改造`IndexComponent` + +这里也要修改`getBooks`方法,使用`subscribe`去订阅服务返回回来的值: +```js +// index.component.ts +getBooks(): void{ + this.booksservice.getBookList() + .subscribe(books => this.books = books); +} +``` +由于原本直接赋值数据,在实际场景中是不可能这样同步的,所以这里`subscribe`函数,会在`Observable`发出数据以后,再把书本列表传到里面的回调函数,再复制给`books`属性。 +使用这种异步方式,当 `BooksService` 从远端服务器获取英雄数据时,不用担心还没拿到数据就执行后面。 + +下一步,我们就要改造一下项目了。 + +**本部分内容到这结束** + +|Author|王平安| +|---|---| +|E-mail|pingan8787@qq.com| +|博 客|www.pingan8787.com| +|微 信|pingan8787| +|每日文章推荐|https://github.com/pingan8787/Leo_Reading/issues| +|JS小册|js.pingan8787.com| +|微信公众号|前端自习课| + + +![前端自习课](https://camo.githubusercontent.com/7d890fb10cccf99c03dcf144e0e290357195ac44/68747470733a2f2f757365722d676f6c642d63646e2e786974752e696f2f323031392f322f31362f313638663439663032333831393163613f773d3130373826683d36343726663d706e6726733d323832353135) diff --git "a/Cute-Angular/books\351\241\271\347\233\256demo/angular_books_4.md" "b/Cute-Angular/books\351\241\271\347\233\256demo/angular_books_4.md" new file mode 100644 index 00000000..f99493dd --- /dev/null +++ "b/Cute-Angular/books\351\241\271\347\233\256demo/angular_books_4.md" @@ -0,0 +1,562 @@ +## 本文目录 +* 一、[项目起步](https://juejin.im/post/5c70ae586fb9a049c64476a0) +* 二、[编写路由组件](https://juejin.im/post/5c70ae586fb9a049c64476a0) +* 三、[编写页面组件](https://juejin.im/post/5c70b48d6fb9a04a0b22cbce) + * 1.[编写单一组件](https://juejin.im/post/5c70b48d6fb9a04a0b22cbce) + * 2.[模拟数据](https://juejin.im/post/5c70b48d6fb9a04a0b22cbce) + * 3.[编写主从组件](https://juejin.im/post/5c70b48d6fb9a04a0b22cbce) +* 四、[编写服务](https://juejin.im/post/5c70b5486fb9a049d7484fdf) + * 1.[为什么需要服务](https://juejin.im/post/5c70b5486fb9a049d7484fdf) + * 2.[编写服务](https://juejin.im/post/5c70b5486fb9a049d7484fdf) +* 五、[引入RxJS](https://juejin.im/post/5c70b5486fb9a049d7484fdf) + * 1.[关于RxJS](https://juejin.im/post/5c70b5486fb9a049d7484fdf) + * 2.[引入RxJS](https://juejin.im/post/5c70b5486fb9a049d7484fdf) + * 3.[改造数据获取方式](https://juejin.im/post/5c70b5486fb9a049d7484fdf) +* 六、**改造组件** + * 1.**添加历史记录组件** + * 2.**添加和删除历史记录** +* 七、**HTTP改造** + * 1.**引入HTTP** + * 2.**通过HTTP请求数据** + * 3.**通过HTTP修改数据** + * 4.**通过HTTP增加数据** + * 5.**通过HTTP删除数据** + * 6.**通过HTTP查找数据** + + +## 六、改造组件 +从这里开始,我们要使用RxJS来改造组件和添加新功能了,让整个项目更加完善。 + +### 1.添加历史记录组件 + +* 创建`HistoryComponent`组件 +```sh +ng g component hostory +``` +然后在`app.component.html`文件夹中添加组件: +```html + + +``` + +### 2.添加增删改查功能 + +这里我们要开始做书本的增删改查功能,需要先创建一个`HistoryService`服务,方便我们实现这几个功能: + +* 创建`HistoryService`服务 +```sh +ng g service history +``` +然后在生成的ts文件中,增加`add`和`clear`方法,`add`方法用来添加历史记录到`history`数组中,`clear`方法则是清空`history`数组: +```js +// history.service.ts +export class HistoryService { + history: string[] = []; + add(history: string){ + this.history.push(history); + } + clear(){ + this.history = []; + } +} +``` + +* 使用`HistoryService`服务 + +在将这个服务,注入到`BooksService`中,并改造`getBooks`方法: +```js +// books.service.ts +import { HistoryService } from './history.service'; +constructor( + private historyservice: HistoryService +) { } +getBooks(): void{ + this.historyservice.add('请求书本数据') + this.booksservice.getBookList() + .subscribe(books => this.books = books); +} +``` +也可以用相同方法,在`IndexComponent`中添加`访问首页书本列表`的记录。 +```js +// index.component.ts +import { HistoryService } from '../history.service'; +constructor( + private booksservice: BooksService, + private historyservice: HistoryService +) { } +getBooks(): void{ + this.historyservice.add('访问首页书本列表'); + this.booksservice.getBookList() + .subscribe(books => this.books = books); +} +``` + +接下来,将我们的`HistoryService`注入到`HistoryComponent`中,然后才能将历史数据显示到页面上: +```js +// history.component.ts +import { HistoryService } from '../history.service'; +export class HistoryComponent implements OnInit { + constructor(private historyservice: HistoryService) { } + ngOnInit() {} +} +``` +```html + +
+

操作历史:

+
+ +
{{item}}
+
+
+``` +**代码解释**: +`*ngIf="historyservice.history.length"`,是为了防止还没有拿到历史数据,导致后面的报错。 +`(click)="historyservice.clear()"`, 绑定我们服务中的`clear`事件,实现清除缓存。 +`*ngFor="let item of historyservice.history"`,将我们的历史数据渲染到页面上。 + + +到了这一步,就能看到历史数据了,每次也换到首页,都会增加一条。 + +![图片5-1](http://images.pingan8787.com/angular_books_5_1.png) + +接下来,我们要在书本详情页也加上历史记录的统计,导入文件,注入服务,然后改造`getBooks`方法,实现历史记录的统计: +```js +// detail.component.ts +import { HistoryService } from '../history.service'; + +export class DetailComponent implements OnInit { + constructor( + private route: ActivatedRoute, + private location: Location, + private booksservice: BooksService, + private historyservice: HistoryService + ) { } + //... + getBooks(id: number): void { + this.books = this.booksservice.getBook(id); + this.historyservice.add(`查看书本${this.books.title},id为${this.books.id}`); + console.log(this.books) + } +} +``` +![图片5-2](http://images.pingan8787.com/angular_books_5_2.png) + +这时候就可以在历史记录中,看到这些操作的记录了,并且**清除**按钮也正常使用。 + +## 七、HTTP改造 +原本我只想写到上一章,但是想到,我们实际开发中,哪有什么本地数据,基本上数据都是要从服务端去请求,所以这边也有必要引入这一张,模拟实际的HTTP请求。 + +### 1.引入HTTP +在这一章,我们使用Angular提供的 `HttpClient` 来添加一些数据持久化特性。 +然后实现对书本数据进行**获取,增加,修改,删除和查找**功能。 + +`HttpClient`是Angular通过 HTTP 与远程服务器通讯的机制。 + +这里我们为了让`HttpClient`在整个应用全局使用,所以将`HttpClient`导入到根模块`app.module.ts`中,然后把它加入 `@NgModule.imports` 数组: +```js +import { HttpClientModule } from '@angular/common/http'; +@NgModule({ + //... + imports: [ + BrowserModule, + AppRoutingModule, + HttpClientModule + ], + //... +}) +``` + +这边我们使用 [内存 Web API(In-memory Web API) ](https://github.com/angular/in-memory-web-api)模拟出的远程数据服务器通讯。 +**注意:** 这个内存 Web API 模块与 Angular 中的 HTTP 模块无关。 + +通过下面命令来安装: +```sh +npm install angular-in-memory-web-api --save +``` +然后在`app.module.ts`中导入 `HttpClientInMemoryWebApiModule` 和 `InMemoryDataService` 类(后面创建): +```js +// app.module.ts +import { HttpClientInMemoryWebApiModule } from 'angular-in-memory-web-api'; +import { InMemoryDataService } from './in-memory-data.service'; +@NgModule({ + // ... + imports: [ + // ... + HttpClientInMemoryWebApiModule.forRoot( + InMemoryDataService, {dataEncapsulation:false} + ) + ], + // ... +}) +export class AppModule { } +``` +**知识点:** +`forRoot()` 配置方法接受一个 InMemoryDataService 类(初期的内存数据库)作为参数。 + +然后我们要创建`InMemoryDataService`类: +```sh +ng g service InMemoryData +``` +并将生成的`in-memory-data.service.ts`修改为: +```js +// in-memory-data.service.ts +import { Injectable } from '@angular/core'; +import { InMemoryDbService } from 'angular-in-memory-web-api'; +import { Books } from './books'; +@Injectable({ + providedIn: 'root' +}) +export class InMemoryDataService implements InMemoryDbService { + createDb(){ + const books = [ + { + id: 1, + url: 'https://img3.doubanio.com/view/subject/m/public/s29988481.jpg', + title: '像火焰像灰烬', + author: '程姬', + }, + // 省略其他9条数据 + ]; + return {books}; + } + constructor() { } +} +``` + +这里先总结`InMemoryDbService`所提供的RESTful API,后面都要用到: +例如如果`url`是`api/books`,那么 +* 查询所有成员:以**GET**方法访问`api/books` +* 查询某个成员:以**GET**方法访问`api/books/id`,比如`id`是`1`,那么访问`api/books/1` +* 更新某个成员:以**PUT**方法访问`api/books/id` +* 删除某个成员:以**DELETE**方法访问`api/books/id` +* 增加一个成员:以**POST**方法访问`api/books` + + +### 2.通过HTTP请求数据 + +现在要为接下来的网络请求做一些准备,先在`books.service.ts`中引入HTTP符号,然后注入`HttpClient`并改造: +```js +// books.service.ts +import { HttpClient, HttpHeaders} from '@angular/common/http'; +// ... +export class BooksService { + constructor( + private historyservice: HistoryService, + private http: HttpClient + ) { } + private log(histories: string){ + this.historyservice.add(`正在执行:${histories}`) + } + private booksUrl = 'api/books'; // 提供一个API供调用 + // ... +} +``` +这里我们还新增一个私有方法`log`和一个私有变量`booksUrl`。 + +接下来我们要开始发起http请求数据,开始改造`getBookList`方法: +```js +// books.service.ts +// ... +getBookList(): Observable { + this.historyservice.add('请求书本数据') + return this.http.get(this.booksUrl); +} +// ... +``` +这里我们使用 `http.get` 替换了 `of`,其它没修改,但是应用仍然在正常工作,这是因为这两个函数都返回了 `Observable`。 + +实际开发中,我们还需要考虑到**请求的错误处理**,要捕获错误,我们就要使用 RxJS 的 `catchError()` 操作符来建立对 Observable 结果的处理管道(pipe)。 + +我们引入`catchError `并改造原本`getBookList`方法: + +```js +// books.service.ts +getBookList(): Observable { + this.historyservice.add('请求书本数据') + return this.http.get(this.booksUrl).pipe( + catchError(this.handleError('getHeroes', [])) + ); +} +private handleError (operation = 'operation', result?: T) { + return (error: any): Observable => { + this.log(`${operation} 失败: ${error.message}`); // 发出错误通知 + return of(result as T); // 返回空结果避免程序出错 + }; +} +``` +**知识点**: +`.pipe()` 方法用来扩展 `Observable` 的结果。 +`catchError()` 操作符会拦截失败的 Observable。并把错误对象传给错误处理器,错误处理器会处理这个错误。 +`handleError()` 错误处理函数做了两件事,发出错误通知和返回空结果避免程序出错。 + +这里还需要使用`tap`操作符改造`getBookList`方法,来窥探`Observable`数据流,它会查看`Observable`的值,然后我们使用`log`方法,记录一条历史记录。 +`tap` 回调不会改变这些值本身。 +```js +// books.service.ts +getBookList(): Observable { + return this.http.get(this.booksUrl) + .pipe( + tap( _ => this.log('请求书本数据')), + catchError(this.handleError('getHeroes', [])) + ); +} +``` + +### 3.通过HTTP修改数据 +这里我们需要在原来`DetailComponent`上面,添加一个输入框、保存按钮和返回按钮,就像这样: +```html + + +
+

修改信息:

+ + + +
+``` +这边切记一点,一定要在`app.module.ts`中引入 `FormsModule`模块,并在`@NgModule`的`imports`中引入,不然要报错了。 +```js +// app.module.ts +// ... +import { FormsModule } from '@angular/forms'; +@NgModule({ + // ... + imports: [ + // ... + FormsModule + ], + // ... +}) +``` +`input`框绑定书本的标题`books.title`,而保存按钮绑定一个`save()`方法,这里还要实现这个方法: +```js +// detail.component.ts +save(): void { + this.historyservice.updateBooks(this.books) + .subscribe(() => this.goBack()); +} +goBack(): void { + this.location.back(); +} +``` +这里通过调用`BooksService`的`updateBooks`方法,将当前修改后的书本信息修改到源数据中,这里我们需要去`books.service.ts`中添加`updateBooks`方法: +```js +// books.service.ts +// ... +updateBooks(books: Books): Observable{ + return this.http.put(this.booksUrl, books, httpOptions).pipe( + tap(_ => this.log(`修改书本的id是${books.id}`)), + catchError(this.handleError(`getBooks请求是id为${books.id}`)) + ) +} +// ... +``` +**知识点**: +`HttpClient.put()` 方法接受三个参数:`URL 地址`、`要修改的数据`和`其他选项`。 +`httpOptions` 常量需要定义在`@Injectable`修饰器之前。 + +现在,我们点击首页,选择一本书进入详情,修改标题然后保存,会发现,首页上这本书的名称也会跟着改变呢。这算是好了。 + + +### 4.通过HTTP增加数据 +我们可以新增一个页面,并添加上路由和按钮: +```sh +ng g component add +``` +添加路由: +```js +// app-routing.module.ts +// ... +import { AddComponent } from './add/add.component'; + +const routes: Routes = [ + { path: '', redirectTo:'/index', pathMatch:'full' }, + { path: 'index', component: IndexComponent}, + { path: 'detail/:id', component: DetailComponent}, + { path: 'add', component: AddComponent}, +] +``` +添加路由入口: +```html + + +添加书本 +``` +编辑添加书本的页面: +```html + +
+

添加书本:

+ + + + +
+
+``` +初始化添加书本的数据: +```js +// add.component.ts +// ... +import { Books } from '../books'; +import { BooksService } from '../books.service'; +import { HistoryService } from '../history.service'; +import { Location } from '@angular/common'; +export class AddComponent implements OnInit { + books: Books = { + id: 0, + url: '', + title: '', + author: '' + } + constructor( + private location: Location, + private booksservice: BooksService, + private historyservice: HistoryService + ) { } + ngOnInit() {} + add(books: Books): void{ + books.title = books.title.trim(); + books.author = books.author.trim(); + this.booksservice.addBooks(books) + .subscribe( book => { + this.historyservice.add(`新增书本${books.title},id为${books.id}`); + this.location.back(); + }); + } +} +``` +然后在`books.service.ts`中添加`addBooks`方法,来添加一本书本的数据: +```js +// books.service.ts +addBooks(books: Books): Observable{ + return this.http.post(this.booksUrl, books, httpOptions).pipe( + tap((newBook: Books) => this.log(`新增书本的id为${newBook.id}`)), + catchError(this.handleError('添加新书')) + ); +} +``` + + +现在就可以正常添加书本啦。 + +![图片5-3](http://images.pingan8787.com/angular_books_5_3.png) + + +### 5.通过HTTP删除数据 +这里我们先为每个书本后面添加一个删除按钮,并绑定删除事件`delete`: +```html + + +X +``` +```js +// books.component.ts +import { BooksService } from '../books.service'; +export class BooksComponent implements OnInit { + @Input() list: Books; + constructor( + private booksservice: BooksService + ) { } + // ... + delete(books: Books): void { + this.booksservice.deleteBooks(books) + .subscribe(); + } +} +``` +然后还要再`books.service.ts`中添加`deleteBooks`方法来删除: +```js +// books.service.ts +deleteBooks(books: Books): Observable{ + const id = books.id; + const url = `${this.booksUrl}/${id}`; + return this.http.delete(url, httpOptions).pipe( + tap(_ => this.log(`删除书本${books.title},id为${books.id}`)), + catchError(this.handleError('删除书本')) + ); +} +``` +这里需要在删除书本结束后,通知`IndexComponent`将数据列表中的这条数据删除,这里还需要再了解一下[Angular 父子组件数据通信](https://blog.csdn.net/u010730126/article/details/68080139)。 +然后我们在父组件`IndexComponent`上添加`change`事件监听,并传入本地的`funChange`: +```html + + +``` +在对应的`index.component.ts`中添加`funChange`方法: +```js +// index.component.ts +funChange(books, $event){ + this.books = this.books.filter(h => h.id !== books.id); +} +``` + +再来,我们在子组件`BooksComponent`上多导入`Output`和`EventEmitter`,并添加`@Output()`修饰器和调用`emit`: +```js +import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core'; +export class BooksComponent implements OnInit { + // ... + @Output() + change = new EventEmitter() + // ... + delete(books: Books): void { + this.booksservice.deleteBooks(books) + .subscribe(()=>{ + this.change.emit(books); + }); + } +} +``` +这样就实现了我们父子组件之间的事件传递啦,现在我们的页面还是正常运行,并且删除一条数据后,页面数据会更新。 + + +### 6.通过HTTP查找数据 +还是在`books.service.ts`,我们添加一个方法`getBooks`,来实现通过ID来查找指定书本,因为我们是通过ID查找,所以返回的是单个数据,这里就是`Observable`类型: +```js +// books.service.ts +getBooks(id: number): Observable{ + const url = `${this.booksUrl}/${id}`; + return this.http.get(url).pipe( + tap( _ => this.log(`请求书本的id为${id}`)), + catchError(this.handleError(`getBooks请求是id为${id}`)) + ) +} +``` +注意,这里 `getBooks` 会返回 `Observable`,是一个可观察的单个对象,而不是一个可观察的对象数组。 + + +## 八、结语 +这个项目其实很简单,但是我还是一步一步的写下来,一方面让自己更熟悉Angular,另一方面也是希望能帮助到更多朋友哈~ +最终效果: + +![图片结果](http://images.pingan8787.com/angular_books_result.png) + + +**本部分内容到这结束** + +|Author|王平安| +|---|---| +|E-mail|pingan8787@qq.com| +|博 客|www.pingan8787.com| +|微 信|pingan8787| +|每日文章推荐|https://github.com/pingan8787/Leo_Reading/issues| +|JS小册|js.pingan8787.com| +|微信公众号|前端自习课| + + +![前端自习课](https://camo.githubusercontent.com/7d890fb10cccf99c03dcf144e0e290357195ac44/68747470733a2f2f757365722d676f6c642d63646e2e786974752e696f2f323031392f322f31362f313638663439663032333831393163613f773d3130373826683d36343726663d706e6726733d323832353135) diff --git "a/Cute-Angular/books\351\241\271\347\233\256demo/angular_books_all.md" "b/Cute-Angular/books\351\241\271\347\233\256demo/angular_books_all.md" new file mode 100644 index 00000000..77c29e9b --- /dev/null +++ "b/Cute-Angular/books\351\241\271\347\233\256demo/angular_books_all.md" @@ -0,0 +1,1197 @@ +* 零、Angular安装 +* 一、项目起步 +* 二、编写路由组件 +* 三、编写页面组件 + * 1.编写单一组件 + * 2.模拟数据 + * 3.编写主从组件 +* 四、编写服务 + * 1.为什么需要服务 + * 2.编写服务 +* 五、引入RxJS改造项目 + * 1.关于RxJS + * 2.引入RxJS + * 3.改造数据获取方式 +* 六、改造组件 + * 1.添加历史记录组件 + * 2.添加增删改查功能 +* 七、HTTP改造 + * 1.引入HTTP + * 2.通过HTTP请求数据 + * 3.通过HTTP修改数据 + * 4.通过HTTP增加数据 + * 5.通过HTTP删除数据 + * 6.通过HTTP查找数据 +* 八、结语 + + +这个入门项目是我学习完[Angular 英雄指南教程](https://angular.cn/tutorial)后,自己手写的一个练习项目,一步一步来,最终的[项目源码可以这里查看](https://github.com/pingan8787/Leo-JavaScript/tree/master/Cute-Angular/books%E9%A1%B9%E7%9B%AEdemo),大佬们请指点啦。 + +推荐两个Angular学习网站: +1. [Angular 中文网](https://angular.cn/) +2. [Angular 修仙之路](http://www.semlinker.com/) + +还有呢,我没怎么关注到样式,所以样式会有点丑,主要都放在核心逻辑中了。 +**最终实现:** +* 首页书本列表数据展示 +* 各个页面静态/动态路由跳转 +* 本地模拟数据服务 +* 书本数据的增删改查 +* 父子组件通信 +* 常用指令使用和介绍 + +![图片结果](https://camo.githubusercontent.com/ded9be340188be8d3cb8ee2fd6adfe614f07e043/68747470733a2f2f757365722d676f6c642d63646e2e786974752e696f2f323031392f322f32332f313639313832386637643766663866373f773d38393526683d38343726663d706e6726733d313031303632) + +后面我将把这个系列的文章,收录到我的[【CuteJavaScript】](http://js.pingan8787.com)中,里面有整理了**ES6/7/8/9知识点**和**重温JS基础系列**文章。 + +那么,快跟我一步步来完成这个入门项目吧。 + +## 零、Angular安装 +Angular 需要 `Node.js` 的 `8.x` 或 `10.x` 版本。 +检查你的`Node.js`版本,请在终端/控制台窗口中运行 `node -v` 命令。 +要想安装` Node.js`,请访问 nodejs.org。 + +1. 安装Angular CLI + +```sh +npm install -g @angular/cli +``` + +2. 常用命令 + +后续用到会详细介绍这些命令。 + +* 启动服务,并打开新窗口 +```sh +ng serve --open +# --open 可简写 -o +``` + +* 创建新组件 +```sh +ng generate component books +# generate 可简写 g +``` + +* 创建新服务 +```sh +ng generate service books +``` + +* 创建路由模块 +```sh +ng generate module app-routing --flat --module=app +``` + +* 其他 +另外Angular CLI还有很多的命令提供,详细可以查阅官方文档 [Angular CLI 命令](https://angular.cn/cli)。 + +最后搭建完是这样: + +![图片创建](https://camo.githubusercontent.com/9c67d00f8b707eae15fd6c5d1a907942641909ad/68747470733a2f2f757365722d676f6c642d63646e2e786974752e696f2f323031392f322f32332f313639313832386637633431386237393f773d35373926683d36333126663d706e6726733d3239313137) + +## 一、项目起步 +1. 创建项目 +```sh +ng new books +cd books +``` + +2. 创建所需的两个页面组件 +```sh +ng g component index +ng g component detail +``` +`g`是`generate`的简写。 + + +## 二、编写路由组件 +这里为了项目结构先起来,所以先简单配置一下路由,后面路由会调整,如果遇到什么不懂,可以查看[Angular 路由与导航](https://angular.cn/guide/router)。 + +1. 安装**路由模块** +```sh +ng g module app-routing --flat --module=app +``` +**知识点:** +`--flat` 把这个文件放进了 `src/app` 中,而不是单独的目录中。 +`--module=app` 告诉 CLI 把它注册到 `AppModule` 的 `imports` 数组中。 + +2. 引入**路由模块** +```js +// app-routing.module.ts +import { RouterModule, Routes } from '@angular/router'; +``` +3. 导出**路由模块**的指令 + +这里需要添加一个 `@NgModule.exports` 数组,并传入`RouterModule`,导出 `RouterModule` 让路由器的相关指令可以在 `AppModule` 中的组件中使用。 +```js +// app-routing.module.ts +@NgModule({ + imports: [CommonModule], + declarations: [], + exports: [RouterModule] +}) +``` + +4. 添加定义路由 + +这里添加路由的时候,记得将所需要指向的组件也引入进来,这里我们需要引入两个页面的组件: +```js +// app-routing.module.ts +import { IndexComponent } from './index/index.component'; +import { DetailComponent } from './detail/detail.component'; +``` +然后将我们所需要的路由定义在`routes`变量中,类型是我们引入的`Routes`: +```js +// app-routing.module.ts +const routes: Routes = [ + { path: '', redirectTo:'/index', pathMatch:'full' }, // 1 + { path: 'index', component: IndexComponent}, // 2 + { path: 'detail/:id', component: DetailComponent}, // 3 +] +``` +**知识点**: +`angular`的路由接收两个参数: +* `path`:用于匹配浏览器地址栏中 `URL` 的字符串。 +* `component`:当导航到此路由时,路由器展示的组件名称。 + +**第1行代码**: +作为路由系统的默认路由,当所有路由都不匹配的话,就会重定向到这个路由,并展示对应的组件。 +**第2行代码**: +正常情况下的路由配置。 +**第3行代码**: +配置的是携带参数的路由,在路由`/`后,用 `:` 拼接参数名来实现,**获取这个参数的值的方法后面会介绍**。 + +另外,我们还可以这么传递参数,直接将数据通过路由传入,后面还会介绍: +```js +{ path: 'pathname', component: DemoComponent, data: { title: 'pingan8787' } }, +``` + +5. 添加路由监视 + +配置好路由还不能使用,需要一个监视路由变化的工具,这时候需要把`RouterModule`添加到 `@NgModule.imports` 数组中,并用 `routes` 来配置它。 +这里只需要调用` imports `数组中的 `RouterModule.forRoot()` 函数就行了,就像这样: +```js +// app-routing.module.ts +imports: [ RouterModule.forRoot(routes) ], +``` + +6. 添加路由出口 + +所谓的路由出口,就是路由所对应的组件展示的地方,接下来我们在`app.component.html`内容中,添加``: +```html + +
+

欢迎来到我的个人书屋!

+ +
+``` +这里的``就是我们路由输出的地方,也是组件展示的地方,简单理解就是,它会告诉路由器要在哪里显示路由的视图。 + +7. 添加路由链接 + +所谓的路由链接,就是出发路由跳转事件的地方,比如一个按钮,一张图片等,我们还是在`app.component.html`中,使用``添加3个按钮: +```html + +
+

欢迎来到我的个人书屋!

+ 重定向 | + 打开首页 | + 打开书本详情 + +
+``` +这边3个按钮的路由,我们将上面定义的3种路由,传入到`routerLink`参数中,现在就项目就可以实现页面跳转了。 + +另外,这里还可以传入一个可选参数`routerLinkActive="className"`,表示当这个``标签激活的时候显示的样式,值是一个字符串,为样式的类名: +```html +打开首页 | +``` + +8. 获取带参数路由的参数 + +在第7步中,我们点击 **打开书本详情** 按钮中,在路由中带了参数,这时候我们需要这么来获取这个参数: +* 先导出模块`ActivatedRoute`和`Location`: +```js +// detail.component.ts +import { ActivatedRoute } from '@angular/router'; +import { Location } from '@angular/common'; +``` +* 再注入到构造函数中,并将值作为私有变量: +```js +// detail.component.ts +export class DetailComponent implements OnInit { + constructor( + private route: ActivatedRoute, + private location: Location + ) { } + ngOnInit() {} +} +``` +**知识点:** +`ActivatedRoute` 保存该 `DetailComponent` 实例的路由信息。可以从这个组件获取URL中的路由参数和其他数据。 +`Location` 是一个 `Angular` 的服务,用来与浏览器打交道。后续会使用它来导航回上一个视图。 + +* 提取路由参数: + +这里声明`getDetail`方法,提取路由参数,并`ngOnInit`**生命周期钩子方法**在中执行。 +```js +// detail.component.ts +ngOnInit() { + this.getDetail() +} +getDetail(): void{ + const id = +this.route.snapshot.paramMap.get('id'); + console.log(`此课本的id是${id}`) +} +``` +**知识点**: +`route.snapshot` 是一个路由信息的**静态快照**,抓取自组件刚刚创建完毕之后。 +`paramMap` 是一个URL中路由所携带的参数值的对象。"id"对应的值就是要获取的书本的 id。 +**注意**: +路由参数总会是字符串。这里我们使用 (+) 操作符,将字符串转换成数字。 + +现在在浏览器上刷新下页面,再点击 **打开书本详情** 按钮,可以看到控制台输出了` 此课本的id是1 `的结果。 +到这一步,我们算是把路由配置完成了,接下来可以开始做页面的逻辑了。 + + + +## 三、编写页面组件 +接下来开始编写页面组件,这里我们挑重点来写,一些布局的样式,后面可以看源码。 + +### 1.编写单一组件 +我们首先写一个书本信息的组件,代码如下: +```html + +
+
+ +
+ + +
程姬
+
+
+
+``` +**知识点**: +`*ngFor` 是一个 Angular 的复写器(repeater)指令,就像**angular1**中的`ng-for`和**vuejs**中的`v-for`。 它会为列表中的每项数据复写它的宿主元素。 +这时候可以看到页面变成下面这个样子: +![图片3-1](http://images.pingan8787.com/angular_books_3_1.png) + + +接下来我们要把写死在HTML上面的数据,抽到JS中: + +现在先新建一个`books.ts`文件来定义一个`Book`类,并添加`id`,`url`,`title`和`author`四个属性: +```js +// src/app/books.ts +export class Book { + id: number; + url: string; + title: string; + author: string; +} +``` +然后回到`index.component.ts`文件去引入它,并定义一个`books`属性,使用导入进来的`Book`类作为类型: +```js +// index.component.ts +import { Book } from '../books'; +export class IndexComponent implements OnInit { + books: Book = { + id: 1, + url: 'https://img3.doubanio.com/view/subject/m/public/s29988481.jpg', + title: '像火焰像灰烬', + author: '程姬', + } +} +``` +然后再改造前面的组件文件`index.component.html`: +```html + +
+ {{books.id}} + +
{{books.author}}
+
+``` + +接着,我们再为每个课本添加一个点击事件,来实现点击封面图能查看大图的效果,现在`index.component.ts`中定义一个`getDetailImage`方法,并在`index.component.html`中绑定该方法: +```js +// index.component.ts +export class IndexComponent implements OnInit { + getDetailImage(books){ + alert(`正在查看id为${books.id}的大图!`); + } +} +``` +这边方法的具体实现,不写,不是本文重点。下面是增加点击事件的绑定: +```html + +{{books.id}} +``` +**知识点**: +`(click)`是Angular用来绑定事件,它会让 Angular 监听这个` ` 元素的 `click` 事件。 当用户点击 `` 时,Angular 就会执行表达式 `getDetailImage(books)`。 + +再来,我们引入前面学到的**路由链接**指令来改造HTML: +```html + +{{books.title}} +``` +这时候,我们在点击书本的标题,发现页面跳转到URL地址为`http://localhost:4200/detail/1`的页面,这就说明,我们页面的路由跳转也成功了~ + +改造完成后,可以看到,页面显示的还是一样,接下来我们先这样放着,因为我们后面会进行数据模拟,和模拟服务器请求。 + +我们就这样写好第一个单一组件,并且数据是从JS中读取的。 + +### 2.模拟数据 +这时候为了方便后面数据渲染,我们这里需要模拟一些本地数据,我们创建一个本地` mock-books.ts`文件来存放模拟的数据: +```js +// app/mock-books.ts +import { Books } from './books'; +export const BookList: Books[] = [ + { + id: 1, + url: 'https://img3.doubanio.com/view/subject/m/public/s29988481.jpg', + title: '像火焰像灰烬', + author: '程姬', + }, + // 省略其他9条 +] +``` +然后在`index.component.ts`中导入模拟的数据,并将原有的`books`值修改成导入的模拟数据`BookList`: +```js +// index.component.ts +import { BookList } from '../mock-books'; +books = BookList; +``` +并将原本的`*ngFor`中修改成这样,绑定真正的数据: +```html + +
+ {{item.id}} + +
{{item.author}}
+
+``` + +### 3.编写主从组件 +当我们写完一个单一组件后,我们会发现,如果我们把每个组件都写到同一个HTML文件中,这是很糟糕的事情,这样做有缺点: +* 代码复用性差;(导致每次相同功能要重新写) +* 代码难维护;(因为一个文件会非常长) +* 影响性能;(打开每个页面都要重复加载很多) + +为了解决这个问题,我们这里就要开始使用真正的**组件化思维**,将通用常用组件抽离出来,通过参数传递来控制组件的不同业务形态。 +这便是我们接下来要写的主从组件。 + +思考一下,我们这里现在能抽成组件作为公共代码的,就是这个单个书本的内容,因为每个书本的内容都一致,只是里面数据的差异,于是我们再新建一个组件: +```sh +ng g component books +``` +并将前面`index.component.html`中关于课本的代码剪切到`books.component.html`中来,然后删除掉`*ngFor`的内容,并将原本本地的变量`books`替换成`list`,这个变量我们等会会取到: +```html + +
+ {{list.id}} + +
{{list.author}}
+
+``` +再将这个组件,引用到它的父组件中,这里是要引用到`index.component.html`的组件中,并将前面的`*ngFor`再次传入``: +```html +
+
+ +
+
+``` + +接下来要做的就是获取到`list`变量的值,显然这个值是要从外面组件传进来的,我们需要在`books.component.ts`引入前面定义的 `Books`类 和 `@Input() 装饰器`,还要添加一个带有 `@Input() 装饰器`的 `list` 属性,另外还要记得将`getDetailImage`方法也剪切过来: +```js +// books.component.ts +import { Component, OnInit, Input } from '@angular/core'; +import { Books } from '../books'; + +export class BooksComponent implements OnInit { + @Input() list: Books; + constructor() { } + ngOnInit() {} + getDetailImage(books){ + alert(`正在查看id为${books.id}的大图!`); + } +} +``` +`@Input() 装饰器`介绍具体可以查看 [手册](https://angular.cn/guide/template-syntax#inputs-outputs) + +我们要获取的 `list` 属性必须是一个带有` @Input() `装饰器的输入属性,因为外部的 `IndexComponent` 组件将会绑定到它。就像这样: +```html + +``` + +**知识点**: +`[list]="item"` 是 `Angular` 的**属性绑定**语法。这是一种**单向数据绑定**。从 `IndexComponent` 的 `item` 属性绑定到目标元素的 `list` 属性,并映射到了 `BooksComponent` 的 `list` 属性。 + +做到这里,我们已经将`BooksComponent`作为`IndexComponent`的子组件来引用了,在实际开发过程中,这样的父子组件关系,会用的非常多。 + +写到这里,看看我们项目,还是一样正常在运行,只是现在项目中组件分工更加明确了。 + +现在的效果图: +![图片3-2](http://images.pingan8787.com/angular_books_3_2.png) + + + +## 四、编写服务 +截止到这部分,我们的`BooksComponent`组件获取和显示的都是本地模拟的数据。 +接下来我们要开始对这些进行重构,让聚焦于为它的视图提供支持,这也让它更容易使用模拟服务进行单元测试。 + +### 1.为什么需要服务 +我们不应该让组件来直接获取或保存数据,它们应该聚焦于展示数据,而数据访问的工作交给其他服务来做。 +这里我们需要创建一个名为`BooksService`的服务,让我们应用中所有的类都使用它来获取书本列表的数据,使用的时候,只需要将它通过Angular的**依赖注入机制**注入到需要用的组件的构造函数中。 + +**知识点:** +服务可以实现多个不同组件之间信息共享,后面我们还会将它注入到两个地方: +`BooksService`中,使用该服务发送消息。 +`IndexService`中,使用该服务来展示消息。 + +接下来我们使用命令行,创建`BooksService `: +```sh +ng g service books +``` +在生成的`books.service.ts`文件中: +```js +// books.service.ts +import { Injectable } from '@angular/core'; +@Injectable({ + providedIn: 'root' +}) +``` +新导入了`@Injectable`装饰器,是为了让`BooksService`提供一个可注入的服务,并且它还可以拥有自己的待注入的依赖,简单理解就是**如果你的服务需要依赖,那么你就需要导入它**。 +并且它接收该服务的元数据对象。 + +### 2.编写服务 +接下来我们开始编写`books.service.ts`服务。 + +* 导入服务所需组件 + +这里我们导入`Books`和`BookList`,并添加一个`getBooks`方法来返回所有书本的数据,并且还需要添加一个`getBooks`方法来返回指定id的书本信息: +```js +// index.component.ts +import { Books } from './books'; +import { BookList } from './mock-books'; +@Injectable({ + providedIn: 'root' +}) +export class BooksService { + constructor() { } + getBookList(): Books[] { + return BookList; + } + getBook(id: number): Books{ + return BookList.find(book => book.id === id) + } +} +``` +在我们使用这个服务之前,需要先注册该服务,因为我们在使用`ng g service books`命令创建服务时,CLI已经默认为我们添加了注册了,这是方法就是上面代码中的: +```js +providedIn: 'root' +``` +表示将我们的服务注册在**根注入器**上,这样我们就可以把这个服务注入到任何享用的类上了。 + +* 修改`IndexComponent` + +先删除`BookList`的引入,并修改`books`属性的定义: + +```js +// index.component.ts +import { BooksService } from '../books.service'; +export class IndexComponent implements OnInit { + books : Books[]; + ngOnInit() {} +} +``` +然后注入我们的`BooksService`服务,需要先往构造函数中添加一个私有的`booksservice`,使用注入的`BooksService`作为类型,理解成一个注入点: +```js +// index.component.ts +constructor(private booksservice: BooksService) { } +``` + +之后我们需要添加一个`getBooks`方法来获取这些书本数据,并在生命周期函数`ngOnInit`中调用: +```js +export class IndexComponent implements OnInit { + ngOnInit() { + this.getBooks(); + } + getBooks(): void{ + this.books = this.booksservice.getBookList(); + } +} +``` + +* 修改`DetailComponent` +我们先改造书本详情页的HTML结构: +```html + +
+

《{{books.title}}》介绍

+
+ +
+

书本标题: {{books.title}}

+

书本作者: {{books.author}}

+

书本id: {{books.id}}

+
+
+

暂无信息

+
+``` +**知识点**: +这里使用了`*ngIf`指令,当条件为`true`则显示其HTML内容。 + +```js +// detail.component.ts +import { Books } from '../books'; +import { BooksService } from '../books.service'; +export class DetailComponent implements OnInit { + constructor( + private route: ActivatedRoute, + private location: Location, + private booksservice: BooksService // 引入BooksService服务 + ) { } + + books: Books; // 定义books类型 + ngOnInit() { + this.getDetail() + } + getDetail(): void{ + const id = +this.route.snapshot.paramMap.get('id'); + this.getBooks(id); + } + getBooks(id: number): void { + this.books = this.booksservice.getBook(id); + } +} +``` +这段代码,主要定义了`getBooks`方法,当刚进入页面时,将书本`id`传入`getBooks`方法,去`BooksService`去获取对应id的书本信息,并复制给变量`books`,然后展示到页面。 + +改造之后,我们的页面显示依旧正常。 + +![图片3-2](http://images.pingan8787.com/angular_books_3_2.png) + +但是我们要知道,这背后的逻辑已经改变了。 + +## 五、引入RxJS改造项目 +### 1.关于RxJS +这里简单介绍关键概念,具体可以查看 [RxJS 官网](https://RxJS.dev/),也可以参考 [浅析Angular之RxJS](https://www.jianshu.com/p/36d85f8cafdd)。 + +#### 什么是RxJS + +RxJS全称`Reactive Extensions for JavaScript`,中文意思: JavaScript的响应式扩展。 +RxJS主要是提供一种更加强大和优雅的方式,来利用响应式编程的模式,实现JavaScript的异步编程。 + +#### RxJS优点 + +* 纯净性; +* 流动性; + +#### RxJS核心概念 + +RxJS 是基于观察者模式和迭代器模式以函数式编程思维来实现的。RxJS 中含有两个基本概念:`Observables` 与 `Observer`。 +`Observables` 作为被观察者,是一个值或事件的流集合;而 `Observer` 则作为观察者,根据 `Observables` 进行处理。它们之间的订阅发布关系(观察者模式) 如下: +**订阅**:`Observer` 通过 `Observable` 提供的 `subscribe()` 方法订阅 `Observable`。 +**发布**:`Observable` 通过回调 `next` 方法向 `Observer` 发布事件。 + +———— 来源[Angular修仙之路 RxJS Observable](http://www.semlinker.com/rxjs-observable/) + +另外这里列出来一些核心,具体还是看官网咯,并且下面使用到的时候会具体介绍。 +* `Observable` (可观察对象): 表示一个概念,这个概念是一个可调用的未来值或事件的集合。 +* `Observer`(观察者): 一个回调函数的集合,它知道如何去监听由 `Observable` 提供的值。 +* `Subscription` (订阅): 表示 `Observable` 的执行,主要用于取消 `Observable` 的执行。 +* `Operators` (操作符): 采用函数式编程风格的纯函数 (`pure function`),使用像 `map`、`filter`、`concat`、`flatMap` 等这样的操作符来处理集合。 +* `Subject` (主体): 相当于 `EventEmitter`,并且是将值或事件多路推送给多个 `Observer` 的唯一方式。 +* `Schedulers` (调度器): 用来控制并发并且是中央集权的调度员,允许我们在发生计算时进行协调,例如 `setTimeout` 或` requestAnimationFrame `或其他。 + +### 2.引入RxJS +在我们的真实应用中,我们必须要等到服务器响应后,我们才能获取到数据,因此这天生就需要用异步思维来操作。 + +由于Angular中已经自带RxJS,所以我们只要在需要使用的时候,引入即可使用: + +### 3.改造数据获取方式 +了解完RxJS的一些概念后,我们开始改造下这些书本的数据获取方式。 + +* 改造`BooksService` + +首先我们从 RxJS 中导入 `Observable` 和 `of` 符号: +```js +// books.service.ts +import { Observable, of } from 'rxjs'; +``` +**知识点**: +`Observable`: 观察者模式中的观察者,具体可以参考 [Angular修仙之路 RxJS Observable](http://www.semlinker.com/rxjs-observable/) +`of`: 用来获取观察者拿到的数据,通常是一个`Observable`。 + +然后修改`getBookList`方法 +```js +// books.service.ts +getBookList(): Observable { + return of(BookList); +} +``` +这里 `of(BookList)` 返回一个` Observable`,它会发出单个值,这个值就是这些模拟书本的数组。 + +* 改造`IndexComponent` + +这里也要修改`getBooks`方法,使用`subscribe`去订阅服务返回回来的值: +```js +// index.component.ts +getBooks(): void{ + this.booksservice.getBookList() + .subscribe(books => this.books = books); +} +``` +由于原本直接赋值数据,在实际场景中是不可能这样同步的,所以这里`subscribe`函数,会在`Observable`发出数据以后,再把书本列表传到里面的回调函数,再复制给`books`属性。 +使用这种异步方式,当 `BooksService` 从远端服务器获取英雄数据时,不用担心还没拿到数据就执行后面。 + +下一步,我们就要改造一下项目了。 + + +## 六、改造组件 +从这里开始,我们要使用RxJS来改造组件和添加新功能了,让整个项目更加完善。 + +### 1.添加历史记录组件 + +* 创建`HistoryComponent`组件 +```sh +ng g component hostory +``` +然后在`app.component.html`文件夹中添加组件: +```html + + +``` + +### 2.添加增删改查功能 + +这里我们要开始做书本的增删改查功能,需要先创建一个`HistoryService`服务,方便我们实现这几个功能: + +* 创建`HistoryService`服务 +```sh +ng g service history +``` +然后在生成的ts文件中,增加`add`和`clear`方法,`add`方法用来添加历史记录到`history`数组中,`clear`方法则是清空`history`数组: +```js +// history.service.ts +export class HistoryService { + history: string[] = []; + add(history: string){ + this.history.push(history); + } + clear(){ + this.history = []; + } +} +``` + +* 使用`HistoryService`服务 + +在将这个服务,注入到`BooksService`中,并改造`getBooks`方法: +```js +// books.service.ts +import { HistoryService } from './history.service'; +constructor( + private historyservice: HistoryService +) { } +getBooks(): void{ + this.historyservice.add('请求书本数据') + this.booksservice.getBookList() + .subscribe(books => this.books = books); +} +``` +也可以用相同方法,在`IndexComponent`中添加`访问首页书本列表`的记录。 +```js +// index.component.ts +import { HistoryService } from '../history.service'; +constructor( + private booksservice: BooksService, + private historyservice: HistoryService +) { } +getBooks(): void{ + this.historyservice.add('访问首页书本列表'); + this.booksservice.getBookList() + .subscribe(books => this.books = books); +} +``` + +接下来,将我们的`HistoryService`注入到`HistoryComponent`中,然后才能将历史数据显示到页面上: +```js +// history.component.ts +import { HistoryService } from '../history.service'; +export class HistoryComponent implements OnInit { + constructor(private historyservice: HistoryService) { } + ngOnInit() {} +} +``` +```html + +
+

操作历史:

+
+ +
{{item}}
+
+
+``` +**代码解释**: +`*ngIf="historyservice.history.length"`,是为了防止还没有拿到历史数据,导致后面的报错。 +`(click)="historyservice.clear()"`, 绑定我们服务中的`clear`事件,实现清除缓存。 +`*ngFor="let item of historyservice.history"`,将我们的历史数据渲染到页面上。 + + +到了这一步,就能看到历史数据了,每次也换到首页,都会增加一条。 + +![图片5-1](http://images.pingan8787.com/angular_books_5_1.png) + +接下来,我们要在书本详情页也加上历史记录的统计,导入文件,注入服务,然后改造`getBooks`方法,实现历史记录的统计: +```js +// detail.component.ts +import { HistoryService } from '../history.service'; + +export class DetailComponent implements OnInit { + constructor( + private route: ActivatedRoute, + private location: Location, + private booksservice: BooksService, + private historyservice: HistoryService + ) { } + //... + getBooks(id: number): void { + this.books = this.booksservice.getBook(id); + this.historyservice.add(`查看书本${this.books.title},id为${this.books.id}`); + console.log(this.books) + } +} +``` +![图片5-2](http://images.pingan8787.com/angular_books_5_2.png) + +这时候就可以在历史记录中,看到这些操作的记录了,并且**清除**按钮也正常使用。 + +## 七、HTTP改造 +原本我只想写到上一章,但是想到,我们实际开发中,哪有什么本地数据,基本上数据都是要从服务端去请求,所以这边也有必要引入这一张,模拟实际的HTTP请求。 + +### 1.引入HTTP +在这一章,我们使用Angular提供的 `HttpClient` 来添加一些数据持久化特性。 +然后实现对书本数据进行**获取,增加,修改,删除和查找**功能。 + +`HttpClient`是Angular通过 HTTP 与远程服务器通讯的机制。 + +这里我们为了让`HttpClient`在整个应用全局使用,所以将`HttpClient`导入到根模块`app.module.ts`中,然后把它加入 `@NgModule.imports` 数组: +```js +import { HttpClientModule } from '@angular/common/http'; +@NgModule({ + //... + imports: [ + BrowserModule, + AppRoutingModule, + HttpClientModule + ], + //... +}) +``` + +这边我们使用 [内存 Web API(In-memory Web API) ](https://github.com/angular/in-memory-web-api)模拟出的远程数据服务器通讯。 +**注意:** 这个内存 Web API 模块与 Angular 中的 HTTP 模块无关。 + +通过下面命令来安装: +```sh +npm install angular-in-memory-web-api --save +``` +然后在`app.module.ts`中导入 `HttpClientInMemoryWebApiModule` 和 `InMemoryDataService` 类(后面创建): +```js +// app.module.ts +import { HttpClientInMemoryWebApiModule } from 'angular-in-memory-web-api'; +import { InMemoryDataService } from './in-memory-data.service'; +@NgModule({ + // ... + imports: [ + // ... + HttpClientInMemoryWebApiModule.forRoot( + InMemoryDataService, {dataEncapsulation:false} + ) + ], + // ... +}) +export class AppModule { } +``` +**知识点:** +`forRoot()` 配置方法接受一个 InMemoryDataService 类(初期的内存数据库)作为参数。 + +然后我们要创建`InMemoryDataService`类: +```sh +ng g service InMemoryData +``` +并将生成的`in-memory-data.service.ts`修改为: +```js +// in-memory-data.service.ts +import { Injectable } from '@angular/core'; +import { InMemoryDbService } from 'angular-in-memory-web-api'; +import { Books } from './books'; +@Injectable({ + providedIn: 'root' +}) +export class InMemoryDataService implements InMemoryDbService { + createDb(){ + const books = [ + { + id: 1, + url: 'https://img3.doubanio.com/view/subject/m/public/s29988481.jpg', + title: '像火焰像灰烬', + author: '程姬', + }, + // 省略其他9条数据 + ]; + return {books}; + } + constructor() { } +} +``` + +这里先总结`InMemoryDbService`所提供的RESTful API,后面都要用到: +例如如果`url`是`api/books`,那么 +* 查询所有成员:以**GET**方法访问`api/books` +* 查询某个成员:以**GET**方法访问`api/books/id`,比如`id`是`1`,那么访问`api/books/1` +* 更新某个成员:以**PUT**方法访问`api/books/id` +* 删除某个成员:以**DELETE**方法访问`api/books/id` +* 增加一个成员:以**POST**方法访问`api/books` + + +### 2.通过HTTP请求数据 + +现在要为接下来的网络请求做一些准备,先在`books.service.ts`中引入HTTP符号,然后注入`HttpClient`并改造: +```js +// books.service.ts +import { HttpClient, HttpHeaders} from '@angular/common/http'; +// ... +export class BooksService { + constructor( + private historyservice: HistoryService, + private http: HttpClient + ) { } + private log(histories: string){ + this.historyservice.add(`正在执行:${histories}`) + } + private booksUrl = 'api/books'; // 提供一个API供调用 + // ... +} +``` +这里我们还新增一个私有方法`log`和一个私有变量`booksUrl`。 + +接下来我们要开始发起http请求数据,开始改造`getBookList`方法: +```js +// books.service.ts +// ... +getBookList(): Observable { + this.historyservice.add('请求书本数据') + return this.http.get(this.booksUrl); +} +// ... +``` +这里我们使用 `http.get` 替换了 `of`,其它没修改,但是应用仍然在正常工作,这是因为这两个函数都返回了 `Observable`。 + +实际开发中,我们还需要考虑到**请求的错误处理**,要捕获错误,我们就要使用 RxJS 的 `catchError()` 操作符来建立对 Observable 结果的处理管道(pipe)。 + +我们引入`catchError `并改造原本`getBookList`方法: + +```js +// books.service.ts +getBookList(): Observable { + this.historyservice.add('请求书本数据') + return this.http.get(this.booksUrl).pipe( + catchError(this.handleError('getHeroes', [])) + ); +} +private handleError (operation = 'operation', result?: T) { + return (error: any): Observable => { + this.log(`${operation} 失败: ${error.message}`); // 发出错误通知 + return of(result as T); // 返回空结果避免程序出错 + }; +} +``` +**知识点**: +`.pipe()` 方法用来扩展 `Observable` 的结果。 +`catchError()` 操作符会拦截失败的 Observable。并把错误对象传给错误处理器,错误处理器会处理这个错误。 +`handleError()` 错误处理函数做了两件事,发出错误通知和返回空结果避免程序出错。 + +这里还需要使用`tap`操作符改造`getBookList`方法,来窥探`Observable`数据流,它会查看`Observable`的值,然后我们使用`log`方法,记录一条历史记录。 +`tap` 回调不会改变这些值本身。 +```js +// books.service.ts +getBookList(): Observable { + return this.http.get(this.booksUrl) + .pipe( + tap( _ => this.log('请求书本数据')), + catchError(this.handleError('getHeroes', [])) + ); +} +``` + +### 3.通过HTTP修改数据 +这里我们需要在原来`DetailComponent`上面,添加一个输入框、保存按钮和返回按钮,就像这样: +```html + + +
+

修改信息:

+ + + +
+``` +这边切记一点,一定要在`app.module.ts`中引入 `FormsModule`模块,并在`@NgModule`的`imports`中引入,不然要报错了。 +```js +// app.module.ts +// ... +import { FormsModule } from '@angular/forms'; +@NgModule({ + // ... + imports: [ + // ... + FormsModule + ], + // ... +}) +``` +`input`框绑定书本的标题`books.title`,而保存按钮绑定一个`save()`方法,这里还要实现这个方法: +```js +// detail.component.ts +save(): void { + this.historyservice.updateBooks(this.books) + .subscribe(() => this.goBack()); +} +goBack(): void { + this.location.back(); +} +``` +这里通过调用`BooksService`的`updateBooks`方法,将当前修改后的书本信息修改到源数据中,这里我们需要去`books.service.ts`中添加`updateBooks`方法: +```js +// books.service.ts +// ... +updateBooks(books: Books): Observable{ + return this.http.put(this.booksUrl, books, httpOptions).pipe( + tap(_ => this.log(`修改书本的id是${books.id}`)), + catchError(this.handleError(`getBooks请求是id为${books.id}`)) + ) +} +// ... +``` +**知识点**: +`HttpClient.put()` 方法接受三个参数:`URL 地址`、`要修改的数据`和`其他选项`。 +`httpOptions` 常量需要定义在`@Injectable`修饰器之前。 + +现在,我们点击首页,选择一本书进入详情,修改标题然后保存,会发现,首页上这本书的名称也会跟着改变呢。这算是好了。 + + +### 4.通过HTTP增加数据 +我们可以新增一个页面,并添加上路由和按钮: +```sh +ng g component add +``` +添加路由: +```js +// app-routing.module.ts +// ... +import { AddComponent } from './add/add.component'; + +const routes: Routes = [ + { path: '', redirectTo:'/index', pathMatch:'full' }, + { path: 'index', component: IndexComponent}, + { path: 'detail/:id', component: DetailComponent}, + { path: 'add', component: AddComponent}, +] +``` +添加路由入口: +```html + + +添加书本 +``` +编辑添加书本的页面: +```html + +
+

添加书本:

+ + + + +
+
+``` +初始化添加书本的数据: +```js +// add.component.ts +// ... +import { Books } from '../books'; +import { BooksService } from '../books.service'; +import { HistoryService } from '../history.service'; +import { Location } from '@angular/common'; +export class AddComponent implements OnInit { + books: Books = { + id: 0, + url: '', + title: '', + author: '' + } + constructor( + private location: Location, + private booksservice: BooksService, + private historyservice: HistoryService + ) { } + ngOnInit() {} + add(books: Books): void{ + books.title = books.title.trim(); + books.author = books.author.trim(); + this.booksservice.addBooks(books) + .subscribe( book => { + this.historyservice.add(`新增书本${books.title},id为${books.id}`); + this.location.back(); + }); + } +} +``` +然后在`books.service.ts`中添加`addBooks`方法,来添加一本书本的数据: +```js +// books.service.ts +addBooks(books: Books): Observable{ + return this.http.post(this.booksUrl, books, httpOptions).pipe( + tap((newBook: Books) => this.log(`新增书本的id为${newBook.id}`)), + catchError(this.handleError('添加新书')) + ); +} +``` + + +现在就可以正常添加书本啦。 + +![图片5-3](http://images.pingan8787.com/angular_books_5_3.png) + + +### 5.通过HTTP删除数据 +这里我们先为每个书本后面添加一个删除按钮,并绑定删除事件`delete`: +```html + + +X +``` +```js +// books.component.ts +import { BooksService } from '../books.service'; +export class BooksComponent implements OnInit { + @Input() list: Books; + constructor( + private booksservice: BooksService + ) { } + // ... + delete(books: Books): void { + this.booksservice.deleteBooks(books) + .subscribe(); + } +} +``` +然后还要再`books.service.ts`中添加`deleteBooks`方法来删除: +```js +// books.service.ts +deleteBooks(books: Books): Observable{ + const id = books.id; + const url = `${this.booksUrl}/${id}`; + return this.http.delete(url, httpOptions).pipe( + tap(_ => this.log(`删除书本${books.title},id为${books.id}`)), + catchError(this.handleError('删除书本')) + ); +} +``` +这里需要在删除书本结束后,通知`IndexComponent`将数据列表中的这条数据删除,这里还需要再了解一下[Angular 父子组件数据通信](https://blog.csdn.net/u010730126/article/details/68080139)。 +然后我们在父组件`IndexComponent`上添加`change`事件监听,并传入本地的`funChange`: +```html + + +``` +在对应的`index.component.ts`中添加`funChange`方法: +```js +// index.component.ts +funChange(books, $event){ + this.books = this.books.filter(h => h.id !== books.id); +} +``` + +再来,我们在子组件`BooksComponent`上多导入`Output`和`EventEmitter`,并添加`@Output()`修饰器和调用`emit`: +```js +import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core'; +export class BooksComponent implements OnInit { + // ... + @Output() + change = new EventEmitter() + // ... + delete(books: Books): void { + this.booksservice.deleteBooks(books) + .subscribe(()=>{ + this.change.emit(books); + }); + } +} +``` +这样就实现了我们父子组件之间的事件传递啦,现在我们的页面还是正常运行,并且删除一条数据后,页面数据会更新。 + + +### 6.通过HTTP查找数据 +还是在`books.service.ts`,我们添加一个方法`getBooks`,来实现通过ID来查找指定书本,因为我们是通过ID查找,所以返回的是单个数据,这里就是`Observable`类型: +```js +// books.service.ts +getBooks(id: number): Observable{ + const url = `${this.booksUrl}/${id}`; + return this.http.get(url).pipe( + tap( _ => this.log(`请求书本的id为${id}`)), + catchError(this.handleError(`getBooks请求是id为${id}`)) + ) +} +``` +注意,这里 `getBooks` 会返回 `Observable`,是一个可观察的单个对象,而不是一个可观察的对象数组。 + + +## 八、结语 +这个项目其实很简单,但是我还是一步一步的写下来,一方面让自己更熟悉Angular,另一方面也是希望能帮助到更多朋友哈~ +最终效果: + +![图片结果](http://images.pingan8787.com/angular_books_result.png) + + +**本部分内容到这结束** + +|Author|王平安| +|---|---| +|E-mail|pingan8787@qq.com| +|博 客|www.pingan8787.com| +|微 信|pingan8787| +|每日文章推荐|https://github.com/pingan8787/Leo_Reading/issues| +|JS小册|js.pingan8787.com| +|微信公众号|前端自习课| + + +![前端自习课](https://camo.githubusercontent.com/7d890fb10cccf99c03dcf144e0e290357195ac44/68747470733a2f2f757365722d676f6c642d63646e2e786974752e696f2f323031392f322f31362f313638663439663032333831393163613f773d3130373826683d36343726663d706e6726733d323832353135) diff --git "a/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/.editorconfig" "b/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/.editorconfig" new file mode 100644 index 00000000..6e87a003 --- /dev/null +++ "b/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/.editorconfig" @@ -0,0 +1,13 @@ +# Editor configuration, see http://editorconfig.org +root = true + +[*] +charset = utf-8 +indent_style = space +indent_size = 2 +insert_final_newline = true +trim_trailing_whitespace = true + +[*.md] +max_line_length = off +trim_trailing_whitespace = false diff --git "a/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/.gitignore" "b/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/.gitignore" new file mode 100644 index 00000000..ee5c9d83 --- /dev/null +++ "b/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/.gitignore" @@ -0,0 +1,39 @@ +# See http://help.github.com/ignore-files/ for more about ignoring files. + +# compiled output +/dist +/tmp +/out-tsc + +# dependencies +/node_modules + +# IDEs and editors +/.idea +.project +.classpath +.c9/ +*.launch +.settings/ +*.sublime-workspace + +# IDE - VSCode +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json + +# misc +/.sass-cache +/connect.lock +/coverage +/libpeerconnection.log +npm-debug.log +yarn-error.log +testem.log +/typings + +# System Files +.DS_Store +Thumbs.db diff --git "a/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/README.md" "b/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/README.md" new file mode 100644 index 00000000..55356bf0 --- /dev/null +++ "b/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/README.md" @@ -0,0 +1,27 @@ +# Books + +This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 6.2.4. + +## Development server + +Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files. + +## Code scaffolding + +Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`. + +## Build + +Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `--prod` flag for a production build. + +## Running unit tests + +Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io). + +## Running end-to-end tests + +Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/). + +## Further help + +To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md). diff --git "a/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/angular.json" "b/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/angular.json" new file mode 100644 index 00000000..1f14688a --- /dev/null +++ "b/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/angular.json" @@ -0,0 +1,127 @@ +{ + "$schema": "./node_modules/@angular/cli/lib/config/schema.json", + "version": 1, + "newProjectRoot": "projects", + "projects": { + "books": { + "root": "", + "sourceRoot": "src", + "projectType": "application", + "prefix": "app", + "schematics": {}, + "architect": { + "build": { + "builder": "@angular-devkit/build-angular:browser", + "options": { + "outputPath": "dist/books", + "index": "src/index.html", + "main": "src/main.ts", + "polyfills": "src/polyfills.ts", + "tsConfig": "src/tsconfig.app.json", + "assets": [ + "src/favicon.ico", + "src/assets" + ], + "styles": [ + "src/styles.css" + ], + "scripts": [] + }, + "configurations": { + "production": { + "fileReplacements": [ + { + "replace": "src/environments/environment.ts", + "with": "src/environments/environment.prod.ts" + } + ], + "optimization": true, + "outputHashing": "all", + "sourceMap": false, + "extractCss": true, + "namedChunks": false, + "aot": true, + "extractLicenses": true, + "vendorChunk": false, + "buildOptimizer": true + } + } + }, + "serve": { + "builder": "@angular-devkit/build-angular:dev-server", + "options": { + "browserTarget": "books:build" + }, + "configurations": { + "production": { + "browserTarget": "books:build:production" + } + } + }, + "extract-i18n": { + "builder": "@angular-devkit/build-angular:extract-i18n", + "options": { + "browserTarget": "books:build" + } + }, + "test": { + "builder": "@angular-devkit/build-angular:karma", + "options": { + "main": "src/test.ts", + "polyfills": "src/polyfills.ts", + "tsConfig": "src/tsconfig.spec.json", + "karmaConfig": "src/karma.conf.js", + "styles": [ + "src/styles.css" + ], + "scripts": [], + "assets": [ + "src/favicon.ico", + "src/assets" + ] + } + }, + "lint": { + "builder": "@angular-devkit/build-angular:tslint", + "options": { + "tsConfig": [ + "src/tsconfig.app.json", + "src/tsconfig.spec.json" + ], + "exclude": [ + "**/node_modules/**" + ] + } + } + } + }, + "books-e2e": { + "root": "e2e/", + "projectType": "application", + "architect": { + "e2e": { + "builder": "@angular-devkit/build-angular:protractor", + "options": { + "protractorConfig": "e2e/protractor.conf.js", + "devServerTarget": "books:serve" + }, + "configurations": { + "production": { + "devServerTarget": "books:serve:production" + } + } + }, + "lint": { + "builder": "@angular-devkit/build-angular:tslint", + "options": { + "tsConfig": "e2e/tsconfig.e2e.json", + "exclude": [ + "**/node_modules/**" + ] + } + } + } + } + }, + "defaultProject": "books" +} \ No newline at end of file diff --git "a/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/e2e/protractor.conf.js" "b/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/e2e/protractor.conf.js" new file mode 100644 index 00000000..86776a39 --- /dev/null +++ "b/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/e2e/protractor.conf.js" @@ -0,0 +1,28 @@ +// Protractor configuration file, see link for more information +// https://github.com/angular/protractor/blob/master/lib/config.ts + +const { SpecReporter } = require('jasmine-spec-reporter'); + +exports.config = { + allScriptsTimeout: 11000, + specs: [ + './src/**/*.e2e-spec.ts' + ], + capabilities: { + 'browserName': 'chrome' + }, + directConnect: true, + baseUrl: 'http://localhost:4200/', + framework: 'jasmine', + jasmineNodeOpts: { + showColors: true, + defaultTimeoutInterval: 30000, + print: function() {} + }, + onPrepare() { + require('ts-node').register({ + project: require('path').join(__dirname, './tsconfig.e2e.json') + }); + jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } })); + } +}; \ No newline at end of file diff --git "a/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/e2e/src/app.e2e-spec.ts" "b/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/e2e/src/app.e2e-spec.ts" new file mode 100644 index 00000000..21fad901 --- /dev/null +++ "b/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/e2e/src/app.e2e-spec.ts" @@ -0,0 +1,14 @@ +import { AppPage } from './app.po'; + +describe('workspace-project App', () => { + let page: AppPage; + + beforeEach(() => { + page = new AppPage(); + }); + + it('should display welcome message', () => { + page.navigateTo(); + expect(page.getParagraphText()).toEqual('Welcome to books!'); + }); +}); diff --git "a/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/e2e/src/app.po.ts" "b/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/e2e/src/app.po.ts" new file mode 100644 index 00000000..82ea75ba --- /dev/null +++ "b/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/e2e/src/app.po.ts" @@ -0,0 +1,11 @@ +import { browser, by, element } from 'protractor'; + +export class AppPage { + navigateTo() { + return browser.get('/'); + } + + getParagraphText() { + return element(by.css('app-root h1')).getText(); + } +} diff --git "a/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/e2e/tsconfig.e2e.json" "b/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/e2e/tsconfig.e2e.json" new file mode 100644 index 00000000..a6dd6220 --- /dev/null +++ "b/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/e2e/tsconfig.e2e.json" @@ -0,0 +1,13 @@ +{ + "extends": "../tsconfig.json", + "compilerOptions": { + "outDir": "../out-tsc/app", + "module": "commonjs", + "target": "es5", + "types": [ + "jasmine", + "jasminewd2", + "node" + ] + } +} \ No newline at end of file diff --git "a/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/package-lock.json" "b/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/package-lock.json" new file mode 100644 index 00000000..57f8ebbb --- /dev/null +++ "b/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/package-lock.json" @@ -0,0 +1,10968 @@ +{ + "name": "books", + "version": "0.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@angular-devkit/architect": { + "version": "0.8.9", + "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.8.9.tgz", + "integrity": "sha512-2tiGPkvJyFY/G3a27uC8r6Jj3H5m8SxjMqhjNUQ5AtNumweTBPt3YIYMNAvHUmxG0nA9upDolVXFmoQGK9AhKQ==", + "dev": true, + "requires": { + "@angular-devkit/core": "0.8.9", + "rxjs": "6.2.2" + } + }, + "@angular-devkit/build-angular": { + "version": "0.8.9", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-0.8.9.tgz", + "integrity": "sha512-J6o0MwIG1cJT29p87c7uUn7NY3QLEoQOVw4VXWM9cqG9bv99VK7f7eOSDhHJbXn7Snm4XYrye0zRa3RFXhMG+A==", + "dev": true, + "requires": { + "@angular-devkit/architect": "0.8.9", + "@angular-devkit/build-optimizer": "0.8.9", + "@angular-devkit/build-webpack": "0.8.9", + "@angular-devkit/core": "0.8.9", + "@ngtools/webpack": "6.2.9", + "ajv": "6.4.0", + "autoprefixer": "8.6.5", + "circular-dependency-plugin": "5.0.2", + "clean-css": "4.2.1", + "copy-webpack-plugin": "4.5.2", + "file-loader": "1.1.11", + "glob": "7.1.3", + "html-webpack-plugin": "3.2.0", + "istanbul": "0.4.5", + "istanbul-instrumenter-loader": "3.0.1", + "karma-source-map-support": "1.3.0", + "less": "3.8.1", + "less-loader": "4.1.0", + "license-webpack-plugin": "1.5.0", + "loader-utils": "1.1.0", + "mini-css-extract-plugin": "0.4.4", + "minimatch": "3.0.4", + "node-sass": "^4.9.3", + "opn": "5.4.0", + "parse5": "4.0.0", + "portfinder": "1.0.19", + "postcss": "6.0.23", + "postcss-import": "11.1.0", + "postcss-loader": "2.1.6", + "postcss-url": "7.3.2", + "raw-loader": "0.5.1", + "rxjs": "6.2.2", + "sass-loader": "7.1.0", + "semver": "5.6.0", + "source-map-loader": "0.2.4", + "source-map-support": "0.5.9", + "stats-webpack-plugin": "0.6.2", + "style-loader": "0.21.0", + "stylus": "0.54.5", + "stylus-loader": "3.0.2", + "tree-kill": "1.2.1", + "uglifyjs-webpack-plugin": "1.3.0", + "url-loader": "1.1.2", + "webpack": "4.16.4", + "webpack-dev-middleware": "3.4.0", + "webpack-dev-server": "3.1.14", + "webpack-merge": "4.1.4", + "webpack-sources": "1.3.0", + "webpack-subresource-integrity": "1.1.0-rc.4" + } + }, + "@angular-devkit/build-optimizer": { + "version": "0.8.9", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-optimizer/-/build-optimizer-0.8.9.tgz", + "integrity": "sha512-h8u5iAhSmt0TsLDZXZCmOkXZDMgP2itLkgZvOIsGInyMAESJuWK4P1qegMSv2R5ELOsinJiuhe218M4K2enEdA==", + "dev": true, + "requires": { + "loader-utils": "1.1.0", + "source-map": "0.5.7", + "typescript": "2.9.2", + "webpack-sources": "1.3.0" + } + }, + "@angular-devkit/build-webpack": { + "version": "0.8.9", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.8.9.tgz", + "integrity": "sha512-2csJ6utodPSLABTXfBLymYLrndJURF3xVqVjEDzUFl9zLqK1YOkKH4XPr12vfH8SfAtvzIutNLRxBtAuWJmDlw==", + "dev": true, + "requires": { + "@angular-devkit/architect": "0.8.9", + "@angular-devkit/core": "0.8.9", + "rxjs": "6.2.2" + } + }, + "@angular-devkit/core": { + "version": "0.8.9", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-0.8.9.tgz", + "integrity": "sha512-Umax3YKBPTQy360TeoSNaIIOJOKoXvN/S2WNTV8wDjSWWNiWLTIlckWMb9DVsafAifjUi0mtOLRFuM4YatKgTw==", + "dev": true, + "requires": { + "ajv": "6.4.0", + "chokidar": "2.0.4", + "rxjs": "6.2.2", + "source-map": "0.5.7" + } + }, + "@angular-devkit/schematics": { + "version": "0.8.9", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-0.8.9.tgz", + "integrity": "sha512-JZiK1aHJUFV6xDtUMBLoH3cLgi7EtR1bXjNqqa11MAjnHMqzm2GBazPvzGkMwVbCxC1sdYgswwGX9GS2tpHawA==", + "dev": true, + "requires": { + "@angular-devkit/core": "0.8.9", + "rxjs": "6.2.2" + } + }, + "@angular/animations": { + "version": "6.1.10", + "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-6.1.10.tgz", + "integrity": "sha512-dd/lq7kw3uwfHPICan8psu2nthuUpp7PvMLuNIm0XxObZ4oNs0ls6uxKEDPnEkRKoGdiJpvmsyzZZN9ACMPEAA==", + "requires": { + "tslib": "^1.9.0" + } + }, + "@angular/cli": { + "version": "6.2.9", + "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-6.2.9.tgz", + "integrity": "sha512-4xuTbmMKGx1bMi0KA3Xmtx/emy10wlSwTXoUijlhd2tcWmlI2wRjAYjR7efSbFo8dVskiq0CyAVFWr1IanYQZw==", + "dev": true, + "requires": { + "@angular-devkit/architect": "0.8.9", + "@angular-devkit/core": "0.8.9", + "@angular-devkit/schematics": "0.8.9", + "@schematics/angular": "0.8.9", + "@schematics/update": "0.8.9", + "@yarnpkg/lockfile": "1.1.0", + "ini": "1.3.5", + "json-schema-traverse": "0.4.1", + "npm-package-arg": "6.1.0", + "opn": "5.4.0", + "pacote": "9.2.3", + "rxjs": "6.2.2", + "semver": "5.6.0", + "symbol-observable": "1.2.0", + "yargs-parser": "10.1.0" + }, + "dependencies": { + "camelcase": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", + "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", + "dev": true + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "yargs-parser": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-10.1.0.tgz", + "integrity": "sha512-VCIyR1wJoEBZUqk5PA+oOBF6ypbwh5aNB3I50guxAL/quggdfs4TtNHQrSazFA3fYZ+tEqfs0zIGlv0c/rgjbQ==", + "dev": true, + "requires": { + "camelcase": "^4.1.0" + } + } + } + }, + "@angular/common": { + "version": "6.1.10", + "resolved": "https://registry.npmjs.org/@angular/common/-/common-6.1.10.tgz", + "integrity": "sha512-73xxTSYJNKfiJ7C1Ajg+sz5l8y+blb/vNgHYg7O3yem5zLBnfPpidJ1UGg4W4d2Y+jwUVJbZKh8SKJarqAJVUQ==", + "requires": { + "tslib": "^1.9.0" + } + }, + "@angular/compiler": { + "version": "6.1.10", + "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-6.1.10.tgz", + "integrity": "sha512-FPIb2j3zfoBwb6vo/u0gQeu70h8InGlSisBr3xMACs/35/pwB6kbQR+JQiUr0D7k6QApg7AuMkvq8aFNelg0aw==", + "requires": { + "tslib": "^1.9.0" + } + }, + "@angular/compiler-cli": { + "version": "6.1.10", + "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-6.1.10.tgz", + "integrity": "sha512-GCWdyeNQSnF4RfzO4A0+WHsNEgxKpl5arg4ldLSWMNkj/DrhMD4TnmxhR+IVY+7ieMkUBwpcuWRnjdOdnbmV+w==", + "dev": true, + "requires": { + "chokidar": "^1.4.2", + "minimist": "^1.2.0", + "reflect-metadata": "^0.1.2", + "tsickle": "^0.32.1" + }, + "dependencies": { + "anymatch": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-1.3.2.tgz", + "integrity": "sha512-0XNayC8lTHQ2OI8aljNCN3sSx6hsr/1+rlcDAotXJR7C1oZZHCNsfpbKwMjRA3Uqb5tF1Rae2oloTr4xpq+WjA==", + "dev": true, + "requires": { + "micromatch": "^2.1.5", + "normalize-path": "^2.0.0" + } + }, + "arr-diff": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz", + "integrity": "sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=", + "dev": true, + "requires": { + "arr-flatten": "^1.0.1" + } + }, + "array-unique": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz", + "integrity": "sha1-odl8yvy8JiXMcPrc6zalDFiwGlM=", + "dev": true + }, + "braces": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz", + "integrity": "sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=", + "dev": true, + "requires": { + "expand-range": "^1.8.1", + "preserve": "^0.2.0", + "repeat-element": "^1.1.2" + } + }, + "chokidar": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-1.7.0.tgz", + "integrity": "sha1-eY5ol3gVHIB2tLNg5e3SjNortGg=", + "dev": true, + "requires": { + "anymatch": "^1.3.0", + "async-each": "^1.0.0", + "fsevents": "^1.0.0", + "glob-parent": "^2.0.0", + "inherits": "^2.0.1", + "is-binary-path": "^1.0.0", + "is-glob": "^2.0.0", + "path-is-absolute": "^1.0.0", + "readdirp": "^2.0.0" + } + }, + "expand-brackets": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz", + "integrity": "sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s=", + "dev": true, + "requires": { + "is-posix-bracket": "^0.1.0" + } + }, + "extglob": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz", + "integrity": "sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE=", + "dev": true, + "requires": { + "is-extglob": "^1.0.0" + } + }, + "glob-parent": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz", + "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=", + "dev": true, + "requires": { + "is-glob": "^2.0.0" + } + }, + "is-extglob": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", + "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", + "dev": true + }, + "is-glob": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", + "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", + "dev": true, + "requires": { + "is-extglob": "^1.0.0" + } + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + }, + "micromatch": { + "version": "2.3.11", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz", + "integrity": "sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU=", + "dev": true, + "requires": { + "arr-diff": "^2.0.0", + "array-unique": "^0.2.1", + "braces": "^1.8.2", + "expand-brackets": "^0.1.4", + "extglob": "^0.3.1", + "filename-regex": "^2.0.0", + "is-extglob": "^1.0.0", + "is-glob": "^2.0.1", + "kind-of": "^3.0.2", + "normalize-path": "^2.0.1", + "object.omit": "^2.0.0", + "parse-glob": "^3.0.4", + "regex-cache": "^0.4.2" + } + }, + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "dev": true + } + } + }, + "@angular/core": { + "version": "6.1.10", + "resolved": "https://registry.npmjs.org/@angular/core/-/core-6.1.10.tgz", + "integrity": "sha512-61l3rIQTVdT45eOf6/fBJIeVmV10mcrxqS4N/1OWkuDT29YSJTZSxGcv8QjAyyutuhcqWWpO6gVRkN07rWmkPg==", + "requires": { + "tslib": "^1.9.0" + } + }, + "@angular/forms": { + "version": "6.1.10", + "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-6.1.10.tgz", + "integrity": "sha512-zAPx2kMV1/FbP5DrY472Sd/ze1m+GS6T5ullZCtP392r62p2RkwzDCXieR51YiRJjZj3M6c3AcRND7PWBdXT7A==", + "requires": { + "tslib": "^1.9.0" + } + }, + "@angular/http": { + "version": "6.1.10", + "resolved": "https://registry.npmjs.org/@angular/http/-/http-6.1.10.tgz", + "integrity": "sha512-LDsSqyexh8fj23y+G2oSGLWSZVhbxBBo2ehYHnRgH/jlp0pmZVLRaGgUMNSCVtZc1rxLzpEjZjtw+P+qlutAtw==", + "requires": { + "tslib": "^1.9.0" + } + }, + "@angular/language-service": { + "version": "6.1.10", + "resolved": "https://registry.npmjs.org/@angular/language-service/-/language-service-6.1.10.tgz", + "integrity": "sha512-nN29Ovomg21eL8acwOSUFAYwWFI1TuFwUgUu37ZssfVQrYdaV+BFx3yv3P0nKU90h3Hp+oIkWHd8U34UYrvBCg==", + "dev": true + }, + "@angular/platform-browser": { + "version": "6.1.10", + "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-6.1.10.tgz", + "integrity": "sha512-CB7pqMwtgb7KjdHDAJlsXcs0rrU+2xQVaoOaqEfJtUrKhtGMLaZh8Qoic5l92SoGattkOw7SYarAOsWlAsVfvw==", + "requires": { + "tslib": "^1.9.0" + } + }, + "@angular/platform-browser-dynamic": { + "version": "6.1.10", + "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-6.1.10.tgz", + "integrity": "sha512-DmBSUyFPoyKqkmBXyJ2CrP1oXDioeoBlPA8lmWUDUv2yBuoHIzIkdY/OkTZbdyu/QYa1hK2Jl9OlfoeoenKddg==", + "requires": { + "tslib": "^1.9.0" + } + }, + "@angular/router": { + "version": "6.1.10", + "resolved": "https://registry.npmjs.org/@angular/router/-/router-6.1.10.tgz", + "integrity": "sha512-tekI3dkdvd65oMoxjjgRA+16uDgPUBWHhYxids6pgO8vobZNtCo8VaVlcDyLUhdmtS5kONELx0iL5E2M0Y2Bag==", + "requires": { + "tslib": "^1.9.0" + } + }, + "@babel/code-frame": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.0.0.tgz", + "integrity": "sha512-OfC2uemaknXr87bdLUkWog7nYuliM9Ij5HUcajsVcMCpQrcLmtxRbVFTIqmcSkSeYRBFBRxs2FiUqFJDLdiebA==", + "dev": true, + "requires": { + "@babel/highlight": "^7.0.0" + } + }, + "@babel/generator": { + "version": "7.3.3", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.3.3.tgz", + "integrity": "sha512-aEADYwRRZjJyMnKN7llGIlircxTCofm3dtV5pmY6ob18MSIuipHpA2yZWkPlycwu5HJcx/pADS3zssd8eY7/6A==", + "dev": true, + "requires": { + "@babel/types": "^7.3.3", + "jsesc": "^2.5.1", + "lodash": "^4.17.11", + "source-map": "^0.5.0", + "trim-right": "^1.0.1" + }, + "dependencies": { + "jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true + } + } + }, + "@babel/helper-function-name": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.1.0.tgz", + "integrity": "sha512-A95XEoCpb3TO+KZzJ4S/5uW5fNe26DjBGqf1o9ucyLyCmi1dXq/B3c8iaWTfBk3VvetUxl16e8tIrd5teOCfGw==", + "dev": true, + "requires": { + "@babel/helper-get-function-arity": "^7.0.0", + "@babel/template": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "@babel/helper-get-function-arity": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.0.0.tgz", + "integrity": "sha512-r2DbJeg4svYvt3HOS74U4eWKsUAMRH01Z1ds1zx8KNTPtpTL5JAsdFv8BNyOpVqdFhHkkRDIg5B4AsxmkjAlmQ==", + "dev": true, + "requires": { + "@babel/types": "^7.0.0" + } + }, + "@babel/helper-split-export-declaration": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.0.0.tgz", + "integrity": "sha512-MXkOJqva62dfC0w85mEf/LucPPS/1+04nmmRMPEBUB++hiiThQ2zPtX/mEWQ3mtzCEjIJvPY8nuwxXtQeQwUag==", + "dev": true, + "requires": { + "@babel/types": "^7.0.0" + } + }, + "@babel/highlight": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.0.0.tgz", + "integrity": "sha512-UFMC4ZeFC48Tpvj7C8UgLvtkaUuovQX+5xNWrsIoMG8o2z+XFKjKaN9iVmS84dPwVN00W4wPmqvYoZF3EGAsfw==", + "dev": true, + "requires": { + "chalk": "^2.0.0", + "esutils": "^2.0.2", + "js-tokens": "^4.0.0" + }, + "dependencies": { + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + } + } + }, + "@babel/parser": { + "version": "7.3.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.3.3.tgz", + "integrity": "sha512-xsH1CJoln2r74hR+y7cg2B5JCPaTh+Hd+EbBRk9nWGSNspuo6krjhX0Om6RnRQuIvFq8wVXCLKH3kwKDYhanSg==", + "dev": true + }, + "@babel/template": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.2.2.tgz", + "integrity": "sha512-zRL0IMM02AUDwghf5LMSSDEz7sBCO2YnNmpg3uWTZj/v1rcG2BmQUvaGU8GhU8BvfMh1k2KIAYZ7Ji9KXPUg7g==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "@babel/parser": "^7.2.2", + "@babel/types": "^7.2.2" + } + }, + "@babel/traverse": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.2.3.tgz", + "integrity": "sha512-Z31oUD/fJvEWVR0lNZtfgvVt512ForCTNKYcJBGbPb1QZfve4WGH8Wsy7+Mev33/45fhP/hwQtvgusNdcCMgSw==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "@babel/generator": "^7.2.2", + "@babel/helper-function-name": "^7.1.0", + "@babel/helper-split-export-declaration": "^7.0.0", + "@babel/parser": "^7.2.3", + "@babel/types": "^7.2.2", + "debug": "^4.1.0", + "globals": "^11.1.0", + "lodash": "^4.17.10" + }, + "dependencies": { + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "globals": { + "version": "11.11.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.11.0.tgz", + "integrity": "sha512-WHq43gS+6ufNOEqlrDBxVEbb8ntfXrfAUU2ZOpCxrBdGKW3gyv8mCxAfIBD0DroPKGrJ2eSsXsLtY9MPntsyTw==", + "dev": true + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "dev": true + } + } + }, + "@babel/types": { + "version": "7.3.3", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.3.3.tgz", + "integrity": "sha512-2tACZ80Wg09UnPg5uGAOUvvInaqLk3l/IAhQzlxLQOIXacr6bMsra5SH6AWw/hIDRCSbCdHP2KzSOD+cT7TzMQ==", + "dev": true, + "requires": { + "esutils": "^2.0.2", + "lodash": "^4.17.11", + "to-fast-properties": "^2.0.0" + }, + "dependencies": { + "to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", + "dev": true + } + } + }, + "@ngtools/webpack": { + "version": "6.2.9", + "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-6.2.9.tgz", + "integrity": "sha512-wZ9ROI4FdA9gnx21ULtA8u6Gmtig3BbT6EfUh2uYisjyL4P9k2p22sfY/Txwu/InnF4LDHOs5xZutOnGvC87vw==", + "dev": true, + "requires": { + "@angular-devkit/core": "0.8.9", + "rxjs": "6.2.2", + "tree-kill": "1.2.1", + "webpack-sources": "1.3.0" + } + }, + "@schematics/angular": { + "version": "0.8.9", + "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-0.8.9.tgz", + "integrity": "sha512-QTCcvBr+HLk8oJuPvonc8myYVU4ko5qsIhTon0gyWQg8McQnjCf8dTpnLM/VSzrmR71wOQ9fqvl+qS+eujrtnA==", + "dev": true, + "requires": { + "@angular-devkit/core": "0.8.9", + "@angular-devkit/schematics": "0.8.9", + "typescript": ">=2.6.2 <2.10" + } + }, + "@schematics/update": { + "version": "0.8.9", + "resolved": "https://registry.npmjs.org/@schematics/update/-/update-0.8.9.tgz", + "integrity": "sha512-duefusf5YdKGcNfNlHM/eMpkuvR2o4rqLDX0wo59Pu1KKXoDEUy5lj0mOA+hXbzjHtrtOxpffxKU0+AzovEMPA==", + "dev": true, + "requires": { + "@angular-devkit/core": "0.8.9", + "@angular-devkit/schematics": "0.8.9", + "npm-registry-client": "8.6.0", + "rxjs": "6.2.2", + "semver": "5.6.0", + "semver-intersect": "1.4.0" + } + }, + "@types/jasmine": { + "version": "2.8.16", + "resolved": "https://registry.npmjs.org/@types/jasmine/-/jasmine-2.8.16.tgz", + "integrity": "sha512-056oRlBBp7MDzr+HoU5su099s/s7wjZ3KcHxLfv+Byqb9MwdLUvsfLgw1VS97hsh3ddxSPyQu+olHMnoVTUY6g==", + "dev": true + }, + "@types/jasminewd2": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/jasminewd2/-/jasminewd2-2.0.6.tgz", + "integrity": "sha512-2ZOKrxb8bKRmP/po5ObYnRDgFE4i+lQiEB27bAMmtMWLgJSqlIDqlLx6S0IRorpOmOPRQ6O80NujTmQAtBkeNw==", + "dev": true, + "requires": { + "@types/jasmine": "*" + } + }, + "@types/node": { + "version": "8.9.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-8.9.5.tgz", + "integrity": "sha512-jRHfWsvyMtXdbhnz5CVHxaBgnV6duZnPlQuRSo/dm/GnmikNcmZhxIES4E9OZjUmQ8C+HCl4KJux+cXN/ErGDQ==", + "dev": true + }, + "@types/q": { + "version": "0.0.32", + "resolved": "https://registry.npmjs.org/@types/q/-/q-0.0.32.tgz", + "integrity": "sha1-vShOV8hPEyXacCur/IKlMoGQwMU=", + "dev": true + }, + "@types/selenium-webdriver": { + "version": "3.0.15", + "resolved": "https://registry.npmjs.org/@types/selenium-webdriver/-/selenium-webdriver-3.0.15.tgz", + "integrity": "sha512-5nh8/K2u9p4bk95GGCJB7KBvewaB0TUziZ9DTr+mR2I6RoO4OJVqx7rxK83hs2J1tomwtCGkhiW+Dy8EUnfB+Q==", + "dev": true + }, + "@webassemblyjs/ast": { + "version": "1.5.13", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.5.13.tgz", + "integrity": "sha512-49nwvW/Hx9i+OYHg+mRhKZfAlqThr11Dqz8TsrvqGKMhdI2ijy3KBJOun2Z4770TPjrIJhR6KxChQIDaz8clDA==", + "dev": true, + "requires": { + "@webassemblyjs/helper-module-context": "1.5.13", + "@webassemblyjs/helper-wasm-bytecode": "1.5.13", + "@webassemblyjs/wast-parser": "1.5.13", + "debug": "^3.1.0", + "mamacro": "^0.0.3" + }, + "dependencies": { + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "dev": true + } + } + }, + "@webassemblyjs/floating-point-hex-parser": { + "version": "1.5.13", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.5.13.tgz", + "integrity": "sha512-vrvvB18Kh4uyghSKb0NTv+2WZx871WL2NzwMj61jcq2bXkyhRC+8Q0oD7JGVf0+5i/fKQYQSBCNMMsDMRVAMqA==", + "dev": true + }, + "@webassemblyjs/helper-api-error": { + "version": "1.5.13", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.5.13.tgz", + "integrity": "sha512-dBh2CWYqjaDlvMmRP/kudxpdh30uXjIbpkLj9HQe+qtYlwvYjPRjdQXrq1cTAAOUSMTtzqbXIxEdEZmyKfcwsg==", + "dev": true + }, + "@webassemblyjs/helper-buffer": { + "version": "1.5.13", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.5.13.tgz", + "integrity": "sha512-v7igWf1mHcpJNbn4m7e77XOAWXCDT76Xe7Is1VQFXc4K5jRcFrl9D0NrqM4XifQ0bXiuTSkTKMYqDxu5MhNljA==", + "dev": true, + "requires": { + "debug": "^3.1.0" + }, + "dependencies": { + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "dev": true + } + } + }, + "@webassemblyjs/helper-code-frame": { + "version": "1.5.13", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.5.13.tgz", + "integrity": "sha512-yN6ScQQDFCiAXnVctdVO/J5NQRbwyTbQzsGzEgXsAnrxhjp0xihh+nNHQTMrq5UhOqTb5LykpJAvEv9AT0jnAQ==", + "dev": true, + "requires": { + "@webassemblyjs/wast-printer": "1.5.13" + } + }, + "@webassemblyjs/helper-fsm": { + "version": "1.5.13", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-fsm/-/helper-fsm-1.5.13.tgz", + "integrity": "sha512-hSIKzbXjVMRvy3Jzhgu+vDd/aswJ+UMEnLRCkZDdknZO3Z9e6rp1DAs0tdLItjCFqkz9+0BeOPK/mk3eYvVzZg==", + "dev": true + }, + "@webassemblyjs/helper-module-context": { + "version": "1.5.13", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-module-context/-/helper-module-context-1.5.13.tgz", + "integrity": "sha512-zxJXULGPLB7r+k+wIlvGlXpT4CYppRz8fLUM/xobGHc9Z3T6qlmJD9ySJ2jknuktuuiR9AjnNpKYDECyaiX+QQ==", + "dev": true, + "requires": { + "debug": "^3.1.0", + "mamacro": "^0.0.3" + }, + "dependencies": { + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "dev": true + } + } + }, + "@webassemblyjs/helper-wasm-bytecode": { + "version": "1.5.13", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.5.13.tgz", + "integrity": "sha512-0n3SoNGLvbJIZPhtMFq0XmmnA/YmQBXaZKQZcW8maGKwLpVcgjNrxpFZHEOLKjXJYVN5Il8vSfG7nRX50Zn+aw==", + "dev": true + }, + "@webassemblyjs/helper-wasm-section": { + "version": "1.5.13", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.5.13.tgz", + "integrity": "sha512-IJ/goicOZ5TT1axZFSnlAtz4m8KEjYr12BNOANAwGFPKXM4byEDaMNXYowHMG0yKV9a397eU/NlibFaLwr1fbw==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.5.13", + "@webassemblyjs/helper-buffer": "1.5.13", + "@webassemblyjs/helper-wasm-bytecode": "1.5.13", + "@webassemblyjs/wasm-gen": "1.5.13", + "debug": "^3.1.0" + }, + "dependencies": { + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "dev": true + } + } + }, + "@webassemblyjs/ieee754": { + "version": "1.5.13", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.5.13.tgz", + "integrity": "sha512-TseswvXEPpG5TCBKoLx9tT7+/GMACjC1ruo09j46ULRZWYm8XHpDWaosOjTnI7kr4SRJFzA6MWoUkAB+YCGKKg==", + "dev": true, + "requires": { + "ieee754": "^1.1.11" + } + }, + "@webassemblyjs/leb128": { + "version": "1.5.13", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.5.13.tgz", + "integrity": "sha512-0NRMxrL+GG3eISGZBmLBLAVjphbN8Si15s7jzThaw1UE9e5BY1oH49/+MA1xBzxpf1OW5sf9OrPDOclk9wj2yg==", + "dev": true, + "requires": { + "long": "4.0.0" + }, + "dependencies": { + "long": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", + "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==", + "dev": true + } + } + }, + "@webassemblyjs/utf8": { + "version": "1.5.13", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.5.13.tgz", + "integrity": "sha512-Ve1ilU2N48Ew0lVGB8FqY7V7hXjaC4+PeZM+vDYxEd+R2iQ0q+Wb3Rw8v0Ri0+rxhoz6gVGsnQNb4FjRiEH/Ng==", + "dev": true + }, + "@webassemblyjs/wasm-edit": { + "version": "1.5.13", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.5.13.tgz", + "integrity": "sha512-X7ZNW4+Hga4f2NmqENnHke2V/mGYK/xnybJSIXImt1ulxbCOEs/A+ZK/Km2jgihjyVxp/0z0hwIcxC6PrkWtgw==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.5.13", + "@webassemblyjs/helper-buffer": "1.5.13", + "@webassemblyjs/helper-wasm-bytecode": "1.5.13", + "@webassemblyjs/helper-wasm-section": "1.5.13", + "@webassemblyjs/wasm-gen": "1.5.13", + "@webassemblyjs/wasm-opt": "1.5.13", + "@webassemblyjs/wasm-parser": "1.5.13", + "@webassemblyjs/wast-printer": "1.5.13", + "debug": "^3.1.0" + }, + "dependencies": { + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "dev": true + } + } + }, + "@webassemblyjs/wasm-gen": { + "version": "1.5.13", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.5.13.tgz", + "integrity": "sha512-yfv94Se8R73zmr8GAYzezFHc3lDwE/lBXQddSiIZEKZFuqy7yWtm3KMwA1uGbv5G1WphimJxboXHR80IgX1hQA==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.5.13", + "@webassemblyjs/helper-wasm-bytecode": "1.5.13", + "@webassemblyjs/ieee754": "1.5.13", + "@webassemblyjs/leb128": "1.5.13", + "@webassemblyjs/utf8": "1.5.13" + } + }, + "@webassemblyjs/wasm-opt": { + "version": "1.5.13", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.5.13.tgz", + "integrity": "sha512-IkXSkgzVhQ0QYAdIayuCWMmXSYx0dHGU8Ah/AxJf1gBvstMWVnzJnBwLsXLyD87VSBIcsqkmZ28dVb0mOC3oBg==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.5.13", + "@webassemblyjs/helper-buffer": "1.5.13", + "@webassemblyjs/wasm-gen": "1.5.13", + "@webassemblyjs/wasm-parser": "1.5.13", + "debug": "^3.1.0" + }, + "dependencies": { + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "dev": true + } + } + }, + "@webassemblyjs/wasm-parser": { + "version": "1.5.13", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.5.13.tgz", + "integrity": "sha512-XnYoIcu2iqq8/LrtmdnN3T+bRjqYFjRHqWbqK3osD/0r/Fcv4d9ecRzjVtC29ENEuNTK4mQ9yyxCBCbK8S/cpg==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.5.13", + "@webassemblyjs/helper-api-error": "1.5.13", + "@webassemblyjs/helper-wasm-bytecode": "1.5.13", + "@webassemblyjs/ieee754": "1.5.13", + "@webassemblyjs/leb128": "1.5.13", + "@webassemblyjs/utf8": "1.5.13" + } + }, + "@webassemblyjs/wast-parser": { + "version": "1.5.13", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-parser/-/wast-parser-1.5.13.tgz", + "integrity": "sha512-Lbz65T0LQ1LgzKiUytl34CwuhMNhaCLgrh0JW4rJBN6INnBB8NMwUfQM+FxTnLY9qJ+lHJL/gCM5xYhB9oWi4A==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.5.13", + "@webassemblyjs/floating-point-hex-parser": "1.5.13", + "@webassemblyjs/helper-api-error": "1.5.13", + "@webassemblyjs/helper-code-frame": "1.5.13", + "@webassemblyjs/helper-fsm": "1.5.13", + "long": "^3.2.0", + "mamacro": "^0.0.3" + } + }, + "@webassemblyjs/wast-printer": { + "version": "1.5.13", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.5.13.tgz", + "integrity": "sha512-QcwogrdqcBh8Z+eUF8SG+ag5iwQSXxQJELBEHmLkk790wgQgnIMmntT2sMAMw53GiFNckArf5X0bsCA44j3lWQ==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.5.13", + "@webassemblyjs/wast-parser": "1.5.13", + "long": "^3.2.0" + } + }, + "@yarnpkg/lockfile": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz", + "integrity": "sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==", + "dev": true + }, + "JSONStream": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz", + "integrity": "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==", + "dev": true, + "requires": { + "jsonparse": "^1.2.0", + "through": ">=2.2.7 <3" + } + }, + "abbrev": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.0.9.tgz", + "integrity": "sha1-kbR5JYinc4wl813W9jdSovh3YTU=", + "dev": true + }, + "accepts": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.5.tgz", + "integrity": "sha1-63d99gEXI6OxTopywIBcjoZ0a9I=", + "dev": true, + "requires": { + "mime-types": "~2.1.18", + "negotiator": "0.6.1" + } + }, + "acorn": { + "version": "5.7.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.3.tgz", + "integrity": "sha512-T/zvzYRfbVojPWahDsE5evJdHb3oJoQfFbsrKM7w5Zcs++Tr257tia3BmMP8XYVjp1S9RZXQMh7gao96BlqZOw==", + "dev": true + }, + "acorn-dynamic-import": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/acorn-dynamic-import/-/acorn-dynamic-import-3.0.0.tgz", + "integrity": "sha512-zVWV8Z8lislJoOKKqdNMOB+s6+XV5WERty8MnKBeFgwA+19XJjJHs2RP5dzM57FftIs+jQnRToLiWazKr6sSWg==", + "dev": true, + "requires": { + "acorn": "^5.0.0" + } + }, + "adm-zip": { + "version": "0.4.13", + "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.4.13.tgz", + "integrity": "sha512-fERNJX8sOXfel6qCBCMPvZLzENBEhZTzKqg6vrOW5pvoEaQuJhRU4ndTAh6lHOxn1I6jnz2NHra56ZODM751uw==", + "dev": true + }, + "after": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/after/-/after-0.8.2.tgz", + "integrity": "sha1-/ts5T58OAqqXaOcCvaI7UF+ufh8=", + "dev": true + }, + "agent-base": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.2.1.tgz", + "integrity": "sha512-JVwXMr9nHYTUXsBFKUqhJwvlcYU/blreOEUkhNR2eXZIvwd+c+o5V4MgDPKWnMS/56awN3TRzIP+KoPn+roQtg==", + "dev": true, + "requires": { + "es6-promisify": "^5.0.0" + } + }, + "agentkeepalive": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-3.5.2.tgz", + "integrity": "sha512-e0L/HNe6qkQ7H19kTlRRqUibEAwDK5AFk6y3PtMsuut2VAH6+Q4xZml1tNDJD7kSAyqmbG/K08K5WEJYtUrSlQ==", + "dev": true, + "requires": { + "humanize-ms": "^1.2.1" + } + }, + "ajv": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.4.0.tgz", + "integrity": "sha1-06/3jpJ3VJdx2vAWTP9ISCt1T8Y=", + "dev": true, + "requires": { + "fast-deep-equal": "^1.0.0", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.3.0", + "uri-js": "^3.0.2" + } + }, + "ajv-errors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ajv-errors/-/ajv-errors-1.0.1.tgz", + "integrity": "sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ==", + "dev": true + }, + "ajv-keywords": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.4.0.tgz", + "integrity": "sha512-aUjdRFISbuFOl0EIZc+9e4FfZp0bDZgAdOOf30bJmw8VM9v84SHyVyxDfbWxpGYbdZD/9XoKxfHVNmxPkhwyGw==", + "dev": true + }, + "amdefine": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", + "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=", + "dev": true + }, + "angular-in-memory-web-api": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/angular-in-memory-web-api/-/angular-in-memory-web-api-0.8.0.tgz", + "integrity": "sha512-2n0YtCLFxZo4JePHvH6q8b7JmBmhZq44Ic8VaBPRSXE4vAmlKXHU+kI2quNa612EAETDRkZcvLOU8K8CkhIZgQ==" + }, + "ansi-colors": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.3.tgz", + "integrity": "sha512-LEHHyuhlPY3TmuUYMh2oz89lTShfvgbmzaBcxve9t/9Wuy7Dwf4yoAKcND7KFT1HAQfqZ12qtc+DUrBMeKF9nw==", + "dev": true + }, + "ansi-html": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/ansi-html/-/ansi-html-0.0.7.tgz", + "integrity": "sha1-gTWEAhliqenm/QOflA0S9WynhZ4=", + "dev": true + }, + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "anymatch": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", + "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", + "dev": true, + "requires": { + "micromatch": "^3.1.4", + "normalize-path": "^2.1.1" + } + }, + "app-root-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/app-root-path/-/app-root-path-2.1.0.tgz", + "integrity": "sha1-mL9lmTJ+zqGZMJhm6BQDaP0uZGo=", + "dev": true + }, + "append-transform": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-1.0.0.tgz", + "integrity": "sha512-P009oYkeHyU742iSZJzZZywj4QRJdnTWffaKuJQLablCZ1uz6/cW4yaRgcDaoQ+uwOxxnt0gRUcwfsNP2ri0gw==", + "dev": true, + "requires": { + "default-require-extensions": "^2.0.0" + } + }, + "aproba": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", + "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", + "dev": true + }, + "are-we-there-yet": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz", + "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", + "dev": true, + "requires": { + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" + } + }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "arr-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", + "dev": true + }, + "arr-flatten": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", + "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", + "dev": true + }, + "arr-union": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", + "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", + "dev": true + }, + "array-find-index": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", + "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=", + "dev": true, + "optional": true + }, + "array-flatten": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz", + "integrity": "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==", + "dev": true + }, + "array-slice": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-0.2.3.tgz", + "integrity": "sha1-3Tz7gO15c6dRF82sabC5nshhhvU=", + "dev": true + }, + "array-union": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", + "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", + "dev": true, + "requires": { + "array-uniq": "^1.0.1" + } + }, + "array-uniq": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", + "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=", + "dev": true + }, + "array-unique": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", + "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", + "dev": true + }, + "arraybuffer.slice": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/arraybuffer.slice/-/arraybuffer.slice-0.0.7.tgz", + "integrity": "sha512-wGUIVQXuehL5TCqQun8OW81jGzAWycqzFF8lFp+GOM5BXLYj3bKNsYC4daB7n6XjCqxQA/qgTJ+8ANR3acjrog==", + "dev": true + }, + "arrify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", + "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", + "dev": true + }, + "asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=", + "dev": true, + "optional": true + }, + "asn1": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", + "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", + "dev": true, + "requires": { + "safer-buffer": "~2.1.0" + } + }, + "asn1.js": { + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.10.1.tgz", + "integrity": "sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==", + "dev": true, + "requires": { + "bn.js": "^4.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, + "assert": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/assert/-/assert-1.4.1.tgz", + "integrity": "sha1-mZEtWRg2tab1s0XA8H7vwI/GXZE=", + "dev": true, + "requires": { + "util": "0.10.3" + }, + "dependencies": { + "inherits": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", + "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=", + "dev": true + }, + "util": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", + "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", + "dev": true, + "requires": { + "inherits": "2.0.1" + } + } + } + }, + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "dev": true + }, + "assign-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", + "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", + "dev": true + }, + "async": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", + "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=", + "dev": true + }, + "async-each": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.1.tgz", + "integrity": "sha1-GdOGodntxufByF04iu28xW0zYC0=", + "dev": true + }, + "async-foreach": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/async-foreach/-/async-foreach-0.1.3.tgz", + "integrity": "sha1-NhIfhFwFeBct5Bmpfb6x0W7DRUI=", + "dev": true, + "optional": true + }, + "async-limiter": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz", + "integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg==", + "dev": true + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", + "dev": true + }, + "atob": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", + "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", + "dev": true + }, + "autoprefixer": { + "version": "8.6.5", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-8.6.5.tgz", + "integrity": "sha512-PLWJN3Xo/rycNkx+mp8iBDMTm3FeWe4VmYaZDSqL5QQB9sLsQkG5k8n+LNDFnhh9kdq2K+egL/icpctOmDHwig==", + "dev": true, + "requires": { + "browserslist": "^3.2.8", + "caniuse-lite": "^1.0.30000864", + "normalize-range": "^0.1.2", + "num2fraction": "^1.2.2", + "postcss": "^6.0.23", + "postcss-value-parser": "^3.2.3" + } + }, + "aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", + "dev": true + }, + "aws4": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz", + "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==", + "dev": true + }, + "babel-code-frame": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", + "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=", + "dev": true, + "requires": { + "chalk": "^1.1.3", + "esutils": "^2.0.2", + "js-tokens": "^3.0.2" + }, + "dependencies": { + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + } + } + }, + "babel-generator": { + "version": "6.26.1", + "resolved": "https://registry.npmjs.org/babel-generator/-/babel-generator-6.26.1.tgz", + "integrity": "sha512-HyfwY6ApZj7BYTcJURpM5tznulaBvyio7/0d4zFOeMPUmfxkCjHocCuoLa2SAGzBI8AREcH3eP3758F672DppA==", + "dev": true, + "requires": { + "babel-messages": "^6.23.0", + "babel-runtime": "^6.26.0", + "babel-types": "^6.26.0", + "detect-indent": "^4.0.0", + "jsesc": "^1.3.0", + "lodash": "^4.17.4", + "source-map": "^0.5.7", + "trim-right": "^1.0.1" + } + }, + "babel-messages": { + "version": "6.23.0", + "resolved": "https://registry.npmjs.org/babel-messages/-/babel-messages-6.23.0.tgz", + "integrity": "sha1-8830cDhYA1sqKVHG7F7fbGLyYw4=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-runtime": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", + "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", + "dev": true, + "requires": { + "core-js": "^2.4.0", + "regenerator-runtime": "^0.11.0" + } + }, + "babel-template": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-template/-/babel-template-6.26.0.tgz", + "integrity": "sha1-3gPi0WOWsGn0bdn/+FIfsaDjXgI=", + "dev": true, + "requires": { + "babel-runtime": "^6.26.0", + "babel-traverse": "^6.26.0", + "babel-types": "^6.26.0", + "babylon": "^6.18.0", + "lodash": "^4.17.4" + } + }, + "babel-traverse": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.26.0.tgz", + "integrity": "sha1-RqnL1+3MYsjlwGTi0tjQ9ANXZu4=", + "dev": true, + "requires": { + "babel-code-frame": "^6.26.0", + "babel-messages": "^6.23.0", + "babel-runtime": "^6.26.0", + "babel-types": "^6.26.0", + "babylon": "^6.18.0", + "debug": "^2.6.8", + "globals": "^9.18.0", + "invariant": "^2.2.2", + "lodash": "^4.17.4" + } + }, + "babel-types": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-types/-/babel-types-6.26.0.tgz", + "integrity": "sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc=", + "dev": true, + "requires": { + "babel-runtime": "^6.26.0", + "esutils": "^2.0.2", + "lodash": "^4.17.4", + "to-fast-properties": "^1.0.3" + } + }, + "babylon": { + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz", + "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==", + "dev": true + }, + "backo2": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz", + "integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc=", + "dev": true + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true + }, + "base": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", + "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", + "dev": true, + "requires": { + "cache-base": "^1.0.1", + "class-utils": "^0.3.5", + "component-emitter": "^1.2.1", + "define-property": "^1.0.0", + "isobject": "^3.0.1", + "mixin-deep": "^1.2.0", + "pascalcase": "^0.1.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "base64-arraybuffer": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz", + "integrity": "sha1-c5JncZI7Whl0etZmqlzUv5xunOg=", + "dev": true + }, + "base64-js": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.0.tgz", + "integrity": "sha512-ccav/yGvoa80BQDljCxsmmQ3Xvx60/UpBIij5QN21W3wBi/hhIC9OoO+KLpu9IJTS9j4DRVJ3aDDF9cMSoa2lw==", + "dev": true + }, + "base64id": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/base64id/-/base64id-1.0.0.tgz", + "integrity": "sha1-R2iMuZu2gE8OBtPnY7HDLlfY5rY=", + "dev": true + }, + "batch": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", + "integrity": "sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY=", + "dev": true + }, + "bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", + "dev": true, + "requires": { + "tweetnacl": "^0.14.3" + } + }, + "better-assert": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/better-assert/-/better-assert-1.0.2.tgz", + "integrity": "sha1-QIZrnhueC1W0gYlDEeaPr/rrxSI=", + "dev": true, + "requires": { + "callsite": "1.0.0" + } + }, + "big.js": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-3.2.0.tgz", + "integrity": "sha512-+hN/Zh2D08Mx65pZ/4g5bsmNiZUuChDiQfTUQ7qJr4/kuopCr88xZsAXv6mBoZEsUI4OuGHlX59qE94K2mMW8Q==", + "dev": true + }, + "binary-extensions": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.0.tgz", + "integrity": "sha512-EgmjVLMn22z7eGGv3kcnHwSnJXmFHjISTY9E/S5lIcTD3Oxw05QTcBLNkJFzcb3cNueUdF/IN4U+d78V0zO8Hw==", + "dev": true + }, + "blob": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/blob/-/blob-0.0.5.tgz", + "integrity": "sha512-gaqbzQPqOoamawKg0LGVd7SzLgXS+JH61oWprSLH+P+abTczqJbhTR8CmJ2u9/bUYNmHTGJx/UEmn6doAvvuig==", + "dev": true + }, + "block-stream": { + "version": "0.0.9", + "resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz", + "integrity": "sha1-E+v+d4oDIFz+A3UUgeu0szAMEmo=", + "dev": true, + "optional": true, + "requires": { + "inherits": "~2.0.0" + } + }, + "blocking-proxy": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/blocking-proxy/-/blocking-proxy-1.0.1.tgz", + "integrity": "sha512-KE8NFMZr3mN2E0HcvCgRtX7DjhiIQrwle+nSVJVC/yqFb9+xznHl2ZcoBp2L9qzkI4t4cBFJ1efXF8Dwi132RA==", + "dev": true, + "requires": { + "minimist": "^1.2.0" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "dev": true + } + } + }, + "bluebird": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.3.tgz", + "integrity": "sha512-/qKPUQlaW1OyR51WeCPBvRnAlnZFUJkCSG5HzGnuIqhgyJtF+T94lFnn33eiazjRm2LAHVy2guNnaq48X9SJuw==", + "dev": true + }, + "bn.js": { + "version": "4.11.8", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", + "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==", + "dev": true + }, + "body-parser": { + "version": "1.18.3", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.3.tgz", + "integrity": "sha1-WykhmP/dVTs6DyDe0FkrlWlVyLQ=", + "dev": true, + "requires": { + "bytes": "3.0.0", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "~1.1.2", + "http-errors": "~1.6.3", + "iconv-lite": "0.4.23", + "on-finished": "~2.3.0", + "qs": "6.5.2", + "raw-body": "2.3.3", + "type-is": "~1.6.16" + } + }, + "bonjour": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/bonjour/-/bonjour-3.5.0.tgz", + "integrity": "sha1-jokKGD2O6aI5OzhExpGkK897yfU=", + "dev": true, + "requires": { + "array-flatten": "^2.1.0", + "deep-equal": "^1.0.1", + "dns-equal": "^1.0.0", + "dns-txt": "^2.0.2", + "multicast-dns": "^6.0.1", + "multicast-dns-service-types": "^1.1.0" + } + }, + "boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=", + "dev": true + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "dev": true, + "requires": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "brorand": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=", + "dev": true + }, + "browserify-aes": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", + "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", + "dev": true, + "requires": { + "buffer-xor": "^1.0.3", + "cipher-base": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.3", + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "browserify-cipher": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz", + "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==", + "dev": true, + "requires": { + "browserify-aes": "^1.0.4", + "browserify-des": "^1.0.0", + "evp_bytestokey": "^1.0.0" + } + }, + "browserify-des": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz", + "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==", + "dev": true, + "requires": { + "cipher-base": "^1.0.1", + "des.js": "^1.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "browserify-rsa": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz", + "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=", + "dev": true, + "requires": { + "bn.js": "^4.1.0", + "randombytes": "^2.0.1" + } + }, + "browserify-sign": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.0.4.tgz", + "integrity": "sha1-qk62jl17ZYuqa/alfmMMvXqT0pg=", + "dev": true, + "requires": { + "bn.js": "^4.1.1", + "browserify-rsa": "^4.0.0", + "create-hash": "^1.1.0", + "create-hmac": "^1.1.2", + "elliptic": "^6.0.0", + "inherits": "^2.0.1", + "parse-asn1": "^5.0.0" + } + }, + "browserify-zlib": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz", + "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==", + "dev": true, + "requires": { + "pako": "~1.0.5" + } + }, + "browserslist": { + "version": "3.2.8", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-3.2.8.tgz", + "integrity": "sha512-WHVocJYavUwVgVViC0ORikPHQquXwVh939TaelZ4WDqpWgTX/FsGhl/+P4qBUAGcRvtOgDgC+xftNWWp2RUTAQ==", + "dev": true, + "requires": { + "caniuse-lite": "^1.0.30000844", + "electron-to-chromium": "^1.3.47" + } + }, + "browserstack": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/browserstack/-/browserstack-1.5.2.tgz", + "integrity": "sha512-+6AFt9HzhKykcPF79W6yjEUJcdvZOV0lIXdkORXMJftGrDl0OKWqRF4GHqpDNkxiceDT/uB7Fb/aDwktvXX7dg==", + "dev": true, + "requires": { + "https-proxy-agent": "^2.2.1" + } + }, + "buffer": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", + "integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=", + "dev": true, + "requires": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4", + "isarray": "^1.0.0" + } + }, + "buffer-alloc": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.2.0.tgz", + "integrity": "sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==", + "dev": true, + "requires": { + "buffer-alloc-unsafe": "^1.1.0", + "buffer-fill": "^1.0.0" + } + }, + "buffer-alloc-unsafe": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz", + "integrity": "sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==", + "dev": true + }, + "buffer-fill": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz", + "integrity": "sha1-+PeLdniYiO858gXNY39o5wISKyw=", + "dev": true + }, + "buffer-from": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", + "dev": true + }, + "buffer-indexof": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-indexof/-/buffer-indexof-1.1.1.tgz", + "integrity": "sha512-4/rOEg86jivtPTeOUUT61jJO1Ya1TrR/OkqCSZDyq84WJh3LuuiphBYJN+fm5xufIk4XAFcEwte/8WzC8If/1g==", + "dev": true + }, + "buffer-xor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", + "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=", + "dev": true + }, + "builtin-modules": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", + "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=", + "dev": true + }, + "builtin-status-codes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", + "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=", + "dev": true + }, + "builtins": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/builtins/-/builtins-1.0.3.tgz", + "integrity": "sha1-y5T662HIaWRR2zZTThQi+U8K7og=", + "dev": true + }, + "bytes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=", + "dev": true + }, + "cacache": { + "version": "10.0.4", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-10.0.4.tgz", + "integrity": "sha512-Dph0MzuH+rTQzGPNT9fAnrPmMmjKfST6trxJeK7NQuHRaVw24VzPRWTmg9MpcwOVQZO0E1FBICUlFeNaKPIfHA==", + "dev": true, + "requires": { + "bluebird": "^3.5.1", + "chownr": "^1.0.1", + "glob": "^7.1.2", + "graceful-fs": "^4.1.11", + "lru-cache": "^4.1.1", + "mississippi": "^2.0.0", + "mkdirp": "^0.5.1", + "move-concurrently": "^1.0.1", + "promise-inflight": "^1.0.1", + "rimraf": "^2.6.2", + "ssri": "^5.2.4", + "unique-filename": "^1.1.0", + "y18n": "^4.0.0" + } + }, + "cache-base": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", + "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", + "dev": true, + "requires": { + "collection-visit": "^1.0.0", + "component-emitter": "^1.2.1", + "get-value": "^2.0.6", + "has-value": "^1.0.0", + "isobject": "^3.0.1", + "set-value": "^2.0.0", + "to-object-path": "^0.3.0", + "union-value": "^1.0.0", + "unset-value": "^1.0.0" + } + }, + "callsite": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/callsite/-/callsite-1.0.0.tgz", + "integrity": "sha1-KAOY5dZkvXQDi28JBRU+borxvCA=", + "dev": true + }, + "camel-case": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-3.0.0.tgz", + "integrity": "sha1-yjw2iKTpzzpM2nd9xNy8cTJJz3M=", + "dev": true, + "requires": { + "no-case": "^2.2.0", + "upper-case": "^1.1.1" + } + }, + "camelcase": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", + "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=", + "dev": true, + "optional": true + }, + "camelcase-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz", + "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=", + "dev": true, + "optional": true, + "requires": { + "camelcase": "^2.0.0", + "map-obj": "^1.0.0" + } + }, + "caniuse-lite": { + "version": "1.0.30000938", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30000938.tgz", + "integrity": "sha512-ekW8NQ3/FvokviDxhdKLZZAx7PptXNwxKgXtnR5y+PR3hckwuP3yJ1Ir+4/c97dsHNqtAyfKUGdw8P4EYzBNgw==", + "dev": true + }, + "caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", + "dev": true + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "chokidar": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.0.4.tgz", + "integrity": "sha512-z9n7yt9rOvIJrMhvDtDictKrkFHeihkNl6uWMmZlmL6tJtX9Cs+87oK+teBx+JIgzvbX3yZHT3eF8vpbDxHJXQ==", + "dev": true, + "requires": { + "anymatch": "^2.0.0", + "async-each": "^1.0.0", + "braces": "^2.3.0", + "fsevents": "^1.2.2", + "glob-parent": "^3.1.0", + "inherits": "^2.0.1", + "is-binary-path": "^1.0.0", + "is-glob": "^4.0.0", + "lodash.debounce": "^4.0.8", + "normalize-path": "^2.1.1", + "path-is-absolute": "^1.0.0", + "readdirp": "^2.0.0", + "upath": "^1.0.5" + } + }, + "chownr": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.1.tgz", + "integrity": "sha512-j38EvO5+LHX84jlo6h4UzmOwi0UgW61WRyPtJz4qaadK5eY3BTS5TY/S1Stc3Uk2lIM6TPevAlULiEJwie860g==", + "dev": true + }, + "chrome-trace-event": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.0.tgz", + "integrity": "sha512-xDbVgyfDTT2piup/h8dK/y4QZfJRSa73bw1WZ8b4XM1o7fsFubUVGYcE+1ANtOzJJELGpYoG2961z0Z6OAld9A==", + "dev": true, + "requires": { + "tslib": "^1.9.0" + } + }, + "cipher-base": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", + "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "circular-dependency-plugin": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/circular-dependency-plugin/-/circular-dependency-plugin-5.0.2.tgz", + "integrity": "sha512-oC7/DVAyfcY3UWKm0sN/oVoDedQDQiw/vIiAnuTWTpE5s0zWf7l3WY417Xw/Fbi/QbAjctAkxgMiS9P0s3zkmA==", + "dev": true + }, + "circular-json": { + "version": "0.5.9", + "resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.5.9.tgz", + "integrity": "sha512-4ivwqHpIFJZBuhN3g/pEcdbnGUywkBblloGbkglyloVjjR3uT6tieI89MVOfbP2tHX5sgb01FuLgAOzebNlJNQ==", + "dev": true + }, + "class-utils": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", + "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", + "dev": true, + "requires": { + "arr-union": "^3.1.0", + "define-property": "^0.2.5", + "isobject": "^3.0.0", + "static-extend": "^0.1.1" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + } + } + }, + "clean-css": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.1.tgz", + "integrity": "sha512-4ZxI6dy4lrY6FHzfiy1aEOXgu4LIsW2MhwG0VBKdcoGoH/XLFgaHSdLTGr4O8Be6A8r3MOphEiI8Gc1n0ecf3g==", + "dev": true, + "requires": { + "source-map": "~0.6.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "cliui": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", + "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", + "dev": true, + "optional": true, + "requires": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wrap-ansi": "^2.0.0" + } + }, + "clone": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", + "integrity": "sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18=", + "dev": true + }, + "clone-deep": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-2.0.2.tgz", + "integrity": "sha512-SZegPTKjCgpQH63E+eN6mVEEPdQBOUzjyJm5Pora4lrwWRFS8I0QAxV/KD6vV/i0WuijHZWQC1fMsPEdxfdVCQ==", + "dev": true, + "requires": { + "for-own": "^1.0.0", + "is-plain-object": "^2.0.4", + "kind-of": "^6.0.0", + "shallow-clone": "^1.0.0" + } + }, + "co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", + "dev": true + }, + "code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", + "dev": true + }, + "codelyzer": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/codelyzer/-/codelyzer-4.3.0.tgz", + "integrity": "sha512-RLMrtLwrBS0dfo2/KTP+2NHofCpzcuh0bEp/A/naqvQonbUL4AW/qWQdbpn8dMNudtpmzEx9eS8KEpGdVPg1BA==", + "dev": true, + "requires": { + "app-root-path": "^2.0.1", + "css-selector-tokenizer": "^0.7.0", + "cssauron": "^1.4.0", + "semver-dsl": "^1.0.1", + "source-map": "^0.5.7", + "sprintf-js": "^1.0.3" + } + }, + "collection-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", + "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", + "dev": true, + "requires": { + "map-visit": "^1.0.0", + "object-visit": "^1.0.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "colors": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz", + "integrity": "sha1-FopHAXVran9RoSzgyXv6KMCE7WM=", + "dev": true + }, + "combine-lists": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/combine-lists/-/combine-lists-1.0.1.tgz", + "integrity": "sha1-RYwH4J4NkA/Ci3Cj/sLazR0st/Y=", + "dev": true, + "requires": { + "lodash": "^4.5.0" + } + }, + "combined-stream": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.7.tgz", + "integrity": "sha512-brWl9y6vOB1xYPZcpZde3N9zDByXTosAeMDo4p1wzo6UMOX4vumB+TP1RZ76sfE6Md68Q0NJSrE/gbezd4Ul+w==", + "dev": true, + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "commander": { + "version": "2.17.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.17.1.tgz", + "integrity": "sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg==", + "dev": true + }, + "commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", + "dev": true + }, + "compare-versions": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-3.4.0.tgz", + "integrity": "sha512-tK69D7oNXXqUW3ZNo/z7NXTEz22TCF0pTE+YF9cxvaAM9XnkLo1fV621xCLrRR6aevJlKxExkss0vWqUCUpqdg==", + "dev": true + }, + "component-bind": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/component-bind/-/component-bind-1.0.0.tgz", + "integrity": "sha1-AMYIq33Nk4l8AAllGx06jh5zu9E=", + "dev": true + }, + "component-emitter": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", + "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=", + "dev": true + }, + "component-inherit": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/component-inherit/-/component-inherit-0.0.3.tgz", + "integrity": "sha1-ZF/ErfWLcrZJ1crmUTVhnbJv8UM=", + "dev": true + }, + "compressible": { + "version": "2.0.16", + "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.16.tgz", + "integrity": "sha512-JQfEOdnI7dASwCuSPWIeVYwc/zMsu/+tRhoUvEfXz2gxOA2DNjmG5vhtFdBlhWPPGo+RdT9S3tgc/uH5qgDiiA==", + "dev": true, + "requires": { + "mime-db": ">= 1.38.0 < 2" + } + }, + "compression": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.3.tgz", + "integrity": "sha512-HSjyBG5N1Nnz7tF2+O7A9XUhyjru71/fwgNb7oIsEVHR0WShfs2tIS/EySLgiTe98aOK18YDlMXpzjCXY/n9mg==", + "dev": true, + "requires": { + "accepts": "~1.3.5", + "bytes": "3.0.0", + "compressible": "~2.0.14", + "debug": "2.6.9", + "on-headers": "~1.0.1", + "safe-buffer": "5.1.2", + "vary": "~1.1.2" + } + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + } + }, + "connect": { + "version": "3.6.6", + "resolved": "https://registry.npmjs.org/connect/-/connect-3.6.6.tgz", + "integrity": "sha1-Ce/2xVr3I24TcTWnJXSFi2eG9SQ=", + "dev": true, + "requires": { + "debug": "2.6.9", + "finalhandler": "1.1.0", + "parseurl": "~1.3.2", + "utils-merge": "1.0.1" + }, + "dependencies": { + "finalhandler": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.0.tgz", + "integrity": "sha1-zgtoVbRYU+eRsvzGgARtiCU91/U=", + "dev": true, + "requires": { + "debug": "2.6.9", + "encodeurl": "~1.0.1", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.2", + "statuses": "~1.3.1", + "unpipe": "~1.0.0" + } + }, + "statuses": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.3.1.tgz", + "integrity": "sha1-+vUbnrdKrvOzrPStX2Gr8ky3uT4=", + "dev": true + } + } + }, + "connect-history-api-fallback": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz", + "integrity": "sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg==", + "dev": true + }, + "console-browserify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.1.0.tgz", + "integrity": "sha1-8CQcRXMKn8YyOyBtvzjtx0HQuxA=", + "dev": true, + "requires": { + "date-now": "^0.1.4" + } + }, + "console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", + "dev": true + }, + "constants-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", + "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=", + "dev": true + }, + "content-disposition": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", + "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ=", + "dev": true + }, + "content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", + "dev": true + }, + "convert-source-map": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.6.0.tgz", + "integrity": "sha512-eFu7XigvxdZ1ETfbgPBohgyQ/Z++C0eEhTor0qRwBw9unw+L0/6V8wkSuGgzdThkiS5lSpdptOQPD8Ak40a+7A==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.1" + } + }, + "cookie": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", + "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=", + "dev": true + }, + "cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=", + "dev": true + }, + "copy-concurrently": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/copy-concurrently/-/copy-concurrently-1.0.5.tgz", + "integrity": "sha512-f2domd9fsVDFtaFcbaRZuYXwtdmnzqbADSwhSWYxYB/Q8zsdUUFMXVRwXGDMWmbEzAn1kdRrtI1T/KTFOL4X2A==", + "dev": true, + "requires": { + "aproba": "^1.1.1", + "fs-write-stream-atomic": "^1.0.8", + "iferr": "^0.1.5", + "mkdirp": "^0.5.1", + "rimraf": "^2.5.4", + "run-queue": "^1.0.0" + } + }, + "copy-descriptor": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", + "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", + "dev": true + }, + "copy-webpack-plugin": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-4.5.2.tgz", + "integrity": "sha512-zmC33E8FFSq3AbflTvqvPvBo621H36Afsxlui91d+QyZxPIuXghfnTsa1CuqiAaCPgJoSUWfTFbKJnadZpKEbQ==", + "dev": true, + "requires": { + "cacache": "^10.0.4", + "find-cache-dir": "^1.0.0", + "globby": "^7.1.1", + "is-glob": "^4.0.0", + "loader-utils": "^1.1.0", + "minimatch": "^3.0.4", + "p-limit": "^1.0.0", + "serialize-javascript": "^1.4.0" + } + }, + "core-js": { + "version": "2.6.5", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.5.tgz", + "integrity": "sha512-klh/kDpwX8hryYL14M9w/xei6vrv6sE8gTHDG7/T/+SEovB/G4ejwcfE/CBzO6Edsu+OETZMZ3wcX/EjUkrl5A==" + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", + "dev": true + }, + "cosmiconfig": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-4.0.0.tgz", + "integrity": "sha512-6e5vDdrXZD+t5v0L8CrurPeybg4Fmf+FCSYxXKYVAqLUtyCSbuyqE059d0kDthTNRzKVjL7QMgNpEUlsoYH3iQ==", + "dev": true, + "requires": { + "is-directory": "^0.3.1", + "js-yaml": "^3.9.0", + "parse-json": "^4.0.0", + "require-from-string": "^2.0.1" + }, + "dependencies": { + "parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", + "dev": true, + "requires": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + } + } + } + }, + "create-ecdh": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.3.tgz", + "integrity": "sha512-GbEHQPMOswGpKXM9kCWVrremUcBmjteUaQ01T9rkKCPDXfUHX0IoP9LpHYo2NPFampa4e+/pFDc3jQdxrxQLaw==", + "dev": true, + "requires": { + "bn.js": "^4.1.0", + "elliptic": "^6.0.0" + } + }, + "create-hash": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", + "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", + "dev": true, + "requires": { + "cipher-base": "^1.0.1", + "inherits": "^2.0.1", + "md5.js": "^1.3.4", + "ripemd160": "^2.0.1", + "sha.js": "^2.4.0" + } + }, + "create-hmac": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", + "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", + "dev": true, + "requires": { + "cipher-base": "^1.0.3", + "create-hash": "^1.1.0", + "inherits": "^2.0.1", + "ripemd160": "^2.0.0", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "cross-spawn": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-3.0.1.tgz", + "integrity": "sha1-ElYDfsufDF9549bvE14wdwGEuYI=", + "dev": true, + "optional": true, + "requires": { + "lru-cache": "^4.0.1", + "which": "^1.2.9" + } + }, + "crypto-browserify": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", + "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==", + "dev": true, + "requires": { + "browserify-cipher": "^1.0.0", + "browserify-sign": "^4.0.0", + "create-ecdh": "^4.0.0", + "create-hash": "^1.1.0", + "create-hmac": "^1.1.0", + "diffie-hellman": "^5.0.0", + "inherits": "^2.0.1", + "pbkdf2": "^3.0.3", + "public-encrypt": "^4.0.0", + "randombytes": "^2.0.0", + "randomfill": "^1.0.3" + } + }, + "css-parse": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/css-parse/-/css-parse-1.7.0.tgz", + "integrity": "sha1-Mh9s9zeCpv91ERE5D8BeLGV9jJs=", + "dev": true + }, + "css-select": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz", + "integrity": "sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg=", + "dev": true, + "requires": { + "boolbase": "~1.0.0", + "css-what": "2.1", + "domutils": "1.5.1", + "nth-check": "~1.0.1" + } + }, + "css-selector-tokenizer": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/css-selector-tokenizer/-/css-selector-tokenizer-0.7.1.tgz", + "integrity": "sha512-xYL0AMZJ4gFzJQsHUKa5jiWWi2vH77WVNg7JYRyewwj6oPh4yb/y6Y9ZCw9dsj/9UauMhtuxR+ogQd//EdEVNA==", + "dev": true, + "requires": { + "cssesc": "^0.1.0", + "fastparse": "^1.1.1", + "regexpu-core": "^1.0.0" + } + }, + "css-what": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-2.1.3.tgz", + "integrity": "sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg==", + "dev": true + }, + "cssauron": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/cssauron/-/cssauron-1.4.0.tgz", + "integrity": "sha1-pmAt/34EqDBtwNuaVR6S6LVmKtg=", + "dev": true, + "requires": { + "through": "X.X.X" + } + }, + "cssesc": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-0.1.0.tgz", + "integrity": "sha1-yBSQPkViM3GgR3tAEJqq++6t27Q=", + "dev": true + }, + "cuint": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/cuint/-/cuint-0.2.2.tgz", + "integrity": "sha1-QICG1AlVDCYxFVYZ6fp7ytw7mRs=", + "dev": true + }, + "currently-unhandled": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", + "integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=", + "dev": true, + "optional": true, + "requires": { + "array-find-index": "^1.0.1" + } + }, + "custom-event": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/custom-event/-/custom-event-1.0.1.tgz", + "integrity": "sha1-XQKkaFCt8bSjF5RqOSj8y1v9BCU=", + "dev": true + }, + "cyclist": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/cyclist/-/cyclist-0.2.2.tgz", + "integrity": "sha1-GzN5LhHpFKL9bW7WRHRkRE5fpkA=", + "dev": true + }, + "dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "dev": true, + "requires": { + "assert-plus": "^1.0.0" + } + }, + "date-format": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/date-format/-/date-format-1.2.0.tgz", + "integrity": "sha1-YV6CjiM90aubua4JUODOzPpuytg=", + "dev": true + }, + "date-now": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz", + "integrity": "sha1-6vQ5/U1ISK105cx9vvIAZyueNFs=", + "dev": true + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "dev": true + }, + "decode-uri-component": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", + "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", + "dev": true + }, + "deep-equal": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.0.1.tgz", + "integrity": "sha1-9dJgKStmDghO/0zbyfCK0yR0SLU=", + "dev": true + }, + "deep-is": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", + "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", + "dev": true + }, + "default-gateway": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-2.7.2.tgz", + "integrity": "sha512-lAc4i9QJR0YHSDFdzeBQKfZ1SRDG3hsJNEkrpcZa8QhBfidLAilT60BDEIVUUGqosFp425KOgB3uYqcnQrWafQ==", + "dev": true, + "requires": { + "execa": "^0.10.0", + "ip-regex": "^2.1.0" + } + }, + "default-require-extensions": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-2.0.0.tgz", + "integrity": "sha1-9fj7sYp9bVCyH2QfZJ67Uiz+JPc=", + "dev": true, + "requires": { + "strip-bom": "^3.0.0" + }, + "dependencies": { + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "dev": true + } + } + }, + "define-properties": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", + "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "dev": true, + "requires": { + "object-keys": "^1.0.12" + } + }, + "define-property": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", + "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", + "dev": true, + "requires": { + "is-descriptor": "^1.0.2", + "isobject": "^3.0.1" + }, + "dependencies": { + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "del": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/del/-/del-3.0.0.tgz", + "integrity": "sha1-U+z2mf/LyzljdpGrE7rxYIGXZuU=", + "dev": true, + "requires": { + "globby": "^6.1.0", + "is-path-cwd": "^1.0.0", + "is-path-in-cwd": "^1.0.0", + "p-map": "^1.1.1", + "pify": "^3.0.0", + "rimraf": "^2.2.8" + }, + "dependencies": { + "globby": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", + "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=", + "dev": true, + "requires": { + "array-union": "^1.0.1", + "glob": "^7.0.3", + "object-assign": "^4.0.1", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + }, + "dependencies": { + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + } + } + } + } + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", + "dev": true + }, + "delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", + "dev": true + }, + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", + "dev": true + }, + "des.js": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.0.tgz", + "integrity": "sha1-wHTS4qpqipoH29YfmhXCzYPsjsw=", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, + "destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=", + "dev": true + }, + "detect-indent": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-4.0.0.tgz", + "integrity": "sha1-920GQ1LN9Docts5hnE7jqUdd4gg=", + "dev": true, + "requires": { + "repeating": "^2.0.0" + } + }, + "detect-node": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.0.4.tgz", + "integrity": "sha512-ZIzRpLJrOj7jjP2miAtgqIfmzbxa4ZOr5jJc601zklsfEx9oTzmmj2nVpIPRpNlRTIh8lc1kyViIY7BWSGNmKw==", + "dev": true + }, + "di": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/di/-/di-0.0.1.tgz", + "integrity": "sha1-gGZJMmzqp8qjMG112YXqJ0i6kTw=", + "dev": true + }, + "diff": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", + "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", + "dev": true + }, + "diffie-hellman": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", + "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", + "dev": true, + "requires": { + "bn.js": "^4.1.0", + "miller-rabin": "^4.0.0", + "randombytes": "^2.0.0" + } + }, + "dir-glob": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-2.2.2.tgz", + "integrity": "sha512-f9LBi5QWzIW3I6e//uxZoLBlUt9kcp66qo0sSCxL6YZKc75R1c4MFCoe/LaZiBGmgujvQdxc5Bn3QhfyvK5Hsw==", + "dev": true, + "requires": { + "path-type": "^3.0.0" + } + }, + "dns-equal": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz", + "integrity": "sha1-s55/HabrCnW6nBcySzR1PEfgZU0=", + "dev": true + }, + "dns-packet": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-1.3.1.tgz", + "integrity": "sha512-0UxfQkMhYAUaZI+xrNZOz/as5KgDU0M/fQ9b6SpkyLbk3GEswDi6PADJVaYJradtRVsRIlF1zLyOodbcTCDzUg==", + "dev": true, + "requires": { + "ip": "^1.1.0", + "safe-buffer": "^5.0.1" + } + }, + "dns-txt": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/dns-txt/-/dns-txt-2.0.2.tgz", + "integrity": "sha1-uR2Ab10nGI5Ks+fRB9iBocxGQrY=", + "dev": true, + "requires": { + "buffer-indexof": "^1.0.0" + } + }, + "dom-converter": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/dom-converter/-/dom-converter-0.2.0.tgz", + "integrity": "sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA==", + "dev": true, + "requires": { + "utila": "~0.4" + } + }, + "dom-serialize": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/dom-serialize/-/dom-serialize-2.2.1.tgz", + "integrity": "sha1-ViromZ9Evl6jB29UGdzVnrQ6yVs=", + "dev": true, + "requires": { + "custom-event": "~1.0.0", + "ent": "~2.2.0", + "extend": "^3.0.0", + "void-elements": "^2.0.0" + } + }, + "dom-serializer": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.1.tgz", + "integrity": "sha512-l0IU0pPzLWSHBcieZbpOKgkIn3ts3vAh7ZuFyXNwJxJXk/c4Gwj9xaTJwIDVQCXawWD0qb3IzMGH5rglQaO0XA==", + "dev": true, + "requires": { + "domelementtype": "^1.3.0", + "entities": "^1.1.1" + } + }, + "domain-browser": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz", + "integrity": "sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==", + "dev": true + }, + "domelementtype": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz", + "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==", + "dev": true + }, + "domhandler": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.1.0.tgz", + "integrity": "sha1-0mRvXlf2w7qxHPbLBdPArPdBJZQ=", + "dev": true, + "requires": { + "domelementtype": "1" + } + }, + "domutils": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz", + "integrity": "sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=", + "dev": true, + "requires": { + "dom-serializer": "0", + "domelementtype": "1" + } + }, + "duplexify": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz", + "integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==", + "dev": true, + "requires": { + "end-of-stream": "^1.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.0.0", + "stream-shift": "^1.0.0" + } + }, + "ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", + "dev": true, + "requires": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, + "ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=", + "dev": true + }, + "ejs": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-2.6.1.tgz", + "integrity": "sha512-0xy4A/twfrRCnkhfk8ErDi5DqdAsAqeGxht4xkCUrsvhhbQNs7E+4jV0CN7+NKIY0aHE72+XvqtBIXzD31ZbXQ==", + "dev": true + }, + "electron-to-chromium": { + "version": "1.3.113", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.113.tgz", + "integrity": "sha512-De+lPAxEcpxvqPTyZAXELNpRZXABRxf+uL/rSykstQhzj/B0l1150G/ExIIxKc16lI89Hgz81J0BHAcbTqK49g==", + "dev": true + }, + "elliptic": { + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.4.1.tgz", + "integrity": "sha512-BsXLz5sqX8OHcsh7CqBMztyXARmGQ3LWPtGjJi6DiJHq5C/qvi9P3OqgswKSDftbu8+IoI/QDTAm2fFnQ9SZSQ==", + "dev": true, + "requires": { + "bn.js": "^4.4.0", + "brorand": "^1.0.1", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.0" + } + }, + "emojis-list": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz", + "integrity": "sha1-TapNnbAPmBmIDHn6RXrlsJof04k=", + "dev": true + }, + "encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", + "dev": true + }, + "encoding": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.12.tgz", + "integrity": "sha1-U4tm8+5izRq1HsMjgp0flIDHS+s=", + "dev": true, + "requires": { + "iconv-lite": "~0.4.13" + } + }, + "end-of-stream": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", + "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==", + "dev": true, + "requires": { + "once": "^1.4.0" + } + }, + "engine.io": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-3.2.1.tgz", + "integrity": "sha512-+VlKzHzMhaU+GsCIg4AoXF1UdDFjHHwMmMKqMJNDNLlUlejz58FCy4LBqB2YVJskHGYl06BatYWKP2TVdVXE5w==", + "dev": true, + "requires": { + "accepts": "~1.3.4", + "base64id": "1.0.0", + "cookie": "0.3.1", + "debug": "~3.1.0", + "engine.io-parser": "~2.1.0", + "ws": "~3.3.1" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + } + } + }, + "engine.io-client": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-3.2.1.tgz", + "integrity": "sha512-y5AbkytWeM4jQr7m/koQLc5AxpRKC1hEVUb/s1FUAWEJq5AzJJ4NLvzuKPuxtDi5Mq755WuDvZ6Iv2rXj4PTzw==", + "dev": true, + "requires": { + "component-emitter": "1.2.1", + "component-inherit": "0.0.3", + "debug": "~3.1.0", + "engine.io-parser": "~2.1.1", + "has-cors": "1.1.0", + "indexof": "0.0.1", + "parseqs": "0.0.5", + "parseuri": "0.0.5", + "ws": "~3.3.1", + "xmlhttprequest-ssl": "~1.5.4", + "yeast": "0.1.2" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + } + } + }, + "engine.io-parser": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-2.1.3.tgz", + "integrity": "sha512-6HXPre2O4Houl7c4g7Ic/XzPnHBvaEmN90vtRO9uLmwtRqQmTOw0QMevL1TOfL2Cpu1VzsaTmMotQgMdkzGkVA==", + "dev": true, + "requires": { + "after": "0.8.2", + "arraybuffer.slice": "~0.0.7", + "base64-arraybuffer": "0.1.5", + "blob": "0.0.5", + "has-binary2": "~1.0.2" + } + }, + "enhanced-resolve": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.1.0.tgz", + "integrity": "sha512-F/7vkyTtyc/llOIn8oWclcB25KdRaiPBpZYDgJHgh/UHtpgT2p2eldQgtQnLtUvfMKPKxbRaQM/hHkvLHt1Vng==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "memory-fs": "^0.4.0", + "tapable": "^1.0.0" + } + }, + "ent": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/ent/-/ent-2.2.0.tgz", + "integrity": "sha1-6WQhkyWiHQX0RGai9obtbOX13R0=", + "dev": true + }, + "entities": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", + "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==", + "dev": true + }, + "err-code": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/err-code/-/err-code-1.1.2.tgz", + "integrity": "sha1-BuARbTAo9q70gGhJ6w6mp0iuaWA=", + "dev": true + }, + "errno": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.7.tgz", + "integrity": "sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg==", + "dev": true, + "requires": { + "prr": "~1.0.1" + } + }, + "error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "requires": { + "is-arrayish": "^0.2.1" + } + }, + "es-abstract": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.13.0.tgz", + "integrity": "sha512-vDZfg/ykNxQVwup/8E1BZhVzFfBxs9NqMzGcvIJrqg5k2/5Za2bWo40dK2J1pgLngZ7c+Shh8lwYtLGyrwPutg==", + "dev": true, + "requires": { + "es-to-primitive": "^1.2.0", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "is-callable": "^1.1.4", + "is-regex": "^1.0.4", + "object-keys": "^1.0.12" + } + }, + "es-to-primitive": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.0.tgz", + "integrity": "sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg==", + "dev": true, + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, + "es6-promise": { + "version": "4.2.6", + "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.6.tgz", + "integrity": "sha512-aRVgGdnmW2OiySVPUC9e6m+plolMAJKjZnQlCwNSuK5yQ0JN61DZSO1X1Ufd1foqWRAlig0rhduTCHe7sVtK5Q==", + "dev": true + }, + "es6-promisify": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz", + "integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=", + "dev": true, + "requires": { + "es6-promise": "^4.0.3" + } + }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=", + "dev": true + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true + }, + "escodegen": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.8.1.tgz", + "integrity": "sha1-WltTr0aTEQvrsIZ6o0MN07cKEBg=", + "dev": true, + "requires": { + "esprima": "^2.7.1", + "estraverse": "^1.9.1", + "esutils": "^2.0.2", + "optionator": "^0.8.1", + "source-map": "~0.2.0" + }, + "dependencies": { + "source-map": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.2.0.tgz", + "integrity": "sha1-2rc/vPwrqBm03gO9b26qSBZLP50=", + "dev": true, + "optional": true, + "requires": { + "amdefine": ">=0.0.4" + } + } + } + }, + "eslint-scope": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.0.tgz", + "integrity": "sha512-1G6UTDi7Jc1ELFwnR58HV4fK9OQK4S6N985f166xqXxpjU6plxFISJa2Ba9KCQuFa8RCnj/lSFJbHo7UFDBnUA==", + "dev": true, + "requires": { + "esrecurse": "^4.1.0", + "estraverse": "^4.1.1" + }, + "dependencies": { + "estraverse": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz", + "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=", + "dev": true + } + } + }, + "esprima": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz", + "integrity": "sha1-luO3DVd59q1JzQMmc9HDEnZ7pYE=", + "dev": true + }, + "esrecurse": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz", + "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==", + "dev": true, + "requires": { + "estraverse": "^4.1.0" + }, + "dependencies": { + "estraverse": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz", + "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=", + "dev": true + } + } + }, + "estraverse": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-1.9.3.tgz", + "integrity": "sha1-r2fy3JIlgkFZUJJgkaQAXSnJu0Q=", + "dev": true + }, + "esutils": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", + "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", + "dev": true + }, + "etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=", + "dev": true + }, + "eventemitter3": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-3.1.0.tgz", + "integrity": "sha512-ivIvhpq/Y0uSjcHDcOIccjmYjGLcP09MFGE7ysAwkAvkXfpZlC985pH2/ui64DKazbTW/4kN3yqozUxlXzI6cA==", + "dev": true + }, + "events": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.0.0.tgz", + "integrity": "sha512-Dc381HFWJzEOhQ+d8pkNon++bk9h6cdAoAj4iE6Q4y6xgTzySWXlKn05/TVNpjnfRqi/X0EpJEJohPjNI3zpVA==", + "dev": true + }, + "eventsource": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-1.0.7.tgz", + "integrity": "sha512-4Ln17+vVT0k8aWq+t/bF5arcS3EpT9gYtW66EPacdj/mAFevznsnyoHLPy2BA8gbIQeIHoPsvwmfBftfcG//BQ==", + "dev": true, + "requires": { + "original": "^1.0.0" + } + }, + "evp_bytestokey": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", + "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", + "dev": true, + "requires": { + "md5.js": "^1.3.4", + "safe-buffer": "^5.1.1" + } + }, + "execa": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-0.10.0.tgz", + "integrity": "sha512-7XOMnz8Ynx1gGo/3hyV9loYNPWM94jG3+3T3Y8tsfSstFmETmENCMU/A/zj8Lyaj1lkgEepKepvd6240tBRvlw==", + "dev": true, + "requires": { + "cross-spawn": "^6.0.0", + "get-stream": "^3.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + }, + "dependencies": { + "cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "dev": true, + "requires": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + } + } + }, + "exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=", + "dev": true + }, + "expand-braces": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/expand-braces/-/expand-braces-0.1.2.tgz", + "integrity": "sha1-SIsdHSRRyz06axks/AMPRMWFX+o=", + "dev": true, + "requires": { + "array-slice": "^0.2.3", + "array-unique": "^0.2.1", + "braces": "^0.1.2" + }, + "dependencies": { + "array-unique": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz", + "integrity": "sha1-odl8yvy8JiXMcPrc6zalDFiwGlM=", + "dev": true + }, + "braces": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/braces/-/braces-0.1.5.tgz", + "integrity": "sha1-wIVxEIUpHYt1/ddOqw+FlygHEeY=", + "dev": true, + "requires": { + "expand-range": "^0.1.0" + } + }, + "expand-range": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/expand-range/-/expand-range-0.1.1.tgz", + "integrity": "sha1-TLjtoJk8pW+k9B/ELzy7TMrf8EQ=", + "dev": true, + "requires": { + "is-number": "^0.1.1", + "repeat-string": "^0.2.2" + } + }, + "is-number": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-0.1.1.tgz", + "integrity": "sha1-aaevEWlj1HIG7JvZtIoUIW8eOAY=", + "dev": true + }, + "repeat-string": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-0.2.2.tgz", + "integrity": "sha1-x6jTI2BoNiBZp+RlH8aITosftK4=", + "dev": true + } + } + }, + "expand-brackets": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", + "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", + "dev": true, + "requires": { + "debug": "^2.3.3", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "posix-character-classes": "^0.1.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "expand-range": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/expand-range/-/expand-range-1.8.2.tgz", + "integrity": "sha1-opnv/TNf4nIeuujiV+x5ZE/IUzc=", + "dev": true, + "requires": { + "fill-range": "^2.1.0" + }, + "dependencies": { + "fill-range": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-2.2.4.tgz", + "integrity": "sha512-cnrcCbj01+j2gTG921VZPnHbjmdAf8oQV/iGeV2kZxGSyfYjjTyY79ErsK1WJWMpw6DaApEX72binqJE+/d+5Q==", + "dev": true, + "requires": { + "is-number": "^2.1.0", + "isobject": "^2.0.0", + "randomatic": "^3.0.0", + "repeat-element": "^1.1.2", + "repeat-string": "^1.5.2" + } + }, + "is-number": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-2.1.0.tgz", + "integrity": "sha1-Afy7s5NGOlSPL0ZszhbezknbkI8=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + } + }, + "isobject": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", + "dev": true, + "requires": { + "isarray": "1.0.0" + } + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "express": { + "version": "4.16.4", + "resolved": "https://registry.npmjs.org/express/-/express-4.16.4.tgz", + "integrity": "sha512-j12Uuyb4FMrd/qQAm6uCHAkPtO8FDTRJZBDd5D2KOL2eLaz1yUNdUB/NOIyq0iU4q4cFarsUCrnFDPBcnksuOg==", + "dev": true, + "requires": { + "accepts": "~1.3.5", + "array-flatten": "1.1.1", + "body-parser": "1.18.3", + "content-disposition": "0.5.2", + "content-type": "~1.0.4", + "cookie": "0.3.1", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "~1.1.2", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.1.1", + "fresh": "0.5.2", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "~2.3.0", + "parseurl": "~1.3.2", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.4", + "qs": "6.5.2", + "range-parser": "~1.2.0", + "safe-buffer": "5.1.2", + "send": "0.16.2", + "serve-static": "1.13.2", + "setprototypeof": "1.1.0", + "statuses": "~1.4.0", + "type-is": "~1.6.16", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "dependencies": { + "array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=", + "dev": true + } + } + }, + "extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "dev": true + }, + "extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", + "dev": true, + "requires": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "dependencies": { + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dev": true, + "requires": { + "is-plain-object": "^2.0.4" + } + } + } + }, + "extglob": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", + "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", + "dev": true, + "requires": { + "array-unique": "^0.3.2", + "define-property": "^1.0.0", + "expand-brackets": "^2.1.4", + "extend-shallow": "^2.0.1", + "fragment-cache": "^0.2.1", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", + "dev": true + }, + "fast-deep-equal": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", + "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=", + "dev": true + }, + "fast-json-stable-stringify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", + "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=", + "dev": true + }, + "fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", + "dev": true + }, + "fastparse": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/fastparse/-/fastparse-1.1.2.tgz", + "integrity": "sha512-483XLLxTVIwWK3QTrMGRqUfUpoOs/0hbQrl2oz4J0pAcm3A3bu84wxTFqGqkJzewCLdME38xJLJAxBABfQT8sQ==", + "dev": true + }, + "faye-websocket": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.10.0.tgz", + "integrity": "sha1-TkkvjQTftviQA1B/btvy1QHnxvQ=", + "dev": true, + "requires": { + "websocket-driver": ">=0.5.1" + } + }, + "figgy-pudding": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/figgy-pudding/-/figgy-pudding-3.5.1.tgz", + "integrity": "sha512-vNKxJHTEKNThjfrdJwHc7brvM6eVevuO5nTj6ez8ZQ1qbXTvGthucRF7S4vf2cr71QVnT70V34v0S1DyQsti0w==", + "dev": true + }, + "file-loader": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-1.1.11.tgz", + "integrity": "sha512-TGR4HU7HUsGg6GCOPJnFk06RhWgEWFLAGWiT6rcD+GRC2keU3s9RGJ+b3Z6/U73jwwNb2gKLJ7YCrp+jvU4ALg==", + "dev": true, + "requires": { + "loader-utils": "^1.0.2", + "schema-utils": "^0.4.5" + } + }, + "filename-regex": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/filename-regex/-/filename-regex-2.0.1.tgz", + "integrity": "sha1-wcS5vuPglyXdsQa3XB4wH+LxiyY=", + "dev": true + }, + "fileset": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/fileset/-/fileset-2.0.3.tgz", + "integrity": "sha1-jnVIqW08wjJ+5eZ0FocjozO7oqA=", + "dev": true, + "requires": { + "glob": "^7.0.3", + "minimatch": "^3.0.3" + } + }, + "fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "finalhandler": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz", + "integrity": "sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg==", + "dev": true, + "requires": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.2", + "statuses": "~1.4.0", + "unpipe": "~1.0.0" + } + }, + "find-cache-dir": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-1.0.0.tgz", + "integrity": "sha1-kojj6ePMN0hxfTnq3hfPcfww7m8=", + "dev": true, + "requires": { + "commondir": "^1.0.1", + "make-dir": "^1.0.0", + "pkg-dir": "^2.0.0" + } + }, + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "dev": true, + "requires": { + "locate-path": "^2.0.0" + } + }, + "flush-write-stream": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.1.1.tgz", + "integrity": "sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "readable-stream": "^2.3.6" + } + }, + "follow-redirects": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.7.0.tgz", + "integrity": "sha512-m/pZQy4Gj287eNy94nivy5wchN3Kp+Q5WgUPNy5lJSZ3sgkVKSYV/ZChMAQVIgx1SqfZ2zBZtPA2YlXIWxxJOQ==", + "dev": true, + "requires": { + "debug": "^3.2.6" + }, + "dependencies": { + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "dev": true + } + } + }, + "for-in": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", + "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", + "dev": true + }, + "for-own": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/for-own/-/for-own-1.0.0.tgz", + "integrity": "sha1-xjMy9BXO3EsE2/5wz4NklMU8tEs=", + "dev": true, + "requires": { + "for-in": "^1.0.1" + } + }, + "forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", + "dev": true + }, + "form-data": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "dev": true, + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + } + }, + "forwarded": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", + "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=", + "dev": true + }, + "fragment-cache": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", + "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", + "dev": true, + "requires": { + "map-cache": "^0.2.2" + } + }, + "fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=", + "dev": true + }, + "from2": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz", + "integrity": "sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8=", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "readable-stream": "^2.0.0" + } + }, + "fs-access": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/fs-access/-/fs-access-1.0.1.tgz", + "integrity": "sha1-1qh/JiJxzv6+wwxVNAf7mV2od3o=", + "dev": true, + "requires": { + "null-check": "^1.0.0" + } + }, + "fs-minipass": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.5.tgz", + "integrity": "sha512-JhBl0skXjUPCFH7x6x61gQxrKyXsxB5gcgePLZCwfyCGGsTISMoIeObbrvVeP6Xmyaudw4TT43qV2Gz+iyd2oQ==", + "dev": true, + "requires": { + "minipass": "^2.2.1" + } + }, + "fs-write-stream-atomic": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz", + "integrity": "sha1-tH31NJPvkR33VzHnCp3tAYnbQMk=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "iferr": "^0.1.5", + "imurmurhash": "^0.1.4", + "readable-stream": "1 || 2" + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "fsevents": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.7.tgz", + "integrity": "sha512-Pxm6sI2MeBD7RdD12RYsqaP0nMiwx8eZBXCa6z2L+mRHm2DYrOYwihmhjpkdjUHwQhslWQjRpEgNq4XvBmaAuw==", + "dev": true, + "optional": true, + "requires": { + "nan": "^2.9.2", + "node-pre-gyp": "^0.10.0" + }, + "dependencies": { + "abbrev": { + "version": "1.1.1", + "bundled": true, + "dev": true, + "optional": true + }, + "ansi-regex": { + "version": "2.1.1", + "bundled": true, + "dev": true + }, + "aproba": { + "version": "1.2.0", + "bundled": true, + "dev": true, + "optional": true + }, + "are-we-there-yet": { + "version": "1.1.5", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" + } + }, + "balanced-match": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "brace-expansion": { + "version": "1.1.11", + "bundled": true, + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "chownr": { + "version": "1.1.1", + "bundled": true, + "dev": true, + "optional": true + }, + "code-point-at": { + "version": "1.1.0", + "bundled": true, + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "bundled": true, + "dev": true + }, + "console-control-strings": { + "version": "1.1.0", + "bundled": true, + "dev": true + }, + "core-util-is": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "debug": { + "version": "2.6.9", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "ms": "2.0.0" + } + }, + "deep-extend": { + "version": "0.6.0", + "bundled": true, + "dev": true, + "optional": true + }, + "delegates": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "detect-libc": { + "version": "1.0.3", + "bundled": true, + "dev": true, + "optional": true + }, + "fs-minipass": { + "version": "1.2.5", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "minipass": "^2.2.1" + } + }, + "fs.realpath": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "gauge": { + "version": "2.7.4", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "aproba": "^1.0.3", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.0", + "object-assign": "^4.1.0", + "signal-exit": "^3.0.0", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wide-align": "^1.1.0" + } + }, + "glob": { + "version": "7.1.3", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "has-unicode": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "iconv-lite": { + "version": "0.4.24", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "ignore-walk": { + "version": "3.0.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "minimatch": "^3.0.4" + } + }, + "inflight": { + "version": "1.0.6", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.3", + "bundled": true, + "dev": true + }, + "ini": { + "version": "1.3.5", + "bundled": true, + "dev": true, + "optional": true + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "isarray": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "minimatch": { + "version": "3.0.4", + "bundled": true, + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "0.0.8", + "bundled": true, + "dev": true + }, + "minipass": { + "version": "2.3.5", + "bundled": true, + "dev": true, + "requires": { + "safe-buffer": "^5.1.2", + "yallist": "^3.0.0" + } + }, + "minizlib": { + "version": "1.2.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "minipass": "^2.2.1" + } + }, + "mkdirp": { + "version": "0.5.1", + "bundled": true, + "dev": true, + "requires": { + "minimist": "0.0.8" + } + }, + "ms": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "needle": { + "version": "2.2.4", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "debug": "^2.1.2", + "iconv-lite": "^0.4.4", + "sax": "^1.2.4" + } + }, + "node-pre-gyp": { + "version": "0.10.3", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "detect-libc": "^1.0.2", + "mkdirp": "^0.5.1", + "needle": "^2.2.1", + "nopt": "^4.0.1", + "npm-packlist": "^1.1.6", + "npmlog": "^4.0.2", + "rc": "^1.2.7", + "rimraf": "^2.6.1", + "semver": "^5.3.0", + "tar": "^4" + } + }, + "nopt": { + "version": "4.0.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "abbrev": "1", + "osenv": "^0.1.4" + } + }, + "npm-bundled": { + "version": "1.0.5", + "bundled": true, + "dev": true, + "optional": true + }, + "npm-packlist": { + "version": "1.2.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "ignore-walk": "^3.0.1", + "npm-bundled": "^1.0.1" + } + }, + "npmlog": { + "version": "4.1.2", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "are-we-there-yet": "~1.1.2", + "console-control-strings": "~1.1.0", + "gauge": "~2.7.3", + "set-blocking": "~2.0.0" + } + }, + "number-is-nan": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "object-assign": { + "version": "4.1.1", + "bundled": true, + "dev": true, + "optional": true + }, + "once": { + "version": "1.4.0", + "bundled": true, + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "os-homedir": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "os-tmpdir": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "osenv": { + "version": "0.1.5", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "os-homedir": "^1.0.0", + "os-tmpdir": "^1.0.0" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "process-nextick-args": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "rc": { + "version": "1.2.8", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "bundled": true, + "dev": true, + "optional": true + } + } + }, + "readable-stream": { + "version": "2.3.6", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "rimraf": { + "version": "2.6.3", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "glob": "^7.1.3" + } + }, + "safe-buffer": { + "version": "5.1.2", + "bundled": true, + "dev": true + }, + "safer-buffer": { + "version": "2.1.2", + "bundled": true, + "dev": true, + "optional": true + }, + "sax": { + "version": "1.2.4", + "bundled": true, + "dev": true, + "optional": true + }, + "semver": { + "version": "5.6.0", + "bundled": true, + "dev": true, + "optional": true + }, + "set-blocking": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "signal-exit": { + "version": "3.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "string-width": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "string_decoder": { + "version": "1.1.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "bundled": true, + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "strip-json-comments": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "tar": { + "version": "4.4.8", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "chownr": "^1.1.1", + "fs-minipass": "^1.2.5", + "minipass": "^2.3.4", + "minizlib": "^1.1.1", + "mkdirp": "^0.5.0", + "safe-buffer": "^5.1.2", + "yallist": "^3.0.2" + } + }, + "util-deprecate": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "wide-align": { + "version": "1.1.3", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "string-width": "^1.0.2 || 2" + } + }, + "wrappy": { + "version": "1.0.2", + "bundled": true, + "dev": true + }, + "yallist": { + "version": "3.0.3", + "bundled": true, + "dev": true + } + } + }, + "fstream": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.11.tgz", + "integrity": "sha1-XB+x8RdHcRTwYyoOtLcbPLD9MXE=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "inherits": "~2.0.0", + "mkdirp": ">=0.5 0", + "rimraf": "2" + } + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "gauge": { + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", + "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", + "dev": true, + "requires": { + "aproba": "^1.0.3", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.0", + "object-assign": "^4.1.0", + "signal-exit": "^3.0.0", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wide-align": "^1.1.0" + } + }, + "gaze": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/gaze/-/gaze-1.1.3.tgz", + "integrity": "sha512-BRdNm8hbWzFzWHERTrejLqwHDfS4GibPoq5wjTPIoJHoBtKGPg3xAFfxmM+9ztbXelxcf2hwQcaz1PtmFeue8g==", + "dev": true, + "optional": true, + "requires": { + "globule": "^1.0.0" + } + }, + "genfun": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/genfun/-/genfun-5.0.0.tgz", + "integrity": "sha512-KGDOARWVga7+rnB3z9Sd2Letx515owfk0hSxHGuqjANb1M+x2bGZGqHLiozPsYMdM2OubeMni/Hpwmjq6qIUhA==", + "dev": true + }, + "get-caller-file": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", + "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==", + "dev": true + }, + "get-stdin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", + "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=", + "dev": true + }, + "get-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", + "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", + "dev": true + }, + "get-value": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", + "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", + "dev": true + }, + "getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "dev": true, + "requires": { + "assert-plus": "^1.0.0" + } + }, + "glob": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", + "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "glob-base": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/glob-base/-/glob-base-0.3.0.tgz", + "integrity": "sha1-27Fk9iIbHAscz4Kuoyi0l98Oo8Q=", + "dev": true, + "requires": { + "glob-parent": "^2.0.0", + "is-glob": "^2.0.0" + }, + "dependencies": { + "glob-parent": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz", + "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=", + "dev": true, + "requires": { + "is-glob": "^2.0.0" + } + }, + "is-extglob": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", + "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", + "dev": true + }, + "is-glob": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", + "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", + "dev": true, + "requires": { + "is-extglob": "^1.0.0" + } + } + } + }, + "glob-parent": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", + "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", + "dev": true, + "requires": { + "is-glob": "^3.1.0", + "path-dirname": "^1.0.0" + }, + "dependencies": { + "is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "dev": true, + "requires": { + "is-extglob": "^2.1.0" + } + } + } + }, + "globals": { + "version": "9.18.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz", + "integrity": "sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==", + "dev": true + }, + "globby": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/globby/-/globby-7.1.1.tgz", + "integrity": "sha1-+yzP+UAfhgCUXfral0QMypcrhoA=", + "dev": true, + "requires": { + "array-union": "^1.0.1", + "dir-glob": "^2.0.0", + "glob": "^7.1.2", + "ignore": "^3.3.5", + "pify": "^3.0.0", + "slash": "^1.0.0" + } + }, + "globule": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/globule/-/globule-1.2.1.tgz", + "integrity": "sha512-g7QtgWF4uYSL5/dn71WxubOrS7JVGCnFPEnoeChJmBnyR9Mw8nGoEwOgJL/RC2Te0WhbsEUCejfH8SZNJ+adYQ==", + "dev": true, + "optional": true, + "requires": { + "glob": "~7.1.1", + "lodash": "~4.17.10", + "minimatch": "~3.0.2" + } + }, + "graceful-fs": { + "version": "4.1.15", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz", + "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==", + "dev": true + }, + "handle-thing": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.0.tgz", + "integrity": "sha512-d4sze1JNC454Wdo2fkuyzCr6aHcbL6PGGuFAz0Li/NcOm1tCHGnWDRmJP85dh9IhQErTc2svWFEX5xHIOo//kQ==", + "dev": true + }, + "handlebars": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.1.0.tgz", + "integrity": "sha512-l2jRuU1NAWK6AW5qqcTATWQJvNPEwkM7NEKSiv/gqOsoSQbVoWyqVEY5GS+XPQ88zLNmqASRpzfdm8d79hJS+w==", + "dev": true, + "requires": { + "async": "^2.5.0", + "optimist": "^0.6.1", + "source-map": "^0.6.1", + "uglify-js": "^3.1.4" + }, + "dependencies": { + "async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.2.tgz", + "integrity": "sha512-H1qVYh1MYhEEFLsP97cVKqCGo7KfCyTt6uEWqsTBr9SO84oK9Uwbyd/yCW+6rKJLHksBNUVWZDAjfS+Ccx0Bbg==", + "dev": true, + "requires": { + "lodash": "^4.17.11" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", + "dev": true + }, + "har-validator": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz", + "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==", + "dev": true, + "requires": { + "ajv": "^6.5.5", + "har-schema": "^2.0.0" + }, + "dependencies": { + "ajv": { + "version": "6.9.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.9.1.tgz", + "integrity": "sha512-XDN92U311aINL77ieWHmqCcNlwjoP5cHXDxIxbf2MaPYuCXOHS7gHH8jktxeK5omgd52XbSTX6a4Piwd1pQmzA==", + "dev": true, + "requires": { + "fast-deep-equal": "^2.0.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "fast-deep-equal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", + "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=", + "dev": true + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "uri-js": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", + "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", + "dev": true, + "requires": { + "punycode": "^2.1.0" + } + } + } + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-ansi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "has-binary2": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-binary2/-/has-binary2-1.0.3.tgz", + "integrity": "sha512-G1LWKhDSvhGeAQ8mPVQlqNcOB2sJdwATtZKl2pDKKHfpf/rYj24lkinxf69blJbnsvtqqNU+L3SL50vzZhXOnw==", + "dev": true, + "requires": { + "isarray": "2.0.1" + }, + "dependencies": { + "isarray": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz", + "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4=", + "dev": true + } + } + }, + "has-cors": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-cors/-/has-cors-1.1.0.tgz", + "integrity": "sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk=", + "dev": true + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "has-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.0.tgz", + "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=", + "dev": true + }, + "has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", + "dev": true + }, + "has-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", + "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", + "dev": true, + "requires": { + "get-value": "^2.0.6", + "has-values": "^1.0.0", + "isobject": "^3.0.0" + } + }, + "has-values": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", + "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", + "dev": true, + "requires": { + "is-number": "^3.0.0", + "kind-of": "^4.0.0" + }, + "dependencies": { + "kind-of": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", + "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "hash-base": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.4.tgz", + "integrity": "sha1-X8hoaEfs1zSZQDMZprCj8/auSRg=", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "hash.js": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", + "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, + "he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true + }, + "hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", + "dev": true, + "requires": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "hosted-git-info": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.7.1.tgz", + "integrity": "sha512-7T/BxH19zbcCTa8XkMlbK5lTo1WtgkFi3GvdWEyNuc4Vex7/9Dqbnpsf4JMydcfj9HCg4zUWFTL3Za6lapg5/w==", + "dev": true + }, + "hpack.js": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", + "integrity": "sha1-h3dMCUnlE/QuhFdbPEVoH63ioLI=", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "obuf": "^1.0.0", + "readable-stream": "^2.0.1", + "wbuf": "^1.1.0" + } + }, + "html-entities": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-1.2.1.tgz", + "integrity": "sha1-DfKTUfByEWNRXfueVUPl9u7VFi8=", + "dev": true + }, + "html-minifier": { + "version": "3.5.21", + "resolved": "https://registry.npmjs.org/html-minifier/-/html-minifier-3.5.21.tgz", + "integrity": "sha512-LKUKwuJDhxNa3uf/LPR/KVjm/l3rBqtYeCOAekvG8F1vItxMUpueGd94i/asDDr8/1u7InxzFA5EeGjhhG5mMA==", + "dev": true, + "requires": { + "camel-case": "3.0.x", + "clean-css": "4.2.x", + "commander": "2.17.x", + "he": "1.2.x", + "param-case": "2.1.x", + "relateurl": "0.2.x", + "uglify-js": "3.4.x" + } + }, + "html-webpack-plugin": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-3.2.0.tgz", + "integrity": "sha1-sBq71yOsqqeze2r0SS69oD2d03s=", + "dev": true, + "requires": { + "html-minifier": "^3.2.3", + "loader-utils": "^0.2.16", + "lodash": "^4.17.3", + "pretty-error": "^2.0.2", + "tapable": "^1.0.0", + "toposort": "^1.0.0", + "util.promisify": "1.0.0" + }, + "dependencies": { + "loader-utils": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-0.2.17.tgz", + "integrity": "sha1-+G5jdNQyBabmxg6RlvF8Apm/s0g=", + "dev": true, + "requires": { + "big.js": "^3.1.3", + "emojis-list": "^2.0.0", + "json5": "^0.5.0", + "object-assign": "^4.0.1" + } + } + } + }, + "htmlparser2": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.3.0.tgz", + "integrity": "sha1-zHDQWln2VC5D8OaFyYLhTJJKnv4=", + "dev": true, + "requires": { + "domelementtype": "1", + "domhandler": "2.1", + "domutils": "1.1", + "readable-stream": "1.0" + }, + "dependencies": { + "domutils": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.1.6.tgz", + "integrity": "sha1-vdw94Jm5ou+sxRxiPyj0FuzFdIU=", + "dev": true, + "requires": { + "domelementtype": "1" + } + }, + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", + "dev": true + }, + "readable-stream": { + "version": "1.0.34", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", + "dev": true + } + } + }, + "http-cache-semantics": { + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-3.8.1.tgz", + "integrity": "sha512-5ai2iksyV8ZXmnZhHH4rWPoxxistEexSi5936zIQ1bnNTW5VnA85B6P/VpXiRM017IgRvb2kKo1a//y+0wSp3w==", + "dev": true + }, + "http-deceiver": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", + "integrity": "sha1-+nFolEq5pRnTN8sL7HKE3D5yPYc=", + "dev": true + }, + "http-errors": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", + "dev": true, + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.0", + "statuses": ">= 1.4.0 < 2" + } + }, + "http-parser-js": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.0.tgz", + "integrity": "sha512-cZdEF7r4gfRIq7ezX9J0T+kQmJNOub71dWbgAXVHDct80TKP4MCETtZQ31xyv38UwgzkWPYF/Xc0ge55dW9Z9w==", + "dev": true + }, + "http-proxy": { + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.17.0.tgz", + "integrity": "sha512-Taqn+3nNvYRfJ3bGvKfBSRwy1v6eePlm3oc/aWVxZp57DQr5Eq3xhKJi7Z4hZpS8PC3H4qI+Yly5EmFacGuA/g==", + "dev": true, + "requires": { + "eventemitter3": "^3.0.0", + "follow-redirects": "^1.0.0", + "requires-port": "^1.0.0" + } + }, + "http-proxy-agent": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-2.1.0.tgz", + "integrity": "sha512-qwHbBLV7WviBl0rQsOzH6o5lwyOIvwp/BdFnvVxXORldu5TmjFfjzBcWUWS5kWAZhmv+JtiDhSuQCp4sBfbIgg==", + "dev": true, + "requires": { + "agent-base": "4", + "debug": "3.1.0" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + } + } + }, + "http-proxy-middleware": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-0.18.0.tgz", + "integrity": "sha512-Fs25KVMPAIIcgjMZkVHJoKg9VcXcC1C8yb9JUgeDvVXY0S/zgVIhMb+qVswDIgtJe2DfckMSY2d6TuTEutlk6Q==", + "dev": true, + "requires": { + "http-proxy": "^1.16.2", + "is-glob": "^4.0.0", + "lodash": "^4.17.5", + "micromatch": "^3.1.9" + } + }, + "http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", + "dev": true, + "requires": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + } + }, + "https-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", + "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=", + "dev": true + }, + "https-proxy-agent": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.1.tgz", + "integrity": "sha512-HPCTS1LW51bcyMYbxUIOO4HEOlQ1/1qRaFWcyxvwaqUS9TY88aoEuHUY33kuAh1YhVVaDQhLZsnPd+XNARWZlQ==", + "dev": true, + "requires": { + "agent-base": "^4.1.0", + "debug": "^3.1.0" + }, + "dependencies": { + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "dev": true + } + } + }, + "humanize-ms": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", + "integrity": "sha1-xG4xWaKT9riW2ikxbYtv6Lt5u+0=", + "dev": true, + "requires": { + "ms": "^2.0.0" + } + }, + "iconv-lite": { + "version": "0.4.23", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz", + "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==", + "dev": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "ieee754": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.12.tgz", + "integrity": "sha512-GguP+DRY+pJ3soyIiGPTvdiVXjZ+DbXOxGpXn3eMvNW4x4irjqXm4wHKscC+TfxSJ0yw/S1F24tqdMNsMZTiLA==", + "dev": true + }, + "iferr": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/iferr/-/iferr-0.1.5.tgz", + "integrity": "sha1-xg7taebY/bazEEofy8ocGS3FtQE=", + "dev": true + }, + "ignore": { + "version": "3.3.10", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.10.tgz", + "integrity": "sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug==", + "dev": true + }, + "ignore-walk": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.1.tgz", + "integrity": "sha512-DTVlMx3IYPe0/JJcYP7Gxg7ttZZu3IInhuEhbchuqneY9wWe5Ojy2mXLBaQFUQmo0AW2r3qG7m1mg86js+gnlQ==", + "dev": true, + "requires": { + "minimatch": "^3.0.4" + } + }, + "image-size": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/image-size/-/image-size-0.5.5.tgz", + "integrity": "sha1-Cd/Uq50g4p6xw+gLiZA3jfnjy5w=", + "dev": true, + "optional": true + }, + "immediate": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", + "integrity": "sha1-nbHb0Pr43m++D13V5Wu2BigN5ps=", + "dev": true + }, + "import-cwd": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/import-cwd/-/import-cwd-2.1.0.tgz", + "integrity": "sha1-qmzzbnInYShcs3HsZRn1PiQ1sKk=", + "dev": true, + "requires": { + "import-from": "^2.1.0" + } + }, + "import-from": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/import-from/-/import-from-2.1.0.tgz", + "integrity": "sha1-M1238qev/VOqpHHUuAId7ja387E=", + "dev": true, + "requires": { + "resolve-from": "^3.0.0" + } + }, + "import-local": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-2.0.0.tgz", + "integrity": "sha512-b6s04m3O+s3CGSbqDIyP4R6aAwAeYlVq9+WUWep6iHa8ETRf9yei1U48C5MmfJmV9AiLYYBKPMq/W+/WRpQmCQ==", + "dev": true, + "requires": { + "pkg-dir": "^3.0.0", + "resolve-cwd": "^2.0.0" + }, + "dependencies": { + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "requires": { + "locate-path": "^3.0.0" + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "p-limit": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.1.0.tgz", + "integrity": "sha512-NhURkNcrVB+8hNfLuysU8enY5xn2KXphsHBaC2YmRNTZRc7RWusw6apSpdEj3jo4CMb6W9nrF6tTnsJsJeyu6g==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "requires": { + "p-limit": "^2.0.0" + } + }, + "p-try": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.0.0.tgz", + "integrity": "sha512-hMp0onDKIajHfIkdRk3P4CdCmErkYAxxDtP3Wx/4nZ3aGlau2VKh3mZpcuFkH27WQkL/3WBCPOktzA9ZOAnMQQ==", + "dev": true + }, + "pkg-dir": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", + "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", + "dev": true, + "requires": { + "find-up": "^3.0.0" + } + } + } + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "dev": true + }, + "in-publish": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/in-publish/-/in-publish-2.0.0.tgz", + "integrity": "sha1-4g/146KvwmkDILbcVSaCqcf631E=", + "dev": true, + "optional": true + }, + "indent-string": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz", + "integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=", + "dev": true, + "optional": true, + "requires": { + "repeating": "^2.0.0" + } + }, + "indexof": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz", + "integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10=", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true + }, + "ini": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", + "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", + "dev": true + }, + "internal-ip": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/internal-ip/-/internal-ip-3.0.1.tgz", + "integrity": "sha512-NXXgESC2nNVtU+pqmC9e6R8B1GpKxzsAQhffvh5AL79qKnodd+L7tnEQmTiUAVngqLalPbSqRA7XGIEL5nCd0Q==", + "dev": true, + "requires": { + "default-gateway": "^2.6.0", + "ipaddr.js": "^1.5.2" + } + }, + "invariant": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", + "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", + "dev": true, + "requires": { + "loose-envify": "^1.0.0" + } + }, + "invert-kv": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", + "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=", + "dev": true, + "optional": true + }, + "ip": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz", + "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=", + "dev": true + }, + "ip-regex": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz", + "integrity": "sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk=", + "dev": true + }, + "ipaddr.js": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.8.0.tgz", + "integrity": "sha1-6qM9bd16zo9/b+DJygRA5wZzix4=", + "dev": true + }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", + "dev": true + }, + "is-binary-path": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", + "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", + "dev": true, + "requires": { + "binary-extensions": "^1.0.0" + } + }, + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true + }, + "is-callable": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.4.tgz", + "integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==", + "dev": true + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-date-object": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz", + "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=", + "dev": true + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + }, + "dependencies": { + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true + } + } + }, + "is-directory": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/is-directory/-/is-directory-0.3.1.tgz", + "integrity": "sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE=", + "dev": true + }, + "is-dotfile": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-dotfile/-/is-dotfile-1.0.3.tgz", + "integrity": "sha1-pqLzL/0t+wT1yiXs0Pa4PPeYoeE=", + "dev": true + }, + "is-equal-shallow": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz", + "integrity": "sha1-IjgJj8Ih3gvPpdnqxMRdY4qhxTQ=", + "dev": true, + "requires": { + "is-primitive": "^2.0.0" + } + }, + "is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "dev": true + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "dev": true + }, + "is-finite": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.2.tgz", + "integrity": "sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko=", + "dev": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "dev": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "is-glob": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.0.tgz", + "integrity": "sha1-lSHHaEXMJhCoUgPd8ICpWML/q8A=", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-path-cwd": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-1.0.0.tgz", + "integrity": "sha1-0iXsIxMuie3Tj9p2dHLmLmXxEG0=", + "dev": true + }, + "is-path-in-cwd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-1.0.1.tgz", + "integrity": "sha512-FjV1RTW48E7CWM7eE/J2NJvAEEVektecDBVBE5Hh3nM1Jd0kvhHtX68Pr3xsDf857xt3Y4AkwVULK1Vku62aaQ==", + "dev": true, + "requires": { + "is-path-inside": "^1.0.0" + } + }, + "is-path-inside": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.1.tgz", + "integrity": "sha1-jvW33lBDej/cprToZe96pVy0gDY=", + "dev": true, + "requires": { + "path-is-inside": "^1.0.1" + } + }, + "is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dev": true, + "requires": { + "isobject": "^3.0.1" + } + }, + "is-posix-bracket": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz", + "integrity": "sha1-MzTceXdDaOkvAW5vvAqI9c1ua8Q=", + "dev": true + }, + "is-primitive": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-primitive/-/is-primitive-2.0.0.tgz", + "integrity": "sha1-IHurkWOEmcB7Kt8kCkGochADRXU=", + "dev": true + }, + "is-regex": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz", + "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=", + "dev": true, + "requires": { + "has": "^1.0.1" + } + }, + "is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", + "dev": true + }, + "is-symbol": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.2.tgz", + "integrity": "sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw==", + "dev": true, + "requires": { + "has-symbols": "^1.0.0" + } + }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", + "dev": true + }, + "is-utf8": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", + "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=", + "dev": true + }, + "is-windows": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", + "dev": true + }, + "is-wsl": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", + "integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=", + "dev": true + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "isbinaryfile": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-3.0.3.tgz", + "integrity": "sha512-8cJBL5tTd2OS0dM4jz07wQd5g0dCCqIhUxPIGtZfa5L6hWlvV5MHTITy/DBAsF+Oe2LS1X3krBUhNwaGUWpWxw==", + "dev": true, + "requires": { + "buffer-alloc": "^1.2.0" + } + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true + }, + "isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", + "dev": true + }, + "istanbul": { + "version": "0.4.5", + "resolved": "https://registry.npmjs.org/istanbul/-/istanbul-0.4.5.tgz", + "integrity": "sha1-ZcfXPUxNqE1POsMQuRj7C4Azczs=", + "dev": true, + "requires": { + "abbrev": "1.0.x", + "async": "1.x", + "escodegen": "1.8.x", + "esprima": "2.7.x", + "glob": "^5.0.15", + "handlebars": "^4.0.1", + "js-yaml": "3.x", + "mkdirp": "0.5.x", + "nopt": "3.x", + "once": "1.x", + "resolve": "1.1.x", + "supports-color": "^3.1.0", + "which": "^1.1.1", + "wordwrap": "^1.0.0" + }, + "dependencies": { + "glob": { + "version": "5.0.15", + "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz", + "integrity": "sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E=", + "dev": true, + "requires": { + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "2 || 3", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", + "dev": true + }, + "supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "dev": true, + "requires": { + "has-flag": "^1.0.0" + } + } + } + }, + "istanbul-api": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/istanbul-api/-/istanbul-api-2.1.1.tgz", + "integrity": "sha512-kVmYrehiwyeBAk/wE71tW6emzLiHGjYIiDrc8sfyty4F8M02/lrgXSm+R1kXysmF20zArvmZXjlE/mg24TVPJw==", + "dev": true, + "requires": { + "async": "^2.6.1", + "compare-versions": "^3.2.1", + "fileset": "^2.0.3", + "istanbul-lib-coverage": "^2.0.3", + "istanbul-lib-hook": "^2.0.3", + "istanbul-lib-instrument": "^3.1.0", + "istanbul-lib-report": "^2.0.4", + "istanbul-lib-source-maps": "^3.0.2", + "istanbul-reports": "^2.1.1", + "js-yaml": "^3.12.0", + "make-dir": "^1.3.0", + "minimatch": "^3.0.4", + "once": "^1.4.0" + }, + "dependencies": { + "async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.2.tgz", + "integrity": "sha512-H1qVYh1MYhEEFLsP97cVKqCGo7KfCyTt6uEWqsTBr9SO84oK9Uwbyd/yCW+6rKJLHksBNUVWZDAjfS+Ccx0Bbg==", + "dev": true, + "requires": { + "lodash": "^4.17.11" + } + }, + "istanbul-lib-coverage": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz", + "integrity": "sha512-dKWuzRGCs4G+67VfW9pBFFz2Jpi4vSp/k7zBcJ888ofV5Mi1g5CUML5GvMvV6u9Cjybftu+E8Cgp+k0dI1E5lw==", + "dev": true + }, + "istanbul-lib-instrument": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-3.1.0.tgz", + "integrity": "sha512-ooVllVGT38HIk8MxDj/OIHXSYvH+1tq/Vb38s8ixt9GoJadXska4WkGY+0wkmtYCZNYtaARniH/DixUGGLZ0uA==", + "dev": true, + "requires": { + "@babel/generator": "^7.0.0", + "@babel/parser": "^7.0.0", + "@babel/template": "^7.0.0", + "@babel/traverse": "^7.0.0", + "@babel/types": "^7.0.0", + "istanbul-lib-coverage": "^2.0.3", + "semver": "^5.5.0" + } + } + } + }, + "istanbul-instrumenter-loader": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-instrumenter-loader/-/istanbul-instrumenter-loader-3.0.1.tgz", + "integrity": "sha512-a5SPObZgS0jB/ixaKSMdn6n/gXSrK2S6q/UfRJBT3e6gQmVjwZROTODQsYW5ZNwOu78hG62Y3fWlebaVOL0C+w==", + "dev": true, + "requires": { + "convert-source-map": "^1.5.0", + "istanbul-lib-instrument": "^1.7.3", + "loader-utils": "^1.1.0", + "schema-utils": "^0.3.0" + }, + "dependencies": { + "ajv": { + "version": "5.5.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", + "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=", + "dev": true, + "requires": { + "co": "^4.6.0", + "fast-deep-equal": "^1.0.0", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.3.0" + } + }, + "schema-utils": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-0.3.0.tgz", + "integrity": "sha1-9YdyIs4+kx7a4DnxfrNxbnE3+M8=", + "dev": true, + "requires": { + "ajv": "^5.0.0" + } + } + } + }, + "istanbul-lib-coverage": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-1.2.1.tgz", + "integrity": "sha512-PzITeunAgyGbtY1ibVIUiV679EFChHjoMNRibEIobvmrCRaIgwLxNucOSimtNWUhEib/oO7QY2imD75JVgCJWQ==", + "dev": true + }, + "istanbul-lib-hook": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-2.0.3.tgz", + "integrity": "sha512-CLmEqwEhuCYtGcpNVJjLV1DQyVnIqavMLFHV/DP+np/g3qvdxu3gsPqYoJMXm15sN84xOlckFB3VNvRbf5yEgA==", + "dev": true, + "requires": { + "append-transform": "^1.0.0" + } + }, + "istanbul-lib-instrument": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-1.10.2.tgz", + "integrity": "sha512-aWHxfxDqvh/ZlxR8BBaEPVSWDPUkGD63VjGQn3jcw8jCp7sHEMKcrj4xfJn/ABzdMEHiQNyvDQhqm5o8+SQg7A==", + "dev": true, + "requires": { + "babel-generator": "^6.18.0", + "babel-template": "^6.16.0", + "babel-traverse": "^6.18.0", + "babel-types": "^6.18.0", + "babylon": "^6.18.0", + "istanbul-lib-coverage": "^1.2.1", + "semver": "^5.3.0" + } + }, + "istanbul-lib-report": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-2.0.4.tgz", + "integrity": "sha512-sOiLZLAWpA0+3b5w5/dq0cjm2rrNdAfHWaGhmn7XEFW6X++IV9Ohn+pnELAl9K3rfpaeBfbmH9JU5sejacdLeA==", + "dev": true, + "requires": { + "istanbul-lib-coverage": "^2.0.3", + "make-dir": "^1.3.0", + "supports-color": "^6.0.0" + }, + "dependencies": { + "istanbul-lib-coverage": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz", + "integrity": "sha512-dKWuzRGCs4G+67VfW9pBFFz2Jpi4vSp/k7zBcJ888ofV5Mi1g5CUML5GvMvV6u9Cjybftu+E8Cgp+k0dI1E5lw==", + "dev": true + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "istanbul-lib-source-maps": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-3.0.2.tgz", + "integrity": "sha512-JX4v0CiKTGp9fZPmoxpu9YEkPbEqCqBbO3403VabKjH+NRXo72HafD5UgnjTEqHL2SAjaZK1XDuDOkn6I5QVfQ==", + "dev": true, + "requires": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^2.0.3", + "make-dir": "^1.3.0", + "rimraf": "^2.6.2", + "source-map": "^0.6.1" + }, + "dependencies": { + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "istanbul-lib-coverage": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz", + "integrity": "sha512-dKWuzRGCs4G+67VfW9pBFFz2Jpi4vSp/k7zBcJ888ofV5Mi1g5CUML5GvMvV6u9Cjybftu+E8Cgp+k0dI1E5lw==", + "dev": true + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "istanbul-reports": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-2.1.1.tgz", + "integrity": "sha512-FzNahnidyEPBCI0HcufJoSEoKykesRlFcSzQqjH9x0+LC8tnnE/p/90PBLu8iZTxr8yYZNyTtiAujUqyN+CIxw==", + "dev": true, + "requires": { + "handlebars": "^4.1.0" + } + }, + "jasmine": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/jasmine/-/jasmine-2.8.0.tgz", + "integrity": "sha1-awicChFXax8W3xG4AUbZHU6Lij4=", + "dev": true, + "requires": { + "exit": "^0.1.2", + "glob": "^7.0.6", + "jasmine-core": "~2.8.0" + }, + "dependencies": { + "jasmine-core": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-2.8.0.tgz", + "integrity": "sha1-vMl5rh+f0FcB5F5S5l06XWPxok4=", + "dev": true + } + } + }, + "jasmine-core": { + "version": "2.99.1", + "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-2.99.1.tgz", + "integrity": "sha1-5kAN8ea1bhMLYcS80JPap/boyhU=", + "dev": true + }, + "jasmine-diff": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/jasmine-diff/-/jasmine-diff-0.1.3.tgz", + "integrity": "sha1-k8zC3MQQKMXd1GBlWAdIOfLe6qg=", + "dev": true, + "requires": { + "diff": "^3.2.0" + } + }, + "jasmine-spec-reporter": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/jasmine-spec-reporter/-/jasmine-spec-reporter-4.2.1.tgz", + "integrity": "sha512-FZBoZu7VE5nR7Nilzy+Np8KuVIOxF4oXDPDknehCYBDE080EnlPu0afdZNmpGDBRCUBv3mj5qgqCRmk6W/K8vg==", + "dev": true, + "requires": { + "colors": "1.1.2" + } + }, + "jasminewd2": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/jasminewd2/-/jasminewd2-2.2.0.tgz", + "integrity": "sha1-43zwsX8ZnM4jvqcbIDk5Uka07E4=", + "dev": true + }, + "js-base64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.5.1.tgz", + "integrity": "sha512-M7kLczedRMYX4L8Mdh4MzyAMM9O5osx+4FcOQuTvr3A9F2D9S5JXheN0ewNbrvK2UatkTRhL5ejGmGSjNMiZuw==", + "dev": true, + "optional": true + }, + "js-tokens": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", + "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=", + "dev": true + }, + "js-yaml": { + "version": "3.12.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.12.1.tgz", + "integrity": "sha512-um46hB9wNOKlwkHgiuyEVAybXBjwFUV0Z/RaHJblRd9DXltue9FTYvzCr9ErQrK9Adz5MU4gHWVaNUfdmrC8qA==", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "dependencies": { + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true + } + } + }, + "jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", + "dev": true + }, + "jsesc": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-1.3.0.tgz", + "integrity": "sha1-RsP+yMGJKxKwgz25vHYiF226s0s=", + "dev": true + }, + "json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", + "dev": true + }, + "json-schema": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", + "dev": true + }, + "json-schema-traverse": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", + "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=", + "dev": true + }, + "json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", + "dev": true + }, + "json3": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/json3/-/json3-3.3.2.tgz", + "integrity": "sha1-PAQ0dD35Pi9cQq7nsZvLSDV19OE=", + "dev": true + }, + "json5": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", + "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=", + "dev": true + }, + "jsonparse": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", + "integrity": "sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA=", + "dev": true + }, + "jsprim": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", + "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", + "dev": true, + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.2.3", + "verror": "1.10.0" + } + }, + "jszip": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.1.5.tgz", + "integrity": "sha512-5W8NUaFRFRqTOL7ZDDrx5qWHJyBXy6velVudIzQUSoqAAYqzSh2Z7/m0Rf1QbmQJccegD0r+YZxBjzqoBiEeJQ==", + "dev": true, + "requires": { + "core-js": "~2.3.0", + "es6-promise": "~3.0.2", + "lie": "~3.1.0", + "pako": "~1.0.2", + "readable-stream": "~2.0.6" + }, + "dependencies": { + "core-js": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.3.0.tgz", + "integrity": "sha1-+rg/uwstjchfpjbEudNMdUIMbWU=", + "dev": true + }, + "es6-promise": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.0.2.tgz", + "integrity": "sha1-AQ1YWEI6XxGJeWZfRkhqlcbuK7Y=", + "dev": true + }, + "process-nextick-args": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", + "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=", + "dev": true + }, + "readable-stream": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.6.tgz", + "integrity": "sha1-j5A0HmilPMySh4jaz80Rs265t44=", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "~1.0.0", + "process-nextick-args": "~1.0.6", + "string_decoder": "~0.10.x", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", + "dev": true + } + } + }, + "karma": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/karma/-/karma-3.0.0.tgz", + "integrity": "sha512-ZTjyuDXVXhXsvJ1E4CnZzbCjSxD6sEdzEsFYogLuZM0yqvg/mgz+O+R1jb0J7uAQeuzdY8kJgx6hSNXLwFuHIQ==", + "dev": true, + "requires": { + "bluebird": "^3.3.0", + "body-parser": "^1.16.1", + "chokidar": "^2.0.3", + "colors": "^1.1.0", + "combine-lists": "^1.0.0", + "connect": "^3.6.0", + "core-js": "^2.2.0", + "di": "^0.0.1", + "dom-serialize": "^2.2.0", + "expand-braces": "^0.1.1", + "glob": "^7.1.1", + "graceful-fs": "^4.1.2", + "http-proxy": "^1.13.0", + "isbinaryfile": "^3.0.0", + "lodash": "^4.17.4", + "log4js": "^3.0.0", + "mime": "^2.3.1", + "minimatch": "^3.0.2", + "optimist": "^0.6.1", + "qjobs": "^1.1.4", + "range-parser": "^1.2.0", + "rimraf": "^2.6.0", + "safe-buffer": "^5.0.1", + "socket.io": "2.1.1", + "source-map": "^0.6.1", + "tmp": "0.0.33", + "useragent": "2.2.1" + }, + "dependencies": { + "mime": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.0.tgz", + "integrity": "sha512-ikBcWwyqXQSHKtciCcctu9YfPbFYZ4+gbHEmE0Q8jzcTYQg5dHCr3g2wwAZjPoJfQVXZq6KXAjpXOTf5/cjT7w==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "karma-chrome-launcher": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/karma-chrome-launcher/-/karma-chrome-launcher-2.2.0.tgz", + "integrity": "sha512-uf/ZVpAabDBPvdPdveyk1EPgbnloPvFFGgmRhYLTDH7gEB4nZdSBk8yTU47w1g/drLSx5uMOkjKk7IWKfWg/+w==", + "dev": true, + "requires": { + "fs-access": "^1.0.0", + "which": "^1.2.1" + } + }, + "karma-coverage-istanbul-reporter": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/karma-coverage-istanbul-reporter/-/karma-coverage-istanbul-reporter-2.0.5.tgz", + "integrity": "sha512-yPvAlKtY3y+rKKWbOo0CzBMVTvJEeMOgbMXuVv3yWvS8YtYKC98AU9vFF0mVBZ2RP1E9SgS90+PT6Kf14P3S4w==", + "dev": true, + "requires": { + "istanbul-api": "^2.1.1", + "minimatch": "^3.0.4" + } + }, + "karma-jasmine": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/karma-jasmine/-/karma-jasmine-1.1.2.tgz", + "integrity": "sha1-OU8rJf+0pkS5rabyLUQ+L9CIhsM=", + "dev": true + }, + "karma-jasmine-html-reporter": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/karma-jasmine-html-reporter/-/karma-jasmine-html-reporter-0.2.2.tgz", + "integrity": "sha1-SKjl7xiAdhfuK14zwRlMNbQ5Ukw=", + "dev": true, + "requires": { + "karma-jasmine": "^1.0.2" + } + }, + "karma-source-map-support": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/karma-source-map-support/-/karma-source-map-support-1.3.0.tgz", + "integrity": "sha512-HcPqdAusNez/ywa+biN4EphGz62MmQyPggUsDfsHqa7tSe4jdsxgvTKuDfIazjL+IOxpVWyT7Pr4dhAV+sxX5Q==", + "dev": true, + "requires": { + "source-map-support": "^0.5.5" + } + }, + "killable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/killable/-/killable-1.0.1.tgz", + "integrity": "sha512-LzqtLKlUwirEUyl/nicirVmNiPvYs7l5n8wOPP7fyJVpUPkvCnW/vuiXGpylGUlnPDnB7311rARzAt3Mhswpjg==", + "dev": true + }, + "kind-of": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", + "dev": true + }, + "lcid": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", + "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", + "dev": true, + "optional": true, + "requires": { + "invert-kv": "^1.0.0" + } + }, + "less": { + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/less/-/less-3.8.1.tgz", + "integrity": "sha512-8HFGuWmL3FhQR0aH89escFNBQH/nEiYPP2ltDFdQw2chE28Yx2E3lhAIq9Y2saYwLSwa699s4dBVEfCY8Drf7Q==", + "dev": true, + "requires": { + "clone": "^2.1.2", + "errno": "^0.1.1", + "graceful-fs": "^4.1.2", + "image-size": "~0.5.0", + "mime": "^1.4.1", + "mkdirp": "^0.5.0", + "promise": "^7.1.1", + "request": "^2.83.0", + "source-map": "~0.6.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "optional": true + } + } + }, + "less-loader": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/less-loader/-/less-loader-4.1.0.tgz", + "integrity": "sha512-KNTsgCE9tMOM70+ddxp9yyt9iHqgmSs0yTZc5XH5Wo+g80RWRIYNqE58QJKm/yMud5wZEvz50ugRDuzVIkyahg==", + "dev": true, + "requires": { + "clone": "^2.1.1", + "loader-utils": "^1.1.0", + "pify": "^3.0.0" + } + }, + "levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", + "dev": true, + "requires": { + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" + } + }, + "license-webpack-plugin": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/license-webpack-plugin/-/license-webpack-plugin-1.5.0.tgz", + "integrity": "sha512-Of/H79rZqm2aeg4RnP9SMSh19qkKemoLT5VaJV58uH5AxeYWEcBgGFs753JEJ/Hm6BPvQVfIlrrjoBwYj8p7Tw==", + "dev": true, + "requires": { + "ejs": "^2.5.7" + } + }, + "lie": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.1.1.tgz", + "integrity": "sha1-mkNrLMd0bKWd56QfpGmz77dr2H4=", + "dev": true, + "requires": { + "immediate": "~3.0.5" + } + }, + "load-json-file": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", + "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^2.2.0", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0", + "strip-bom": "^2.0.0" + }, + "dependencies": { + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + } + } + }, + "loader-runner": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-2.4.0.tgz", + "integrity": "sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw==", + "dev": true + }, + "loader-utils": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.1.0.tgz", + "integrity": "sha1-yYrvSIvM7aL/teLeZG1qdUQp9c0=", + "dev": true, + "requires": { + "big.js": "^3.1.3", + "emojis-list": "^2.0.0", + "json5": "^0.5.0" + } + }, + "locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "dev": true, + "requires": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + } + }, + "lodash": { + "version": "4.17.11", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", + "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==", + "dev": true + }, + "lodash.assign": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.assign/-/lodash.assign-4.2.0.tgz", + "integrity": "sha1-DZnzzNem0mHRm9rrkkUAXShYCOc=", + "dev": true, + "optional": true + }, + "lodash.clonedeep": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", + "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=", + "dev": true + }, + "lodash.debounce": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168=", + "dev": true + }, + "lodash.mergewith": { + "version": "4.6.1", + "resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.1.tgz", + "integrity": "sha512-eWw5r+PYICtEBgrBE5hhlT6aAa75f411bgDz/ZL2KZqYV03USvucsxcHUIlGTDTECs1eunpI7HOV7U+WLDvNdQ==", + "dev": true, + "optional": true + }, + "lodash.tail": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.tail/-/lodash.tail-4.1.1.tgz", + "integrity": "sha1-0jM6NtnncXyK0vfKyv7HwytERmQ=", + "dev": true + }, + "log4js": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/log4js/-/log4js-3.0.6.tgz", + "integrity": "sha512-ezXZk6oPJCWL483zj64pNkMuY/NcRX5MPiB0zE6tjZM137aeusrOnW1ecxgF9cmwMWkBMhjteQxBPoZBh9FDxQ==", + "dev": true, + "requires": { + "circular-json": "^0.5.5", + "date-format": "^1.2.0", + "debug": "^3.1.0", + "rfdc": "^1.1.2", + "streamroller": "0.7.0" + }, + "dependencies": { + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "dev": true + } + } + }, + "loglevel": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.6.1.tgz", + "integrity": "sha1-4PyVEztu8nbNyIh82vJKpvFW+Po=", + "dev": true + }, + "long": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/long/-/long-3.2.0.tgz", + "integrity": "sha1-2CG3E4yhy1gcFymQ7xTbIAtcR0s=", + "dev": true + }, + "loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "dev": true, + "requires": { + "js-tokens": "^3.0.0 || ^4.0.0" + } + }, + "loud-rejection": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz", + "integrity": "sha1-W0b4AUft7leIcPCG0Eghz5mOVR8=", + "dev": true, + "optional": true, + "requires": { + "currently-unhandled": "^0.4.1", + "signal-exit": "^3.0.0" + } + }, + "lower-case": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-1.1.4.tgz", + "integrity": "sha1-miyr0bno4K6ZOkv31YdcOcQujqw=", + "dev": true + }, + "lru-cache": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", + "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", + "dev": true, + "requires": { + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" + } + }, + "make-dir": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz", + "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==", + "dev": true, + "requires": { + "pify": "^3.0.0" + } + }, + "make-error": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.5.tgz", + "integrity": "sha512-c3sIjNUow0+8swNwVpqoH4YCShKNFkMaw6oH1mNS2haDZQqkeZFlHS3dhoeEbKKmJB4vXpJucU6oH75aDYeE9g==", + "dev": true + }, + "make-fetch-happen": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-4.0.1.tgz", + "integrity": "sha512-7R5ivfy9ilRJ1EMKIOziwrns9fGeAD4bAha8EB7BIiBBLHm2KeTUGCrICFt2rbHfzheTLynv50GnNTK1zDTrcQ==", + "dev": true, + "requires": { + "agentkeepalive": "^3.4.1", + "cacache": "^11.0.1", + "http-cache-semantics": "^3.8.1", + "http-proxy-agent": "^2.1.0", + "https-proxy-agent": "^2.2.1", + "lru-cache": "^4.1.2", + "mississippi": "^3.0.0", + "node-fetch-npm": "^2.0.2", + "promise-retry": "^1.1.1", + "socks-proxy-agent": "^4.0.0", + "ssri": "^6.0.0" + }, + "dependencies": { + "cacache": { + "version": "11.3.2", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-11.3.2.tgz", + "integrity": "sha512-E0zP4EPGDOaT2chM08Als91eYnf8Z+eH1awwwVsngUmgppfM5jjJ8l3z5vO5p5w/I3LsiXawb1sW0VY65pQABg==", + "dev": true, + "requires": { + "bluebird": "^3.5.3", + "chownr": "^1.1.1", + "figgy-pudding": "^3.5.1", + "glob": "^7.1.3", + "graceful-fs": "^4.1.15", + "lru-cache": "^5.1.1", + "mississippi": "^3.0.0", + "mkdirp": "^0.5.1", + "move-concurrently": "^1.0.1", + "promise-inflight": "^1.0.1", + "rimraf": "^2.6.2", + "ssri": "^6.0.1", + "unique-filename": "^1.1.1", + "y18n": "^4.0.0" + }, + "dependencies": { + "lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "requires": { + "yallist": "^3.0.2" + } + } + } + }, + "mississippi": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-3.0.0.tgz", + "integrity": "sha512-x471SsVjUtBRtcvd4BzKE9kFC+/2TeWgKCgw0bZcw1b9l2X3QX5vCWgF+KaZaYm87Ss//rHnWryupDrgLvmSkA==", + "dev": true, + "requires": { + "concat-stream": "^1.5.0", + "duplexify": "^3.4.2", + "end-of-stream": "^1.1.0", + "flush-write-stream": "^1.0.0", + "from2": "^2.1.0", + "parallel-transform": "^1.1.0", + "pump": "^3.0.0", + "pumpify": "^1.3.3", + "stream-each": "^1.1.0", + "through2": "^2.0.0" + } + }, + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "ssri": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.1.tgz", + "integrity": "sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA==", + "dev": true, + "requires": { + "figgy-pudding": "^3.5.1" + } + }, + "yallist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.3.tgz", + "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==", + "dev": true + } + } + }, + "mamacro": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/mamacro/-/mamacro-0.0.3.tgz", + "integrity": "sha512-qMEwh+UujcQ+kbz3T6V+wAmO2U8veoq2w+3wY8MquqwVA3jChfwY+Tk52GZKDfACEPjuZ7r2oJLejwpt8jtwTA==", + "dev": true + }, + "map-age-cleaner": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz", + "integrity": "sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==", + "dev": true, + "requires": { + "p-defer": "^1.0.0" + } + }, + "map-cache": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", + "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", + "dev": true + }, + "map-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", + "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=", + "dev": true + }, + "map-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", + "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", + "dev": true, + "requires": { + "object-visit": "^1.0.0" + } + }, + "math-random": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/math-random/-/math-random-1.0.4.tgz", + "integrity": "sha512-rUxjysqif/BZQH2yhd5Aaq7vXMSx9NdEsQcyA07uEzIvxgI7zIr33gGsh+RU0/XjmQpCW7RsVof1vlkvQVCK5A==", + "dev": true + }, + "md5.js": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", + "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", + "dev": true, + "requires": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", + "dev": true + }, + "mem": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/mem/-/mem-4.1.0.tgz", + "integrity": "sha512-I5u6Q1x7wxO0kdOpYBB28xueHADYps5uty/zg936CiG8NTe5sJL8EjrCuLneuDW3PlMdZBGDIn8BirEVdovZvg==", + "dev": true, + "requires": { + "map-age-cleaner": "^0.1.1", + "mimic-fn": "^1.0.0", + "p-is-promise": "^2.0.0" + } + }, + "memory-fs": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz", + "integrity": "sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=", + "dev": true, + "requires": { + "errno": "^0.1.3", + "readable-stream": "^2.0.1" + } + }, + "meow": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz", + "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=", + "dev": true, + "optional": true, + "requires": { + "camelcase-keys": "^2.0.0", + "decamelize": "^1.1.2", + "loud-rejection": "^1.0.0", + "map-obj": "^1.0.1", + "minimist": "^1.1.3", + "normalize-package-data": "^2.3.4", + "object-assign": "^4.0.1", + "read-pkg-up": "^1.0.1", + "redent": "^1.0.0", + "trim-newlines": "^1.0.0" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "dev": true, + "optional": true + } + } + }, + "merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=", + "dev": true + }, + "methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", + "dev": true + }, + "micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "dev": true, + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + } + }, + "miller-rabin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", + "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", + "dev": true, + "requires": { + "bn.js": "^4.0.0", + "brorand": "^1.0.1" + } + }, + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true + }, + "mime-db": { + "version": "1.38.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.38.0.tgz", + "integrity": "sha512-bqVioMFFzc2awcdJZIzR3HjZFX20QhilVS7hytkKrv7xFAn8bM1gzc/FOX2awLISvWe0PV8ptFKcon+wZ5qYkg==", + "dev": true + }, + "mime-types": { + "version": "2.1.22", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.22.tgz", + "integrity": "sha512-aGl6TZGnhm/li6F7yx82bJiBZwgiEa4Hf6CNr8YO+r5UHr53tSTYZb102zyU50DOWWKeOv0uQLRL0/9EiKWCog==", + "dev": true, + "requires": { + "mime-db": "~1.38.0" + } + }, + "mimic-fn": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", + "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", + "dev": true + }, + "mini-css-extract-plugin": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-0.4.4.tgz", + "integrity": "sha512-o+Jm+ocb0asEngdM6FsZWtZsRzA8koFUudIDwYUfl94M3PejPHG7Vopw5hN9V8WsMkSFpm3tZP3Fesz89EyrfQ==", + "dev": true, + "requires": { + "loader-utils": "^1.1.0", + "schema-utils": "^1.0.0", + "webpack-sources": "^1.1.0" + }, + "dependencies": { + "schema-utils": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", + "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", + "dev": true, + "requires": { + "ajv": "^6.1.0", + "ajv-errors": "^1.0.0", + "ajv-keywords": "^3.1.0" + } + } + } + }, + "minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", + "dev": true + }, + "minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=", + "dev": true + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", + "dev": true + }, + "minipass": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.3.5.tgz", + "integrity": "sha512-Gi1W4k059gyRbyVUZQ4mEqLm0YIUiGYfvxhF6SIlk3ui1WVxMTGfGdQ2SInh3PDrRTVvPKgULkpJtT4RH10+VA==", + "dev": true, + "requires": { + "safe-buffer": "^5.1.2", + "yallist": "^3.0.0" + }, + "dependencies": { + "yallist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.3.tgz", + "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==", + "dev": true + } + } + }, + "minizlib": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.2.1.tgz", + "integrity": "sha512-7+4oTUOWKg7AuL3vloEWekXY2/D20cevzsrNT2kGWm+39J9hGTCBv8VI5Pm5lXZ/o3/mdR4f8rflAPhnQb8mPA==", + "dev": true, + "requires": { + "minipass": "^2.2.1" + } + }, + "mississippi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-2.0.0.tgz", + "integrity": "sha512-zHo8v+otD1J10j/tC+VNoGK9keCuByhKovAvdn74dmxJl9+mWHnx6EMsDN4lgRoMI/eYo2nchAxniIbUPb5onw==", + "dev": true, + "requires": { + "concat-stream": "^1.5.0", + "duplexify": "^3.4.2", + "end-of-stream": "^1.1.0", + "flush-write-stream": "^1.0.0", + "from2": "^2.1.0", + "parallel-transform": "^1.1.0", + "pump": "^2.0.1", + "pumpify": "^1.3.3", + "stream-each": "^1.1.0", + "through2": "^2.0.0" + } + }, + "mixin-deep": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.1.tgz", + "integrity": "sha512-8ZItLHeEgaqEvd5lYBXfm4EZSFCX29Jb9K+lAHhDKzReKBQKj3R+7NOF6tjqYi9t4oI8VUfaWITJQm86wnXGNQ==", + "dev": true, + "requires": { + "for-in": "^1.0.2", + "is-extendable": "^1.0.1" + }, + "dependencies": { + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dev": true, + "requires": { + "is-plain-object": "^2.0.4" + } + } + } + }, + "mixin-object": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mixin-object/-/mixin-object-2.0.1.tgz", + "integrity": "sha1-T7lJRB2rGCVA8f4DW6YOGUel5X4=", + "dev": true, + "requires": { + "for-in": "^0.1.3", + "is-extendable": "^0.1.1" + }, + "dependencies": { + "for-in": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/for-in/-/for-in-0.1.8.tgz", + "integrity": "sha1-2Hc5COMSVhCZUrH9ubP6hn0ndeE=", + "dev": true + } + } + }, + "mkdirp": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "dev": true, + "requires": { + "minimist": "0.0.8" + } + }, + "move-concurrently": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz", + "integrity": "sha1-viwAX9oy4LKa8fBdfEszIUxwH5I=", + "dev": true, + "requires": { + "aproba": "^1.1.1", + "copy-concurrently": "^1.0.0", + "fs-write-stream-atomic": "^1.0.8", + "mkdirp": "^0.5.1", + "rimraf": "^2.5.4", + "run-queue": "^1.0.3" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "multicast-dns": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-6.2.3.tgz", + "integrity": "sha512-ji6J5enbMyGRHIAkAOu3WdV8nggqviKCEKtXcOqfphZZtQrmHKycfynJ2V7eVPUA4NhJ6V7Wf4TmGbTwKE9B6g==", + "dev": true, + "requires": { + "dns-packet": "^1.3.1", + "thunky": "^1.0.2" + } + }, + "multicast-dns-service-types": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz", + "integrity": "sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE=", + "dev": true + }, + "nan": { + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.12.1.tgz", + "integrity": "sha512-JY7V6lRkStKcKTvHO5NVSQRv+RV+FIL5pvDoLiAtSL9pKlC5x9PKQcZDsq7m4FO4d57mkhC6Z+QhAh3Jdk5JFw==", + "dev": true, + "optional": true + }, + "nanomatch": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", + "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", + "dev": true, + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "fragment-cache": "^0.2.1", + "is-windows": "^1.0.2", + "kind-of": "^6.0.2", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + } + }, + "negotiator": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz", + "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=", + "dev": true + }, + "neo-async": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.0.tgz", + "integrity": "sha512-MFh0d/Wa7vkKO3Y3LlacqAEeHK0mckVqzDieUKTT+KGxi+zIpeVsFxymkIiRpbpDziHc290Xr9A1O4Om7otoRA==", + "dev": true + }, + "nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", + "dev": true + }, + "no-case": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/no-case/-/no-case-2.3.2.tgz", + "integrity": "sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ==", + "dev": true, + "requires": { + "lower-case": "^1.1.1" + } + }, + "node-fetch-npm": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/node-fetch-npm/-/node-fetch-npm-2.0.2.tgz", + "integrity": "sha512-nJIxm1QmAj4v3nfCvEeCrYSoVwXyxLnaPBK5W1W5DGEJwjlKuC2VEUycGw5oxk+4zZahRrB84PUJJgEmhFTDFw==", + "dev": true, + "requires": { + "encoding": "^0.1.11", + "json-parse-better-errors": "^1.0.0", + "safe-buffer": "^5.1.1" + } + }, + "node-forge": { + "version": "0.7.5", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.7.5.tgz", + "integrity": "sha512-MmbQJ2MTESTjt3Gi/3yG1wGpIMhUfcIypUCGtTizFR9IiccFwxSpfp0vtIZlkFclEqERemxfnSdZEMR9VqqEFQ==", + "dev": true + }, + "node-gyp": { + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-3.8.0.tgz", + "integrity": "sha512-3g8lYefrRRzvGeSowdJKAKyks8oUpLEd/DyPV4eMhVlhJ0aNaZqIrNUIPuEWWTAoPqyFkfGrM67MC69baqn6vA==", + "dev": true, + "optional": true, + "requires": { + "fstream": "^1.0.0", + "glob": "^7.0.3", + "graceful-fs": "^4.1.2", + "mkdirp": "^0.5.0", + "nopt": "2 || 3", + "npmlog": "0 || 1 || 2 || 3 || 4", + "osenv": "0", + "request": "^2.87.0", + "rimraf": "2", + "semver": "~5.3.0", + "tar": "^2.0.0", + "which": "1" + }, + "dependencies": { + "semver": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz", + "integrity": "sha1-myzl094C0XxgEq0yaqa00M9U+U8=", + "dev": true, + "optional": true + } + } + }, + "node-libs-browser": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.2.0.tgz", + "integrity": "sha512-5MQunG/oyOaBdttrL40dA7bUfPORLRWMUJLQtMg7nluxUvk5XwnLdL9twQHFAjRx/y7mIMkLKT9++qPbbk6BZA==", + "dev": true, + "requires": { + "assert": "^1.1.1", + "browserify-zlib": "^0.2.0", + "buffer": "^4.3.0", + "console-browserify": "^1.1.0", + "constants-browserify": "^1.0.0", + "crypto-browserify": "^3.11.0", + "domain-browser": "^1.1.1", + "events": "^3.0.0", + "https-browserify": "^1.0.0", + "os-browserify": "^0.3.0", + "path-browserify": "0.0.0", + "process": "^0.11.10", + "punycode": "^1.2.4", + "querystring-es3": "^0.2.0", + "readable-stream": "^2.3.3", + "stream-browserify": "^2.0.1", + "stream-http": "^2.7.2", + "string_decoder": "^1.0.0", + "timers-browserify": "^2.0.4", + "tty-browserify": "0.0.0", + "url": "^0.11.0", + "util": "^0.11.0", + "vm-browserify": "0.0.4" + }, + "dependencies": { + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", + "dev": true + } + } + }, + "node-sass": { + "version": "4.11.0", + "resolved": "https://registry.npmjs.org/node-sass/-/node-sass-4.11.0.tgz", + "integrity": "sha512-bHUdHTphgQJZaF1LASx0kAviPH7sGlcyNhWade4eVIpFp6tsn7SV8xNMTbsQFpEV9VXpnwTTnNYlfsZXgGgmkA==", + "dev": true, + "optional": true, + "requires": { + "async-foreach": "^0.1.3", + "chalk": "^1.1.1", + "cross-spawn": "^3.0.0", + "gaze": "^1.0.0", + "get-stdin": "^4.0.1", + "glob": "^7.0.3", + "in-publish": "^2.0.0", + "lodash.assign": "^4.2.0", + "lodash.clonedeep": "^4.3.2", + "lodash.mergewith": "^4.6.0", + "meow": "^3.7.0", + "mkdirp": "^0.5.1", + "nan": "^2.10.0", + "node-gyp": "^3.8.0", + "npmlog": "^4.0.0", + "request": "^2.88.0", + "sass-graph": "^2.2.4", + "stdout-stream": "^1.4.0", + "true-case-path": "^1.0.2" + }, + "dependencies": { + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true, + "optional": true + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "optional": true, + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true, + "optional": true + } + } + }, + "nopt": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz", + "integrity": "sha1-xkZdvwirzU2zWTF/eaxopkayj/k=", + "dev": true, + "requires": { + "abbrev": "1" + } + }, + "normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "requires": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + }, + "dependencies": { + "resolve": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.10.0.tgz", + "integrity": "sha512-3sUr9aq5OfSg2S9pNtPA9hL1FVEAjvfOC4leW0SNf/mpnaakz2a9femSd6LqAww2RaFctwyf1lCqnTHuF1rxDg==", + "dev": true, + "requires": { + "path-parse": "^1.0.6" + } + } + } + }, + "normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", + "dev": true, + "requires": { + "remove-trailing-separator": "^1.0.1" + } + }, + "normalize-range": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", + "integrity": "sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=", + "dev": true + }, + "npm-bundled": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.0.6.tgz", + "integrity": "sha512-8/JCaftHwbd//k6y2rEWp6k1wxVfpFzB6t1p825+cUb7Ym2XQfhwIC5KwhrvzZRJu+LtDE585zVaS32+CGtf0g==", + "dev": true + }, + "npm-package-arg": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-6.1.0.tgz", + "integrity": "sha512-zYbhP2k9DbJhA0Z3HKUePUgdB1x7MfIfKssC+WLPFMKTBZKpZh5m13PgexJjCq6KW7j17r0jHWcCpxEqnnncSA==", + "dev": true, + "requires": { + "hosted-git-info": "^2.6.0", + "osenv": "^0.1.5", + "semver": "^5.5.0", + "validate-npm-package-name": "^3.0.0" + } + }, + "npm-packlist": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.3.0.tgz", + "integrity": "sha512-qPBc6CnxEzpOcc4bjoIBJbYdy0D/LFFPUdxvfwor4/w3vxeE0h6TiOVurCEPpQ6trjN77u/ShyfeJGsbAfB3dA==", + "dev": true, + "requires": { + "ignore-walk": "^3.0.1", + "npm-bundled": "^1.0.1" + } + }, + "npm-pick-manifest": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-2.2.3.tgz", + "integrity": "sha512-+IluBC5K201+gRU85vFlUwX3PFShZAbAgDNp2ewJdWMVSppdo/Zih0ul2Ecky/X7b51J7LrrUAP+XOmOCvYZqA==", + "dev": true, + "requires": { + "figgy-pudding": "^3.5.1", + "npm-package-arg": "^6.0.0", + "semver": "^5.4.1" + } + }, + "npm-registry-client": { + "version": "8.6.0", + "resolved": "https://registry.npmjs.org/npm-registry-client/-/npm-registry-client-8.6.0.tgz", + "integrity": "sha512-Qs6P6nnopig+Y8gbzpeN/dkt+n7IyVd8f45NTMotGk6Qo7GfBmzwYx6jRLoOOgKiMnaQfYxsuyQlD8Mc3guBhg==", + "dev": true, + "requires": { + "concat-stream": "^1.5.2", + "graceful-fs": "^4.1.6", + "normalize-package-data": "~1.0.1 || ^2.0.0", + "npm-package-arg": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0", + "npmlog": "2 || ^3.1.0 || ^4.0.0", + "once": "^1.3.3", + "request": "^2.74.0", + "retry": "^0.10.0", + "safe-buffer": "^5.1.1", + "semver": "2 >=2.2.1 || 3.x || 4 || 5", + "slide": "^1.1.3", + "ssri": "^5.2.4" + } + }, + "npm-registry-fetch": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-3.9.0.tgz", + "integrity": "sha512-srwmt8YhNajAoSAaDWndmZgx89lJwIZ1GWxOuckH4Coek4uHv5S+o/l9FLQe/awA+JwTnj4FJHldxhlXdZEBmw==", + "dev": true, + "requires": { + "JSONStream": "^1.3.4", + "bluebird": "^3.5.1", + "figgy-pudding": "^3.4.1", + "lru-cache": "^4.1.3", + "make-fetch-happen": "^4.0.1", + "npm-package-arg": "^6.1.0" + } + }, + "npm-run-path": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", + "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", + "dev": true, + "requires": { + "path-key": "^2.0.0" + } + }, + "npmlog": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", + "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", + "dev": true, + "requires": { + "are-we-there-yet": "~1.1.2", + "console-control-strings": "~1.1.0", + "gauge": "~2.7.3", + "set-blocking": "~2.0.0" + } + }, + "nth-check": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz", + "integrity": "sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==", + "dev": true, + "requires": { + "boolbase": "~1.0.0" + } + }, + "null-check": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/null-check/-/null-check-1.0.0.tgz", + "integrity": "sha1-l33/1xdgErnsMNKjnbXPcqBDnt0=", + "dev": true + }, + "num2fraction": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/num2fraction/-/num2fraction-1.2.2.tgz", + "integrity": "sha1-b2gragJ6Tp3fpFZM0lidHU5mnt4=", + "dev": true + }, + "number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", + "dev": true + }, + "oauth-sign": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", + "dev": true + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "dev": true + }, + "object-component": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/object-component/-/object-component-0.0.3.tgz", + "integrity": "sha1-8MaapQ78lbhmwYb0AKM3acsvEpE=", + "dev": true + }, + "object-copy": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", + "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", + "dev": true, + "requires": { + "copy-descriptor": "^0.1.0", + "define-property": "^0.2.5", + "kind-of": "^3.0.3" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "object-keys": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.0.tgz", + "integrity": "sha512-6OO5X1+2tYkNyNEx6TsCxEqFfRWaqx6EtMiSbGrw8Ob8v9Ne+Hl8rBAgLBZn5wjEz3s/s6U1WXFUFOcxxAwUpg==", + "dev": true + }, + "object-visit": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", + "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", + "dev": true, + "requires": { + "isobject": "^3.0.0" + } + }, + "object.getownpropertydescriptors": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz", + "integrity": "sha1-h1jIRvW0B62rDyNuCYbxSwUcqhY=", + "dev": true, + "requires": { + "define-properties": "^1.1.2", + "es-abstract": "^1.5.1" + } + }, + "object.omit": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/object.omit/-/object.omit-2.0.1.tgz", + "integrity": "sha1-Gpx0SCnznbuFjHbKNXmuKlTr0fo=", + "dev": true, + "requires": { + "for-own": "^0.1.4", + "is-extendable": "^0.1.1" + }, + "dependencies": { + "for-own": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/for-own/-/for-own-0.1.5.tgz", + "integrity": "sha1-UmXGgaTylNq78XyVCbZ2OqhFEM4=", + "dev": true, + "requires": { + "for-in": "^1.0.1" + } + } + } + }, + "object.pick": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", + "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", + "dev": true, + "requires": { + "isobject": "^3.0.1" + } + }, + "obuf": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", + "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==", + "dev": true + }, + "on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "dev": true, + "requires": { + "ee-first": "1.1.1" + } + }, + "on-headers": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.1.tgz", + "integrity": "sha1-ko9dD0cNSTQmUepnlLCFfBAGk/c=", + "dev": true + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "opn": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/opn/-/opn-5.4.0.tgz", + "integrity": "sha512-YF9MNdVy/0qvJvDtunAOzFw9iasOQHpVthTCvGzxt61Il64AYSGdK+rYwld7NAfk9qJ7dt+hymBNSc9LNYS+Sw==", + "dev": true, + "requires": { + "is-wsl": "^1.1.0" + } + }, + "optimist": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", + "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=", + "dev": true, + "requires": { + "minimist": "~0.0.1", + "wordwrap": "~0.0.2" + }, + "dependencies": { + "wordwrap": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz", + "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc=", + "dev": true + } + } + }, + "optionator": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz", + "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=", + "dev": true, + "requires": { + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.4", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "wordwrap": "~1.0.0" + } + }, + "original": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/original/-/original-1.0.2.tgz", + "integrity": "sha512-hyBVl6iqqUOJ8FqRe+l/gS8H+kKYjrEndd5Pm1MfBtsEKA038HkkdbAl/72EAXGyonD/PFsvmVG+EvcIpliMBg==", + "dev": true, + "requires": { + "url-parse": "^1.4.3" + } + }, + "os-browserify": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", + "integrity": "sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=", + "dev": true + }, + "os-homedir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", + "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", + "dev": true + }, + "os-locale": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", + "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=", + "dev": true, + "optional": true, + "requires": { + "lcid": "^1.0.0" + } + }, + "os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", + "dev": true + }, + "osenv": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", + "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", + "dev": true, + "requires": { + "os-homedir": "^1.0.0", + "os-tmpdir": "^1.0.0" + } + }, + "p-defer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz", + "integrity": "sha1-n26xgvbJqozXQwBKfU+WsZaw+ww=", + "dev": true + }, + "p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", + "dev": true + }, + "p-is-promise": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-2.0.0.tgz", + "integrity": "sha512-pzQPhYMCAgLAKPWD2jC3Se9fEfrD9npNos0y150EeqZll7akhEgGhTW/slB6lHku8AvYGiJ+YJ5hfHKePPgFWg==", + "dev": true + }, + "p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "requires": { + "p-try": "^1.0.0" + } + }, + "p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "dev": true, + "requires": { + "p-limit": "^1.1.0" + } + }, + "p-map": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-1.2.0.tgz", + "integrity": "sha512-r6zKACMNhjPJMTl8KcFH4li//gkrXWfbD6feV8l6doRHlzljFWGJ2AP6iKaCJXyZmAUMOPtvbW7EXkbWO/pLEA==", + "dev": true + }, + "p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", + "dev": true + }, + "pacote": { + "version": "9.2.3", + "resolved": "https://registry.npmjs.org/pacote/-/pacote-9.2.3.tgz", + "integrity": "sha512-Y3+yY3nBRAxMlZWvr62XLJxOwCmG9UmkGZkFurWHoCjqF0cZL72cTOCRJTvWw8T4OhJS2RTg13x4oYYriauvEw==", + "dev": true, + "requires": { + "bluebird": "^3.5.2", + "cacache": "^11.2.0", + "figgy-pudding": "^3.5.1", + "get-stream": "^4.1.0", + "glob": "^7.1.3", + "lru-cache": "^4.1.3", + "make-fetch-happen": "^4.0.1", + "minimatch": "^3.0.4", + "minipass": "^2.3.5", + "mississippi": "^3.0.0", + "mkdirp": "^0.5.1", + "normalize-package-data": "^2.4.0", + "npm-package-arg": "^6.1.0", + "npm-packlist": "^1.1.12", + "npm-pick-manifest": "^2.2.3", + "npm-registry-fetch": "^3.8.0", + "osenv": "^0.1.5", + "promise-inflight": "^1.0.1", + "promise-retry": "^1.1.1", + "protoduck": "^5.0.1", + "rimraf": "^2.6.2", + "safe-buffer": "^5.1.2", + "semver": "^5.6.0", + "ssri": "^6.0.1", + "tar": "^4.4.6", + "unique-filename": "^1.1.1", + "which": "^1.3.1" + }, + "dependencies": { + "cacache": { + "version": "11.3.2", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-11.3.2.tgz", + "integrity": "sha512-E0zP4EPGDOaT2chM08Als91eYnf8Z+eH1awwwVsngUmgppfM5jjJ8l3z5vO5p5w/I3LsiXawb1sW0VY65pQABg==", + "dev": true, + "requires": { + "bluebird": "^3.5.3", + "chownr": "^1.1.1", + "figgy-pudding": "^3.5.1", + "glob": "^7.1.3", + "graceful-fs": "^4.1.15", + "lru-cache": "^5.1.1", + "mississippi": "^3.0.0", + "mkdirp": "^0.5.1", + "move-concurrently": "^1.0.1", + "promise-inflight": "^1.0.1", + "rimraf": "^2.6.2", + "ssri": "^6.0.1", + "unique-filename": "^1.1.1", + "y18n": "^4.0.0" + }, + "dependencies": { + "lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "requires": { + "yallist": "^3.0.2" + } + } + } + }, + "get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "dev": true, + "requires": { + "pump": "^3.0.0" + } + }, + "mississippi": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-3.0.0.tgz", + "integrity": "sha512-x471SsVjUtBRtcvd4BzKE9kFC+/2TeWgKCgw0bZcw1b9l2X3QX5vCWgF+KaZaYm87Ss//rHnWryupDrgLvmSkA==", + "dev": true, + "requires": { + "concat-stream": "^1.5.0", + "duplexify": "^3.4.2", + "end-of-stream": "^1.1.0", + "flush-write-stream": "^1.0.0", + "from2": "^2.1.0", + "parallel-transform": "^1.1.0", + "pump": "^3.0.0", + "pumpify": "^1.3.3", + "stream-each": "^1.1.0", + "through2": "^2.0.0" + } + }, + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "ssri": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.1.tgz", + "integrity": "sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA==", + "dev": true, + "requires": { + "figgy-pudding": "^3.5.1" + } + }, + "tar": { + "version": "4.4.8", + "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.8.tgz", + "integrity": "sha512-LzHF64s5chPQQS0IYBn9IN5h3i98c12bo4NCO7e0sGM2llXQ3p2FGC5sdENN4cTW48O915Sh+x+EXx7XW96xYQ==", + "dev": true, + "requires": { + "chownr": "^1.1.1", + "fs-minipass": "^1.2.5", + "minipass": "^2.3.4", + "minizlib": "^1.1.1", + "mkdirp": "^0.5.0", + "safe-buffer": "^5.1.2", + "yallist": "^3.0.2" + } + }, + "yallist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.3.tgz", + "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==", + "dev": true + } + } + }, + "pako": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.8.tgz", + "integrity": "sha512-6i0HVbUfcKaTv+EG8ZTr75az7GFXcLYk9UyLEg7Notv/Ma+z/UG3TCoz6GiNeOrn1E/e63I0X/Hpw18jHOTUnA==", + "dev": true + }, + "parallel-transform": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/parallel-transform/-/parallel-transform-1.1.0.tgz", + "integrity": "sha1-1BDwZbBdojCB/NEPKIVMKb2jOwY=", + "dev": true, + "requires": { + "cyclist": "~0.2.2", + "inherits": "^2.0.3", + "readable-stream": "^2.1.5" + } + }, + "param-case": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/param-case/-/param-case-2.1.1.tgz", + "integrity": "sha1-35T9jPZTHs915r75oIWPvHK+Ikc=", + "dev": true, + "requires": { + "no-case": "^2.2.0" + } + }, + "parse-asn1": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.4.tgz", + "integrity": "sha512-Qs5duJcuvNExRfFZ99HDD3z4mAi3r9Wl/FOjEOijlxwCZs7E7mW2vjTpgQ4J8LpTF8x5v+1Vn5UQFejmWT11aw==", + "dev": true, + "requires": { + "asn1.js": "^4.0.0", + "browserify-aes": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.0", + "pbkdf2": "^3.0.3", + "safe-buffer": "^5.1.1" + } + }, + "parse-glob": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/parse-glob/-/parse-glob-3.0.4.tgz", + "integrity": "sha1-ssN2z7EfNVE7rdFz7wu246OIORw=", + "dev": true, + "requires": { + "glob-base": "^0.3.0", + "is-dotfile": "^1.0.0", + "is-extglob": "^1.0.0", + "is-glob": "^2.0.0" + }, + "dependencies": { + "is-extglob": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", + "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", + "dev": true + }, + "is-glob": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", + "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", + "dev": true, + "requires": { + "is-extglob": "^1.0.0" + } + } + } + }, + "parse-json": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", + "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", + "dev": true, + "requires": { + "error-ex": "^1.2.0" + } + }, + "parse5": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-4.0.0.tgz", + "integrity": "sha512-VrZ7eOd3T1Fk4XWNXMgiGBK/z0MG48BWG2uQNU4I72fkQuKUTZpl+u9k+CxEG0twMVzSmXEEz12z5Fnw1jIQFA==", + "dev": true + }, + "parseqs": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.5.tgz", + "integrity": "sha1-1SCKNzjkZ2bikbouoXNoSSGouJ0=", + "dev": true, + "requires": { + "better-assert": "~1.0.0" + } + }, + "parseuri": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.5.tgz", + "integrity": "sha1-gCBKUNTbt3m/3G6+J3jZDkvOMgo=", + "dev": true, + "requires": { + "better-assert": "~1.0.0" + } + }, + "parseurl": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz", + "integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M=", + "dev": true + }, + "pascalcase": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", + "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", + "dev": true + }, + "path-browserify": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.0.tgz", + "integrity": "sha1-oLhwcpquIUAFt9UDLsLLuw+0RRo=", + "dev": true + }, + "path-dirname": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", + "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=", + "dev": true + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, + "path-is-inside": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", + "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=", + "dev": true + }, + "path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", + "dev": true + }, + "path-parse": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", + "dev": true + }, + "path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=", + "dev": true + }, + "path-type": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", + "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", + "dev": true, + "requires": { + "pify": "^3.0.0" + } + }, + "pbkdf2": { + "version": "3.0.17", + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.17.tgz", + "integrity": "sha512-U/il5MsrZp7mGg3mSQfn742na2T+1/vHDCG5/iTI3X9MKUuYUZVLQhyRsg06mCgDBTd57TxzgZt7P+fYfjRLtA==", + "dev": true, + "requires": { + "create-hash": "^1.1.2", + "create-hmac": "^1.1.4", + "ripemd160": "^2.0.1", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", + "dev": true + }, + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "dev": true + }, + "pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", + "dev": true + }, + "pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", + "dev": true, + "requires": { + "pinkie": "^2.0.0" + } + }, + "pkg-dir": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz", + "integrity": "sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=", + "dev": true, + "requires": { + "find-up": "^2.1.0" + } + }, + "portfinder": { + "version": "1.0.19", + "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.19.tgz", + "integrity": "sha512-23aeQKW9KgHe6citUrG3r9HjeX6vls0h713TAa+CwTKZwNIr/pD2ApaxYF4Um3ZZyq4ar+Siv3+fhoHaIwSOSw==", + "dev": true, + "requires": { + "async": "^1.5.2", + "debug": "^2.2.0", + "mkdirp": "0.5.x" + } + }, + "posix-character-classes": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", + "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", + "dev": true + }, + "postcss": { + "version": "6.0.23", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.23.tgz", + "integrity": "sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag==", + "dev": true, + "requires": { + "chalk": "^2.4.1", + "source-map": "^0.6.1", + "supports-color": "^5.4.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "postcss-import": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-11.1.0.tgz", + "integrity": "sha512-5l327iI75POonjxkXgdRCUS+AlzAdBx4pOvMEhTKTCjb1p8IEeVR9yx3cPbmN7LIWJLbfnIXxAhoB4jpD0c/Cw==", + "dev": true, + "requires": { + "postcss": "^6.0.1", + "postcss-value-parser": "^3.2.3", + "read-cache": "^1.0.0", + "resolve": "^1.1.7" + } + }, + "postcss-load-config": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-2.0.0.tgz", + "integrity": "sha512-V5JBLzw406BB8UIfsAWSK2KSwIJ5yoEIVFb4gVkXci0QdKgA24jLmHZ/ghe/GgX0lJ0/D1uUK1ejhzEY94MChQ==", + "dev": true, + "requires": { + "cosmiconfig": "^4.0.0", + "import-cwd": "^2.0.0" + } + }, + "postcss-loader": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-2.1.6.tgz", + "integrity": "sha512-hgiWSc13xVQAq25cVw80CH0l49ZKlAnU1hKPOdRrNj89bokRr/bZF2nT+hebPPF9c9xs8c3gw3Fr2nxtmXYnNg==", + "dev": true, + "requires": { + "loader-utils": "^1.1.0", + "postcss": "^6.0.0", + "postcss-load-config": "^2.0.0", + "schema-utils": "^0.4.0" + } + }, + "postcss-url": { + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/postcss-url/-/postcss-url-7.3.2.tgz", + "integrity": "sha512-QMV5mA+pCYZQcUEPQkmor9vcPQ2MT+Ipuu8qdi1gVxbNiIiErEGft+eny1ak19qALoBkccS5AHaCaCDzh7b9MA==", + "dev": true, + "requires": { + "mime": "^1.4.1", + "minimatch": "^3.0.4", + "mkdirp": "^0.5.0", + "postcss": "^6.0.1", + "xxhashjs": "^0.2.1" + } + }, + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + }, + "prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", + "dev": true + }, + "preserve": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/preserve/-/preserve-0.2.0.tgz", + "integrity": "sha1-gV7R9uvGWSb4ZbMQwHE7yzMVzks=", + "dev": true + }, + "pretty-error": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-2.1.1.tgz", + "integrity": "sha1-X0+HyPkeWuPzuoerTPXgOxoX8aM=", + "dev": true, + "requires": { + "renderkid": "^2.0.1", + "utila": "~0.4" + } + }, + "process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=", + "dev": true + }, + "process-nextick-args": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", + "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", + "dev": true + }, + "promise": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", + "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", + "dev": true, + "optional": true, + "requires": { + "asap": "~2.0.3" + } + }, + "promise-inflight": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", + "integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=", + "dev": true + }, + "promise-retry": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-1.1.1.tgz", + "integrity": "sha1-ZznpaOMFHaIM5kl/srUPaRHfPW0=", + "dev": true, + "requires": { + "err-code": "^1.0.0", + "retry": "^0.10.0" + } + }, + "protoduck": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/protoduck/-/protoduck-5.0.1.tgz", + "integrity": "sha512-WxoCeDCoCBY55BMvj4cAEjdVUFGRWed9ZxPlqTKYyw1nDDTQ4pqmnIMAGfJlg7Dx35uB/M+PHJPTmGOvaCaPTg==", + "dev": true, + "requires": { + "genfun": "^5.0.0" + } + }, + "protractor": { + "version": "5.4.2", + "resolved": "https://registry.npmjs.org/protractor/-/protractor-5.4.2.tgz", + "integrity": "sha512-zlIj64Cr6IOWP7RwxVeD8O4UskLYPoyIcg0HboWJL9T79F1F0VWtKkGTr/9GN6BKL+/Q/GmM7C9kFVCfDbP5sA==", + "dev": true, + "requires": { + "@types/q": "^0.0.32", + "@types/selenium-webdriver": "^3.0.0", + "blocking-proxy": "^1.0.0", + "browserstack": "^1.5.1", + "chalk": "^1.1.3", + "glob": "^7.0.3", + "jasmine": "2.8.0", + "jasminewd2": "^2.1.0", + "optimist": "~0.6.0", + "q": "1.4.1", + "saucelabs": "^1.5.0", + "selenium-webdriver": "3.6.0", + "source-map-support": "~0.4.0", + "webdriver-js-extender": "2.1.0", + "webdriver-manager": "^12.0.6" + }, + "dependencies": { + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + } + }, + "del": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/del/-/del-2.2.2.tgz", + "integrity": "sha1-wSyYHQZ4RshLyvhiz/kw2Qf/0ag=", + "dev": true, + "requires": { + "globby": "^5.0.0", + "is-path-cwd": "^1.0.0", + "is-path-in-cwd": "^1.0.0", + "object-assign": "^4.0.1", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0", + "rimraf": "^2.2.8" + } + }, + "globby": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-5.0.0.tgz", + "integrity": "sha1-69hGZ8oNuzMLmbz8aOrCvFQ3Dg0=", + "dev": true, + "requires": { + "array-union": "^1.0.1", + "arrify": "^1.0.0", + "glob": "^7.0.3", + "object-assign": "^4.0.1", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + }, + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "dev": true + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + }, + "source-map-support": { + "version": "0.4.18", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.18.tgz", + "integrity": "sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA==", + "dev": true, + "requires": { + "source-map": "^0.5.6" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + }, + "webdriver-manager": { + "version": "12.1.1", + "resolved": "https://registry.npmjs.org/webdriver-manager/-/webdriver-manager-12.1.1.tgz", + "integrity": "sha512-L9TEQmZs6JbMMRQI1w60mfps265/NCr0toYJl7p/R2OAk6oXAfwI6jqYP7EWae+d7Ad2S2Aj4+rzxoSjqk3ZuA==", + "dev": true, + "requires": { + "adm-zip": "^0.4.9", + "chalk": "^1.1.1", + "del": "^2.2.0", + "glob": "^7.0.3", + "ini": "^1.3.4", + "minimist": "^1.2.0", + "q": "^1.4.1", + "request": "^2.87.0", + "rimraf": "^2.5.2", + "semver": "^5.3.0", + "xml2js": "^0.4.17" + } + } + } + }, + "proxy-addr": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.4.tgz", + "integrity": "sha512-5erio2h9jp5CHGwcybmxmVqHmnCBZeewlfJ0pex+UW7Qny7OOZXTtH56TGNyBizkgiOwhJtMKrVzDTeKcySZwA==", + "dev": true, + "requires": { + "forwarded": "~0.1.2", + "ipaddr.js": "1.8.0" + } + }, + "prr": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", + "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=", + "dev": true + }, + "pseudomap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", + "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", + "dev": true + }, + "psl": { + "version": "1.1.31", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.1.31.tgz", + "integrity": "sha512-/6pt4+C+T+wZUieKR620OpzN/LlnNKuWjy1iFLQ/UG35JqHlR/89MP1d96dUfkf6Dne3TuLQzOYEYshJ+Hx8mw==", + "dev": true + }, + "public-encrypt": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", + "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==", + "dev": true, + "requires": { + "bn.js": "^4.1.0", + "browserify-rsa": "^4.0.0", + "create-hash": "^1.1.0", + "parse-asn1": "^5.0.0", + "randombytes": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "pump": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz", + "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "pumpify": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.5.1.tgz", + "integrity": "sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==", + "dev": true, + "requires": { + "duplexify": "^3.6.0", + "inherits": "^2.0.3", + "pump": "^2.0.0" + } + }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true + }, + "q": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/q/-/q-1.4.1.tgz", + "integrity": "sha1-VXBbzZPF82c1MMLCy8DCs63cKG4=", + "dev": true + }, + "qjobs": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/qjobs/-/qjobs-1.2.0.tgz", + "integrity": "sha512-8YOJEHtxpySA3fFDyCRxA+UUV+fA+rTWnuWvylOK/NCjhY+b4ocCtmu8TtsWb+mYeU+GCHf/S66KZF/AsteKHg==", + "dev": true + }, + "qs": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", + "dev": true + }, + "querystring": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", + "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=", + "dev": true + }, + "querystring-es3": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz", + "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=", + "dev": true + }, + "querystringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.1.0.tgz", + "integrity": "sha512-sluvZZ1YiTLD5jsqZcDmFyV2EwToyXZBfpoVOmktMmW+VEnhgakFHnasVph65fOjGPTWN0Nw3+XQaSeMayr0kg==", + "dev": true + }, + "randomatic": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/randomatic/-/randomatic-3.1.1.tgz", + "integrity": "sha512-TuDE5KxZ0J461RVjrJZCJc+J+zCkTb1MbH9AQUq68sMhOMcy9jLcb3BrZKgp9q9Ncltdg4QVqWrH02W2EFFVYw==", + "dev": true, + "requires": { + "is-number": "^4.0.0", + "kind-of": "^6.0.0", + "math-random": "^1.0.1" + }, + "dependencies": { + "is-number": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz", + "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==", + "dev": true + } + } + }, + "randombytes": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.0.6.tgz", + "integrity": "sha512-CIQ5OFxf4Jou6uOKe9t1AOgqpeU5fd70A8NPdHSGeYXqXsPe6peOwI0cUl88RWZ6sP1vPMV3avd/R6cZ5/sP1A==", + "dev": true, + "requires": { + "safe-buffer": "^5.1.0" + } + }, + "randomfill": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz", + "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", + "dev": true, + "requires": { + "randombytes": "^2.0.5", + "safe-buffer": "^5.1.0" + } + }, + "range-parser": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", + "integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4=", + "dev": true + }, + "raw-body": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.3.tgz", + "integrity": "sha512-9esiElv1BrZoI3rCDuOuKCBRbuApGGaDPQfjSflGxdy4oyzqghxu6klEkkVIvBje+FF0BX9coEv8KqW6X/7njw==", + "dev": true, + "requires": { + "bytes": "3.0.0", + "http-errors": "1.6.3", + "iconv-lite": "0.4.23", + "unpipe": "1.0.0" + } + }, + "raw-loader": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/raw-loader/-/raw-loader-0.5.1.tgz", + "integrity": "sha1-DD0L6u2KAclm2Xh793goElKpeao=", + "dev": true + }, + "read-cache": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", + "integrity": "sha1-5mTvMRYRZsl1HNvo28+GtftY93Q=", + "dev": true, + "requires": { + "pify": "^2.3.0" + }, + "dependencies": { + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + } + } + }, + "read-pkg": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", + "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", + "dev": true, + "requires": { + "load-json-file": "^1.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^1.0.0" + }, + "dependencies": { + "path-type": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", + "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + } + } + }, + "read-pkg-up": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", + "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", + "dev": true, + "requires": { + "find-up": "^1.0.0", + "read-pkg": "^1.0.0" + }, + "dependencies": { + "find-up": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", + "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", + "dev": true, + "requires": { + "path-exists": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + }, + "path-exists": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", + "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", + "dev": true, + "requires": { + "pinkie-promise": "^2.0.0" + } + } + } + }, + "readable-stream": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "readdirp": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", + "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.11", + "micromatch": "^3.1.10", + "readable-stream": "^2.0.2" + } + }, + "redent": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz", + "integrity": "sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94=", + "dev": true, + "optional": true, + "requires": { + "indent-string": "^2.1.0", + "strip-indent": "^1.0.1" + } + }, + "reflect-metadata": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz", + "integrity": "sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==", + "dev": true + }, + "regenerate": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.0.tgz", + "integrity": "sha512-1G6jJVDWrt0rK99kBjvEtziZNCICAuvIPkSiUFIQxVP06RCVpq3dmDo2oi6ABpYaDYaTRr67BEhL8r1wgEZZKg==", + "dev": true + }, + "regenerator-runtime": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", + "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==", + "dev": true + }, + "regex-cache": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/regex-cache/-/regex-cache-0.4.4.tgz", + "integrity": "sha512-nVIZwtCjkC9YgvWkpM55B5rBhBYRZhAaJbgcFYXXsHnbZ9UZI9nnVWYZpBlCqv9ho2eZryPnWrZGsOdPwVWXWQ==", + "dev": true, + "requires": { + "is-equal-shallow": "^0.1.3" + } + }, + "regex-not": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", + "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", + "dev": true, + "requires": { + "extend-shallow": "^3.0.2", + "safe-regex": "^1.1.0" + } + }, + "regexpu-core": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-1.0.0.tgz", + "integrity": "sha1-hqdj9Y7k18L2sQLkdkBQ3n7ZDGs=", + "dev": true, + "requires": { + "regenerate": "^1.2.1", + "regjsgen": "^0.2.0", + "regjsparser": "^0.1.4" + } + }, + "regjsgen": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.2.0.tgz", + "integrity": "sha1-bAFq3qxVT3WCP+N6wFuS1aTtsfc=", + "dev": true + }, + "regjsparser": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.1.5.tgz", + "integrity": "sha1-fuj4Tcb6eS0/0K4ijSS9lJ6tIFw=", + "dev": true, + "requires": { + "jsesc": "~0.5.0" + }, + "dependencies": { + "jsesc": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", + "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=", + "dev": true + } + } + }, + "relateurl": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", + "integrity": "sha1-VNvzd+UUQKypCkzSdGANP/LYiKk=", + "dev": true + }, + "remove-trailing-separator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", + "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=", + "dev": true + }, + "renderkid": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-2.0.2.tgz", + "integrity": "sha512-FsygIxevi1jSiPY9h7vZmBFUbAOcbYm9UwyiLNdVsLRs/5We9Ob5NMPbGYUTWiLq5L+ezlVdE0A8bbME5CWTpg==", + "dev": true, + "requires": { + "css-select": "^1.1.0", + "dom-converter": "~0.2", + "htmlparser2": "~3.3.0", + "strip-ansi": "^3.0.0", + "utila": "^0.4.0" + } + }, + "repeat-element": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz", + "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==", + "dev": true + }, + "repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", + "dev": true + }, + "repeating": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz", + "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=", + "dev": true, + "requires": { + "is-finite": "^1.0.0" + } + }, + "request": { + "version": "2.88.0", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz", + "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==", + "dev": true, + "requires": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.0", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.4.3", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" + } + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "dev": true + }, + "require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true + }, + "require-main-filename": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", + "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=", + "dev": true + }, + "requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=", + "dev": true + }, + "resolve": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", + "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=", + "dev": true + }, + "resolve-cwd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-2.0.0.tgz", + "integrity": "sha1-AKn3OHVW4nA46uIyyqNypqWbZlo=", + "dev": true, + "requires": { + "resolve-from": "^3.0.0" + } + }, + "resolve-from": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", + "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=", + "dev": true + }, + "resolve-url": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", + "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", + "dev": true + }, + "ret": { + "version": "0.1.15", + "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", + "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", + "dev": true + }, + "retry": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.10.1.tgz", + "integrity": "sha1-52OI0heZLCUnUCQdPTlW/tmNj/Q=", + "dev": true + }, + "rfdc": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.1.2.tgz", + "integrity": "sha512-92ktAgvZhBzYTIK0Mja9uen5q5J3NRVMoDkJL2VMwq6SXjVCgqvQeVP2XAaUY6HT+XpQYeLSjb3UoitBryKmdA==", + "dev": true + }, + "rimraf": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", + "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "ripemd160": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", + "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", + "dev": true, + "requires": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1" + } + }, + "run-queue": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/run-queue/-/run-queue-1.0.3.tgz", + "integrity": "sha1-6Eg5bwV9Ij8kOGkkYY4laUFh7Ec=", + "dev": true, + "requires": { + "aproba": "^1.1.1" + } + }, + "rxjs": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.2.2.tgz", + "integrity": "sha512-0MI8+mkKAXZUF9vMrEoPnaoHkfzBPP4IGwUYRJhIRJF6/w3uByO1e91bEHn8zd43RdkTMKiooYKmwz7RH6zfOQ==", + "requires": { + "tslib": "^1.9.0" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "safe-regex": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", + "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", + "dev": true, + "requires": { + "ret": "~0.1.10" + } + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true + }, + "sass-graph": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/sass-graph/-/sass-graph-2.2.4.tgz", + "integrity": "sha1-E/vWPNHK8JCLn9k0dq1DpR0eC0k=", + "dev": true, + "optional": true, + "requires": { + "glob": "^7.0.0", + "lodash": "^4.0.0", + "scss-tokenizer": "^0.2.3", + "yargs": "^7.0.0" + } + }, + "sass-loader": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-7.1.0.tgz", + "integrity": "sha512-+G+BKGglmZM2GUSfT9TLuEp6tzehHPjAMoRRItOojWIqIGPloVCMhNIQuG639eJ+y033PaGTSjLaTHts8Kw79w==", + "dev": true, + "requires": { + "clone-deep": "^2.0.1", + "loader-utils": "^1.0.1", + "lodash.tail": "^4.1.1", + "neo-async": "^2.5.0", + "pify": "^3.0.0", + "semver": "^5.5.0" + } + }, + "saucelabs": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/saucelabs/-/saucelabs-1.5.0.tgz", + "integrity": "sha512-jlX3FGdWvYf4Q3LFfFWS1QvPg3IGCGWxIc8QBFdPTbpTJnt/v17FHXYVAn7C8sHf1yUXo2c7yIM0isDryfYtHQ==", + "dev": true, + "requires": { + "https-proxy-agent": "^2.2.1" + } + }, + "sax": { + "version": "0.5.8", + "resolved": "https://registry.npmjs.org/sax/-/sax-0.5.8.tgz", + "integrity": "sha1-1HLbIo6zMcJQaw6MFVJK25OdEsE=", + "dev": true + }, + "schema-utils": { + "version": "0.4.7", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-0.4.7.tgz", + "integrity": "sha512-v/iwU6wvwGK8HbU9yi3/nhGzP0yGSuhQMzL6ySiec1FSrZZDkhm4noOSWzrNFo/jEc+SJY6jRTwuwbSXJPDUnQ==", + "dev": true, + "requires": { + "ajv": "^6.1.0", + "ajv-keywords": "^3.1.0" + } + }, + "scss-tokenizer": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/scss-tokenizer/-/scss-tokenizer-0.2.3.tgz", + "integrity": "sha1-jrBtualyMzOCTT9VMGQRSYR85dE=", + "dev": true, + "optional": true, + "requires": { + "js-base64": "^2.1.8", + "source-map": "^0.4.2" + }, + "dependencies": { + "source-map": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", + "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=", + "dev": true, + "optional": true, + "requires": { + "amdefine": ">=0.0.4" + } + } + } + }, + "select-hose": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", + "integrity": "sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo=", + "dev": true + }, + "selenium-webdriver": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/selenium-webdriver/-/selenium-webdriver-3.6.0.tgz", + "integrity": "sha512-WH7Aldse+2P5bbFBO4Gle/nuQOdVwpHMTL6raL3uuBj/vPG07k6uzt3aiahu352ONBr5xXh0hDlM3LhtXPOC4Q==", + "dev": true, + "requires": { + "jszip": "^3.1.3", + "rimraf": "^2.5.4", + "tmp": "0.0.30", + "xml2js": "^0.4.17" + }, + "dependencies": { + "tmp": { + "version": "0.0.30", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.30.tgz", + "integrity": "sha1-ckGdSovn1s51FI/YsyTlk6cRwu0=", + "dev": true, + "requires": { + "os-tmpdir": "~1.0.1" + } + } + } + }, + "selfsigned": { + "version": "1.10.4", + "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-1.10.4.tgz", + "integrity": "sha512-9AukTiDmHXGXWtWjembZ5NDmVvP2695EtpgbCsxCa68w3c88B+alqbmZ4O3hZ4VWGXeGWzEVdvqgAJD8DQPCDw==", + "dev": true, + "requires": { + "node-forge": "0.7.5" + } + }, + "semver": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz", + "integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==", + "dev": true + }, + "semver-dsl": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/semver-dsl/-/semver-dsl-1.0.1.tgz", + "integrity": "sha1-02eN5VVeimH2Ke7QJTZq5fJzQKA=", + "dev": true, + "requires": { + "semver": "^5.3.0" + } + }, + "semver-intersect": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/semver-intersect/-/semver-intersect-1.4.0.tgz", + "integrity": "sha512-d8fvGg5ycKAq0+I6nfWeCx6ffaWJCsBYU0H2Rq56+/zFePYfT8mXkB3tWBSjR5BerkHNZ5eTPIk1/LBYas35xQ==", + "dev": true, + "requires": { + "semver": "^5.0.0" + } + }, + "send": { + "version": "0.16.2", + "resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz", + "integrity": "sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw==", + "dev": true, + "requires": { + "debug": "2.6.9", + "depd": "~1.1.2", + "destroy": "~1.0.4", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "~1.6.2", + "mime": "1.4.1", + "ms": "2.0.0", + "on-finished": "~2.3.0", + "range-parser": "~1.2.0", + "statuses": "~1.4.0" + }, + "dependencies": { + "mime": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz", + "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==", + "dev": true + } + } + }, + "serialize-javascript": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-1.6.1.tgz", + "integrity": "sha512-A5MOagrPFga4YaKQSWHryl7AXvbQkEqpw4NNYMTNYUNV51bA8ABHgYFpqKx+YFFrw59xMV1qGH1R4AgoNIVgCw==", + "dev": true + }, + "serve-index": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", + "integrity": "sha1-03aNabHn2C5c4FD/9bRTvqEqkjk=", + "dev": true, + "requires": { + "accepts": "~1.3.4", + "batch": "0.6.1", + "debug": "2.6.9", + "escape-html": "~1.0.3", + "http-errors": "~1.6.2", + "mime-types": "~2.1.17", + "parseurl": "~1.3.2" + } + }, + "serve-static": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.2.tgz", + "integrity": "sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw==", + "dev": true, + "requires": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.2", + "send": "0.16.2" + } + }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", + "dev": true + }, + "set-value": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.0.tgz", + "integrity": "sha512-hw0yxk9GT/Hr5yJEYnHNKYXkIA8mVJgd9ditYZCe16ZczcaELYYcfvaXesNACk2O8O0nTiPQcQhGUQj8JLzeeg==", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-extendable": "^0.1.1", + "is-plain-object": "^2.0.3", + "split-string": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=", + "dev": true + }, + "setprototypeof": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==", + "dev": true + }, + "sha.js": { + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "shallow-clone": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-1.0.0.tgz", + "integrity": "sha512-oeXreoKR/SyNJtRJMAKPDSvd28OqEwG4eR/xc856cRGBII7gX9lvAqDxusPm0846z/w/hWYjI1NpKwJ00NHzRA==", + "dev": true, + "requires": { + "is-extendable": "^0.1.1", + "kind-of": "^5.0.0", + "mixin-object": "^2.0.1" + }, + "dependencies": { + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true + } + } + }, + "shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "dev": true, + "requires": { + "shebang-regex": "^1.0.0" + } + }, + "shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", + "dev": true + }, + "signal-exit": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", + "dev": true + }, + "slash": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz", + "integrity": "sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=", + "dev": true + }, + "slide": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/slide/-/slide-1.1.6.tgz", + "integrity": "sha1-VusCfWW00tzmyy4tMsTUr8nh1wc=", + "dev": true + }, + "smart-buffer": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.0.2.tgz", + "integrity": "sha512-JDhEpTKzXusOqXZ0BUIdH+CjFdO/CR3tLlf5CN34IypI+xMmXW1uB16OOY8z3cICbJlDAVJzNbwBhNO0wt9OAw==", + "dev": true + }, + "snapdragon": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", + "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", + "dev": true, + "requires": { + "base": "^0.11.1", + "debug": "^2.2.0", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "map-cache": "^0.2.2", + "source-map": "^0.5.6", + "source-map-resolve": "^0.5.0", + "use": "^3.1.0" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "snapdragon-node": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", + "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", + "dev": true, + "requires": { + "define-property": "^1.0.0", + "isobject": "^3.0.0", + "snapdragon-util": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "snapdragon-util": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", + "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", + "dev": true, + "requires": { + "kind-of": "^3.2.0" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "socket.io": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-2.1.1.tgz", + "integrity": "sha512-rORqq9c+7W0DAK3cleWNSyfv/qKXV99hV4tZe+gGLfBECw3XEhBy7x85F3wypA9688LKjtwO9pX9L33/xQI8yA==", + "dev": true, + "requires": { + "debug": "~3.1.0", + "engine.io": "~3.2.0", + "has-binary2": "~1.0.2", + "socket.io-adapter": "~1.1.0", + "socket.io-client": "2.1.1", + "socket.io-parser": "~3.2.0" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + } + } + }, + "socket.io-adapter": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-1.1.1.tgz", + "integrity": "sha1-KoBeihTWNyEk3ZFZrUUC+MsH8Gs=", + "dev": true + }, + "socket.io-client": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-2.1.1.tgz", + "integrity": "sha512-jxnFyhAuFxYfjqIgduQlhzqTcOEQSn+OHKVfAxWaNWa7ecP7xSNk2Dx/3UEsDcY7NcFafxvNvKPmmO7HTwTxGQ==", + "dev": true, + "requires": { + "backo2": "1.0.2", + "base64-arraybuffer": "0.1.5", + "component-bind": "1.0.0", + "component-emitter": "1.2.1", + "debug": "~3.1.0", + "engine.io-client": "~3.2.0", + "has-binary2": "~1.0.2", + "has-cors": "1.1.0", + "indexof": "0.0.1", + "object-component": "0.0.3", + "parseqs": "0.0.5", + "parseuri": "0.0.5", + "socket.io-parser": "~3.2.0", + "to-array": "0.1.4" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + } + } + }, + "socket.io-parser": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.2.0.tgz", + "integrity": "sha512-FYiBx7rc/KORMJlgsXysflWx/RIvtqZbyGLlHZvjfmPTPeuD/I8MaW7cfFrj5tRltICJdgwflhfZ3NVVbVLFQA==", + "dev": true, + "requires": { + "component-emitter": "1.2.1", + "debug": "~3.1.0", + "isarray": "2.0.1" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "isarray": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz", + "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4=", + "dev": true + } + } + }, + "sockjs": { + "version": "0.3.19", + "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.19.tgz", + "integrity": "sha512-V48klKZl8T6MzatbLlzzRNhMepEys9Y4oGFpypBFFn1gLI/QQ9HtLLyWJNbPlwGLelOVOEijUbTTJeLLI59jLw==", + "dev": true, + "requires": { + "faye-websocket": "^0.10.0", + "uuid": "^3.0.1" + } + }, + "sockjs-client": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/sockjs-client/-/sockjs-client-1.3.0.tgz", + "integrity": "sha512-R9jxEzhnnrdxLCNln0xg5uGHqMnkhPSTzUZH2eXcR03S/On9Yvoq2wyUZILRUhZCNVu2PmwWVoyuiPz8th8zbg==", + "dev": true, + "requires": { + "debug": "^3.2.5", + "eventsource": "^1.0.7", + "faye-websocket": "~0.11.1", + "inherits": "^2.0.3", + "json3": "^3.3.2", + "url-parse": "^1.4.3" + }, + "dependencies": { + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "faye-websocket": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.1.tgz", + "integrity": "sha1-8O/hjE9W5PQK/H4Gxxn9XuYYjzg=", + "dev": true, + "requires": { + "websocket-driver": ">=0.5.1" + } + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "dev": true + } + } + }, + "socks": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.2.3.tgz", + "integrity": "sha512-+2r83WaRT3PXYoO/1z+RDEBE7Z2f9YcdQnJ0K/ncXXbV5gJ6wYfNAebYFYiiUjM6E4JyXnPY8cimwyvFYHVUUA==", + "dev": true, + "requires": { + "ip": "^1.1.5", + "smart-buffer": "4.0.2" + } + }, + "socks-proxy-agent": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-4.0.1.tgz", + "integrity": "sha512-Kezx6/VBguXOsEe5oU3lXYyKMi4+gva72TwJ7pQY5JfqUx2nMk7NXA6z/mpNqIlfQjWYVfeuNvQjexiTaTn6Nw==", + "dev": true, + "requires": { + "agent-base": "~4.2.0", + "socks": "~2.2.0" + } + }, + "source-list-map": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz", + "integrity": "sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==", + "dev": true + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + }, + "source-map-loader": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/source-map-loader/-/source-map-loader-0.2.4.tgz", + "integrity": "sha512-OU6UJUty+i2JDpTItnizPrlpOIBLmQbWMuBg9q5bVtnHACqw1tn9nNwqJLbv0/00JjnJb/Ee5g5WS5vrRv7zIQ==", + "dev": true, + "requires": { + "async": "^2.5.0", + "loader-utils": "^1.1.0" + }, + "dependencies": { + "async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.2.tgz", + "integrity": "sha512-H1qVYh1MYhEEFLsP97cVKqCGo7KfCyTt6uEWqsTBr9SO84oK9Uwbyd/yCW+6rKJLHksBNUVWZDAjfS+Ccx0Bbg==", + "dev": true, + "requires": { + "lodash": "^4.17.11" + } + } + } + }, + "source-map-resolve": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.2.tgz", + "integrity": "sha512-MjqsvNwyz1s0k81Goz/9vRBe9SZdB09Bdw+/zYyO+3CuPk6fouTaxscHkgtE8jKvf01kVfl8riHzERQ/kefaSA==", + "dev": true, + "requires": { + "atob": "^2.1.1", + "decode-uri-component": "^0.2.0", + "resolve-url": "^0.2.1", + "source-map-url": "^0.4.0", + "urix": "^0.1.0" + } + }, + "source-map-support": { + "version": "0.5.9", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.9.tgz", + "integrity": "sha512-gR6Rw4MvUlYy83vP0vxoVNzM6t8MUXqNuRsuBmBHQDu1Fh6X015FrLdgoDKcNdkwGubozq0P4N0Q37UyFVr1EA==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "source-map-url": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", + "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=", + "dev": true + }, + "spdx-correct": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz", + "integrity": "sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==", + "dev": true, + "requires": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-exceptions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz", + "integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==", + "dev": true + }, + "spdx-expression-parse": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz", + "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", + "dev": true, + "requires": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-license-ids": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.3.tgz", + "integrity": "sha512-uBIcIl3Ih6Phe3XHK1NqboJLdGfwr1UN3k6wSD1dZpmPsIkb8AGNbZYJ1fOBk834+Gxy8rpfDxrS6XLEMZMY2g==", + "dev": true + }, + "spdy": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.0.tgz", + "integrity": "sha512-ot0oEGT/PGUpzf/6uk4AWLqkq+irlqHXkrdbk51oWONh3bxQmBuljxPNl66zlRRcIJStWq0QkLUCPOPjgjvU0Q==", + "dev": true, + "requires": { + "debug": "^4.1.0", + "handle-thing": "^2.0.0", + "http-deceiver": "^1.2.7", + "select-hose": "^2.0.0", + "spdy-transport": "^3.0.0" + }, + "dependencies": { + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "dev": true + } + } + }, + "spdy-transport": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-3.0.0.tgz", + "integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==", + "dev": true, + "requires": { + "debug": "^4.1.0", + "detect-node": "^2.0.4", + "hpack.js": "^2.1.6", + "obuf": "^1.1.2", + "readable-stream": "^3.0.6", + "wbuf": "^1.7.3" + }, + "dependencies": { + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "dev": true + }, + "readable-stream": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.1.1.tgz", + "integrity": "sha512-DkN66hPyqDhnIQ6Jcsvx9bFjhw214O4poMBcIMgPVpQvNy9a0e0Uhg5SqySyDKAmUlwt8LonTBz1ezOnM8pUdA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + } + } + }, + "split-string": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", + "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", + "dev": true, + "requires": { + "extend-shallow": "^3.0.0" + } + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "dev": true + }, + "sshpk": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", + "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", + "dev": true, + "requires": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + } + }, + "ssri": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-5.3.0.tgz", + "integrity": "sha512-XRSIPqLij52MtgoQavH/x/dU1qVKtWUAAZeOHsR9c2Ddi4XerFy3mc1alf+dLJKl9EUIm/Ht+EowFkTUOA6GAQ==", + "dev": true, + "requires": { + "safe-buffer": "^5.1.1" + } + }, + "static-extend": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", + "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", + "dev": true, + "requires": { + "define-property": "^0.2.5", + "object-copy": "^0.1.0" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + } + } + }, + "stats-webpack-plugin": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/stats-webpack-plugin/-/stats-webpack-plugin-0.6.2.tgz", + "integrity": "sha1-LFlJtTHgf4eojm6k3PrFOqjHWis=", + "dev": true, + "requires": { + "lodash": "^4.17.4" + } + }, + "statuses": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", + "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==", + "dev": true + }, + "stdout-stream": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/stdout-stream/-/stdout-stream-1.4.1.tgz", + "integrity": "sha512-j4emi03KXqJWcIeF8eIXkjMFN1Cmb8gUlDYGeBALLPo5qdyTfA9bOtl8m33lRoC+vFMkP3gl0WsDr6+gzxbbTA==", + "dev": true, + "optional": true, + "requires": { + "readable-stream": "^2.0.1" + } + }, + "stream-browserify": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.2.tgz", + "integrity": "sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg==", + "dev": true, + "requires": { + "inherits": "~2.0.1", + "readable-stream": "^2.0.2" + } + }, + "stream-each": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/stream-each/-/stream-each-1.2.3.tgz", + "integrity": "sha512-vlMC2f8I2u/bZGqkdfLQW/13Zihpej/7PmSiMQsbYddxuTsJp8vRe2x2FvVExZg7FaOds43ROAuFJwPR4MTZLw==", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "stream-shift": "^1.0.0" + } + }, + "stream-http": { + "version": "2.8.3", + "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-2.8.3.tgz", + "integrity": "sha512-+TSkfINHDo4J+ZobQLWiMouQYB+UVYFttRA94FpEzzJ7ZdqcL4uUUQ7WkdkI4DSozGmgBUE/a47L+38PenXhUw==", + "dev": true, + "requires": { + "builtin-status-codes": "^3.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.3.6", + "to-arraybuffer": "^1.0.0", + "xtend": "^4.0.0" + } + }, + "stream-shift": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.0.tgz", + "integrity": "sha1-1cdSgl5TZ+eG944Y5EXqIjoVWVI=", + "dev": true + }, + "streamroller": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/streamroller/-/streamroller-0.7.0.tgz", + "integrity": "sha512-WREzfy0r0zUqp3lGO096wRuUp7ho1X6uo/7DJfTlEi0Iv/4gT7YHqXDjKC2ioVGBZtE8QzsQD9nx1nIuoZ57jQ==", + "dev": true, + "requires": { + "date-format": "^1.2.0", + "debug": "^3.1.0", + "mkdirp": "^0.5.1", + "readable-stream": "^2.3.0" + }, + "dependencies": { + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "dev": true + } + } + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "dev": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "strip-bom": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", + "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", + "dev": true, + "requires": { + "is-utf8": "^0.2.0" + } + }, + "strip-eof": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", + "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", + "dev": true + }, + "strip-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz", + "integrity": "sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI=", + "dev": true, + "optional": true, + "requires": { + "get-stdin": "^4.0.1" + } + }, + "style-loader": { + "version": "0.21.0", + "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-0.21.0.tgz", + "integrity": "sha512-T+UNsAcl3Yg+BsPKs1vd22Fr8sVT+CJMtzqc6LEw9bbJZb43lm9GoeIfUcDEefBSWC0BhYbcdupV1GtI4DGzxg==", + "dev": true, + "requires": { + "loader-utils": "^1.1.0", + "schema-utils": "^0.4.5" + } + }, + "stylus": { + "version": "0.54.5", + "resolved": "https://registry.npmjs.org/stylus/-/stylus-0.54.5.tgz", + "integrity": "sha1-QrlWCTHKcJDOhRWnmLqeaqPW3Hk=", + "dev": true, + "requires": { + "css-parse": "1.7.x", + "debug": "*", + "glob": "7.0.x", + "mkdirp": "0.5.x", + "sax": "0.5.x", + "source-map": "0.1.x" + }, + "dependencies": { + "glob": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.0.6.tgz", + "integrity": "sha1-IRuvr0nlJbjNkyYNFKsTYVKz9Xo=", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.2", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "source-map": { + "version": "0.1.43", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.1.43.tgz", + "integrity": "sha1-wkvBRspRfBRx9drL4lcbK3+eM0Y=", + "dev": true, + "requires": { + "amdefine": ">=0.0.4" + } + } + } + }, + "stylus-loader": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/stylus-loader/-/stylus-loader-3.0.2.tgz", + "integrity": "sha512-+VomPdZ6a0razP+zinir61yZgpw2NfljeSsdUF5kJuEzlo3khXhY19Fn6l8QQz1GRJGtMCo8nG5C04ePyV7SUA==", + "dev": true, + "requires": { + "loader-utils": "^1.0.2", + "lodash.clonedeep": "^4.5.0", + "when": "~3.6.x" + } + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + }, + "symbol-observable": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz", + "integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==", + "dev": true + }, + "tapable": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.1.tgz", + "integrity": "sha512-9I2ydhj8Z9veORCw5PRm4u9uebCn0mcCa6scWoNcbZ6dAtoo2618u9UUzxgmsCOreJpqDDuv61LvwofW7hLcBA==", + "dev": true + }, + "tar": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tar/-/tar-2.2.1.tgz", + "integrity": "sha1-jk0qJWwOIYXGsYrWlK7JaLg8sdE=", + "dev": true, + "optional": true, + "requires": { + "block-stream": "*", + "fstream": "^1.0.2", + "inherits": "2" + } + }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", + "dev": true + }, + "through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "dev": true, + "requires": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, + "thunky": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.0.3.tgz", + "integrity": "sha512-YwT8pjmNcAXBZqrubu22P4FYsh2D4dxRmnWBOL8Jk8bUcRUtc5326kx32tuTmFDAZtLOGEVNl8POAR8j896Iow==", + "dev": true + }, + "timers-browserify": { + "version": "2.0.10", + "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.10.tgz", + "integrity": "sha512-YvC1SV1XdOUaL6gx5CoGroT3Gu49pK9+TZ38ErPldOWW4j49GI1HKs9DV+KGq/w6y+LZ72W1c8cKz2vzY+qpzg==", + "dev": true, + "requires": { + "setimmediate": "^1.0.4" + } + }, + "tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dev": true, + "requires": { + "os-tmpdir": "~1.0.2" + } + }, + "to-array": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/to-array/-/to-array-0.1.4.tgz", + "integrity": "sha1-F+bBH3PdTz10zaek/zI46a2b+JA=", + "dev": true + }, + "to-arraybuffer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz", + "integrity": "sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M=", + "dev": true + }, + "to-fast-properties": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz", + "integrity": "sha1-uDVx+k2MJbguIxsG46MFXeTKGkc=", + "dev": true + }, + "to-object-path": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", + "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "to-regex": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", + "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", + "dev": true, + "requires": { + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "regex-not": "^1.0.2", + "safe-regex": "^1.1.0" + } + }, + "to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "dev": true, + "requires": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + } + }, + "toposort": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/toposort/-/toposort-1.0.7.tgz", + "integrity": "sha1-LmhELZ9k7HILjMieZEOsbKqVACk=", + "dev": true + }, + "tough-cookie": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz", + "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==", + "dev": true, + "requires": { + "psl": "^1.1.24", + "punycode": "^1.4.1" + }, + "dependencies": { + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", + "dev": true + } + } + }, + "tree-kill": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.1.tgz", + "integrity": "sha512-4hjqbObwlh2dLyW4tcz0Ymw0ggoaVDMveUB9w8kFSQScdRLo0gxO9J7WFcUBo+W3C1TLdFIEwNOWebgZZ0RH9Q==", + "dev": true + }, + "trim-newlines": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz", + "integrity": "sha1-WIeWa7WCpFA6QetST301ARgVphM=", + "dev": true, + "optional": true + }, + "trim-right": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz", + "integrity": "sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM=", + "dev": true + }, + "true-case-path": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/true-case-path/-/true-case-path-1.0.3.tgz", + "integrity": "sha512-m6s2OdQe5wgpFMC+pAJ+q9djG82O2jcHPOI6RNg1yy9rCYR+WD6Nbpl32fDpfC56nirdRy+opFa/Vk7HYhqaew==", + "dev": true, + "optional": true, + "requires": { + "glob": "^7.1.2" + } + }, + "ts-node": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-7.0.1.tgz", + "integrity": "sha512-BVwVbPJRspzNh2yfslyT1PSbl5uIk03EZlb493RKHN4qej/D06n1cEhjlOJG69oFsE7OT8XjpTUcYf6pKTLMhw==", + "dev": true, + "requires": { + "arrify": "^1.0.0", + "buffer-from": "^1.1.0", + "diff": "^3.1.0", + "make-error": "^1.1.1", + "minimist": "^1.2.0", + "mkdirp": "^0.5.1", + "source-map-support": "^0.5.6", + "yn": "^2.0.0" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "dev": true + } + } + }, + "tsickle": { + "version": "0.32.1", + "resolved": "https://registry.npmjs.org/tsickle/-/tsickle-0.32.1.tgz", + "integrity": "sha512-JW9j+W0SaMSZGejIFZBk0AiPfnhljK3oLx5SaqxrJhjlvzFyPml5zqG1/PuScUj6yTe1muEqwk5CnDK0cOZmKw==", + "dev": true, + "requires": { + "jasmine-diff": "^0.1.3", + "minimist": "^1.2.0", + "mkdirp": "^0.5.1", + "source-map": "^0.6.0", + "source-map-support": "^0.5.0" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "tslib": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz", + "integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ==" + }, + "tslint": { + "version": "5.11.0", + "resolved": "https://registry.npmjs.org/tslint/-/tslint-5.11.0.tgz", + "integrity": "sha1-mPMMAurjzecAYgHkwzywi0hYHu0=", + "dev": true, + "requires": { + "babel-code-frame": "^6.22.0", + "builtin-modules": "^1.1.1", + "chalk": "^2.3.0", + "commander": "^2.12.1", + "diff": "^3.2.0", + "glob": "^7.1.1", + "js-yaml": "^3.7.0", + "minimatch": "^3.0.4", + "resolve": "^1.3.2", + "semver": "^5.3.0", + "tslib": "^1.8.0", + "tsutils": "^2.27.2" + }, + "dependencies": { + "resolve": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.10.0.tgz", + "integrity": "sha512-3sUr9aq5OfSg2S9pNtPA9hL1FVEAjvfOC4leW0SNf/mpnaakz2a9femSd6LqAww2RaFctwyf1lCqnTHuF1rxDg==", + "dev": true, + "requires": { + "path-parse": "^1.0.6" + } + } + } + }, + "tsutils": { + "version": "2.29.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.29.0.tgz", + "integrity": "sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA==", + "dev": true, + "requires": { + "tslib": "^1.8.1" + } + }, + "tty-browserify": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", + "integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=", + "dev": true + }, + "tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "dev": true, + "requires": { + "safe-buffer": "^5.0.1" + } + }, + "tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", + "dev": true + }, + "type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", + "dev": true, + "requires": { + "prelude-ls": "~1.1.2" + } + }, + "type-is": { + "version": "1.6.16", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.16.tgz", + "integrity": "sha512-HRkVv/5qY2G6I8iab9cI7v1bOIdhm94dVjQCPFElW9W+3GeDOSHmy2EBYe4VTApuzolPcmgFTN3ftVJRKR2J9Q==", + "dev": true, + "requires": { + "media-typer": "0.3.0", + "mime-types": "~2.1.18" + } + }, + "typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", + "dev": true + }, + "typescript": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-2.9.2.tgz", + "integrity": "sha512-Gr4p6nFNaoufRIY4NMdpQRNmgxVIGMs4Fcu/ujdYk3nAZqk7supzBE9idmvfZIlH/Cuj//dvi+019qEue9lV0w==", + "dev": true + }, + "uglify-js": { + "version": "3.4.9", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.4.9.tgz", + "integrity": "sha512-8CJsbKOtEbnJsTyv6LE6m6ZKniqMiFWmm9sRbopbkGs3gMPPfd3Fh8iIA4Ykv5MgaTbqHr4BaoGLJLZNhsrW1Q==", + "dev": true, + "requires": { + "commander": "~2.17.1", + "source-map": "~0.6.1" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "uglifyjs-webpack-plugin": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/uglifyjs-webpack-plugin/-/uglifyjs-webpack-plugin-1.3.0.tgz", + "integrity": "sha512-ovHIch0AMlxjD/97j9AYovZxG5wnHOPkL7T1GKochBADp/Zwc44pEWNqpKl1Loupp1WhFg7SlYmHZRUfdAacgw==", + "dev": true, + "requires": { + "cacache": "^10.0.4", + "find-cache-dir": "^1.0.0", + "schema-utils": "^0.4.5", + "serialize-javascript": "^1.4.0", + "source-map": "^0.6.1", + "uglify-es": "^3.3.4", + "webpack-sources": "^1.1.0", + "worker-farm": "^1.5.2" + }, + "dependencies": { + "commander": { + "version": "2.13.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.13.0.tgz", + "integrity": "sha512-MVuS359B+YzaWqjCL/c+22gfryv+mCBPHAv3zyVI2GN8EY6IRP8VwtasXn8jyyhvvq84R4ImN1OKRtcbIasjYA==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "uglify-es": { + "version": "3.3.9", + "resolved": "https://registry.npmjs.org/uglify-es/-/uglify-es-3.3.9.tgz", + "integrity": "sha512-r+MU0rfv4L/0eeW3xZrd16t4NZfK8Ld4SWVglYBb7ez5uXFWHuVRs6xCTrf1yirs9a4j4Y27nn7SRfO6v67XsQ==", + "dev": true, + "requires": { + "commander": "~2.13.0", + "source-map": "~0.6.1" + } + } + } + }, + "ultron": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.1.1.tgz", + "integrity": "sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og==", + "dev": true + }, + "union-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.0.tgz", + "integrity": "sha1-XHHDTLW61dzr4+oM0IIHulqhrqQ=", + "dev": true, + "requires": { + "arr-union": "^3.1.0", + "get-value": "^2.0.6", + "is-extendable": "^0.1.1", + "set-value": "^0.4.3" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + }, + "set-value": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-0.4.3.tgz", + "integrity": "sha1-fbCPnT0i3H945Trzw79GZuzfzPE=", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-extendable": "^0.1.1", + "is-plain-object": "^2.0.1", + "to-object-path": "^0.3.0" + } + } + } + }, + "unique-filename": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", + "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==", + "dev": true, + "requires": { + "unique-slug": "^2.0.0" + } + }, + "unique-slug": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.1.tgz", + "integrity": "sha512-n9cU6+gITaVu7VGj1Z8feKMmfAjEAQGhwD9fE3zvpRRa0wEIx8ODYkVGfSc94M2OX00tUFV8wH3zYbm1I8mxFg==", + "dev": true, + "requires": { + "imurmurhash": "^0.1.4" + } + }, + "unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", + "dev": true + }, + "unset-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", + "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", + "dev": true, + "requires": { + "has-value": "^0.3.1", + "isobject": "^3.0.0" + }, + "dependencies": { + "has-value": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", + "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", + "dev": true, + "requires": { + "get-value": "^2.0.3", + "has-values": "^0.1.4", + "isobject": "^2.0.0" + }, + "dependencies": { + "isobject": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", + "dev": true, + "requires": { + "isarray": "1.0.0" + } + } + } + }, + "has-values": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", + "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=", + "dev": true + } + } + }, + "upath": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/upath/-/upath-1.1.0.tgz", + "integrity": "sha512-bzpH/oBhoS/QI/YtbkqCg6VEiPYjSZtrHQM6/QnJS6OL9pKUFLqb3aFh4Scvwm45+7iAgiMkLhSbaZxUqmrprw==", + "dev": true + }, + "upper-case": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/upper-case/-/upper-case-1.1.3.tgz", + "integrity": "sha1-9rRQHC7EzdJrp4vnIilh3ndiFZg=", + "dev": true + }, + "uri-js": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-3.0.2.tgz", + "integrity": "sha1-+QuFhQf4HepNz7s8TD2/orVX+qo=", + "dev": true, + "requires": { + "punycode": "^2.1.0" + } + }, + "urix": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", + "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", + "dev": true + }, + "url": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", + "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", + "dev": true, + "requires": { + "punycode": "1.3.2", + "querystring": "0.2.0" + }, + "dependencies": { + "punycode": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", + "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=", + "dev": true + } + } + }, + "url-loader": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/url-loader/-/url-loader-1.1.2.tgz", + "integrity": "sha512-dXHkKmw8FhPqu8asTc1puBfe3TehOCo2+RmOOev5suNCIYBcT626kxiWg1NBVkwc4rO8BGa7gP70W7VXuqHrjg==", + "dev": true, + "requires": { + "loader-utils": "^1.1.0", + "mime": "^2.0.3", + "schema-utils": "^1.0.0" + }, + "dependencies": { + "mime": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.0.tgz", + "integrity": "sha512-ikBcWwyqXQSHKtciCcctu9YfPbFYZ4+gbHEmE0Q8jzcTYQg5dHCr3g2wwAZjPoJfQVXZq6KXAjpXOTf5/cjT7w==", + "dev": true + }, + "schema-utils": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", + "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", + "dev": true, + "requires": { + "ajv": "^6.1.0", + "ajv-errors": "^1.0.0", + "ajv-keywords": "^3.1.0" + } + } + } + }, + "url-parse": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.4.4.tgz", + "integrity": "sha512-/92DTTorg4JjktLNLe6GPS2/RvAd/RGr6LuktmWSMLEOa6rjnlrFXNgSbSmkNvCoL2T028A0a1JaJLzRMlFoHg==", + "dev": true, + "requires": { + "querystringify": "^2.0.0", + "requires-port": "^1.0.0" + } + }, + "use": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", + "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", + "dev": true + }, + "useragent": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/useragent/-/useragent-2.2.1.tgz", + "integrity": "sha1-z1k+9PLRdYdei7ZY6pLhik/QbY4=", + "dev": true, + "requires": { + "lru-cache": "2.2.x", + "tmp": "0.0.x" + }, + "dependencies": { + "lru-cache": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.2.4.tgz", + "integrity": "sha1-bGWGGb7PFAMdDQtZSxYELOTcBj0=", + "dev": true + } + } + }, + "util": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/util/-/util-0.11.1.tgz", + "integrity": "sha512-HShAsny+zS2TZfaXxD9tYj4HQGlBezXZMZuM/S5PKLLoZkShZiGk9o5CzukI1LVHZvjdvZ2Sj1aW/Ndn2NB/HQ==", + "dev": true, + "requires": { + "inherits": "2.0.3" + } + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "dev": true + }, + "util.promisify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.0.tgz", + "integrity": "sha512-i+6qA2MPhvoKLuxnJNpXAGhg7HphQOSUq2LKMZD0m15EiskXUkMvKdF4Uui0WYeCUGea+o2cw/ZuwehtfsrNkA==", + "dev": true, + "requires": { + "define-properties": "^1.1.2", + "object.getownpropertydescriptors": "^2.0.3" + } + }, + "utila": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/utila/-/utila-0.4.0.tgz", + "integrity": "sha1-ihagXURWV6Oupe7MWxKk+lN5dyw=", + "dev": true + }, + "utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=", + "dev": true + }, + "uuid": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", + "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==", + "dev": true + }, + "validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "requires": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "validate-npm-package-name": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-3.0.0.tgz", + "integrity": "sha1-X6kS2B630MdK/BQN5zF/DKffQ34=", + "dev": true, + "requires": { + "builtins": "^1.0.3" + } + }, + "vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=", + "dev": true + }, + "verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "dev": true, + "requires": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + }, + "vm-browserify": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-0.0.4.tgz", + "integrity": "sha1-XX6kW7755Kb/ZflUOOCofDV9WnM=", + "dev": true, + "requires": { + "indexof": "0.0.1" + } + }, + "void-elements": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-2.0.1.tgz", + "integrity": "sha1-wGavtYK7HLQSjWDqkjkulNXp2+w=", + "dev": true + }, + "watchpack": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.6.0.tgz", + "integrity": "sha512-i6dHe3EyLjMmDlU1/bGQpEw25XSjkJULPuAVKCbNRefQVq48yXKUpwg538F7AZTf9kyr57zj++pQFltUa5H7yA==", + "dev": true, + "requires": { + "chokidar": "^2.0.2", + "graceful-fs": "^4.1.2", + "neo-async": "^2.5.0" + } + }, + "wbuf": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz", + "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==", + "dev": true, + "requires": { + "minimalistic-assert": "^1.0.0" + } + }, + "webdriver-js-extender": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/webdriver-js-extender/-/webdriver-js-extender-2.1.0.tgz", + "integrity": "sha512-lcUKrjbBfCK6MNsh7xaY2UAUmZwe+/ib03AjVOpFobX4O7+83BUveSrLfU0Qsyb1DaKJdQRbuU+kM9aZ6QUhiQ==", + "dev": true, + "requires": { + "@types/selenium-webdriver": "^3.0.0", + "selenium-webdriver": "^3.0.1" + } + }, + "webpack": { + "version": "4.16.4", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.16.4.tgz", + "integrity": "sha512-RqUfwp4qMqv3oFwBQQOoK69C2tdu2FHJEqPABPqgjGDvOIOLqkTOhmmdJjpiRabzNAAH1ahmkA3z4xowlHN+VA==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.5.13", + "@webassemblyjs/helper-module-context": "1.5.13", + "@webassemblyjs/wasm-edit": "1.5.13", + "@webassemblyjs/wasm-opt": "1.5.13", + "@webassemblyjs/wasm-parser": "1.5.13", + "acorn": "^5.6.2", + "acorn-dynamic-import": "^3.0.0", + "ajv": "^6.1.0", + "ajv-keywords": "^3.1.0", + "chrome-trace-event": "^1.0.0", + "enhanced-resolve": "^4.1.0", + "eslint-scope": "^4.0.0", + "json-parse-better-errors": "^1.0.2", + "loader-runner": "^2.3.0", + "loader-utils": "^1.1.0", + "memory-fs": "~0.4.1", + "micromatch": "^3.1.8", + "mkdirp": "~0.5.0", + "neo-async": "^2.5.0", + "node-libs-browser": "^2.0.0", + "schema-utils": "^0.4.4", + "tapable": "^1.0.0", + "uglifyjs-webpack-plugin": "^1.2.4", + "watchpack": "^1.5.0", + "webpack-sources": "^1.0.1" + } + }, + "webpack-core": { + "version": "0.6.9", + "resolved": "https://registry.npmjs.org/webpack-core/-/webpack-core-0.6.9.tgz", + "integrity": "sha1-/FcViMhVjad76e+23r3Fo7FyvcI=", + "dev": true, + "requires": { + "source-list-map": "~0.1.7", + "source-map": "~0.4.1" + }, + "dependencies": { + "source-list-map": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-0.1.8.tgz", + "integrity": "sha1-xVCyq1Qn9rPyH1r+rYjE9Vh7IQY=", + "dev": true + }, + "source-map": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", + "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=", + "dev": true, + "requires": { + "amdefine": ">=0.0.4" + } + } + } + }, + "webpack-dev-middleware": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-3.4.0.tgz", + "integrity": "sha512-Q9Iyc0X9dP9bAsYskAVJ/hmIZZQwf/3Sy4xCAZgL5cUkjZmUZLt4l5HpbST/Pdgjn3u6pE7u5OdGd1apgzRujA==", + "dev": true, + "requires": { + "memory-fs": "~0.4.1", + "mime": "^2.3.1", + "range-parser": "^1.0.3", + "webpack-log": "^2.0.0" + }, + "dependencies": { + "mime": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.0.tgz", + "integrity": "sha512-ikBcWwyqXQSHKtciCcctu9YfPbFYZ4+gbHEmE0Q8jzcTYQg5dHCr3g2wwAZjPoJfQVXZq6KXAjpXOTf5/cjT7w==", + "dev": true + } + } + }, + "webpack-dev-server": { + "version": "3.1.14", + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-3.1.14.tgz", + "integrity": "sha512-mGXDgz5SlTxcF3hUpfC8hrQ11yhAttuUQWf1Wmb+6zo3x6rb7b9mIfuQvAPLdfDRCGRGvakBWHdHOa0I9p/EVQ==", + "dev": true, + "requires": { + "ansi-html": "0.0.7", + "bonjour": "^3.5.0", + "chokidar": "^2.0.0", + "compression": "^1.5.2", + "connect-history-api-fallback": "^1.3.0", + "debug": "^3.1.0", + "del": "^3.0.0", + "express": "^4.16.2", + "html-entities": "^1.2.0", + "http-proxy-middleware": "~0.18.0", + "import-local": "^2.0.0", + "internal-ip": "^3.0.1", + "ip": "^1.1.5", + "killable": "^1.0.0", + "loglevel": "^1.4.1", + "opn": "^5.1.0", + "portfinder": "^1.0.9", + "schema-utils": "^1.0.0", + "selfsigned": "^1.9.1", + "semver": "^5.6.0", + "serve-index": "^1.7.2", + "sockjs": "0.3.19", + "sockjs-client": "1.3.0", + "spdy": "^4.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^5.1.0", + "url": "^0.11.0", + "webpack-dev-middleware": "3.4.0", + "webpack-log": "^2.0.0", + "yargs": "12.0.2" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + }, + "camelcase": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", + "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", + "dev": true + }, + "cliui": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz", + "integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==", + "dev": true, + "requires": { + "string-width": "^2.1.1", + "strip-ansi": "^4.0.0", + "wrap-ansi": "^2.0.0" + }, + "dependencies": { + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + } + } + } + }, + "cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "dev": true, + "requires": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "decamelize": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-2.0.0.tgz", + "integrity": "sha512-Ikpp5scV3MSYxY39ymh45ZLEecsTdv/Xj2CaQfI8RLMuwi7XvjX9H/fhraiSuU+C5w5NTDu4ZU72xNiZnurBPg==", + "dev": true, + "requires": { + "xregexp": "4.0.0" + } + }, + "execa": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", + "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", + "dev": true, + "requires": { + "cross-spawn": "^6.0.0", + "get-stream": "^4.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + } + }, + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "requires": { + "locate-path": "^3.0.0" + } + }, + "get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "dev": true, + "requires": { + "pump": "^3.0.0" + } + }, + "invert-kv": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-2.0.0.tgz", + "integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "lcid": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lcid/-/lcid-2.0.0.tgz", + "integrity": "sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==", + "dev": true, + "requires": { + "invert-kv": "^2.0.0" + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "dev": true + }, + "os-locale": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.1.0.tgz", + "integrity": "sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==", + "dev": true, + "requires": { + "execa": "^1.0.0", + "lcid": "^2.0.0", + "mem": "^4.0.0" + } + }, + "p-limit": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.1.0.tgz", + "integrity": "sha512-NhURkNcrVB+8hNfLuysU8enY5xn2KXphsHBaC2YmRNTZRc7RWusw6apSpdEj3jo4CMb6W9nrF6tTnsJsJeyu6g==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "requires": { + "p-limit": "^2.0.0" + } + }, + "p-try": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.0.0.tgz", + "integrity": "sha512-hMp0onDKIajHfIkdRk3P4CdCmErkYAxxDtP3Wx/4nZ3aGlau2VKh3mZpcuFkH27WQkL/3WBCPOktzA9ZOAnMQQ==", + "dev": true + }, + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "schema-utils": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", + "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", + "dev": true, + "requires": { + "ajv": "^6.1.0", + "ajv-errors": "^1.0.0", + "ajv-keywords": "^3.1.0" + } + }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "requires": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + }, + "dependencies": { + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + } + } + } + }, + "which-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", + "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", + "dev": true + }, + "yargs": { + "version": "12.0.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-12.0.2.tgz", + "integrity": "sha512-e7SkEx6N6SIZ5c5H22RTZae61qtn3PYUE8JYbBFlK9sYmh3DMQ6E5ygtaG/2BW0JZi4WGgTR2IV5ChqlqrDGVQ==", + "dev": true, + "requires": { + "cliui": "^4.0.0", + "decamelize": "^2.0.0", + "find-up": "^3.0.0", + "get-caller-file": "^1.0.1", + "os-locale": "^3.0.0", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^2.0.0", + "which-module": "^2.0.0", + "y18n": "^3.2.1 || ^4.0.0", + "yargs-parser": "^10.1.0" + } + }, + "yargs-parser": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-10.1.0.tgz", + "integrity": "sha512-VCIyR1wJoEBZUqk5PA+oOBF6ypbwh5aNB3I50guxAL/quggdfs4TtNHQrSazFA3fYZ+tEqfs0zIGlv0c/rgjbQ==", + "dev": true, + "requires": { + "camelcase": "^4.1.0" + } + } + } + }, + "webpack-log": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/webpack-log/-/webpack-log-2.0.0.tgz", + "integrity": "sha512-cX8G2vR/85UYG59FgkoMamwHUIkSSlV3bBMRsbxVXVUk2j6NleCKjQ/WE9eYg9WY4w25O9w8wKP4rzNZFmUcUg==", + "dev": true, + "requires": { + "ansi-colors": "^3.0.0", + "uuid": "^3.3.2" + } + }, + "webpack-merge": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-4.1.4.tgz", + "integrity": "sha512-TmSe1HZKeOPey3oy1Ov2iS3guIZjWvMT2BBJDzzT5jScHTjVC3mpjJofgueEzaEd6ibhxRDD6MIblDr8tzh8iQ==", + "dev": true, + "requires": { + "lodash": "^4.17.5" + } + }, + "webpack-sources": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.3.0.tgz", + "integrity": "sha512-OiVgSrbGu7NEnEvQJJgdSFPl2qWKkWq5lHMhgiToIiN9w34EBnjYzSYs+VbL5KoYiLNtFFa7BZIKxRED3I32pA==", + "dev": true, + "requires": { + "source-list-map": "^2.0.0", + "source-map": "~0.6.1" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "webpack-subresource-integrity": { + "version": "1.1.0-rc.4", + "resolved": "https://registry.npmjs.org/webpack-subresource-integrity/-/webpack-subresource-integrity-1.1.0-rc.4.tgz", + "integrity": "sha1-xcTj1pD50vZKlVDgeodn+Xlqpdg=", + "dev": true, + "requires": { + "webpack-core": "^0.6.8" + } + }, + "websocket-driver": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.0.tgz", + "integrity": "sha1-DK+dLXVdk67gSdS90NP+LMoqJOs=", + "dev": true, + "requires": { + "http-parser-js": ">=0.4.0", + "websocket-extensions": ">=0.1.1" + } + }, + "websocket-extensions": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.3.tgz", + "integrity": "sha512-nqHUnMXmBzT0w570r2JpJxfiSD1IzoI+HGVdd3aZ0yNi3ngvQ4jv1dtHt5VGxfI2yj5yqImPhOK4vmIh2xMbGg==", + "dev": true + }, + "when": { + "version": "3.6.4", + "resolved": "https://registry.npmjs.org/when/-/when-3.6.4.tgz", + "integrity": "sha1-RztRfsFZ4rhQBUl6E5g/CVQS404=", + "dev": true + }, + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "which-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-1.0.0.tgz", + "integrity": "sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8=", + "dev": true, + "optional": true + }, + "wide-align": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", + "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", + "dev": true, + "requires": { + "string-width": "^1.0.2 || 2" + } + }, + "wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", + "dev": true + }, + "worker-farm": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/worker-farm/-/worker-farm-1.6.0.tgz", + "integrity": "sha512-6w+3tHbM87WnSWnENBUvA2pxJPLhQUg5LKwUQHq3r+XPhIM+Gh2R5ycbwPCyuGbNg+lPgdcnQUhuC02kJCvffQ==", + "dev": true, + "requires": { + "errno": "~0.1.7" + } + }, + "wrap-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", + "dev": true, + "requires": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1" + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "ws": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-3.3.3.tgz", + "integrity": "sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA==", + "dev": true, + "requires": { + "async-limiter": "~1.0.0", + "safe-buffer": "~5.1.0", + "ultron": "~1.1.0" + } + }, + "xml2js": { + "version": "0.4.19", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.19.tgz", + "integrity": "sha512-esZnJZJOiJR9wWKMyuvSE1y6Dq5LCuJanqhxslH2bxM6duahNZ+HMpCLhBQGZkbX6xRf8x1Y2eJlgt2q3qo49Q==", + "dev": true, + "requires": { + "sax": ">=0.6.0", + "xmlbuilder": "~9.0.1" + }, + "dependencies": { + "sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", + "dev": true + } + } + }, + "xmlbuilder": { + "version": "9.0.7", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz", + "integrity": "sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0=", + "dev": true + }, + "xmlhttprequest-ssl": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.5.tgz", + "integrity": "sha1-wodrBhaKrcQOV9l+gRkayPQ5iz4=", + "dev": true + }, + "xregexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/xregexp/-/xregexp-4.0.0.tgz", + "integrity": "sha512-PHyM+sQouu7xspQQwELlGwwd05mXUFqwFYfqPO0cC7x4fxyHnnuetmQr6CjJiafIDoH4MogHb9dOoJzR/Y4rFg==", + "dev": true + }, + "xtend": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", + "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=", + "dev": true + }, + "xxhashjs": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/xxhashjs/-/xxhashjs-0.2.2.tgz", + "integrity": "sha512-AkTuIuVTET12tpsVIQo+ZU6f/qDmKuRUcjaqR+OIvm+aCBsZ95i7UVY5WJ9TMsSaZ0DA2WxoZ4acu0sPH+OKAw==", + "dev": true, + "requires": { + "cuint": "^0.2.2" + } + }, + "y18n": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", + "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", + "dev": true + }, + "yallist": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", + "dev": true + }, + "yargs": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-7.1.0.tgz", + "integrity": "sha1-a6MY6xaWFyf10oT46gA+jWFU0Mg=", + "dev": true, + "optional": true, + "requires": { + "camelcase": "^3.0.0", + "cliui": "^3.2.0", + "decamelize": "^1.1.1", + "get-caller-file": "^1.0.1", + "os-locale": "^1.4.0", + "read-pkg-up": "^1.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^1.0.2", + "which-module": "^1.0.0", + "y18n": "^3.2.1", + "yargs-parser": "^5.0.0" + }, + "dependencies": { + "camelcase": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", + "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=", + "dev": true, + "optional": true + }, + "y18n": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", + "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=", + "dev": true, + "optional": true + } + } + }, + "yargs-parser": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-5.0.0.tgz", + "integrity": "sha1-J17PDX/+Bcd+ZOfIbkzZS/DhIoo=", + "dev": true, + "optional": true, + "requires": { + "camelcase": "^3.0.0" + }, + "dependencies": { + "camelcase": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", + "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=", + "dev": true, + "optional": true + } + } + }, + "yeast": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz", + "integrity": "sha1-AI4G2AlDIMNy28L47XagymyKxBk=", + "dev": true + }, + "yn": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yn/-/yn-2.0.0.tgz", + "integrity": "sha1-5a2ryKz0CPY4X8dklWhMiOavaJo=", + "dev": true + }, + "zone.js": { + "version": "0.8.29", + "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.8.29.tgz", + "integrity": "sha512-mla2acNCMkWXBD+c+yeUrBUrzOxYMNFdQ6FGfigGGtEVBPJx07BQeJekjt9DmH1FtZek4E9rE1eRR9qQpxACOQ==" + } + } +} diff --git "a/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/package.json" "b/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/package.json" new file mode 100644 index 00000000..552ba76a --- /dev/null +++ "b/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/package.json" @@ -0,0 +1,49 @@ +{ + "name": "books", + "version": "0.0.0", + "scripts": { + "ng": "ng", + "start": "ng serve", + "build": "ng build", + "test": "ng test", + "lint": "ng lint", + "e2e": "ng e2e" + }, + "private": true, + "dependencies": { + "@angular/animations": "^6.1.0", + "@angular/common": "^6.1.0", + "@angular/compiler": "^6.1.0", + "@angular/core": "^6.1.0", + "@angular/forms": "^6.1.0", + "@angular/http": "^6.1.0", + "@angular/platform-browser": "^6.1.0", + "@angular/platform-browser-dynamic": "^6.1.0", + "@angular/router": "^6.1.0", + "angular-in-memory-web-api": "^0.8.0", + "core-js": "^2.5.4", + "rxjs": "~6.2.0", + "zone.js": "~0.8.26" + }, + "devDependencies": { + "@angular-devkit/build-angular": "~0.8.0", + "@angular/cli": "~6.2.4", + "@angular/compiler-cli": "^6.1.0", + "@angular/language-service": "^6.1.0", + "@types/jasmine": "~2.8.8", + "@types/jasminewd2": "~2.0.3", + "@types/node": "~8.9.4", + "codelyzer": "~4.3.0", + "jasmine-core": "~2.99.1", + "jasmine-spec-reporter": "~4.2.1", + "karma": "~3.0.0", + "karma-chrome-launcher": "~2.2.0", + "karma-coverage-istanbul-reporter": "~2.0.1", + "karma-jasmine": "~1.1.2", + "karma-jasmine-html-reporter": "^0.2.2", + "protractor": "~5.4.0", + "ts-node": "~7.0.0", + "tslint": "~5.11.0", + "typescript": "~2.9.2" + } +} diff --git "a/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/src/app/add/add.component.css" "b/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/src/app/add/add.component.css" new file mode 100644 index 00000000..ba0564ca --- /dev/null +++ "b/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/src/app/add/add.component.css" @@ -0,0 +1,14 @@ +.add{ + width: 260px; + margin: 0 auto; +} +.add label{ + display: block; + margin: 5px 0; +} +.add label input{ + float: right; +} +.add div{ + text-align: center; +} \ No newline at end of file diff --git "a/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/src/app/add/add.component.html" "b/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/src/app/add/add.component.html" new file mode 100644 index 00000000..945c2e08 --- /dev/null +++ "b/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/src/app/add/add.component.html" @@ -0,0 +1,16 @@ +
+

添加书本:

+ + + + +
+
\ No newline at end of file diff --git "a/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/src/app/add/add.component.spec.ts" "b/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/src/app/add/add.component.spec.ts" new file mode 100644 index 00000000..fdcddf4c --- /dev/null +++ "b/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/src/app/add/add.component.spec.ts" @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { AddComponent } from './add.component'; + +describe('AddComponent', () => { + let component: AddComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ AddComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(AddComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git "a/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/src/app/add/add.component.ts" "b/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/src/app/add/add.component.ts" new file mode 100644 index 00000000..5d5fb48b --- /dev/null +++ "b/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/src/app/add/add.component.ts" @@ -0,0 +1,36 @@ +import { Component, OnInit } from '@angular/core'; +import { Books } from '../books'; +import { BooksService } from '../books.service'; +import { HistoryService } from '../history.service'; +import { Location } from '@angular/common'; + +@Component({ + selector: 'app-add', + templateUrl: './add.component.html', + styleUrls: ['./add.component.css'] +}) +export class AddComponent implements OnInit { + books: Books = { + id: 0, + url: '', + title: '', + author: '' + } + constructor( + private location: Location, + private booksservice: BooksService, + private historyservice: HistoryService + ) { } + + ngOnInit() { + } + add(books: Books): void{ + books.title = books.title.trim(); + books.author = books.author.trim(); + this.booksservice.addBooks(books) + .subscribe( book => { + this.historyservice.add(`新增书本${books.title},id为${books.id}`); + this.location.back(); + }); + } +} diff --git "a/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/src/app/app-routing.module.spec.ts" "b/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/src/app/app-routing.module.spec.ts" new file mode 100644 index 00000000..d68ef067 --- /dev/null +++ "b/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/src/app/app-routing.module.spec.ts" @@ -0,0 +1,13 @@ +import { AppRoutingModule } from './app-routing.module'; + +describe('AppRoutingModule', () => { + let appRoutingModule: AppRoutingModule; + + beforeEach(() => { + appRoutingModule = new AppRoutingModule(); + }); + + it('should create an instance', () => { + expect(appRoutingModule).toBeTruthy(); + }); +}); diff --git "a/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/src/app/app-routing.module.ts" "b/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/src/app/app-routing.module.ts" new file mode 100644 index 00000000..5521ccc5 --- /dev/null +++ "b/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/src/app/app-routing.module.ts" @@ -0,0 +1,20 @@ +import { NgModule } from '@angular/core'; +import { RouterModule, Routes } from '@angular/router'; + +import { IndexComponent } from './index/index.component'; +import { DetailComponent } from './detail/detail.component'; +import { AddComponent } from './add/add.component'; + +const routes: Routes = [ + { path: '', redirectTo:'/index', pathMatch:'full' }, + { path: 'index', component: IndexComponent}, + { path: 'detail/:id', component: DetailComponent}, + { path: 'add', component: AddComponent}, +] + +@NgModule({ + imports: [ RouterModule.forRoot(routes) ], + declarations: [], + exports: [RouterModule] +}) +export class AppRoutingModule { } diff --git "a/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/src/app/app.component.css" "b/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/src/app/app.component.css" new file mode 100644 index 00000000..395f433b --- /dev/null +++ "b/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/src/app/app.component.css" @@ -0,0 +1,14 @@ +.my_books{ + width: 770px; + margin: 0 auto; + border: 1px solid rebeccapurple; + border-radius: 10px; + box-sizing: border-box; +} +.my_books_title{ + text-align: center; +} + +.my_books .router{ + text-align: center; +} \ No newline at end of file diff --git "a/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/src/app/app.component.html" "b/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/src/app/app.component.html" new file mode 100644 index 00000000..9154d026 --- /dev/null +++ "b/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/src/app/app.component.html" @@ -0,0 +1,12 @@ +
+

欢迎来到我的个人书屋!

+ + + + +
\ No newline at end of file diff --git "a/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/src/app/app.component.spec.ts" "b/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/src/app/app.component.spec.ts" new file mode 100644 index 00000000..64497cb0 --- /dev/null +++ "b/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/src/app/app.component.spec.ts" @@ -0,0 +1,31 @@ +import { TestBed, async } from '@angular/core/testing'; +import { AppComponent } from './app.component'; + +describe('AppComponent', () => { + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ + AppComponent + ], + }).compileComponents(); + })); + + it('should create the app', () => { + const fixture = TestBed.createComponent(AppComponent); + const app = fixture.debugElement.componentInstance; + expect(app).toBeTruthy(); + }); + + it(`should have as title 'books'`, () => { + const fixture = TestBed.createComponent(AppComponent); + const app = fixture.debugElement.componentInstance; + expect(app.title).toEqual('books'); + }); + + it('should render title in a h1 tag', () => { + const fixture = TestBed.createComponent(AppComponent); + fixture.detectChanges(); + const compiled = fixture.debugElement.nativeElement; + expect(compiled.querySelector('h1').textContent).toContain('Welcome to books!'); + }); +}); diff --git "a/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/src/app/app.component.ts" "b/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/src/app/app.component.ts" new file mode 100644 index 00000000..6d034386 --- /dev/null +++ "b/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/src/app/app.component.ts" @@ -0,0 +1,10 @@ +import { Component } from '@angular/core'; + +@Component({ + selector: 'app-root', + templateUrl: './app.component.html', + styleUrls: ['./app.component.css'] +}) +export class AppComponent { + title = 'books'; +} diff --git "a/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/src/app/app.module.ts" "b/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/src/app/app.module.ts" new file mode 100644 index 00000000..ba73ce1c --- /dev/null +++ "b/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/src/app/app.module.ts" @@ -0,0 +1,37 @@ +import { BrowserModule } from '@angular/platform-browser'; +import { NgModule } from '@angular/core'; +import { HttpClientModule } from '@angular/common/http'; +import { FormsModule } from '@angular/forms'; + +import { AppComponent } from './app.component'; +import { IndexComponent } from './index/index.component'; +import { DetailComponent } from './detail/detail.component'; +import { AppRoutingModule } from './app-routing.module'; +import { BooksComponent } from './books/books.component'; +import { HistoryComponent } from './history/history.component'; + +import { HttpClientInMemoryWebApiModule } from 'angular-in-memory-web-api'; +import { InMemoryDataService } from './in-memory-data.service'; +import { AddComponent } from './add/add.component'; +@NgModule({ + declarations: [ + AppComponent, + IndexComponent, + DetailComponent, + BooksComponent, + HistoryComponent, + AddComponent + ], + imports: [ + BrowserModule, + AppRoutingModule, + HttpClientModule, + HttpClientInMemoryWebApiModule.forRoot( + InMemoryDataService, {dataEncapsulation:false} + ), + FormsModule + ], + providers: [], + bootstrap: [AppComponent] +}) +export class AppModule { } diff --git "a/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/src/app/books.service.spec.ts" "b/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/src/app/books.service.spec.ts" new file mode 100644 index 00000000..40950f51 --- /dev/null +++ "b/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/src/app/books.service.spec.ts" @@ -0,0 +1,12 @@ +import { TestBed } from '@angular/core/testing'; + +import { BooksService } from './books.service'; + +describe('BooksService', () => { + beforeEach(() => TestBed.configureTestingModule({})); + + it('should be created', () => { + const service: BooksService = TestBed.get(BooksService); + expect(service).toBeTruthy(); + }); +}); diff --git "a/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/src/app/books.service.ts" "b/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/src/app/books.service.ts" new file mode 100644 index 00000000..64366e6b --- /dev/null +++ "b/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/src/app/books.service.ts" @@ -0,0 +1,75 @@ +import { Injectable } from '@angular/core'; +import { Books } from './books'; +import { BookList } from './mock-books'; +import { Observable, of } from 'rxjs'; +import { HistoryService } from './history.service'; +import { HttpClient, HttpHeaders} from '@angular/common/http'; +import { catchError, map, tap } from 'rxjs/operators'; +import { Options } from 'selenium-webdriver/chrome'; + +const httpOptions = { + headers: new HttpHeaders({ 'Content-Type': 'application/json' }) +} +@Injectable({ + providedIn: 'root' +}) +export class BooksService { + constructor( + private historyservice: HistoryService, + private http: HttpClient + ) { } + private log(histories: string){ + this.historyservice.add(`正在执行:${histories}`) + } + private booksUrl = 'api/books'; // 提供一个API供调用 + // 获取书本列表 + getBookList(): Observable { + return this.http.get(this.booksUrl) + .pipe( + tap( _ => this.log('请求书本数据')), + catchError(this.handleError('getHeroes', [])) + ); + } + // 获取指定id的书本 + getBook(id: number): Books{ + return BookList.find(book => book.id === id) + } + // 获取指定id的书本 + getBooks(id: number): Observable{ + const url = `${this.booksUrl}/${id}`; + return this.http.get(url).pipe( + tap( _ => this.log(`请求书本的id为${id}`)), + catchError(this.handleError(`getBooks请求是id为${id}`)) + ) + } + // 更新书本数据 + updateBooks(books: Books): Observable{ + return this.http.put(this.booksUrl, books, httpOptions).pipe( + tap(_ => this.log(`修改书本的id是${books.id}`)), + catchError(this.handleError(`getBooks请求是id为${books.id}`)) + ) + } + // 添加书本 + addBooks(books: Books): Observable{ + return this.http.post(this.booksUrl, books, httpOptions).pipe( + tap((newBook: Books) => this.log(`新增书本的id为${newBook.id}`)), + catchError(this.handleError('添加新书')) + ); + } + // 删除书本 + deleteBooks(books: Books): Observable{ + const id = books.id; + const url = `${this.booksUrl}/${id}`; + return this.http.delete(url, httpOptions).pipe( + tap(_ => this.log(`删除书本${books.title},id为${books.id}`)), + catchError(this.handleError('删除书本')) + ); + } + + private handleError (operation = 'operation', result?: T) { + return (error: any): Observable => { + this.log(`${operation} 失败: ${error.message}`); // 发出错误通知 + return of(result as T); // 返回空结果避免程序出错 + }; + } +} diff --git "a/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/src/app/books.ts" "b/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/src/app/books.ts" new file mode 100644 index 00000000..fe4b7954 --- /dev/null +++ "b/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/src/app/books.ts" @@ -0,0 +1,6 @@ +export class Books { + id: number; + url: string; + title: string; + author: string; +} \ No newline at end of file diff --git "a/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/src/app/books/books.component.css" "b/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/src/app/books/books.component.css" new file mode 100644 index 00000000..b9c4248f --- /dev/null +++ "b/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/src/app/books/books.component.css" @@ -0,0 +1,33 @@ +.books_item{ + width: 116px; + margin: 6px; + padding: 10px; + border: 1px solid green; + display: inline-block; + position: relative; +} +.books_item img{ + width: 100%; + height: 160px; +} +.books_item div{ + margin: 0; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} +.books_item .title a{ + font-size: 14px; +} +.books_item .author{ + font-size: 13px; +} +.books_item .delete{ + position: absolute; + right: 0; + top: 0; + background: red; + color: #fff; + padding: 0 5px; + cursor: pointer; +} diff --git "a/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/src/app/books/books.component.html" "b/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/src/app/books/books.component.html" new file mode 100644 index 00000000..2a0bcd67 --- /dev/null +++ "b/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/src/app/books/books.component.html" @@ -0,0 +1,8 @@ +
+ {{list.id}} + +
{{list.author}}
+ X +
\ No newline at end of file diff --git "a/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/src/app/books/books.component.spec.ts" "b/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/src/app/books/books.component.spec.ts" new file mode 100644 index 00000000..13067bab --- /dev/null +++ "b/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/src/app/books/books.component.spec.ts" @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { BooksComponent } from './books.component'; + +describe('BooksComponent', () => { + let component: BooksComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ BooksComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(BooksComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git "a/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/src/app/books/books.component.ts" "b/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/src/app/books/books.component.ts" new file mode 100644 index 00000000..6d91b66b --- /dev/null +++ "b/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/src/app/books/books.component.ts" @@ -0,0 +1,32 @@ +import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core'; +import { Books } from '../books'; +import { BooksService } from '../books.service'; + +@Component({ + selector: 'app-books', + templateUrl: './books.component.html', + styleUrls: ['./books.component.css'] +}) +export class BooksComponent implements OnInit { + @Input() list: Books; + + @Output() + change = new EventEmitter() + + constructor( + private booksservice: BooksService + ) { } + ngOnInit() {} + + getDetailImage(books){ + alert(`正在查看id为${books.id}的大图!`); + } + + delete(books: Books): void { + this.booksservice.deleteBooks(books) + .subscribe(()=>{ + this.change.emit(books); + }); + } + +} diff --git "a/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/src/app/detail/detail.component.css" "b/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/src/app/detail/detail.component.css" new file mode 100644 index 00000000..8efcf1e1 --- /dev/null +++ "b/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/src/app/detail/detail.component.css" @@ -0,0 +1,8 @@ +.detail{ + width: 360px; + margin: 0 auto; +} +.detail img{ + width:200px; + height: 300px; +} \ No newline at end of file diff --git "a/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/src/app/detail/detail.component.html" "b/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/src/app/detail/detail.component.html" new file mode 100644 index 00000000..abe744cc --- /dev/null +++ "b/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/src/app/detail/detail.component.html" @@ -0,0 +1,20 @@ +
+

《{{books.title}}》介绍

+
+ +
+

书本标题: {{books.title}}

+

书本作者: {{books.author}}

+

书本id: {{books.id}}

+
+
+

暂无信息

+
+ +
+

修改信息:

+ + +
\ No newline at end of file diff --git "a/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/src/app/detail/detail.component.spec.ts" "b/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/src/app/detail/detail.component.spec.ts" new file mode 100644 index 00000000..149b9be7 --- /dev/null +++ "b/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/src/app/detail/detail.component.spec.ts" @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { DetailComponent } from './detail.component'; + +describe('DetailComponent', () => { + let component: DetailComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ DetailComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(DetailComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git "a/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/src/app/detail/detail.component.ts" "b/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/src/app/detail/detail.component.ts" new file mode 100644 index 00000000..1280cd15 --- /dev/null +++ "b/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/src/app/detail/detail.component.ts" @@ -0,0 +1,41 @@ +import { Component, OnInit } from '@angular/core'; +import { ActivatedRoute } from '@angular/router'; +import { Location } from '@angular/common'; +import { Books } from '../books'; +import { BooksService } from '../books.service'; +import { HistoryService } from '../history.service'; + +@Component({ + selector: 'app-detail', + templateUrl: './detail.component.html', + styleUrls: ['./detail.component.css'] +}) +export class DetailComponent implements OnInit { + constructor( + private route: ActivatedRoute, + private location: Location, + private booksservice: BooksService, + private historyservice: HistoryService + ) { } + + books: Books; + ngOnInit() { + this.getDetail() + } + getDetail(): void{ + const id = +this.route.snapshot.paramMap.get('id'); + this.getBooks(id); + } + getBooks(id: number): void { + this.books = this.booksservice.getBook(id); + this.historyservice.add(`查看书本${this.books.title},id为${this.books.id}`); + console.log(this.books) + } + save(): void { + this.booksservice.updateBooks(this.books) + .subscribe(() => this.goBack()); + } + goBack(): void { + this.location.back(); + } +} diff --git "a/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/src/app/history.service.spec.ts" "b/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/src/app/history.service.spec.ts" new file mode 100644 index 00000000..594fa69e --- /dev/null +++ "b/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/src/app/history.service.spec.ts" @@ -0,0 +1,12 @@ +import { TestBed } from '@angular/core/testing'; + +import { HistoryService } from './history.service'; + +describe('HistoryService', () => { + beforeEach(() => TestBed.configureTestingModule({})); + + it('should be created', () => { + const service: HistoryService = TestBed.get(HistoryService); + expect(service).toBeTruthy(); + }); +}); diff --git "a/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/src/app/history.service.ts" "b/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/src/app/history.service.ts" new file mode 100644 index 00000000..6c05ce63 --- /dev/null +++ "b/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/src/app/history.service.ts" @@ -0,0 +1,16 @@ +import { Injectable } from '@angular/core'; + +@Injectable({ + providedIn: 'root' +}) +export class HistoryService { + history: string[] = []; + + add(histories: string){ + this.history.push(histories); + } + clear(){ + this.history = []; + } + constructor() { } +} diff --git "a/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/src/app/history/history.component.css" "b/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/src/app/history/history.component.css" new file mode 100644 index 00000000..e69de29b diff --git "a/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/src/app/history/history.component.html" "b/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/src/app/history/history.component.html" new file mode 100644 index 00000000..8d836c75 --- /dev/null +++ "b/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/src/app/history/history.component.html" @@ -0,0 +1,9 @@ +
+

操作历史:

+
+ +
{{item}}
+
+
\ No newline at end of file diff --git "a/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/src/app/history/history.component.spec.ts" "b/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/src/app/history/history.component.spec.ts" new file mode 100644 index 00000000..f68be4f2 --- /dev/null +++ "b/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/src/app/history/history.component.spec.ts" @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { HistoryComponent } from './history.component'; + +describe('HistoryComponent', () => { + let component: HistoryComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ HistoryComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(HistoryComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git "a/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/src/app/history/history.component.ts" "b/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/src/app/history/history.component.ts" new file mode 100644 index 00000000..50606ddf --- /dev/null +++ "b/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/src/app/history/history.component.ts" @@ -0,0 +1,16 @@ +import { Component, OnInit } from '@angular/core'; +import { HistoryService } from '../history.service'; + +@Component({ + selector: 'app-history', + templateUrl: './history.component.html', + styleUrls: ['./history.component.css'] +}) +export class HistoryComponent implements OnInit { + + constructor(private historyservice: HistoryService) { } + + ngOnInit() { + } + +} diff --git "a/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/src/app/in-memory-data.service.spec.ts" "b/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/src/app/in-memory-data.service.spec.ts" new file mode 100644 index 00000000..a75ef029 --- /dev/null +++ "b/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/src/app/in-memory-data.service.spec.ts" @@ -0,0 +1,12 @@ +import { TestBed } from '@angular/core/testing'; + +import { InMemoryDataService } from './in-memory-data.service'; + +describe('InMemoryDataService', () => { + beforeEach(() => TestBed.configureTestingModule({})); + + it('should be created', () => { + const service: InMemoryDataService = TestBed.get(InMemoryDataService); + expect(service).toBeTruthy(); + }); +}); diff --git "a/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/src/app/in-memory-data.service.ts" "b/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/src/app/in-memory-data.service.ts" new file mode 100644 index 00000000..2c84ad77 --- /dev/null +++ "b/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/src/app/in-memory-data.service.ts" @@ -0,0 +1,76 @@ +import { Injectable } from '@angular/core'; +import { InMemoryDbService } from 'angular-in-memory-web-api'; +import { Books } from './books'; + +@Injectable({ + providedIn: 'root' +}) +export class InMemoryDataService implements InMemoryDbService { + createDb(){ + const books = [ + { + id: 1, + url: 'https://img3.doubanio.com/view/subject/m/public/s29988481.jpg', + title: '像火焰像灰烬', + author: '程姬', + }, + { + id: 2, + url: 'https://img3.doubanio.com/view/subject/m/public/s30002856.jpg', + title: '拜占庭帝国史', + author: '[美] A.A.瓦西列夫', + }, + { + id: 3, + url: 'https://img3.doubanio.com/view/subject/m/public/s30005383.jpg', + title: '吴承恩捉妖记 上', + author: '有时右逝', + }, + { + id: 4, + url: 'https://img3.doubanio.com/view/subject/m/public/s29952612.jpg', + title: '生命是什么', + author: '[以色列]埃迪·普罗斯', + }, + { + id: 5, + url: 'https://img3.doubanio.com/view/subject/m/public/s29965934.jpg', + title: '圆屋', + author: '[美]厄德里克(Louise Erdrich)', + }, + { + id: 6, + url: 'https://img3.doubanio.com/view/subject/m/public/s29960204.jpg', + title: '通识', + author: '日本实业出版社 / [日] 茂木健一郎 主编', + }, + { + id: 7, + url: 'https://img3.doubanio.com/view/subject/m/public/s30002353.jpg', + title: '读心师', + author: '向林', + }, + { + id: 8, + url: 'https://img1.doubanio.com/view/subject/m/public/s29951649.jpg', + title: '微精通', + author: '[英] 罗伯特·特威格尔', + }, + { + id: 9, + url: 'https://img3.doubanio.com/view/subject/m/public/s29958456.jpg', + title: '人生最焦虑的就是吃些什么', + author: '刘汀', + }, + { + id: 10, + url: 'https://img3.doubanio.com/view/subject/m/public/s29906241.jpg', + title: '过剩之地', + author: '[美]莫妮卡·普拉萨德', + }, + ]; + return {books}; + } + constructor() { } + +} diff --git "a/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/src/app/index/index.component.css" "b/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/src/app/index/index.component.css" new file mode 100644 index 00000000..56a3a8a5 --- /dev/null +++ "b/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/src/app/index/index.component.css" @@ -0,0 +1,6 @@ +.content{ + width: 100%; + padding: 10px; +} + + diff --git "a/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/src/app/index/index.component.html" "b/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/src/app/index/index.component.html" new file mode 100644 index 00000000..b83baab5 --- /dev/null +++ "b/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/src/app/index/index.component.html" @@ -0,0 +1,7 @@ +
+
+ +
+
\ No newline at end of file diff --git "a/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/src/app/index/index.component.spec.ts" "b/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/src/app/index/index.component.spec.ts" new file mode 100644 index 00000000..03122420 --- /dev/null +++ "b/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/src/app/index/index.component.spec.ts" @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { IndexComponent } from './index.component'; + +describe('IndexComponent', () => { + let component: IndexComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ IndexComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(IndexComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git "a/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/src/app/index/index.component.ts" "b/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/src/app/index/index.component.ts" new file mode 100644 index 00000000..684eabcd --- /dev/null +++ "b/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/src/app/index/index.component.ts" @@ -0,0 +1,40 @@ +import { Component, OnInit } from '@angular/core'; +import { Books } from '../books'; +// import { BookList } from '../mock-books'; +import { BooksService } from '../books.service'; +import { HistoryService } from '../history.service'; +import { HttpClient, HttpHeaders} from '@angular/common/http'; + + +@Component({ + selector: 'app-index', + templateUrl: './index.component.html', + styleUrls: ['./index.component.css'] +}) +export class IndexComponent implements OnInit { + // books: Books = { + // id: 1, + // url: 'https://img3.doubanio.com/view/subject/m/public/s29988481.jpg', + // title: '像火焰像灰烬', + // author: '程姬', + // } + books : Books[]; + constructor( + private booksservice: BooksService, + private historyservice: HistoryService, + private http: HttpClient + ) { } + + ngOnInit() { + this.getBooks(); + } + getBooks(): void{ + this.historyservice.add('访问首页书本列表'); + this.booksservice.getBookList() + .subscribe(books => this.books = books); + } + funChange(books, $event){ + this.books = this.books.filter(h => h.id !== books.id); + console.log('ssssssss') + } +} diff --git "a/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/src/app/mock-books.ts" "b/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/src/app/mock-books.ts" new file mode 100644 index 00000000..4a7863a9 --- /dev/null +++ "b/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/src/app/mock-books.ts" @@ -0,0 +1,64 @@ +import { Books } from './books'; + +export const BookList: Books[] = [ + { + id: 1, + url: 'https://img3.doubanio.com/view/subject/m/public/s29988481.jpg', + title: '像火焰像灰烬', + author: '程姬', + }, + { + id: 2, + url: 'https://img3.doubanio.com/view/subject/m/public/s30002856.jpg', + title: '拜占庭帝国史', + author: '[美] A.A.瓦西列夫', + }, + { + id: 3, + url: 'https://img3.doubanio.com/view/subject/m/public/s30005383.jpg', + title: '吴承恩捉妖记 上', + author: '有时右逝', + }, + { + id: 4, + url: 'https://img3.doubanio.com/view/subject/m/public/s29952612.jpg', + title: '生命是什么', + author: '[以色列]埃迪·普罗斯', + }, + { + id: 5, + url: 'https://img3.doubanio.com/view/subject/m/public/s29965934.jpg', + title: '圆屋', + author: '[美]厄德里克(Louise Erdrich)', + }, + { + id: 6, + url: 'https://img3.doubanio.com/view/subject/m/public/s29960204.jpg', + title: '通识', + author: '日本实业出版社 / [日] 茂木健一郎 主编', + }, + { + id: 7, + url: 'https://img3.doubanio.com/view/subject/m/public/s30002353.jpg', + title: '读心师', + author: '向林', + }, + { + id: 8, + url: 'https://img1.doubanio.com/view/subject/m/public/s29951649.jpg', + title: '微精通', + author: '[英] 罗伯特·特威格尔', + }, + { + id: 9, + url: 'https://img3.doubanio.com/view/subject/m/public/s29958456.jpg', + title: '人生最焦虑的就是吃些什么', + author: '刘汀', + }, + { + id: 10, + url: 'https://img3.doubanio.com/view/subject/m/public/s29906241.jpg', + title: '过剩之地', + author: '[美]莫妮卡·普拉萨德', + }, +] \ No newline at end of file diff --git "a/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/src/assets/.gitkeep" "b/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/src/assets/.gitkeep" new file mode 100644 index 00000000..e69de29b diff --git "a/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/src/browserslist" "b/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/src/browserslist" new file mode 100644 index 00000000..37371cb0 --- /dev/null +++ "b/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/src/browserslist" @@ -0,0 +1,11 @@ +# This file is currently used by autoprefixer to adjust CSS to support the below specified browsers +# For additional information regarding the format and rule options, please see: +# https://github.com/browserslist/browserslist#queries +# +# For IE 9-11 support, please remove 'not' from the last line of the file and adjust as needed + +> 0.5% +last 2 versions +Firefox ESR +not dead +not IE 9-11 \ No newline at end of file diff --git "a/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/src/environments/environment.prod.ts" "b/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/src/environments/environment.prod.ts" new file mode 100644 index 00000000..3612073b --- /dev/null +++ "b/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/src/environments/environment.prod.ts" @@ -0,0 +1,3 @@ +export const environment = { + production: true +}; diff --git "a/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/src/environments/environment.ts" "b/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/src/environments/environment.ts" new file mode 100644 index 00000000..7b4f817a --- /dev/null +++ "b/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/src/environments/environment.ts" @@ -0,0 +1,16 @@ +// This file can be replaced during build by using the `fileReplacements` array. +// `ng build --prod` replaces `environment.ts` with `environment.prod.ts`. +// The list of file replacements can be found in `angular.json`. + +export const environment = { + production: false +}; + +/* + * For easier debugging in development mode, you can import the following file + * to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`. + * + * This import should be commented out in production mode because it will have a negative impact + * on performance if an error is thrown. + */ +// import 'zone.js/dist/zone-error'; // Included with Angular CLI. diff --git "a/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/src/favicon.ico" "b/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/src/favicon.ico" new file mode 100644 index 00000000..8081c7ce Binary files /dev/null and "b/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/src/favicon.ico" differ diff --git "a/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/src/index.html" "b/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/src/index.html" new file mode 100644 index 00000000..d2020127 --- /dev/null +++ "b/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/src/index.html" @@ -0,0 +1,14 @@ + + + + + Books + + + + + + + + + diff --git "a/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/src/karma.conf.js" "b/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/src/karma.conf.js" new file mode 100644 index 00000000..b6e00421 --- /dev/null +++ "b/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/src/karma.conf.js" @@ -0,0 +1,31 @@ +// Karma configuration file, see link for more information +// https://karma-runner.github.io/1.0/config/configuration-file.html + +module.exports = function (config) { + config.set({ + basePath: '', + frameworks: ['jasmine', '@angular-devkit/build-angular'], + plugins: [ + require('karma-jasmine'), + require('karma-chrome-launcher'), + require('karma-jasmine-html-reporter'), + require('karma-coverage-istanbul-reporter'), + require('@angular-devkit/build-angular/plugins/karma') + ], + client: { + clearContext: false // leave Jasmine Spec Runner output visible in browser + }, + coverageIstanbulReporter: { + dir: require('path').join(__dirname, '../coverage'), + reports: ['html', 'lcovonly'], + fixWebpackSourcePaths: true + }, + reporters: ['progress', 'kjhtml'], + port: 9876, + colors: true, + logLevel: config.LOG_INFO, + autoWatch: true, + browsers: ['Chrome'], + singleRun: false + }); +}; \ No newline at end of file diff --git "a/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/src/main.ts" "b/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/src/main.ts" new file mode 100644 index 00000000..28bfa9e1 --- /dev/null +++ "b/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/src/main.ts" @@ -0,0 +1,13 @@ +import { enableProdMode } from '@angular/core'; +import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; + +import { AppModule } from './app/app.module'; +import { environment } from './environments/environment'; + +if (environment.production) { + enableProdMode(); +} + +platformBrowserDynamic().bootstrapModule(AppModule) + .catch(err => console.error(err)); + diff --git "a/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/src/polyfills.ts" "b/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/src/polyfills.ts" new file mode 100644 index 00000000..d310405a --- /dev/null +++ "b/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/src/polyfills.ts" @@ -0,0 +1,80 @@ +/** + * This file includes polyfills needed by Angular and is loaded before the app. + * You can add your own extra polyfills to this file. + * + * This file is divided into 2 sections: + * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers. + * 2. Application imports. Files imported after ZoneJS that should be loaded before your main + * file. + * + * The current setup is for so-called "evergreen" browsers; the last versions of browsers that + * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera), + * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile. + * + * Learn more in https://angular.io/docs/ts/latest/guide/browser-support.html + */ + +/*************************************************************************************************** + * BROWSER POLYFILLS + */ + +/** IE9, IE10 and IE11 requires all of the following polyfills. **/ +// import 'core-js/es6/symbol'; +// import 'core-js/es6/object'; +// import 'core-js/es6/function'; +// import 'core-js/es6/parse-int'; +// import 'core-js/es6/parse-float'; +// import 'core-js/es6/number'; +// import 'core-js/es6/math'; +// import 'core-js/es6/string'; +// import 'core-js/es6/date'; +// import 'core-js/es6/array'; +// import 'core-js/es6/regexp'; +// import 'core-js/es6/map'; +// import 'core-js/es6/weak-map'; +// import 'core-js/es6/set'; + +/** IE10 and IE11 requires the following for NgClass support on SVG elements */ +// import 'classlist.js'; // Run `npm install --save classlist.js`. + +/** IE10 and IE11 requires the following for the Reflect API. */ +// import 'core-js/es6/reflect'; + + +/** Evergreen browsers require these. **/ +// Used for reflect-metadata in JIT. If you use AOT (and only Angular decorators), you can remove. +import 'core-js/es7/reflect'; + + +/** + * Web Animations `@angular/platform-browser/animations` + * Only required if AnimationBuilder is used within the application and using IE/Edge or Safari. + * Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0). + **/ +// import 'web-animations-js'; // Run `npm install --save web-animations-js`. + +/** + * By default, zone.js will patch all possible macroTask and DomEvents + * user can disable parts of macroTask/DomEvents patch by setting following flags + */ + + // (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame + // (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick + // (window as any).__zone_symbol__BLACK_LISTED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames + + /* + * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js + * with the following flag, it will bypass `zone.js` patch for IE/Edge + */ +// (window as any).__Zone_enable_cross_context_check = true; + +/*************************************************************************************************** + * Zone JS is required by default for Angular itself. + */ +import 'zone.js/dist/zone'; // Included with Angular CLI. + + + +/*************************************************************************************************** + * APPLICATION IMPORTS + */ diff --git "a/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/src/styles.css" "b/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/src/styles.css" new file mode 100644 index 00000000..90d4ee00 --- /dev/null +++ "b/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/src/styles.css" @@ -0,0 +1 @@ +/* You can add global styles to this file, and also import other style files */ diff --git "a/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/src/test.ts" "b/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/src/test.ts" new file mode 100644 index 00000000..16317897 --- /dev/null +++ "b/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/src/test.ts" @@ -0,0 +1,20 @@ +// This file is required by karma.conf.js and loads recursively all the .spec and framework files + +import 'zone.js/dist/zone-testing'; +import { getTestBed } from '@angular/core/testing'; +import { + BrowserDynamicTestingModule, + platformBrowserDynamicTesting +} from '@angular/platform-browser-dynamic/testing'; + +declare const require: any; + +// First, initialize the Angular testing environment. +getTestBed().initTestEnvironment( + BrowserDynamicTestingModule, + platformBrowserDynamicTesting() +); +// Then we find all the tests. +const context = require.context('./', true, /\.spec\.ts$/); +// And load the modules. +context.keys().map(context); diff --git "a/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/src/tsconfig.app.json" "b/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/src/tsconfig.app.json" new file mode 100644 index 00000000..190fd300 --- /dev/null +++ "b/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/src/tsconfig.app.json" @@ -0,0 +1,11 @@ +{ + "extends": "../tsconfig.json", + "compilerOptions": { + "outDir": "../out-tsc/app", + "types": [] + }, + "exclude": [ + "test.ts", + "**/*.spec.ts" + ] +} diff --git "a/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/src/tsconfig.spec.json" "b/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/src/tsconfig.spec.json" new file mode 100644 index 00000000..de773363 --- /dev/null +++ "b/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/src/tsconfig.spec.json" @@ -0,0 +1,18 @@ +{ + "extends": "../tsconfig.json", + "compilerOptions": { + "outDir": "../out-tsc/spec", + "types": [ + "jasmine", + "node" + ] + }, + "files": [ + "test.ts", + "polyfills.ts" + ], + "include": [ + "**/*.spec.ts", + "**/*.d.ts" + ] +} diff --git "a/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/src/tslint.json" "b/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/src/tslint.json" new file mode 100644 index 00000000..52e2c1a5 --- /dev/null +++ "b/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/src/tslint.json" @@ -0,0 +1,17 @@ +{ + "extends": "../tslint.json", + "rules": { + "directive-selector": [ + true, + "attribute", + "app", + "camelCase" + ], + "component-selector": [ + true, + "element", + "app", + "kebab-case" + ] + } +} diff --git "a/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/tsconfig.json" "b/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/tsconfig.json" new file mode 100644 index 00000000..916247e4 --- /dev/null +++ "b/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/tsconfig.json" @@ -0,0 +1,21 @@ +{ + "compileOnSave": false, + "compilerOptions": { + "baseUrl": "./", + "outDir": "./dist/out-tsc", + "sourceMap": true, + "declaration": false, + "module": "es2015", + "moduleResolution": "node", + "emitDecoratorMetadata": true, + "experimentalDecorators": true, + "target": "es5", + "typeRoots": [ + "node_modules/@types" + ], + "lib": [ + "es2017", + "dom" + ] + } +} diff --git "a/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/tslint.json" "b/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/tslint.json" new file mode 100644 index 00000000..6ddb6b29 --- /dev/null +++ "b/Cute-Angular/books\351\241\271\347\233\256demo/books_angular/tslint.json" @@ -0,0 +1,131 @@ +{ + "rulesDirectory": [ + "node_modules/codelyzer" + ], + "rules": { + "arrow-return-shorthand": true, + "callable-types": true, + "class-name": true, + "comment-format": [ + true, + "check-space" + ], + "curly": true, + "deprecation": { + "severity": "warn" + }, + "eofline": true, + "forin": true, + "import-blacklist": [ + true, + "rxjs/Rx" + ], + "import-spacing": true, + "indent": [ + true, + "spaces" + ], + "interface-over-type-literal": true, + "label-position": true, + "max-line-length": [ + true, + 140 + ], + "member-access": false, + "member-ordering": [ + true, + { + "order": [ + "static-field", + "instance-field", + "static-method", + "instance-method" + ] + } + ], + "no-arg": true, + "no-bitwise": true, + "no-console": [ + true, + "debug", + "info", + "time", + "timeEnd", + "trace" + ], + "no-construct": true, + "no-debugger": true, + "no-duplicate-super": true, + "no-empty": false, + "no-empty-interface": true, + "no-eval": true, + "no-inferrable-types": [ + true, + "ignore-params" + ], + "no-misused-new": true, + "no-non-null-assertion": true, + "no-redundant-jsdoc": true, + "no-shadowed-variable": true, + "no-string-literal": false, + "no-string-throw": true, + "no-switch-case-fall-through": true, + "no-trailing-whitespace": true, + "no-unnecessary-initializer": true, + "no-unused-expression": true, + "no-use-before-declare": true, + "no-var-keyword": true, + "object-literal-sort-keys": false, + "one-line": [ + true, + "check-open-brace", + "check-catch", + "check-else", + "check-whitespace" + ], + "prefer-const": true, + "quotemark": [ + true, + "single" + ], + "radix": true, + "semicolon": [ + true, + "always" + ], + "triple-equals": [ + true, + "allow-null-check" + ], + "typedef-whitespace": [ + true, + { + "call-signature": "nospace", + "index-signature": "nospace", + "parameter": "nospace", + "property-declaration": "nospace", + "variable-declaration": "nospace" + } + ], + "unified-signatures": true, + "variable-name": false, + "whitespace": [ + true, + "check-branch", + "check-decl", + "check-operator", + "check-separator", + "check-type" + ], + "no-output-on-prefix": true, + "use-input-property-decorator": true, + "use-output-property-decorator": true, + "use-host-property-decorator": true, + "no-input-rename": true, + "no-output-rename": true, + "use-life-cycle-interface": true, + "use-pipe-transform-interface": true, + "component-class-suffix": true, + "directive-class-suffix": true + } +} diff --git "a/Cute-Angular/\347\237\245\350\257\206\347\202\271\346\225\264\347\220\206/README.md" "b/Cute-Angular/\347\237\245\350\257\206\347\202\271\346\225\264\347\220\206/README.md" new file mode 100644 index 00000000..02f4e27c --- /dev/null +++ "b/Cute-Angular/\347\237\245\350\257\206\347\202\271\346\225\264\347\220\206/README.md" @@ -0,0 +1,33 @@ +Angular知识点整理,内容以Angular4实战课程中整理: + +1. 程序架构 +* 组件:Angular基本构建块,是一段含业务逻辑和数据的html +* 服务:封装可重用的业务逻辑 +* 指令:允许向html元素添加指定行为 +* 模块:将不同部分组成一个单元 + + +2. 组件相关概念 +Component 必备: +* 装饰器 @Component() 告知Angular如何处理类,它包含的值叫**元数据**,根据元数据来渲染和展示组件。 +@叫装饰器,@Component()叫组件元数据装饰器 + +* 模版 Template +* 控制器 Controller 包含绝大多数页面逻辑 + +可选的可注入对象: +* 输入属性 @Imports() 组件之间传递数据 +* 提供器 providers 依赖注入 +* 生命周期钩子 Lifecycle Hooks + +可选的输出对象: +* 输出属性 @Outputs +* 样式表 styles +* 动画 Animations +* 生命周期钩子 Lifecycle Hooks + +@NgModule: +* declatations 模块包含的内容,只能组件指令和管道 +* imports 组件依赖的模块 +* providers 模块提供的服务 +* bootstrap 模块的主组件 \ No newline at end of file diff --git a/Cute-Article/README.md b/Cute-Article/README.md new file mode 100644 index 00000000..ec4b1d26 --- /dev/null +++ b/Cute-Article/README.md @@ -0,0 +1,102 @@ + +## 目录 + +### 0、前端工程化 + +* [74-《大前端工程化的时实践和思考》狼叔分享](https://github.com/pingan8787/Leo-JavaScript/blob/master/Cute-Article/article/74-%E3%80%8A%E5%A4%A7%E5%89%8D%E7%AB%AF%E5%B7%A5%E7%A8%8B%E5%8C%96%E7%9A%84%E6%97%B6%E5%AE%9E%E8%B7%B5%E5%92%8C%E6%80%9D%E8%80%83%E3%80%8B%E7%8B%BC%E5%8F%94%E5%88%86%E4%BA%AB.md) + +### 1、JS业务逻辑实现 +* [1-JavaScript实现页面防抖](https://github.com/pingan8787/Leo-JavaScript/blob/master/article/1-JavaScript%E5%AE%9E%E7%8E%B0%E9%A1%B5%E9%9D%A2%E9%98%B2%E6%8A%96.md) +* [2-同步返回ajax请求结果方法](https://github.com/pingan8787/Leo-JavaScript/blob/master/article/2-%E5%90%8C%E6%AD%A5%E8%BF%94%E5%9B%9Eajax%E8%AF%B7%E6%B1%82%E7%BB%93%E6%9E%9C%E6%96%B9%E6%B3%95.md) +* [★ 9-常用业务模块代码整理](https://github.com/pingan8787/Leo-JavaScript/blob/master/article/9-%E5%B8%B8%E7%94%A8%E4%B8%9A%E5%8A%A1%E6%A8%A1%E5%9D%97%E4%BB%A3%E7%A0%81%E6%95%B4%E7%90%86.md) +* [12-javascript开发的一些简写技巧](https://github.com/pingan8787/Leo-JavaScript/blob/master/article/12-javascript%E5%BC%80%E5%8F%91%E7%9A%84%E4%B8%80%E4%BA%9B%E7%AE%80%E5%86%99%E6%8A%80%E5%B7%A7.md) +* [15-精心收集的48个JavaScript片段,简单理解](https://github.com/pingan8787/Leo-JavaScript/blob/master/article/15-%E7%B2%BE%E5%BF%83%E6%94%B6%E9%9B%86%E7%9A%8448%E4%B8%AAJavaScript%E7%89%87%E6%AE%B5%EF%BC%8C%E7%AE%80%E5%8D%95%E7%90%86%E8%A7%A3.md) +* [29-关于随机数的一些总结](https://github.com/pingan8787/Leo-JavaScript/blob/master/article/29-%E5%85%B3%E4%BA%8E%E9%9A%8F%E6%9C%BA%E6%95%B0%E7%9A%84%E4%B8%80%E4%BA%9B%E6%80%BB%E7%BB%93.md) + +### 2、VueJS +* [4-基于Vue配置axios](https://github.com/pingan8787/Leo-JavaScript/blob/master/article/4-%E5%9F%BA%E4%BA%8EVue%E9%85%8D%E7%BD%AEaxios.md) +* [★ 5-[原创]VUE中实现通用js函数库封装](https://github.com/pingan8787/Leo-JavaScript/blob/master/article/5-%5B%E5%8E%9F%E5%88%9B%5DVUE%E4%B8%AD%E5%AE%9E%E7%8E%B0%E9%80%9A%E7%94%A8js%E5%87%BD%E6%95%B0%E5%BA%93%E5%B0%81%E8%A3%85.md) +* [★ 7-[原创]缩小Vuejs打包体积方法](https://github.com/pingan8787/Leo-JavaScript/blob/master/article/7-%5B%E5%8E%9F%E5%88%9B%5D%E7%BC%A9%E5%B0%8FVuejs%E6%89%93%E5%8C%85%E4%BD%93%E7%A7%AF%E6%96%B9%E6%B3%95.md) +* [14-Vue的一些小注意点](https://github.com/pingan8787/Leo-JavaScript/blob/master/article/14-Vue%E7%9A%84%E4%B8%80%E4%BA%9B%E5%B0%8F%E6%B3%A8%E6%84%8F%E7%82%B9.md) +* [★ 24-Vue折腾记-给Axios做个挺靠谱的封装(报错,鉴权,跳转,拦截,提示)](https://github.com/pingan8787/Leo-JavaScript/blob/master/article/24-Vue%E6%8A%98%E8%85%BE%E8%AE%B0-%E7%BB%99Axios%E5%81%9A%E4%B8%AA%E6%8C%BA%E9%9D%A0%E8%B0%B1%E7%9A%84%E5%B0%81%E8%A3%85%EF%BC%88%E6%8A%A5%E9%94%99%2C%E9%89%B4%E6%9D%83%2C%E8%B7%B3%E8%BD%AC%2C%E6%8B%A6%E6%88%AA%2C%E6%8F%90%E7%A4%BA%EF%BC%89.md) +* [40-解密Vue SSR](https://github.com/pingan8787/Leo-JavaScript/blob/master/article/40-%E8%A7%A3%E5%AF%86Vue%20SSR.md) +* [49-Vue 面试中常问知识点整理](https://github.com/pingan8787/Leo-JavaScript/blob/master/article/49-Vue%20%E9%9D%A2%E8%AF%95%E4%B8%AD%E5%B8%B8%E9%97%AE%E7%9F%A5%E8%AF%86%E7%82%B9%E6%95%B4%E7%90%86.md) + +### 3、ES6/ES7/ES8... +* [3-Promise简单用法](https://github.com/pingan8787/Leo-JavaScript/blob/master/article/3-Promise%E7%AE%80%E5%8D%95%E7%94%A8%E6%B3%95.md) +* [13-ES7和ES8的一点新东西](https://github.com/pingan8787/Leo-JavaScript/blob/master/article/13-ES7%E5%92%8CES8%E7%9A%84%E4%B8%80%E7%82%B9%E6%96%B0%E4%B8%9C%E8%A5%BF.md) +* [19-ES6的7个实用技巧](https://github.com/pingan8787/Leo-JavaScript/blob/master/article/19-ES6%E7%9A%847%E4%B8%AA%E5%AE%9E%E7%94%A8%E6%8A%80%E5%B7%A7.md) +* [★ 31-ES6这些就够了](https://github.com/pingan8787/Leo-JavaScript/blob/master/article/31-ES6%E8%BF%99%E4%BA%9B%E5%B0%B1%E5%A4%9F%E4%BA%86.md) +* [★ 34-我眼中的async&await](https://github.com/pingan8787/Leo-JavaScript/blob/master/article/34-%E6%88%91%E7%9C%BC%E4%B8%AD%E7%9A%84async%26await.md) +* [35-ES6中的模块导入导出整理](https://github.com/pingan8787/Leo-JavaScript/blob/master/article/35-ES6%E4%B8%AD%E7%9A%84%E6%A8%A1%E5%9D%97%E5%AF%BC%E5%85%A5%E5%AF%BC%E5%87%BA%E6%95%B4%E7%90%86.md) +* [41-ES2018(ES9)的新特性](https://github.com/pingan8787/Leo-JavaScript/blob/master/article/41-ES2018%EF%BC%88ES9%EF%BC%89%E7%9A%84%E6%96%B0%E7%89%B9%E6%80%A7.md) +* [63-ES6汇总.md](https://github.com/pingan8787/Leo-JavaScript/blob/master/article/63-ES6%E6%B1%87%E6%80%BB.md) +* [64-ES7汇总.md](https://github.com/pingan8787/Leo-JavaScript/blob/master/article/64-ES7%E6%B1%87%E6%80%BB.md) +* [65-ES8汇总.md](https://github.com/pingan8787/Leo-JavaScript/blob/master/article/65-ES8%E6%B1%87%E6%80%BB.md) +* [66-ES9汇总.md](https://github.com/pingan8787/Leo-JavaScript/blob/master/article/66-ES9%E6%B1%87%E6%80%BB.md) + +### 4、Webpack +* [8-vue-cli2的webpack配置分析](https://github.com/pingan8787/Leo-JavaScript/blob/master/article/8-vue-cli2%E7%9A%84webpack%E9%85%8D%E7%BD%AE%E5%88%86%E6%9E%90.md) +* [25-Webpack入门教程整理(整理中)](https://github.com/pingan8787/Leo-JavaScript/blob/master/article/25-Webpack%E5%85%A5%E9%97%A8%E6%95%99%E7%A8%8B%E6%95%B4%E7%90%86%EF%BC%88%E6%95%B4%E7%90%86%E4%B8%AD%EF%BC%89.md) +* [26-Webpack常用配置整理(整理中)](https://github.com/pingan8787/Leo-JavaScript/blob/master/article/26-Webpack%E5%B8%B8%E7%94%A8%E9%85%8D%E7%BD%AE%E6%95%B4%E7%90%86%EF%BC%88%E6%95%B4%E7%90%86%E4%B8%AD%EF%BC%89.md) +* [75-Webpack怎么运行?](https://github.com/pingan8787/Leo-JavaScript/blob/master/Cute-Article/article/75-Webpack%E6%80%8E%E4%B9%88%E8%BF%90%E8%A1%8C%EF%BC%9F.md) + +### 5、WebSocket +* [18-websocket常用demo](https://github.com/pingan8787/Leo-JavaScript/blob/master/article/18-websocket%E5%B8%B8%E7%94%A8demo.md) +* [★ 20-WebSocket重新学习](https://github.com/pingan8787/Leo-JavaScript/blob/master/article/20-WebSocket%E9%87%8D%E6%96%B0%E5%AD%A6%E4%B9%A0.md) + +### 6、细节知识点 +* [6-关于js的作用域和声明提前](https://github.com/pingan8787/Leo-JavaScript/blob/master/article/6-%E5%85%B3%E4%BA%8Ejs%E7%9A%84%E4%BD%9C%E7%94%A8%E5%9F%9F%E5%92%8C%E5%A3%B0%E6%98%8E%E6%8F%90%E5%89%8D.md) +* [★ 10-知识点整理1](https://github.com/pingan8787/Leo-JavaScript/blob/master/article/10-%E7%9F%A5%E8%AF%86%E7%82%B9%E6%95%B4%E7%90%861.md) +* [16-带你理解 JS 容易出错的坑和细节](https://github.com/pingan8787/Leo-JavaScript/blob/master/article/16-%E5%B8%A6%E4%BD%A0%E7%90%86%E8%A7%A3%20JS%20%E5%AE%B9%E6%98%93%E5%87%BA%E9%94%99%E7%9A%84%E5%9D%91%E5%92%8C%E7%BB%86%E8%8A%82.md) +* [17-TypeScript和 JavaScript 深度对比](https://github.com/pingan8787/Leo-JavaScript/blob/master/article/17-TypeScript%E5%92%8C%20JavaScript%20%E6%B7%B1%E5%BA%A6%E5%AF%B9%E6%AF%94.md) +* [★ 21-JavaScript异步机制详解](https://github.com/pingan8787/Leo-JavaScript/blob/master/article/21-JavaScript%E5%BC%82%E6%AD%A5%E6%9C%BA%E5%88%B6%E8%AF%A6%E8%A7%A3.md) +* [22-JavaScript中有趣的区分同步和异步Ajax](https://github.com/pingan8787/Leo-JavaScript/blob/master/article/22-JavaScript%E4%B8%AD%E6%9C%89%E8%B6%A3%E7%9A%84%E5%8C%BA%E5%88%86%E5%90%8C%E6%AD%A5%E5%92%8C%E5%BC%82%E6%AD%A5Ajax.md) +* [23-JavaScript八张思维导图](https://github.com/pingan8787/Leo-JavaScript/blob/master/article/23-JavaScript%E5%85%AB%E5%BC%A0%E6%80%9D%E7%BB%B4%E5%AF%BC%E5%9B%BE.md) +* [28-JavaScript中的void运算符](https://github.com/pingan8787/Leo-JavaScript/blob/master/article/28-JavaScript%E4%B8%AD%E7%9A%84void%E8%BF%90%E7%AE%97%E7%AC%A6.md) +* [31-聊一聊JavaScript的IIFE](https://github.com/pingan8787/Leo-JavaScript/blob/master/article/32-%E8%81%8A%E4%B8%80%E8%81%8AJavaScript%E7%9A%84IIFE.md) +* [33-javascript的纯函数](https://github.com/pingan8787/Leo-JavaScript/blob/master/article/33-javascript%E7%9A%84%E7%BA%AF%E5%87%BD%E6%95%B0.md) +* [36-好好学习toLocaleString方法](https://github.com/pingan8787/Leo-JavaScript/blob/master/article/36-%E5%A5%BD%E5%A5%BD%E5%AD%A6%E4%B9%A0toLocaleString%E6%96%B9%E6%B3%95.md) +* [37-JavaScript事件委托详解](https://github.com/pingan8787/Leo-JavaScript/blob/master/article/37-JavaScript%E4%BA%8B%E4%BB%B6%E5%A7%94%E6%89%98%E8%AF%A6%E8%A7%A3.md) +* [38-JavaScript中常见设计模式](https://github.com/pingan8787/Leo-JavaScript/blob/master/article/38-JavaScript%E4%B8%AD%E5%B8%B8%E8%A7%81%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F.md) +* [42-JS高程中的垃圾回收机制与常见内存泄露的解决方法](https://github.com/pingan8787/Leo-JavaScript/blob/master/article/42-JS%E9%AB%98%E7%A8%8B%E4%B8%AD%E7%9A%84%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6%E6%9C%BA%E5%88%B6%E4%B8%8E%E5%B8%B8%E8%A7%81%E5%86%85%E5%AD%98%E6%B3%84%E9%9C%B2%E7%9A%84%E8%A7%A3%E5%86%B3%E6%96%B9%E6%B3%95.md) +* [43-手机端页面开发常见问题和解决](https://github.com/pingan8787/Leo-JavaScript/blob/master/article/43-%E6%89%8B%E6%9C%BA%E7%AB%AF%E9%A1%B5%E9%9D%A2%E5%BC%80%E5%8F%91%E5%B8%B8%E8%A7%81%E9%97%AE%E9%A2%98%E5%92%8C%E8%A7%A3%E5%86%B3.md) +* [44-前端本地文件操作和上传](https://github.com/pingan8787/Leo-JavaScript/blob/master/article/44-%E5%89%8D%E7%AB%AF%E6%9C%AC%E5%9C%B0%E6%96%87%E4%BB%B6%E6%93%8D%E4%BD%9C%E5%92%8C%E4%B8%8A%E4%BC%A0.md) +* [45-js中reduce的神奇用法](https://github.com/pingan8787/Leo-JavaScript/blob/master/article/45-js%E4%B8%ADreduce%E7%9A%84%E7%A5%9E%E5%A5%87%E7%94%A8%E6%B3%95.md) +* [46-在JavaScript中更好的使用数组](https://github.com/pingan8787/Leo-JavaScript/blob/master/article/46-%E5%9C%A8JavaScript%E4%B8%AD%E6%9B%B4%E5%A5%BD%E7%9A%84%E4%BD%BF%E7%94%A8%E6%95%B0%E7%BB%84.md) +* [48-js获取元素高度和浏览器各种高度方法汇总](https://github.com/pingan8787/Leo-JavaScript/blob/master/article/48-js获取元素高度和浏览器各种高度方法汇总.md) +* [50-js中get和post的区别](https://github.com/pingan8787/Leo-JavaScript/blob/master/article/50-js%E4%B8%ADget%E5%92%8Cpost%E7%9A%84%E5%8C%BA%E5%88%AB.md) +* [52-前端模块化(CommonJs,AMD和CMD)](https://github.com/pingan8787/Leo-JavaScript/blob/master/article/52-%E5%89%8D%E7%AB%AF%E6%A8%A1%E5%9D%97%E5%8C%96%EF%BC%88CommonJs%2CAMD%E5%92%8CCMD%EF%BC%89.md) +* [53-js中call和apply和bind方法介绍](https://github.com/pingan8787/Leo-JavaScript/blob/master/article/53-js%E4%B8%ADcall%E5%92%8Capply%E5%92%8Cbind%E6%96%B9%E6%B3%95%E4%BB%8B%E7%BB%8D.md) +* [54-ajax详解](https://github.com/pingan8787/Leo-JavaScript/blob/master/article/54-ajax%E8%AF%A6%E8%A7%A3.md) +* [55-JS中attribute和property区别](https://github.com/pingan8787/Leo-JavaScript/blob/master/article/55-JS%E4%B8%ADattribute%E5%92%8Cproperty%E5%8C%BA%E5%88%AB.md) +* [56-js中原型继承原理](https://github.com/pingan8787/Leo-JavaScript/blob/master/article/56-js%E4%B8%AD%E5%8E%9F%E5%9E%8B%E7%BB%A7%E6%89%BF%E5%8E%9F%E7%90%86.md) +* [58-详解HTML5data-自定义属性](https://github.com/pingan8787/Leo-JavaScript/blob/master/article/58-%E8%AF%A6%E8%A7%A3HTML5data-%E8%87%AA%E5%AE%9A%E4%B9%89%E5%B1%9E%E6%80%A7.md) +* [59-前端HTML5几种存储方式的总结](https://github.com/pingan8787/Leo-JavaScript/blob/master/article/59-%E5%89%8D%E7%AB%AFHTML5%E5%87%A0%E7%A7%8D%E5%AD%98%E5%82%A8%E6%96%B9%E5%BC%8F%E7%9A%84%E6%80%BB%E7%BB%93.md) +* [60-懒加载和预加载](https://github.com/pingan8787/Leo-JavaScript/blob/master/article/60-%E6%87%92%E5%8A%A0%E8%BD%BD%E5%92%8C%E9%A2%84%E5%8A%A0%E8%BD%BD.md) +* [61-JS中this的4种绑定规则](https://github.com/pingan8787/Leo-JavaScript/blob/master/article/61-JS%E4%B8%ADthis%E7%9A%844%E7%A7%8D%E7%BB%91%E5%AE%9A%E8%A7%84%E5%88%99.md) +* [67-JS箭头函数的适用和不适用的场景.md](https://github.com/pingan8787/Leo-JavaScript/blob/master/article/67-JS%E7%AE%AD%E5%A4%B4%E5%87%BD%E6%95%B0%E7%9A%84%E9%80%82%E7%94%A8%E5%92%8C%E4%B8%8D%E9%80%82%E7%94%A8%E7%9A%84%E5%9C%BA%E6%99%AF.md) +* [68-创建对象的七种方式](https://github.com/pingan8787/Leo-JavaScript/blob/master/article/68-%E5%88%9B%E5%BB%BA%E5%AF%B9%E8%B1%A1%E7%9A%84%E4%B8%83%E7%A7%8D%E6%96%B9%E5%BC%8F.md) +* [69-秒懂this](https://github.com/pingan8787/Leo-JavaScript/blob/master/article/69-秒懂this.md) +* [70-JS复杂判断的更优雅写法](https://github.com/pingan8787/Leo-JavaScript/blob/master/article/70-JS复杂判断的更优雅写法.md) +* [71-复习instanceof运算符](https://github.com/pingan8787/Leo-JavaScript/blob/master/article/71-%E5%A4%8D%E4%B9%A0instanceof%E8%BF%90%E7%AE%97%E7%AC%A6.md) +* [72-【重温基础】JS中的高阶函数](https://github.com/pingan8787/Leo-JavaScript/blob/master/article/72-%E3%80%90%E9%87%8D%E6%B8%A9%E5%9F%BA%E7%A1%80%E3%80%91JS%E4%B8%AD%E7%9A%84%E9%AB%98%E9%98%B6%E5%87%BD%E6%95%B0.md) + +### 7、HTTP +* [47-http请求头与响应头的应用](https://github.com/pingan8787/Leo-JavaScript/blob/master/article/47-http%E8%AF%B7%E6%B1%82%E5%A4%B4%E4%B8%8E%E5%93%8D%E5%BA%94%E5%A4%B4%E7%9A%84%E5%BA%94%E7%94%A8.md) +* [51-Apache之HTTP协议](https://github.com/pingan8787/Leo-JavaScript/blob/master/article/51-Apache%E4%B9%8BHTTP%E5%8D%8F%E8%AE%AE.md) + +### 8、正则表达式 +* [30-一次记住js的6个正则方法](https://github.com/pingan8787/Leo-JavaScript/blob/master/article/30-%E4%B8%80%E6%AC%A1%E8%AE%B0%E4%BD%8Fjs%E7%9A%846%E4%B8%AA%E6%AD%A3%E5%88%99%E6%96%B9%E6%B3%95.md) +* [11-20个超级常用的正则表达式](https://github.com/pingan8787/Leo-JavaScript/blob/master/article/11-20%E4%B8%AA%E8%B6%85%E7%BA%A7%E5%B8%B8%E7%94%A8%E7%9A%84%E6%AD%A3%E5%88%99%E8%A1%A8%E8%BE%BE%E5%BC%8F.md) +* [57-ES5ES6正则表达式总结](https://github.com/pingan8787/Leo-JavaScript/blob/master/article/57-ES5ES6%E6%AD%A3%E5%88%99%E8%A1%A8%E8%BE%BE%E5%BC%8F%E6%80%BB%E7%BB%93.md) + +### 9、面试题 +* [62-2018各大公司近期面试题](https://github.com/pingan8787/Leo-JavaScript/blob/master/article/62-2018%E5%90%84%E5%A4%A7%E5%85%AC%E5%8F%B8%E8%BF%91%E6%9C%9F%E9%9D%A2%E8%AF%95%E9%A2%98.md) + +### 10、其他 +* [2017-全年总结](https://github.com/pingan8787/Leo-JavaScript/blob/master/article/2017-%E5%85%A8%E5%B9%B4%E6%80%BB%E7%BB%93.md) +* [2018-全年计划](https://github.com/pingan8787/Leo-JavaScript/blob/master/article/2018-%E5%85%A8%E5%B9%B4%E8%AE%A1%E5%88%92.md) +* [2018-全年总结](https://github.com/pingan8787/Leo-JavaScript/blob/master/article/2018-%E5%85%A8%E5%B9%B4%E6%80%BB%E7%BB%93.md) +* [★ 27-Markdowm语法整理](https://github.com/pingan8787/Leo-JavaScript/blob/master/article/27-Markdowm%E8%AF%AD%E6%B3%95%E6%95%B4%E7%90%86.md) +* [39-作为前端需要了解的开源协议知识](https://github.com/pingan8787/Leo-JavaScript/blob/master/article/39-%E4%BD%9C%E4%B8%BA%E5%89%8D%E7%AB%AF%E9%9C%80%E8%A6%81%E4%BA%86%E8%A7%A3%E7%9A%84%E5%BC%80%E6%BA%90%E5%8D%8F%E8%AE%AE%E7%9F%A5%E8%AF%86.md) diff --git "a/1-JavaScript\345\256\236\347\216\260\351\241\265\351\235\242\351\230\262\346\212\226.md" "b/Cute-Article/article/1-JavaScript\345\256\236\347\216\260\351\241\265\351\235\242\351\230\262\346\212\226.md" similarity index 100% rename from "1-JavaScript\345\256\236\347\216\260\351\241\265\351\235\242\351\230\262\346\212\226.md" rename to "Cute-Article/article/1-JavaScript\345\256\236\347\216\260\351\241\265\351\235\242\351\230\262\346\212\226.md" diff --git "a/10-\347\237\245\350\257\206\347\202\271\346\225\264\347\220\2061.md" "b/Cute-Article/article/10-\347\237\245\350\257\206\347\202\271\346\225\264\347\220\2061.md" similarity index 96% rename from "10-\347\237\245\350\257\206\347\202\271\346\225\264\347\220\2061.md" rename to "Cute-Article/article/10-\347\237\245\350\257\206\347\202\271\346\225\264\347\220\2061.md" index fe117fcf..44ed17a6 100644 --- "a/10-\347\237\245\350\257\206\347\202\271\346\225\264\347\220\2061.md" +++ "b/Cute-Article/article/10-\347\237\245\350\257\206\347\202\271\346\225\264\347\220\2061.md" @@ -400,7 +400,27 @@ console.log(samesums[0]); // 1 > * `defer`并行加载js文件,会按照页面上`script`标签的顺序执行 > * `async`并行加载js文件,下载完成立即执行,不会按照页面上`script`标签的顺序执行 +### 13、检测属性 +用于判断对象是否存在某个属性: +```js +let a = { + x : 11, + y : 22 +} +``` +> 1.通过 in 运算符判断: +```js +'x' in a ; // true +'z' in a ; // false +``` +> 2.通过hasOwnProperty()方法判断: +```js +a.hasOwnProperty("x") ; //true : a有一个自有属性x,若是继承属性,返回false +``` +> 3.更便捷 !== undefined: +```js +a.x !== undefined ;//true: a 中有属性 x +``` - -### 最近更新 2018.04.09 +### 最近更新 2018.05.17 回到顶部 [介绍](#介绍) diff --git "a/11-20\344\270\252\350\266\205\347\272\247\345\270\270\347\224\250\347\232\204\346\255\243\345\210\231\350\241\250\350\276\276\345\274\217.md" "b/Cute-Article/article/11-20\344\270\252\350\266\205\347\272\247\345\270\270\347\224\250\347\232\204\346\255\243\345\210\231\350\241\250\350\276\276\345\274\217.md" similarity index 100% rename from "11-20\344\270\252\350\266\205\347\272\247\345\270\270\347\224\250\347\232\204\346\255\243\345\210\231\350\241\250\350\276\276\345\274\217.md" rename to "Cute-Article/article/11-20\344\270\252\350\266\205\347\272\247\345\270\270\347\224\250\347\232\204\346\255\243\345\210\231\350\241\250\350\276\276\345\274\217.md" diff --git "a/12-javascript\345\274\200\345\217\221\347\232\204\344\270\200\344\272\233\347\256\200\345\206\231\346\212\200\345\267\247.md" "b/Cute-Article/article/12-javascript\345\274\200\345\217\221\347\232\204\344\270\200\344\272\233\347\256\200\345\206\231\346\212\200\345\267\247.md" similarity index 100% rename from "12-javascript\345\274\200\345\217\221\347\232\204\344\270\200\344\272\233\347\256\200\345\206\231\346\212\200\345\267\247.md" rename to "Cute-Article/article/12-javascript\345\274\200\345\217\221\347\232\204\344\270\200\344\272\233\347\256\200\345\206\231\346\212\200\345\267\247.md" diff --git "a/13-ES7\345\222\214ES8\347\232\204\344\270\200\347\202\271\346\226\260\344\270\234\350\245\277.md" "b/Cute-Article/article/13-ES7\345\222\214ES8\347\232\204\344\270\200\347\202\271\346\226\260\344\270\234\350\245\277.md" similarity index 92% rename from "13-ES7\345\222\214ES8\347\232\204\344\270\200\347\202\271\346\226\260\344\270\234\350\245\277.md" rename to "Cute-Article/article/13-ES7\345\222\214ES8\347\232\204\344\270\200\347\202\271\346\226\260\344\270\234\350\245\277.md" index 67c3854a..e5431109 100644 --- "a/13-ES7\345\222\214ES8\347\232\204\344\270\200\347\202\271\346\226\260\344\270\234\350\245\277.md" +++ "b/Cute-Article/article/13-ES7\345\222\214ES8\347\232\204\344\270\200\347\202\271\346\226\260\344\270\234\350\245\277.md" @@ -55,8 +55,8 @@ Object.keys(obj).forEach((key) => // Object.keys() 对象=>数组 }); ``` -2-不使用ES8 -使用Object.values()遍历对象的属性值,无需使用使用属性名: +2-使用ES8 +使用Object.values()遍历对象的属性值,无需使用属性名: ```js let obj = {a: 1, b: 2, c: 3} Object.values(obj).forEach(value => @@ -77,8 +77,8 @@ Object.keys(obj).forEach((key) => }) ``` -2-不使用ES8 -使用Object.values()遍历对象的属性值,无需使用使用属性名: +2-使用ES8 +使用Object.entries()遍历对象的属性值,无需使用属性名: ```js let obj = {a: 1, b: 2, c: 3}; Object.entries(obj).forEach(([key, value]) => @@ -90,14 +90,13 @@ Object.entries(obj).forEach(([key, value]) => ### 5、ES8 - padStart() 在字符串前面填充指定的字符串。 1-不使用ES8 -使用Object.keys()遍历对象的属性名和属性值: ```js console.log('0.00') // 0.00 console.log('10,000.00') // 10,000.00 console.log('250,000.00') // 250,000.00 ``` -2-不使用ES8 +2-使用ES8 使用padStart()可以在字符串前面填充指定的字符串: ```js console.log('0.00'.padStart(20)) // 0.00 @@ -108,14 +107,13 @@ console.log('250,000.00'.padStart(20)) // 250,000.00 ### 6、ES8 - padEnd() 在字符串后面填充指定的字符串。 1-不使用ES8 -使用Object.keys()遍历对象的属性名和属性值: ```js console.log('0.00 ' + '0.00' ) // 0.00 0.00 console.log('10,000.00 ' + '10,000.00' ) // 10,000.00 10,000.00 console.log('250,000.00 ' + '250,000.00') // 250,000.00 250,000.00 ``` -2-不使用ES8 +2-使用ES8 使用padEnd()可以在字符串后面填充指定的字符串: ```js console.log('0.00'.padEnd(20) + '0.00' ) // 0.00 0.00 @@ -181,4 +179,4 @@ fetchData(query).then(data => this.props.processfetchedData(data) }) ``` -`Async/Await` 是写异步代码的新方式,以前的方法有 `回调函数` 和 `Promise` 。相比于 `Promise` ,它更加简洁,并且处理错误、条件语句、中间值都更加方便,因此有望替代 `Promise` ,成为新一代的一步代码编写方式。 \ No newline at end of file +`Async/Await` 是写异步代码的新方式,以前的方法有 `回调函数` 和 `Promise` 。相比于 `Promise` ,它更加简洁,并且处理错误、条件语句、中间值都更加方便,因此有望替代 `Promise` ,成为新一代的一步代码编写方式。 diff --git "a/14-Vue\347\232\204\344\270\200\344\272\233\345\260\217\346\263\250\346\204\217\347\202\271.md" "b/Cute-Article/article/14-Vue\347\232\204\344\270\200\344\272\233\345\260\217\346\263\250\346\204\217\347\202\271.md" similarity index 100% rename from "14-Vue\347\232\204\344\270\200\344\272\233\345\260\217\346\263\250\346\204\217\347\202\271.md" rename to "Cute-Article/article/14-Vue\347\232\204\344\270\200\344\272\233\345\260\217\346\263\250\346\204\217\347\202\271.md" diff --git "a/15-\347\262\276\345\277\203\346\224\266\351\233\206\347\232\20448\344\270\252JavaScript\347\211\207\346\256\265\357\274\214\347\256\200\345\215\225\347\220\206\350\247\243.md" "b/Cute-Article/article/15-\347\262\276\345\277\203\346\224\266\351\233\206\347\232\20448\344\270\252JavaScript\347\211\207\346\256\265\357\274\214\347\256\200\345\215\225\347\220\206\350\247\243.md" similarity index 100% rename from "15-\347\262\276\345\277\203\346\224\266\351\233\206\347\232\20448\344\270\252JavaScript\347\211\207\346\256\265\357\274\214\347\256\200\345\215\225\347\220\206\350\247\243.md" rename to "Cute-Article/article/15-\347\262\276\345\277\203\346\224\266\351\233\206\347\232\20448\344\270\252JavaScript\347\211\207\346\256\265\357\274\214\347\256\200\345\215\225\347\220\206\350\247\243.md" diff --git "a/16-\345\270\246\344\275\240\347\220\206\350\247\243 JS \345\256\271\346\230\223\345\207\272\351\224\231\347\232\204\345\235\221\345\222\214\347\273\206\350\212\202.md" "b/Cute-Article/article/16-\345\270\246\344\275\240\347\220\206\350\247\243 JS \345\256\271\346\230\223\345\207\272\351\224\231\347\232\204\345\235\221\345\222\214\347\273\206\350\212\202.md" similarity index 100% rename from "16-\345\270\246\344\275\240\347\220\206\350\247\243 JS \345\256\271\346\230\223\345\207\272\351\224\231\347\232\204\345\235\221\345\222\214\347\273\206\350\212\202.md" rename to "Cute-Article/article/16-\345\270\246\344\275\240\347\220\206\350\247\243 JS \345\256\271\346\230\223\345\207\272\351\224\231\347\232\204\345\235\221\345\222\214\347\273\206\350\212\202.md" diff --git "a/17-TypeScript\345\222\214 JavaScript \346\267\261\345\272\246\345\257\271\346\257\224.md" "b/Cute-Article/article/17-TypeScript\345\222\214 JavaScript \346\267\261\345\272\246\345\257\271\346\257\224.md" similarity index 100% rename from "17-TypeScript\345\222\214 JavaScript \346\267\261\345\272\246\345\257\271\346\257\224.md" rename to "Cute-Article/article/17-TypeScript\345\222\214 JavaScript \346\267\261\345\272\246\345\257\271\346\257\224.md" diff --git "a/18-websocket\345\270\270\347\224\250demo.md" "b/Cute-Article/article/18-websocket\345\270\270\347\224\250demo.md" similarity index 100% rename from "18-websocket\345\270\270\347\224\250demo.md" rename to "Cute-Article/article/18-websocket\345\270\270\347\224\250demo.md" diff --git "a/19-ES6\347\232\2047\344\270\252\345\256\236\347\224\250\346\212\200\345\267\247.md" "b/Cute-Article/article/19-ES6\347\232\2047\344\270\252\345\256\236\347\224\250\346\212\200\345\267\247.md" similarity index 100% rename from "19-ES6\347\232\2047\344\270\252\345\256\236\347\224\250\346\212\200\345\267\247.md" rename to "Cute-Article/article/19-ES6\347\232\2047\344\270\252\345\256\236\347\224\250\346\212\200\345\267\247.md" diff --git "a/2-\345\220\214\346\255\245\350\277\224\345\233\236ajax\350\257\267\346\261\202\347\273\223\346\236\234\346\226\271\346\263\225.md" "b/Cute-Article/article/2-\345\220\214\346\255\245\350\277\224\345\233\236ajax\350\257\267\346\261\202\347\273\223\346\236\234\346\226\271\346\263\225.md" similarity index 100% rename from "2-\345\220\214\346\255\245\350\277\224\345\233\236ajax\350\257\267\346\261\202\347\273\223\346\236\234\346\226\271\346\263\225.md" rename to "Cute-Article/article/2-\345\220\214\346\255\245\350\277\224\345\233\236ajax\350\257\267\346\261\202\347\273\223\346\236\234\346\226\271\346\263\225.md" diff --git "a/20-WebSocket\351\207\215\346\226\260\345\255\246\344\271\240.md" "b/Cute-Article/article/20-WebSocket\351\207\215\346\226\260\345\255\246\344\271\240.md" similarity index 100% rename from "20-WebSocket\351\207\215\346\226\260\345\255\246\344\271\240.md" rename to "Cute-Article/article/20-WebSocket\351\207\215\346\226\260\345\255\246\344\271\240.md" diff --git "a/2017-\345\205\250\345\271\264\346\200\273\347\273\223.md" "b/Cute-Article/article/2017-\345\205\250\345\271\264\346\200\273\347\273\223.md" similarity index 100% rename from "2017-\345\205\250\345\271\264\346\200\273\347\273\223.md" rename to "Cute-Article/article/2017-\345\205\250\345\271\264\346\200\273\347\273\223.md" diff --git "a/Cute-Article/article/2018-\345\205\250\345\271\264\346\200\273\347\273\223.md" "b/Cute-Article/article/2018-\345\205\250\345\271\264\346\200\273\347\273\223.md" new file mode 100644 index 00000000..3867c47b --- /dev/null +++ "b/Cute-Article/article/2018-\345\205\250\345\271\264\346\200\273\347\273\223.md" @@ -0,0 +1,202 @@ +**** +|Author|王平安| +|---|---| +|E-mail|pingan8787@qq.com| +|博 客|www.pingan8787.com| +|微 信|pingan8787| +|每日文章推荐|https://github.com/pingan8787/Leo_Reading/issues| +|JS小册|js.pingan8787.com| +**** + +今年一整年,有这么几个感悟: +> 我相信每个人都是幸运,而且聪明。 +> 厚积才能薄发,基础牢固才能做好。 +> 做出改变,做好调整,接受挑战,对自己有好处。 +> 自律很重要。 + +# 零、瞎聊瞎感慨 +2018年,我很高兴,体重比和一个朋友打赌说的那个数字多了15斤,哇。 +2018年,我也很难过,离开了一群很棒的小伙伴,哎。 + +这一年,我买了好几本新书,但都没有看完。 +这一年,我独自去了很多地方,锻炼自己的内心。 + +程序员第二年,我边放边学了深度学习,有点时间不够。 +程序员第二年,我可以总结文章,和同事朋友聊代码聊网络聊计算机原理算法(也是皮毛)。 + +当了第26年的儿子和弟弟,我多往母亲的卡里打了很多钱(我的能力范围内),让家人省心但也让家人操心了。 +当了第26年的儿子和弟弟,我更多的替哥哥姐姐着想,也更清楚家的重要。 + +离开第三年,思想总在围城,但总有一件事能让人彻底释放。 +离开第三年,清空以后,发现我应该我可以做更多事情。 + +这一年的其他角色,有好有坏,愿随时间长河,消逝,冲散,堆积,沉淀。 + +# 一、2018计划完成情况 +开始写这篇总结的时候,我翻了一下 [49-【总结】2017全年总结](http://pingan8787.com/2018/02/21/49-%E3%80%90%E6%80%BB%E7%BB%93%E3%80%912017%E5%85%A8%E5%B9%B4%E6%80%BB%E7%BB%93/) 也深深回忆了一下,感叹,感谢你们。 + +我忐忑的打开 [49-【总结】2018全年计划](pingan8787.com/2018/02/21/49-【总结】2018全年计划/),总觉得,这一年,这么快,也这么慢。 + +## 1.1 2018计划概述 +大概整理一些: +**学习上** +* 前端: +前端知识的基础打牢固,学习React框架,深入研究Vuejs源码,和其他NPM包。 + +* 后端: +学习Nodejs,从Express/Koa去实践,Mongodb也要研究和运用。 + +* 客户端开发: +移动端APP从RN和Weex入手,桌面端放弃掉。 + +* 博客: +改版和模块进来,Github和掘金,开始发原创和维护自己的项目。 + +* 机器学习: +深入去学习这块,可以从图像识别入手,另外可以考虑物联网方面。 + +* 计算机研究: +从一本书开始研究计算机原理,《算法导论》去学习算法,适当学习计算机通信知识。 + +**生活上** +* 日常生活: +换个新宿舍,自己学着做健康早餐,考完驾照,和女票游玩一次。 + +* 健康生活: +多跑步,研究健康饮食。 + +* 家庭生活: +替母亲分担,争取过年给更大礼物,帮家里重新装修,多和哥哥姐姐分享交流,找个女票。 + + +## 1.2 2018计划统计 +检查了下 **2018全年计划** 的完成情况,有些高兴,有些失望,也有些欣慰。 +先来一张github的contributions图: + +![github2018](http://images.pingan8787.com/github2018.png) + +**学习上** +* 前端: +前端基础知识已经复习起来了,在掘金整理了一个系列的文章,还在更新中。 +[pingan8787 掘金](https://juejin.im/user/586fc337a22b9d0058807d53/posts) 和 +[【复习资料】ES6/ES7/ES8/ES9资料整理(个人整理)](https://juejin.im/post/5c02b106f265da61764aa0c1) +这两块大的文章。 +React有学习一些,Vuejs源码也有研究一些,但是现在的工作,用的都是Angular,所以React和Vuejs对于我而言更多的意义是参考和学习。 +NPM包,今年就研究了lodash的源码。 + +* 后端: +`Nodejs`和`Mongodb`开始学习,有用了`Express`和`Mongodb`做了个自己用的小记事本,源码放在 [小小日记本](https://github.com/pingan8787/Leo_Nodejs/tree/master/express/express%2Bmongoose%20%E6%97%A5%E8%AE%B0%E6%9C%AC) 里面,功能比较简单,以后还会开发新的项目。 + +* 客户端开发: +移动端APP:RN研究过一点皮毛,但是如今主攻`flutter`这个非常棒,也要学习`dart`,桌面端放弃掉。 + +* 博客: +博客:添加了【微博】【评论】【样式】等一些东西。 +模块:添加了【ES规范小册】,后面还会有【ES基础】【ES面试】等。 +Github掘金简书思否:开始在同步自己的原创文章,目前主要两块[pingan8787 掘金](https://juejin.im/user/586fc337a22b9d0058807d53/posts) 和 +[【复习资料】ES6/ES7/ES8/ES9资料整理(个人整理)](https://juejin.im/post/5c02b106f265da61764aa0c1) ,也有一些其他的。 +另外我的[每日文章](https://github.com/pingan8787/Leo_Reading/issues)系列也坚持了2018年完整一年了。 + +* 机器学习: +深度学习这块,今年算是从入门到入门吧,后面真的是没有时间去研究,也没有机会去实战,导致`python`的也不熟练,如果接下来还继续,会考虑`JavaScript`实现。但是原理和高数这些,还是得实打实。 + +* 计算机研究: +今年通过网易云课堂学了一些大学计算机基础的知识,了解了很多原理层面的知识,不过还要再深入点。 + +**生活上** +* 日常生活: +宿舍还是没搬,在这里离公司比较近,而且房东一家都很好,觉得可以再住一年。 +健康早餐,一直在坚持煮粥或者燕麦或者核桃粉,搭配面包或包子,感觉还是很棒的。 +女票木有,驾照科三挂了就没去了,因为烦也因为不好请假那么多天事情也多。 + +* 健康生活: +好像今年,跑的真的很少,主要是骑行,30多公里是最多的。 + +* 家庭生活: +借着让母亲帮忙存钱的理由,往家里多寄了更多钱,也存好给母亲买礼物的钱。 +家里重新装修好了,家电设备也换了新的了。 +哥哥姐姐交流更多,也更多的互相理解。 + +# 二、2018总结和分析 +总结和分析,按照以前的框架,总结了下这一年: + +## 2.1 最正确的一件事 +在这一年,做正确的事情,很多,但不可否认,在我内心认为,做得最重要的事情,是来到现在的公司。 +写下这句,心里有点惭愧,但确实是如此,我追求技能提高,技术进步,价值提升,我追求事业追求理想。 +我在WLHD很开心,也很自豪,一群小伙伴,每个人都这么的友善和友好,感谢每个人也很感谢镇智的亦师亦友。 +![WLHD生日蛋糕](http://images.pingan8787.com/2018%E6%80%BB%E7%BB%93-%E8%9B%8B%E7%B3%95.png) + +之所以作为最正确的一件事情,是因为进入到EXE的三个月,我真的学习到很多,跟我自己理想的,计划的是一致的,总结有这几方面: +* 公司主项目,是我所喜欢的,是用户使用频率高,贴近生活的,有前景和价值的,是正导向的。 +* 公司福利,比较符合我的想法。 +* 项目技术栈,是`Angular`这个我爱恨交加的框架,虽然现在我负责的还是用1的版本,但是`Angular`确实是个好家伙,国外大厂都喜欢,并且项目也混合这其他技术进来,好像还有一些我不知道的,也有一些未来的尝试(具体涉及到的名称我删除掉了)。 +* 团队协作,是`git流`,这真的是超级棒,代码管理起来非常方便(虽然刚开始用的时候踩了很多坑),公司用的`JIRA`也特别棒。 +* 团队氛围,开发职责分明,有大牛,还有宝哥这个超神的人,每周二周四晚上的技术分享会,技术氛围特别好。 + +在公司每次和同事,特别是宝哥,都会给我们讲很多很棒的知识,大到前沿技术小到源码阅读等,受到宝哥的影响,我开始尝试学习和分享,坚持下来。感觉很棒。 +家里没有矿也没有油,我只能激励自己,做得更好,希望路过我的世界的你们,都能好,能更好。 + +## 2.2 最错误的一件事 +可能,没有最,因为我回忆了好久,没有想到最错误。 +但有一件事,我心里比较清楚,只是有点愧疚,不想写,愿这样做,会是做好的结果。 + +## 2.3 最疯狂的一件事 +这必须是**世界杯**,哈哈,四年一届,2014年的世界杯,我还在实习,不得不说时间过得真的快。 +这届世界杯,我差不多看了35场左右,基本都是半夜12点 2点 4点开始的球赛,有时候一个晚上2场,直接看到早上,并且第二天要上班,然而我还能精神,也是不容易。 +这届世界杯,喜怒哀乐都有,花了点小球买了球,德国的早早淘汰,中国的迟迟观望,法国克罗地亚的超常发挥,日本韩国的眼前一亮,西班牙葡萄牙的惊险刺激,还有一些熟悉的名字:莫德里奇 C罗 梅西 姆巴佩 凯恩 库尔图瓦 萨拉赫 久巴 苏亚雷斯 小豌豆 佩佩 德赫亚 伊涅斯塔 吉鲁 格里兹曼 阿圭罗 伊瓜因 等等等等,太多了太棒了。 +我也在想,下届世界杯,那时候的我,会是神马样? + +## 2.4 最开心的一件事 +还是和去年一样,一整年下来,大大小小的开心事,贼多呢,but,最开心,应该是和我哥一起做了一个小项目,一个很有意思的项目。 +这个项目我们两个人,一个出想法,一个敲代码,我感觉很棒,和哥哥齐心协力,赚钱,兄弟应该是这样的。 +虽然呢,赚不多,但是,是好的开始,兄弟应该一起加油的。 +现在也是经常跟我哥一起聊一些赚钱的项目,聊一些我们的人生观价值观等,对一些事情的看法,更加互相了解,我哥是我的榜样。 + +## 2.5 2018工作学习上 +现在是北京时间(2019.01.06 02:14),可能写完会更晚。 +工作上分为两块: +* WLHD期间: +**第一阶段:** +人工智能研究,还在为人工智能深度学习挥洒汗水,虽然工作上没有实际用到,但是更重要的意义在于,让我拓展了视野,知道更广阔的计算机世界,还有知识与实践结合,也清楚了人工智能对于未来的意义所在。 +**第二阶段:** +公司旧项目维护和新项目开展,大约有半年左右,这段时间,对于后台管理系统的完善,我有个更清晰的认识,就是对于开发后台管理系统,要多考虑数据默认值,搜索性能,路由安全权限,组件模块化。对于公司人才的招聘,我也是有些自己的感想,由于团队小,招聘进来的人,需要性格跟团队合得来,技术上需要看得到热情。 +**第三阶段:** +项目转型,转型往往会使人一下子难以适应,产生迷茫,自己该坚持的事情依然坚持,但环境却在变化,变化着,有时候转过身来才发现,原来自己再原地走。 + +* EXE期间: +**第一阶段:** +适应期,这段时间有各种不适应,也担心出错,也积极去融入团队和公司,这段时间经历了尴尬的团队成员的认识,经历了公司的羽毛球比赛,经历了合并掉其他同事的代码,经历了发版之前的代码大修改,经历了好多次的通宵,经历了一次严重的感冒,经历... +**第二阶段:** +稳定成长期,这段时间算开始稳定和熟悉了,日常工作也开始正常,也跟着公司的脚步开始学习一些新技术,特别是`flutter`,真香。 +现在也更懂得项目的团队协作方式,并且这是非常重要的,好比前面写到的。 + +学习上: +* 今年也参加了好几个前端技术分享会,确实,大公司大团队新技术,这些都是让我大开眼界,更加坚信前端的未来前景。 +* 这一年学习的新知识可以说是内容多范围广,大到人工智能机器学习,小到冒泡算法等,学的越多,越需要总结和记录。 + +## 2.6 2018感情上 +这一年的感情生活,相比去年,有进步,被困扰的感情,已经都放开了,放过彼此,我也过得更好,对于自己内心的想法,还是早点告知免得浪费对方时间。出现在我的世界的人,依然在。 +这一年,似乎对于找女朋友的看法,也在转变,有很多的应该,也有很多的至少,这算是一些条件。 +反正,对我来说,能聊的开心,这是重要的。 + +## 2.7 2018养成新习惯 +这一年新养成的一些习惯,相比去年,增加了几个,也时常告诉着自己,自律自律自律: +* 第一个,学习上,开始去按照一个系列的内容,去系统的学习和回顾知识,这包括我整理下来的[pingan8787 掘金](https://juejin.im/user/586fc337a22b9d0058807d53/posts) 和 +[【复习资料】ES6/ES7/ES8/ES9资料整理(个人整理)](https://juejin.im/post/5c02b106f265da61764aa0c1) 这两个系列,按照系列的课程系统学习,是一种循序渐进的过程,才能深入去学习。现在的系统学习,我更偏向于,学习+记录,将复习和学习完的知识点,按照自己的逻辑写一遍,补充完善,成为自己的东西,也方便以后查阅。 +* 第二个,迁移自己的开发环境,慢慢把自己开发环境往Mac迁移,因为公司给的电脑是Win的额,只能用自己的Mac系统,不过确实Mac很适合我们前端开发呢。 +* 第三个,更注重健康生活,每天必须坚持做的几个:早上不会着急马上起床,必须伸懒腰全身舒展后休息一会才起床,毕竟急急忙忙起床会影响一天的工作情绪;早起必须做早操,活动下全身,很棒的感觉;晚上睡觉前必须做几组俯卧撑,已经是习惯了。 +* 第四个,坚持公众号推送,不为给别人看,只为自己做积累。 +## 2.8 2018小小欣慰的事 +我更愿意,花时间去看一些书,虽然是后半年才开始,新加入两本书《南方高速》和《地球上最后的夜晚》,似乎境界还不足理解作者真正或深入的内涵,但看书,确实能让人静心,养性,抛开外面的纷繁世界,沉浸自我。 + + +# 完、再来几句 +原来,总结就是个回忆的过程,各种感受都会有,相信我们现在走的每一步,都是我们的成长,以后成为自己喜欢的那个人。 +这一年,也更加相信“你在成就公司,公司也在成就你”这句话了。 + +Tips:我用Markdown也算是熟练一些了,比起去年总结的排版好看多了呢。 + + + + +完:2019.01.06 12:39 \ No newline at end of file diff --git "a/2018-\345\205\250\345\271\264\350\256\241\345\210\222.md" "b/Cute-Article/article/2018-\345\205\250\345\271\264\350\256\241\345\210\222.md" similarity index 59% rename from "2018-\345\205\250\345\271\264\350\256\241\345\210\222.md" rename to "Cute-Article/article/2018-\345\205\250\345\271\264\350\256\241\345\210\222.md" index fbccd84b..07403f46 100644 --- "a/2018-\345\205\250\345\271\264\350\256\241\345\210\222.md" +++ "b/Cute-Article/article/2018-\345\205\250\345\271\264\350\256\241\345\210\222.md" @@ -22,35 +22,43 @@ # 1、工作 今年我会换一份工作,公司偏向正常化,项目正常化,也许我还是比较实在,很多现在公司在做的项目和方向我并不喜欢,有各种担心。 我想说现在的工作,公司的方向不明确项目不靠谱,画饼太多真实性差,同事积极性差,唯我难以带起所有人,我已经很努力做好我本职,包括不是本职我也努力帮忙,努力维护军心,但是依旧难以调动公司氛围,于自身能力也好,于职位有碍也好,我认为公司的Leader必须要有几点,非常重要(个人想法): -* 1.决策力,在该做决定时能有决策能力; -* 2.影响力,能让自己(积极工作等)影响到其他人; -* 3.执行力,在确定的项目上不轻易放弃一个项目,一个idea,一句title; + +1. 决策力,在该做决定时能有决策能力; +2. 影响力,能让自己(积极工作等)影响到其他人; +3. 执行力,在确定的项目上不轻易放弃一个项目,一个idea,一句title; + 其他还有一个,大概意思就是Leader需要对员工负责,关心员工自身发展。 这一年,我想继续试试心目中比较向往的那家公司,一年多的沉淀也许技术上不是非常牛逼,但是我想继续学习更多知识,学习更多项目开发的经验,项目管理等,若能成我将在此潜心学习修行很长时间,我不喜欢现在的虚虚实实现在公司的迷茫,性格问题,我想接触更多有意思的人和事。 # 2、学习 通过去年一年的各种踩坑试错,慢慢总结出自己的一套学习方法,不能说高效,但适合现在的自己,2018年继续按照这套学习方法,学习更多知识,踩更多坑,并总结起来,优化调整学习方法。 经过去年的尝试和踩坑,2018年,是时候该好好学习下面几个内容: -* 前端知识: -* 1. 前端基础:HTML/CSS/JavaScript更多基础,将原生知识基础打牢固,还有ES6/7/8/9研究; -* 2. 前端框架:React需要开始学习,Vuejs深入源码学习更多原理知识,还有很多不错的NPM包; -* 后端知识: -* 1. 服务端开发:Nodejs(包含Express/koa等框架)是时候开始研究并运用了; -* 2. 数据库开发:Mongodb(包含Mongoose)也需要开始研究和运用了; -* 客户端开发: -* 1. APP开发:React Native或者Weex,今年应该从这两个方向入手APP开发,开发环境也将开始转向Mac开发; -* 2. 桌面应用程序开发:Electorn可以用,但是我放弃桌面应用程序开发,no why 就是不喜欢; -* 博客: -* 1. 个人博客:需要做改版和调整,添加新标签进来; -* 2. github:几个仓库和issue继续维护,也许会再添加一个自己项目的仓库; -* 3. 掘金:开始尝试发表自己的原创文章; -* 机器学习: -* 1. 深度学习:找个方向深入学习,可能会是图像识别; -* 2. 物联网:我有兴趣; -* 计算机研究: -* 1. 计算机原理:这是我一直想好好研究的内容,去年碍于各种问题未能开展,今年从一本书入手研究; -* 2. 计算机算法:算法真的很有意思,《算法导论》很棒但很难,尝试静心研究下去; -* 3. 计算机通信:反正很棒,暂未想好如何研究; + +**前端知识**: +1. 前端基础:HTML/CSS/JavaScript更多基础,将原生知识基础打牢固,还有ES6/7/8/9研究; +2. 前端框架:React需要开始学习,Vuejs深入源码学习更多原理知识,还有很多不错的NPM包; + +**后端知识**: +1. 服务端开发:Nodejs(包含Express/koa等框架)是时候开始研究并运用了; +2. 数据库开发:Mongodb(包含Mongoose)也需要开始研究和运用了; + +**客户端开发**: +1. APP开发:React Native或者Weex,今年应该从这两个方向入手APP开发,开发环境也将开始转向Mac开发; +2. 桌面应用程序开发:Electorn可以用,但是我放弃桌面应用程序开发,no why 就是不喜欢; + +**博客**: +1. 个人博客:需要做改版和调整,添加新标签进来; +2. github:几个仓库和issue继续维护,也许会再添加一个自己项目的仓库; +3. 掘金:开始尝试发表自己的原创文章; + +**机器学习**: +1. 深度学习:找个方向深入学习,可能会是图像识别; +2. 物联网:我有兴趣; + +**计算机研究**: +1. 计算机原理:这是我一直想好好研究的内容,去年碍于各种问题未能开展,今年从一本书入手研究; +2. 计算机算法:算法真的很有意思,《算法导论》很棒但很难,尝试静心研究下去; +3. 计算机通信:反正很棒,暂未想好如何研究; 差不多这样,满满一整年的任务,还是很重的,毕竟今年是工作上开始转向成熟稳定的一年。 > 这里插入一件小事,一个朋友再向我讨教一个框架的问题,这个框架我花了很多时间研究,ta说我怎么这么厉害懂这么多,我说多花时间研究就懂啦,ta的回答有点出乎我的意料,说真的,我的下巴都快掉了,ta说(大概意思):“我除了上班没事做才研究,不然都没时间研究这些,下班了谁还敲代码!”,这句话当时真心吓到我了,我总结的是:“出门在外学习知识是为了提升自身价值,并非被工作所左右,想要学习知识你就会想办法主动学习,主动自觉的学习!”。说完,便不想回复ta微信,因为发现跟ta认识到现在,基本能了解这个人,抱怨太多而不脚踏实地研究学习。 @@ -59,18 +67,21 @@ # 3.生活 从前年至今,我经历了最苦逼的日子(真正是快吃不起饭的日子),经历了最迷茫混沌的日子(离职和学习的迷茫),经历了最黑暗的加班赶项目的日子(第一次每天加班到晚上十点多周末无休),经历了感情上最纠缠的日子(难以割舍的真正初恋的她),还经历了最奋斗的日子(人工智能深度学习让我真的着迷)等等,这一年的神奇竟然我是都撑了下来,自己的一次成长,看待这些事情更多了一份成熟。 2018年生活规划: -* 日常生活: -* 1. 住宿条件:换一个新的安静点的房间,为以后女朋友一起住做准备; -* 2. 住宿生活:尽量自己煮饭,外卖真心难吃且油腻不健康,早餐一定自己煮,健康且营养; -* 3. 驾驶证:6月份前考完; -* 4. 关于游玩:跟女朋友游玩一次,任性,哈哈; -* 健康生活: -* 1. 跑步健身:6月份左右或者提前,担心自己程序猿坐久了身体出问题,而且我也挺喜欢跑步,放空自己; -* 2. 饮食健康:早餐食谱研究一套,或者在现在的基础丰富,午餐公司,晚餐尽量更好点,夜宵感觉不需要,怕变胖就成中年大叔了; -* 家庭生活: -* 1. 母亲:替母亲分担重任,每个月打更多钱回家,过年买一条比今年大一倍的金项链什么的给母亲,过年红包也会更大,今年还要帮母亲把家里重新装修; -* 2. 女朋友:感情需要更加深入,也许来我这边一起工作生活,彼此磨合,更加了解对方,然后带她去看更大的世界; -* 3. 哥哥姐姐:帮他们分担更多烦恼,多跟他们交流,帮他们做更多我办得到的事情还要我的小外甥,常回家看看; + +**日常生活**: +1. 住宿条件:换一个新的安静点的房间,为以后女朋友一起住做准备; +2. 住宿生活:尽量自己煮饭,外卖真心难吃且油腻不健康,早餐一定自己煮,健康且营养; +3. 驾驶证:6月份前考完; +4. 关于游玩:跟女朋友游玩一次,任性,哈哈; + +**健康生活**: +1. 跑步健身:6月份左右或者提前,担心自己程序猿坐久了身体出问题,而且我也挺喜欢跑步,放空自己; +2. 饮食健康:早餐食谱研究一套,或者在现在的基础丰富,午餐公司,晚餐尽量更好点,夜宵感觉不需要,怕变胖就成中年大叔了; + +**家庭生活**: +1. 母亲:替母亲分担重任,每个月打更多钱回家,过年买一条比今年大一倍的金项链什么的给母亲,过年红包也会更大,今年还要帮母亲把家里重新装修; +2. 女朋友:感情需要更加深入,也许来我这边一起工作生活,彼此磨合,更加了解对方,然后带她去看更大的世界; +3. 哥哥姐姐:帮他们分担更多烦恼,多跟他们交流,帮他们做更多我办得到的事情还要我的小外甥,常回家看看; 2018全年规划基本如此,需要让自己将24小时过成26小时。 去年一年的沉淀,让我看来很多事情更有自己的考虑和考量,2018继续加油,脚踏实地,不忘初心,为自己为家庭也为公司,继续加油!Love coding , Love life . diff --git "a/Cute-Article/article/2019-\345\205\250\345\271\264\350\256\241\345\210\222.md" "b/Cute-Article/article/2019-\345\205\250\345\271\264\350\256\241\345\210\222.md" new file mode 100644 index 00000000..e4716209 --- /dev/null +++ "b/Cute-Article/article/2019-\345\205\250\345\271\264\350\256\241\345\210\222.md" @@ -0,0 +1,100 @@ +**** +|Author|王平安| +|---|---| +|E-mail|pingan8787@qq.com| +|博 客|www.pingan8787.com| +|微 信|pingan8787| +|每日文章推荐|https://github.com/pingan8787/Leo_Reading/issues| +|JS小册|js.pingan8787.com| +**** + +> 我相信每个人都是幸运,而且聪明。 +> 厚积才能薄发,基础牢固才能做好。 +> 做出改变,做好调整,接受挑战,对自己有好处。 +> 自律很重要。 + +以上是摘自我的《2018-全年计划》(这里就不放地址了) + +也许还有一句,我比较喜欢的:你在成就公司,公司也在成就你 + +2019年对我来说,应该会变得更加理性,为人处世,日常生活,今年剩下的12个月(到2020年春节前),计划依旧分三方面:`工作`、`学习`和`生活`。 + +# 1、工作 +今年我会坚持在现在公司沉淀自己,完善自己,也多为公司出力。对于现在公司,我挺满意,产品也好,技术氛围也好,学习氛围还有同事间的氛围,我觉得都很符合我的理想工作环境。而且公司给我们提供很多很棒的资源,如**技术分享会**,**书籍借阅**,**设备支持**,**实践机会**等等,所以今年要利用好这些呢。 +今年呢,工作上有这样的安排: +1. 跟着公司项目的脚步,学习和开发,另外在需求提交之前,一定要做好UI稿**核对**,原型逻辑**核对**等,减少BUG。 +2. 与小伙伴们分享自己的**知识总结**和**技术分享**,锻炼自己,思路和思维。 +3. 向公司老大前辈们学习,对于一些**问题的处理方式**,和**管理团队的方式**等。 +4. 努力尝试能为公司写一些工具,**简化工作流程**,或者**提高工作效率**等。 +5. 多**使用公司产品**,**参与产品运营**,发现问题,提出改善意见等。 +6. 研究一套项目基础架构(如公司现在这一套`flutter`的架构)。 + +这一年工作上,会有更多的挑战,已经能感受到了,还是必须要相信自己,踩坑填坑踩坑填坑...,增长经验沉淀技术咯。 + +# 2.学习 +经过这两年的程序员学习和工作生涯,也算是找到一套自己的学习方法,也是挺适合自己,相信今年的学习之路,效率和质量会高一些。 +今年的话,学习的内容会更加集中,而不会想去年那样涉及那么多方面: + +## 2.1学习内容 + +**前端知识(核心)**: +1. 前端基础:基础依然非常重要,今年需要更深入研究`JavaScript原理层面`(如引擎原理,工作原理等),还有CSS的一些不太常用的知识,另外ES规范还需复习,特别是如`Proxy`/`Set`和`Map`这类用的比较少的,但是用起来效率又特别高的这类知识。 +2. 前端框架:`Angular6`(也有可能升级到7)这个必须学会,而且能用在工作中使用,也需要针对一些知识点去研究原理(这方面可以多跟宝哥还有其他人学习),其他框架,就不需要太去研究,现在觉得学好一个框架,研究清楚原理,其他都会是一样的。 +3. 前端趋势:把握和跟进前端的发展趋势,新技术,新工具等,学着了解未来的变化。 + +**后端知识**: +1. 服务端开发:今年需要把`Nodejs`用于工作中了,开发一些工具,提高公司的开发效率,还是使用`Express`和`Koa`,重点朝`Koa`方向。 +2. 数据库开发:搭配服务端开发,使用`Mongodb`和它延伸出来的`Mongoose`进行实际应用。 + +**混合开发**: +1. APP开发:熟练mac环境开发,学习`flutter`,并且开始使用到公司的一些模块上面(看老大安排),毕竟这个技术很棒,今年自己也要自己开发一个App(demo会是现在在开始的TIMI,正式的后面再规划),`RN`和`Weex`今年不会花时间去研究,除非公司业务需要。 + +**博客**: +1. 个人博客:内容改版,新增栏目(待定),“作品”这个栏目内容要充实上去,同步自己在其他平台推送的文章。今年希望将【原创文章】突破到60篇(目前17篇)。 +2. Cute-JavaScrip:需要添加功能,访问量统计(博客也需要,使用百度流量统计),另外【JS基础系列】【面试题系列】必须完成,有空也需要改一改之前的文章。 +3. github:开源新项目,目前暂定2个(Flutter的两个),将学习过程中生产的一些笔记和代码,整理后也放到github上。另外【每日文章】继续维护,坚持第三年。 +4. 掘金/思否/CSDN/简书:各个平台同步自己的原创文章,首发掘金,另外掘金今年加油**粉丝突破5000**,**点赞突破1W**(虽然不太看重这些)。 + +5. 公众号:继续维护,每日一篇,不做宣传,不去吸粉,只想积累,也许某一天,一个幸运的粉丝发现了它,希望能让他有种如获至宝的感觉。截至今天已经发了第106期 + +**机器学习**: +1. 深度学习:`tensorflow.js`有必要尝试下,毕竟对图像识别还是挺有兴趣的呢。 + +**技术书籍**: +1. CSS:《CSS世界》(喜欢)。 +2. JS:《你不知道的JavaScript》上中下,也是加强基础和原理理解。 +3. 算法:《算法图解》和《学习JavaScript数据结构与算法》(这本之前看过,但没看完),主要还是要多刷题,好的算法能提高代码质量和效率。 +书太多,要取舍,研究好几本就好。 +4. 架构:《前端技术架构》这本需要结合自己工作再去看。 + +**非技术书籍**: +1. 《地球上最后的夜晚》和《南方高速》,尝试着去理解作者想表达的思想和道理。 + +## 2.2小结 +这学习计划,看上去真的多,但是在我看来,这些都是必须学的。 +所以,这当做是一整年的计划,我还需要拆分到具体的时间上面,做安排。 +坚持! +自律! + +# 3.生活 +生活,我向往简单,人和事。 +新公司加班的现象比较常见,但是也是给我提供很多的学习机会。 +这一年的生活,应该是健康,且有更多幸福感的。 + +**日常生活**: +1. 业余生活:少熬夜(必须少熬夜),多锻炼(早操和睡前锻炼要坚持),坚持能自己做饭尽量自己做饭(早餐不吃外面),学习做饭做菜(找老妈讨教,换宿舍以后),毕竟我有做饭神器——小米电饭煲和小米电磁炉,研究下健康食谱。 +2. 住宿条件:换个宿舍,不用多么高档,安静舒适,适合自己或者和未来女票。 +3. 旅游:出去旅游一次,国内外都行(现在护照港澳通行证都有),需要旅游一次放松也开阔下视野。 + +**家庭生活**: +1. 未来的她:还需要好好加油,一起加油,一起做一件事的感觉会非常好。 +2. 母亲:为母亲分担更多的压力,每个月多汇钱给母亲,帮忙开个店铺给母亲做,照顾好母亲。 +3. 哥哥姐姐:常回家看看,多跟他们交流,今年找个项目再和哥哥一起做,亲兄弟团结。另外哥哥今年结婚,我要给他包个很大的红包。 + +2019全年规划基本如此,不论多与少,需要分配好,偶尔迷茫的时候,会给我带来动力。和去年一样,加油让自己24小时过程26小时。 + +沉淀自己,自律生活,为人和善,为公司,为家庭,也为自己。 + + + +leo 2019.01.12 \ No newline at end of file diff --git "a/21-JavaScript\345\274\202\346\255\245\346\234\272\345\210\266\350\257\246\350\247\243.md" "b/Cute-Article/article/21-JavaScript\345\274\202\346\255\245\346\234\272\345\210\266\350\257\246\350\247\243.md" similarity index 100% rename from "21-JavaScript\345\274\202\346\255\245\346\234\272\345\210\266\350\257\246\350\247\243.md" rename to "Cute-Article/article/21-JavaScript\345\274\202\346\255\245\346\234\272\345\210\266\350\257\246\350\247\243.md" diff --git "a/22-JavaScript\344\270\255\346\234\211\350\266\243\347\232\204\345\214\272\345\210\206\345\220\214\346\255\245\345\222\214\345\274\202\346\255\245Ajax.md" "b/Cute-Article/article/22-JavaScript\344\270\255\346\234\211\350\266\243\347\232\204\345\214\272\345\210\206\345\220\214\346\255\245\345\222\214\345\274\202\346\255\245Ajax.md" similarity index 100% rename from "22-JavaScript\344\270\255\346\234\211\350\266\243\347\232\204\345\214\272\345\210\206\345\220\214\346\255\245\345\222\214\345\274\202\346\255\245Ajax.md" rename to "Cute-Article/article/22-JavaScript\344\270\255\346\234\211\350\266\243\347\232\204\345\214\272\345\210\206\345\220\214\346\255\245\345\222\214\345\274\202\346\255\245Ajax.md" diff --git "a/23-JavaScript\345\205\253\345\274\240\346\200\235\347\273\264\345\257\274\345\233\276.md" "b/Cute-Article/article/23-JavaScript\345\205\253\345\274\240\346\200\235\347\273\264\345\257\274\345\233\276.md" similarity index 100% rename from "23-JavaScript\345\205\253\345\274\240\346\200\235\347\273\264\345\257\274\345\233\276.md" rename to "Cute-Article/article/23-JavaScript\345\205\253\345\274\240\346\200\235\347\273\264\345\257\274\345\233\276.md" diff --git "a/24-Vue\346\212\230\350\205\276\350\256\260-\347\273\231Axios\345\201\232\344\270\252\346\214\272\351\235\240\350\260\261\347\232\204\345\260\201\350\243\205\357\274\210\346\212\245\351\224\231,\351\211\264\346\235\203,\350\267\263\350\275\254,\346\213\246\346\210\252,\346\217\220\347\244\272\357\274\211.md" "b/Cute-Article/article/24-Vue\346\212\230\350\205\276\350\256\260-\347\273\231Axios\345\201\232\344\270\252\346\214\272\351\235\240\350\260\261\347\232\204\345\260\201\350\243\205\357\274\210\346\212\245\351\224\231,\351\211\264\346\235\203,\350\267\263\350\275\254,\346\213\246\346\210\252,\346\217\220\347\244\272\357\274\211.md" similarity index 100% rename from "24-Vue\346\212\230\350\205\276\350\256\260-\347\273\231Axios\345\201\232\344\270\252\346\214\272\351\235\240\350\260\261\347\232\204\345\260\201\350\243\205\357\274\210\346\212\245\351\224\231,\351\211\264\346\235\203,\350\267\263\350\275\254,\346\213\246\346\210\252,\346\217\220\347\244\272\357\274\211.md" rename to "Cute-Article/article/24-Vue\346\212\230\350\205\276\350\256\260-\347\273\231Axios\345\201\232\344\270\252\346\214\272\351\235\240\350\260\261\347\232\204\345\260\201\350\243\205\357\274\210\346\212\245\351\224\231,\351\211\264\346\235\203,\350\267\263\350\275\254,\346\213\246\346\210\252,\346\217\220\347\244\272\357\274\211.md" diff --git "a/25-Webpack\345\205\245\351\227\250\346\225\231\347\250\213\346\225\264\347\220\206\357\274\210\346\225\264\347\220\206\344\270\255\357\274\211.md" "b/Cute-Article/article/25-Webpack\345\205\245\351\227\250\346\225\231\347\250\213\346\225\264\347\220\206\357\274\210\346\225\264\347\220\206\344\270\255\357\274\211.md" similarity index 100% rename from "25-Webpack\345\205\245\351\227\250\346\225\231\347\250\213\346\225\264\347\220\206\357\274\210\346\225\264\347\220\206\344\270\255\357\274\211.md" rename to "Cute-Article/article/25-Webpack\345\205\245\351\227\250\346\225\231\347\250\213\346\225\264\347\220\206\357\274\210\346\225\264\347\220\206\344\270\255\357\274\211.md" diff --git "a/26-Webpack\345\270\270\347\224\250\351\205\215\347\275\256\346\225\264\347\220\206\357\274\210\346\225\264\347\220\206\344\270\255\357\274\211.md" "b/Cute-Article/article/26-Webpack\345\270\270\347\224\250\351\205\215\347\275\256\346\225\264\347\220\206\357\274\210\346\225\264\347\220\206\344\270\255\357\274\211.md" similarity index 100% rename from "26-Webpack\345\270\270\347\224\250\351\205\215\347\275\256\346\225\264\347\220\206\357\274\210\346\225\264\347\220\206\344\270\255\357\274\211.md" rename to "Cute-Article/article/26-Webpack\345\270\270\347\224\250\351\205\215\347\275\256\346\225\264\347\220\206\357\274\210\346\225\264\347\220\206\344\270\255\357\274\211.md" diff --git "a/27-Markdowm\350\257\255\346\263\225\346\225\264\347\220\206.md" "b/Cute-Article/article/27-Markdowm\350\257\255\346\263\225\346\225\264\347\220\206.md" similarity index 100% rename from "27-Markdowm\350\257\255\346\263\225\346\225\264\347\220\206.md" rename to "Cute-Article/article/27-Markdowm\350\257\255\346\263\225\346\225\264\347\220\206.md" diff --git "a/28-JavaScript\344\270\255\347\232\204void\350\277\220\347\256\227\347\254\246.md" "b/Cute-Article/article/28-JavaScript\344\270\255\347\232\204void\350\277\220\347\256\227\347\254\246.md" similarity index 100% rename from "28-JavaScript\344\270\255\347\232\204void\350\277\220\347\256\227\347\254\246.md" rename to "Cute-Article/article/28-JavaScript\344\270\255\347\232\204void\350\277\220\347\256\227\347\254\246.md" diff --git "a/29-\345\205\263\344\272\216\351\232\217\346\234\272\346\225\260\347\232\204\344\270\200\344\272\233\346\200\273\347\273\223.md" "b/Cute-Article/article/29-\345\205\263\344\272\216\351\232\217\346\234\272\346\225\260\347\232\204\344\270\200\344\272\233\346\200\273\347\273\223.md" similarity index 100% rename from "29-\345\205\263\344\272\216\351\232\217\346\234\272\346\225\260\347\232\204\344\270\200\344\272\233\346\200\273\347\273\223.md" rename to "Cute-Article/article/29-\345\205\263\344\272\216\351\232\217\346\234\272\346\225\260\347\232\204\344\270\200\344\272\233\346\200\273\347\273\223.md" diff --git "a/3-Promise\347\256\200\345\215\225\347\224\250\346\263\225.md" "b/Cute-Article/article/3-Promise\347\256\200\345\215\225\347\224\250\346\263\225.md" similarity index 100% rename from "3-Promise\347\256\200\345\215\225\347\224\250\346\263\225.md" rename to "Cute-Article/article/3-Promise\347\256\200\345\215\225\347\224\250\346\263\225.md" diff --git "a/30-\344\270\200\346\254\241\350\256\260\344\275\217js\347\232\2046\344\270\252\346\255\243\345\210\231\346\226\271\346\263\225.md" "b/Cute-Article/article/30-\344\270\200\346\254\241\350\256\260\344\275\217js\347\232\2046\344\270\252\346\255\243\345\210\231\346\226\271\346\263\225.md" similarity index 100% rename from "30-\344\270\200\346\254\241\350\256\260\344\275\217js\347\232\2046\344\270\252\346\255\243\345\210\231\346\226\271\346\263\225.md" rename to "Cute-Article/article/30-\344\270\200\346\254\241\350\256\260\344\275\217js\347\232\2046\344\270\252\346\255\243\345\210\231\346\226\271\346\263\225.md" diff --git "a/31-ES6\350\277\231\344\272\233\345\260\261\345\244\237\344\272\206.md" "b/Cute-Article/article/31-ES6\350\277\231\344\272\233\345\260\261\345\244\237\344\272\206.md" similarity index 100% rename from "31-ES6\350\277\231\344\272\233\345\260\261\345\244\237\344\272\206.md" rename to "Cute-Article/article/31-ES6\350\277\231\344\272\233\345\260\261\345\244\237\344\272\206.md" diff --git "a/32-\350\201\212\344\270\200\350\201\212JavaScript\347\232\204IIFE.md" "b/Cute-Article/article/32-\350\201\212\344\270\200\350\201\212JavaScript\347\232\204IIFE.md" similarity index 100% rename from "32-\350\201\212\344\270\200\350\201\212JavaScript\347\232\204IIFE.md" rename to "Cute-Article/article/32-\350\201\212\344\270\200\350\201\212JavaScript\347\232\204IIFE.md" diff --git "a/33-javascript\347\232\204\347\272\257\345\207\275\346\225\260.md" "b/Cute-Article/article/33-javascript\347\232\204\347\272\257\345\207\275\346\225\260.md" similarity index 100% rename from "33-javascript\347\232\204\347\272\257\345\207\275\346\225\260.md" rename to "Cute-Article/article/33-javascript\347\232\204\347\272\257\345\207\275\346\225\260.md" diff --git "a/34-\346\210\221\347\234\274\344\270\255\347\232\204async&await.md" "b/Cute-Article/article/34-\346\210\221\347\234\274\344\270\255\347\232\204async&await.md" similarity index 100% rename from "34-\346\210\221\347\234\274\344\270\255\347\232\204async&await.md" rename to "Cute-Article/article/34-\346\210\221\347\234\274\344\270\255\347\232\204async&await.md" diff --git "a/35-ES6\344\270\255\347\232\204\346\250\241\345\235\227\345\257\274\345\205\245\345\257\274\345\207\272\346\225\264\347\220\206.md" "b/Cute-Article/article/35-ES6\344\270\255\347\232\204\346\250\241\345\235\227\345\257\274\345\205\245\345\257\274\345\207\272\346\225\264\347\220\206.md" similarity index 97% rename from "35-ES6\344\270\255\347\232\204\346\250\241\345\235\227\345\257\274\345\205\245\345\257\274\345\207\272\346\225\264\347\220\206.md" rename to "Cute-Article/article/35-ES6\344\270\255\347\232\204\346\250\241\345\235\227\345\257\274\345\205\245\345\257\274\345\207\272\346\225\264\347\220\206.md" index 7908ac22..73ae0428 100644 --- "a/35-ES6\344\270\255\347\232\204\346\250\241\345\235\227\345\257\274\345\205\245\345\257\274\345\207\272\346\225\264\347\220\206.md" +++ "b/Cute-Article/article/35-ES6\344\270\255\347\232\204\346\250\241\345\235\227\345\257\274\345\205\245\345\257\274\345\207\272\346\225\264\347\220\206.md" @@ -8,7 +8,7 @@ **** ## 1、导出export命令 -如果需要外部获取文件内部变量,需要用`expor`t将变量输出,有三种方式: +如果需要外部获取文件内部变量,需要用`export`将变量输出,有三种方式: ```js //*方式一 单独输出 //main.js @@ -102,4 +102,4 @@ function foo() { export default foo; ``` -> 参考阮一峰[ES6 Module语法](http://es6.ruanyifeng.com/#docs/module) \ No newline at end of file +> 参考阮一峰[ES6 Module语法](http://es6.ruanyifeng.com/#docs/module) diff --git "a/36-\345\245\275\345\245\275\345\255\246\344\271\240toLocaleString\346\226\271\346\263\225.md" "b/Cute-Article/article/36-\345\245\275\345\245\275\345\255\246\344\271\240toLocaleString\346\226\271\346\263\225.md" similarity index 100% rename from "36-\345\245\275\345\245\275\345\255\246\344\271\240toLocaleString\346\226\271\346\263\225.md" rename to "Cute-Article/article/36-\345\245\275\345\245\275\345\255\246\344\271\240toLocaleString\346\226\271\346\263\225.md" diff --git "a/37-JavaScript\344\272\213\344\273\266\345\247\224\346\211\230\350\257\246\350\247\243.md" "b/Cute-Article/article/37-JavaScript\344\272\213\344\273\266\345\247\224\346\211\230\350\257\246\350\247\243.md" similarity index 100% rename from "37-JavaScript\344\272\213\344\273\266\345\247\224\346\211\230\350\257\246\350\247\243.md" rename to "Cute-Article/article/37-JavaScript\344\272\213\344\273\266\345\247\224\346\211\230\350\257\246\350\247\243.md" diff --git "a/Cute-Article/article/38-JavaScript\344\270\255\345\270\270\350\247\201\350\256\276\350\256\241\346\250\241\345\274\217.md" "b/Cute-Article/article/38-JavaScript\344\270\255\345\270\270\350\247\201\350\256\276\350\256\241\346\250\241\345\274\217.md" new file mode 100644 index 00000000..b1578d37 --- /dev/null +++ "b/Cute-Article/article/38-JavaScript\344\270\255\345\270\270\350\247\201\350\256\276\350\256\241\346\250\241\345\274\217.md" @@ -0,0 +1,1247 @@ +开发中,我们或多或少地接触了设计模式,但是很多时候不知道自己使用了哪种设计模式或者说该使用何种设计模式。本文意在梳理常见设计模式的特点,从而对它们有比较清晰的认知。 + +### JavaScript 中常见设计模式 +* 单例模式 +* 策略模式 +* 代理模式 +* 迭代器模式 +* 发布-订阅模式 +* 命令模式 +* 组合模式 +* 模板方法模式 +* 享元模式 +* 职责链模式 +* 中介者模式 +* 装饰者模式 +* 状态模式 +* 适配者模式 + +### 各设计模式关键词 +看完了上述设计模式后,把它们的关键词特点罗列出来,以后提到某种设计模式,进而联想相应的关键词和例子,从而心中有数。 + +|设计模式|特点|案例| +|-|-|-| +|单例模式|一个类只能构造出唯一实例|创建菜单对象| +|策略模式|根据不同参数可以命中不同的策略|动画库里的算法函数| +|代理模式|代理对象和本体对象具有一致的接口|图片预加载| +|迭代器模式|能获取聚合对象的顺序和元素|each([1, 2, 3], cb)| +|发布-订阅模式|PubSub|瀑布流库| +|命令模式|不同对象间约定好相应的接口|按钮和命令的分离| +|组合模式|组合模式在对象间形成一致对待的树形结构|扫描文件夹| +|模板方法模式|父类中定好执行顺序|咖啡和茶| +|享元模式|减少创建实例的个数|男女模具试装| +|职责链模式|通过请求第一个条件,会持续执行后续的条件,直到返回结果为止|if else 优化| +|中介者模式|对象和对象之间借助第三方中介者进行通信|测试结束告知结果| +|装饰者模式|动态地给函数赋能|天冷了穿衣服,热了脱衣服| +|状态模式|每个状态建立一个类,状态改变会产生不同行为|电灯换挡| +|适配者模式|一种数据结构改成另一种数据结构|枚举值接口变更| + + +## 1.单例模式 +### 两个条件 +* 确保只有一个实例 +* 可以全局访问 + +### 适用 +适用于弹框的实现,全局缓存 +### 实现单例模式 +```js +const singleton = function(name) { + this.name = name + this.instance = null +} + +singleton.prototype.getName = function() { + console.log(this.name) +} + +singleton.getInstance = function(name) { + if (!this.instance) { // 关键语句 + this.instance = new singleton(name) + } + return this.instance +} + +// test +const a = singleton.getInstance('a') // 通过 getInstance 来获取实例 +const b = singleton.getInstance('b') +console.log(a === b) +``` +### JavaScript 中的单例模式 +因为 JavaScript 是无类的语言,而且 JS 中的全局对象符合单例模式两个条件。很多时候我们把全局对象当成单例模式来使用, +```js +var obj = {} +``` +### 弹框层的实践 +实现弹框的一种做法是先创建好弹框,然后使之隐藏,这样子的话会浪费部分不必要的 DOM 开销,我们可以在需要弹框的时候再进行创建,同时结合单例模式实现只有一个实例,从而节省部分 DOM 开销。下列为登入框部分代码: +```js +const createLoginLayer = function() { + const div = document.createElement('div') + div.innerHTML = '登入浮框' + div.style.display = 'none' + document.appendChild(div) + return div +} +``` +使单例模式和创建弹框代码解耦 +```js +const getSingle = function(fn) { + const result + return function() { + return result || result = fn.apply(this, arguments) + } +} +``` +```js +const createSingleLoginLayer = getSingle(createLoginLayer) + +document.getElementById('loginBtn').onclick = function() { + createSingleLoginLayer() +} +``` + +*** + +## 2.策略模式 +> 定义:根据不同参数可以命中不同的策略 + +### JavaScript 中的策略模式 +观察如下获取年终奖的 demo,根据不同的参数(level)获得不同策略方法(规则),这是策略模式在 JS 比较经典的运用之一。 +```js +const strategy = { + 'S': function(salary) { + return salary * 4 + }, + 'A': function(salary) { + return salary * 3 + }, + 'B': function(salary) { + return salary * 2 + } +} + +const calculateBonus = function(level, salary) { + return strategy[level](salary) +} + +calculateBonus('A', 10000) // 30000 +``` +在函数是一等公民的 JS 中,策略模式的使用常常隐藏在高阶函数中,稍微变换下上述 demo 的形式如下,可以发现我们平时已经在使用它了,恭喜我们又掌握了一种设计模式。 +```js +const S = function(salary) { + return salary * 4 +} + +const A = function(salary) { + return salary * 3 +} + +const B = function(salary) { + return salary * 2 +} + +const calculateBonus = function(func, salary) { + return func(salary) +} + +calculateBonus(A, 10000) // 30000 +``` +### 优点 +* 能减少大量的 if 语句 +* 复用性好 + +*** + +## 3.代理模式 +情景:小明追女生 A +* 非代理模式:小明 =花=> 女生A +* 代理模式:小明 =花=> 让女生A的好友B帮忙 =花=> 女生A + +### 代理模式的特点 +* 代理对象和本体对象具有一致的接口,对使用者友好 + +代理模式的种类有很多,在 JS 中最常用的为虚拟代理和缓存代理。 +#### 虚拟代理实现图片预加载 +下面这段代码运用代理模式来实现图片预加载,可以看到通过代理模式巧妙地将创建图片与预加载逻辑分离,并且在未来如果不需要预加载,只要改成请求本体代替请求代理对象就行。 +```js +const myImage = (function() { + const imgNode = document.createElement('img') + document.body.appendChild(imgNode) + return { + setSrc: function(src) { + imgNode.src = src + } + } +})() + +const proxyImage = (function() { + const img = new Image() + img.onload = function() { // http 图片加载完毕后才会执行 + myImage.setSrc(this.src) + } + return { + setSrc: function(src) { + myImage.setSrc('loading.jpg') // 本地 loading 图片 + img.src = src + } + } +})() + +proxyImage.setSrc('http://loaded.jpg') +``` + +缓存代理实现乘积计算 +```js +const mult = function() { + let a = 1 + for (let i = 0, l; l = arguments[i++];) { + a = a * l + } + return a +} + +const proxyMult = (function() { + const cache = {} + return function() { + const tag = Array.prototype.join.call(arguments, ',') + if (cache[tag]) { + return cache[tag] + } + cache[tag] = mult.apply(this, arguments) + return cache[tag] + } +})() + +proxyMult(1, 2, 3, 4) // 24 +``` + +### 小 tip +在开发时候不要先去猜测是否需要使用代理模式,如果发现直接使用某个对象不方便时,再来优化不迟。 + +*** + +## 4.迭代器模式 +> 定义:能访问到聚合对象的顺序与元素 +### 实现一个内部迭代器 +```js +function each(arr, fn) { + for (let i = 0; i < arr.length; i++) { + fn(i, arr[i]) + } +} + +each([1, 2, 3], function(i, n) { + console.log(i) // 0 1 2 + console.log(n) // 1 2 3 +}) +``` +可以看出内部迭代器在调用的时候非常简单,使用者不用关心迭代器内部实现的细节,但这也是内部迭代器的缺点。比如要比较两数组是否相等,只能在其回调函数中作文章了,代码如下: +```js +const compare = function(arr1, arr2) { + each(arr1, function(i, n) { + if (arr2[i] !== n) { + console.log('两数组不等') + return + } + }) + console.log('两数组相等') +} + +const arr1 = [1, 2, 3] +const arr2 = [1, 2, 3] +compare(arr1, arr2) // 两数组相等 +``` +### 实现一个外部迭代器 + +相较于内部迭代器,外部迭代器将遍历的权利转移到外部,因此在调用的时候拥有了更多的自由性,不过缺点是调用方式较复杂。 +```js +const iterator = function(arr) { + let current = 0 + const next = function() { + current = current + 1 + } + const done = function() { + return current >= arr.length + } + const value = function() { + return arr[current] + } + return { + next, + done, + value, + } +} + +const arr1 = [1, 2 ,3] +const arr2 = [1, 2, 3] +const iterator1 = iterator(arr1) +const iterator2 = iterator(arr2) + +const compare = function(iterator1, iterator2) { + while (!iterator1.done() && !iterator2.done()) { + if (iterator1.value() !== iterator2.value()) { + console.log('两数组不等') + return + } + iterator1.next() // 外部迭代器将遍历的权利转移到外部 + iterator2.next() + } + console.log('两数组相等') +} + +compare(iterator1, iterator2) +``` + +*** + +## 5.发布订阅模式 +事件发布/订阅模式 (PubSub) 在异步编程中帮助我们完成更松的解耦,甚至在 MVC、MVVC 的架构中以及设计模式中也少不了发布-订阅模式的参与。 +优点:在异步编程中实现更深的解耦 +缺点:如果过多的使用发布订阅模式,会增加维护的难度 + +### 实现一个发布订阅模式 +```js +var Event = function() { + this.obj = {} +} + +Event.prototype.on = function(eventType, fn) { + if (!this.obj[eventType]) { + this.obj[eventType] = [] + } + this.obj[eventType].push(fn) +} + +Event.prototype.emit = function() { + var eventType = Array.prototype.shift.call(arguments) + var arr = this.obj[eventType] + for (let i = 0; i < arr.length; i++) { + arr[i].apply(arr[i], arguments) + } +} + +var ev = new Event() + +ev.on('click', function(a) { // 订阅函数 + console.log(a) // 1 +}) + +ev.emit('click', 1) // 发布函数 +``` + +### 订阅函数逻辑一定要优先于发布函数吗 +考虑以下场景: +```js +$.ajax('', () => { + // 异步订阅函数逻辑 +}) + +// 在其他地方执行发布函数,此时并不能保证执行发布函数的时候,订阅函数已经执行 +``` +我们需要实现这样的逻辑: +```js +var ev = new Event() +ev.emit('click', 1) + +ev.on('click', function(a) { + console.log(a) // 1 +}) +``` +目标明确后,来着手实现它: +```js +var Event = function() { + this.obj = {} + this.cacheList = [] +} + +Event.prototype.on = function(eventType, fn) { + if (!this.obj[eventType]) { + this.obj[eventType] = [] + } + this.obj[eventType].push(fn) + + for (let i = 0; i < this.cacheList.length; i++) { + this.cacheList[i]() + } +} + +Event.prototype.emit = function() { + const arg = arguments + const that = this + function cache() { + var eventType = Array.prototype.shift.call(arg) + var arr = that.obj[eventType] + for (let i = 0; i < arr.length; i++) { + arr[i].apply(arr[i], arg) + } + } + this.cacheList.push(cache) +} +``` +以上代码实现思路就是把原本在 `emit` 里触发的函数存到 `cacheList`,再转交到 `on` 中触发。从而实现了发布函数先于订阅函数执行。 + +*** + +## 6.命令模式 +命令模式与策略模式有些类似,在 JavaScript 中它们都是隐式的。 +重要性:较低 +### JavaScript 中的命令模式 +命令模式在 JavaScript 中也比较简单,下面代码中对按钮和命令进行了抽离,因此可以复杂项目中可以使用命令模式将界面的代码和功能的代码交付给不同的人去写。 +```js +const setCommand = function(button, command) { + button.onClick = function() { + command.excute() + } +} + +// -------------------- 上面的界面逻辑由A完成,下面的由B完成 + +const menu = { + updateMenu: function() { + console.log('更新菜单') + }, +} + +const UpdateCommand = function(receive) { + return { + excute: receive.updateMenu, + } +} + +const updateCommand = UpdateCommand(menu) // 创建命令 + +const button1 = document.getElementById('button1') +setCommand(button1, updateCommand) +``` + +*** + +## 7.组合模式 +* 组合模式在对象间形成树形结构; +* 组合模式中基本对象和组合对象被一致对待; +* 无须关心对象有多少层,调用时只需在根部进行调用; + +### demo1 —— 宏命令 + +想象我们现在手上有个万能遥控器,当我们回家,按一下开关,下列事情将被执行: +1. 煮咖啡 +2. 打开电视、打开音响 +3. 打开空调、打开电脑 + +我们把任务划分为 3 类,效果图如下: +![任务划分](https://camo.githubusercontent.com/a44e97b789bd6e25d1aa13b15b12239c0566b015/687474703a2f2f6f71687473637573302e626b742e636c6f7564646e2e636f6d2f39633837636538333535313566336439623630613836613066323838393764392e6a70672d343030) +接着看看结合了命令模式和组合模式的具体实现: +```js +const MacroCommand = function() { + return { + lists: [], + add: function(task) { + this.lists.push(task) + }, + excute: function() { // ①:组合对象调用这里的 excute, + for (let i = 0; i < this.lists.length; i++) { + this.lists[i].excute() + } + }, + } +} + +const command1 = MacroCommand() // 基本对象 + +command1.add({ + excute: () => console.log('煮咖啡') // ②:基本对象调用这里的 excute, +}) + +const command2 = MacroCommand() // 组合对象 + +command2.add({ + excute: () => console.log('打开电视') +}) + +command2.add({ + excute: () => console.log('打开音响') +}) + +const command3 = MacroCommand() + +command3.add({ + excute: () => console.log('打开空调') +}) + +command3.add({ + excute: () => console.log('打开电脑') +}) + +const macroCommand = MacroCommand() +macroCommand.add(command1) +macroCommand.add(command2) +macroCommand.add(command3) + +macroCommand.excute() + +// 煮咖啡 +// 打开电视 +// 打开音响 +// 打开空调 +// 打开电脑 +``` + +可以看出在组合模式中基本对象和组合对象被一致对待,所以要保证基本对象(叶对象)和组合对象具有一致方法。 + +### demo2 —— 扫描文件夹 +扫描文件夹时,文件夹下面可以为另一个文件夹也可以为文件,我们希望统一对待这些文件夹和文件,这种情形适合使用组合模式。 +```js +const Folder = function(folder) { + this.folder = folder + this.lists = [] +} + +Folder.prototype.add = function(resource) { + this.lists.push(resource) +} + +Folder.prototype.scan = function() { + console.log('开始扫描文件夹:', this.folder) + for (let i = 0, folder; folder = this.lists[i++];) { + folder.scan() + } +} + +const File = function(file) { + this.file = file +} + +File.prototype.add = function() { + throw Error('文件下不能添加其它文件夹或文件') +} + +File.prototype.scan = function() { + console.log('开始扫描文件:', this.file) +} + +const folder = new Folder('根文件夹') +const folder1 = new Folder('JS') +const folder2 = new Folder('life') + +const file1 = new File('深入React技术栈.pdf') +const file2 = new File('JavaScript权威指南.pdf') +const file3 = new File('小王子.pdf') + +folder1.add(file1) +folder1.add(file2) + +folder2.add(file3) + +folder.add(folder1) +folder.add(folder2) + +folder.scan() + +// 开始扫描文件夹: 根文件夹 +// 开始扫描文件夹: JS +// 开始扫描文件: 深入React技术栈.pdf +// 开始扫描文件: JavaScript权威指南.pdf +// 开始扫描文件夹: life +// 开始扫描文件: 小王子.pdf +``` + +*** + +## 8.模板方法模式 +> 定义:在继承的基础上,在父类中定义好执行的算法。 +> +### 泡茶和泡咖啡 +来对比下泡茶和泡咖啡过程中的异同 + +|步骤|泡茶|泡咖啡| +|-|-|-| +|1|烧开水|烧开水| +|2|浸泡茶叶|冲泡咖啡| +|3|倒入杯子|倒入杯子| +|4|加柠檬|加糖| + +可以清晰地看出仅仅在步骤 2 和 4 上有细微的差别,下面着手实现: +```js +const Drinks = function() {} + +Drinks.prototype.firstStep = function() { + console.log('烧开水') +} + +Drinks.prototype.secondStep = function() {} + +Drinks.prototype.thirdStep = function() { + console.log('倒入杯子') +} + +Drinks.prototype.fourthStep = function() {} + +Drinks.prototype.init = function() { // 模板方法模式核心:在父类上定义好执行算法 + this.firstStep() + this.secondStep() + this.thirdStep() + this.fourthStep() +} + +const Tea = function() {} + +Tea.prototype = new Drinks + +Tea.prototype.secondStep = function() { + console.log('浸泡茶叶') +} + +Tea.prototype.fourthStep = function() { + console.log('加柠檬') +} + +const Coffee = function() {} + +Coffee.prototype = new Drinks + +Coffee.prototype.secondStep = function() { + console.log('冲泡咖啡') +} + +Coffee.prototype.fourthStep = function() { + console.log('加糖') +} + +const tea = new Tea() +tea.init() + +// 烧开水 +// 浸泡茶叶 +// 倒入杯子 +// 加柠檬 + +const coffee = new Coffee() +coffee.init() + +// 烧开水 +// 冲泡咖啡 +// 倒入杯子 +// 加糖 +``` + +### 钩子 +假如客人不想加佐料(糖、柠檬)怎么办,这时可以引人钩子来实现之,实现逻辑如下: +```js +// ... + +Drinks.prototype.ifNeedFlavour = function() { // 加上钩子 + return true +} + +Drinks.prototype.init = function() { // 模板方法模式核心:在父类上定义好执行算法 + this.firstStep() + this.secondStep() + this.thirdStep() + if (this.ifNeedFlavour()) { // 默认是 true,也就是要加调料 + this.fourthStep() + } +} + +// ... +const Coffee = function() {} + +Coffee.prototype = new Drinks() +// ... + +Coffee.prototype.ifNeedFlavour = function() { + return window.confirm('是否需要佐料吗?') // 弹框选择是否佐料 +} +``` + +*** + +## 9.享元模式 +享元模式是一种优化程序性能的模式,本质为减少对象创建的个数。 + +以下情况可以使用享元模式: +* 有大量相似的对象,占用了大量内存 +* 对象中大部分状态可以抽离为外部状态 + +### demo +某商家有 50 种男款内衣和 50 种款女款内衣,要展示它们 + +方案一:造 50 个塑料男模和 50 个塑料女模,让他们穿上展示,代码如下: +```js +const Model = function(gender, underwear) { + this.gender = gender + this.underwear = underwear +} + +Model.prototype.takephoto = function() { + console.log(`${this.gender}穿着${this.underwear}`) +} + +for (let i = 1; i < 51; i++) { + const maleModel = new Model('male', `第${i}款衣服`) + maleModel.takephoto() +} + +for (let i = 1; i < 51; i++) { + const female = new Model('female', `第${i}款衣服`) + female.takephoto() +} +``` + +方案二:造 1 个塑料男模特 1 个塑料女模特,分别试穿 50 款内衣 +```js +const Model = function(gender) { + this.gender = gender +} + +Model.prototype.takephoto = function() { + console.log(`${this.sex}穿着${this.underwear}`) +} + +const maleModel = new Model('male') +const femaleModel = new Model('female') + +for (let i = 1; i < 51; i++) { + maleModel.underwear = `第${i}款衣服` + maleModel.takephoto() +} + +for (let i = 1; i < 51; i++) { + femaleModel.underwear = `第${i}款衣服` + femaleModel.takephoto() +} +``` +对比发现:方案一创建了 100 个对象,方案二只创建了 2 个对象,在该 demo 中,gender(性别) 是内部对象,underwear(穿着) 是外部对象。 + +当然在方案二的 demo 中,还可以进一步改善: + +* 一开始就通过构造函数显示地创建实例,可用工场模式将其升级成可控生成 +* 在实例上手动添加 underwear 不是很优雅,可以在外部单独在写个 manager 函数 +```js +const Model = function(gender) { + this.gender = gender +} + +Model.prototype.takephoto = function() { + console.log(`${this.gender}穿着${this.underwear}`) +} + +const modelFactory = (function() { // 优化第一点 + const modelGender = {} + return { + createModel: function(gender) { + if (modelGender[gender]) { + return modelGender[gender] + } + return modelGender[gender] = new Model(gender) + } + } +}()) + +const modelManager = (function() { + const modelObj = {} + return { + add: function(gender, i) { + modelObj[i] = { + underwear: `第${i}款衣服` + } + return modelFactory.createModel(gender) + }, + copy: function(model, i) { // 优化第二点 + model.underwear = modelObj[i].underwear + } + } +}()) + +for (let i = 1; i < 51; i++) { + const maleModel = modelManager.add('male', i) + modelManager.copy(maleModel, i) + maleModel.takephoto() +} + +for (let i = 1; i < 51; i++) { + const femaleModel = modelManager.add('female', i) + modelManager.copy(femaleModel, i) + femaleModel.takephoto() +} +``` + +*** + +## 10.职责链模式 +职责链模式:类似多米诺骨牌,通过请求第一个条件,会持续执行后续的条件,直到返回结果为止。 +![职责链模式](https://camo.githubusercontent.com/cb2073f5e9c165843754e0b5984652c3291e88a6/687474703a2f2f6f71687473637573302e626b742e636c6f7564646e2e636f6d2f63626338633130626261653230326263643234336636623037303464653362612e6a70672d333030) +重要性:4 星,在项目中能对 if-else 语句进行优化 +### 场景 demo +场景:某电商针对已付过定金的用户有优惠政策,在正式购买后,已经支付过 500 元定金的用户会收到 100 元的优惠券,200 元定金的用户可以收到 50 元优惠券,没有支付过定金的用户只能正常购买。 +```js +// orderType: 表示订单类型,1:500 元定金用户;2:200 元定金用户;3:普通购买用户 +// pay:表示用户是否已经支付定金,true: 已支付;false:未支付 +// stock: 表示当前用于普通购买的手机库存数量,已支付过定金的用户不受此限制 + +const order = function( orderType, pay, stock ) { + if ( orderType === 1 ) { + if ( pay === true ) { + console.log('500 元定金预购,得到 100 元优惠券') + } else { + if (stock > 0) { + console.log('普通购买,无优惠券') + } else { + console.log('库存不够,无法购买') + } + } + } else if ( orderType === 2 ) { + if ( pay === true ) { + console.log('200 元定金预购,得到 50 元优惠券') + } else { + if (stock > 0) { + console.log('普通购买,无优惠券') + } else { + console.log('库存不够,无法购买') + } + } + } else if ( orderType === 3 ) { + if (stock > 0) { + console.log('普通购买,无优惠券') + } else { + console.log('库存不够,无法购买') + } + } +} + +order( 3, true, 500 ) // 普通购买,无优惠券 +``` + +下面用职责链模式改造代码: +```js +const order500 = function(orderType, pay, stock) { + if ( orderType === 1 && pay === true ) { + console.log('500 元定金预购,得到 100 元优惠券') + } else { + order200(orderType, pay, stock) + } +} + +const order200 = function(orderType, pay, stock) { + if ( orderType === 2 && pay === true ) { + console.log('200 元定金预购,得到 50 元优惠券') + } else { + orderCommon(orderType, pay, stock) + } +} + +const orderCommon = function(orderType, pay, stock) { + if (orderType === 3 && stock > 0) { + console.log('普通购买,无优惠券') + } else { + console.log('库存不够,无法购买') + } +} + +order500( 3, true, 500 ) // 普通购买,无优惠券 +``` + +改造后可以发现代码相对清晰了,但是链路代码和业务代码依然耦合在一起,进一步优化: +```js +// 业务代码 +const order500 = function(orderType, pay, stock) { + if ( orderType === 1 && pay === true ) { + console.log('500 元定金预购,得到 100 元优惠券') + } else { + return 'nextSuccess' + } +} + +const order200 = function(orderType, pay, stock) { + if ( orderType === 2 && pay === true ) { + console.log('200 元定金预购,得到 50 元优惠券') + } else { + return 'nextSuccess' + } +} + +const orderCommon = function(orderType, pay, stock) { + if (orderType === 3 && stock > 0) { + console.log('普通购买,无优惠券') + } else { + console.log('库存不够,无法购买') + } +} + +// 链路代码 +const chain = function(fn) { + this.fn = fn + this.sucessor = null +} + +chain.prototype.setNext = function(sucessor) { + this.sucessor = sucessor +} + +chain.prototype.init = function() { + const result = this.fn.apply(this, arguments) + if (result === 'nextSuccess') { + this.sucessor.init.apply(this.sucessor, arguments) + } +} + +const order500New = new chain(order500) +const order200New = new chain(order200) +const orderCommonNew = new chain(orderCommon) + +order500New.setNext(order200New) +order200New.setNext(orderCommonNew) + +order500New.init( 3, true, 500 ) // 普通购买,无优惠券 +``` + +重构后,链路代码和业务代码彻底地分离。假如未来需要新增 order300,那只需新增与其相关的函数而不必改动原有业务代码。 +另外结合 AOP 还能简化上述链路代码: +```js +// 业务代码 +const order500 = function(orderType, pay, stock) { + if ( orderType === 1 && pay === true ) { + console.log('500 元定金预购,得到 100 元优惠券') + } else { + return 'nextSuccess' + } +} + +const order200 = function(orderType, pay, stock) { + if ( orderType === 2 && pay === true ) { + console.log('200 元定金预购,得到 50 元优惠券') + } else { + return 'nextSuccess' + } +} + +const orderCommon = function(orderType, pay, stock) { + if (orderType === 3 && stock > 0) { + console.log('普通购买,无优惠券') + } else { + console.log('库存不够,无法购买') + } +} + +// 链路代码 +Function.prototype.after = function(fn) { + const self = this + return function() { + const result = self.apply(self, arguments) + if (result === 'nextSuccess') { + return fn.apply(self, arguments) // 这里 return 别忘记了~ + } + } +} + +const order = order500.after(order200).after(orderCommon) + +order( 3, true, 500 ) // 普通购买,无优惠券 +``` +职责链模式比较重要,项目中能用到它的地方会有很多,用上它能解耦 1 个请求对象和 n 个目标对象的关系。 + +*** + +## 11.中介者模式 +中介者模式:对象和对象之间借助第三方中介者进行通信。 +![中介者模式](https://camo.githubusercontent.com/8411d6ad7b3c4e4f4fa3a14115f33428f5e4ab0f/687474703a2f2f6f71687473637573302e626b742e636c6f7564646e2e636f6d2f61653039353866383539393039373863343862336136616132636137366561312e6a70672d343030) +### 场景 demo +一场测试结束后,公布结果:告知解答出题目的人挑战成功,否则挑战失败。 +```js +const player = function(name) { + this.name = name + playerMiddle.add(name) +} + +player.prototype.win = function() { + playerMiddle.win(this.name) +} + +player.prototype.lose = function() { + playerMiddle.lose(this.name) +} + +const playerMiddle = (function() { // 将就用下这个 demo,这个函数当成中介者 + const players = [] + const winArr = [] + const loseArr = [] + return { + add: function(name) { + players.push(name) + }, + win: function(name) { + winArr.push(name) + if (winArr.length + loseArr.length === players.length) { + this.show() + } + }, + lose: function(name) { + loseArr.push(name) + if (winArr.length + loseArr.length === players.length) { + this.show() + } + }, + show: function() { + for (let winner of winArr) { + console.log(winner + '挑战成功;') + } + for (let loser of loseArr) { + console.log(loser + '挑战失败;') + } + }, + } +}()) + +const a = new player('A 选手') +const b = new player('B 选手') +const c = new player('C 选手') + +a.win() +b.win() +c.lose() + +// A 选手挑战成功; +// B 选手挑战成功; +// C 选手挑战失败; +``` +在这段代码中 A、B、C 之间没有直接发生关系,而是通过另外的 playerMiddle 对象建立链接,姑且将之当成是中介者模式了。 + +*** + +## 12.装饰者模式 +装饰器模式:动态地给函数赋能。 + +### JavaScript 的装饰者模式 +生活中的例子:天气冷了,就添加衣服来保暖;天气热了,就将外套脱下;这个例子很形象地含盖了装饰器的神韵,随着天气的冷暖变化,衣服可以动态的穿上脱下。 +```js +let wear = function() { + console.log('穿上第一件衣服') +} + +const _wear1 = wear + +wear = function() { + _wear1() + console.log('穿上第二件衣服') +} + +const _wear2 = wear + +wear = function() { + _wear2() + console.log('穿上第三件衣服') +} + +wear() + +// 穿上第一件衣服 +// 穿上第二件衣服 +// 穿上第三件衣服 +``` + +这种方式有以下缺点:1:临时变量会变得越来越多;2:this 指向有时会出错 +### AOP 装饰函数 +```js +// 前置代码 +Function.prototype.before = function(fn) { + const self = this + return function() { + fn.apply(this, arguments) + return self.apply(this, arguments) + } +} + +// 后置代码 +Function.prototype.after = function(fn) { + const self = this + return function() { + self.apply(this, arguments) + return fn.apply(this, arguments) + } +} +``` +用后置代码来实验下上面穿衣服的 demo, +```js +const wear1 = function() { + console.log('穿上第一件衣服') +} + +const wear2 = function() { + console.log('穿上第二件衣服') +} + +const wear3 = function() { + console.log('穿上第三件衣服') +} + +const wear = wear1.after(wear2).after(wear3) +wear() + +// 穿上第一件衣服 +// 穿上第二件衣服 +// 穿上第三件衣服 +``` + +但这样子有时会污染原生函数,可以做点通变 +```js +const after = function(fn, afterFn) { + return function() { + fn.apply(this, arguments) + afterFn.apply(this, arguments) + } +} + +const wear = after(after(wear1, wear2), wear3) +wear() +``` + +*** + +## 13.状态模式 +状态模式:将事物内部的每个状态分别封装成类,内部状态改变会产生不同行为。 + +优点:用对象代替字符串记录当前状态,状态易维护 +缺点:需编写大量状态类对象 + +### 场景 demo +某某牌电灯,按一下按钮打开弱光,按两下按钮打开强光,按三下按钮关闭灯光。 +```js +// 将状态封装成不同类 +const weakLight = function(light) { + this.light = light +} + +weakLight.prototype.press = function() { + console.log('打开强光') + this.light.setState(this.light.strongLight) +} + +const strongLight = function(light) { + this.light = light +} + +strongLight.prototype.press = function() { + console.log('关灯') + this.light.setState(this.light.offLight) +} + +const offLight = function(light) { + this.light = light +} + +offLight.prototype.press = function() { + console.log('打开弱光') + this.light.setState(this.light.weakLight) +} + +const Light = function() { + this.weakLight = new weakLight(this) + this.strongLight = new strongLight(this) + this.offLight = new offLight(this) + this.currentState = this.offLight // 初始状态 +} + +Light.prototype.init = function() { + const btn = document.createElement('button') + btn.innerHTML = '按钮' + document.body.append(btn) + const self = this + btn.addEventListener('click', function() { + self.currentState.press() + }) +} + +Light.prototype.setState = function(state) { // 改变当前状态 + this.currentState = state +} + +const light = new Light() +light.init() + +// 打开弱光 +// 打开强光 +// 关灯 +``` + +### 非面向对象实现的状态模式 +借助于 JavaScript 的委托机制,可以像如下实现状态模式: +```js +const obj = { + 'weakLight': { + press: function() { + console.log('打开强光') + this.currentState = obj.strongLight + } + }, + 'strongLight': { + press: function() { + console.log('关灯') + this.currentState = obj.offLight + } + }, + 'offLight': { + press: function() { + console.log('打开弱光') + this.currentState = obj.weakLight + } + }, +} + +const Light = function() { + this.currentState = obj.offLight +} + +Light.prototype.init = function() { + const btn = document.createElement('button') + btn.innerHTML = '按钮' + document.body.append(btn) + const self = this + btn.addEventListener('click', function() { + self.currentState.press.call(self) // 通过 call 完成委托 + }) +} + +const light = new Light() +light.init() +``` + +*** + +## 14.适配者模式 +适配者模式:主要用于解决两个接口之间不匹配的问题。 +### demo +```js +// 老接口 +const zhejiangCityOld = (function() { + return [ + { + name: 'hangzhou', + id: 11, + }, + { + name: 'jinhua', + id: 12 + } + ] +}()) + +console.log(getZhejiangCityOld()) + +// 新接口希望是下面形式 +{ + hangzhou: 11, + jinhua: 12, +} + +// 这时候就可采用适配者模式 +const const adaptor = (function(oldCity) { + const obj = {} + for (let city of zhejiangCityOld) { + obj[city.name] = city.id + } + return obj +}()) +``` + + +> 原文地址 [JavaScript 中常见设计模式整理](https://juejin.im/post/5afe6430518825428630bc4d) \ No newline at end of file diff --git "a/Cute-Article/article/39-\344\275\234\344\270\272\345\211\215\347\253\257\351\234\200\350\246\201\344\272\206\350\247\243\347\232\204\345\274\200\346\272\220\345\215\217\350\256\256\347\237\245\350\257\206.md" "b/Cute-Article/article/39-\344\275\234\344\270\272\345\211\215\347\253\257\351\234\200\350\246\201\344\272\206\350\247\243\347\232\204\345\274\200\346\272\220\345\215\217\350\256\256\347\237\245\350\257\206.md" new file mode 100644 index 00000000..8345ff42 --- /dev/null +++ "b/Cute-Article/article/39-\344\275\234\344\270\272\345\211\215\347\253\257\351\234\200\350\246\201\344\272\206\350\247\243\347\232\204\345\274\200\346\272\220\345\215\217\350\256\256\347\237\245\350\257\206.md" @@ -0,0 +1,112 @@ +* 作为前端工程师,开发中在所难免会用到一些开源框架,而每个框架都有自己的开源协议,每个开源协议之间有什么差别呢? 如果你要开源一个项目,又应该选择哪种开源协议呢? +* 许多开发者,对于开源协议的认知很少,本文从这些常用的前端框架入手,介绍开源常用开源协议的基础知识。 + +### 什么是开源协议? +根据 [开源协议](https://en.wikipedia.org/wiki/Open-source_license) 在维基百科的定义: +> 开源许可是一种计算机软件和其他产品的许可类型,允许使用、修改或在定义的条款和条件下使用、修改或共享的源代码、蓝图和设计。 +> 这允许终端用户和商业公司对源代码、图纸或设计进行审查和修改,以满足自己的定制、好奇心或故障排除的需要。 +> 开源许可的软件大多是免费的,尽管这并不一定是必须的。许可证只允许非商业的重新分配或修改个人使用的源代码,通常不被认为是开源许可。 +> 然而,开源许可可能会有一些限制,尤其是对软件的起源的表达,比如要求保留作者的名字和代码中的版权声明,或要求重新分配授权软件只有在相同的许可(如copyleft许可证)。 +> 一组流行的开源软件许可证是由开源计划(OSI)根据其开源定义(OSD)批准的。 + + +### 为什么要选用开源协议? +在 [GcsSloop](http://www.gcssloop.com/) 写的文章 [程序员不可不知的版权协议](http://www.gcssloop.com/tips/choose-license) 中给出了很好的概括。 +* 首先是对作者的保护,防止知识成果被恶意利用。开源协议中一般都包含免责声明(禁止代码的作者承担代码被使用后产生的风险及后果),比如你开源了一个破解智能锁的代码,如果有人利用这个去盗窃导致他人损失,你是无需承担责任的。 +* 其次是对使用者的保护,方便使用者。使用者一看就知道自己允许进行哪些操作,不允许进行哪些操作。未添加协议的代码默认是作者保留所有权利的(对此不同国家的法律可能稍微存在区别),这就像一颗定时炸弹,如果你在项目中使用了这一份没有协议的代码,原作者只要能证明你未经许可使用了他的代码,是能够起诉你的。 + +### 当前主流开源许可证(GPL、BSD、MIT、Mozilla、Apache、LGPL)和它们的异同? +![当前主流开源许可证](https://pic2.zhimg.com/80/v2-1c76c3c63f4db1727ebe07815423f3b7_hd.jpg) +相关概念解析: +> * 协议和版权信息(License and copyright notice):在代码中保留作者提供的协议和版权信息 +> * 声明变更(State Changes):在代码中声明对原来代码的重大修改及变更 +> * 公开源码(Disclose Source):代码必需公开。如果是基于LGPL协议 下,则只需使用的开源代码公开,不必将整个软件源码公开 +> * 库引用(Library usage):该库可以用于商业软件中 +> * 责任承担(Hold Liable):代码的作者承担代码使用后的风险及产生的后果 +> * 商标使用(Use Trademark):可以使用作者的姓名,作品的Logo,或商标 +> * 附加协议(Sublicensing):允许在软件分发传播过程中附加上原来没有的协议条款等 + + +### 当前前端主流框架选取的开源协议 +纵观比较常用的前端框架,用的最广泛的便是 MIT 开源协议。 +> * Vue: MIT [点击阅读](https://github.com/vuejs/vue/blob/dev/LICENSE ) +> * React:MIT [点击阅读]( https://github.com/facebook/react/blob/master/LICENSE) +> * Element: MIT [点击阅读](https://github.com/ElemeFE/element/blob/master/LICENSE ) +> * Ant Design:MIT [点击阅读](https://github.com/ant-design/ant-design/blob/master/LICENSE ) + +列举的框架开源协议都是 MIT 。那么为什么选择 MIT 呢? +MIT是一种简短而简单的许可,只需要保留版权和许可通知。许可的作品、修改和更大的作品可以在不同的条件下分发,并且没有源代码。MIT允许别人用作者的代码做任何事情,但必须保证作者的所有权,并且作者无须承担代码使用产生的风险。 +其中,要重点说一下 React 的开源协议,从 github 的提交历史来看,React 的开源协议经历了一个动荡的过程。从 LICENCE 的提交历史看,Facebook 对专利的重视程度可见一斑。 +![React的开源协议](https://pic2.zhimg.com/80/v2-3d9fea33d17605fefe04d121b0a55b4d_hd.jpg) +去年知乎上一个《如何看待百度要求内部全面停止使用 React / React Native?》的文章引起了前端界的热议,事情的起因是大家发现了 Facebook 专利许可证上的描述暗藏玄机。在技术开源的世界,对于开发者而言,许可证就是他们使用开源软件的 “用户协议”。而 Facebook 的开源方式跟其他家都不太一样,别家一般用的都是开源社区公认通用的许可证,而 Facebook 使用的是两个许可证,第一个是通用的 BSD 许可证,第二个是自己写的专利许可证 (patent grant)。 +而在 React 的开源协议中这么写到: +![ React 的开源协议](https://pic4.zhimg.com/80/v2-a04b940e9f42b1c9801b2612d04a6bee_hd.jpg) +意思就是: +当发生下列情况时,facebook 有权益吊销你的 React 使用权: +> * 与 facebook 及其附属机构发生利益冲突 +> * 同任何一个和 facebook 有关的组织发生了法律纠纷 +> * 同任何与 React 有关的组织发生利益冲突 + + +翻译成大白话就是:如果你觉得 Facebook 侵犯了你的知识产权,同时你的核心产品是基于 React 实现,如果你想起诉 Facebook,就要权衡一下了,因为根据条款它有权利吊销你的 React 使用权。或者说你用 React 做了一个产品并且在某些领域对 Facebook 构成了利益冲突,那么它就可以强制你的产品下线。 + +可以说,一旦你开始使用 React 去构建你的核心产品,你的公司就被 facebook 埋下了一颗定时炸弹,并且,炸弹的引爆按钮就握在 facebook 手中。 + +其实这种事情,从去年就在前端技术圈开吵,后来愈演愈烈,形势每况日下:开源社区在更多 Facebook 开源的热门项目中发现了相同的许可证模式和条款。开发者认为 Facebook 的这种许可证模式正在毒害社区,污染开源精神。而且 Apache 软件基金会宣布所有使用 Apache 开源协议的软件都不得使用带有 Facebook BSD + 专利许可证模式的组件。 + +不过 Facebook 最后还是意识到了这些问题,修改了开源协议。 +![Facebook开源协议](https://pic2.zhimg.com/80/v2-6809df1f7f8a7915a3f8909a5b9cff5d_hd.jpg) + +### 如何为我们的项目选择一个开源协议? +首先,我们要清楚我们选择开源的目的是什么? +作为个人,在开源的情况下,我们可以帮助他人,也可以获得他人的帮助,还是一个提升个人代码质量的好方法,同样,也是一个展示自己能力的好方法。世界上开源软件协议的种类非常之多,并且同一款协议有很多变种,协议太宽松会导致作者丧失对作品的很多权利,太严格又不便于使用者使用及作品的传播,所以开源作者要考虑自己对作品想保留哪些权利,放开哪些限制。 +作为公司,代码开源后,会提升公司的地位,树立一个良好的品牌形象,也可以帮助公司发掘潜在员工。 +那么,我们如何选择适合我们的开源许可证呢? + +由一张图直观了解如何选取所需要的开源许可证。(原著:乌克兰程序员Paul Bagwell,翻译:阮一峰) +![开源许可证](https://pic4.zhimg.com/80/v2-253a7b1819e2af555ed0a7e0f11a0b59_hd.jpg) + +举例来说: +如果我只是想专心的写代码,那么可以选择 MIT ,MIT在保证了作者的所有权的前提下允许别人使用作者的代码,且作者不需要承担使用时的风险。如果我想保护我的代码、专利,那么可以选择 Apache ,Apache 与 MIT 的区别就是提供了专利贡献者的授权,使用者需要明确这一点。 + +Github 专门发布了一个网站 [Choosing an OSS license doesn’t need to be scary](https://choosealicense.com/) 来帮助开源项目开发者。 + +> * 我想要一个简单宽松的许可证建议: MIT 许可证。这是一个宽松的、简明扼要的许可证,只要用户在项目副本中包含了版权声明和许可声明,他们就可以拿你的代码做任何想做的事情,你也无需承担任何责任。 +> 使用该许可证的项目:jQuery、Rails +> * 我比较关心专利 +> 建议: Apache许可证。这类似于 MIT 许可证,但它同时还包含了贡献者向用户提供专利授权相关的条款。 +> 使用该许可证的项目:Apache、SVN和NuGet +> * 我关心项目的共享改进 +> 建议:GPL( V2或 V3)许可证。这是一种 copyleft 许可证,要求修改项目代码的用户再次分发源码或二进制代码时,必须公布他的相关修改。V3版本与V2类>似,但其进一步约束了在某些限制软件更改的硬件上的使用范围。 +> 使用该许可证的项目:Linux、Git +> * 我的开源项目不是代码 +> 建议: Creative Commons。这是一个相对宽松的版权协议。它只保留几种了权利(some rights reserved)。使用者可以明确知道所有者的权利,不容易侵犯对>方的版权,作品可以得到有效传播。作为作者,你可以选择以下1~4种权利组合: +> 1) 署名(Attribution,简写为BY):必须提到原作者。 +> 2) 非商业用途(Noncommercial,简写为NC):不得用于盈利性目的。 +> 3) 禁止演绎(No Derivative Works,简写为ND):不得修改原作品, 不得再创作。 +> 4) 相同方式共享(Share Alike,简写为SA):允许修改原作品,但必须使用相同的许可证发布。 +> * 更多选择 +> Licenses - [http://ChooseALicense.com](https://choosealicense.com/),这里提供了Apache/ GPL/ MIT/ Artistic/ Eclipse/ BSD/ LGPL/ Mozilla/ No License/ Public Domain >Dedication 协议的适用情形、许可内容、禁止内容,及协议全文。 + +### 如何为代码添加开源协议? +#### GitHub +1. 首先需要注册一个 GitHub 账号,并登录 +2. 在 GitHub 上选择创建一个新的 repository +![GitHub](https://pic4.zhimg.com/80/v2-5ab3d4f220e6524d445135c029014fe6_hd.jpg) +3. 进入创建 repository 页面后,输入基本信息后,点击右下角的 Add a license 选择开源协议,默认是 none。对应的 license 可以直接选择,也可以输入自己想要的 license +![GitHub](https://pic4.zhimg.com/80/v2-fb3c51c8d10b9767792603877e32ec65_hd.jpg) +![GitHub](https://pic1.zhimg.com/80/v2-209460d2fa43ea984f1f19de8606be3e_hd.jpg) + +4. 点击最下方 Create repository,就创建成功了。 +5. 创建成功后,代码库中就可以看到自动生成了一个 LICENSE 文件。 + +### 参考文献 +> [选择一个开源软件协议](http://choosealicense.online/) +> [程序员不可不知的版权协议](http://www.gcssloop.com/tips/choose-license) +> [开源许可证都有什么区别,一般开源项目用什么许可证?](https://www.zhihu.com/question/28292322) +> [都在封杀 React/React Native ,那我到底还该不该继续学呢?](https://zhuanlan.zhihu.com/p/29492362) +> [React开源协议之争知多少?](https://cauu.github.io/2017/09/React-Opensource-license/) +> [how to choose a license](https://www.cnblogs.com/Wayou/p/how_to_choose_a_license.html) + + +> [原文地址](https://zhuanlan.zhihu.com/p/35876146) \ No newline at end of file diff --git "a/4-\345\237\272\344\272\216Vue\351\205\215\347\275\256axios.md" "b/Cute-Article/article/4-\345\237\272\344\272\216Vue\351\205\215\347\275\256axios.md" similarity index 100% rename from "4-\345\237\272\344\272\216Vue\351\205\215\347\275\256axios.md" rename to "Cute-Article/article/4-\345\237\272\344\272\216Vue\351\205\215\347\275\256axios.md" diff --git "a/Cute-Article/article/40-\350\247\243\345\257\206Vue SSR.md" "b/Cute-Article/article/40-\350\247\243\345\257\206Vue SSR.md" new file mode 100644 index 00000000..ab2faccf --- /dev/null +++ "b/Cute-Article/article/40-\350\247\243\345\257\206Vue SSR.md" @@ -0,0 +1,251 @@ +## 1.引言 +最近笔者和小伙伴在研究Vue SSR,但是市面上充斥了太多的从0到1的文章,对大家理解这其中的原理帮助并不是很大,因此,本文将从 *Vue SSR的构建流程、运行流程、SSR的特点和利弊* 这几方面对Vue SSR有一个较为详细的介绍。最后还将附上一个笔者实现的 *去除Vue全家桶的Demo案例* 。 + +## 2.剖析构建流程 +首先我们镇上一张官网给出的构建图: +![Vue SSR构建流程](https://pic3.zhimg.com/80/v2-8f5dc75e94e8cfe49416e460f6bd2a0e_hd.jpg) + +### app.js入口文件 +`app.js` 是我们的通用`entry`,它的作用就是构建一个Vue的实例以供服务端和客户端使用,注意一下,在纯客户端的程序中我们的`app.js`将会挂载实例到`dom`中,而在`ssr`中这一部分的功能放到了`Client entry`中去做了。 + +### 两个entry +接下里我们来看`Client entry`和`Server entry`,这两者分别是客户端的入口和服务端的入口。*Client entry的功能很简单,就是挂载我们的Vue实例到指定的dom元素上*;`Server entry`是一个使用`export`导出的函数。主要负责调用组件内定义的获取数据的方法,获取到SSR渲染所需数据,并存储到上下文环境中。*这个函数会在每一次的渲染中重复的调用*。 + +### webpack打包构建 +然后我们的服务端代码和客户端代码通过`webpack`分别打包,生成`Server Bundle`和`Client Bundle`,前者会运行在服务器上通过node生成预渲染的`HTML字符串`,发送到我们的客户端以便完成初始化渲染;而客户端bundle就自由了,初始化渲染完全不依赖它了。客户端拿到服务端返回的HTML字符串后,会去“激活”这些静态HTML,是其变成由`Vue动态管理`的DOM,以便响应后续数据的变化。 + +## 3.剖析运行流程 +到这里我们该谈谈`ssr`的程序是怎么跑起来的了。首先我们得去构建一个vue的实例,也就是我们前面构建流程中说到的`app.js`做的事情,但是这里不同于传统的客户端渲染的程序,我们*需要用一个工厂函数去封装它,以便每一个用户的请求都能够返回一个新的实例,也就是官网说到的避免交叉污染了*。 + +然后我们可以暂时移步到服务端的`entry`中了,这里要做的就是拿到当前路由匹配的组件,调用组件里定义的一个方法(官网取名叫`asyncData`)拿到初始化渲染的数据,而这个方法要做的也很简单,就是去调用我们`vuex store`中的方法去异步获取数据。 + +接下来`node服务器`如期启动了,跑的是我们刚写好的服务端`entry`里的函数。在这里还要做的就是将我们刚刚构建好的Vue实例渲染成`HTML字符串`,然后将拿到的数据混入我们的`HTML字符串`中,最后发送到我们客户端。 + +打开浏览器的network,我们看到了初始化渲染的HTML,并且是我们想要初始化的结构,且完全不依赖于客户端的js文件了。再仔细研究研究,里面有初始化的dom结构,有css,还有一个script标签。script标签里把我们在服务端`entry`拿到的数据挂载了`window`上。原来只是一个纯静态的HTML页面啊,没有任何的交互逻辑,所以啊,现在知道为啥子需要服务端跑一个`vue客户端`再跑一个`vue`了,服务端的`vue`只是混入了个数据渲染了个静态页面,客户端的`vue`才是去实现交互的! +![chrome network](https://pic1.zhimg.com/80/v2-8354c78be3249def1cfc6b40d795c3a4_hd.jpg) + +顺着前面的思路,我们该看客户端的`entry`了。在这里客户端拿到存在`window`中的数据混入我们客户端的`vuex`中,然后分析数据去执行我们熟悉的其余客户端操作了。 + +## 4.SSR独特之处 +在SSR中,创建`Vue实例`、创建`store`和创建`router`都是套了一层`工厂函数`的,目的就是`避免数据的交叉污染`。 + +在服务端只能执行生命周期中的`created`和`beforeCreate`,原因是在服务端是无法操纵dom的,所以可想而知其他的周期也就是不能执行的了。 + +服务端渲染和客户端渲染不同,需要创建两个`entry`分别跑在`服务端`和`客户端`,并且需要*webpack对其分别打包*; + +SSR服务端请求不带`cookie`,需要手动拿到浏览器的`cookie`传给服务端的请求。[实现方式戳这里](https://www.mmxiaowu.com/article/596cbb2d436eb550a5423c30)。 + +SSR要求dom结构规范,因为浏览器会自动给HTML添加一些结构比如tbody,但是客户端进行混淆服务端放回的HTML时,不会添加这些标签,导致混淆后的HTML和浏览器渲染的HTML不匹配。 + +*性能问题需要多加关注*。 +* vue.mixin、axios拦截请求使用不当,会内存泄漏。[原因戳这里](https://github.com/vuejs/vue/issues/5089) +* lru-cache向内存中缓存数据,需要合理缓存改动不频繁的资源。 + +## 5.可能是把双刃剑 +### SSR的优点 + +* 更利于SEO。 + +不同爬虫工作原理类似,只会爬取源码,不会执行网站的任何脚本(Google除外,据说Googlebot可以运行javaScript)。 +使用了`Vue`或者其它`MVVM框架`之后,页面大多数DOM元素都是在客户端根据js动态生成,可供爬虫抓取分析的内容大大减少。 +另外,浏览器爬虫不会等待我们的数据完成之后再去抓取我们的页面数据。服务端渲染返回给客户端的是已经获取了异步数据并执行JavaScript脚本的最终HTML,网络爬中就可以抓取到完整页面的信息。 + +* 更利于首屏渲染 +首屏的渲染是node发送过来的html字符串,并不依赖于js文件了,这就会使用户更快的看到页面的内容。尤其是针对大型单页应用,打包后文件体积比较大,普通客户端渲染加载所有所需文件时间较长,首页就会有一个很长的白屏等待时间。 + + +## 6.SSR的局限 + +* 服务端压力较大 +本来是通过客户端完成渲染,现在统一到服务端node服务去做。尤其是高并发访问的情况,会大量占用服务端CPU资源; + +* 开发条件受限 +在服务端渲染中,`created`和`beforeCreate`之外的生命周期钩子不可用,因此项目引用的第三方的库也不可用其它生命周期钩子,这对引用库的选择产生了很大的限制; + +* 学习成本相对较高 +除了对`webpack`、`Vue`要熟悉,还需要掌握`node`、`Express`相关技术。相对于客户端渲染,项目构建、部署过程更加复杂。 + +## 6.去除VUEX的SSR实践 +先附上demo地址,[戳这里](https://github.com/LNoe-lzy/vue-ssr-demo/tree/vue-ssr-without-vuex)! + +说在前面: + +* vue-router不是必须的,不用router其实做个vue的[preRender](https://github.com/chrisvfritz/prerender-spa-plugin)就可以了,完全没必要做ssr; +* vuex不是必须的,vuex是实现我们客户端和服务端的状态共享的关键,我们可以不使用vuex,但是我们得去实现一套数据预取的逻辑; + +官网的demo大而全,集成了`vue-router`和`vuex`,想想我们的项目如果没有使用到这两者,光引入就又需要改造成本,这并不是我们想搞的“丝滑般”过渡,接下来笔者将带领大家一步一步的做个“啥都没有的”demo。 + +在此笔者的思路是:*构造一个Vue的实例,那么我们可以用这个实例的data来存储我们的预取数据,而用methods中的方法去做数据的异步获取,这样我们只在需要预取数据的组件中去调用这个方法就可以了*。 + +首先我们需要让我们的组件“共享”这个EventBus,为此笔者简单的封装了一个plugin: +```js +export default { + install (Vue) { + const EventBus = new Vue({ + data () { + return { + list: [], + nav: [] + } + }, + methods: { + getList () { + // get list + }, + getNav () { + // get nav + } + } + }) + + Vue.prototype.$events = EventBus + Vue.$events = EventBus + } +} +``` + +然后我们需要在`main.js`中`export`出我们的`EventBus`以便两个`entry`使用。这样我们的`main.js`就像下面这样: +```js +import Vue from 'vue' +import App from './App' +import EventBus from './event' + +Vue.use(EventBus) +Vue.config.devtools = true + +export function createApp () { + const app = new Vue({ + // 注入 router 到根 Vue 实例 + router, + render: h => h(App) + }) + + return { app, router, eventBus: app.$events } +} +``` + +接下来是我们的两个`entry`了。`server`用来匹配我们的组件并调用组件的`asyncData`方法去获取数据,`client`用来将预渲染的数据存储到我们`eventBus`中的`data`中。 +```js +// server +import { createApp } from './main' + +export default context => { + return new Promise((resolve, reject) => { + const { app, eventBus, App } = createApp() + // 这里笔者的demo比较简单,仅app组件需要预取数据,复杂业务可以递归遍历哈; + const matchedComponents = [App] + + Promise.all(matchedComponents.map(({ asyncData }) => asyncData && asyncData({ + eventBus + }))).then(() => { + context.state = eventBus._data + resolve(app) + }).catch(reject) + }) +} + + +// client +import Vue from 'vue' +import { createApp } from './main' +const { app, eventBus } = createApp() + +if (window.__INITIAL_STATE__) { + eventBus._data = window.__INITIAL_STATE__ +} + +app.$mount('#app') +``` + +然后我们需要改造我们的组件了,只需要定义一个`async`方法去调用`EventBus`中的方法获取,考虑到服务端只会执行`beforeCreate`和`created`两个生命周期而`beforeCreate`不能拿到`data`,所以我们需要在`created`中去做数据的获取。 +```js +// 服务端渲染数据预取; +asyncData ({ store, eventBus }) { + return eventBus.getNav() +} +// 将服务端拿到的数据混入vue组件中; +created () { + this.nav = this.$events.nav +} +``` + +然后是`webpack`的改造了,`webpack`的配置其实和纯客户端应用类似,为了区分客户端和服务端两个环境我们将配置分为`base`、`client`和`server`三部分,`base`就是我们的通用基础配置,而`client`和`server`分别用来打包我们的客户端和服务端代码。 + +首先是`webpack.server.conf.js`,用于生成`server bundle`来传递给`createBundleRenderer函数`在node服务器上调用,入口文件是我们的`entry-server`: +```js +const webpack = require('webpack') +const merge = require('webpack-merge') +const nodeExternals = require('webpack-node-externals') +const baseConfig = require('./webpack.base.conf.js') +const VueSSRServerPlugin = require('vue-server-renderer/server-plugin') +// 去除打包css的配置 +baseConfig.module.rules[1].options = '' + +module.exports = merge(baseConfig, { + entry: './src/entry-server.js', + // 以 Node 适用方式导入 + target: 'node', + // 对 bundle renderer 提供 source map 支持 + devtool: '#source-map', + output: { + filename: 'server-bundle.js', + libraryTarget: 'commonjs2' + }, + externals: nodeExternals({ + whitelist: /\.css$/ + }), + plugins: [ + new webpack.DefinePlugin({ + 'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV || 'development'), + 'process.env.VUE_ENV': '"server"' + }), + // 这是将服务器的整个输出 + // 构建为单个 JSON 文件的插件。 + // 默认文件名为 `vue-ssr-server-bundle.json` + new VueSSRServerPlugin() + ] +}) +``` +其次是`webpack.client.conf.js`,这里我们可以根据官方的配置生成`clientManifest`,自动推断和注入资源预加载,以及 css 链接 / script 标签到所渲染的 HTML。入口是我们的`client-server`: +```js +const webpack = require('webpack') +const merge = require('webpack-merge') +const base = require('./webpack.base.conf') +const VueSSRClientPlugin = require('vue-server-renderer/client-plugin') + +const config = merge(base, { + entry: { + app: './src/entry-client.js' + }, + plugins: [ + new webpack.DefinePlugin({ + 'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV || 'development'), + 'process.env.VUE_ENV': '"client"' + }), + new webpack.optimize.CommonsChunkPlugin({ + name: 'vendor', + minChunks: function (module) { + return ( + /node_modules/.test(module.context) && + !/\.css$/.test(module.request) + ) + } + }), + // 这将 webpack 运行时分离到一个引导 chunk 中, + // 以便可以在之后正确注入异步 chunk。 + // 这也为你的 应用程序/vendor 代码提供了更好的缓存。 + new webpack.optimize.CommonsChunkPlugin({ + name: 'manifest' + }), + new VueSSRClientPlugin() + ] +}) +``` + +从`localhost`中我们看到`ssr`预取的数据已经成功出来了,大功告成! +![localhost](https://pic1.zhimg.com/80/v2-8354c78be3249def1cfc6b40d795c3a4_hd.jpg) + +## 7.结语 +本文介绍了Vue的SSR的构建和运行流程,也分析了SSR的特点和利弊,希望对大家了解SSR有一定的帮助。最后针对不使用vuex的SSR实现方案进行了介绍,如果感兴趣或者有疑问,欢迎大家留言交流。 + +[阅读原文](https://zhuanlan.zhihu.com/p/35871344) \ No newline at end of file diff --git "a/Cute-Article/article/41-ES2018\357\274\210ES9\357\274\211\347\232\204\346\226\260\347\211\271\346\200\247.md" "b/Cute-Article/article/41-ES2018\357\274\210ES9\357\274\211\347\232\204\346\226\260\347\211\271\346\200\247.md" new file mode 100644 index 00000000..a10e3cdc --- /dev/null +++ "b/Cute-Article/article/41-ES2018\357\274\210ES9\357\274\211\347\232\204\346\226\260\347\211\271\346\200\247.md" @@ -0,0 +1,226 @@ +在这篇文章中,我将介绍ES2018(ES9)的新特性,并介绍如何使用它们。 + +JavaScript(ECMAScript)是跨多个平台的许多厂商实施的不断发展的标准。ES6(ECMAScript 2015)花费六年的时间敲定,是一个很大的发行版。新的年度发布流程被制定,以简化流程并更快地添加功能。 ES9(ES2018)是撰写本文时的最新版本。 + +TC39由包括浏览器厂商在内的各方组成,他们开会推动JavaScript提案沿着一条严格的发展道路前进: + +* Stage 0: strawman——最初想法的提交。 +* Stage 1: proposal(提案)——由TC39至少一名成员倡导的正式提案文件,该文件包括API事例。 +* Stage 2: draft(草案)——功能规范的初始版本,该版本包含功能规范的两个实验实现。 +* Stage 3: candidate(候选)——提案规范通过审查并从厂商那里收集反馈 +* Stage 4: finished(完成)——提案准备加入ECMAScript,但是到浏览器或者Nodejs中可能需要更长的时间 + +## ES2016 +ES2016添加了两个小的特性来说明标准化过程: + +1. 数组`includes()`方法,用来判断一个数组是否包含一个指定的值,根据情况,如果包含则返回`true`,否则返回`false`。 + +2. `a ** b`指数运算符,它与 `Math.pow(a, b)`相同。 + +## ES2017 +ES2017提供了更多的新特性: + +1. `Async` 函数呈现更清晰的 `Promise` 语法 + +2. `Object.values` 方法返回一个给定对象自己的所有可枚举属性值的数组,值的顺序与使用`for...in`循环的顺序相同(区别在于`for...in`循环枚举原型链中的属性) + +3. `Object.entries()`方法返回一个给定对象自身可枚举属性的键值对数组,其排列与使用`for...in`循环遍历改对象时返回的顺序一致(区别在于`for...in`循环也枚举原型链中的属性) + +4. `Object.getOwnPropertyDescriptors()`返回一个对象的所有自身属性的描述符(`.value`,`.writable`,`.get`,`.set`,`.configurable`,`enumerable`) + +5. `padStart()`和`padEnd()`,填充字符串达到当前长度 + +6. 结尾逗号,数组定义和函数参数列表 + +7. `ShareArrayBuffer`和`Atomics`用于从共享内存位置读取和写入 + +关于ES2017的更多信息请[参阅](https://www.sitepoint.com/es2017-whats-new/) + +## ES2018 +ECMAScript 2018(或者叫ES9)现在已经可用了。以下功能已经到达 stage 4,但是在撰写本文时在各个浏览器的实现还不完整。 + +### 异步迭代 +在`async/await`的某些时刻,你可能尝试在同步循环中调用异步函数。例如: +```js +async function process(array) { + for (let i of array) { + await doSomething(i); + } +} +``` +这段代码不会正常运行,下面这段同样也不会: +```js +async function process(array) { + array.forEach(async i => { + await doSomething(i); + }); +} +``` +这段代码中,循环本身依旧保持同步,并在在内部异步函数之前全部调用完成。 +ES2018引入异步迭代器(asynchronous iterators),这就像常规迭代器,除了`next()`方法返回一个`Promise`。因此`await`可以和`for...of`循环一起使用,以串行的方式运行异步操作。例如: +```js +async function process(array) { + for await (let i of array) { + doSomething(i); + } +} +``` + +### Promise.finally() +一个`Promise`调用链要么成功到达最后一个`.then()`,要么失败触发`.catch()`。在某些情况下,你想要在无论`Promise`运行成功还是失败,运行相同的代码,例如清除,删除对话,关闭数据库连接等。 +`.finally()`允许你指定最终的逻辑: +```js +function doSomething() { + doSomething1() + .then(doSomething2) + .then(doSomething3) + .catch(err => { + console.log(err); + }) + .finally(() => { + // finish here! + }); +} +``` + +## Rest/Spread 属性 +ES2015引入了[Rest参数](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Functions/Rest_parameters)和[扩展运算符](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/Spread_syntax)。三个点(...)仅用于数组。Rest参数语法允许我们将一个布丁数量的参数表示为一个数组。 +```js +restParam(1, 2, 3, 4, 5); + +function restParam(p1, p2, ...p3) { + // p1 = 1 + // p2 = 2 + // p3 = [3, 4, 5] +} +``` + +展开操作符以相反的方式工作,将数组转换成可传递给函数的单独参数。例如`Math.max()`返回给定数字中的最大值: +```js +const values = [99, 100, -1, 48, 16]; +console.log( Math.max(...values) ); // 100 +``` + +ES2018为对象解构提供了和数组一样的Rest参数()和展开操作符,一个简单的例子: +```js +const myObject = { + a: 1, + b: 2, + c: 3 +}; + +const { a, ...x } = myObject; +// a = 1 +// x = { b: 2, c: 3 } +``` + +或者你可以使用它给函数传递参数: +```js +restParam({ + a: 1, + b: 2, + c: 3 +}); + +function restParam({ a, ...x }) { + // a = 1 + // x = { b: 2, c: 3 } +} +``` +跟数组一样,Rest参数只能在声明的结尾处使用。此外,它只适用于每个对象的顶层,如果对象中嵌套对象则无法适用。 +扩展运算符可以在其他对象内使用,例如: +```js +const obj1 = { a: 1, b: 2, c: 3 }; +const obj2 = { ...obj1, z: 26 }; +// obj2 is { a: 1, b: 2, c: 3, z: 26 } +``` + +可以使用扩展运算符拷贝一个对象,像是这样`obj2 = {...obj1}`,但是 这只是一个对象的浅拷贝。另外,如果一个对象A的属性是对象B,那么在克隆后的对象cloneB中,该属性指向对象B。 + +### 正则表达式命名捕获组(Regular Expression Named Capture Groups) +JavaScript正则表达式可以返回一个匹配的对象——一个包含匹配字符串的类数组,例如:以YYYY-MM-DD的格式解析日期: +```js +const + reDate = /([0-9]{4})-([0-9]{2})-([0-9]{2})/, + match = reDate.exec('2018-04-30'), + year = match[1], // 2018 + month = match[2], // 04 + day = match[3]; // 30 +``` + +这样的代码很难读懂,并且改变正则表达式的结构有可能改变匹配对象的索引。 +ES2018允许命名捕获组使用符号`?`,在打开捕获括号(后立即命名,示例如下: +```js +const + reDate = /(?[0-9]{4})-(?[0-9]{2})-(?[0-9]{2})/, + match = reDate.exec('2018-04-30'), + year = match.groups.year, // 2018 + month = match.groups.month, // 04 + day = match.groups.day; // 30 +``` +任何匹配失败的命名组都将返回`undefined`。 +命名捕获也可以使用在`replace()`方法中。例如将日期转换为美国的 MM-DD-YYYY 格式: +```js +const + reDate = /(?[0-9]{4})-(?[0-9]{2})-(?[0-9]{2})/, + d = '2018-04-30', + usDate = d.replace(reDate, '$-$-$'); +``` + +### 正则表达式反向断言(lookbehind) +目前JavaScript在正则表达式中支持先行断言(lookahead)。这意味着匹配会发生,但不会有任何捕获,并且断言没有包含在整个匹配字段中。例如从价格中捕获货币符号: +```js +const + reLookahead = /\D(?=\d+)/, + match = reLookahead.exec('$123.89'); + +console.log( match[0] ); // $ +``` + +ES2018引入以相同方式工作但是匹配前面的反向断言(lookbehind),这样我就可以忽略货币符号,单纯的捕获价格的数字: +```js +const + reLookbehind = /(?<=\D)\d+/, + match = reLookbehind.exec('$123.89'); + +console.log( match[0] ); // 123.89 +``` + +以上是 肯定反向断言,非数字`\D`必须存在。同样的,还存在 否定反向断言,表示一个值必须不存在,例如: +```js +const + reLookbehindNeg = /(? [阅读原文 obkoro1.com](http://obkoro1.com/2018/07/08/JS%E9%AB%98%E7%A8%8B%E4%B8%AD%E7%9A%84%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6%E6%9C%BA%E5%88%B6%E4%B8%8E%E5%B8%B8%E8%A7%81%E5%86%85%E5%AD%98%E6%B3%84%E9%9C%B2%E7%9A%84%E8%A7%A3%E5%86%B3%E6%96%B9%E6%B3%95/) + +### 内存的生命周期: +1. 分配你所需要的内存: + +由于字符串、对象等没有固定的大小,js程序在每次创建字符串、对象的时候,程序都会**分配内存来存储那个实体**。 + +2. 使用分配到的内存做点什么。 + +3. 不需要时将其释放回归: + +在不需要字符串、对象的时候,需要释放其所占用的内存,否则将会消耗完系统中所有可用的内存,造成系统崩溃,这就是**垃圾回收机制所存在的意义**。 + +**所谓的内存泄漏**指的是:由于疏忽或错误造成程序未能释放那些已经不再使用的内存,造成内存的浪费。 +*** +### 垃圾回收机制: +在C和C++之类的语言中,需要手动来管理内存的,这也是造成许多不必要问题的根源。幸运的是,在编写js的过程中,内存的分配以及内存的回收完全实现了自动管理,我们不用操心这种事情。 + +### 垃圾收集机制的原理: +垃圾收集器会按照固定的时间间隔,**周期性的找出不再继续使用的变量,然后释放其占用的内存**。 + +#### 什么叫不再继续使用的变量? + +不再使用的变量也就是生命周期结束的变量,是局部变量,局部变量只在函数的执行过程中存在,当函数运行结束,没有其他引用(闭包),那么该变量会被标记回收。 + +全局变量的生命周期直至浏览器卸载页面才会结束,也就是说**全局变量不会被当成垃圾回收**。 + +### 标记清除:当前采用的垃圾收集策略 +工作原理: + +当变量进入环境时(例如在函数中声明一个变量),将这个变量标记为“进入环境”,当变量离开环境时,则将其标记为“离开环境”。标记“离开环境”的就回收内存。 + +工作流程: + +1. 垃圾收集器会在运行的时候会给存储在内存中的**所有变量都加上标记**。 +2. 去掉环境中的变量以及被环境中的变量引用的变量的标记。 +3. 那些还存在标记的变量被视为准备删除的变量。 +4. 最后垃圾收集器会执行最后一步内存清除的工作,销毁那些带标记的值并回收它们所占用的内存空间。 +到2008年为止,IE、Chorme、Fireofx、Safari、Opera **都使用标记清除式的垃圾收集策略**,只不过垃圾收集的时间间隔互有不同。 + +### 引用计数略:被废弃的垃圾收集策 +循环引用:跟踪记录每个值被引用的技术 + +在老版本的浏览器中(对,又是IE),IE9以下BOM和DOM对象就是使用C++以COM对象的形式实现的。 + +COM的垃圾收集机制采用的就是引用计数策略,这种机制在出现循环引用的时候永远都释放不掉内存。 +```js +var element = document.getElementById('something'); +var myObject = new Object(); +myObject.element = element; // element属性指向dom +element.someThing = myObject; // someThing回指myObject 出现循环引用(两个对象一直互相包含 一直存在计数)。 +``` +解决方式是,当我们不使用它们的时候,手动切断链接: +```js +myObject.element = null; +element.someThing = null; +``` +#### 淘汰: + +IE9把BOM和DOM对象转为了真正的js对象,避免了使用这种垃圾收集策略,消除了IE9以下常见的内存泄漏的主要原因。 + +IE7以下有一个声明狼藉的性能问题,大家了解一下: + +1. 256个变量,4096个对象(或数组)字面或者64KB的字符串,达到任何一个临界值会触发垃圾收集器运行。 +2. 如果一个js脚本的生命周期一直保有那么多变量,垃圾收集器会一直频繁的运行,引发严重的性能问题。 +IE7已修复这个问题。 +*** + +### 哪些情况会引起内存泄漏? +虽然有垃圾回收机制,但我们在编写代码的时候,有些情况还是会造成内存泄漏,了解这些情况,并在编写程序的时候,注意避免,我们的程序会更具健壮性。 + +#### 意外的全局变量: +上文我们提到了**全局变量不会被当成垃圾回收**,我们在编码中有时会出现下面这种情况: +```js +function foo() { + this.bar2 = '默认绑定this指向全局' // 全局变量=> window.bar2 + bar = '全局变量'; // 没有声明变量 实际上是全局变量=>window.bar +} +foo(); +``` +当我们使用[默认绑定](https://juejin.im/post/5b3715def265da59af40a630#heading-3),this会指向全局,`this.something`也会创建一个全局变量,这一点可能很多人没有注意到。 + +**解决方法:在函数内使用严格模式or细心一点** +```js +function foo() { + "use strict"; + this.bar2 = "严格模式下this指向undefined"; + bar = "报错"; +} +foo(); +``` +当然我们也可以手动释放全局变量的内存: +```js +window.bar = undefined +delete window.bar2 +``` +#### 被遗忘的定时器和回调函数 +当不需要`setInterval`或者`setTimeout`时,**定时器没有被clear**,定时器的**回调函数以及内部依赖的变量都不能被回收**,造成内存泄漏。 +```js +var someResource = getData(); +setInterval(function() { + var node = document.getElementById('Node'); + if(node) { + node.innerHTML = JSON.stringify(someResource)); + // 定时器也没有清除 + } + // node、someResource 存储了大量数据 无法回收 +}, 1000); +``` +**解决方法**: 在定时器完成工作的时候,手动清除定时器。 + +### 闭包: +**闭包可以维持函数内局部变量,使其得不到释放,造成内存泄漏。** +```js +function bindEvent() { + var obj = document.createElement("XXX"); + var unused = function () { + console.log(obj,'闭包内引用obj obj不会被释放'); + }; + // obj = null; +} +``` +**解决方法**:手动解除引用,`obj = null`。 + +#### 循环引用问题 +就是IE9以下的循环引用问题,上文讲过了。 + +#### 没有清理DOM元素引用: +```js +var refA = document.getElementById('refA'); +document.body.removeChild(refA); // dom删除了 +console.log(refA, "refA"); // 但是还存在引用 能console出整个div 没有被回收 +``` +不信的话,可以看下这个[dom](https://codepen.io/OBKoro1/pen/vroKbg)。 + +**解决办法**:`refA = null`; + +#### console保存大量数据在内存中。 +过多的console,比如定时器的console会导致浏览器卡死。 + +**解决**:合理利用console,线上项目尽量少的使用console,当然如果你要发招聘,除外。 +*** +### 如何避免内存泄漏: +**记住一个原则:不用的东西,及时归还,毕竟你是’借的’嘛。** + +1. 减少不必要的全局变量,使用严格模式避免意外创建全局变量。 +2. 在你使用完数据后,及时解除引用(闭包中的变量,dom引用,定时器清除)。 +3. 组织好你的逻辑,避免死循环等造成浏览器卡顿,崩溃的问题。 + +#### 关于内存泄漏: +1. 即使是1byte的内存,也叫内存泄漏,并不一定是导致浏览器崩溃、卡顿才能叫做内存泄漏。 +2. 一般是堆区内存泄漏,栈区不会泄漏。 +基本类型的值存在内存中,被保存在栈内存中,引用类型的值是**对象,保存在堆内存中。所以对象、数组之类的,才会发生内存泄漏**。 + +3. 使用chorme监控内存泄漏,可以看一下[这篇文章](https://jinlong.github.io/2016/05/01/4-Types-of-Memory-Leaks-in-JavaScript-and-How-to-Get-Rid-Of-Them/) diff --git "a/Cute-Article/article/43-\346\211\213\346\234\272\347\253\257\351\241\265\351\235\242\345\274\200\345\217\221\345\270\270\350\247\201\351\227\256\351\242\230\345\222\214\350\247\243\345\206\263.md" "b/Cute-Article/article/43-\346\211\213\346\234\272\347\253\257\351\241\265\351\235\242\345\274\200\345\217\221\345\270\270\350\247\201\351\227\256\351\242\230\345\222\214\350\247\243\345\206\263.md" new file mode 100644 index 00000000..ffb5253f --- /dev/null +++ "b/Cute-Article/article/43-\346\211\213\346\234\272\347\253\257\351\241\265\351\235\242\345\274\200\345\217\221\345\270\270\350\247\201\351\227\256\351\242\230\345\222\214\350\247\243\345\206\263.md" @@ -0,0 +1,356 @@ +## 1.解决页面使用 overflow: scroll 在 iOS 上滑动卡顿的问题? +首先你可能会给页面的 html 和 body 增加了 height: 100%, 然后就可能造成 iOS 上页面滑动的卡顿问题。解决方案是: +(1) 看是否能把 body 和 html 的 height: 100% 去除掉。 +(2) 在滚动的容器中增加:`-webkit-overflow-scrolling: touch` 或者给 body 增加:`body {overflow-x: hidden}`。 + +## 2.iOS 页面橡皮弹回效果遮挡页面选项卡? +(1) 有时 body 和 html 的 height: 100% 去除掉问题可能就没有了。 +(2) 到达临界值的时候在阻止事件默认行为 +```js +var startY,endY; +//记录手指触摸的起点坐标 +$('body').on('touchstart',function (e) { + startY = e.touches[0].pageY; +}); +$('body').on('touchmove',function (e) { + endY = e.touches[0].pageY; //记录手指触摸的移动中的坐标 + //手指下滑,页面到达顶端不能继续下滑 + if(endY>startY&& $(window).scrollTop()<=0){ + e.preventDefault(); + } + //手指上滑,页面到达底部能继续上滑 + if(endY=$('body')[0].scrollHeight){ + e.preventDefault(); + } +}) +``` +有时也会碰见弹窗出来后两个层的橡皮筋效果出现问题,我们可以在弹出弹出时给底层页面加上一个类名,类名禁止页面滑动这样下层的橡皮筋效果就会被禁止,就不会影响弹窗层。 + +## 3.iOS 机型 margin 属性无效问题? +(1) 设置 html body 的高度为百分比时,margin-bottom 在 safari 里失效 +(2) 直接 padding 代替 margin + +## 4.iOS 绑定点击事件不执行? +(1) 添加样式 `cursor :pointer`。点击后消除背景闪一下的 css:`-webkit-tap-highlight-color:transparent`; + +## 5.iOS 键盘换行变为搜索? +首先,input 要放在 form 里面。 +这时 "换行" 已经变成 “前往”。 +如果想变成 “搜索”,input 设置 `type="search"`。 + +## 6.jQuery对 a 标签点击事件不生效? +出现这种情况的原因不明,有的朋友解释:我们平时都是点击的 A 标签中的文字了。 所以要想用 JS 模拟点击 A 标签事件,就得先往 A 标签中的文字添加能被 JS 捕获的元素,然后再用 JS 模拟点击该元素即可。但是我觉得不合理,虽然找不到原因但是解决办法还是有的。 +```js +// 方法1 +document.getElementById("abc").click(); +// 方法2 +$("#abc")[0].click(); +``` +## 7.有时因为服务器或者别的原因导致页面上的图片没有找到? +这是我们想需要用一个本地的图片代替没有找的的图片 +```html + + +``` + +## 8.transform 属性影响 position:fixed? +(1) 规范中有规定:如果元素的 transform 值不为 none,则该元素会生成包含块和层叠上下文。CSS Transforms Module Level 1 不只在手机上,电脑上也一样。除了 fixed 元素会受影响之外,z-index(层叠上下文)值也会受影响。绝对定位元素等和包含块有关的属性都会受到影响。当然如果 transform 元素的 display 值为 inline 时又会有所不同。最简单的解决方法就是 transform 元素内部不能有 absolute、fixed 元素. + +## 9.iOS 对 position: fixed 不太友好,有时我们需要加点处理? +在安卓上面,点击页面底部的输入框,软键盘弹出,页面移动上移。 而 iOS 上面,点击页面底部输入框,软键盘弹出,输入框看不到了。。。查资料说什么的都有,iscroll,jquery-moblie,absolute,fixe,static 都非常复杂,要改很多。。。 让他弹出时让滚动条在最低部 +```js +var u = navigator.userAgent, app = navigator.appVersion; +var isiOS = !!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/); //iOS终端 +if (isiOS) { + $('textarea').focus(function () { + window.setTimeout('scrollBottom()', 500); + }); +} +function scrollBottom() { + window.scrollTo(0, $('body').height()); +} +``` + +## 10.jQuery validate 插件验证问题? +所有的 input 必须有 name 不然会出错 + +## 11.有时手机会出现断网的情况,我没可能会对断网的情况做一些处理? +`navigator.onLine` 可判断是否是脱机状态. + +## 12.判断对象的长度? +(1) 用 `Object.keys`,`Object.keys` 方法返回的是一个数组,数组里面装的是对象的属性。 +```js +var person = { + "name" : "zhangshan", + "sex" : "man", + "age" : "50", + "height" : "180", + "phone" : "1xxxxxxxxxx", + "email" : "xxxxxxxxx@xxx.com" +}; +var arr = Object.keys(person); +console.log(arr.length); +``` +(2)Object.getOwnPropertyNames(obj).length + +## 13.上一题我们用到了 Object.keys 与 Object.getOwnPropertyNames 他们的区别? +`Object.keys` 定义:返回一个对象可枚举属性的字符串数组; +`Object.getOwnPropertyNames` 定义:返回一个对象可枚举、不可枚举属性的名称; +属性的可枚举性、不可枚举性:定义:可枚举属性是指那些内部 “可枚举” 标志设置为 true 的属性,对于通过直接的赋值和属性初始化的属性,该标识值默认为即为 true,对于通过 `Object.defineProperty` 等定义的属性,该标识值默认为 false。 +```js +var obj = { "prop1": "v1" }; +Object.defineProperty(obj, "prop2", { value: "v2", enumerable: false }); +console.log(Object.keys(obj).length); //output:1 +console.log(Object.getOwnPropertyNames(obj).length); //output:2 +console.log(Object.keys(obj)); //output:Array[1] => [0: "prop1"] +console.log(Object.getOwnPropertyNames(obj)); //output:Array[2] => [0: "prop1", 1: "prop2"] +``` +![内置的判断,访问和迭代方法](http://p3nqtyvgo.bkt.clouddn.com/196513361-5b16021f6db85_articlex.png) +综合实例: +```js +var obj = { "prop1": "v1" }; +Object.defineProperty(obj, "prop2", { value: "v2", enumerable: false}); +console.log(obj.hasOwnProperty("prop1")); //output: true +console.log(obj.hasOwnProperty("prop2")); //output: true +console.log(obj.propertyIsEnumerable("prop1")); //output: true +console.log(obj.propertyIsEnumerable("prop2")); //output: false +console.log('prop1' in obj); //output: true +console.log('prop2' in obj); //output: true +for (var item in obj) { + console.log(item); +} +//output:prop1 +for (var item in Object.getOwnPropertyNames(obj)) { + console.log(Object.getOwnPropertyNames(obj)[item]); +} +//ouput:[prop1,prop2] +``` + +## 14.移动开发不同手机弹出数字键盘问题? +#### 1. type="tel" +iOS 和 Android 的键盘表现都差不多 + +#### 2. type="number" +**优点**:Android 下实现的一个真正的数字键盘 +**缺点一**:iOS 下不是九宫格键盘,输入不方便 +**缺点二**:旧版 Android(包括微信所用的 X5 内核)在输入框后面会有超级鸡肋的小尾巴,好在 Android 4.4.4 以后给去掉了。 不过对于缺点二,我们可以用 webkit 私有的伪元素给 fix 掉: +```js +input[type=number]::-webkit-inner-spin-button, +input[type=number]::-webkit-outer-spin-button { + -webkit-appearance: none; + appearance: none; + margin: 0; +} +``` +#### 3. pattern 属性 +pattern 用于验证表单输入的内容,通常 HTML5 的 type 属性,比如 email、tel、number、data 类、url 等,已经自带了简单的数据格式验证功能了,加上 pattern 后,前端部分的验证更加简单高效了。 +显而易见,pattern 的属性值要用正则表达式。 +实例 简单的数字验证 +数字的验证有两个: +```html + + +``` + +## 15.input[number] 类型输入非数字字符 +js 获取的值是空;比如 - 12,+123 等 + +## 16.Javascript:history.go() 和 history.back() 的用法与区别? +`go(-1)`: 返回上一页,原页面表单中的内容会丢失; +`back()`: 返回上一页,原页表表单中的内容会保留; +`history.go(-1)`: 后退 + 刷新; +`history.back()`: 后退; + +之所以注意到这个区别,是因为不同的浏览器后退行为也是有区别的,而区别就跟 `javascript:history.go()` 和 `history.back()` 的区别类似。 +Chrome 和 ff 浏览器后退页面,会刷新后退的页面,若有数据请求也会提交数据申请。类似于 `history.go(-1)`; +而 safari(包括桌面版和 ipad 版)的后退按钮则不会刷新页面,也不会提交数据申请。类似于 `javascript:history.back()`; + +## 17.Meta 基础知识: +```html + +// width 设置viewport宽度,为一个正整数,或字符串‘device-width’ +// height 设置viewport高度,一般设置了宽度,会自动解析出高度,可以不用设置 +// initial-scale 默认缩放比例,为一个数字,可以带小数 +// minimum-scale 允许用户最小缩放比例,为一个数字,可以带小数 +// maximum-scale 允许用户最大缩放比例,为一个数字,可以带小数 +// user-scalable 是否允许手动缩放 +空白页基本meta标签 + + + + + + + + +其他meta标签 + + + + + + + + + + + + + + + + + + + + + + +``` +## 18.移动端如何定义字体 font-family? +@ ------------ 中文字体的英文名称 +@ 宋体 SimSun +@ 黑体 SimHei +@ 微信雅黑 Microsoft Yahei +@ 微软正黑体 Microsoft JhengHei +@ 新宋体 NSimSun +@ 新细明体 MingLiU +@ 细明体 MingLiU +@ 标楷体 DFKai-SB +@ 仿宋 FangSong +@ 楷体 KaiTi +@ 仿宋GB2312 FangSongGB2312 +@ 楷体GB2312 KaiTiGB2312 +**说明**:中文字体多数使用宋体、雅黑,英文用 Helvetica +```css +body {font-family: Microsoft Yahei,SimSun,Helvetica;} +``` + +## 19.打电话发短信写邮件怎么实现? +```html +// 一、打电话 +打电话给:0755-10086 +// 二、发短信,winphone系统无效 +发短信给: 10086 +// 三、写邮件 +点击我发邮件 +//2.收件地址后添加?cc=开头,可添加抄送地址(Android存在兼容问题) +点击我发邮件 +//3.跟着抄送地址后,写上&bcc=,可添加密件抄送地址(Android存在兼容问题) +点击我发邮件 +//4.包含多个收件人、抄送、密件抄送人,用分号(;)隔开多个邮件人的地址 +点击我发邮件 +//5.包含主题,用?subject= +点击我发邮件 +//6.包含内容,用?body=;如内容包含文本,使用%0A给文本换行 +点击我发邮件 +//7.内容包含链接,含http(s)://等的文本自动转化为链接 +点击我发邮件 +//8.内容包含图片(PC不支持) +点击我发邮件 +//9.完整示例 +点击我发邮件 +``` + +## 20.移动端 touch 事件(区分 webkit 和 winphone)? +#### 1. 以下支持 webkit +**touchstart**——当手指触碰屏幕时候发生。不管当前有多少只手指 +**touchmove**——当手指在屏幕上滑动时连续触发。通常我们再滑屏页面,会调用 event 的 **preventDefault() 可以阻止默认情况的发生:阻止页面滚动 +**touchend**——当手指离开屏幕时触发 +**touchcancel**——系统停止跟踪触摸时候会触发。例如在触摸过程中突然页面 alert() 一个提示框,此时会触发该事件,这个事件比较少用 + +#### 2. TouchEvent 说明: +**touches**:屏幕上所有手指的信息 +**targetTouches**:手指在目标区域的手指信息 +**changedTouches**:最近一次触发该事件的手指信息 +touchend 时,touches 与 targetTouches 信息会被删除,changedTouches 保存的最后一次的信息,最好用于计算手指信息 +#### 3.参数信息 (changedTouches[0]) +**clientX**、**clientY** 在显示区的坐标 +**target**:当前元素 +#### 4.事件响应顺序 +ontouchstart > ontouchmove > ontouchend > onclick + +## 21.点击元素产生背景或边框怎么去掉 +* **iOS用户** 点击一个链接,会出现一个半透明灰色遮罩, 如果想要禁用,可设置`-webkit-tap-highlight-color`的alpha值为`0`去除灰色半透明遮罩; +* **android用户** 点击一个链接,会出现一个边框或者半透明灰色遮罩, 不同生产商定义出来额效果不一样,可设置`-webkit-tap-highlight-color`的alpha值为`0`去除部分机器自带的效果; +* **winphone系统** 点击标签产生的灰色半透明背景,能通过设置``去掉; +* 特殊说明:有些机型去除不了,如小米2。对于按钮类还有个办法,不使用a或者input标签,直接用div标签 +```css +a,button,input,textarea { + -webkit-tap-highlight-color: rgba(0,0,0,0); + -webkit-user-modify:read-write-plaintext-only; + //-webkit-user-modify 副作用 输入法不再能够输入多个字符 +} +``` +也可以 +```css +* { -webkit-tap-highlight-color: rgba(0,0,0,0); } +``` +winphone下 +```html + +``` +## 22.美化表单元素 +#### 1. 使用 appearance 改变 webkit 浏览器的默认外观 +```css +input,select {-webkit-appearance:none; appearance: none;} +``` +#### 2.winphone 下,使用伪元素改变表单元素默认外观 +* 1) 禁用 select 默认箭头,`::-ms-expand` 修改表单控件下拉箭头,设置隐藏并使用背景图片来修饰 +```css +select::-ms-expand {display:none;} +``` +* 2) 禁用 radio 和 checkbox 默认样式,`::-ms-check` 修改表单复选框或单选框默认图标,设置隐藏并使用背景图片来修饰 +```css +input[type=radio]::-ms-check, +input[type=checkbox]::-ms-check { display:none; } +``` +* 3) 禁用 pc 端表单输入框默认清除按钮,`::-ms-clear` 修改清除按钮,设置隐藏并使用背景图片来修饰 +```css +input[type=text]::-ms-clear, +input[type=tel]::-ms-clear, +input[type=number]::-ms-clear { display:none; } +``` + +## 23.移动端字体单位 font-size 选择 px 还是 rem? +如需适配多种移动设备,建议使用 rem。以下为参考值: +```css +html {font-size: 62.5%;} //10*16 = 62.5% +``` +设置 12px 字体 这里注意在 rem 前要加上对应的 px 值,解决不支持 rem 的浏览器的兼容问题,做到优雅降级 +```css +body {font-size:12px; font-size:1.2rem;} +``` + +## 24.input 标签添加上 disable 属性在 iOS 端字体颜色不兼容的问题? +```css +input[disabled],input:disabled,input.disabled{ + color: #3e3e3e; + -webkit-text-fill-color: #3e3e3e; + -webkit-opacity:1; + opacity: 1; +} +``` + +## 25.iOS 的光标大小问题 +#### IE: +不管该行有没有文字,光标高度与 font-size 一致。 +#### FF: +该行有文字时,光标高度与 font-size 一致。该行无文字时,光标高度与 input 的 height 一致。 +#### Chrome: +该行无文字时,光标高度与 line-height 一致;该行有文字时,光标高度从 input 顶部到文字底部 (这两种情况都是在有设定 line-height 的时候),如果没有 line-height,则是与 font-size 一致。 + +iOS 中情况和 Chrome 相似。 +设置字体大小和行高一致,然后通过 padding 撑开大小,只给 IE 浏览器设置 +```css +line-height:-ms-line-height:40px; +``` + +原文:https://segmentfault.com/a/1190000015178877 作者:键盘上的眼泪 \ No newline at end of file diff --git "a/Cute-Article/article/44-\345\211\215\347\253\257\346\234\254\345\234\260\346\226\207\344\273\266\346\223\215\344\275\234\345\222\214\344\270\212\344\274\240.md" "b/Cute-Article/article/44-\345\211\215\347\253\257\346\234\254\345\234\260\346\226\207\344\273\266\346\223\215\344\275\234\345\222\214\344\270\212\344\274\240.md" new file mode 100644 index 00000000..195d40fc --- /dev/null +++ "b/Cute-Article/article/44-\345\211\215\347\253\257\346\234\254\345\234\260\346\226\207\344\273\266\346\223\215\344\275\234\345\222\214\344\270\212\344\274\240.md" @@ -0,0 +1,352 @@ +[原文地址](https://juejin.im/post/5a193b4bf265da43052e528a) + +前端无法像原生APP一样直接操作本地文件,否则的话打开个网页就能把用户电脑上的文件偷光了,所以需要通过用户触发,用户可通过以下三种方式操作触发: + +1. 通过input type="file" 选择本地文件 +2. 通过拖拽的方式把文件拖过来 +3. 在编辑框里面复制粘贴 + +### 第一种 +第一种是最常用的手段,通常还会自定义一个按钮,然后盖在它上面,因为`type="file"`的input不好改变样式。如下代码写一个选择控件,并放在form里面: +```html +
+ +
+``` +然后就可以用FormData获取整个表单的内容: +```js +$("#file-input").on("change", function() { + console.log(`file name is ${this.value}`); + let formData = new FormData(this.form); + formData.append("fileName", this.value); + console.log(formData); +}); +``` +把input的value和formData打印出来是这样的: +![预览](https://user-gold-cdn.xitu.io/2017/11/25/15ff349ac0afefa0?imageView2/0/w/1280/h/960/format/webp/ignore-error/1) + +可以看到文件的路径是一个假的路径,也就是说在浏览器无法获取到文件的真实存放位置。同时FormData打印出来是一个空的Objet,但并不是说它的内容是空的,只是它对前端开发人员是透明的,无法查看、修改、删除里面的内容,只能`append`添加字段。 + +`FormData`无法得到文件的内容,而使用`FileReader`可以读取整个文件的内容。用户选择文件之后,`input.files`就可以得到用户选中的文件,如下代码: +```js +$("#file-input").on("change", function() { + let fileReader = new FileReader(), + fileType = this.files[0].type; + fileReader.onload = function() { + if (/^image/.test(fileType)) { + // 读取结果在fileReader.result里面 + $(``).appendTo("body"); + } + } + // 打印原始File对象 + console.log(this.files[0]); + // base64方式读取 + fileReader.readAsDataURL(this.files[0]); +}); +``` +把原始的File对象打印出来是这样的: +![预览](https://user-gold-cdn.xitu.io/2017/11/25/15ff349ac079ba68?imageView2/0/w/1280/h/960/format/webp/ignore-error/1) + + +它是一个window.File的实例,包含了文件的修改时间、文件名、文件的大小、文件的mime类型等。 +如果需要`限制上传文件的大小`就可以通过判断`size`属性有没有超,单位是字节,而要判断是否为图片文件就可以通过type类型是否以image开头。通过判断文件名的后缀可能会不准,而通过这种判断会比较准。上面的代码使用了一个正则判断,如果是一张图片的话就把它赋值给img的src,并添加到dom里面,但其实这段代码有点问题,就是web不是所有的图片都能通过img标签展示出来,通常是jpg/png/gif这三种,所以你应该需要再判断一下图片格式,如可以把判断改成: +```js +/^image\/[jpeg|png|gif]/.test(this.type) +``` +然后实例化一个`FileReader`,调它的`readAsDataURL`并把`File`对象传给它,监听它的`onload`事件,load完读取的结果就在它的`result`属性里了。它是一个`base64`格式的,可直接赋值给一个img的src。 + +使用`FileReader`除了可读取为`base64`之外,还能读取为以下格式: +```js +// 按base64的方式读取,结果是base64,任何文件都可转成base64的形式 +fileReader.readAsDataURL(this.files[0]); + +// 以二进制字符串方式读取,结果是二进制内容的utf-8形式,已被废弃了 +fileReader.readAsBinaryString(this.files[0]); + +// 以原始二进制方式读取,读取结果可直接转成整数数组 +fileReader.readAsArrayBuffer(this.files[0]); +``` +其它的主要是能读取为`ArrayBuffer`,它是一个原始二进制格式的结果。把`ArrayBuffer`打印出来是这样的: +![预览](https://user-gold-cdn.xitu.io/2017/11/25/15ff349ac05a9b42?imageView2/0/w/1280/h/960/format/webp/ignore-error/1) +可以看到,它对前端开发人员也是透明的,不能够直接读取里面的内容,但可以通过`ArrayBuffer.length`得到长度,还能转成整型数组,就能知道文件的原始二进制内容了: +```js +let buffer = this.result; +// 依次每字节8位读取,放到一个整数数组 +let view = new Uint8Array(buffer); +console.log(view); +``` + +### 第二种 +如果是通过第二种拖拽的方式,应该怎么读取文件呢?如下html(样式略): +```html +
+ drop your image here +
+``` +这将在页面显示一个框: +![预览](https://user-gold-cdn.xitu.io/2017/11/25/15ff349ac1f2ebd5?imageView2/0/w/1280/h/960/format/webp/ignore-error/1) + +然后监听它的拖拽事件: +```js +$(".img-container").on("dragover", function (event) { + event.preventDefault(); +}) + +.on("drop", function(event) { + event.preventDefault(); + // 数据在event的dataTransfer对象里 + let file = event.originalEvent.dataTransfer.files[0]; + + // 然后就可以使用FileReader进行操作 + fileReader.readAsDataURL(file); + + // 或者是添加到一个FormData + let formData = new FormData(); + formData.append("fileContent", file); +}) +``` +数据在`drop`事件的`event.dataTransfer.files`里面,拿到这个`File`对象之后就可以和输入框进行一样的操作了,即使用`FileReader`读取,或者是新建一个空的`formData`,然后把它`append`到`formData`里面。 + + +### 第三种 +第三种粘贴的方式,通常是在一个编辑框里操作,如把`div`的`contenteditable`设置为true: +```html +
+ hello, paste your image here +
+``` +粘贴的数据是在`event.clipboardData.files`里面: +```js +$("#editor").on("paste", function(event) { + let file = event.originalEvent.clipboardData.files[0]; +}); +``` +但是Safari的粘贴不是通过`event`传递的,它是直接在输入框里面添加一张图片,如下图所示: +![预览](https://user-gold-cdn.xitu.io/2017/11/25/15ff349ac3330972?imageView2/0/w/1280/h/960/format/webp/ignore-error/1) + +它新建了一个`img`标签,并把`img`的`src`指向一个`blob`的本地数据。什么是`blob`呢,如何读取`blob`的内容呢? +blob是一种类文件的存储格式,它可以存储几乎任何格式的内容,如json: +```js +let data = {hello: "world"}; +let blob = new Blob([JSON.stringify(data)], + {type : 'application/json'}); +``` +为了获取本地的blob数据,我们可以用ajax发个本地的请求: +```js +$("#editor").on("paste", function(event) { + // 需要setTimeout 0等图片出来了再处理 + setTimeout(() => { + let img = $(this).find("img[src^='blob']")[0]; + console.log(img.src); + // 用一个xhr获取blob数据 + let xhr = new XMLHttpRequest(); + xhr.open("GET", img.src); + // 改变mime类型 + xhr.responseType = "blob"; + xhr.onload = function () { + // response就是一个Blob对象 + console.log(this.response); + }; + xhr.send(); + }, 0); +}); +``` +上面代码把blob打印出来是这样的: +![预览](https://user-gold-cdn.xitu.io/2017/11/25/15ff349ac408b172?imageView2/0/w/1280/h/960/format/webp/ignore-error/1) + +能得到它的大小和类型,但是具体内容也是不可见的,它有一个`slice`的方法,可用于切割大文件。和`File`一样,可以使用`FileReader`读取它的内容: +```js +function readBlob(blobImg) { + let fileReader = new FileReader(); + fileReader.onload = function() { + console.log(this.result); + } + fileReader.onerror = function(err) { + console.log(err); + } + fileReader.readAsDataURL(blobImg); +} +readBlob(this.response); +``` +除此,还能使用`window.URL`读取,这是一个新的API,经常和`Service Worker`配套使用,因为SW里面常常要解析url。如下代码: +```js +function readBlob(blobImg) { + let urlCreator = window.URL || window.webkitURL; + // 得到base64结果 + let imageUrl = urlCreator.createObjectURL(this.response); + return imageUrl; +} + +readBlob(this.response); +``` +关于src使用的是blob链接的,除了上面提到的img之外,另外一个很常见的是video标签,如youtobe的视频就是使用的blob: +![](https://user-gold-cdn.xitu.io/2017/11/25/15ff349af40951d9?imageView2/0/w/1280/h/960/format/webp/ignore-error/1) + +这种数据不是直接在本地的,而是通过持续请求视频数据,然后再通过`blob`这个容器媒介添加到`video`里面,它也是通过URL的API创建的: +```js +let mediaSource = new MediaSource(); +video.src = URL.createObjectURL(mediaSource); +let sourceBuffer = mediaSource.addSourceBuffer('video/mp4; codecs="avc1.42E01E, mp4a.40.2"'); +sourceBuffer.appendBuffer(buf); +``` +具体我也没实践过,不再展开讨论。 + +上面,我们使用了三种方式获取文件内容,最后得到: + +1. `FormData`格式 +2. `FileReader`读取得到的`base64`或者`ArrayBuffer`二进制格式 + +如果直接就是一个`FormData`了,那么直接用`ajax`发出去就行了,不用做任何处理: +```js +let form = document.querySelector("form"), + formData = new FormData(form), +formData.append("fileName", "photo.png"); + +let xhr = new XMLHttpRequest(); +// 假设上传文件的接口叫upload +xhr.open("POST", "/upload"); +xhr.send(formData); +``` +如果用jQuery的话,要设置两个属性为false: +```js +$.ajax({ + url: "/upload", + type: "POST", + data: formData, + processData: false, // 不处理数据 + contentType: false // 不设置内容类型 +}); +``` +因为jQuery会自动把内容做一些转义,并且根据`data`自动设置请求`mime`类型,这里告诉jQuery直接用`xhr.send`发出去就行了。 + +观察控制台发请求的数据: +![预览](https://user-gold-cdn.xitu.io/2017/11/25/15ff349b104c3494?imageView2/0/w/1280/h/960/format/webp/ignore-error/1) + +可以看到这是一种区别于用`&`连接参数的方式,它的编码格式是`multipart/form-data`,就是上传文件`form`表单写的`enctype`: +```html +
+ +
+``` +如果`xhr.send`的是`FormData`类型话,它会自动设置`enctype`,如果你用默认表单提交上传文件的话就得在`form`上面设置这个属性,因为上传文件只能使用`POST`的这种编码。常用的`POST`编码是`application/x-www-form-urlencoded`,它和`GET`一样,发送的数据里面,参数和参数之间使用`&`连接,如: +```js +key1=value1&key2=value2 +``` +特殊字符做转义,这个数据`POST`是放在请求`body`里的,而`GET`是拼在`url`上面的,如果用jq的话,jq会帮你拼并做转义。 + +而上传文件用的这种`multipart/form-data`,参数和参数之间是且一个相同的字符串隔开的,上面的是使用: +``` +------WebKitFormBoundary72yvM25iSPYZ4a3F +``` +这个字符通常会取得比较长、比较随机,因为要保证正常的内容里面不会出现这个字符串,这样内容的特殊字符就不用做转义了。 + +请求的contentType被浏览器设置成: +``` +Content-Type: +multipart/form-data; boundary=----WebKitFormBoundary72yvM25iSPYZ4a3F +``` +后端服务通过这个就知道怎么解析这么一段数据了。(通常是使用的框架处理了,而具体的接口不需要关心应该怎么解析) + +如果读取结果是`ArrayBuffer`的话,也是可以直接用`xhr.send`发送出去的,但是一般我们不会直接把一个文件的内容发出去,而是用某个字段名等于文件内容的方式。如果你读取为`ArrayBuffer`的话再上传的话其实作用不是很大,还不如直接用`formData`添加一个`File`对象的内容,因为上面三种方式都可以拿到`File`对象。如果一开始就是一个`ArrayBuffer`了,那么可以转成`blob`然后再`append`到`FormData`里面。 + +使用比较多的应该是`base64`,因为前端经常要处理图片,读取为`base64`之后就可以把它画到一个`canvas`里面,然后就可以做一些处理,如压缩、裁剪、旋转等。最后再用`canvas`导出一个`base64`格式的图片,那怎么上传`base64`格式的呢? + +### 怎么上传`base64`格式 +第一种是拼一个表单上传的`multipart/form-data`的格式,再用`xhr.sendAsBinary`发出去,如下代码: +```js +let base64Data = base64Data.replace(/^data:image\/[^;]+;base64,/, ""); +let boundary = "----------boundaryasoifvlkasldvavoadv"; +xhr.sendAsBinary([ + // name=data + boundary, + 'Content-Disposition: form-data; name="data"; filename="' + fileName + '"', + 'Content-Type: ' + "image/" + fileType, '', + atob(base64Data), boundary, + //name=imageType + boundary, + 'Content-Disposition: form-data; name="imageType"', '', + fileType, + boundary + '--' +].join('\r\n')); +``` +上面代码使用了`window.atob`的api,它可以把`base64`还原成原始内容的字符串表示,如下图所示: +![预览](https://user-gold-cdn.xitu.io/2017/11/25/15ff349b4c18f7d8?imageView2/0/w/1280/h/960/format/webp/ignore-error/1) + +`btoa`是把内容转化成`base64`编码,而`atob`是把`base64`还原。在调`atob`之前,需要把表示内容格式的不属于`base64`内容的字符串去掉,即上面代码第一行的`replace`处理。 + +这样就和使用`formData`类似了,但是由于`sendAsBinary`已经被`deprecated`了,所以新代码不建议再使用这种方式。那怎么办呢? + +可以把`base64`转化成`blob`,然后再`append`到一个`formData`里面,下面的函数(来自b64-to-blob)可以把`base64`转成`blob`: +```js +function b64toBlob(b64Data, contentType, sliceSize) { + contentType = contentType || ''; + sliceSize = sliceSize || 512; + + var byteCharacters = atob(b64Data); + var byteArrays = []; + + for (var offset = 0; offset < byteCharacters.length; offset += sliceSize) { + var slice = byteCharacters.slice(offset, offset + sliceSize); + + var byteNumbers = new Array(slice.length); + for (var i = 0; i < slice.length; i++) { + byteNumbers[i] = slice.charCodeAt(i); + } + + var byteArray = new Uint8Array(byteNumbers); + + byteArrays.push(byteArray); + } + + var blob = new Blob(byteArrays, {type: contentType}); + return blob; +} +``` +然后就可以`append`到`formData`里面: +```js +let blob = b64toBlob(b64Data, "image/png"), + formData = new FormData(); +formData.append("fileContent", blob); +``` +这样就不用自己去拼一个`multipart/form-data`的格式数据了。 + +上面处理和上传文件的API可以兼容到IE10+,如果要兼容老的浏览器应该怎么办呢? + +可以借助一个`iframe`,原理是默认的`form`表单提交会刷新页面,或者跳到`target`指定的那个url,但是如果把`ifrmae`的`target`指向一个`iframe`,那么刷新的就是`iframe`,返回结果也会显示在`ifame`,然后获取这个`ifrmae`的内容就可得到上传接口返回的结果。 + +如下代码: +```js +let iframe = document.createElement("iframe"); +iframe.display = "none"; +iframe.name = "form-iframe"; +document.body.appendChild(iframe); +// 改变form的target +form.target = "form-iframe"; + +iframe.onload = function() { + //获取iframe的内容,即服务返回的数据 + let responseText = this.contentDocument.body.textContent + || this.contentWindow.document.body.textContent; +}; + +form.submit(); +``` +`form.submit`会触发表单提交,当请求完成(成功或者失败)之后就会触发iframe的onload事件,然后在onload事件获取返回的数据,如果请求失败了的话,iframe里的内容就为空,可以用这个判断请求有没有成功。 + + + +使用iframe没有办法获取上传进度,使用xhr可以获取当前上传的进度,这个是在XMLHttpRequest 2.0引入的: +```js +xhr.upload.onprogress = function (event) { + if (event.lengthComputable) { + // 当前上传进度的百分比 + duringCallback ((event.loaded / event.total)*100); + } +}; +``` +这样就可以做一个真实的loading进度条。 + + +本文讨论了3种交互方式的读取方式,通过`input`控件在`input.files`可以得到File文件对象,通过拖拽的是在`drop`事件的`event.dataTransfer.files`里面,而通过粘贴的`paste`事件在`event.clipboardData.files`里面,Safari这个怪胎是在编辑器里面插入一个src指向本地的img标签,可以通过发送一个请求加载本地的`blob`数据,然后再通过`FileReader`读取,或者直接`append`到`formData`里面。得到的File对象就可以直接添加到`FormData`里面,如果需要先读取`base64`格式做处理的,那么可以把处理后的`base64`转化为`blob`数据再`append`到`formData`里面。对于老浏览器,可以使用一个iframe解决表单提交刷新页面或者跳页的问题。 + +总之,前端处理和上传本地文件应该差不多就是这些内容了,但是应该还有好多细节没有提及到,读者可通过本文列的方向自行实践。如果有其它的上传方式还请告知。 \ No newline at end of file diff --git "a/Cute-Article/article/45-js\344\270\255reduce\347\232\204\347\245\236\345\245\207\347\224\250\346\263\225.md" "b/Cute-Article/article/45-js\344\270\255reduce\347\232\204\347\245\236\345\245\207\347\224\250\346\263\225.md" new file mode 100644 index 00000000..01b61708 --- /dev/null +++ "b/Cute-Article/article/45-js\344\270\255reduce\347\232\204\347\245\236\345\245\207\347\224\250\346\263\225.md" @@ -0,0 +1,135 @@ +最近经常在项目中经常看到别人用reduce处理数据,很是牛掰,很梦幻, 不如自己琢磨琢磨。先看w3c语法。 +## w3c语法 +```js +array.reduce(function(total, currentValue, currentIndex, arr), initialValue); +/* +total: 必需。初始值, 或者计算结束后的返回值。 +currentValue: 必需。当前元素。 +currentIndex: 可选。当前元素的索引; +arr: 可选。当前元素所属的数组对象。 +initialValue: 可选。传递给函数的初始值,相当于total的初始值。 +*/ +``` + +## 常见用法 + +### 1.数组求和 +```js +const arr = [12, 34, 23]; +const sum = arr.reduce((total, num) => total + num); +// 设定初始值求和 +const arr = [12, 34, 23]; +const sum = arr.reduce((total, num) => total + num, 10); // 以10为初始值求和 +// 对象数组求和 +var result = [ +{ subject: 'math', score: 88 }, +{ subject: 'chinese', score: 95 }, +{ subject: 'english', score: 80 } +]; +const sum = result.reduce((prev, cur) => prev + cur.score, 0); +const sum = result.reduce((prev, cur) => prev + cur.score, -10); // 总分扣除10分 +``` + +### 2.数组最大值 +```js +const a = [23,123,342,12]; +const max = a.reduce(function(pre,cur,inde,arr){return pre>cur?pre:cur;}); // 342 +``` + +## 进阶用法 + +### 1.数组对象中的用法 +```js +// 比如生成“老大、老二和老三” +const objArr = [{name: '老大'}, {name: '老二'}, {name: '老三'}]; +const res = objArr.reduce((pre, cur, index, arr) => { +if (index === 0) { +return cur.name; +} +else if (index === (arr.length - 1)) { +return pre + '和' + cur.name; +} +else { +return pre + '、' + cur.name; +} +}, ''); +``` +### 2.求字符串中字母出现的次数 +```js +const str = 'sfhjasfjgfasjuwqrqadqeiqsajsdaiwqdaklldflas-cmxzmnha'; +const res = str.split('').reduce((prev, cur) => {prev[cur] ? prev[cur]++ : prev[cur] = 1; return prev;}, {}); +``` + +### 3.数组转数组 +```js +// 按照一定的规则转成数组 +var arr1 = [2, 3, 4, 5, 6]; // 每个值的平方 +var newarr = arr1.reduce((prev, cur) => {prev.push(cur * cur); return prev;}, []); +``` + +### 4.数组转对象 +```js +// 按照id 取出stream +var streams = [{name: '技术', id: 1}, {name: '设计', id: 2}]; +var obj = streams.reduce((prev, cur) => {prev[cur.id] = cur; return prev;}, {}); +``` + +## 高级用法 + +### 1.多维的叠加执行操作 +```js +// 各科成绩占比重不一样, 求结果 +var result = [ +{ subject: 'math', score: 88 }, +{ subject: 'chinese', score: 95 }, +{ subject: 'english', score: 80 } +]; +var dis = { +math: 0.5, +chinese: 0.3, +english: 0.2 +}; +var res = result.reduce((prev, cur) => dis[cur.subject] * cur.score + prev, 0); + +// 加大难度, 商品对应不同国家汇率不同,求总价格 +var prices = [{price: 23}, {price: 45}, {price: 56}]; +var rates = { +us: '6.5', +eu: '7.5', +}; +var initialState = {usTotal:0, euTotal: 0}; +var res = prices.reduce((prev1, cur1) => Object.keys(rates).reduce((prev2, cur2) => { +console.log(prev1, cur1, prev2, cur2); +prev1[`${cur2}Total`] += cur1.price * rates[cur2]; +return prev1; +}, {}), initialState); + +var manageReducers = function() { +return function(state, item) { +return Object.keys(rates).reduce((nextState, key) => { +state[`${key}Total`] += item.price * rates[key]; +return state; +}, {}); +} +}; +var res1= prices.reduce(manageReducers(), initialState); +``` + +### 2.扁平一个多维数组 +```js +var arr = [[1, 2, 8], [3, 4, 9], [5, 6, 10]]; +var res = arr.reduce((x, y) => x.concat(y), []); +``` + +### 3.对象数组去重 +```js +const hash = {}; +chatlists = chatlists.reduce((obj, next: Object) => { +const hashId = `${next.topic}_${next.stream_id}`; +if (!hash[hashId]) { +hash[`${next.topic}_${next.stream_id}`] = true; +obj.push(next); +} +return obj; +}, []); +``` diff --git "a/Cute-Article/article/46-\345\234\250JavaScript\344\270\255\346\233\264\345\245\275\347\232\204\344\275\277\347\224\250\346\225\260\347\273\204.md" "b/Cute-Article/article/46-\345\234\250JavaScript\344\270\255\346\233\264\345\245\275\347\232\204\344\275\277\347\224\250\346\225\260\347\273\204.md" new file mode 100644 index 00000000..f6ed44b0 --- /dev/null +++ "b/Cute-Article/article/46-\345\234\250JavaScript\344\270\255\346\233\264\345\245\275\347\232\204\344\275\277\347\224\250\346\225\260\347\273\204.md" @@ -0,0 +1,142 @@ +[阅读原文](https://juejin.im/post/5b8d0a74f265da431d0e7ec0) +[MDN Array 介绍](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array) + +本文短小精悍,我保证。在过去的数个月里,我注意到在我审阅的 pull request 中有四个(关于数组使用的)错误经常出现。同时,我自己也会犯这些错误,因此有了这篇文章。让我们一起学习,以确保以后能正确地使用数组方法! + +## 1.使用 `Array.includes` 替代 `Array.indexOf` + +> "如果需要在数组中查找某个元素,请使用 `Array.indexOf`。" + +我记得在我学习 JavaScript 的课程中有类似的这么一句话。毫无疑问,这完全正确! + +在 MDN 文档中,对 `Array.indexOf` 的描述是:返回在数组中可以找到一个给定元素的第一个索引,如果不存在,则返回`-1`。因此,如果在之后的代码中需要用到(给给定元素的)索引,那么` Array.indexOf` 是不二之选。 + +然而,如果我们仅需要知道数组中是否包含给定元素呢?这意味着只是是与否的区别,这是一个布尔问题(boolean question)。针对这种情况,我建议使用直接返回布尔值的 `Array.includes`。 +```js +'use strict'; + +const characters = [ + 'ironman', + 'black_widow', + 'hulk', + 'captain_america', + 'hulk', + 'thor', +]; + +console.log(characters.indexOf('hulk')); +// 2 +console.log(characters.indexOf('batman')); +// -1 + +console.log(characters.includes('hulk')); +// true +console.log(characters.includes('batman')); +// false +``` + +## 2.使用 `Array.find` 替代 `Array.filter` +`Array.filter` 是一个十分有用的方法。它通过回调函数过滤原数组,并将过滤后的项作为新数组返回。正如它的名字所示,我们将这个方法用于过滤,(一般而言)会获得一个长度更短的新数组。 + +然而,如果知道经回调函数过滤后,只会剩余唯一的一项,那么我不建议使用 `Array.filter`。比如:使用等于某个唯一 ID 为过滤条件去过滤一个数组。在这个例子中,`Array.filter` 返回一个仅有一项的新数组。然而,我们仅仅是为了获取 ID 为特定 ID 的那一项,这个新数组显得毫无用处。 + +让我们讨论一下性能。为了获取所有符合回调函数过滤条件的项,`Array.filter` 必须遍历整个数组。如果原数组中有成千上万项,回调函数需要执行的次数是相当多的。 + +为避免这些情况,我建议使用 `Array.find`。它与 `Array.filter` 一样需要一个回调函数,(但只是返回)符合条件的第一项。当找到符合回调函数过滤条件的第一个元素时,它会立即停止往下的搜寻。不再遍历整个数组。 +```js +'use strict'; + +const characters = [ + { id: 1, name: 'ironman' }, + { id: 2, name: 'black_widow' }, + { id: 3, name: 'captain_america' }, + { id: 4, name: 'captain_america' }, +]; + +function getCharacter(name) { + return character => character.name === name; +} + +console.log(characters.filter(getCharacter('captain_america'))); +// [ +// { id: 3, name: 'captain_america' }, +// { id: 4, name: 'captain_america' }, +// ] + +console.log(characters.find(getCharacter('captain_america'))); +// { id: 3, name: 'captain_america' } +``` + +## 3.使用 `Array.some` 替代 `Array.find` +我承认我经常犯这个错误。之后,一位朋友建议我去查看 [MDN 文档](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/some) 以寻找更好的方法。事实上(这错误)与上面 `Array.indexOf`/`Array.includes` 的例子十分相像。 + +在上面的例子中,我们知道 `Array.find` 需要一个回调函数作为参数,并返回(符合条件的)第一个元素。然而,当我们需要知道数组中是否存在一个元素时,`Array.find` 是最好的选择吗?不一定是,因为它返回一个元素,而不是一个布尔值。 + +在下面的例子中,我建议使用 `Array.some`,它返回你需要的布尔值。 +```js +'use strict'; + +const characters = [ + { id: 1, name: 'ironman', env: 'marvel' }, + { id: 2, name: 'black_widow', env: 'marvel' }, + { id: 3, name: 'wonder_woman', env: 'dc_comics' }, +]; + +function hasCharacterFrom(env) { + return character => character.env === env; +} + +console.log(characters.find(hasCharacterFrom('marvel'))); +// { id: 1, name: 'ironman', env: 'marvel' } + +console.log(characters.some(hasCharacterFrom('marvel'))); +// true +``` +译者注:补充一下 `Array.some` 与 `Array.includes` 使用上的区别。两者都返回一个布尔值,表示某项是否存在于数组之中,一旦找到对应的项,立即停止遍历数组。不同的是 `Array.some` 的参数是回调函数,而 `Array.includes` 的参数是一个值(均不考虑第二个可选参数)。 +假设希望知道值为 value 的项是否存在于数组中,既可以编写代码:`[].includes(value)`, 也可以给 `Array.some` 传入 `item => item === value` 作为回调函数。`Array.includes` 使用更简单,`Array.some` 可操控性更强。 + +## 4.使用 `Array.reduce` 替代 `Array.filter` 与 `Array.map` 的组合 +事实上说,`Array.reduce` 不太容易理解。然而,如果我们先使用 `Array.filter` 过滤原数组,之后(对结果)再调用 `Array.map` (以获取一个新数组)。这看起似乎有点问题,是我们忽略了什么吗? + +这样做的问题是:我们遍历了两次数组。第一次是过滤原数组以获取一个长度稍短的新数组,第二次遍历(译者注:指 `Array.map`)是对 `Array.filter` 的返回的新数组进行加工,再次创造了一个新数组!为得到最终的结果,我们结合使用了两个数组方法。每个方法都有它自己的回调函数,而且供 `Array.map` 使用的临时数组是由 `Array.filter` 提供的,(一般而言)该数组无法复用。 + +为避免如此低效场景的出现,我的建议是使用 `Array.reduce` 。一样的结果,更好的代码!`Array.reduce` 允许你将过滤后切加工过的项放进累加器中。累加器可以是需要待递增的数字、待填充的对象、 待拼接的字符串或数组等。 + +在上面的例子中,我们使用了 `Array.map`,(但更)建议使用累加器为待拼接数组的 `Array.reduce` 。在下面的例子中,根据变量 `env` 的值,我们会将它加进累加器中或保持累加器不变(即不作任何处理)。 +```js +'use strict'; + +const characters = [ + { name: 'ironman', env: 'marvel' }, + { name: 'black_widow', env: 'marvel' }, + { name: 'wonder_woman', env: 'dc_comics' }, +]; + +console.log( + characters + .filter(character => character.env === 'marvel') + .map(character => Object.assign({}, character, { alsoSeenIn: ['Avengers'] })) +); +// [ +// { name: 'ironman', env: 'marvel', alsoSeenIn: ['Avengers'] }, +// { name: 'black_widow', env: 'marvel', alsoSeenIn: ['Avengers'] } +// ] + +console.log( + characters + .reduce((acc, character) => { + return character.env === 'marvel' + ? acc.concat(Object.assign({}, character, { alsoSeenIn: ['Avengers'] })) + : acc; + }, []) +) +// [ +// { name: 'ironman', env: 'marvel', alsoSeenIn: ['Avengers'] }, +// { name: 'black_widow', env: 'marvel', alsoSeenIn: ['Avengers'] } +// ] +``` + +### 这就是本文的全部内容! +希望这对你有帮助。如果你对本文有任何意见或(关于数组方法使用的)例子需要讨论,请在评论中告诉我。如果你觉得本文不错,请给我点赞 👏 (译者注:对灯发誓,这是原文,不是译者骗赞!)并分享给更多的小伙伴。感谢你的阅读! + +注意:请在使用 `Array.find` 和 `Array.includes` 前检查浏览器是否支持相关方法,上述两个方法在 Internet Explorer 上并不支持(译者注:可以使用` Polyfill`)。 diff --git "a/Cute-Article/article/47-http\350\257\267\346\261\202\345\244\264\344\270\216\345\223\215\345\272\224\345\244\264\347\232\204\345\272\224\347\224\250.md" "b/Cute-Article/article/47-http\350\257\267\346\261\202\345\244\264\344\270\216\345\223\215\345\272\224\345\244\264\347\232\204\345\272\224\347\224\250.md" new file mode 100644 index 00000000..5d93ba9c --- /dev/null +++ "b/Cute-Article/article/47-http\350\257\267\346\261\202\345\244\264\344\270\216\345\223\215\345\272\224\345\244\264\347\232\204\345\272\224\347\224\250.md" @@ -0,0 +1,547 @@ +> [阅读原文](https://juejin.im/post/5b854ddef265da43635d9302) + +## Chap1 发现headers +当我们随便打开一个网址(比如大家经常拿来测试网络的百度)时,打开Network,会看到如下请求头,响应头: +![图1](https://user-gold-cdn.xitu.io/2018/8/28/16580c211bbcc595?imageView2/0/w/1280/h/960/ignore-error/1) +究竟这些headers都有什么用呢? 咱们挨个探个究竟。 + +## Chap2 headers用途 + +### 2.1 Content-Type +`Content-Type`表示请求头或响应头的内容类型。作为请求头时,利用它可以进行`body-parser`。 +Sooo~ What is body-parser? +body-parser是node常用的中间件,其作用是: + +> Parse incoming request bodies in a middleware before your handlers, available under the req.body property. + +即在处理数据之前用中间件对post请求体进行解析。 +[body-parser](https://www.npmjs.com/package/body-parser)的例子为: + +下面的例子展示了如何给路由添加`body parser`。通常,这是在`express`中最为推荐的使用`body-parser`的方法。 +```js +var express = require('express') +var bodyParser = require('body-parser') +var app = express() +// create application/json parser +var jsonParser = bodyParser.json() +// create application/x-www-form-urlencoded parser +var urlencodedParser = bodyParser.urlencoded({ extended: false }) +// POST /login gets urlencoded bodies +app.post('/login', urlencodedParser, function (req, res) { + if (!req.body) return res.sendStatus(400) + res.send('welcome, ' + req.body.username) +}) +// POST /api/users gets JSON bodies +app.post('/api/users', jsonParser, function (req, res) { + if (!req.body) return res.sendStatus(400) + // create user in req.body +}) +``` +`body-parser`核心源码为: +```js + // this uses a switch for static require analysis + switch (parserName) { + case 'json': + parser = require('./lib/types/json') + break + case 'raw': + parser = require('./lib/types/raw') + break + case 'text': + parser = require('./lib/types/text') + break + case 'urlencoded': + parser = require('./lib/types/urlencoded') + break + } +``` +以`json`为例: +```js +var contentType = require('content-type') +//... +/** + * Get the charset of a request. + * + * @param {object} req + * @api private + */ +function getCharset (req) { + try { + return (contentType.parse(req).parameters.charset || '').toLowerCase() + } catch (e) { + return undefined + } +} +//... +// assert charset per RFC 7159 sec 8.1 +var charset = getCharset(req) || 'utf-8' +if (charset.substr(0, 4) !== 'utf-') { + debug('invalid charset') + next(createError(415, 'unsupported charset "' + charset.toUpperCase() + '"', { + charset: charset, + type: 'charset.unsupported' + })) + return +} +``` +可以看出:其背后工作原理就是通过分析请求头中的`Content-Type`的类型,根据不同的类型进行相应数据处理,我们自己模拟一下: + +step1: 先建立`server.js`: +```js + req.on('end',function (params) { + let r = Buffer.concat(arr).toString(); + // body-parser 解析请求,根据不同的格式进行不同的解析 + if (req.headers['content-type'] === www.js){ + let querystring = require('querystring'); + r = querystring.parse(r); // a=1&b=2 + console.log(r,1); + } else if (req.headers['content-type'] === 'application/json'){ + console.log(JSON.parse(r),2); + } else{ + console.log(r,3); + } + res.end('end'); + }) +``` +step2: 客户端模拟请求: +```js +let opts = { + host:'localhost', + port:3000, + path:'/hello', + headers:{ + 'a':1, + 'Content-Type':'application/json', + "Content-Length":7 //模拟的时候需要带上长度,不然客户端会当成没有传递数据 + } +} +let http = require('http'); +let client = http.request(opts,function (res) { + res.on('data',function (data) { + console.log(data.toString()); + }) +}); +client.end("{\"a\":1}"); // 表示把请求发出去 +``` +step3: 测试。 +先启动server,再启动client,服务端收到按照`application/json`格式解析的数据: `{ a: 1 } 2`.`Content-Type`与`body-parser`之间的关系就先分析到这里了。后面我们接着看请求头。 + +### 2.2 Range:bytes +请求头通过`Range:bytes`可以请求资源的某一部分。利用这个字段可模拟部分读取。如下: +```js + http.createServer(function (req, res) { + let range = req.headers['range']; + }) +``` +server: +```js +let http = require('http'); +let fs = require('fs'); +let path = require('path'); +// 当前要下载的文件的大小 +let size = fs.statSync(path.join(__dirname, 'my.txt')).size; +let server = http.createServer(function (req, res) { + let range = req.headers['range']; // 0-3 + if (range) { + // 模拟请求 curl -v --header "Range:bytes=0-3" http://localhost:3000 + let [, start, end] = range.match(/(\d*)-(\d*)/); + start = start ? Number(start) : 0; + end = end ? Number(end) : size - 1; // 10个字节 size 10 (0-9) + res.setHeader('Content-Range', `bytes ${start}-${end}/${size - 1}`); + fs.createReadStream(path.join(__dirname, 'my.txt'), { start, end }).pipe(res); + } else { + // 会把文件的内容写给客户端 + fs.createReadStream(path.join(__dirname, 'my.txt')).pipe(res); + //可读流可以通过pipe导到可写流 + } +}); +server.listen(3000); +``` +client: +```js +let opts = { + host:'localhost', + port:3000, + headers:{} +} +let http = require('http'); +let start = 0; +let fs = require('fs'); +function download() { + opts.headers.Range = `bytes=${start}-${start+3}`; + start+=4; + console.log(`start is ${start}`) + let client = http.request(opts,function (res) { + let total = res.headers['content-range'].split('/')[1]; + // console.log(half) + res.on('data',function (data) { + fs.appendFileSync('./download1.txt',data); + }); + res.on('end',function () { + setTimeout(() => { + if ((!pause)&&(start < total)) + download(); + }, 1000); + }) + }); + client.end(); +} +download() +``` +分段读取添加暂停功能,监听用户输入 +```js +let pause = false; +process.stdin.on('data',function (data) { + if (data.toString().includes('p')){ + pause = true + }else{ + pause = false; + download() + } +}) +``` +测试结果: +![图2](https://user-gold-cdn.xitu.io/2018/9/10/165c2355ff651d7d?imageView2/0/w/1280/h/960/ignore-error/1) + +分段读取有以下好处: + +> 提高读取速度,多线程并行,分块读取 +> 断点续传 + +模拟并行下载: +```js +let halfFlag = 20 +function download() { + opts.headers.Range = `bytes=${start}-${start+3}`; + start+=4; + console.log(`start is ${start}`) + let client = http.request(opts,function (res) { + let total = res.headers['content-range'].split('/')[1]; + let halfFlag = Math.floor(total/2) + // console.log(half) + res.on('data',function (data) { + fs.appendFileSync('./download1.txt',data); + }); + res.on('end',function () { + setTimeout(() => { + if ((!pause)&&(start < halfFlag)) + download(); + }, 1000); + }) + }); + client.end(); +} +let half = halfFlag + +function downloadTwo() { + opts.headers.Range = `bytes=${half}-${half+3}`; + half+=4; + console.log(`half is ${half}`) + let client = http.request(opts,function (res) { + let total = res.headers['content-range'].split('/')[1]; + res.on('data',function (data) { + fs.appendFileSync('./download2.txt',data); + }); + res.on('end',function () { + setTimeout(() => { + if (!pause&&half < total) + downloadTwo(); + }, 1000); + }) + }); + client.end(); +} +download(); +downloadTwo(); +``` +运行结果,会把原文件分成两部分下载到download1.txt和download2.txt。 +测试: +![图3](https://user-gold-cdn.xitu.io/2018/9/10/165c23670c9165b8?imageView2/0/w/1280/h/960/ignore-error/1) + +理论上,这样的下载方式会比第一种方法节约一半的时间。但是实际中的文件下载怎样实现加速以及并行下载的,还有待考究。 + +### 2.3 Cache-Control与Expires之强制缓存 +Response Header响应头中`Cache-Control: max-age=1233`可以设置相对当前的时间的强制缓存,与它相关的`Expires`可以设置某个绝对时间点限定读取缓存的时间。 +模拟实现: +```js +let url = require('url'); // 专门用来处理url路径的核心模块 +// http://username:password@hostname:port/pathname?query +let server = http.createServer(async function (req,res) { + console.log(req.url) + let { pathname,query} = url.parse(req.url,true); + // true就是将query转化成对象 + let readPath = path.join(__dirname, 'public', pathname); + try { + let statObj = await stat(readPath); + // 根客户端说 10s 内走缓存 + res.setHeader('Cache-Control','max-age=10'); + res.setHeader('Expires',new Date(Date.now()+10*1000).toGMTString()); + // 10s之内的请求都会走cache 返回200, (from disk cache)不发生请求 + if (statObj.isDirectory()) { + let p = path.join(readPath, 'index.html'); + await stat(p); + // 如果当前目录下有html那么就返回这个文件 + fs.createReadStream(p).pipe(res); + } else { + fs.createReadStream(readPath).pipe(res); + } + }catch(e){ + res.statusCode = 404; + res.end(`Not found`); + } +}).listen(3000); +``` +测试: +![图4](https://user-gold-cdn.xitu.io/2018/9/10/165c24917e9fcc8f?imageView2/0/w/1280/h/960/ignore-error/1) +10s内刷新: +![图4](https://user-gold-cdn.xitu.io/2018/9/10/165c2498b620d636?imageView2/0/w/1280/h/960/ignore-error/1) + +### 2.4 对比缓存之Last-Modified和If-Modified-Since +对比响应头Last-Modified and 与请求头If-Modified-Since,可以通过文件修改时间看文件是否修改,从而决定是重新请求还是走缓存。 +模拟如下: +step1 不设置强制缓存 +```js +res.setHeader('Cache-Control','no-cache'); +``` +step2 应用文件修改时间比对是否修改, +```js +res.setHeader('Last-Modified', statObj.ctime.toGMTString()); +if (req.headers['if-modified-since'] === statObj.ctime.toGMTString()) { + res.statusCode = 304; + res.end(); + return; // 走缓存 +} +fs.createReadStream(readPath).pipe(res); +``` +测试: +![图6](https://user-gold-cdn.xitu.io/2018/9/10/165c25325aa08daf?imageView2/0/w/1280/h/960/ignore-error/1) + +### 2.5 对比缓存之Etag和 If-None-Match +对比响应头:Etag 与请求头:If-None-Match,Etag和If-None-Match如果相等,即返回304。 +etag如何添加? + +> 根据文件内容,生成一个md5的摘要,给实体加一个标签。 + +这种方法虽然比较耗性能,但是能够更加精确的对比出文件是否进行了修改。依靠文件修改时间进行对比并不够准确。因为有时文件有改动Last-Modified发生了变化,但是文件的内容可能根本没有变化。所以这种方案要优于2.4. + +实现方法: +```js +let rs = fs.createReadStream(p); +let md5 = crypto.createHash('md5'); // 不能写完响应体再写头 +let arr = []; +rs.on('data',function (data) { + md5.update(data); + arr.push(data); +}); +``` +设置Etag +```js +rs.on('end',function () { +let r = md5.digest('base64'); +res.setHeader('Etag', r); +if (req.headers['if-none-match'] === r ){ + res.statusCode = 304; + res.end(); + return; +} +res.end(Buffer.concat(arr)); +}) +``` +测试: +![图7](https://user-gold-cdn.xitu.io/2018/9/10/165c25456876367f?imageView2/0/w/1280/h/960/ignore-error/1) + +### 2.6 Accept-Encoding +依靠请求头: `Accept-Encoding: gzip, deflate`, br告诉服务端可接受的数据格式。服务端返回后会把数据格式通过响应格式通过Content-Encoding来标记。 +在客户端接受gzip的格式下,后端可通过文件压缩处理传递,提高性能。 +node api中提供了[zlib](http://nodejs.cn/api/zlib.html#zlib_class_zlib_gzip)模块: +> zlib模块提供通过 Gzip 和` Deflate/Inflate` 实现的压缩功能 + +下面我们来应用zlib与请求头`Accept-Encoding`来实现压缩功能。 +```js +let zlib = require('zlib'); +let fs = require('fs'); +let path = require('path'); +function gzip(filePath) { + let transform = zlib.createGzip();//转化流通过transform压缩,然后再写 + fs.createReadStream(filePath).pipe(transform).pipe(fs.createWriteStream(filePath+'.gz')); +} +gzip('2.txt') +``` +解压: +```js +function gunzip(filePath) { + let transform = zlib.createGunzip(); + fs.createReadStream(filePath).pipe(transform).pipe(fs.createWriteStream(path.basename(filePath,'.gz'))); +} +``` +`path.basename(filePath,'.gz')`用来去掉filePath文件名的后缀`.gz`。 +根据请求头接受的类型后端的具体操作 : +```js +if(req.url === '/download'){ + res.setHeader('Content-Disposition', 'attachment' ) + return fs.createReadStream(path.join(__dirname, '1.html')).pipe(res); +} +``` + +```js +let http = require('http'); +let fs = require('fs'); +let path = require('path'); +let zlib = require('zlib'); +http.createServer(function (req,res) { + if(req.url === '/download'){ + res.setHeader('Content-Disposition', 'attachment' ) + return fs.createReadStream(path.join(__dirname, '1.html')).pipe(res); + } + let rule = req.headers['accept-encoding']; + if(rule){ + if(rule.match(/\bgzip\b/)){ + res.setHeader('Content-Encoding','gzip'); + fs.createReadStream(path.join(__dirname, '1.html')) + .pipe(zlib.createGzip()) + .pipe(res); + } else if (rule.match(/\bdeflate\b/)){ + res.setHeader('Content-Encoding', 'deflate'); + fs.createReadStream(path.join(__dirname, '1.html')) + .pipe(zlib.createDeflate()) + .pipe(res); + }else{ + fs.createReadStream(path.join(__dirname, '1.html')).pipe(res); + } + }else{ + fs.createReadStream(path.join(__dirname, '1.html')).pipe(res); + } +}).listen(3000); +``` + +test deflate: +```docker +curl -v --header "Accept-Encoding:deflate" http://localhost:3000 +* Rebuilt URL to: http://localhost:3000/ +* Trying 127.0.0.1... +* TCP_NODELAY set +* Connected to localhost (127.0.0.1) port 3000 (#0) +> GET / HTTP/1.1 +> Host: localhost:3000 +> User-Agent: curl/7.54.0 +> Accept: */* +> Accept-Encoding:deflate +> +< HTTP/1.1 200 OK +< Content-Encoding: deflate +< Date: Thu, 23 Aug 2018 03:01:13 GMT +< Connection: keep-alive +< Transfer-Encoding: chunked +``` +test others: +```docker +curl -v --header "Accept-Encoding:nn" http://localhost:3000 +* Rebuilt URL to: http://localhost:3000/ +* Trying 127.0.0.1... +* TCP_NODELAY set +* Connected to localhost (127.0.0.1) port 3000 (#0) +> GET / HTTP/1.1 +> Host: localhost:3000 +> User-Agent: curl/7.54.0 +> Accept: */* +> Accept-Encoding:nn +> +< HTTP/1.1 200 OK +< Date: Thu, 23 Aug 2018 03:02:51 GMT +< Connection: keep-alive +< Transfer-Encoding: chunked +< + + + + + + + Document + + + 你好 + + +* Connection #0 to host localhost left intact +% +``` +### 2.7 referer +![图8](https://user-gold-cdn.xitu.io/2018/9/10/165c268acf50a0af?imageView2/0/w/1280/h/960/ignore-error/1) +referer表示请求文件的网址,请求时会携带。为了防止自己网站的文件被外网直接引用,可以通过比较referer,即请求的地址,与本地地址比较,设置防盗链。 +```js +let http = require('http'); +let fs = require('fs'); +let url = require('url'); +let path = require('path'); +// 这是百度的服务器 +let server = http.createServer(function (req,res) { + let { pathname } = url.parse(req.url); + let realPath = path.join(__dirname,pathname); + fs.stat(realPath,function(err,statObj) { + if(err){ + res.statusCode = 404; + res.end(); + }else{ + let referer = req.headers['referer'] || req.headers['referred']; + if(referer){ + let current = req.headers['host'] // 代表的是当前图片的地址 + referer = url.parse(referer).host // 引用图片的网址 + if (current === referer){ + fs.createReadStream(realPath).pipe(res); + }else{ + fs.createReadStream(path.join(__dirname,'images/2.jpg')).pipe(res); + } + }else{ + fs.createReadStream(realPath).pipe(res); + } + } + }) +}).listen(3000); +``` +### 2.8 Accept-Language +请求头:`Accept-Language: zh-CN,zh;q=0.9` +多个语言用 ',' 分隔,权重用 '=' 表示',没有默认权重为1 + +后端根据请求接受语言的权重一次查找,查找到就返回,找不到就用默认语言 +```js +let langs = { + en: 'hello world', + 'zh-CN':'你好世界', + zh:'你好', + ja: 'こんにちは、世界' +} +let defualtLanguage = 'en' +// 多语言之服务端方案:来做 (浏览器会发一个头) 前端来做 +// 通过url实现多语言 +let http = require('http'); +http.createServer(function (req,res) { + let lan = req.headers['accept-language']; + //[[zh,q=0.9],[zh-CN]] =>[{name:'zh-CN',q=1},{name:'zh',q:0.9}] + if(lan){ + lan = lan.split(','); + lan = lan.map(l=>{ + let [name,q] = l.split(';'); + q = q?Number(q.split('=')[1]):1 + return {name,q} + }).sort((a,b)=>b.q-a.q); // 排出 权重数组 + + for(let i = 0 ;i `标签,所以也可表示为: +```js +// 表示HTML文档所在窗口的当前高度 +let height = document.body.clientHeight; +// 表示HTML文档所在窗口的当前宽度 +let width = document.body.clientWidth; +``` +结论: +`document.body.clientWidth/Height` 的宽高偏小,高甚至默认200; +`document.documentElement.clientWidth/Height` 和 `window.innerWidth/Height` 的宽高始终相等。 +所以在不同浏览器都实用的的Javascripit方案: +```js +let height = document.documentElement.clientWidth || document.body.clientWidth; +let width = document.documentElement.clientHeight || document.body.clientHeight; +``` + +## 二、网页正文全文宽高 +`scrollWidth` 和 `scrollHeight` 获取网页内容高度和宽度: + +* 1.针对IE.Opera: +`scrollHeight`是网页内容实际高度,可以小于`clientHeight`; + +* 2.针对NS.firefox: +`scrollHeight`是网页内容高度,不过最小值是`clientHeight`;也就是说网页内容实际高度小于`clientHeight`的时候,`scrollHeight`返回`clientHeight`; + +* 3.浏览器兼容代码: + +```js +let height = document.documentElement.scrollHeight || document.body.scrollHeight; +let width = document.documentElement.scrollWidth || document.body.scrollWidth; +``` + +## 三、网页可见区域宽高,包括滚动条等边线(会随窗口的显示大小改变) +* 1.值: +offsetWidth = scrollWidth + 左右滚动条 + 左右边框; +offsetHeight = scrollHeight + 上下滚动条 + 上下边框; + +* 2.浏览器兼容代码: + +```js +let width = document.documentElement.offsetWidth || document.body.offsetWidth ; +let height = document.documentElement.offsetHeight || document.body.offsetHeight ; +``` + +## 四、网页卷去的距离与偏移量 +1.`scrollLeft`:设置或获取位于给定**对象左边界**与窗口中**目前可见内容的最左端**之间的距离; +2.`scrollTop`:设置或获取位于给定**对象最顶端**与窗口中**目前可见内容的最左端**之间的距离; +3.`offsetLeft`:设置或获取位于给定对象相对于版面或由offsetParent属性指定的父坐标的计算左侧位置; +4.`offsetTop`:设置或获取位于给定对象相对于版面或由offsetParent属性指定的父坐标的计算顶端位置; + +## 常用高度/宽度获取的整理 +* 1.获取屏幕的高度和宽度(屏幕分辨率): + +```js +window.screen.height +window.screen.width +``` + +* 2.获取屏幕工作区域的高度和宽度(去掉状态栏): + +```js +window.screen.availHeight +window.screen.availWidth +``` + +* 3.网页全文的高度和宽度: + +```js +document.body.scrollHeight +document.body.scrollWidth +``` + +* 4.滚动条卷上去的高度和向右卷的宽度: + +```js +document.body.scrollTop +document.body.scrollLeft +``` +* 5.网页可见区域的高度和宽度(不加边线): + +```js +document.body.clientHeight +document.body.clientWidth +``` +* 6.网页可见区域的高度和宽度(加边线): + +```js +document.body.offsetHeight +document.body.offsetWidth +``` \ No newline at end of file diff --git "a/Cute-Article/article/49-Vue \351\235\242\350\257\225\344\270\255\345\270\270\351\227\256\347\237\245\350\257\206\347\202\271\346\225\264\347\220\206.md" "b/Cute-Article/article/49-Vue \351\235\242\350\257\225\344\270\255\345\270\270\351\227\256\347\237\245\350\257\206\347\202\271\346\225\264\347\220\206.md" new file mode 100644 index 00000000..b1166a77 --- /dev/null +++ "b/Cute-Article/article/49-Vue \351\235\242\350\257\225\344\270\255\345\270\270\351\227\256\347\237\245\350\257\206\347\202\271\346\225\264\347\220\206.md" @@ -0,0 +1,208 @@ +[原文](https://mp.weixin.qq.com/s/5tiAmJCLlPTQObMDY2ZgkA) + +看看面试题,只是为了查漏补缺,看看自己那些方面还不懂。切记不要以为背了面试题,就万事大吉了,最好是理解背后的原理,这样面试的时候才能侃侃而谈。不然,稍微有水平的面试官一看就能看出,是否有真才实学还是刚好背中了这道面试题(有空再把例子中代码补上)。 + +## 一、对于MVVM的理解? +MVVM 是 Model-View-ViewModel 的缩写。 + +* **Model** 代表数据模型,也可以在Model中定义数据修改和操作的业务逻辑。 +* **View** 代表UI 组件,它负责将数据模型转化成UI 展现出来。 +* **ViewModel** 监听模型数据的改变和控制视图行为、处理用户交互,简单理解就是一个同步View 和 Model的对象,连接Model和View。 + +在MVVM架构下,View 和 Model 之间并没有直接的联系,而是通过ViewModel进行交互,Model 和 ViewModel 之间的交互是双向的, 因此View 数据的变化会同步到Model中,而Model 数据的变化也会立即反应到View 上。 + +ViewModel 通过双向数据绑定把 View 层和 Model 层连接了起来,而View 和 Model 之间的同步工作完全是自动的,无需人为干涉,因此开发者只需关注业务逻辑,不需要手动操作DOM,不需要关注数据状态的同步问题,复杂的数据状态维护完全由 MVVM 来统一管理。 + +## 二、Vue的生命周期 + +`beforeCreate`(创建前),在数据观测和初始化事件还未开始 + +`created`(创建后),完成数据观测,属性和方法的运算,初始化事件, $el 属性还没有显示出来 + +`beforeMount`(载入前),在挂载开始之前被调用,相关的render函数首次被调用。实例已完成以下的配置:编译模板,把data里面的数据和模板生成html。注意此时还没有挂载html到页面上。 + +`mounted`(载入后),在el 被新创建的 vm.$el 替换,并挂载到实例上去之后调用。实例已完成以下的配置:用上面编译好的html内容替换el属性指向的DOM对象。完成模板中的html渲染到html页面中。此过程中进行ajax交互。 + +`beforeUpdate`(更新前),在数据更新之前调用,发生在虚拟DOM重新渲染和打补丁之前。可以在该钩子中进一步地更改状态,不会触发附加的重渲染过程。 + +`updated`(更新后),在由于数据更改导致的虚拟DOM重新渲染和打补丁之后调用。调用时,组件DOM已经更新,所以可以执行依赖于DOM的操作。然而在大多数情况下,应该避免在此期间更改状态,因为这可能会导致更新无限循环。该钩子在服务器端渲染期间不被调用。 + +`beforeDestroy`(销毁前),在实例销毁之前调用。实例仍然完全可用。 + +`destroyed`(销毁后),在实例销毁之后调用。调用后,所有的事件监听器会被移除,所有的子实例也会被销毁。该钩子在服务器端渲染期间不被调用。 + +### 1、什么是vue生命周期? + +答: Vue 实例从创建到销毁的过程,就是生命周期。从`开始创建`、`初始化数据`、`编译模板`、`挂载Dom→渲染`、`更新→渲染`、`销毁`等一系列过程,称之为 Vue 的**生命周期**。 + +### 2、vue生命周期的作用是什么? + +答:它的生命周期中有多个事件钩子,让我们在控制整个Vue实例的过程时更容易形成好的逻辑。 + +### 3、vue生命周期总共有几个阶段? + +答:它可以总共分为8个阶段:创建前/后、载入前/后、更新前/后、销毁前/销毁后。 + +### 4、第一次页面加载会触发哪几个钩子? + +答:会触发下面这几个`beforeCreate`、`created`、`beforeMount`、`mounted` 。 + +### 5、DOM 渲染在哪个周期中就已经完成? + +答:DOM 渲染在 `mounted` 中就已经完成了。 + +## 三、 Vue实现数据双向绑定的原理:Object.defineProperty() + +vue实现数据双向绑定主要是: +采用**数据劫持**结合**发布者-订阅者模式**的方式,通过 `Object.defineProperty()` 来劫持各个属性的`setter`,`getter`,在数据变动时发布消息给订阅者,触发相应监听回调。当把一个普通 Javascript 对象传给 Vue 实例来作为它的 data 选项时,Vue 将遍历它的属性,用 `Object.defineProperty()` 将它们转为 `getter/setter`。用户看不到 `getter/sette`r,但是在内部它们让 Vue 追踪依赖,在属性被访问和修改时通知变化。 + +vue的数据双向绑定: +将MVVM作为数据绑定的入口,整合`Observer`,`Compile`和`Watcher`三者,通过`Observer`来监听自己的`model`的数据变化,通过`Compile`来解析编译模板指令(vue中是用来解析` {{}}`),最终利用`watcher`搭起`observer`和`Compile`之间的通信桥梁,达到**数据变化 —>视图更新**;视图交互变化(input)—>数据model变更双向绑定效果。 + +js实现简单的双向绑定: +```html + +   
+    +   

+
+ + + +``` + + +## 四、Vue组件间的参数传递 + +### 1、父组件与子组件传值 +父组件传给子组件:子组件通过`props`方法接受数据; + +子组件传给父组件: `$emit` 方法传递参数 + +### 2、非父子组件间的数据传递,兄弟组件传值 +`eventBus`,就是创建一个事件中心,相当于中转站,可以用它来传递事件和接收事件。项目比较小时,用这个比较合适(虽然也有不少人推荐直接用VUEX,具体来说看需求咯。技术只是手段,目的达到才是王道)。 + +## 五、Vue的路由实现:hash模式 和 history模式 +**hash模式**:在浏览器中符号 `#`,#以及#后面的字符称之为`hash`,用 `window.location.hash` 读取。 +特点:`hash`虽然在URL中,但不被包括在HTTP请求中;用来指导浏览器动作,对服务端安全无用,`hash`不会重加载页面。 + +**history模式**:`history`采用HTML5的新特性;且提供了两个新方法: `pushState()`, `replaceState()`可以对浏览器历史记录栈进行修改,以及`popState`事件的监听到状态变更。 + +## 六、Vue与Angular以及React的区别? +版本在不断更新,以下的区别有可能不是很正确。我工作中只用到vue,对angular和react不怎么熟。 + +### 1、与AngularJS的区别 +#### 相同点: +* 都支持指令:内置指令和自定义指令; +* 都支持过滤器:内置过滤器和自定义过滤器; +* 都支持双向数据绑定; +* 都不支持低端浏览器。 + +#### 不同点: +* AngularJS的学习成本高,比如增加了Dependency Injection特性,而Vue.js本身提供的API都比较简单、直观; +* 在性能上,AngularJS依赖对数据做脏检查,所以Watcher越多越慢; +* Vue.js使用基于依赖追踪的观察并且使用异步队列更新,所有的数据都是独立触发的。 + +### 2、与React的区别 +#### 相同点: +* React采用特殊的`JSX`语法,Vue.js在组件开发中也推崇编写`.vue`特殊文件格式,对文件内容都有一些约定,两者都需要编译后使用; +* 中心思想相同:一切都是组件,组件实例之间可以嵌套;都提供合理的钩子函数,可以让开发者定制化地去处理需求; +* 都不内置列数AJAX,Route等功能到核心包,而是以插件的方式加载; +* 在组件开发中都支持`mixins`的特性。 + +#### 不同点: +* React采用的Virtual DOM会对渲染出来的结果做脏检查; +* Vue.js在模板中提供了指令,过滤器等,可以非常方便,快捷地操作Virtual DOM。 + +## 七、vue路由的钩子函数 +首页可以控制导航跳转,`beforeEach`,`afterEach`等,一般用于页面`title`的修改。一些需要登录才能调整页面的重定向功能。 + +**beforeEach**主要有3个参数`to`,`from`,`next`。 +**`to`**:route即将进入的目标路由对象。 +**`from`**:route当前导航正要离开的路由。 +**`next`**:function一定要调用该方法`resolve`这个钩子。执行效果依赖`next`方法的调用参数。可以控制网页的跳转。 + +## 八、vuex是什么?怎么使用?哪种功能场景使用它? +只用来读取的状态集中放在`store`中; +改变状态的方式是提交`mutations`,这是个同步的事物; +异步逻辑应该封装在`action`中。 + +在`main.js`引入`store`,注入。新建了一个目录`store`,`… export` 。 + +场景有:单页应用中,组件之间的状态、音乐播放、登录状态、加入购物车。 +![vue生命周期](http://p3nqtyvgo.bkt.clouddn.com/vue%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F.jpg) + +**state**:Vuex 使用单一状态树,即每个应用将仅仅包含一个`store` 实例,但单一状态树和模块化并不冲突。存放的数据状态,不可以直接修改里面的数据。 + +**mutations**:`mutations`定义的方法动态修改`Vuex `的 `store` 中的状态或数据。 + +**getters**:类似`vue`的计算属性,主要用来过滤一些数据。 + +**action**:`actions`可以理解为通过将`mutations`里面处里数据的方法变成可异步的处理数据的方法,简单的说就是异步操作数据。`view` 层通过` store.dispath` 来分发 `action`。 +```js +onst store = new Vuex.Store({ //store实例 +     state: { +        count: 0 +            }, +     mutations: {                 +        increment (state) { +         state.count++ +        } +         }, +     actions: { +        increment (context) { +         context.commit('increment') +   } + } +}) + +``` +**modules**:项目特别复杂的时候,可以让每一个模块拥有自己的`state`、`mutation`、`action`、`getters`,使得结构非常清晰,方便管理。 +```js +const moduleA = { + state: { ... }, + mutations: { ... }, + actions: { ... }, + getters: { ... } +} +const moduleB = { + state: { ... }, + mutations: { ... }, + actions: { ... } +} +const store = new Vuex.Store({ + modules: { +   a: moduleA, +   b: moduleB +}) + +``` + + +## 九、其它小知识点 + +### 1、css只在当前组件起作用 +答:在`style`标签中写入`scoped`即可 例如: `` + +### 2、`v-if` 和 `v-show` 区别 +答:`v-if`按照条件是否渲染,`v-show`是`display`的`block`或`none`; + +### 3、`$route`和`$router` 区别 + +答:`$route`是 `路由信息对象`,包括`path`,`params`,`hash`,`query`,`fullPath`,`matched`,`name`等路由信息参数。而`$router`是 `路由实例` 对象包括了路由的跳转方法,钩子函数等。 + +PS:缺少的案例代码,这几天再补上去。有些地方可能描述的不够清楚,如果有歧义,可能是我理解错了。 \ No newline at end of file diff --git "a/5-[\345\216\237\345\210\233]VUE\344\270\255\345\256\236\347\216\260\351\200\232\347\224\250js\345\207\275\346\225\260\345\272\223\345\260\201\350\243\205.md" "b/Cute-Article/article/5-[\345\216\237\345\210\233]VUE\344\270\255\345\256\236\347\216\260\351\200\232\347\224\250js\345\207\275\346\225\260\345\272\223\345\260\201\350\243\205.md" similarity index 100% rename from "5-[\345\216\237\345\210\233]VUE\344\270\255\345\256\236\347\216\260\351\200\232\347\224\250js\345\207\275\346\225\260\345\272\223\345\260\201\350\243\205.md" rename to "Cute-Article/article/5-[\345\216\237\345\210\233]VUE\344\270\255\345\256\236\347\216\260\351\200\232\347\224\250js\345\207\275\346\225\260\345\272\223\345\260\201\350\243\205.md" diff --git "a/Cute-Article/article/50-js\344\270\255get\345\222\214post\347\232\204\345\214\272\345\210\253.md" "b/Cute-Article/article/50-js\344\270\255get\345\222\214post\347\232\204\345\214\272\345\210\253.md" new file mode 100644 index 00000000..f6027312 --- /dev/null +++ "b/Cute-Article/article/50-js\344\270\255get\345\222\214post\347\232\204\345\214\272\345\210\253.md" @@ -0,0 +1,33 @@ +在常见的客户端传递参数的方式有`GET`和`POST`两种: +* **浏览器地址栏直接输入**:一定是`GET`请求; +* **超链接**:一定是`GET`请求; +* **表单**:可以是`GET`,也可以是`POST`,这取决与`
`的`method`属性值; + +### 两者区别: +#### 1.效率 +* `GET`的意思是 `得` ,从服务器获取数据(也可以上传数据,参数就是),效率较高; +* `POST`的意思是 `给` ,但可以向服务器发送数据和下载数据,效率不如`GET`; + +#### 2.缓存 +* `GET` 请求能够被缓存,默认的请求方式也是有缓存的; +* `POST` 请求默认不会缓存; + +> * 缓存是针对`URL`来进行缓存的,`GET`请求由于其参数是直接加在`URL`上的,一种参数组合就有一种URL的缓存,可以根据参数来进行一一对应,重复请求是幂等的(不论请求多少次,结果都一样); +> * 而POST请求的URL没有参数,每次请求的URL都相同,数据体(HTTPBody)可能不同,无法一一对应,所以缓存没有意义; + +#### 3.安全性 +* `GET` 的所有参数全部包装在`URL`中,明文显示,且服务器的访问日志会记录,非常不安全; +* `POST` 的`URL`中只有资源路径,不包含参数,参数封装在二进制的数据体中,服务器也不会记录参数,相对安全。所有涉及用户隐私的数据都要用 `POST` 传输; + +> `POST`的安全是**相对的**,对于普通用户来说他们看不到明文,数据封装对他们来说就是屏障。但是对于专业人士,它们会抓包会分析,没有加密的数据包对他们来说也是小case。所以POST仅仅是相对安全,唯有对数据进行加密才会更安全。当然加密也有被破解的可能性,**理论上所有的加密方式都可以破解**,只是时间长短的问题。而加密算法要做的就是使得破解需要的时间尽量长,越长越安全。由于我们也需要解密,加密算法太过复杂也并非好事,这就要结合使用情况进行折中或者足够实际使用即可。绕的有点远,具体的话,我将在后续的文章之中介提及,并介绍一些常用的加密算法。 + +#### 4.数据量 +`HTTP`协议中均没有对`GET`和`POST`请求的**数据大小**进行限制,但是实际应用中它们通常受限于软硬件平台的设计和性能。 +* `GET`:不同的浏览器和服务器不同,一般限制在`2~8K`之间,更加常见的是`1k`以内; +* `POST`:提交的数据较大,大小靠服务器的设定值限制,PHP默认是`2M`(具体的话大家以后看后端给的开发文档就行了); + +#### 5.数据获取方式 +* `GET` 需要使用` Request.QueryString `来取得变量的值; +* `POST` 通过 `Request.Form` 来获取变量的值; + +也就是说 `Get` 是通过**地址栏**来传值,而 `Post` 是通过**提交表单**来传值。 \ No newline at end of file diff --git "a/Cute-Article/article/51-Apache\344\271\213HTTP\345\215\217\350\256\256.md" "b/Cute-Article/article/51-Apache\344\271\213HTTP\345\215\217\350\256\256.md" new file mode 100644 index 00000000..87e79f96 --- /dev/null +++ "b/Cute-Article/article/51-Apache\344\271\213HTTP\345\215\217\350\256\256.md" @@ -0,0 +1,480 @@ +**`HTTP`(Hypertext Transfer Protocol)超文本传输协议**。是一种详细规定了客户端浏览器和万维网服务器之间相互通讯的规则,通过因特网传送万维网文档的数据传送协议。 + +## 一、HTTP的前世今生 +**超文本传输协议**的前身是Xanadu项目,超文本的概念是泰德.纳尔森在1960年提出的。而HTTP在1989年诞生在CERN(欧洲量子物理实验室)。1990年12月,超文本协议在CERN首次上线。1991年夏天,继Telnet等协议之后,超文本传输协议正式成为互联网诸多协议的一份子。 + +**HTTP诞生的原因**:为了实现从一台计算机上获取并显示存放在多台计算机里的文件、数据、图片和其他类型的文件而诞生HTTP协议。因为在当时其他诸多已经诞生的协议解决不了这个问题。例如:Telnet、SMTP、POP3、IAMP4、FTP等。所以HTTP协议应运而生。 + +## 二、HTTP协议的版本 +### HTTP 0.9 +**HTTP 0.9**作为HTTP协议的第一个成熟版本。此版本功能非常薄弱。 +* 1.请求只有一行 +* 2.没有HTTP头部信息和错误代码信息 +* 3.只能接收一种类型的数据:纯文本 +* 4.只有一种方法:GET + +### HTTP 1.0 +随着互联网的发展,**HTTP 0.9**已经不能满足互联网发展的需求。因此**HTTP 1.0**就这样诞生了。**HTTP 1.0**的最大改变是引入了`POST`方法,使得客户端通过`HTML表单`向服务器发送数据成为可能。从而实现了客户端和服务器端的数据交互。这是WEB应用程序的一个基础。另一个巨大的改变是引入了`HTTP头`,使得HTTP协议不仅能返回错误代码,并且借助于MIME技术能够传输更为丰富的文件类型,不再局限于纯文本。还可以是图片、动画等其他文件格式。 + +除此之外,还允许保持连接,即一次TCP连接,可以实现多次通讯。`HTTP 1.0`默认是**传输一次数据后就关闭连接**。 + +### HTTP 1.1 +2000年5月,`HTTP 1.1`诞生。`HTTP 1.1`并不像`HTTP 1.0`对`HTTP 0.9`那样的革命性。但是对`HTTP 1.0`做了很多功能性的增强。 +* 1.增加了`Host头` + 使得GET后面只需使用相对路径; + 使得一台主机可以使用多个域名; +* 2.引入了`Range头 + 使得客户端通过`HTTP`下载时只下载内容的一部分,使得多线程下载成为可能; + +## 三、HTTP协议的特点 +* **1.支持C/S模式** + 支持基本认证和安全认证。 + +* **2.简单快速** + 客户端向服务器请求服务时,只需传送请求方法和路径。请求方法常用的有`GET`、`HEAD`、`POST`。每种方法规定了客户端与服务器端联系的不同类型。由于`HTTP协议`简单,使得HTTP服务器的程序规模小,因而通讯速度快。 + +* **3.灵活** + HTTP协议允许传输任意类型的数据对象。正在传输的类型由Content-Type加以标记。 + +* **4.HTTP 0.9和HTTP 1.0使用非持续连接** + 限制每次连接只处理一个请求,服务器处理完客户端的请求,并收到客户端的应答后,即断开连接。采用这种方式可以节省传输时间。 + HTTP 1.1使用持续连接:不必为每个Web对象创建一个新的连接,一个连接可以传送多个对象。 + +* **5.无状态:** + HTTP协议是无状态协议。 + 无状态是指协议对于事物处理没有记忆能力。缺少状态意味着如果后续处理需要前面的信息,则它必须重传,这样可能导致每次连接传送的数据量增大。 + +## 四、HTTP实现原理 +**在TCP/IP协议栈中的位置**: + HTTP协议通常承载于TCP协议之上,有时也承载于TLS或SSL协议层之上,这个时候,就成了我们常说的HTTPS。如下图所示: +![HTTPS](http://s3.51cto.com/wyfs02/M02/38/15/wKioL1OybmbgvkXfAABJlqiTUPM914.jpg) + + 默认HTTP的端口号为80,HTTPS的端口号为443。 + +**HTTP的请求响应模型**: + HTTP协议永远都是客户端发起请求,服务器回送响应。如下图所示: +![发起请求](http://s3.51cto.com/wyfs02/M00/38/15/wKiom1OybujR2rlWAABZ59w_LlM241.jpg) + + + 这样就限制了使用HTTP协议,无法实现在客户端没有发起请求的时候,服务器将消息推送给客户端。 + + HTTP协议是一个无状态的协议,同一个客户端的这次请求和上次请求是没有对应关系。 + +**工作流程:** +一次HTTP操作称为一个事务,其工作过程可分为四步: +* 1.首先客户机与服务器需要**建立连接**。只要单击某个超级链接,HTTP的工作开始。 + +* 2.建立连接后,客户机发送一个请求给服务器,请求方式的格式为:**统一资源标识符(URL)、协议版本号**,后边是MIME信息包括请求修饰符、客户机信息和可能的内容。 + +* 3.服务器接到请求后,给予相应的响应信息,其格式为一个状态行,包括**信息的协议版本号**、**一个成功或错误的代码**,后边是MIME信息包括服务器信息、实体信息和可能的内容。 + +* 4.客户端接收服务器所返回的信息通过浏览器显示在用户的显示屏上,然后客户机与服务器**断开连接**。 + +如果在以上过程中的某一步出现错误,那么产生错误的信息将返回到客户端,有显示屏输出。对于用户来说,这些过程是由HTTP自己完成的,用户只要用鼠标点击,等待信息显示就可以了。 + +**简化版**: +* 1.建立连接 +* 2.接收请求 +* 3.处理请求 +* 4.获取资源 +* 5.构建响应 +* 6.回送响应 +* 7.记录日志 + +## 五、HTTP的几个重要概念 +### URI、URL、URN: +* **URI**: Uniform Resource Identifier,统一资源标识符; +* **URL**: Uniform Resource Locator,统一资源定位符; +* **URN**: Uniform Resource Name,统一资源命名符; + +其中,URL,URN是URI的**子集**。 + +Web上地址的**基本形式**是URI,它代表**通用资源标识符**。有两种形式: +* **URL**:目前URI的最普遍形式就是无处不在的URL或统一资源定位器。 +* **URN**:URL的一种更新形式,统一资源名称(URN, Uniform Resource Name)不依赖于位置,并且有可能减少失效连接的个数。但是其流行还需假以时日,因为它需要更精密软件的支持。 + +**URI格式**: +``` +scheme://[username:password@]HOST:port/path/to/source +``` + +### 连接(Connection): +一个传输层的实际环流,它是建立在**两个相互通讯的应用程序之间**。 + +在**http1.1**,`request`和`reponse`头中都有可能出现一个`connection`的头,此`header`的含义是当`client`和`serve`r通信时对于长链接如何进行处理。 + +在**http1.1**中,`client`和`server`都是默认**对方支持长链接**的, 如果`client`使用**http1.1协议**,但又不希望使用长链接,则需要在`header`中指明`connection`的值为`close`;如果`server`方也不想支持长链接,则在`response`中也需要明确说明`connection`的值为`close`。不论`request`还是`response`的`header`中包含了值为`close`的`connection`,都表明当前正在使用的tcp链接在**当天请求处理完毕后会被断掉**。以后client再进行新的请求时就必须**创建新的tcp链接**了。 + +### 消息(Message): + HTTP通讯的基本单位,包括一个结构化的八元组序列并通过连接传输。 + +### 请求(Request): + 一个从客户端到服务器的请求信息包括**应用于资源的方法**、**资源的标识符**和**协议的版本号**。 + +### 响应(Response): + 一个从服务器返回的信息包括**HTTP协议的版本号**、**请求的状态**(例如“成功”或“没找到”)和**文档的MIME类型**。 + +### 资源(Resource): + 由**URI**标识的网络数据对象或服务。 + +### 实体(Entity): + 数据资源或来自服务资源的回映的一种特殊表示方法,它可能被包围在一个请求或响应信息中。一个实体包括**实体头信息**和**实体的本身内容**。 + +### 客户机(Client): + 一个为发送请求目的而建立连接的应用程序。 + +### 用户代理(UserAgent): + 初始化一个请求的客户机。它们是浏览器、编辑器或其它用户工具。 + +### 服务器(Server): + 一个接受连接并对请求返回信息的应用程序。 + +### 源服务器(OriginServer): + 是一个给定资源可以在其上驻留或被创建的服务器。 + +### 代理(Proxy): + 一个中间程序,它可以充当一个**服务器**,也可以充当一个**客户机**,为其它客户机建立请求。请求是通过可能的翻译在内部或经过传递到其它的服务器中。一个代理在发送请求信息之前,必须解释并且如果可能重写它。 + + 代理经常作为通过防火墙的客户机端的门户,代理还可以作为一个帮助应用来通过协议处理没有被用户代理完成的请求。 + +### 网关(Gateway): + 一个作为**其它服务器中间媒介的服务器**。与代理不同的是,网关接受请求就好象对被请求的资源来说它就是源服务器;发出请求的客户机并没有意识到它在同网关打交道。 + + 网关经常作为通过防火墙的服务器端的门户,网关还可以作为一个协议翻译器以便存取那些存储在非HTTP系统中的资源。 + +### 通道(Tunnel): + 是作为两个连接中继的中介程序。一旦激活,通道便被认为不属于HTTP通讯,尽管通道可能是被一个HTTP请求初始化的。当被中继的连接两端关闭时,通道便消失。当一个门户(Portal)必须存在或中介(Intermediary)不能解释中继的通讯时通道被经常使用。 + +### 缓存(Cache): + 反应信息的局域存储。 + +## 六、HTTP协议头 +HTTP头按照其不同的作用,分为四类: + +### 1.通用头(General Header) + 通用头**即可以包含在HTTP请求中,也可以包含在HTTP响应中**。 + 通用头的作用是描叙HTTP协议本身。比如**描叙HTTP是否持续连接**的`Connection头`,**HTTP发送日期**的`Date头`,**描述HTTP所在的TCP连接时间**的`Keep-Alive头`,用于**缓存控制**的`Cache-Control头`等。 + +### 2.实体头(Entity Header) + 实体头是那些**描叙HTTP信息的头**。即可出现在HTTP POST方法的请求中,也可以出现在HTTP响应中。 + 例如`Content-Type`和`Content-length`都是描述实体的类型和大小的头都属于实体头。其它还有用于描述实体的`Content-Language`,`Content-MD5`,`Content-Encoding`以及控制实体缓存的`Expires`和`Last-Modifies头`等。 + +**常见的实体头**如下: +> * **Allow**:服务器支持哪些请求方法(如GET、POST等); +> * **Content-Encoding**:文档的编码(Encode)方法,例如:gzip,见“2.5 响应头”; +> * **Content-Language**:内容的语言类型,例如:zh-cn; +> * **Content-Length**:表示内容长度,eg:80,可参考“2.5响应头”; +> * **Content-Location**:表示客户应当到哪里去提取文档; +> * **Content-MD5**:MD5 实体的一种MD5摘要,用作校验和。发送方和接受方都计算MD5摘要,接受方将其计算的值与此头标中传递的值进行比较。Eg1:Content-MD5: 。`Eg2:dfdfdfdfdfdfdff==`; +> * **Content-Range**:随部分实体一同发送;标明被插入字节的低位与高位字节偏移,也标明此实体的总长度。Eg1:`Content-Range: 1001-2000/5000,eg2:bytes 2543-4532/7898`; +> * **Content-Type**:标明发送或者接收的实体的MIME类型。Eg:`text/html`; `charset=GB2312`主类型/子类型; +> * **Expires**:为0证明不缓存; +> * **Last-Modified**:WEB 服务器认为对象的最后修改时间,比如文件的最后修改时间,动态页面的最后产生时间等等。例如:`Last-Modified:Tue, 06 May 2008 02:42:43 GMT`; + +### 3.请求头(HTTP Request Header) + 请求头是那些由客户端发往服务器端以便帮助服务器端更好的满足客户端请求的头。 + 请求头只能出现在HTTP请求中。比如告诉服务器**只接收某种响应内容**的`Accept头`,**发送Cookies**的`Cookie头`,**显示请求主机域**的`HOST头`,**用于缓存**的`If-Match`,`If-Match-Since`,`If-None-Match头`,**用于只取HTTP响应信息中部分信息**的`Range头`,**用于附属HTML相关请求引用**的`Referee头`等。 + +**常见请求头如下:** + +> * **Accept**:浏览器可接受的MIME类型; +> * **Accept-Charse**t:浏览器可接受的字符集; +> * **Accept-Encoding**:浏览器能够进行解码的数据编码方式,比如gzip。Servlet能够向支持gzip的浏览器返回经gzip编码的HTML页面。许多情形下这可以减少5到10倍的下载时间; +> * **Accept-Language**:浏览器所希望的语言种类,当服务器能够提供一种以上的语言版本时要用到; +> * **Authorization**:授权信息,通常出现在对服务器发送的WWW-Authenticate头的应答中; +> * **Connection**:表示是否需要持久连接。如果Servlet看到这里的值为“Keep-Alive” ,或者看到请求使用的是HTTP 1.1(HTTP 1.1默认进行持久连接),它就可以利用持久连接的优点,当页面包含多个元素时(例如Applet,图片),显著地减少下载所需要的时间。要实现这一点,Servlet需要在应答中发送一个Content-Length头,最简单的实现方法是:先把内容写入ByteArrayOutputStream,然后在正式写出内容之前计算它的大小; +> * **Content-Length**:表示请求消息正文的长度; +> * **Cookie**:这是最重要的请求头信息之一; +> * **From**:请求发送者的email地址,由一些特殊的Web客户程序使用,浏览器不会用到它; +> * **Host**:初始URL中的主机和端口; +> * **If-Modified-Since**:只有当所请求的内容在指定的日期之后又经过修改才返回它,否则返回304“Not Modified”应答; +> * **Pragma**:指定“no-cache”值表示服务器必须返回一个刷新后的文档,即使它是代理服务器而且已经有了页面的本地拷贝; +> * **Referer**:包含一个URL,用户从该URL代表的页面出发访问当前请求的页面。 +> * **User-Agent**:浏览器类型,如果Servlet返回的内容与浏览器类型有关则该值非常有用; +> * **UA-Pixels,UA-Color,UA-OS,UA-CPU**:由某些版本的IE浏览器所发送的非标准的请求头,表示屏幕大小、颜色深度、操作系统和CPU类型。 + +### 4.响应头(HTTP Response Header) +HTTP响应头是那些描述HTTP响应本身的头,这里并不包含描述HTTP响应中第三部分也就是HTTP信息的头(这部分由实体头负责)。 +比如说定时刷新的Refresh头,当遇到503错误时自动重试的Retry-After头,显示服务器信息的Server头,设置COOKIE的Set-Cookie头,告诉客户端可以部分请求的Accept-Ranges头等。 + +**常见响应头如下:** + +> * **Allow**服务器支持哪些请求方法(如`GET`、`POST`等); +> * **Content-Encoding**文档的编码(Encode)方法。 + 只有在解码之后才可以得到` Content-Type头`指定的内容类型。利用`gzip压缩文档`能够显著地减少HTML文档的下载时间。Java的`GZIPOutputStream`可以很方便地进行`gzip压缩`,但只有`Unix`上的`Netscape`和`Windows`上的`IE 4`、`IE 5`才支持它。因此,`Servlet`应该通过查看`Accept-Encoding头`(即`request.getHeader`("`Accept-Encoding`"))检查浏览器是否支持`gzip`,为支持`gzip`的浏览器返回经`gzip`压缩的HTML页面,为其他浏览器返回普通页面; +> * **Content-Length**表示内容长度。只有当浏览器使用持久HTTP连接时才需要这个数据。如果你想要利用持久连接的优势,可以把输出文档写入`ByteArrayOutputStram`,完成后查看其大小,然后把该值放入`Content-Length头`,最后通过`byteArrayStream.writeTo(response.getOutputStream())`发送内容; +> * **Content-Type** 表示后面的文档属于什么`MIME类`型。`Servlet`默认为`text/plain`,但通常需要显式地指定为`text/html`。由于经常要设置`Content-Type`,因此`HttpServletResponse`提供了一个专用的方法`setContentTyep`。 可在web.xml文件中配置扩展名和MIME类型的对应关系; +> * **Date**当前的GMT时间。你可以用`setDateHeader`来设置这个头以避免转换时间格式 的麻烦; +> * **Server**服务器软件名称及版本。 +> * **Age**响应给客户端的文档可以缓存多长时间 +> * **Public** +> * **Vary** +> * **Set-Cookie** +> * **Set-Cookie2** +> * **Expires**指明应该在什么时候认为文档已经过期,从而不再缓存它。 +> * **Last-Modified**文档的最后改动时间。客户可以通过`If-Modified-Since`请求头提供一个日期,该请求将被视为一个条件`GET`,只有改动时间迟于指定时间的文档才会返回,否则返回一个**304**(`Not Modified`)状态。`Last-Modified`也可用`setDateHeader`方法来设置; +> * **Location**表示客户应当到哪里去提取文档。 + `Location`通常不是直接设置的,而是通过`HttpServletResponse`的`sendRedirect`方法,该方法同时设置状态代码为**302**; +> * **Refresh**表示浏览器应该在多少时间之后刷新文档,以秒计。 + 除了刷新当前文档之外,你还可以通过`setHeader("Refresh", "5; URL=http://host/path")`让浏览器读取指定的页面。注意这种功能通常是通过设置HTML页面HEAD区的``实现,这是因为,自动刷新或重定向对于那些不能使用CGI或`Servle`t的HTML编写者十分重要。但是,对于`Servlet`来说,直接设置`Refresh头`更加方便。注意`Refres`h的意义是“**N秒之后刷新本页面或访问指定页面**”,而不是“**每隔N秒刷新本页面或访问指定页面**”。因此,连续刷新要求每次都发送一个`Refresh头`,而发送**204**状态代码则可以阻止浏览器继续刷新,不管是使用`Refresh头`还是``。注意`Refresh头`不属于**HTTP 1.1正式规范**的一部分,而是一个扩展,但`Netscape`和`IE`都支持它。 + + +## 七、HTTP请求 +HTTP请求由三部分组成,分别是:**请求行**、**消息报头**、**请求正文** +请求行以一个方法符开头,以空格分开,后面跟着请求的URI和协议的版本,格式如下: +``` +Method Request-URI HTTP-Version CRLF +``` +* **Method** 表示请求方法; +* **Request-URI** 是一个统一资源标识符,也就是资源路径; +* **HTTP-Version** 表示HTTP协议版本; +* **CRLF** 表示回车和换行。 + +**请求方法:** +> * **GET** 请求获取Request-URI所标识的资源 +> * **POST** 在Request-URI所标识的资源后附加新的数据,常用于提交表单 +> * **HEAD** 请求获取由Request-URI所标识的资源的响应消息报头 +> * **PUT** 请求服务器存储一个资源,并用Request-URI作为其标识 +> * **DELETE** 请求服务器删除Request-URI所标识的资源 +> * **TRACE** 请求服务器回送收到的请求信息,主要用于测试或诊断 + 可以追踪一次请求中间所经过的代理服务器有哪些 +> * **CONNECT** 保留将来使用 +> * **OPTIONS** 请求查询服务器的性能,或者查询与资源相关的选项和需求 + 可以用来获取服务器端资源支持的方法 + + +## 八、HTTP响应 +在接收和解释请求消息后,服务器返回一个HTTP响应消息。 + +HTTP响应也是由三个部分组成,分别是:**状态行**、**消息报头**、**响应正文** + +**状态行格式如下**: +``` +HTTP-Version Status-Code Reason-Phrase CRLF +``` +> * **HTTP-Version** 表示服务器HTTP协议的版本; +> * **Status-Code** 表示服务器发回的响应状态代码; +> * **Reason-Phrase** 表示状态代码的文本描述。 + +状态代码有**三位数字**组成,第一个数字定义了响应的类别,且有五种可能取值: +> * **1xx**:指示信息--表示请求已接收,继续处理 +> * **2xx**:成功--表示请求已被成功接收、理解、接受 +> * **3xx**:重定向--信息不完整需要进一步补充 +> * **4xx**:客户端错误--请求有语法错误或请求无法实现 +> * **5xx**:服务器端错误--服务器未能实现合法的请求 + +**常见http响应状态码**: +#### 请求收到,继续处理: +> * **100**:客户端必须继续发出请求 +> * **101**:客户端要求服务器根据请求转换HTTP协议版本 + +#### 操作成功收到,分析,接受: +> * **200**:交易成功 +> * **201**:提示知道新文件的URL +> * **202**:接受和处理、但处理未完成 +> * **203**:返回信息不确定或不完整 +> * **204**:请求收到,但返回信息为空 +> * **205**:服务器完成了请求,用户代理必须复位当前已经浏览过的文件 +> * **206**:服务器已经完成了部分用户的GET请求 + +#### 重定向: +> * **300**:请求的资源可在多处得到 +> * **301**:永久重定向,在Location响应首部的值仍为当前URL(隐式重定向) +> * **302**:临时重定向,在Location响应首部的值仍为新的URL(显示重定向) +> * **303**:建议客户端访问其他URL或访问方式 +> * **304**:Not Modified 请求的资源没有改变 可以继续使用缓存 +> * **305**:请求的资源必须从服务器指定的地址得到 +> * **306**:前一版本HTTP中使用的代码,现行版本中不再使用 +> * **307**:声明请求的资源临时性删除 + +#### 客户端错误: +> * **400**:错误请求,如语法错误 +> * **401**:未授权 + **HTTP 401.1** - 未授权,登录失败 + **HTTP 401.2** - 未授权,服务器配置问题导致登录失败 + **HTTP 401.3** - ACL 禁止访问资源 + **HTTP 401.4** - 未授权 授权被筛选器拒绝 + **HTTP 401.5** - 未授权 ISAPI或CGI授权失败 +> * **402**:保留有效ChargeTo头响应 +> * **403**:禁止访问 + **HTTP 403.1** - 禁止访问 禁止可执行访问 + **HTTP 403.2** - 禁止访问 禁止读访问 + **HTTP 403.3** - 禁止访问 禁止写访问 + **HTTP 403.4** - 禁止访问 要求SSL + **HTTP 403.5** - 禁止访问 要求SSL 128 + **HTTP 403.6** - 禁止访问 IP地址被拒绝 + **HTTP 403.7** - 禁止访问 要求客户端证书 + **HTTP 403.8** - 禁止访问 禁止站点访问 + **HTTP 403.9** - 禁止访问 连接的用户过多 + **HTTP 403.10** - 禁止访问 配置无效 + **HTTP 403.11** - 禁止访问 密码更改 + **HTTP 403.12** - 禁止访问 映射器拒绝访问 + **HTTP 403.13** - 禁止访问 客户端证书已被吊销 + **HTTP 403.15** - 禁止访问 客户端访问许可过多 + **HTTP 403.16** - 禁止访问 客户端证书不可信或者无效 + **HTTP 403.17** - 禁止访问 客户端证书已经到期或者尚未生效 +> * **404**:没有发现文件、查询或URL +> * **405**:用户在Request-Line字段定义的方法不允许 +> * **406**:根据用户发送的Accept拖,请求资源不可访问 +> * **407**:类似401,用户必须首先在代理服务器上得到授权 +> * **408**:客户端没有在用户指定的饿时间内完成请求 +> * **409**:对当前资源状态,请求不能完成 +> * **410**:服务器上不再有此资源且无进一步的参考地址 +> * **411**:服务器拒绝用户定义的Content-Length属性请求 +> * **412**:一个或多个请求头字段在当前请求中错误 +> * **413**:请求的资源大于服务器允许的大小 +> * **414**:请求的资源URL长于服务器允许的长度 +> * **415**:请求资源不支持请求项目格式 +> * **416**:请求中包含Range请求头字段,在当前请求资源范围内没有range指示值, 请求也不包含If-Range请求头字段 +> * **417** 服务器不满足请求Expect头字段指定的期望值,如果是代理服务器,可能是下一级服务器不能满足请求长 + +#### 服务器端错误: +> * **500**部服务器错误 + **HTTP 500.100** - 内部服务器错误 + **HTTP 500-11** 服务器关闭 + **HTTP 500-12** 应用程序重新启动 + **HTTP 500-13** - 服务器太忙 + **HTTP 500-14** - 应用程序无效 + **HTTP 500-15** - 不允许请求 +> * **501**实现 +> * **502**关错误 +> * **503**务不可用 +> * **504**关超时 + +## 九、HTTP状态保持 + HTTP 协议本身是无状态的,这与HTTP协议本来的目的是相符的,客户端只需要简单的向服务器请求下载某些文件,无论是客户端还是服务器都没有必要纪录彼此过去的行为,每一次请求之间都是独立的,好比一个顾客和一个自动售货机或者一个普通的(非会员制)大卖场之间的关系一样。 + + 然而聪明(或者贪心?)的人们很快发现如果能够提供一些按需生成的动态信息会使web变得更加有用,就像给有线电视加上点播功能一样。这种需求一方面迫使HTML逐步添加了表单、脚本、DOM等客户端行为,另一方面在服务器端则出现了CGI规范以响应客户端的动态请求,作为传输载体的HTTP协议也添加了文件上载、 cookie这些特性。其中cookie的作用就是为了解决HTTP协议无状态的缺陷所作出的努力。至于后来出现的session机制则是又一种在客户端与服务器之间保持状态的解决方案。 + + +Cookie和Session都为了用来保存状态信息,都是保存客户端状态的机制,它们都是为了解决HTTP无状态的问题而所做的努力。 + +Session可以用Cookie来实现,也可以用URL回写的机制来实现。用Cookie来实现的Session可以认为是对Cookie更高级的应用。 + + +### Cookie和Session区别 + +* 1. Cookie将状态保存在客户端,Session将状态保存在服务器端; + +* 2. Cookies是服务器在本地机器上存储的小段文本并随每一个请求发送至同一个服务器。Cookie最早在RFC2109中实现,后续RFC2965做了增强。网络服务器用HTTP头向客户端发送cookies,在客户终端,浏览器解析这些cookies并将它们保存为一个本地文件,它会自动将同一服务器的任何请求缚上这些cookies。Session并没有在HTTP的协议中定义; + +* 3. Session是针对每一个用户的,变量的值保存在服务器上,用一个sessionID来区分是哪个用户session变量,这个值是通过用户的浏览器在访问的时候返回给服务器,当客户禁用cookie时,这个值也可能设置为由get来返回给服务器; + +* 4. 就安全性来说:当你访问一个使用session 的站点,同时在自己机子上建立一个cookie,建议在服务器端的SESSION机制更安全些.因为它不会任意读取客户存储的信息。 + + +### Session机制 + +Session机制是一种服务器端的机制,服务器使用一种类似于散列表的结构(也可能就是使用散列表)来保存信息。 + +当程序需要为某个客户端的请求创建一个session的时候,服务器首先检查这个客户端的请求里是否已包含了一个session标识 - 称为 session id,如果已包含一个session id则说明以前已经为此客户端创建过session,服务器就按照session id把这个 session检索出来使用(如果检索不到,可能会新建一个),如果客户端请求不包含session id,则为此客户端创建一个session并且生成一个与此session相关联的session id,session id的值应该是一个既不会重复,又不容易被找到规律以仿造的字符串,这个 session id将被在本次响应中返回给客户端保存。 + + + +### Session的实现方式 +**使用Cookie来实现:** +服务器给每个Session分配一个唯一的JSESSIONID,并通过Cookie发送给客户端。 + +当客户端发起新的请求的时候,将在Cookie头中携带这个JSESSIONID。这样服务器能够找到这个客户端对应的Session。 + +流程如下图所示: +![session](http://s3.51cto.com/wyfs02/M00/38/13/wKiom1OyXoHjWI2aAABsbAEnTIs272.jpg) +**使用URL回显来实现** +URL回写是指服务器在发送给浏览器页面的所有链接中都携带JSESSIONID的参数,这样客户端点击任何一个链接都会把JSESSIONID带会服务器。 + +如果直接在浏览器输入服务端资源的url来请求该资源,那么Session是匹配不到的。 + +Tomcat对Session的实现,是一开始同时使用Cookie和URL回写机制,如果发现客户端支持Cookie,就继续使用Cookie,停止使用URL回写。如果发现Cookie被禁用,就一直使用URL回写。jsp开发处理到Session的时候,对页面中的链接记得使用response.encodeURL()。 + +### 与Cookie相关的HTTP扩展头 +* 1. **Cookie**:客户端将服务器设置的Cookie返回到服务器; +* 2. **Set-Cookie**:服务器向客户端设置Cookie; +* 3. **Cookie2 (RFC2965))**:客户端指示服务器支持Cookie的版本; +* 4. **Set-Cookie2 (RFC2965)**:服务器向客户端设置Cookie。 + +### Cookie的流程 +服务器在响应消息中用Set-Cookie头将Cookie的内容回送给客户端,客户端在新的请求中将相同的内容携带在Cookie头中发送给服务器。从而实现会话的保持。 + + 流程如下图所示: +![cookie](http://s3.51cto.com/wyfs02/M00/38/13/wKioL1OyX1-jc2HfAABcl6JnHeo234.jpg) + +## 十、Web缓存 +### 什么是Web缓存 + WEB缓存(cache)位于Web服务器和客户端之间。 + + 缓存会根据请求保存输出内容的副本,例如html页面,图片,文件,当下一个请求来到的时候:如果是相同的URL,缓存直接使用副本响应访问请求,而不是向源服务器再次发送请求。 + + HTTP协议定义了相关的消息头来使WEB缓存尽可能好的工作。 + + + +### Web缓存的优点 +* 1. **减少相应延迟**:因为请求从缓存服务器(离客户端更近)而不是源服务器被相应,这个过程耗时更少,让web服务器看上去相应更快。 + +* 2. **减少网络带宽消耗**:当副本被重用时会减低客户端的带宽消耗;客户可以节省带宽费用,控制带宽的需求的增长并更易于管理。 + + +### 与缓存相关的HTTP消息头 +> * **Expires**:指示响应内容过期的时间,格林威治时间GMT +> * **Cache-Control**:更细致的控制缓存的内容 +> * **Last-Modified**:响应中资源最后一次修改的时间 +> * **ETag**:响应中资源的校验值,在服务器上某个时段是唯一标识的。 +> * **Date**:服务器的时间 +> * **If-Modified-Since**:客户端存取的该资源最后一次修改的时间,同Last-Modified。 +> * **If-None-Match**:客户端存取的该资源的检验值,同ETag。 + +### 客户端缓存生效的常见流程 +服务器收到请求时,会在200 OK中回送该资源的Last-Modified和ETag头,客户端将该资源保存在cache中,并记录这两个属性。当客户端需要发送相同的请求时,会在请求中携带If-Modified-Since和If-None-Match两个头。两个头的值分别是响应中Last-Modified和ETag头的值。服务器通过这两个头判断本地资源未发生变化,客户端不需要重新下载,返回304响应。 + +常见流程如下图所示: +![缓存](http://s3.51cto.com/wyfs02/M02/38/13/wKioL1OyYpTBYuovAABlmj5y42w797.jpg) +### Web缓存机制 +HTTP/1.1中缓存的目的是为了在很多情况下减少发送请求,同时在许多情况下可以不需要发送完整响应。前者减少了网络回路的数量;HTTP利用一个“过期(expiration)”机制来为此目的。后者减少了网络应用的带宽;HTTP用“验证(validation)”机制来为此目的。 + +**HTTP定义了3种缓存机制**: +> * 1. **Freshness**:允许一个回应消息可以在源服务器不被重新检查,并且可以由服务器和客户端来控制。例如,Expires回应头给了一个文档不可用的时间。Cache-Control中的max-age标识指明了缓存的最长时间; +> * 2. **Validation**:用来检查以一个缓存的回应是否仍然可用。例如,如果一个回应有一个Last-Modified回应头,缓存能够使用If-Modified-Since来判断是否已改变,以便判断根据情况发送请求; +> * 3. **Invalidation**: 在另一个请求通过缓存的时候,常常有一个副作用。例如,如果一个URL关联到一个缓存回应,但是其后跟着POST、PUT和DELETE的请求的话,缓存就会过期。 + +## 十一、HTTP代理服务器 +代理服务器英文全称是Proxy Server,其功能就是代理网络用户去取得网络信息。形象的说:它是网络信息的中转站。 + +代理服务器是介于浏览器和Web服务器之间的一台服务器,有了它之后,浏览器不是直接到Web服务器去取回网页而是向代理服务器发出请求,Request信号会先送到代理服务器,由代理服务器来取回浏览器所需要的信息并传送给你的浏览器。 + +而且,大部分代理服务器都具有缓冲的功能,就好象一个大的Cache,它有很大的存储空间,它不断将新取得数据储存到它本机的存储器上,如果浏览器所请求的数据在它本机的存储器上已经存在而且是最新的,那么它就不重新从Web服务器取数据,而直接将存储器上的数据传送给用户的浏览器,这样就能显著提高浏览速度和效率。 + +更重要的是:Proxy Server(代理服务器)是Internet链路级网关所提供的一种重要的安全功能,它的工作主要在开放系统互联(OSI)模型的对话层。 + +### HTTP代理服务器的主要功能 + +* 1. 突破自身IP访问限制,访问国外站点。如:教育网、169网等网络用户可以通过代理访问国外网站; + +* 2. 访问一些单位或团体内部资源,如某大学FTP(前提是该代理地址在该资源的允许访问范围之内),使用教育网内地址段免费代理服务器,就可以用于对教育 网开放的各类FTP下载上传,以及各类资料查询共享等服务; + +* 3. 突破中国电信的IP封锁:中国电信用户有很多网站是被限制访问的,这种限制是人为的,不同Serve对地址的封锁是不同的。所以不能访问时可以换一个国 外的代理服务器试试; + +* 4. 提高访问速度:通常代理服务器都设置一个较大的硬盘缓冲区,当有外界的信息通过时,同时也将其保存到缓冲区中,当其他用户再访问相同的信息时, 则直接由缓冲区中取出信息,传给用户,以提高访问速度; + +* 5. 隐藏真实IP:上网者也可以通过这种方法隐藏自己的IP,免受攻击。 + +HTTP代理图示: +![代理](http://s3.51cto.com/wyfs02/M01/38/14/wKioL1OyZMLCGz4sAABIrUX08kc751.jpg) +对于客户端浏览器而言,http代理服务器相当于服务器。 +而对于Web服务器而言,http代理服务器又担当了客户端的角色。 + +## 十二、虚拟主机的实现 +### 什么是虚拟主机 + 虚拟主机:是在网络服务器上划分出一定的磁盘空间供用户放置站点、应用组件等,提供必要的站点功能与数据存放、传输功能。 + + 所谓虚拟主机,也叫“网站空间”就是把一台运行在互联网上的服务器划分成多个“虚拟”的服务器,每一个虚拟主机都具有独立的域名和完整的Internet服务器(支持WWW、FTP、E-mail等)功能。一台服务器上的不同虚拟主机是各自独立的,并由用户自行管理。但一台服务器主机只能够支持一定数量的虚拟主机,当超过这个数量时,用户将会感到性能急剧下降。 + + + +### 虚拟主机的实现原理 + 虚拟主机是用同一个WEB服务器,为不同域名网站提供服务的技术。Apache、Tomcat等均可通过配置实现这个功能。 + 相关的HTTP消息头:Host。 + 例如:Host: www.baidu.com + 客户端发送HTTP请求的时候,会携带Host头,Host头记录的是客户端输入的域名。这样服务器可以根据Host头确认客户要访问的是哪一个域名。 + + +节选自 [Apache之HTTP协议](http://blog.51cto.com/shjia/1432670) \ No newline at end of file diff --git "a/Cute-Article/article/52-\345\211\215\347\253\257\346\250\241\345\235\227\345\214\226\357\274\210CommonJs,AMD\345\222\214CMD\357\274\211.md" "b/Cute-Article/article/52-\345\211\215\347\253\257\346\250\241\345\235\227\345\214\226\357\274\210CommonJs,AMD\345\222\214CMD\357\274\211.md" new file mode 100644 index 00000000..445bc076 --- /dev/null +++ "b/Cute-Article/article/52-\345\211\215\347\253\257\346\250\241\345\235\227\345\214\226\357\274\210CommonJs,AMD\345\222\214CMD\357\274\211.md" @@ -0,0 +1,273 @@ +前端模块规范有三种:**CommonJs**,**AMD**和**CMD**。 + +* CommonJs用在**服务器端**,AMD和CMD用在**浏览器环境**。 + +* AMD 是 RequireJS 在推广过程中对模块定义的规范化产出。 +* CMD 是 SeaJS 在推广过程中对模块定义的规范化产出。 + +* AMD:**提前执行**(异步加载:依赖先执行)+**延迟执行**。 +* CMD:**延迟执行**(运行到需加载,根据顺序执行)。 + +## 模块 +* **函数写法** +```js +function f1(){ + //... +} +function f2(){ + //... +} +``` + +模块就是实现特定功能的文件,把几个函数放在一个文件里就构成了一个模块。需要的时候加载这个文件,调用其中的函数即可。 +但这样做会污染全局变量,无法保证不与其他模块发生变量名冲突,而且模块成员之间没什么关系。 + +* **对象写法** +```js +var module = { +  star : 0, +  f1 : function (){ +    //... +  }, +  f2 : function (){ +    //... +  } +}; +module.f1(); +module.star = 1; +``` + +模块写成一个对象,模块成员都封装在对象里,通过调用对象属性,访问使用模块成员。但同时也暴露了模块成员,外部可以修改模块内部状态。 + +* **立即执行函数** +```js +var module = (function(){ +  var star = 0; +  var f1 = function (){ +    console.log('ok'); +  }; +  var f2 = function (){ +    //... +  }; + return { + f1:f1, + f2:f2 + }; +})(); +module.f1(); //ok +console.log(module.star) //undefined +``` + +外部无法访问内部私有变量 + +## CommonJs +CommonJS是服务器端模块的规范,由Node推广使用。由于服务端编程的复杂性,如果没有模块很难与操作系统及其他应用程序互动。使用方法如下: +```js +// math.js +exports.add = function() { + var sum = 0, i = 0, args = arguments, l = args.length; + while (i < l) { + sum += args[i++]; + } + return sum; +}; + +increment.js +var add = require('math').add; +exports.increment = function(val) { + return add(val, 1); +}; + +index.js +var increment = require('increment').increment; +var a = increment(1); //2 +``` +根据**CommonJS**规范: + +* 一个单独的文件就是一个模块。每一个模块都是一个单独的作用域,也就是说,在该模块内部定义的变量,无法被其他模块读取,除非定义为`global对象`的属性。 + +* 输出模块变量的最好方法是使用`module.exports`对象。 + +* 加载模块使用`require`方法,该方法读取一个文件并执行,返回文件内部的`module.exports`对象 + +仔细看上面的代码,您会注意到 `require` 是同步的。模块系统需要同步读取模块文件内容,并编译执行以得到模块接口。 +然而, 这在浏览器端问题多多。 + +浏览器端,加载 JavaScript 最佳、最容易的方式是在 `document` 中插入` +``` + +我们在html页面的input元素中设置了a属性,但是在`property`中却是访问不到的;相反我们没有在html页面中设置的`title`,访问它却没有反映`undefined`! + +这是怎么回事? + +因为所有的HTML元素都由`HTMLElement`类型表示,`HTMLElement`类型直接继承自`Element`并添加了一些属性,每个HTML元素都有下面的这5个标准特性:`id`,`title`,`lang`,`dir`,`className`(在DOM中以`property`方式操作这几个特性会同步到html标签中)。 + +所以即使在html中没有指定`id`、`title`等,也会默认赋予一个空串,通过property属性(点操作符)可以访问。而除此之外在html中设置的其他属性是不能通过`Property`访问到的(`attribute`特有的属性)。 + +如果把DOM元素看成是一个普通的Object对象,那么property就是一个以名值对(`name='value'`)的形式存放在Object中的属性。要添加和删除`property`也简单多了,和普通的对象没啥分别: +```js +var a = document.getElementById('txt'); +a.age = 10; +console.log(a.age); // 10 +delete a.age; +console.log(a.age); // undefined +``` +除了`id`、`title`等5个属性(`property`)外(每个element元素都有),个别的元素还有特别的属性,比如input元素有`name`,a元素有`href`等等。 + +## 3、Attribute vs Property + +既然说有些属性既能通过`attribute`访问修改,也能通过`property`,那么有什么值得注意的地方呢? + +之所以`attribute`和`property`容易混倄在一起的原因是,很多`attribute`节点还有一个相对应的`property`属性,比如div元素的`id`和`class`既是`attribute`,也有对应的`property`(id和className),不管使用哪种方法都可以**访问和修改**,如果在TAG对这些属性进行赋值,那么这些值就会作为初始值赋给DOM的同名property。 + + +* **input元素的value** + +input元素的`value`属性是一大坑爹处,看下面代码: + +```js +var a = document.getElementById('txt'); +a.setAttribute('value', 'test'); +console.log(a.value); // test + +a.value = 'change'; +console.log(a.getAttribute('value')); // test +``` + +用**点操作符**改变`value`值,并不会更新`attribute`的`value`值;而相反用`attribute`更新`value`值,却会反映到`property`上...坑吧,谁规定的! + +* **表单元素** + +DOM元素一些默认常见的`attribute`节点都有与之对应的`property`属性,比较特殊的是一些值为`Boolean类型`的`property`,如一些表单元素。对于这些特殊的`attribute`节点,只要存在该节点,对应的`property`的值就为`true`,如: +```html + + +``` +disabled类似。 + +* **href** + +两者对于`href`的获取也有不同之处,`attribute`取到的是实际设置的值(相对路径),而`property`取得的是绝对路径: + +```html + + +``` + +## 4、总结 +`Attribute`属性在`html`上设置,会反应在`html`代码上,两者**同步**;而`Property`属性则可以看做是DOM对象的键值对,用**点操作符**对它们进行操作。 + +实际编程中,基本上的DOM操作都是使用`property`的点操作符。 + +只有两种情况不得不使用`attribute`: + +* 1. 自定义HTML Attribute,因为它不能同步到DOM property上 + +* 2. 访问内置的HTML标签的`Attribute`,这些`attribute`不能从`property`上同步过来,比如`input标签`的`value`值(可以用来检验input值是否变化) + + +## 5、参考 +* [JavaScript中的property和attribute的区别](https://www.jianshu.com/p/rRssiL) + +* [返本求源——DOM元素的特性与属性](http://www.cnblogs.com/dojo-lzz/p/4781563.html) \ No newline at end of file diff --git "a/Cute-Article/article/56-js\344\270\255\345\216\237\345\236\213\347\273\247\346\211\277\345\216\237\347\220\206.md" "b/Cute-Article/article/56-js\344\270\255\345\216\237\345\236\213\347\273\247\346\211\277\345\216\237\347\220\206.md" new file mode 100644 index 00000000..39c6924e --- /dev/null +++ "b/Cute-Article/article/56-js\344\270\255\345\216\237\345\236\213\347\273\247\346\211\277\345\216\237\347\220\206.md" @@ -0,0 +1,86 @@ +在JavaScript当中,对象A如果要继承对象B的属性和方法,那么只要将对象B放到对象A的原型链上即可。而某个对象的原型链,就是由该对象开始,通过`__proto__`属性连接起来的一串对象。`__proto__`属性是JavaScript对象中的内部属性,任何JavaScript对象,包括我们自己构建的对象,JavaScript的`built-in`对象,任何函数(在JavaScript当中,函数也是对象)都具有这个属性。如下图就是一个原型链的例子: +![图片1](http://p3nqtyvgo.bkt.clouddn.com/20180927_01.png) + +上图中,A,B,C分别代表3个对象,**蓝色箭头**串接起来的所有对象就构成了**对象C的原型链**,其中C的`_proto__`属性指向B,B的`__proto__`属性指向A,A的`__proto__`属性可能指向更高层的对象,也可能指向`null`(表示**A不继承任何对象的属性和方法**)。 + +如果我们引用了C的某个属性或者方法,那么JavaScript就会顺着C的原型链进行查找,即首先查找对象C本身,看所引用的属性名或者方法名是否存在,如果存在就停止查找直接返回,如果不存在,就通过C的`__proto__`属性找到原型链中的B对象,继续在B对象中查找,如果B对象中找到所引用的属性名或者方法名,那么就停止查找直接返回,如果B对象中也不存在,就通过对象B的`__proto__`属性找到原型链中的A对象,继续重复上述查找过程,直到找到所引用的属性或者方法为止(同时也可能查找完对象C的整个原型链也没有找到所引用的属性或者方法,那么该属性或者方法就是`undefined`的)。 + +因此,只要能够成功的为某一个对象构造出我们需要的原型链,那么就能让该对象继承我们想要它继承的方法或者属性。而想要成功构造对象的原型链,就还必须理解`prototype`属性,JavaScript当中已经存在的原型链,以及当我们创建对象时,原型链被构造的过程。 + + +### prototype属性 +prototype属性存在于JavaScript的任何函数当中,这个属性指向的对象就是所谓的原型对象,在构造原型链时需要原型对象。 + + +### JavaScript当中已经存在的原型链 + +在JavaScript当中存在`Object`,`Function`,`Array`,`String`,`Boolean`,`Number`,`Date`,`Error`,`RegExp`这9个**built-in函数**,一个`built-in`的`Math对象`,通过这上述9个`built-in函数`我们可以创建相应的对象,同时,这9个`built-in函数`的`prototype`属性所指向的原型对象也是`built-in`的。下面的图示解释了这几个函数以及各自`prototype`属性所指向的原型对象之间的关系。 +![图片2](http://p3nqtyvgo.bkt.clouddn.com/20180927_02.png) + +(如果此图看不清,可点击 [此处下载](http://p3nqtyvgo.bkt.clouddn.com/20180927_02.png)) + +  上面的图示中,**黄色方框**代表`built-in函数对象`,**深绿色方框**代表`built-in函数`的`prototype属性`指向的原型对象,名字都叫`xx prototype object`,**浅绿色方框**(即**Math对象**)代表**普通对象**,蓝色箭头连接非built-in函数对象(无论是普通对象如Math,还是原型对象)的`__proto__`属性,而土黄色箭头连接函数对象的`__proto__`属性。 + +  通过上图可以发现,所有**built-in函数对象**的原型链最终都指向`Function prototype object`,所有**非函数对象的原型链**最终都指向`Object prototype object`,并且`Function prototype object`的`__proto__`属性也指向`Object prototype object`,`Object prototype object`的`__proto__`属性指向为`null`。 + +因此,`Object prototype object`是所有原型链的顶端,通过原型链查找规则可知,所有`built-in`函数对象同时继承了`Object prototype object`和`Function prototype object`上的属性和方法,而所有非built-in函数对象只继承了`Object prototype object`上的方法。`Function prototype object`包含了所有函数共享的属性和方法,而`Object prototype object`包含了所有对象都共享额属性和方法。 + +对于上图中原型对象包含的constructor属性,下文当中有解释。 + + + +### 创建对象时,原型链的构造过程 + +在JavaScript当中创建对象有2中方式,一种是通过定义函数使用**new方法**来构造,另一种是使用**对象字面量**的方式,即: +```js +var obj = { + name: "Jim Green" +}; +``` +使用这两种方式创建对象时,对象的原型链构造过程有所不同。 + +* **1. 使用函数的方式构造对象** + +使用函数的方式构造对象分为两步:首先需要**定义一个函数**作为构造函数,然后使用**new方法构造对象**。接下来就来看一下这两个步骤会发生什么。 + +假设我们定义了一个函数名为`F`,此时JavaScript会为我们做两件事,第一:根据我们定义的函数创建一个函数对象,第二,设置这个函数的`__proto__`属性和`prototype`属性。其中`__proto__`属性指向`built-in`的`Function prototype object`,而`prototype`属性指向一个为函数对象F新创建的原型对象,这个新创建的原型对象通过调用`new Object()`构造出来,并且为这个新创建的对象添加`constructor`属性,该属性指向函数对象`F`。最后的结果如下图所示: + +![图3](http://p3nqtyvgo.bkt.clouddn.com/20180927_03.png) + +上图中为了方便,没有画出`Function prototype object`和`Object prototype object`的`constructor`属性。而`F` `prototype object`的`__proto__`属性为何指向`Object prototype object`,下文介绍**new操作符**时有解释。 + +当我们使用**new方法**调用F函数的时候,JavaScript也会为我们做两件事,第一,分配内存作为新创建的对象,第二,将新创建的对象的__proto__属性指向函数F的原型对象,结果如下图: +![图4](http://p3nqtyvgo.bkt.clouddn.com/20180927_04.png) + +上图中,`obj`就是调用**new方法**通过`函数F`创建出来的对象,我们可以看到对象obj的原型链包含了`函数F`的原型对象,以及`Object prototype object`,这样,对象`obj`通过原型链查找规则,就能继承`函数F`的原型对象,以及`Object prototype object`上面定义的属性和方法了。并且如果我们想知道一个对象是由哪个方法构建的,只需要访问这个对象的`constructor`属性即可,上例中,只要我们访问`obj.constructor`,那么就知道obj是由`函数F`创建的。同时,由于`F` `prototype object`上文中介绍是由`new Object`函数创建的,根据此处介绍,F `prototype object`的`__proto__`属性应该指向`Object函数`的原型对象,即`Object prototype object`。 + +* **2. 使用对象字面量定义对象** + +当使用对象字面量创建对象时,JavaScript会为我们做两件事: + +> 1 分配内存作为新创建的对象。 +> 2 将新创建对象的`__proto__`属性指向`Object prototype object`。 + +结果如下图所示: +![图5](http://p3nqtyvgo.bkt.clouddn.com/20180927_05.png) + +上图为了简化,同样没有画出`Object prototype object`的`constructor`属性 + +### 继承 + +理解了上面所讲的原理之后,假设目前有一个对象`A`(这个对象可以是任意的,包括JavaScript built-in的对象,任何函数对象,任何原型对象,以及我们自己new出来的对象),现在想创建一个对象`obj`,让`obj`继承`A`的属性和方法。 + +通过上面的介绍,我们知道创建对象有两种方式,但是使用对象字面量创建的对象其原型链总是只包含两个对象,一个是其自己,一个是`Object prototype object`,根本不可能包含对象`A`,无法达到让对象`obj`继承对象`A`属性和方法的效果。因此,只能使用函数的方式创建对象,让对象A包含在新创建对象obj的原型链中即可。 + +根据上面的讲解,如果是用函数的方式创建对象,那么在调用`new`方法时,新创建对象的`__proto__`属性会指向函数的原型对象。因此,只要在调用函数之前,将函数的原型对象换成`A`,然后再调用`new`方法,就可以将对象A包含在新创建的对象`obj`的原型链中,这样通过原型链查找规则,`obj`就继承了A的属性和方法。假设用来创建对象`obj`的函数为`B`,则相关代码为: +```js +B.prototype = A; +B.prototype.constructor = B; +var obj = new B(传入的参数) +``` +上面代码中的`B.prototype.constructor = B`,是因为对象A中可能没有`constructor`属性,或者`constructor`属性不指向`B`,而为了方便通过访问任何由`B`函数创建的对象的`constructor`属性,就可以正确的知道该对象是使用函数`B`构造出来的。相关图示如下图: + +![图6](http://p3nqtyvgo.bkt.clouddn.com/20180927_06.png) + + +上图中虚线框所包围的`B prototype object`就是定义函数`B`时,JavaScript为函数B生成的原型对象,但是该对象被我们用代码替换成了对象A。由于这个被替换的`B prototype object`没有其他地方再用到,因此会被回收掉。 \ No newline at end of file diff --git "a/Cute-Article/article/57-ES5ES6\346\255\243\345\210\231\350\241\250\350\276\276\345\274\217\346\200\273\347\273\223.md" "b/Cute-Article/article/57-ES5ES6\346\255\243\345\210\231\350\241\250\350\276\276\345\274\217\346\200\273\347\273\223.md" new file mode 100644 index 00000000..3d40f1e8 --- /dev/null +++ "b/Cute-Article/article/57-ES5ES6\346\255\243\345\210\231\350\241\250\350\276\276\345\274\217\346\200\273\347\273\223.md" @@ -0,0 +1,363 @@ +## 1. +**ʽ**regular expressionһֱıģʽַṹķ + +ʽ**ַʽ** + +һʹ**бܱʾʼͽ** +```js +var regex = /xyz/ +``` + +һʹRegExp캯 +```js +var regex = new RegExp('xyz'); +``` + +ǵҪǣһַʱͻ½ʽڶַʱ½ʽǰߵЧʽϸߡңǰ߱ȽϱֱۣʵӦУ϶ʽ + +## 2.ʵ +* **i** :ԴСд + +* **m** :ģʽ + +* **g** :ȫ + +## 3.ʵ +### 3.1 RegExp.prototype.test() +ʵ`test`һֵʾǰģʽ**Ƿƥ**ַ +```js +/С/.test('С ѧϰִ') // true +``` +`reg.exec(str)` ƥ飬ƥ򷵻`null`ÿִһ`exec`ƥһ + +### 3.2 RegExp.prototype.exec() +* **reg.exec(str)** ƥ飬ƥ򷵻`null`ÿִһ`exec`ƥһ +```js +var s = '_x_x';var r1 = /x/;var r2 = /y/; + +r1.exec(s) // ["x"]r2.exec(s) // null +``` +* ʽ()Ϊƥ䣬ؽУһƥÿƥĽ +```js +var s = '_x_x'; +var r = /_(x)/; + +r.exec(s) // ["_x", "x"] +``` +`exec`ķ黹ԣ +**input**ԭַ + +**index**ģʽƥɹĿʼλã0ʼ +```js +var r = /a(b+)a/; +var arr = r.exec('_abbba_aba_'); + +arr // ["abbba", "bbb"] + +arr.index // 1arr.input // "_abbba_aba_" +``` +* ʽgѡȫԶʹ exec´εƥϴεĽʼ +```js +var reg = /a/g;var str = 'abc_abc_abc'var r1 = reg.exec(str); +r1 // ["a"]r1.index // 0reg.lastIndex // 1var r2 = reg.exec(str); +r2 // ["a"]r2.index // 4reg.lastIndex // 5var r3 = reg.exec(str); +r3 // ["a"]r3.index // 8reg.lastIndex // 9var r4 = reg.exec(str); +r4 // nullreg.lastIndex // 0 +``` + +## 4.ַʵ +### 4.1 str.match(reg) + `reg.exec`ƣǣʹ`g`ѡstr.match`һԷн +```js +var s = 'abba';var r = /a/g; + +s.match(r) // ["a", "a"]r.exec(s) // ["a"] +``` +### 4.2 str.search(reg) +ƥɹĵһλ,ûκƥ䣬򷵻-1 +```js +'_x_x'.search(/x/)// 1 +``` +### 4.3 str.replace(reg,newstr) +õһ`reg`ȥƥ䣬õڶ`newstr` ȥ滻ʽ`g`η滻һƥɹֵ滻ƥɹֵ +```js +'aaa'.replace('a', 'b')// "baa" +'aaa'.replace(/a/, 'b') // "baa" +'aaa'.replace(/a/g, 'b') // "bbb" +``` +### 4.4 str.split(reg[,maxLength]) +ƥģʽиڶƷؽ + +## 5. ƥ +### 5.1 ַԪַ +󲿷ַʽУĺ壬`/a/`ƥ`a``/b/`ƥ`b`ʽ֮Уijַֻʾĺ壨ǰabôǾͽ**ַ**literal characters +ַ⣬һַ⺬壬˼ǽ**Ԫַ**metacharactersҪ¼ + +* (1) ַ`.` + +ַ.ƥسr(n) зָu2028Ͷηָu2029ַ +```js +/c.t/ +``` +Уc.tƥct֮һַֻҪַͬһУcatc2tc-tȵȣDzƥcoot + +* (2)λַ + +**^** ʾַĿʼλ + +**$** ʾַĽλ +```js +// testڿʼλ/ +^test/.test('test123') // true + +// testڽλ +/test$/.test('new test') // true + +// ӿʼλõλֻ +test/^test$/.test('test') // true + +/^test$/.test('test test') // false +``` + +* (3)ѡ| + +߷ţ`|`ʽбʾ`ϵ`ORcat|dogʾƥcatdog +```js +/11|22/.test('911') // true +``` +Уʽָƥ1122 + +### 5.2 ת +ʽЩ⺬ԪַҪƥDZҪǰҪϷбܡҪƥ+Ҫд+ +```js +/1+1/.test('1+1')// false/1\+1/.test('1+1')// true +``` +ʽУҪбתģһ12ַ`^.[$()|*+?{`͡ҪرעǣʹRegExpתҪʹбܣΪַڲתһΡ +```js +(new RegExp('1\+1')).test('1+1')// false +(new RegExp('1\\+1')).test('1+1')// true +``` +### 5.3 ַ +ַࣨclassʾһϵַɹѡֻҪƥһͿˡпɹѡַڷڣ[xyz] ʾxyz֮ѡһƥ䡣 +```js +/[abc]/.test('hello world') // false +/[abc]/.test('apple') // true +``` +ַַ⺬塣 + +* 1ַ^ +ڵĵһַ[^xyz]ʾxyz֮ⶼƥ䣺 +```js +/[^abc]/.test('hello world') // true +/[^abc]/.test('bbc') // false +``` +ûַֻ[^]ͱʾƥһַаз֮£ΪԪַ.Dzзġ +```js +var s = 'Please yes\nmake my day!'; +s.match(/yes.*day/) // nulls.match(/yes[^]*day/) // [ 'yes\nmake my day'] +``` +УַsһзŲзԵһʽƥʧܣڶʽ[^]һַƥɹ + +* 2ַ- + +ijЩ£еַַ`-`ṩдʽʾַΧ磬[abc]д[`a-c`][0123456789]д[0-9]ͬ[A-Z]ʾ26дĸ +```js +/a-z/.test('b') // false +/[a-z]/.test('b') // true +``` +¶ǺϷַдʽ +```js +[0-9.,] +[0-9a-fA-F] +[a-zA-Z0-9-] +[1-31] +``` +һַ[1-31]131ֻ13 + +⣬Ҫʹַ趨һܴķΧܿѡַ֮͵Ӿ[A-z]ѡдӴдAСдz֮52ĸ ASCII ֮УдĸСдĸ֮仹ַͻ֮Ľ +```js +/[A-z]/.test('\\') // true +``` +Уڷбܣ''ASCIIڴдĸСдĸ֮䣬ᱻѡС + +### 5.4 Ԥģʽ +ԤģʽָijЩģʽļдʽ + +**d** ƥ0-9֮һ֣൱[0-9] + +**D** ƥ0-9ַ൱[^0-9] + +**w** ƥĸֺ»ߣ൱[A-Za-z0-9_] + +**W** ĸֺ»ַ൱[^A-Za-z0-9_] + +**s** ƥո񣨰зƱոȣ[ \t\r\n\v\f] + +**S** ƥǿոַ൱[^ \t\r\n\v\f] + +**b** ƥʵı߽硣 + +**B** ƥǴʱ߽磬ڴʵڲ +```js +// \s +/\s\w*/.exec('hello world') // [" world"] + +// \b + +/\bworld/.test('hello world') // true +/\bworld/.test('hello-world') // true +/\bworld/.test('helloworld') // false +// \B +/\Bworld/.test('hello-world') // false +/\Bworld/.test('helloworld') // true +``` +ͨʽзnͻֹͣƥ䡣 +```js +var html = "Hello\nworld!"; + +/.*/.exec(html)[0] +// "Hello" +``` +Уַhtmlһзַ`.`ƥ任зƥܲԭ⡣ʱʹsַ࣬ܰз +```js +var html = "Hello\nworld!"; + +/[\S\s]*/.exec(html)[0] +// "Hello\nworld!" +``` +У[Ss]ָһַ + +### 5.5 ظ +ģʽľȷƥʹôţ`{}`ʾ`{n`}ʾǡظnΣ`{n,}`ʾظnΣ`{n,m}`ʾظnΣmΡ +```js +/lo{2}k/.test('look') // true +/lo{2,5}k/.test('looook') // true +``` +Уһģʽָo2Σڶģʽָo2ε5֮䡣 + +### 5.6 ʷ +`*. ?` ʺűʾijģʽ0λ1Σͬ{0, 1} +`*. *` Ǻűʾijģʽ0λΣͬ{0,} +`*. +` Ӻűʾijģʽ1λΣͬ{1,} + +### 5.7 ̰ģʽ +һСڵʷĬ¶ƥ䣬ƥֱһַƥΪֹⱻΪ̰ģʽ +```js +var s = 'aaa'; +s.match(/a+/) // ["aaa"] +``` +Уģʽ`/a+/`ʾƥ1aaô׻ƥ伸aأΪḬ̆ģʽһֱƥ䵽ַaΪֹƥ3a + +뽫̰ģʽΪ̰ģʽʷһʺš +```js +var s = 'aaa'; +s.match(/a+?/) // ["a"] +``` +˷̰ģʽļӺţз̰ģʽǺţ`*`ͷ̰ģʽʺţ`?` + +`+?`ʾijģʽ1λΣƥʱ÷̰ģʽ + +`*?`ʾijģʽ0λΣƥʱ÷̰ģʽ + +`??`ijģʽ0λ1Σƥʱ÷̰ģʽ + +### 5.8 ƥ +* 1 + +ʽűʾƥ䣬еģʽƥݡ +```js +/fred+/.test('fredd') // true +/(fred)+/.test('fredfred') // true +``` +Уһģʽûţ`+`ֻʾظĸdڶģʽţ`+`ͱʾƥfredʡ + +һ鲶ӡ +```js +var m = 'abcabc'.match(/(.)b(.)/); +m// ['abc', 'a', 'c'] +``` +Уʽ`/(.)b(.)/`һʹţһŲaڶŲc + +ע⣬ʹƥʱͬʱʹgηmatchᲶݡ +```js +var m = 'abcabc'.match(/(.)b(.)/g); +m // ['abc', 'abc'] +``` +ʽڲnƥݣnǴ1ʼȻʾӦ˳š +```js +/(.)b(.)\1b\2/.test("abcabc")// true +``` +ĴУ1ʾһƥݣa2ʾڶƥݣc + +* 2Dz + +`(?:x)`ΪDz飨Non-capturing groupʾظƥݣƥĽвš + +Dz뿼һٶҪƥfoofoofooʽӦд`/(foo){1, 2}/`ռһƥ䡣ʱͿʹ÷Dz飬ʽΪ`/(?:foo){1, 2}/`ǰһһģDzᵥڲݡ +```js +var m = 'abc'.match(/(?:.)b(.)/); +m // ["abc", "c"] +``` +еģʽһʹšеһǷDz飬󷵻صĽûеһţֻеڶƥݡ + +* 3ж + +`x(?=y)`ΪжԣPositive look-aheadxֻyǰƥ䣬yᱻ뷵ؽ磬ҪƥŰٷֺŵ֣д`/d+(?=%)/` + +жԡУIJDz᷵صġ +```js +var m = 'abc'.match(/b(?=c)/); +m // ["b"] +``` +ĴʹжԣbcǰԱƥ䣬ŶӦcᱻء + +* 4з񶨶 + +`x(?!y)`Ϊз񶨶ԣNegative look-aheadxֻвyǰƥ䣬yᱻ뷵ؽ磬ҪƥIJǰٷֺŵ֣Ҫд`/d+(?!%)/` +```js +/\d+(?!\.)/.exec('3.14')// ["14"] +``` +УʽָֻвСǰֲŻᱻƥ䣬˷صĽ14 + +## 6. ʵս +### 6.1 ַβ˵Ŀո +```js +var str = ' #id div.class '; +str.replace(/^\s+|\s+$/g, '') // "#id div.class" +``` +### 6.2 ֻ֤ +```js +var reg = /1[24578]\d{9}/;reg.test('154554568997'); //truereg.test('234554568997'); //false +``` +### 6.3 ֻ滻 * +```js +var reg = /1[24578]\d{9}/;var str = ' ֻ18210999999 Ա'; + +str.replace(reg, '***') //" ֻ*** Ա" +``` +### 6.4 ƥҳǩ +```js +var strHtlm = 'СС
222222@.qq.com
СС'; + +var reg = /<(.+)>.+<\/\1>/; + +strHtlm.match(reg); // ["
222222@.qq.com
"] +``` +### 6.5 滻 +```js +let str = 'ййžл񹲺͹'; +let r = str.replace(/й|/g, input => { + let t = ''; + for (let i = 0; i + Click Here + +``` +е`data-age`һԶԣȻҲͨ**JavaScript**в**HTML5**Ԫضһ`dataset`ԣһ`DOMStringMap`͵ļֵԼϣ +```js +var test = document.getElementById('test'); +test.dataset.my = 'Byron'; +``` +Ϊ`div`һ`data-my`ԶԣʹJavaScript`dataset`Ҫעĵط + +- 1. ******ȡ**ԵʱҪȥǰ׺`data-*`ûʹ`test.dataset.data-my = 'Byron';`ʽ + +- 2. лַ(`-`)Ҫת**շ**ʽCSSʹѡҪʹַʽ + +ΪղŴ׷дݣ +```html + +``` + +```js +test.dataset.birthDate = '19890615'; +``` +ͨJavaScriptdata-birth-dateԶԣCSSʽΪdivһЩʽЧ + +![ͼƬ1](http://p3nqtyvgo.bkt.clouddn.com/2018100601.png) + +![ͼƬ2](http://p3nqtyvgo.bkt.clouddn.com/2018100602.png) + +ȡʱҲͨ`dataset`ʹá`.`ȡԣͬҪȥ`data-`ǰ׺ַҪתΪշ + +```js +var test = document.getElementById('test'); +test.dataset.my = 'Byron'; +test.dataset.birthDate = '19890615'; +test.onclick = function () { + alert(this.dataset.my + ' ' + this.dataset.age+' '+this.dataset.birthDate); +} +``` +![ͼƬ3](http://p3nqtyvgo.bkt.clouddn.com/2018100603.png) + +### 2. getAttribute/setAttribute +Щͬѧܻ`getAttribute`/`setAttribute`ʲôһ£ +```js +var test = document.getElementById('test'); +test.dataset.birthDate = '19890615'; +test.setAttribute('age', 25); +test.setAttribute('data-sex', 'male'); + +console.log(test.getAttribute('data-age')); //24 +console.log(test.getAttribute('data-birth-date')); //19890516 +console.log(test.dataset.age); //24 +console.log(test.dataset.sex); //male +``` + +![ͼƬ4](http://p3nqtyvgo.bkt.clouddn.com/2018100604.png) + +![ͼƬ5](http://p3nqtyvgo.bkt.clouddn.com/20181006045.png) + + +ǿԿ߶õ`attribute`ϻҪ˼ܽԶԣҲ˵`getAttribute`/`setAttribute`Բе`dataset`ݣ`dataset`ֻ`attribute`һӼˣ`dataset`ֻд`data-`ǰ׺ԣû`age=25`Ǹ + +ôΪʲôǻҪ`data-*`أһĺôǿ԰Զ`dataset`ͳһĶŶܷ㣬ɢɢˣûDzġ + +### 3. +ȽϲõϢ`data-*`ʮֲֹۡ +* **Internet Explorer 11+** +* **Chrome 8+** +* **Firefox 6.0+** +* **Opera 11.10+** +* **Safari 6+** + +IE11+ֱϹСۣҪȫʹô·Զӡ + diff --git "a/Cute-Article/article/59-\345\211\215\347\253\257HTML5\345\207\240\347\247\215\345\255\230\345\202\250\346\226\271\345\274\217\347\232\204\346\200\273\347\273\223.md" "b/Cute-Article/article/59-\345\211\215\347\253\257HTML5\345\207\240\347\247\215\345\255\230\345\202\250\346\226\271\345\274\217\347\232\204\346\200\273\347\273\223.md" new file mode 100644 index 00000000..7980659c --- /dev/null +++ "b/Cute-Article/article/59-\345\211\215\347\253\257HTML5\345\207\240\347\247\215\345\255\230\345\202\250\346\226\271\345\274\217\347\232\204\346\200\273\347\273\223.md" @@ -0,0 +1,269 @@ +ҪúܽһЩ֪ʶȻкö֪ʶᣬǻҪŬһ£ֶ˭֪~ + +## + +h5֮ǰ洢Ҫ`cookies``cookies`ȱ**ͷϴ**С**4k֮**DomainȾ + +ҪӦãﳵͻ¼ + +IE`UserData`С64k,ֻIE֧֡ + +## Ŀ +4kĴС +ͷ洢Ϣ +ϵʹ洢 + + +### 1.ش洢localstorage +* **洢ʽ** + +`ֵ`(`Key-Value`)ķʽ洢**ô洢****ʧЧ****ֶɾ** + +* **С** + +ÿ**5M** + +* **֧** +![ͼƬ1](http://p3nqtyvgo.bkt.clouddn.com/2018100701.png) + +ע⣺IE9 `localStorage`ֱ֧ļҪĿ𵽷ſ֧֣ + +* **ⷽ** +```js +if(window.localStorage){ + alert('This browser supports localStorage'); +}else{ + alert('This browser does NOT support localStorage'); +} +``` + +* **õAPI** +``` +getItem //ȡ¼ + +setIten //ü¼ + +removeItem //Ƴ¼ + +key //ȡkeyӦֵ + +clear //¼ +``` + +![ͼƬ2](http://p3nqtyvgo.bkt.clouddn.com/2018100702.png) + + +* **洢** + +飬ͼƬjsonʽűֻҪлַݶԴ洢 + +### 2.ش洢sessionstorage +HTML5 ıش洢 API е `localStorage` `sessionStorage` ʹ÷ͬģ `sessionStorage` **رҳ󼴱** `localStorage` **һֱ** + +### 3.߻棨application cache +ػӦļ + +* **ʹ÷** + +**manifestļ** + +ҳϣ +```html + + +... + +``` + +**Manifest ļ** + +manifest ļǼ򵥵ıļ֪ݣԼݣ + +manifest ļɷΪ֣ + +1. **CACHE MANIFEST** - ڴ˱гļ״غл + +2. **NETWORK** - ڴ˱гļҪӣҲᱻ + +3. **FALLBACK** - ڴ˱гļ涨ҳ޷ʱĻҳ棨 404 ҳ棩 + +demo +``` +CACHE MANIFEST +# 2016-07-24 v1.0.0 +/theme.css +/main.js + +NETWORK: +login.jsp + +FALLBACK: +/html/ /offline.html +``` + +ϣmanifestļҪȷ`MIME-type` "`text/cache-manifest`" + +Tomcat: +```html + + manifest + text/cache-manifest + +``` + +**API** + +`applicationCache`и`status`ԣʾӦûĵǰ״̬ + +**0**UNCACHED : ޻棬 ûҳصӦû + +**1**IDLE : ãӦûδõ + +**2** CHECKING : Уļ + +**3** DOWNLOADING : УӦûļָԴ + +**4** UPDATEREADY : ɣԴ + +**5** IDLE : ӦûļѾˣҳ޷ٷӦû + + + +**ص¼** + +ʾӦû״̬ĸı䣺 + +**checking** : ΪӦûҸʱ + +**error** : ڼ»Դڼ䷢ʹʱ + +**noupdate** : ڼļļޱ仯ʱ + +**downloading** : ڿʼӦûԴʱ + +**progress**ļӦûĹгϵصش + +**updateready** : ҳµӦûϴ + +**cached** : Ӧûʱ + +**Application Cache** + +1. +2. ҳٶ + +3. ͷѹ + +**ע** +1. Իݵƿ̫ܲһijЩõÿվ 5MB +2. manifestļڲоٵijһļأ¹̽ΪʧܣȫʹϵĻ档 +3. manifesthtmlmanifestļͬԴͬһ¡ +4. ԶmanifestļHTMLļ͵HTMLݣҲҪ°汾¡ +5. manifestļCACHENETWORKFALLBACKλ˳ûйϵʽҪǰ档 +6. FALLBACKеԴmanifestļͬԴ +7. 汾󣬱ˢһβŻ°汾ˢһҳҪӼ汾¼ +8. վеҳ漴ʹûmanifestԣԴڻҲӻзʡ +9. manifestļıʱԴҲᴥ¡ + + +**߻봫ͳ** + +1. ߻Ӧãǵļ + +2. ߻˻ǿԴҳ棬治 + +3. ߻֪ͨԴ + +### 4.Web SQL +**ϵݿ**ͨSQL + +Web SQL ݿ API HTML5 淶һ֣һĹ淶һʹ SQL ͻݿ APIs + +**֧** +Web SQL ݿ° **Safari**, **Chrome** **Opera** й + +**ķ** + +1. **openDatabase**ʹеݿ½ݿⴴһݿ + +2. **transaction**ܹһԼִύ߻ع + +3. **executeSql**ִʵʵ SQL ѯ + + + +**ݿ** +```js +var db = openDatabase('mydb', '1.0', 'Test DB', 2 * 1024 * 1024,fn); +//openDatabase() ӦֱΪݿơ汾šıݿСص +``` + +**ִвѯ** +```js +var db = openDatabase('mydb', '1.0', 'Test DB', 2 * 1024 * 1024); +db.transaction(function (tx) { + tx.executeSql('CREATE TABLE IF NOT EXISTS WIN (id unique, name)'); +}); +``` + +**** +```js +var db = openDatabase('mydb', '1.0', 'Test DB', 2 * 1024 * 1024); +db.transaction(function (tx) { + tx.executeSql('CREATE TABLE IF NOT EXISTS WIN (id unique, name)'); + tx.executeSql('INSERT INTO WIN (id, name) VALUES (1, "winty")'); + tx.executeSql('INSERT INTO WIN (id, name) VALUES (2, "LuckyWinty")'); +}); +``` + +**ȡ** +```js +db.transaction(function (tx) { + tx.executeSql('SELECT * FROM WIN', [], function (tx, results) { + var len = results.rows.length, i; + msg = "

ѯ¼: " + len + "

"; + document.querySelector('#status').innerHTML += msg; + + for (i = 0; i < len; i++){ + alert(results.rows.item(i).name ); + } + + }, null); +}); +``` +ЩԿ϶SQLݿزMySQLĻӦñȽá + + +### 5.IndexedDB +ݿ (IndexedDB) APIΪ HTML5 һ֣Դзḻش洢ݵܼ͵ HTML5 Web Ӧóáͬʱ**ػ**ʹͳ Web Ӧó򣨱ƶ Web Ӧóܹ**кӦ** + +**첽API** +IndexedDB󲿷ֲdzõĵ÷ؽģʽ󡪡ӦģʽݿIJ + +![ͼƬ3](http://p3nqtyvgo.bkt.clouddn.com/2018100703.png) + +ǴݿʱʵϷһDB󣬶`result`СͼԿresult֮⡣мҪԾ`onerror``onsuccess``onupgradeneeded`򿪵ݿİ汾źѾڵݿ汾ŲһµʱãǵajaxǷ֮󲢲ȷʲôʱɹҪڻصдһЩ߼ + +**رɾ** +```js +function closeDB(db){ + db.close(); +} +function deleteDB(name){ + indexedDB.deleteDatabase(name); +} +``` + +**ݴ洢** + +**indexedDB**ûбĸ`objectStore`һݿп԰`objectStore``objectStore`һݽṹԴŶݡҲ˵һ`objectStore`൱һű洢ÿݺһ + +ǿʹÿ¼еijֶָΪ**ֵ**keyPathҲʹԶɵĵΪֵkeyGeneratorҲԲָѡͲͬobjectStoreԴ洢ݽṹҲв졣 + +е㸴ˡĽ̳̣ +1.http://www.cnblogs.com/dolphinX/p/3415761.html + +2.http://www.cnblogs.com/dolphinX/p/3416889.html +ϸAPIַhttp://www.ibm.com/developerworks/cn/web/wa-indexeddb/#ibm-pcon + +ʱȷʵЩ洢ʽδùҲֻЩ˵ˡ˽ţԺõϸ¼ɣ^_^ \ No newline at end of file diff --git "a/6-\345\205\263\344\272\216js\347\232\204\344\275\234\347\224\250\345\237\237\345\222\214\345\243\260\346\230\216\346\217\220\345\211\215.md" "b/Cute-Article/article/6-\345\205\263\344\272\216js\347\232\204\344\275\234\347\224\250\345\237\237\345\222\214\345\243\260\346\230\216\346\217\220\345\211\215.md" similarity index 100% rename from "6-\345\205\263\344\272\216js\347\232\204\344\275\234\347\224\250\345\237\237\345\222\214\345\243\260\346\230\216\346\217\220\345\211\215.md" rename to "Cute-Article/article/6-\345\205\263\344\272\216js\347\232\204\344\275\234\347\224\250\345\237\237\345\222\214\345\243\260\346\230\216\346\217\220\345\211\215.md" diff --git "a/Cute-Article/article/60-\346\207\222\345\212\240\350\275\275\345\222\214\351\242\204\345\212\240\350\275\275.md" "b/Cute-Article/article/60-\346\207\222\345\212\240\350\275\275\345\222\214\351\242\204\345\212\240\350\275\275.md" new file mode 100644 index 00000000..64aec48a --- /dev/null +++ "b/Cute-Article/article/60-\346\207\222\345\212\240\350\275\275\345\222\214\351\242\204\345\212\240\350\275\275.md" @@ -0,0 +1,143 @@ +> [ԭ](https://segmentfault.com/a/1190000016666816) + +## һ +### 1.ʲô + +Ҳ**ӳټ**ָڳҳ**ӳټͼ**һֺܺŻҳܵķʽû֮ǰͼ񲻻ءͼԤ෴ڳҳʹӳټؽʹҳظ졣ijЩ£԰**ٷ**ͼƬܶ࣬ҳܳĵվС + +### 2.ΪʲôҪ +* **û**£ûֻԱҳʱҳеͼƬҪأͼƬĿϴ󣬵ȴʱܳûԹӰû顣 + +* **ЧԴļ**Լ˷ѹҲܹСĸ + +* **ֹصԴjsļ**Ӱվʹá + +### 3.صԭ +ȽҳϵͼƬ` src `ΪַͼƬʵ·`data-original`У +ҳʱҪȥ`scroll¼``scroll¼`ĻصУжǵصͼƬǷ,ͼƬڿڽͼƬ` src `Ϊ`data-original` ֵͿʵӳټء + +### 4.ʵֲ +```html + + + + Lazyload + + + + + + + + + + + + + + + + + + +``` + +## Ԥ +### 1.ʲôԤ +ԴԤһŻǿʹøüԤȸ֪ijЩԴڽᱻʹõ**Ԥؼ˵ǽԴǰصأҪõʱֱӴӻȡԴ** + +### 2.ΪʲôҪԤ +ҳȫ֮ǰһЩҪݽмأṩûõ飬ٵȴʱ䡣һҳݹӴûʹԤؼҳͻ᳤ʱչΪһƬհףֱݼϡ + +### .ʵԤصļְ취 +* ʹHTMLǩ +```html + +``` + +* ʹ`Image` +```html + +``` +```js +//myPreload.jsļ +var image= new Image() +image.src="http://pic26.nipic.com/20121213/6168183 004444903000 2.jpg" +``` + +* ʹXMLHttpRequest,Ȼڿ⣬ᾫϸԤع +```js +var xmlhttprequest=new XMLHttpRequest(); +xmlhttprequest.onreadystatechange=callback; +xmlhttprequest.onprogress=progressCallback; +xmlhttprequest.open("GET","http://image.baidu.com/mouse,jpg",true); +xmlhttprequest.send(); +function callback(){ + if(xmlhttprequest.readyState==4&& xmlhttprequest.status==200){ + var responseText=xmlhttprequest.responseText; + }else{ + console.log("Request was unsuccessful:"+xmlhttprequest.status); + } +} +function progressCallback(e){ + e=e || event; + if(e.lengthComputable){ + console.log("Received"+e.loaded+"of"+e.total+"bytes") + } +} +``` + +* ʹPreloadJS +PreloadJSṩһԤݵһ·ʽԱHTMLӦóʹáԤؿʹHTMLǩԼXHRɡĬ£PreloadJS᳢ʹXHRݣΪṩ˶ԽȺ¼ĸ֧֣ڿ⣬ʹûڱǵļؿܸá +```js +//ʹpreload.js +var queue=new createjs.LoadQueue(); +//Ĭxhrnew createjs.LoadQueue(false)ָʹHTMLǩԿ +queue.on("complete",handleComplete,this); +queue.loadManifest([ + {id:"myImage",src:"http://pic26.nipic.com/20121213/61681830044449030002.jpg"}, + {id"myImage2"src:"http://pic9.nipic.com/20100814/28395261931471581702.jpg"} +]); +function handleComplete(){ + var image=queue.getResuLt("myImage"); + document.body.appendChild(image); +} +``` + +## غԤصĶԱ +߶ҳЧİ취ҪһǰأһdzٻءضԷǰһĻѹãԤӷǰѹ + +## ġο +[غԤ(js)](https://www.geekjc.com/post/58d94d0f16a3655650d6fafe) + +[غԤ](https://lilywei739.github.io/2017/02/06/lazyload_Img.html) \ No newline at end of file diff --git "a/Cute-Article/article/61-JS\344\270\255this\347\232\2044\347\247\215\347\273\221\345\256\232\350\247\204\345\210\231.md" "b/Cute-Article/article/61-JS\344\270\255this\347\232\2044\347\247\215\347\273\221\345\256\232\350\247\204\345\210\231.md" new file mode 100644 index 00000000..9732e30b --- /dev/null +++ "b/Cute-Article/article/61-JS\344\270\255this\347\232\2044\347\247\215\347\273\221\345\256\232\350\247\204\345\210\231.md" @@ -0,0 +1,235 @@ +> [ԭĵַ](https://segmentfault.com/a/1190000016678888) + +## this +* ES6е**ͷ**õ**ʷ** +* ΪʲôҪʹthis**ʹAPIƵøڸ** +* thisָҲָĴʷ +* thisָֻȡ**ĵ÷ʽ** + +## this󶨹 +* new > ʾ > ʽ > Ĭϰ + +### Ĭϰ + +* ʱǷڵջУthisָ**ȫֶ**Ϊwindow +* ϸģʽ£ܽȫֶĬϰ󶨡 +```js +var a = 2; +function foo(){ + console.log(this.a); +} +function bar(){ + var a = 5; + foo(); +} +bar(); // 2 +``` + +### ʽ + +* Ķʱʽ󶨹Ѻе`this`󶨵Ķ +* ֻһڵλá +* Ҫ󣺶ڲһָԣöͨԼú +```js +function foo() { + console.log( this.a ); +} + +var obj2 = { + a: 42, + foo: foo +}; + +var obj1 = { + a: 2, + obj2: obj2 +}; + +obj1.obj2.foo(); // 42 +``` + +* ʽʧ +```js +function foo() { + console.log( this.a ); +} + +var obj = { + a: 2, + foo: foo +}; + +var bar = obj.foo; // barfooԲк + +var a = "oops, global"; // aȫֶ + +bar(); // "oops, global" +``` + +ͻص£ʱʽֵ + +```js +function foo() { + console.log( this.a ); +} + +function doFoo(fn) { + // ʱ൱fn = obj.fooͺϸһ + fn(); // <-- call-site! +} + +var obj = { + a: 2, + foo: foo +}; + +var a = "oops, global"; // `a` also property on global object + +doFoo( obj.foo ); // "oops, global" +``` + +### ʽ + +* `call()``apply()`ͨһΪͣᱻװתΪװ䣩`this`󶨵ö +* Ӳ + +```js +function foo() { + console.log( this.a ); +} + +var obj = { + a: 2 +}; + +var bar = function() { + foo.call( obj ); +}; + +bar(); // 2 +setTimeout( bar, 100 ); // 2 + +// Ӳ󶨺barôãӰfoothis +bar.call( window ); // 2 +``` + +Ӳ󶨵ĵӦµİ + +```js +function foo(something) { + console.log( this.a, something ); + return this.a + something; +} + +var obj = { + a: 2 +}; + +var bar = function() { + return foo.apply( obj, arguments ); // objӲȥ +}; + +var b = bar( 3 ); // 2 3 +console.log( b ); // 5 +``` + +ڲ`apply`Ӳ󶨵ijôӰڲ`this` +`bind`£ + +```js +function foo(something) { + console.log( this.a, something ); + return this.a + something; +} + +// simple `bind` helper +function bind(fn, obj) { + return function() { + return fn.apply( obj, arguments ); // òobjȥ + }; +} + +var obj = { + a: 2 +}; + +var bar = bind( foo, obj ); // bind( foo, obj )᷵һ + +var b = bar( 3 ); // 2 3 +console.log( b ); // 5 +``` +* **ܽ**ҪֻһһظдӲķʽ²ܱãijֹҪظʹʱΪ + +## new +κκܱ캯`new`****ʱִ + 1. һ¶úJSõģ򴴽һµObject󣩣 + 2. this󶨵 + 3. ִй캯еĴ루Ϊ¶ԣ + 4. ûзԶ¶returnصǷǶԶ¶󣬼ǸǶ + +```js +function foo(a) { + this.a = a; +} + +var bar = new foo( 2 ); +console.log( bar.a ); // 2 +``` + +### ˵ +* +```js +function foo() { + console.log( this.a ); +} + +var a = 2; +var o = { a: 3, foo: foo }; +var p = { a: 4 }; + +o.foo(); // 3 +(p.foo = o.foo)(); // 2p.foo = o.fooķֵĿ꺯ãԵλfoo()p.foo()o.foo() +``` + +* **ͷ**ʹĸthis򣬸**ʷ**`this` +```js +function foo() { + // һͷ + return (a) => { + // `this` here is lexically adopted from `foo()` + console.log( this.a ); + }; +} + +var obj1 = { + a: 2 +}; + +var obj2 = { + a: 3 +}; + +// foo()Ǽͷthis󶨵obj1 +var bar = foo.call( obj1 ); // foo.call( obj1 )ؼͷbarΪͷ +bar.call( obj2 ); // 2! ͷthis޷޸ģnewҲ +``` + +Ϊͼͷһģʽ + +```js +function foo() { + var self = this; // lexical capture of `this` + setTimeout( function(){ + console.log( self.a ); + }, 100 ); +} + +var obj = { + a: 2 +}; + +foo.call( obj ); // 2 +``` + +this󶨵Ȥ⣺ +[֪-arguments](https://www.zhihu.com/question/21466212) \ No newline at end of file diff --git "a/Cute-Article/article/62-2018\345\220\204\345\244\247\345\205\254\345\217\270\350\277\221\346\234\237\351\235\242\350\257\225\351\242\230.md" "b/Cute-Article/article/62-2018\345\220\204\345\244\247\345\205\254\345\217\270\350\277\221\346\234\237\351\235\242\350\257\225\351\242\230.md" new file mode 100644 index 00000000..2fadae20 --- /dev/null +++ "b/Cute-Article/article/62-2018\345\220\204\345\244\247\345\205\254\345\217\270\350\277\221\346\234\237\351\235\242\350\257\225\351\242\230.md" @@ -0,0 +1,469 @@ +> [ԭĵַ](https://segmentfault.com/a/1190000016789897) + + +## +* ʹùkoa2м +* koa-bodyԭ +* Լдм +* û漰Cluster +* pm2 +* master˵Ļpm2ô +* κMySQLͨ +* ReactڼԼ +* React-Router +* ·ɵĶ̬ģ +* ȾSSR +* ·ɵhistory +* Redux +* Reduxʵֶ֮ͨţʹͬ״̬νй +* ֮βָԵstateÿСԼ״̬֮仹һЩ״̬Ҫά˼ +* ʹùReduxм +* ν +* Httpͷ +* ƶ1px +* flex +* cssʽôֱ +* ΪʲôҪʹtransformΪʲôʹmarginLeft/Top +* ʹùwebpackЩpluginloader +* webpackIJôʵֵ +* dev-serverô +* ĿŻ +* ȡļôõ +* Ŀδȫ +* ôʵthis + +## +* reduxҪʲô +* ļϴϵ +* Կ +* promiseasyncʲô +* δ +* +* ܹ۲ģʽ +* нģʽ +* ۲ߺͶ-𣬸 +* reactŻ +* http2.0 +* ͨʲô +* http1.1ʱθtcp +* service worker +* css3position:sticky +* reduxмδ +* Promise쳣 +* position԰CSS3 +* ¼ +* ¼Լȱ +* Reactô¼ +* React¼ԭ +* this +* ǰôƹ· +* ʹ·ʱν +* Reactôݵļͱ仯 + +## ε +* react-routerôʵ·л +* react-routerǩͱǩʲô +* ǩĬ¼֮ʲôʵת +* ReactŻ +* ǰ·ּ +* import { Button } from 'antd'ʱֻbuttonģأô +* ʹimportʱwebpacknode_modulesʲô +* JS첽ķչԼȱ +* Httpĵм +* cookiecookieʹڵļֵ +* cookietokenheader棬Ϊʲôֻٳǰ +* cookiesessionЩ +* ReactDomṹ仯ڲЩ仯 +* Reactصʱ3textComponentcomposeComponentdomComponent͹ϵDomṹ仯ʱôdataı仯ô£ôȣµʱô +* keyҪǽһ⣬Ϊʲôindexػ棩 +* Redux첽ô +* Reduxмʲôܼ˵Ŀﻯ +* ﻯ˵IJʲô +* мôõstoreactionȻô +* stateôע뵽ģreducerʲôĹ +* koaresponse.sendresponse.roundedresponse.jsonʲô£Ϊʲôʶһjsonṹhtml +* koa-bodyparserôrequest +* webpackڣloaderpluginʲô +* ASTAbstract Syntax Tree﷨ +* ׿Activity֮ôݵ +* ׿4.06.0WebViewjsԵı仯 +* WebViewԭͨ +* ôûʹùApacheȷ + +## ͷ +* asyncawait⣬ڲԭ +* Promiseڲʵ +* +* λ⣨ԶλԶλȣ +* URLҳȫ +* tcp3 +* tcpһ㣨1 -> 2 · -> 3 (ip)-> 4 (tcp) -> 5 Ӧò(http) +* redux˼ +* reduxĹ +* connectĹ +* connectԭ +* webpack +* == ===ʲô== +* bindcallapply +* ˽ +* ԭǼ̳ +* Կ˽ + +## +* Linux 754 +* ðѡðŻ +* transformֱʹlefttopıλʲôȱ +* жǷл +* ܶص +* ʱ +* ES6еmapԭĶʲô +* ۲ߺͷ-ĵ +* react첽Ⱦĸ,Time Slicing Suspense +* 16.Xڵĸı +* 16.Xpropsıĸд +* ܴ +* ǰŻ +* pureComponentFunctionComponent +* JSX +* RNڰ׿IOS˵ +* RNΪʲôԭлƳԭbundle.js +* DOM +* һlocalStorage֤ݵʵЧ +* Promise.all() +* ܸ߽ +* sum(2, 3)ʵsum(2)(3)Ч +* reactŻ +* αȽ + +## ڲ +* JSԭ +* +* callapplybind +* ͽ +* ܸ첽 +* react +* Fiber +* ǰŻ +* DOMԱ +* reactеkey +* ״̬ +* cssxsrf +* http +* ĿӦݽṹ +* nativeṩʲôRN +* ϵŻ +* shouldComponentUpdateΪ˽ʲô +* νprops㼶 +* ǰôԪ +* webpack +* webpack +* õplugins +* pm2ô̹̹ҵô +* pm2ô̹ + +## +* +* ôȥ +* jsonpҪô +* AjaxҪʲôǰˣ +* CORS֮ӷʽɹĹ +* xsrf򹥻İȫô +* ʹAsyncעЩ +* Asyncжawait󣬿ôŻǷ +* PromiseAsyncʧܵʱʲô +* Redux״̬Reactܽ +* Reduxûװ +* reactڣõ +* Ӧʲô +* һĸ +* ôŻ첽... +* дreactЩϸڿŻ +* React¼ƣһ¼һϣ +* ¼Ҫʲô +* ǰ˿õЩģʽ +* React/ReduxЩõЩģʽ +* JSͷΪ֣ʲô +* JSջʲôõ֣ô +* һô֯CSSWebpack + +## ô +* С濪ҳ +* ReactӸ֮δֵ +* Emit¼ôҪʲô +* React߽ͨʲô +* һ飬ÿӶһidnameReactȾȫname +* ĸд +* мnameڣͨ첽ӿڻȡ +* Ⱦʱkeyʲôֵʹindexidûindex +* webpacksassҪЩloader +* cssҪЩloader +* ðjscsshtmlһļ +* divֱˮƽУflexԶλ +* Ԫؿ飬һһңм10 +* ¹̶мʵ +* [1, 2, 3, 4, 5][1, 2, 3, a, b, 5] +* ȡֵES5ES6 +* applycall +* ES5ES6ʲô +* someeveryfindfiltermapforEachʲô +* ȡÿηصֵһ +* 0-595-99 +* ҳ1buttonΰ¼ +* жbutton +* ҳһbuttonҰ¼JSԭDOM +* ѭʱindexǶ٣Ϊʲôô +* ҳһinputһpǩıinputpǩ͸ű仯δ +* inputĸ¼ʲôʱ򴥷 + +## Я +* ReactûһЩ +* ԱհĿΪʲôҪñհ +* дȥغ +* дƽ +* Promise; +* PromiseCallbackʲô +* React +* д㷨 + +## ϲ +* ES6µ +* Promise +* Promiseм״̬ +* ˵һ±հ +* React +* componentWillReceivePropsĴʲô +* React16.3ڵĸı +* ReactFilberܹ +* FilberȾ +* React߽ +* ֮ͨ +* ReduxôʵԴݣԭ +* React-Router汾 +* վSEOô +* HTTP״̬ +* 403301302ʲô +* صHTTPͷ +* HTTPS +* HTTPSôȫͨ +* ǰŻJSԭReact +* ûʲôŻ +* PWAʲô˽ +* ԰ȫʲô˽ +* ǩԭ +* ǰͨʹʲô +* RESTfulõMethod +* ¿ +* Access-Control-Allow-Originڷ +* csrfվô +* ǰ˺ͺô + +## Ұ +* localStoragecookieʲô +* CSSѡЩ +* ģͣԼ׼IEµ +* ʵָ߶Ӧ +* prototype͡proto +* _constructʲô +* newôʵֵ +* promiseľ裬Լȱ +* ʵH5ֻ˵ +* remflexroot em +* empx +* React +* ȥurlе# +* Redux״̬ͱصwindowʲô +* webpackgulpȱ +* ʵ첽 +* ʵַģڣ +* ǰŻ1js css2 ͼƬ3 Ԥأ 4 SSR 5 أ6 ؾ⣩ +* Դޣ6 +* base64Ϊʲôܣȱ +* webpͼƬļʽ +* koa2 +* Promiseʵֵ +* 첽󣬵Ͱ汾fetchεͰ汾 +* ajaxδ +* CORS +* jsonpΪʲô֧post +* ͬԴ +* ReactʹùһЩ +* Immuable +* reduxԭ +* ԭ +* μ̳ + +## ΢ҽ +* JSͣͺ͵ +* ArrayObject +* ͷֱ +* var a = {name: "ǰ˿"}; var b = a; a = nullôbʲô +* var a = {b: 1} +* var a = {b: {c: 1}} +* ջͶѵ +* ʱջͶѵ +* 10ݣȡһԪغ͵10Ԫصʱ +* ջͶѾô洢 +* ܱհԼհΪʲôû +* հʹó +* JSôʵ첽 +* 첽ִ +* Promise״̬ +* Async/Awaitôʵ +* PromisesetTimeoutִȺ +* JSΪʲôҪ΢ͺ +* Promise캯ͬ첽ִУthen +* -ĺ͹۲ģʽ +* JSִйзΪЩ׶ +* ʷthis +* ƽô̳ +* dz +* loadshʵԭ +* ES6letôʵֵ +* ReactsetStateʲô +* setStateΪʲôĬ첽 +* setStateʲôʱͬ +* Ϊʲô3ܳԺͳֺܶnativeRNܣDOM +* DOMҪʲô +* DOMʲôJS +* 304ʲô +* ʱHashôɵ +* ֵһα +* ʹwebpackʱһЩԶ +* webpackʲô +* abťaba˳baaα֤abaPromise.then +* nodeӿתʲôŻ +* nodeα֤ȶԣƽ +* RNûȼ +* RNļ +* RNʵһԭ +* RNԭԭRNʲôͬ +* ʲôǵҳĿ +* ĸҵ񳡾 +* Promise.allʵԭ + +## ¿ +* Promiseԣȱ +* Redux +* RNԭΪʲôͬʱڰ׿IOS +* RNεԭһЩ +* RNȱ +* 㷨Ϳԭ +* Ѻջ +* ܱհ +* հĺʲô +* ģ +* HTTPHTTPS +* HTTPSļܹ +* SSLTLS +* DNS +* JSļ̳з +* +* cookieΪ˽ʲô +* cookielocalStorage +* ν +* ǰŻ + +## +* ʹcanvasͼʱ֯ͨ +* formDataԭajaxʲô +* ±ύformDataʲôϵ +* redux +* ruduxȫֹʲôݿɿءӦ +* RNԭͨ +* MVPô֯ +* 첽 +* promiseʵthen +* koa2мԭ +* õм +* ôͳһ״̬ +* ζ·ýŻ +* nodeļȼ +* npm2npm3+ʲô +* +* knexݿӦص +* 첽 +* δ쳣 +* Ŀιģ +* ǰŻ +* JS̳з +* жһDz +* abν +* ¼ί +*
  • ǩɵDomṹһ +* +* domת +* ܵҳӦúͶҳӦ +* redux״̬Ĺ +* localstorageAPI + +## Ģ +* html廯 +* ͵ +* Ահ +* бհʹó +* thisԭ +* ʹԭĺô +* react˼· +* ΪʲôDOMʵDOMܺ +* reactͨŷʽ +* reduxĹ +* reduxȫֶ֮ +* Reduxݻ˼· +* ۲Ŀʵʳ +* ĿʹóԼ˽ +* ջ + +## +* react +* reactŻ +* ԭ¼ƳΪʲôڴй¶ +* Щطڴй¶ +* setIntervalҪעĵ +* ʱΪʲôDzȷ +* setTimeout(1)setTimeout(2)֮ +* ܺ΢ +* promisethenִʲô +* pureComponet +* Function Component +* React +* propsstate +* react context +* classES5Լ +* ܼͷͨ +* definePropertyʲôʱҪõ +* for..in object.keys +* ܱհʹó +* ʹñհȨʹó +* getpostʲô + +## ٷֵ +* React15/16.x +* ȾrenderЩʲô +* ЩᴥreactȾ +* statepropsµڷֱʲô +* setStateͬ첽 +* ״̬ +* Redux +* ES6Ĺ +* letconstԼvar +* dz +* ܼͷthis +* Promisethen +* ܿ +* 㷨ǰKԪ + +## +* reactȱ +* ʹù⣬ν +* reactʲôúʽҳȾ +* JSʲôʽ(Ǻʽ) +* koaԭΪʲôҪkoa(expresskoaԱ) +* ʹõkoaм +* ES6ʹõ﷨ +* Promise async/await callback +* Promiseûн첽⣨promiseǿĵط +* PromisesetTimeoutEvent Loop +* ̵̺߳һnodeʵһ̣nodeǵ̣߳ͨ¼ѭʵ첽 +* DFS +* ¹۲ģʽ +* ۲ģʽʹõݽṹ(߱˳ һlist) \ No newline at end of file diff --git "a/Cute-Article/article/63-ES6\346\261\207\346\200\273.md" "b/Cute-Article/article/63-ES6\346\261\207\346\200\273.md" new file mode 100644 index 00000000..07c02f50 --- /dev/null +++ "b/Cute-Article/article/63-ES6\346\261\207\346\200\273.md" @@ -0,0 +1,3297 @@ +## 1. ES6 + +### 1.1 let 和 const命令 + +在ES6中,我们通常实用 `let` 表示**变量**,`const` 表示**常量**,并且 `let` 和 `const` 都是**块级作用域**,且在**当前作用域有效**不能重复声明。 + +#### 1.1.1 let 命令 +`let` 命令的用法和 `var` 相似,但是 `let` 只在所在代码块内有效。 +**基础用法**: +```js +{ + let a = 1; + let b = 2; +} +``` + +并且 `let` 有以下特点: + +* **不存在变量提升:** +在ES6之前,我们 `var` 声明一个**变量**一个**函数**,都会伴随着变量提升的问题,导致实际开发过程经常出现一些逻辑上的疑惑,按照一般思维习惯,变量都是需要先声明后使用。 +```js +// var +console.log(v1); // undefined +var v1 = 2; +// 由于变量提升 代码实际如下 +var v1; +console.log(v1) +v1 = 2; + +// let +console.log(v2); // ReferenceError +let v2 = 2; +``` + +* **不允许重复声明:** +`let` 和 `const` 在**相同作用域下**,都**不能重复声明同一变量**,并且**不能在函数内重新声明参数**。 +```js +// 1. 不能重复声明同一变量 +// 报错 +function f1 (){ + let a = 1; + var a = 2; +} +// 报错 +function f2 (){ + let a = 1; + let a = 2; +} + +// 2. 不能在函数内重新声明参数 +// 报错 +function f3 (a1){ + let a1; +} +// 不报错 +function f4 (a2){ + { + let a2 + } +} +``` + +#### 1.1.2 const 命令 +`const` 声明一个**只读**的**常量**。 +**基础用法**: +```js +const PI = 3.1415926; +console.log(PI); // 3.1415926 + +``` +**注意点**: +* `const` 声明后,无法修改值; +```js +const PI = 3.1415926; +PI = 3; +// TypeError: Assignment to constant variable. +``` +* `const` 声明时,必须赋值; +```js +const a ; +// SyntaxError: Missing initializer in const declaration. +``` +* `const` 声明的常量,`let` 不能重复声明; +```js +const PI = 3.1415926; +let PI = 0; +// Uncaught SyntaxError: Identifier 'PI' has already been declared +``` + +[⬆ 返回目录](#二目录) + +### 1.2 变量的解构赋值 +**解构赋值概念**:在ES6中,直接从数组和对象中取值,按照对应位置,赋值给变量的操作。 + +#### 1.2.1 数组 +**基础用法**: +```js +// ES6 之前 +let a = 1; +let b = 2; + +// ES6 之后 +let [a, b] = [1, 2]; +``` + +本质上,只要等号两边模式一致,左边变量即可获取右边对应位置的值,更多用法: + +```js +let [a, [[b], c]] = [1, [[2], 3]]; +console.log(a, b, c); // 1, 2, 3 + +let [ , , c] = [1, 2, 3]; +console.log(c); // 3 + +let [a, , c] = [1, 2, 3]; +console.log(a,c); // 1, 3 + +let [a, ...b] = [1, 2, 3]; +console.log(a,b); // 1, [2,3] + +let [a, b, ..c.] = [1]; +console.log(a, b, c); // 1, undefined, [] +``` + +**注意点**: +* 如果解构不成功,变量的值就等于`undefined`。 +```js +let [a] = []; // a => undefined +let [a, b] = [1]; // a => 1 , b => undefined +``` +* 当左边模式多于右边,也可以解构成功。 +```js +let [a, b] = [1, 2, 3]; +console.log(a, b); // 1, 2 +``` +* 两边模式不同,报错。 +```js +let [a] = 1; +let [a] = false; +let [a] = NaN; +let [a] = undefined; +let [a] = null; +let [a] = {}; +``` + +**指定解构的默认值**: +**基础用法**: +```js +let [a = 1] = []; // a => 1 +let [a, b = 2] = [a]; // a => 1 , b => 2 +``` +特殊情况: +```js +let [a = 1] = [undefined]; // a => 1 +let [a = 1] = [null]; // a => null +``` +右边模式对应的值,必须严格等于`undefined`,默认值才能生效,而`null`不严格等于`undefined`。 + +#### 1.2.2 对象的解构赋值 +与数组解构不同的是,对象解构**不需要严格按照顺序取值**,而只要按照**变量名**去取对应**属性名**的值,若取不到对应**属性名**的值,则为`undefined` 。 + +**基础用法**: +```js +let {a, b} = {a:1, b:2}; // a => 1 , b => 2 +let {a, b} = {a:2, b:1}; // a => 2 , b => 1 +let {a} = {a:3, b:2, c:1};// a => 3 +let {a} = {b:2, c:1}; // a => undefined +``` + +**注意点**: +* 若**变量名**和**属性名**不一致,则需要修改名称。 +```js +let {a:b} = {a:1, c:2}; +// error: a is not defined +// b => 1 +``` +对象的解构赋值的内部机制,是先找到同名属性,然后再赋给对应的变量。真正被赋值的是后者,而不是前者。 +上面代码中,`a` 是匹配的模式,`b`才是变量。真正被赋值的是变量`b`,而不是模式`a`。 + +* 对象解构也支持**嵌套解构**。 +```js +let obj = { + a:[ 1, { b: 2}] +}; +let {a, a: [c, {b}]} = obj; +// a=>[1, {b: 2}], b => 2, c => 1 +``` + +**指定解构的默认值**: +```js +let {a=1} = {}; // a => 1 +let {a, b=1} = {a:2}; // a => 2, b => 1 + +let {a:b=3} = {}; // b => 3 +let {a:b=3} = {a:4}; // b = >4 +// a是模式,b是变量 牢记 + +let {a=1} = {a:undefined}; // a => 1 +let {a=1} = {a:null}; // a => null +// 因为null与undefined不严格相等,所以赋值有效 +// 导致默认值1不会生效。 +``` + +#### 1.2.3 字符串的解构赋值 +字符串的解构赋值中,字符串被转换成了一个**类似数组的对象**。 +**基础用法**: +```js +const [a, b, c, d, e] = 'hello'; +a // "h" +b // "e" +c // "l" +d // "l" +e // "o" + +let {length:len} = 'hello';// len => 5 +``` + +#### 1.2.4 数值和布尔值的解构赋值 +解构赋值的规则是,**只要等号右边的值不是对象或数组,就先将其转为对象**。由于`undefined`和`null`**无法转为对象**,所以对它们进行解构赋值,都会报错。 +```js +// 数值和布尔值的包装对象都有toString属性 +let {toString: s} = 123; +s === Number.prototype.toString // true +let {toString: s} = true; +s === Boolean.prototype.toString // true + +let { prop: x } = undefined; // TypeError +let { prop: y } = null; // TypeError +``` + +#### 1.2.5 函数参数的解构赋值 +**基础用法**: +```js +function fun ([a, b]){ + return a + b; +} +fun ([1, 2]); // 3 +``` +**指定默认值的解构**: +```js +function fun ({a=0, b=0} = {}){ + return [a, b]; +} +fun ({a:1, b:2}); // [1, 2] +fun ({a:1}); // [1, 0] +fun ({}); // [0, 0] +fun (); // [0, 0] + +function fun ({a, b} = {a:0, b:0}){ + return [a, b]; +} +fun ({a:1, b:2}); // [1, 2] +fun ({a:1}); // [1, undefined] +fun ({}); // [undefined, undefined] +fun (); // [0, 0] +``` + +#### 1.2.6 应用 +* **交换变量的值**: +```js +let a = 1,b = 2; +[a, b] = [b, a]; // a =>2 , b => 1 +``` + +* **函数返回多个值**: +```js +// 返回一个数组 +function f (){ + return [1, 2, 3]; +} +let [a, b, c] = f(); // a=>1, b=>2, c=>3 + +// 返回一个对象 +function f (){ + return {a:1, b:2}; +} +let {a, b} = f(); // a=>1, b=>2 +``` + +* **快速对应参数**: +快速的将一组参数与变量名对应。 +```js +function f([a, b, c]) {...} +f([1, 2, 3]); + +function f({a, b, c}) {...} +f({b:2, c:3, a:1}); +``` + +* **提取JSON数据**: +```js +let json = { + name : 'leo', + age: 18 +} +let {name, age} = json; +console.log(name,age); // leo, 18 +``` + +* **遍历Map结构**: +```js +const m = new Map(); +m.set('a':1); +m.set('b':2); +for ([k, v] of m){ + console.log(k + ' : ' + v); +} +// 获取键名 +for (let [k] of m){...} +// 获取键值 +for (let [,k] of m){...} +``` + +* **输入模块的指定方法**: +用于**按需加载**模块中需要用到的方法。 +```js +const {log, sin, cos} = require('math'); + +``` + +[⬆ 返回目录](#二目录) + + +### 1.3 字符串的拓展 +#### 1.3.1 includes(),startsWith(),endsWith() +在我们判断字符串是否包含另一个字符串时,ES6之前,我们只有`typeof`方法,ES6之后我们又多了三种方法: +* **includes()**:返回**布尔值**,表示**是否找到参数字符串**。 +* **startsWith()**:返回**布尔值**,表示参数字符串是否在原字符串的**头部**。 +* **endsWith()**:返回**布尔值**,表示参数字符串是否在原字符串的**尾部**。 +```js +let a = 'hello leo'; +a.startsWith('leo'); // false +a.endsWith('o'); // true +a.includes('lo'); // true +``` +并且这三个方法都支持第二个参数,表示起始搜索的位置。 +```js +let a = 'hello leo'; +a.startsWith('leo',1); // false +a.endsWith('o',5); // true +a.includes('lo',6); // false +``` +`endsWith` 是针对前 `n` 个字符,而其他两个是针对从第`n`个位置直到结束。 + +#### 1.3.2 repeat() +`repeat`方法返回一个新字符串,表示将原字符串重复`n`次。 +**基础用法**: +```js +'ab'.repeat(3); // 'ababab' +'ab'.repeat(0); // '' +``` +**特殊用法**: +* 参数为`小数`,则取整 +```js +'ab'.repeat(2.3); // 'abab' +``` +* 参数为`负数`或`Infinity`,则报错 +```js +'ab'.repeat(-1); // RangeError +'ab'.repeat(Infinity); // RangeError +``` +* 参数为`0到-1的小数`或`NaN`,则取0 +```js +'ab'.repeat(-0.5); // '' +'ab'.repeat(NaN); // '' +``` +* 参数为`字符串`,则转成`数字` +```js +'ab'.repeat('ab'); // '' +'ab'.repeat('3'); // 'ababab' +``` + +####1.3.3 padStart(),padEnd() +用于将字符串**头部**或**尾部**补全长度,`padStart()`为**头部补全**,`padEnd()`为**尾部补全**。 +这两个方法接收**2个**参数,第一个指定**字符串最小长度**,第二个**用于补全的字符串**。 +**基础用法** : +```js +'x'.padStart(5, 'ab'); // 'ababx' +'x'.padEnd(5, 'ab'); // 'xabab' +``` +**特殊用法**: +* 原字符串长度,大于或等于指定最小长度,则返回原字符串。 +```js +'xyzabc'.padStart(5, 'ab'); // 'xyzabc' +``` +* 用来补全的字符串长度和原字符串长度之和,超过指定最小长度,则截去超出部分的补全字符串。 +```js +'ab'.padStart(5,'012345'); // "012ab" +``` +* 省略第二个参数,则用`空格`补全。 +```js +'x'.padStart(4); // ' x' +'x'.endStart(4); // 'x ' +``` +#### 1.3.4 模版字符串 +用于拼接字符串,ES6之前: +```js +let a = 'abc' + + 'def' + + 'ghi'; +``` +ES6之后: +```js +let a = ` + abc + def + ghi +` +``` +**拼接变量**: +在**反引号(\`)**中使用`${}`包裹变量或方法。 +```js +// ES6之前 +let a = 'abc' + v1 + 'def'; + +// ES6之后 +let a = `abc${v1}def` +``` + +[⬆ 返回目录](#二目录) + + +### 1.4 正则的拓展 +#### 1.4.1 介绍 +在ES5中有两种情况。 +* 参数是**字符串**,则第二个参数为正则表达式的修饰符。 +```js +let a = new RegExp('abc', 'i'); +// 等价于 +let a = /abx/i; +``` +* 参数是**正则表达式**,返回一个原表达式的拷贝,且不能有第二个参数,否则报错。 +```js +let a = new RegExp(/abc/i); +//等价于 +let a = /abx/i; + +let a = new RegExp(/abc/, 'i'); +// Uncaught TypeError +``` +ES6中使用: +第一个参数是正则对象,第二个是指定修饰符,如果第一个参数已经有修饰符,则会被第二个参数覆盖。 +```js +new RegExp(/abc/ig, 'i'); +``` + +#### 1.4.2 字符串的正则方法 +常用的四种方法:`match()`、`replace()`、`search()`和`split()`。 + +#### 1.4.3 u修饰符 +添加`u`修饰符,是为了处理大于`uFFFF`的Unicode字符,即正确处理四个字节的UTF-16编码。 +```js +/^\uD83D/u.test('\uD83D\uDC2A'); // false +/^\uD83D/.test('\uD83D\uDC2A'); // true +``` +由于ES5之前不支持四个字节UTF-16编码,会识别为两个字符,导致第二行输出`true`,加入`u`修饰符后ES6就会识别为一个字符,所以输出`false`。 + +**注意:** +加上`u`修饰符后,会改变下面正则表达式的行为: +* (1)点字符 +点字符(`.`)在正则中表示除了**换行符**以外的任意单个字符。对于码点大于`0xFFFF`的Unicode字符,点字符不能识别,必须加上`u`修饰符。 +```js +var a = "𠮷"; +/^.$/.test(a); // false +/^.$/u.test(a); // true +``` +* (2)Unicode字符表示法 +使用ES6新增的大括号表示Unicode字符时,必须在表达式添加`u`修饰符,才能识别大括号。 +```js +/\u{61}/.test('a'); // false +/\u{61}/u.test('a'); // true +/\u{20BB7}/u.test('𠮷'); // true +``` +* (3)量词 +使用`u`修饰符后,所有量词都会正确识别码点大于`0xFFFF`的 Unicode 字符。 +```js +/a{2}/.test('aa'); // true +/a{2}/u.test('aa'); // true +/𠮷{2}/.test('𠮷𠮷'); // false +/𠮷{2}/u.test('𠮷𠮷'); // true +``` +* (4)i修饰符 +不加`u`修饰符,就无法识别非规范的`K`字符。 +```js +/[a-z]/i.test('\u212A') // false +/[a-z]/iu.test('\u212A') // true +``` + +**检查是否设置`u`修饰符:** +使用`unicode`属性。 +```js +const a = /hello/; +const b = /hello/u; + +a.unicode // false +b.unicode // true +``` + +#### 1.4.4 y修饰符 +`y`修饰符与`g`修饰符类似,也是全局匹配,后一次匹配都是从上一次匹配成功的下一个位置开始。区别在于,`g`修饰符**只要**剩余位置中存在匹配即可,而`y`修饰符是必须从**剩余第一个**开始。 +```js +var s = 'aaa_aa_a'; +var r1 = /a+/g; +var r2 = /a+/y; + +r1.exec(s) // ["aaa"] +r2.exec(s) // ["aaa"] + +r1.exec(s) // ["aa"] 剩余 '_aa_a' +r2.exec(s) // null +``` +**`lastIndex`属性**: +指定匹配的开始位置: +```js +const a = /a/y; +a.lastIndex = 2; // 从2号位置开始匹配 +a.exec('wahaha'); // null +a.lastIndex = 3; // 从3号位置开始匹配 +let c = a.exec('wahaha'); +c.index; // 3 +a.lastIndex; // 4 +``` +**返回多个匹配**: +一个`y`修饰符对`match`方法只能返回第一个匹配,与`g`修饰符搭配能返回所有匹配。 +```js +'a1a2a3'.match(/a\d/y); // ["a1"] +'a1a2a3'.match(/a\d/gy); // ["a1", "a2", "a3"] +``` +**检查是否使用`y`修饰符**: +使用`sticky`属性检查。 +```js +const a = /hello\d/y; +a.sticky; // true +``` + +#### 1.4.5 flags属性 +`flags`属性返回所有正则表达式的修饰符。 +```js +/abc/ig.flags; // 'gi' +``` + + +[⬆ 返回目录](#二目录) + + +### 1.5 数值的拓展 +#### 1.5.1 Number.isFinite(), Number.isNaN() +`Number.isFinite()` 用于检查一个数值是否是有限的,即不是`Infinity`,若参数不是`Number`类型,则一律返回`false` 。 +```js +Number.isFinite(10); // true +Number.isFinite(0.5); // true +Number.isFinite(NaN); // false +Number.isFinite(Infinity); // false +Number.isFinite(-Infinity); // false +Number.isFinite('leo'); // false +Number.isFinite('15'); // false +Number.isFinite(true); // false +Number.isFinite(Math.random()); // true +``` + +`Number.isNaN()`用于检查是否是`NaN`,若参数不是`NaN`,则一律返回`false`。 +```js +Number.isNaN(NaN); // true +Number.isNaN(10); // false +Number.isNaN('10'); // false +Number.isNaN(true); // false +Number.isNaN(5/NaN); // true +Number.isNaN('true' / 0); // true +Number.isNaN('true' / 'true'); // true +``` + +**区别**: +与传统全局的`isFinite()`和`isNaN()`方法的区别,传统的这两个方法,是先将参数转换成**数值**,再判断。 +而ES6新增的这两个方法则只对**数值**有效, `Number.isFinite()`对于**非数值**一律返回`false`,` Number.isNaN()`只有对于`NaN`才返回`true`,其他一律返回`false`。 +```js +isFinite(25); // true +isFinite("25"); // true +Number.isFinite(25); // true +Number.isFinite("25"); // false + +isNaN(NaN); // true +isNaN("NaN"); // true +Number.isNaN(NaN); // true +Number.isNaN("NaN"); // false +``` + +#### 1.5.2 Number.parseInt(), Number.parseFloat() +这两个方法与全局方法`parseInt()`和`parseFloat()`一致,目的是逐步**减少全局性的方法**,让**语言更模块化**。 +```js +parseInt('12.34'); // 12 +parseFloat('123.45#'); // 123.45 + +Number.parseInt('12.34'); // 12 +Number.parseFloat('123.45#'); // 123.45 + +Number.parseInt === parseInt; // true +Number.parseFloat === parseFloat; // true +``` + +#### 1.5.3 Number.isInteger() +用来判断一个数值是否是整数,若参数不是数值,则返回`false`。 +```js +Number.isInteger(10); // true +Number.isInteger(10.0); // true +Number.isInteger(10.1); // false +``` + +#### 1.5.4 Math对象的拓展 +ES6新增17个数学相关的**静态方法**,只能在**Math对象**上调用。 +* **Math.trunc**: +用来去除小数的小数部分,**返回整数部分**。 +若参数为**非数值**,则**先转为数值**。 +若参数为**空值**或**无法截取整数的值**,则返回**NaN**。 +```js +// 正常使用 +Math.trunc(1.1); // 1 +Math.trunc(1.9); // 1 +Math.trunc(-1.1); // -1 +Math.trunc(-1.9); // -1 +Math.trunc(-0.1234); // -0 + +// 参数为非数值 +Math.trunc('11.22'); // 11 +Math.trunc(true); // 1 +Math.trunc(false); // 0 +Math.trunc(null); // 0 + +// 参数为空和无法取整 +Math.trunc(NaN); // NaN +Math.trunc('leo'); // NaN +Math.trunc(); // NaN +Math.trunc(undefined); // NaN +``` +**ES5实现**: +```js +Math.trunc = Math.trunc || function(x){ + return x < 0 ? Math.ceil(x) : Math.floor(x); +} +``` + +* **Math.sign()**: +判断一个数是**正数**、**负数**还**是零**,对于非数值,会先转成**数值**。 +返回值: + * 参数为正数, 返回 +1 + * 参数为负数, 返回 -1 + * 参数为0, 返回 0 + * 参数为-0, 返回 -0 + * 参数为其他值, 返回 NaN +```js +Math.sign(-1); // -1 +Math.sign(1); // +1 +Math.sign(0); // 0 +Math.sign(-0); // -0 +Math.sign(NaN); // NaN + +Math.sign(''); // 0 +Math.sign(true); // +1 +Math.sign(false);// 0 +Math.sign(null); // 0 +Math.sign('9'); // +1 +Math.sign('leo');// NaN +Math.sign(); // NaN +Math.sign(undefined); // NaN +``` + +**ES5实现** +```js +Math.sign = Math.sign || function (x){ + x = +x; + if (x === 0 || isNaN(x)){ + return x; + } + return x > 0 ? 1: -1; +} +``` + +* **Math.cbrt()**: +用来计算一个数的立方根,若参数为非数值则先转成数值。 +```js +Math.cbrt(-1); // -1 +Math.cbrt(0); // 0 +Math.cbrt(1); // 1 +Math.cbrt(2); // 1.2599210498 + +Math.cbrt('1'); // 1 +Math.cbrt('leo'); // NaN +``` +**ES5实现** +```js +Math.cbrt = Math.cbrt || function (x){ + var a = Math.pow(Math.abs(x), 1/3); + return x < 0 ? -y : y; +} +``` + +* **Math.clz32()**: +用于返回一个数的 32 位无符号整数形式有多少个前导 0。 +```js +Math.clz32(0) // 32 +Math.clz32(1) // 31 +Math.clz32(1000) // 22 +Math.clz32(0b01000000000000000000000000000000) // 1 +Math.clz32(0b00100000000000000000000000000000) // 2 +``` + +* **Math.imul()**: +用于返回两个数以 32 位带符号整数形式相乘的结果,返回的也是一个 32 位的带符号整数。 +```js +Math.imul(2, 4) // 8 +Math.imul(-1, 8) // -8 +Math.imul(-2, -2) // 4 +``` + +* **Math.fround()**: +用来返回一个数的**2位单精度浮点数**形式。 +```js +Math.fround(0) // 0 +Math.fround(1) // 1 +Math.fround(2 ** 24 - 1) // 16777215 +``` + +* **Math.hypot()**: +用来返回所有参数的平方和的**平方根**。 +```js +Math.hypot(3, 4); // 5 +Math.hypot(3, 4, 5); // 7.0710678118654755 +Math.hypot(); // 0 +Math.hypot(NaN); // NaN +Math.hypot(3, 4, 'foo'); // NaN +Math.hypot(3, 4, '5'); // 7.0710678118654755 +Math.hypot(-3); // 3 +``` + +* **Math.expm1()**: +用来返回` ex - 1`,即`Math.exp(x) - 1`。 +```js +Math.expm1(-1) // -0.6321205588285577 +Math.expm1(0) // 0 +Math.expm1(1) // 1.718281828459045 +``` +**ES5实现** +```js +Math.expm1 = Math.expm1 || function(x) { + return Math.exp(x) - 1; +}; +``` + +* **Math.log1p()**: +用来返回`1 + x`的自然对数,即`Math.log(1 + x)`。如果x小于`-1`,返回`NaN`。 +```js +Math.log1p(1) // 0.6931471805599453 +Math.log1p(0) // 0 +Math.log1p(-1) // -Infinity +Math.log1p(-2) // NaN +``` +**ES5实现** +```js +Math.log1p = Math.log1p || function(x) { + return Math.log(1 + x); +}; +``` + +* **Math.log10()**: +用来返回以 `10 `为底的`x的对数`。如果x小于 0,则返回 `NaN`。 +```js +Math.log10(2) // 0.3010299956639812 +Math.log10(1) // 0 +Math.log10(0) // -Infinity +Math.log10(-2) // NaN +Math.log10(100000) // 5 +``` +**ES5实现** +```js +Math.log10 = Math.log10 || function(x) { + return Math.log(x) / Math.LN10; +}; +``` + +* **Math.log2()**: +用来返回以 `2` 为底的`x的对数`。如果`x`小于` 0`,则返回 `NaN`。 +```js +Math.log2(3) // 1.584962500721156 +Math.log2(2) // 1 +Math.log2(1) // 0 +Math.log2(0) // -Infinity +Math.log2(-2) // NaN +Math.log2(1024) // 10 +Math.log2(1 << 29) // 29 +``` +**ES5实现** +```js +Math.log2 = Math.log2 || function(x) { + return Math.log(x) / Math.LN2; +}; +``` +* **双曲函数方法**: + * `Math.sinh(x)` 返回x的**双曲正弦**(hyperbolic sine) + * `Math.cosh(x)` 返回x的**双曲余弦**(hyperbolic cosine) + * `Math.tanh(x)` 返回x的**双曲正切**(hyperbolic tangent) + * `Math.asinh(x)` 返回x的**反双曲正弦**(inverse hyperbolic sine) + * `Math.acosh(x)` 返回x的**反双曲余弦**(inverse hyperbolic cosine) + * `Math.atanh(x)` 返回x的**反双曲正切**(inverse hyperbolic tangent) + +#### 1.5.5 指数运算符 +新增的指数运算符(`**`): +```js +2 ** 2; // 4 +2 ** 3; // 8 + +2 ** 3 ** 2; // 相当于 2 ** (3 ** 2); 返回 512 +``` +指数运算符(`**`)与`Math.pow`的实现不相同,对于特别大的运算结果,两者会有细微的差异。 +```js +Math.pow(99, 99) +// 3.697296376497263e+197 + +99 ** 99 +// 3.697296376497268e+197 +``` + + +[⬆ 返回目录](#二目录) + + +### 1.6 函数的拓展 +#### 1.6.1 参数默认值 +```js +// ES6 之前 +function f(a, b){ + b = b || 'leo'; + console.log(a, b); +} + +// ES6 之后 +function f(a, b='leo'){ + console.log(a, b); +} + +f('hi'); // hi leo +f('hi', 'jack'); // hi jack +f('hi', ''); // hi leo +``` +**注意**: +* 参数变量是默认声明的,不能用`let`和`const`再次声明: +```js +function f (a = 1){ + let a = 2; // error +} +``` +* 使用参数默认值时,参数名不能相同: +```js +function f (a, a, b){ ... }; // 不报错 +function f (a, a, b = 1){ ... }; // 报错 +``` + +**与解构赋值默认值结合使用**: +```js +function f ({a, b=1}){ + console.log(a,b) +}; +f({}); // undefined 1 +f({a:2}); // 2 1 +f({a:2, b:3}); // 2 3 +f(); // 报错 + +function f ({a, b = 1} = {}){ + console.log(a, b) +} +f(); // undefined 1 +``` + +**尾参数定义默认值**: +通常在尾参数定义默认值,便于观察参数,并且非尾参数无法省略。 +```js +function f (a=1,b){ + return [a, b]; +} +f(); // [1, undefined] +f(2); // [2, undefined] +f(,2); // 报错 + +f(undefined, 2); // [1, 2] + +function f (a, b=1, c){ + return [a, b, c]; +} +f(); // [undefined, 1, undefined] +f(1); // [1,1,undefined] +f(1, ,2); // 报错 +f(1,undefined,2); // [1,1,2] +``` +在给参数传递默认值时,传入`undefined`会触发默认值,传入`null`不会触发。 +```js +function f (a = 1, b = 2){ + console.log(a, b); +} +f(undefined, null); // 1 null +``` + +**函数的length属性**: +`length`属性将返回,没有指定默认值的参数数量,并且rest参数不计入`length`属性。 +```js +function f1 (a){...}; +function f2 (a=1){...}; +function f3 (a, b=2){...}; +function f4 (...a){...}; +function f5 (a,b,...c){...}; + +f1.length; // 1 +f2.length; // 0 +f3.length; // 1 +f4.length; // 0 +f5.length; // 2 +``` + +#### 1.6.2 rest 参数 +`rest`参数形式为(`...变量名`),其值为一个数组,用于获取函数多余参数。 +```js +function f (a, ...b){ + console.log(a, b); +} +f(1,2,3,4); // 1 [2, 3, 4] +``` +**注意**: +* `rest`参数只能放在最后一个,否则报错: +```js +function f(a, ...b, c){...}; // 报错 +``` +* 函数的`length`属性不包含`rest`参数。 +```js +function f1 (a){...}; +function f2 (a,...b){...}; +f1(1); // 1 +f2(1,2); // 1 +``` + +#### 1.6.3 name 属性 +用于返回该函数的函数名。 +```js +function f (){...}; +f.name; // f + +const f = function g(){...}; +f.name; // g +``` + +#### 1.6.4 箭头函数 +使用“箭头”(`=>`)定义函数。 +**基础使用**: +```js +// 有1个参数 +let f = v => v; +// 等同于 +let f = function (v){return v}; + +// 有多个参数 +let f = (v, i) => {return v + i}; +// 等同于 +let f = function (v, i){return v + i}; + +// 没参数 +let f = () => 1; +// 等同于 +let f = function (){return 1}; +``` + +**箭头函数与变量结构结合使用**: +```js +// 正常函数写法 +function f (p) { + return p.a + ':' + p.b; +} + +// 箭头函数写法 +let f = ({a, b}) => a + ':' + b; +``` + +**简化回调函数**: +```js +// 正常函数写法 +[1, 2, 3].map(function (x){ + return x * x; +}) + + +// 箭头函数写法 +[1, 2, 3].map(x => x * x); +``` + +**箭头函数与rest参数结合**: +```js +let f = (...n) => n; +f(1, 2, 3); // [1, 2, 3] +``` + +**注意点**: +* 1.箭头函数内的`this`**总是**指向**定义时所在的对象**,而不是调用时。 +* 2.箭头函数不能当做**构造函数**,即不能用`new`命令,否则报错。 +* 3.箭头函数不存在`arguments`对象,即不能使用,可以使用`rest`参数代替。 +* 4.箭头函数不能使用`yield`命令,即不能用作**Generator**函数。 + +**不适用场景**: +* 1.在定义函数方法,且该方法内部包含`this`。 +```js +const obj = { + a:9, + b: () => { + this.a --; + } +} +``` +上述`b`如果是**普通函数**,函数内部的`this`指向`obj`,但是如果是箭头函数,则`this`会指向**全局**,不是预期结果。 + +* 2.需要动态`this`时。 +```js +let b = document.getElementById('myID'); +b.addEventListener('click', ()=>{ + this.classList.toggle('on'); +}) +``` +上诉按钮点击会报错,因为`b`监听的箭头函数中,`this`是全局对象,若改成**普通函数**,`this`就会指向被点击的按钮对象。 + +#### 1.6.5 双冒号运算符 +双冒号暂时是一个提案,用于解决一些不适用的场合,取代`call`、`apply`、`bind`调用。 +双冒号运算符(`::`)的左边是一个**对象**,右边是一个**函数**。该运算符会自动将左边的对象,作为上下文环境(即`this`对象),绑定到右边函数上。 +```js +f::b; +// 等同于 +b.bind(f); + +f::b(...arguments); +// 等同于 +b.apply(f, arguments); +``` +若双冒号左边为空,右边是一个对象的方法,则等于将该方法绑定到该对象上。 +```js +let f = a::a.b; +// 等同于 +let f = ::a.b; +``` + +[⬆ 返回目录](#二目录) + + +### 1.7 数组的拓展 +#### 1.7.1 拓展运算符 +拓展运算符使用(`...`),类似`rest`参数的逆运算,将数组转为用(`,`)分隔的参数序列。 +```js +console.log(...[1, 2, 3]); // 1 2 3 +console.log(1, ...[2,3], 4); // 1 2 3 4 +``` +拓展运算符主要使用在函数调用。 +```js +function f (a, b){ + console.log(a, b); +} +f(...[1, 2]); // 1 2 + +function g (a, b, c, d, e){ + console.log(a, b, c, d, e); +} +g(0, ...[1, 2], 3, ...[4]); // 0 1 2 3 4 +``` +**若拓展运算符后面是个空数组,则不产生效果**。 +```js +[...[], 1]; // 1 +``` + +**替代apply方法** +```js +// ES6之前 +function f(a, b, c){...}; +var a = [1, 2, 3]; +f.apply(null, a); + +// ES6之后 +function f(a, b, c){...}; +let a = [1, 2, 3]; +f(...a); + +// ES6之前 +Math.max.apply(null, [3,2,6]); + +// ES6之后 +Math.max(...[3,2,6]); +``` + +**拓展运算符的运用** +* **(1)复制数组**: +通常我们直接复制数组时,只是浅拷贝,如果要实现深拷贝,可以使用拓展运算符。 +```js +// 通常情况 浅拷贝 +let a1 = [1, 2]; +let a2 = a1; +a2[0] = 3; +console.log(a1,a2); // [3,2] [3,2] + +// 拓展运算符 深拷贝 +let a1 = [1, 2]; +let a2 = [...a1]; +// let [...a2] = a1; // 作用相同 +a2[0] = 3; +console.log(a1,a2); // [1,2] [3,2] +``` +* **(2)合并数组**: +注意,这里合并数组,只是浅拷贝。 +```js +let a1 = [1,2]; +let a2 = [3]; +let a3 = [4,5]; + +// ES5 +let a4 = a1.concat(a2, a3); + +// ES6 +let a5 = [...a1, ...a2, ...a3]; + +a4[0] === a1[0]; // true +a5[0] === a1[0]; // true +``` +* **(3)与解构赋值结合**: +与解构赋值结合生成数组,但是使用拓展运算符需要放到参数最后一个,否则报错。 +```js +let [a, ...b] = [1, 2, 3, 4]; +// a => 1 b => [2,3,4] + +let [a, ...b] = []; +// a => undefined b => [] + +let [a, ...b] = ["abc"]; +// a => "abc" b => [] +``` + +#### 1.7.2 Array.from() +将 **类数组对象** 和 **可遍历的对象**,转换成真正的数组。 +```js +// 类数组对象 +let a = { + '0':'a', + '1':'b', + length:2 +} +let arr = Array.from(a); + +// 可遍历的对象 +let a = Array.from([1,2,3]); +let b = Array.from({length: 3}); +let c = Array.from([1,2,3]).map(x => x * x); +let d = Array.from([1,2,3].map(x => x * x)); +``` + +#### 1.7.3 Array.of() +将一组数值,转换成**数组**,弥补`Array`方法参数不同导致的差异。 +```js +Array.of(1,2,3); // [1,2,3] +Array.of(1).length; // 1 + +Array(); // [] +Array(2); // [,] 1个参数时,为指定数组长度 +Array(1,2,3); // [1,2,3] 多于2个参数,组成新数组 +``` + +#### 1.7.4 find()和findIndex() +`find()`方法用于找出第一个符合条件的数组成员,参数为一个回调函数,所有成员依次执行该回调函数,返回第一个返回值为`true`的成员,如果没有一个符合则返回`undefined`。 +```js +[1,2,3,4,5].find( a => a < 3 ); // 1 +``` +回调函数接收三个参数,当前值、当前位置和原数组。 +```js +[1,2,3,4,5].find((value, index, arr){ + // ... +}); +``` +`findIndex()`方法与`find()`类似,返回第一个符合条件的数组成员的**位置**,如果都不符合则返回`-1`。 +```js +[1,2,3,4].findIndex((v,i,a)=>{ + return v>2; +}); // 2 +``` + +#### 1.7.5 fill() +用于用指定值**填充**一个数组,通常用来**初始化空数组**,并抹去数组中已有的元素。 +```js +new Array(3).fill('a'); // ['a','a','a'] +[1,2,3].fill('a'); // ['a','a','a'] +``` +并且`fill()`的第二个和第三个参数指定填充的**起始位置**和**结束位置**。 +```js +[1,2,3].fill('a',1,2); +``` + +#### 1.7.6 entries(),keys(),values() +主要用于遍历数组,`entries()`对键值对遍历,`keys()`对键名遍历,`values()`对键值遍历。 +```js +for (let i of ['a', 'b'].keys()){ + console.log(i) +} +// 0 +// 1 + +for (let e of ['a', 'b'].keys()){ + console.log(e) +} +// 'a' +// 'b' + +for (let e of ['a', 'b'].keys()){ + console.log(e) +} +// 0 'a' +// 1 'b' +``` + +#### 1.7.7 includes() +用于表示数组是否包含给定的值,与字符串的`includes`方法类似。 +```js +[1,2,3].includes(2); // true +[1,2,3].includes(4); // false +[1,2,NaN].includes(NaN); // true +``` +第二个参数为**起始位置**,默认为`0`,如果负数,则表示倒数的位置,如果大于数组长度,则重置为`0`开始。 +```js +[1,2,3].includes(3,3); // false +[1,2,3].includes(3,4); // false +[1,2,3].includes(3,-1); // true +[1,2,3].includes(3,-4); // true +``` + +#### 1.7.8 flat(),flatMap() +`flat()`用于将数组一维化,返回一个新数组,不影响原数组。 +默认一次只一维化一层数组,若需多层,则传入一个整数参数指定层数。 +若要一维化所有层的数组,则传入`Infinity`作为参数。 +```js +[1, 2, [2,3]].flat(); // [1,2,2,3] +[1,2,[3,[4,[5,6]]]].flat(3); // [1,2,3,4,5,6] +[1,2,[3,[4,[5,6]]]].flat('Infinity'); // [1,2,3,4,5,6] +``` +`flatMap()`是将原数组每个对象先执行一个函数,在对返回值组成的数组执行`flat()`方法,返回一个新数组,不改变原数组。 + `flatMap()`只能展开一层。 +```js +[2, 3, 4].flatMap((x) => [x, x * 2]); +// [2, 4, 3, 6, 4, 8] +``` + +[⬆ 返回目录](#二目录) + + +### 1.8 对象的拓展 +#### 1.8.1 属性的简洁表示 +```js +let a = 'a1'; +let b = { a }; // b => { a : 'a1' } +// 等同于 +let b = { a : a }; + +function f(a, b){ + return {a, b}; +} +// 等同于 +function f (a, b){ + return {a:a ,b:b}; +} + +let a = { + fun () { + return 'leo'; + } +} +// 等同于 +let a = { + fun : function(){ + return 'leo'; + } +} +``` + +#### 1.8.2 属性名表达式 +`JavaScript`提供2种方法**定义对象的属性**。 +```js +// 方法1 标识符作为属性名 +a.f = true; + +// 方法2 字符串作为属性名 +a['f' + 'un'] = true; +``` +延伸出来的还有: +```js +let a = 'hi leo'; +let b = { + [a]: true, + ['a'+'bc']: 123, + ['my' + 'fun'] (){ + return 'hi'; + } +}; +// b.a => undefined ; b.abc => 123 ; b.myfun() => 'hi' +// b[a] => 'hi leo' ; b['abc'] => 123 ; b['myfun'] => 'hi' +``` +**注意**: +属性名表达式不能与简洁表示法同时使用,否则报错。 +```js +// 报错 +let a1 = 'aa'; +let a2 = 'bb'; +let b1 = {[a1]}; + +// 正确 +let a1 = 'aa'; +let b1 = { [a1] : 'bb'}; +``` + +#### 1.8.3 Object.is() +`.Object.is()` 用于比较两个值是否严格相等,在ES5时候只要使用**相等运算符**(`==`)和**严格相等运算符**(`===`)就可以做比较,但是它们都有缺点,前者会**自动转换数据类型**,后者的`NaN`不等于自身,以及`+0`等于`-0`。 +```js +Object.is('a','a'); // true +Object.is({}, {}); // false + +// ES5 ++0 === -0 ; // true +NaN === NaN; // false + +// ES6 +Object.is(+0,-0); // false +Object.is(NaN,NaN); // true +``` + +#### 1.8.4 Object.assign() +`Object.assign()`方法用于对象的合并,将原对象的所有可枚举属性复制到目标对象。 +**基础用法**: +第一个参数是**目标对象**,后面参数都是**源对象**。 +```js +let a = {a:1}; +let b = {b:2}; +Object.assign(a,b); // a=> {a:1,b:2} +``` +**注意**: +* 若目标对象与源对象有同名属性,则后面属性会覆盖前面属性。 +```js +let a = {a:1, b:2}; +let b = {b:3, c:4}; +Object.assign(a, b); // a => {a:1, b:3, c:4} +``` +* 若只有**一个**参数,则返回该参数。 +```js +let a = {a:1}; +Object.assign(a) === a; // true +``` +* 若参数**不是对象**,则先转成对象后返回。 +```js +typeof Object.assign(2); // 'object' +``` +* 由于`undefined`或`NaN`无法转成对象,所以做为参数会报错。 +```js +Object.assign(undefined) // 报错 +Object.assign(NaN); // 报错 +``` +* `Object.assign()`实现的是浅拷贝。 + +`Object.assign()`拷贝得到的是这个对象的引用。这个对象的任何变化,都会反映到目标对象上面。 +```js +let a = {a: {b:1}}; +let b = Object.assign({},a); +a.a.b = 2; +console.log(b.a.b); // 2 +``` +* 将数组当做对象处理,键名为数组下标,键值为数组下标对应的值。 +```js +Object.assign([1, 2, 3], [4, 5]); // [4, 5, 3] +``` + +[⬆ 返回目录](#二目录) + + +### 1.9 Symbol +#### 1.9.1 介绍 +ES6引入`Symbol`作为一种新的**原始数据类型**,表示**独一无二**的值,主要是为了**防止属性名冲突**。 +ES6之后,JavaScript一共有其中数据类型:`Symbol`、`undefined`、`null`、`Boolean`、`String`、`Number`、`Object`。 +简单实用: +```js +let a = Symbol(); +typeof a; // "symbol" +``` +**注意:** +* `Symbol`函数不能用`new`,会报错。由于`Symbol`是一个原始类型,不是对象,所以不能添加属性,它是类似于字符串的数据类型。 +* `Symbol`都是不相等的,即使参数相同。 +```js +let a1 = Symbol(); +let a2 = Symbal(); +a1 === a2; // false + +let a1 = Symbol('abc'); +let a2 = Symbal('abc'); +a1 === a2; // false +``` +* `Symbol`不能与其他类型的值计算,会报错。 +```js +let a = Symbol('hello'); +a + " world!"; // 报错 +`${a} world!`; // 报错 +``` + +#### 1.9.2 更多介绍 +详细介绍[参考阮一峰老师的ES6 Symbol介绍](http://es6.ruanyifeng.com/#docs/symbol) + + +[⬆ 返回目录](#二目录) + + +### 1.10 Set和Map数据结构 +#### 1.10.1 Set +**介绍**: +`Set`数据结构类似数组,但所有成员的值**唯一**。 +`Set`本身为一个构造函数,用来生成`Set`数据结构,使用`add`方法来添加新成员。 +```js +let a = new Set(); +[1,2,2,1,3,4,5,4,5].forEach(x=>a.add(x)); +for(let k of a){ + console.log(k) +}; +// 1 2 3 4 5 +``` +**基础使用**: +```js +let a = new Set([1,2,3,3,4]); +[...a]; // [1,2,3,4] +a.size; // 4 + +// 数组去重 +[...new Set([1,2,3,4,4,4])];// [1,2,3,4] +``` + +**注意**: +* 向`Set`中添加值的时候,不会类型转换,即`5`和`'5'`是不同的。 +```js +[...new Set([5,'5'])]; // [5, "5"] +``` + +**属性和方法**: +* 属性: + * `Set.prototype.constructor`:构造函数,默认就是`Set`函数。 + * `Set.prototype.size`:返回`Set`实例的成员总数。 + +* 操作方法: + * `add(value)`:添加某个值,返回 Set 结构本身。 + * `delete(value)`:删除某个值,返回一个布尔值,表示删除是否成功。 + * `has(value)`:返回一个布尔值,表示该值是否为Set的成员。 + * `clear()`:清除所有成员,没有返回值。 + +```js +let a = new Set(); +a.add(1).add(2); // a => Set(2) {1, 2} +a.has(2); // true +a.has(3); // false +a.delete(2); // true a => Set(1) {1} +a.clear(); // a => Set(0) {} +``` +**数组去重**: +```js +let a = new Set([1,2,3,3,3,3]); +``` +#### 1.10.2 Set的应用 +**数组去重**: +```js +// 方法1 +[...new Set([1,2,3,4,4,4])]; // [1,2,3,4] +// 方法2 +Array.from([1,2,3,4,4,4]); // [1,2,3,4] +``` +**遍历和过滤**: +```js +let a = new Set([1,2,3,4]); + +// map 遍历操作 +let b = new Set([...a].map(x =>x*2));// b => Set(4) {2,4,6,8} + +// filter 过滤操作 +let c = new Set([...a].filter(x =>(x%2) == 0)); // b => Set(2) {2,4} +``` +**获取并集、交集和差集**: +```js +let a = new Set([1,2,3]); +let b = new Set([4,3,2]); + +// 并集 +let c1 = new Set([...a, ...b]); // Set {1,2,3,4} + +// 交集 +let c2 = new Set([...a].filter(x => b.has(x))); // set {2,3} + +// 差集 +let c3 = new Set([...a].filter(x => !b.has(x))); // set {1,4} +``` + +* 遍历方法: + * `keys()`:返回**键名**的遍历器。 + * `values()`:返回**键值**的遍历器。 + * `entries()`:返回**键值对**的遍历器。 + * `forEach()`:使用回调函数遍历**每个成员**。 + +`Set`遍历顺序是**插入顺序**,当保存多个回调函数,只需按照顺序调用。但由于`Set`结构**没有键名只有键值**,所以`keys()`和`values()`是返回结果相同。 +```js +let a = new Set(['a','b','c']); +for(let i of a.keys()){console.log(i)}; // 'a' 'b' 'c' +for(let i of a.values()){console.log(i)}; // 'a' 'b' 'c' +for(let i of a.entries()){console.log(i)}; +// ['a','a'] ['b','b'] ['c','c'] +``` +并且 还可以使用`for...of`直接遍历`Set`。 +```js +let a = new Set(['a','b','c']); +for(let k of a){console.log(k)}; // 'a' 'b' 'c' +``` +`forEach`与数组相同,对每个成员执行操作,且无返回值。 +```js +let a = new Set(['a','b','c']); +a.forEach((v,k) => console.log(k + ' : ' + v)); +``` + + +#### 1.10.3 Map +由于传统的`JavaScript`对象只能用字符串当做键,给开发带来很大限制,ES6增加`Map`数据结构,使得**各种类型的值**(包括对象)都可以作为键。 +`Map`结构提供了“**值—值**”的对应,是一种更完善的 Hash 结构实现。 +**基础使用**: +```js +let a = new Map(); +let b = {name: 'leo' }; +a.set(b,'my name'); // 添加值 +a.get(b); // 获取值 +a.size; // 获取总数 +a.has(b); // 查询是否存在 +a.delete(b); // 删除一个值 +a.clear(); // 清空所有成员 无返回 +``` +**注意**: +* 传入数组作为参数,**指定键值对的数组**。 +```js +let a = new Map([ + ['name','leo'], + ['age',18] +]) +``` +* 如果对同一个键**多次赋值**,后面的值将**覆盖前面的值**。 +```js +let a = new Map(); +a.set(1,'aaa').set(1,'bbb'); +a.get(1); // 'bbb' +``` +* 如果读取一个未知的键,则返回`undefined`。 +```js +new Map().get('abcdef'); // undefined +``` +* **同样的值**的两个实例,在 Map 结构中被视为两个键。 +```js +let a = new Map(); +let a1 = ['aaa']; +let a2 = ['aaa']; +a.set(a1,111).set(a2,222); +a.get(a1); // 111 +a.get(a2); // 222 +``` +**遍历方法**: +Map 的遍历顺序就是插入顺序。 +* `keys()`:返回键名的遍历器。 +* `values()`:返回键值的遍历器。 +* `entries()`:返回所有成员的遍历器。 +* `forEach()`:遍历 Map 的所有成员。 +```js +let a = new Map([ + ['name','leo'], + ['age',18] +]) + +for (let i of a.keys()){...}; +for (let i of a.values()){...}; +for (let i of a.entries()){...}; +a.forEach((v,k,m)=>{ + console.log(`key:${k},value:${v},map:${m}`) +}) +``` +**将Map结构转成数组结构**: +```js +let a = new Map([ + ['name','leo'], + ['age',18] +]) + +let a1 = [...a.keys()]; // a1 => ["name", "age"] +let a2 = [...a.values()]; // a2 =>  ["leo", 18] +let a3 = [...a.entries()];// a3 => [['name','leo'], ['age',18]] +``` + +#### 1.10.4 Map与其他数据结构互相转换 +* Map 转 数组 +```js +let a = new Map().set(true,1).set({f:2},['abc']); +[...a]; // [[true:1], [ {f:2},['abc'] ]] +``` +* 数组 转 Map +```js +let a = [ ['name','leo'], [1, 'hi' ]] +let b = new Map(a); +``` +* Map 转 对象 +如果所有 Map 的键都是字符串,它可以无损地转为对象。 +如果有非字符串的键名,那么这个键名会被转成字符串,再作为对象的键名。 +```js +function fun(s) { + let obj = Object.create(null); + for (let [k,v] of s) { + obj[k] = v; + } + return obj; +} + +const a = new Map().set('yes', true).set('no', false); +fun(a) +// { yes: true, no: false } +``` +* 对象 转 Map +```js +function fun(obj) { + let a = new Map(); + for (let k of Object.keys(obj)) { + a.set(k, obj[k]); + } + return a; +} + +fun({yes: true, no: false}) +// Map {"yes" => true, "no" => false} +``` + +* Map 转 JSON +**(1)Map键名都是字符串,转为对象JSON:** +```js +function fun (s) { + let obj = Object.create(null); + for (let [k,v] of s) { + obj[k] = v; + } + return JSON.stringify(obj) +} +let a = new Map().set('yes', true).set('no', false); +fun(a); +// '{"yes":true,"no":false}' +``` +**(2)Map键名有非字符串,转为数组JSON:** +```js +function fun (map) { + return JSON.stringify([...map]); +} + +let a = new Map().set(true, 7).set({foo: 3}, ['abc']); +fun(a) +// '[[true,7],[{"foo":3},["abc"]]]' +``` +* JSON 转 Map +**(1)所有键名都是字符串:** +```js +function fun (s) { + let strMap = new Map(); + for (let k of Object.keys(s)) { + strMap.set(k, s[k]); + } + return strMap; + return JSON.parse(strMap); +} +fun('{"yes": true, "no": false}') +// Map {'yes' => true, 'no' => false} +``` +**(2)整个 JSON 就是一个数组,且每个数组成员本身,又是一个有两个成员的数组**: +```js +function fun2(s) { + return new Map(JSON.parse(s)); +} +fun2('[[true,7],[{"foo":3},["abc"]]]') +// Map {true => 7, Object {foo: 3} => ['abc']} +``` + +[⬆ 返回目录](#二目录) + + +### 1.11 Proxy +`proxy` 用于修改某些操作的**默认行为**,可以理解为一种拦截外界对目标对象访问的一种机制,从而对外界的访问进行过滤和修改,即代理某些操作,也称“**代理器**”。 +#### 1.11.1 基础使用 +`proxy`实例化需要传入两个参数,`target`参数表示所要拦截的目标对象,`handler`参数也是一个对象,用来定制拦截行为。 +```js +let p = new Proxy(target, handler); + +let a = new Proxy({}, { + get: function (target, handler){ + return 'leo'; + } +}) +a.name; // leo +a.age; // leo +a.abcd; // leo +``` +上述`a`实例中,在第二个参数中定义了`get`方法,来拦截外界访问,并且`get`方法接收两个参数,分别是**目标对象**和**所要访问的属性**,所以不管外部访问对象中任何属性都会执行`get`方法返回`leo`。 +**注意**: +* 只能使用`Proxy`实例的对象才能使用这些操作。 +* 如果`handler`没有设置拦截,则直接返回原对象。 +```js +let target = {}; +let handler = {}; +let p = new Proxy(target, handler); +p.a = 'leo'; +target.a; // 'leo' +``` +**同个拦截器函数,设置多个拦截操作**: +```js +let p = new Proxy(function(a, b){ + return a + b; +},{ + get:function(){ + return 'get方法'; + }, + apply:function(){ + return 'apply方法'; + } +}) +``` + +**`Proxy`支持的13种拦截操作**: +13种拦截操作的详细介绍:[打开阮一峰老师的链接](http://es6.ruanyifeng.com/#docs/proxy)。 +* `get(target, propKey, receiver)`: +拦截对象属性的读取,比如proxy.foo和proxy['foo']。 + +* `set(target, propKey, value, receiver)`: +拦截对象属性的设置,比如proxy.foo = v或proxy['foo'] = v,返回一个布尔值。 + +* `has(target, propKey)`: +拦截propKey in proxy的操作,返回一个布尔值。 + +* `deleteProperty(target, propKey)`: +拦截delete proxy[propKey]的操作,返回一个布尔值。 + +* `ownKeys(target)`: +拦截Object.getOwnPropertyNames(proxy)、Object.getOwnPropertySymbols(proxy)、Object.keys(proxy)、for...in循环,返回一个数组。该方法返回目标对象所有自身的属性的属性名,而Object.keys()的返回结果仅包括目标对象自身的可遍历属性。 + +* `getOwnPropertyDescriptor(target, propKey)`: +拦截Object.getOwnPropertyDescriptor(proxy, propKey),返回属性的描述对象。 + +* `defineProperty(target, propKey, propDesc)`: +拦截Object.defineProperty(proxy, propKey, propDesc)、Object.defineProperties(proxy, propDescs),返回一个布尔值。 + +* `preventExtensions(target)`: +拦截Object.preventExtensions(proxy),返回一个布尔值。 + +* `getPrototypeOf(target)`: +拦截Object.getPrototypeOf(proxy),返回一个对象。 + +* `isExtensible(target)`: +拦截Object.isExtensible(proxy),返回一个布尔值。 + +* `setPrototypeOf(target, proto)`: +拦截Object.setPrototypeOf(proxy, proto),返回一个布尔值。如果目标对象是函数,那么还有两种额外操作可以拦截。 + +* `apply(target, object, args)`: +拦截 Proxy 实例作为函数调用的操作,比如proxy(...args)、proxy.call(object, ...args)、proxy.apply(...)。 + +* `construct(target, args)`: +拦截 Proxy 实例作为构造函数调用的操作,比如new proxy(...args)。 + +#### 1.11.2 取消Proxy实例 +使用`Proxy.revocale`方法取消`Proxy`实例。 +```js +let a = {}; +let b = {}; +let {proxy, revoke} = Proxy.revocale(a, b); + +proxy.name = 'leo'; // 'leo' +revoeke(); +proxy.name; // TypeError: Revoked +``` + +#### 1.11.3 实现 Web服务的客户端 +```js +const service = createWebService('http://le.com/data'); +service.employees().than(json =>{ + const employees = JSON.parse(json); +}) + +function createWebService(url){ + return new Proxy({}, { + get(target, propKey, receiver{ + return () => httpGet(url+'/'+propKey); + }) + }) +} +``` + +### 1.12 Promise对象 +#### 1.12.1 概念 +主要用途:**解决异步编程带来的回调地狱问题**。 +把`Promise`简单理解一个容器,存放着某个未来才会结束的事件(通常是一个异步操作)的结果。通过`Promise`对象来获取异步操作消息,处理各种异步操作。 + +**`Promise`对象2特点**: +* **对象的状态不受外界影响**。 +> `Promise`对象代表一个异步操作,有三种状态:**pending(进行中)**、**fulfilled(已成功)**和**rejected(已失败)**。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。这也是`Promise`这个名字的由来,它的英语意思就是“承诺”,表示其他手段无法改变。 + +* **一旦状态改变,就不会再变,任何时候都可以得到这个结果**。 +> Promise对象的状态改变,只有两种可能:从**pending**变为**fulfilled**和从**pending**变为**rejected**。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果,这时就称为 **resolved**(已定型)。如果改变已经发生了,你再对**Promise**对象添加回调函数,也会立即得到这个结果。这与事件(Event)完全不同,事件的特点是,如果你错过了它,再去监听,是得不到结果的。 + +注意,为了行文方便,本章后面的`resolve`d统一只指`fulfilled`状态,不包含`rejected`状态。 + +**`Promise`缺点** +* **无法取消**Promise,一旦新建它就会立即执行,无法中途取消。 +* 如果不设置回调函数,Promise内部抛出的错误,不会反应到外部。 +* 当处于pending状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。 + +#### 1.12.2 基本使用 +`Promise`为一个构造函数,需要用`new`来实例化。 +```js +let p = new Promise(function (resolve, reject){ + if(/*异步操作成功*/){ + resolve(value); + } else { + reject(error); + } +}) +``` +`Promise`接收一个函数作为参数,该函数两个参数`resolve`和`reject`,有JS引擎提供。 +* `resolve`作用是将`Promise`的状态从pending变成resolved,在异步操作成功时调用,返回异步操作的结果,作为参数传递出去。 +* `reject`作用是将`Promise`的状态从pending变成rejected,在异步操作失败时报错,作为参数传递出去。 + +`Promise`实例生成以后,可以用`then`方法分别指定`resolved`状态和`rejected`状态的回调函数。 +```js +p.then(function(val){ + // success... +},function(err){ + // error... +}) +``` + +**几个例子来理解** : +* 当一段时间过后,`Promise`状态便成为`resolved`触发`then`方法绑定的回调函数。 +```js +function timeout (s){ + return new Promise((resolve, reject){ + setTimeout(result,ms, 'done'); + }) +} +timeout(100).then(val => { + console.log(val); +}) +``` + +* `Promise`新建后立刻执行。 +```js +let p = new Promise(function(resolve, reject){ + console.log(1); + resolve(); +}) +p.then(()=>{ + console.log(2); +}) +console.log(3); +// 1 +// 2 +// 3 +``` + +**异步加载图片**: +```js +function f(url){ + return new Promise(function(resolve, reject){ + const img = new Image (); + img.onload = function(){ + resolve(img); + } + img.onerror = function(){ + reject(new Error( + 'Could not load image at ' + url + )); + } + img.src = url; + }) +} +``` + +**`resolve`函数和`reject`函数的参数为`resolve`函数或`reject`函数**: +`p1`的状态决定了`p2`的状态,所以`p2`要等待`p1`的结果再执行回调函数。 +```js +const p1 = new Promise(function (resolve, reject) { + setTimeout(() => reject(new Error('fail')), 3000) +}) + +const p2 = new Promise(function (resolve, reject) { + setTimeout(() => resolve(p1), 1000) +}) + +p2 + .then(result => console.log(result)) + .catch(error => console.log(error)) +// Error: fail +``` + +**调用`resolve`或`reject`不会结束`Promise`参数函数的执行,除了`return`**: +```js +new Promise((resolve, reject){ + resolve(1); + console.log(2); +}).then(r => { + console.log(3); +}) +// 2 +// 1 + +new Promise((resolve, reject){ + return resolve(1); + console.log(2); +}) +// 1 +``` + +#### 1.12.3 Promise.prototype.then() +作用是为`Promise`添加状态改变时的回调函数,`then`方法的第一个参数是`resolved`状态的回调函数,第二个参数(可选)是`rejected`状态的回调函数。 +`then`方法返回一个新`Promise`实例,与原来`Promise`实例不同,因此可以使用链式写法,上一个`then`的结果作为下一个`then`的参数。 +```js +getJSON("/posts.json").then(function(json) { + return json.post; +}).then(function(post) { + // ... +}); +``` + +#### 1.12.4 Promise.prototype.catch() +`Promise.prototype.catch`方法是`.then(null, rejection)`的别名,用于指定发生错误时的回调函数。 +```js +getJSON('/posts.json').then(function(posts) { + // ... +}).catch(function(error) { + // 处理 getJSON 和 前一个回调函数运行时发生的错误 + console.log('发生错误!', error); +}); +``` +如果 `Promise` 状态已经变成`resolved`,再抛出错误是无效的。 +```js +const p = new Promise(function(resolve, reject) { + resolve('ok'); + throw new Error('test'); +}); +p + .then(function(value) { console.log(value) }) + .catch(function(error) { console.log(error) }); +// ok +``` +当`promise`抛出一个错误,就被`catch`方法指定的回调函数捕获,下面三种写法相同。 +```js +// 写法一 +const p = new Promise(function(resolve, reject) { + throw new Error('test'); +}); +p.catch(function(error) { + console.log(error); +}); +// Error: test + +// 写法二 +const p = new Promise(function(resolve, reject) { + try { + throw new Error('test'); + } catch(e) { + reject(e); + } +}); +p.catch(function(error) { + console.log(error); +}); + +// 写法三 +const p = new Promise(function(resolve, reject) { + reject(new Error('test')); +}); +p.catch(function(error) { + console.log(error); +}); +``` +一般来说,不要在`then`方法里面定义` Reject` 状态的回调函数(即`then`的第二个参数),总是使用`catch`方法。 +```js +// bad +promise + .then(function(data) { + // success + }, function(err) { + // error + }); + +// good +promise + .then(function(data) { //cb + // success + }) + .catch(function(err) { + // error + }); +``` + +#### 1.12.5 Promise.prototype.finally() +`finally`方法用于指定不管 `Promise` 对象最后状态如何,都会执行的操作。该方法是 ES2018 引入标准的。 +```js +promise +.then(result => {···}) +.catch(error => {···}) +.finally(() => {···}); +``` +`finally`不接收任何参数,与状态无关,本质上是`then`方法的特例。 +```js +promise +.finally(() => { + // 语句 +}); + +// 等同于 +promise +.then( + result => { + // 语句 + return result; + }, + error => { + // 语句 + throw error; + } +); +``` +上面代码中,如果不使用`finally`方法,同样的语句需要为成功和失败两种情况各写一次。有了`finally`方法,则只需要写一次。 +`finally`方法总是会返回原来的值。 +```js +// resolve 的值是 undefined +Promise.resolve(2).then(() => {}, () => {}) + +// resolve 的值是 2 +Promise.resolve(2).finally(() => {}) + +// reject 的值是 undefined +Promise.reject(3).then(() => {}, () => {}) + +// reject 的值是 3 +Promise.reject(3).finally(() => {}) +``` + +#### 1.12.6 Promise.all() +用于将多个 `Promise` 实例,包装成一个新的 `Promise` 实例,参数可以不是数组,但必须是Iterator接口,且返回的每个成员都是`Promise`实例。 +```js +const p = Promise.all([p1, p2, p3]); +``` +`p`的状态由`p1`、`p2`、`p3`决定,分成**两种**情况。 +1. 只有p1、p2、p3的状态都变成fulfilled,p的状态才会变成fulfilled,此时p1、p2、p3的返回值组成一个数组,传递给p的回调函数。 +2. 只要p1、p2、p3之中有一个被rejected,p的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数。 + +```js +// 生成一个Promise对象的数组 +const promises = [2, 3, 5, 7, 11, 13].map(function (id) { + return getJSON('/post/' + id + ".json"); +}); + +Promise.all(promises).then(function (posts) { + // ... +}).catch(function(reason){ + // ... +}); +``` +上面代码中,`promises`是包含 6 个 Promise 实例的数组,只有这 6 个实例的状态都变成`fulfilled`,或者其中有一个变为`rejected`,才会调用`Promise.all`方法后面的回调函数。 + +**注意**:如果`Promise`的参数中定义了`catch`方法,则`rejected`后不会触发`Promise.all()`的`catch`方法,因为参数中的`catch`方法执行完后也会变成`resolved`,当`Promise.all()`方法参数的实例都是`resolved`时就会调用`Promise.all()`的`then`方法。 +```js +const p1 = new Promise((resolve, reject) => { + resolve('hello'); +}) +.then(result => result) +.catch(e => e); + +const p2 = new Promise((resolve, reject) => { + throw new Error('报错了'); +}) +.then(result => result) +.catch(e => e); + +Promise.all([p1, p2]) +.then(result => console.log(result)) +.catch(e => console.log(e)); +// ["hello", Error: 报错了] +``` + +**如果参数里面都没有catch方法,就会调用Promise.all()的catch方法。** +```js +const p1 = new Promise((resolve, reject) => { + resolve('hello'); +}) +.then(result => result); + +const p2 = new Promise((resolve, reject) => { + throw new Error('报错了'); +}) +.then(result => result); + +Promise.all([p1, p2]) +.then(result => console.log(result)) +.catch(e => console.log(e)); +// Error: 报错了 +``` + +#### 1.12.7 Promise.race() +与`Promise.all`方法类似,也是将多个`Promise`实例包装成一个新的`Promise`实例。 +```js +const p = Promise.race([p1, p2, p3]); +``` +与`Promise.all`方法区别在于,`Promise.race`方法是`p1`, `p2`, `p3`中只要一个参数先改变状态,就会把这个参数的返回值传给`p`的回调函数。 + +#### 1.12.8 Promise.resolve() +将现有对象转换成 `Promise` 对象。 +```js +const p = Promise.resolve($.ajax('/whatever.json')); +``` + +#### 1.12.9 Promise.reject() +返回一个`rejected`状态的`Promise`实例。 +```js +const p = Promise.reject('出错了'); +// 等同于 +const p = new Promise((resolve, reject) => reject('出错了')) + +p.then(null, function (s) { + console.log(s) +}); +// 出错了 +``` +注意,`Promise.reject()`方法的参数,会原封不动地作为`reject`的理由,变成后续方法的参数。这一点与`Promise.resolve`方法不一致。 +```js +const thenable = { + then(resolve, reject) { + reject('出错了'); + } +}; + +Promise.reject(thenable) +.catch(e => { + console.log(e === thenable) +}) +// true +``` + + +[⬆ 返回目录](#二目录) + +### 1.13 Iterator和 for...of循环 +#### 1.13.1 Iterator遍历器概念 +> **Iterator**是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署 **Iterator** 接口,就可以完成遍历操作(即依次处理该数据结构的所有成员)。 + +**Iterator三个作用**: +* 为各种数据结构,提供一个**统一**的、**简便**的访问接口; +* 使得数据结构的成员能够按某种次序排列; +* **Iterator** 接口主要供ES6新增的`for...of`消费; + +#### 1.13.2 Iterator遍历过程 +1. 创建一个指针对象,指向当前数据结构的起始位置。也就是说,遍历器对象本质上,就是一个指针对象。 +2. 第一次调用指针对象的`next`方法,可以将指针指向数据结构的第一个成员。 +3. 第二次调用指针对象的`next`方法,指针就指向数据结构的第二个成员。 +4. 不断调用指针对象的`next`方法,直到它指向数据结构的结束位置。 + +每一次调用`next`方法,都会返回数据结构的当前成员的信息。具体来说,就是返回一个包含`value`和`done`两个属性的对象。 + +* `value`属性是当前成员的值; +* `done`属性是一个布尔值,表示遍历是否结束; + +模拟`next`方法返回值: +```js +let f = function (arr){ + var nextIndex = 0; + return { + next:function(){ + return nextIndex < arr.length ? + {value: arr[nextIndex++], done: false}: + {value: undefined, done: true} + } + } +} + +let a = f(['a', 'b']); +a.next(); // { value: "a", done: false } +a.next(); // { value: "b", done: false } +a.next(); // { value: undefined, done: true } +``` + +#### 1.13.3 默认Iterator接口 +若数据**可遍历**,即一种数据部署了Iterator接口。 +ES6中默认的Iterator接口部署在数据结构的`Symbol.iterator`属性,即如果一个数据结构具有`Symbol.iterator`属性,就可以认为是**可遍历**。 +`Symbol.iterator`属性本身是函数,是当前数据结构默认的遍历器生成函数。执行这个函数,就会返回一个遍历器。至于属性名`Symbol.iterator`,它是一个表达式,返回`Symbol`对象的`iterator`属性,这是一个预定义好的、类型为 Symbol 的特殊值,所以要放在方括号内(参见《Symbol》一章)。 + +**原生具有Iterator接口的数据结构有**: +* Array +* Map +* Set +* String +* TypedArray +* 函数的 arguments 对象 +* NodeList 对象 + +#### 1.13.4 Iterator使用场景 +* **(1)解构赋值** +对数组和 `Set` 结构进行解构赋值时,会默认调用`Symbol.iterator`方法。 +```js +let a = new Set().add('a').add('b').add('c'); +let [x, y] = a; // x = 'a' y = 'b' +let [a1, ...a2] = a; // a1 = 'a' a2 = ['b','c'] +``` + +* **(2)扩展运算符** +扩展运算符(`...`)也会调用默认的 Iterator 接口。 +```js +let a = 'hello'; +[...a]; // ['h','e','l','l','o'] + +let a = ['b', 'c']; +['a', ...a, 'd']; // ['a', 'b', 'c', 'd'] +``` + +* **(2)yield*** +`yield*`后面跟的是一个可遍历的结构,它会调用该结构的遍历器接口。 +```js +let a = function*(){ + yield 1; + yield* [2,3,4]; + yield 5; +} + +let b = a(); +b.next() // { value: 1, done: false } +b.next() // { value: 2, done: false } +b.next() // { value: 3, done: false } +b.next() // { value: 4, done: false } +b.next() // { value: 5, done: false } +b.next() // { value: undefined, done: true } +``` + +* **(4)其他场合** +由于数组的遍历会调用遍历器接口,所以任何接受数组作为参数的场合,其实都调用了遍历器接口。下面是一些例子。 + +* for...of +* Array.from() +* Map(), Set(), WeakMap(), WeakSet()(比如`new Map([['a',1],['b',2]])`) +* Promise.all() +* Promise.race() + +#### 1.13.5 for...of循环 +只要数据结构部署了`Symbol.iterator`属性,即具有 iterator 接口,可以用`for...of`循环遍历它的成员。也就是说,`for...of`循环内部调用的是数据结构的`Symbol.iterato`方法。 +**使用场景**: +`for...of`可以使用在**数组**,**`Set`和`Map`结构**,**类数组对象**,**Genetator对象**和**字符串**。 + +* **数组** +`for...of`循环可以代替数组实例的`forEach`方法。 +```js +let a = ['a', 'b', 'c']; +for (let k of a){console.log(k)}; // a b c + +a.forEach((ele, index)=>{ + console.log(ele); // a b c + console.log(index); // 0 1 2 +}) +``` +与`for...in`对比,`for...in`只能获取对象键名,不能直接获取键值,而`for...of`允许直接获取键值。 +```js +let a = ['a', 'b', 'c']; +for (let k of a){console.log(k)}; // a b c +for (let k in a){console.log(k)}; // 0 1 2 +``` + +* **Set和Map** +可以使用数组作为变量,如`for (let [k,v] of b){...}`。 +```js +let a = new Set(['a', 'b', 'c']); +for (let k of a){console.log(k)}; // a b c + +let b = new Map(); +b.set('name','leo'); +b.set('age', 18); +b.set('aaa','bbb'); +for (let [k,v] of b){console.log(k + ":" + v)}; +// name:leo +// age:18 +// aaa:bbb +``` + +* **类数组对象** +```js +// 字符串 +let a = 'hello'; +for (let k of a ){console.log(k)}; // h e l l o + +// DOM NodeList对象 +let b = document.querySelectorAll('p'); +for (let k of b ){ + k.classList.add('test'); +} + +// arguments对象 +function f(){ + for (let k of arguments){ + console.log(k); + } +} +f('a','b'); // a b +``` + +* **对象** +普通对象不能直接使用`for...of`会报错,要部署Iterator才能使用。 +```js +let a = {a:'aa',b:'bb',c:'cc'}; +for (let k in a){console.log(k)}; // a b c +for (let k of a){console>log(k)}; // TypeError +``` + +#### 1.13.6 跳出for...of +使用`break`来实现。 +```js +for (let k of a){ + if(k>100) + break; + console.log(k); +} +``` + +[⬆ 返回目录](#二目录) + + + +### 1.14 Generator函数和应用 +#### 1.14.1 基本概念 +`Generator`函数是一种异步编程解决方案。 +**原理**: +执行`Genenrator`函数会返回一个遍历器对象,依次遍历`Generator`函数内部的每一个状态。 +`Generator`函数是一个普通函数,有以下两个特征: +* `function`关键字与函数名之间有个星号; +* 函数体内使用`yield`表达式,定义不同状态; + +通过调用`next`方法,将指针移向下一个状态,直到遇到下一个`yield`表达式(或`return`语句)为止。简单理解,`Generator`函数分段执行,`yield`表达式是暂停执行的标记,而`next`恢复执行。 +```js +function * f (){ + yield 'hi'; + yield 'leo'; + return 'ending'; +} +let a = f(); +a.next(); // {value: 'hi', done : false} +a.next(); // {value: 'leo', done : false} +a.next(); // {value: 'ending', done : true} +a.next(); // {value: undefined, done : false} +``` + +#### 1.14.2 yield表达式 +`yield`表达式是暂停标志,遍历器对象的`next`方法的运行逻辑如下: +1. 遇到`yield`就暂停执行,将这个`yield`后的表达式的值,作为返回对象的`value`属性值。 +2. 下次调用`next`往下执行,直到遇到下一个`yield`。 +3. 直到函数结束或者`return`为止,并返回`return`语句后面表达式的值,作为返回对象的`value`属性值。 +4. 如果该函数没有`return`语句,则返回对象的`value`为`undefined` 。 + +**注意:** +* `yield`只能用在`Generator`函数里使用,其他地方使用会报错。 +```js +// 错误1 +(function(){ + yiled 1; // SyntaxError: Unexpected number +})() + +// 错误2 forEach参数是个普通函数 +let a = [1, [[2, 3], 4], [5, 6]]; +let f = function * (i){ + i.forEach(function(m){ + if(typeof m !== 'number'){ + yield * f (m); + }else{ + yield m; + } + }) +} +for (let k of f(a)){ + console.log(k) +} +``` + +* `yield`表达式如果用于另一个表达式之中,必须放在**圆括号**内。 +```js +function * a (){ + console.log('a' + yield); // SyntaxErro + console.log('a' + yield 123); // SyntaxErro + console.log('a' + (yield)); // ok + console.log('a' + (yield 123)); // ok +} +``` + +* `yield`表达式用做函数参数或放在表达式右边,可以**不加括号**。 +```js +function * a (){ + f(yield 'a', yield 'b'); // ok + lei i = yield; // ok +} +``` + +#### 1.14.3 next方法 +`yield`本身没有返回值,或者是总返回`undefined`,`next`方法可带一个参数,作为上一个`yield`表达式的返回值。 +```js +function * f (){ + for (let k = 0; true; k++){ + let a = yield k; + if(a){k = -1}; + } +} +let g =f(); +g.next(); // {value: 0, done: false} +g.next(); // {value: 1, done: false} +g.next(true); // {value: 0, done: false} +``` +这一特点,可以让`Generator`函数开始执行之后,可以从外部向内部注入不同值,从而调整函数行为。 +```js +function * f(x){ + let y = 2 * (yield (x+1)); + let z = yield (y/3); + return (x + y + z); +} +let a = f(5); +a.next(); // {value : 6 ,done : false} +a.next(); // {value : NaN ,done : false} +a.next(); // {value : NaN ,done : true} +// NaN因为yeild返回的是对象 和数字计算会NaN + +let b = f(5); +b.next(); // {value : 6 ,done : false} +b.next(12); // {value : 8 ,done : false} +b.next(13); // {value : 42 ,done : false} +// x 5 y 24 z 13 +``` + +#### 1.14.4 for...of循环 +`for...of`循环会自动遍历,不用调用`next`方法,需要注意的是,`for...of`遇到`next`返回值的`done`属性为`true`就会终止,`return`返回的不包括在`for...of`循环中。 +```js +function * f(){ + yield 1; + yield 2; + yield 3; + yield 4; + return 5; +} +for (let k of f()){ + console.log(k); +} +// 1 2 3 4 没有 5 +``` + +#### 1.14.5 Generator.prototype.throw() +`throw`方法用来向函数外抛出错误,并且在Generator函数体内捕获。 +```js +let f = function * (){ + try { yield } + catch (e) { console.log('内部捕获', e) } +} + +let a = f(); +a.next(); + +try{ + a.throw('a'); + a.throw('b'); +}catch(e){ + console.log('外部捕获',e); +} +// 内部捕获 a +// 外部捕获 b +``` + +#### 1.14.6 Generator.prototype.return() +`return`方法用来返回给定的值,并结束遍历Generator函数,如果`return`方法没有参数,则返回值的`value`属性为`undefined`。 +```js +function * f(){ + yield 1; + yield 2; + yield 3; +} +let g = f(); +g.next(); // {value : 1, done : false} +g.return('leo'); // {value : 'leo', done " true} +g.next(); // {value : undefined, done : true} +``` + +#### 1.14.7 next()/throw()/return()共同点 +相同点就是都是用来恢复Generator函数的执行,并且使用不同语句替换`yield`表达式。 +* `next()`将`yield`表达式替换成一个值。 +```js +let f = function * (x,y){ + let r = yield x + y; + return r; +} +let g = f(1, 2); +g.next(); // {value : 3, done : false} +g.next(1); // {value : 1, done : true} +// 相当于把 let r = yield x + y; +// 替换成 let r = 1; +``` +* `throw()`将`yield`表达式替换成一个`throw`语句。 +```js +g.throw(new Error('报错')); // Uncaught Error:报错 +// 相当于将 let r = yield x + y +// 替换成 let r = throw(new Error('报错')); +``` +* `next()`将`yield`表达式替换成一个`return`语句。 +```js +g.return(2); // {value: 2, done: true} +// 相当于将 let r = yield x + y +// 替换成 let r = return 2; +``` + +#### 1.14.8 yield* 表达式 +用于在一个Generator中执行另一个Generator函数,如果没有使用`yield*`会没有效果。 +```js +function * a(){ + yield 1; + yield 2; +} +function * b(){ + yield 3; + yield * a(); + yield 4; +} +// 等同于 +function * b(){ + yield 3; + yield 1; + yield 2; + yield 4; +} +for(let k of b()){console.log(k)} +// 3 +// 1 +// 2 +// 4 +``` + +#### 1.14.9 应用场景 +1. **控制流管理** +解决回调地狱: +```js +// 使用前 +f1(function(v1){ + f2(function(v2){ + f3(function(v3){ + // ... more and more + }) + }) +}) + +// 使用Promise +Promise.resolve(f1) + .then(f2) + .then(f3) + .then(function(v4){ + // ... + },function (err){ + // ... + }).done(); + +// 使用Generator +function * f (v1){ + try{ + let v2 = yield f1(v1); + let v3 = yield f1(v2); + let v4 = yield f1(v3); + // ... + }catch(err){ + // console.log(err) + } +} +function g (task){ + let obj = task.next(task.value); + // 如果Generator函数未结束,就继续调用 + if(!obj.done){ + task.value = obj.value; + g(task); + } +} +g( f(initValue) ); +``` + +2. **异步编程的使用** +在真实的异步任务封装的情况: +```js +let fetch = require('node-fetch'); +function * f(){ + let url = 'http://www.baidu.com'; + let res = yield fetch(url); + console.log(res.bio); +} +// 执行该函数 +let g = f(); +let result = g.next(); +// 由于fetch返回的是Promise对象,所以用then +result.value.then(function(data){ + return data.json(); +}).then(function(data){ + g.next(data); +}) +``` + +[⬆ 返回目录](#二目录) + + +### 1.15 Class语法和继承 +#### 1.15.1 介绍 +ES6中的`class`可以看作只是一个语法糖,绝大部分功能都可以用ES5实现,并且,**类和模块的内部,默认就是严格模式,所以不需要使用use strict指定运行模式**。 +```js +// ES5 +function P (x,y){ + this.x = x; + this.y = y; +} +P.prototype.toString = function () { + return '(' + this.x + ', ' + this.y + ')'; +}; +var a = new P(1, 2); + +// ES6 +class P { + constructor(x, y){ + this.x = x; + this.y = y; + } + toString(){ + return '(' + this.x + ', ' + this.y + ')'; + } +} +let a = new P(1, 2); +``` +**值得注意**: +ES6的**类**的所有方法都是定义在`prototype`属性上,调用类的实例的方法,其实就是调用原型上的方法。 +```js +class P { + constructor(){ ... } + toString(){ ... } + toNumber(){ ... } +} +// 等同于 +P.prototyoe = { + constructor(){ ... }, + toString(){ ... }, + toNumber(){ ... } +} + +let a = new P(); +a.constructor === P.prototype.constructor; // true +``` +类的属性名可以使用**表达式**: +```js +let name = 'leo'; +class P { + constructor (){ ... } + [name](){ ... } +} +``` + +**Class不存在变量提升**: +ES6中的类不存在变量提升,与ES5完全不同: +```js +new P (); // ReferenceError +class P{...}; +``` +**Class的name属性**: +`name`属性总是返回紧跟在`class`后的类名。 +```js +class P {} +P.name; // 'P' +``` + +#### 1.15.2 constructor()方法 +`constructor()`是类的默认方法,通过`new`实例化时自动调用执行,一个类必须有`constructor()`方法,否则一个空的`constructor()`会默认添加。 +`constructor()`方法默认返回实例对象(即`this`)。 +```js +class P { ... } +// 等同于 +class P { + constructor(){ ... } +} +``` + +#### 1.15.3 类的实例对象 +与ES5一样,ES6的类必须使用`new`命令实例化,否则报错。 +```js +class P { ... } +let a = P (1,2); // 报错 +let b = new P(1, 2); // 正确 +``` +与 ES5 一样,实例的属性除非显式定义在其本身(即定义在`this`对象上),否则都是定义在原型上(即定义在`class`上)。 +```js +class P { + constructor(x, y){ + this.x = x; + this.y = y; + } + toString(){ + return '(' + this.x + ', ' + this.y + ')'; + } +} +var point = new Point(2, 3); + +point.toString() // (2, 3) + +point.hasOwnProperty('x') // true +point.hasOwnProperty('y') // true +point.hasOwnProperty('toString') // false +point.__proto__.hasOwnProperty('toString') // true +// toString是原型对象的属性(因为定义在Point类上) +``` + +#### 1.15.4 Class表达式 +与函数一样,类也可以使用表达式来定义,使用表达式来作为类的名字,而`class`后跟的名字,用来指代当前类,只能再Class内部使用。 +```js +let a = class P{ + get(){ + return P.name; + } +} + +let b = new a(); +b.get(); // P +P.name; // ReferenceError: P is not defined +``` +如果类的内部没用到的话,可以省略`P`,也就是可以写成下面的形式。 +```js +let a = class { ... } +``` + +#### 1.15.5 私有方法和私有属性 +由于ES6不提供,只能变通来实现: +* 1.使用命名加以区别,如变量名前添加`_`,但是不保险,外面也可以调用到。 +```js +class P { + // 公有方法 + f1 (x) { + this._x(x); + } + // 私有方法 + _x (x){ + return this.y = x; + } +} +``` +* 2.将私有方法移除模块,再在类内部调用`call`方法。 +```js +class P { + f1 (x){ + f2.call(this, x); + } +} +function f2 (x){ + return this.y = x; +} +``` +* 3.使用`Symbol`为私有方法命名。 +```js +const a1 = Symbol('a1'); +const a2 = Symbol('a2'); +export default class P{ + // 公有方法 + f1 (x){ + this[a1](x); + } + // 私有方法 + [a1](x){ + return this[a2] = x; + } +} +``` + + +#### 1.15.6 this指向问题 +类内部方法的`this`默认指向类的实例,但单独使用该方法可能报错,因为`this`指向的问题。 +```js +class P{ + leoDo(thing = 'any'){ + this.print(`Leo do ${thing}`) + } + print(text){ + console.log(text); + } +} +let a = new P(); +let { leoDo } = a; +leoDo(); // TypeError: Cannot read property 'print' of undefined +// 问题出在 单独使用leoDo时,this指向调用的环境, +// 但是leoDo中的this是指向P类的实例,所以报错 +``` +**解决方法**: +* 1.在类里面绑定`this` +```js +class P { + constructor(){ + this.name = this.name.bind(this); + } +} +``` +* 2.使用箭头函数 +```js +class P{ + constructor(){ + this.name = (name = 'leo' )=>{ + this.print(`my name is ${name}`) + } + } +} +``` + +#### 1.15.7 Class的getter和setter +使用`get`和`set`关键词对属性设置取值函数和存值函数,拦截属性的存取行为。 +```js +class P { + constructor (){ ... } + get f (){ + return 'getter'; + } + set f (val) { + console.log('setter: ' + val); + } +} + +let a = new P(); +a.f = 100; // setter : 100 +a.f; // getter +``` + +#### 1.15.8 Class的generator方法 +只要在方法之前加个(`*`)即可。 +```js +class P { + constructor (...args){ + this.args = args; + } + *[Symbol.iterator](){ + for (let arg of this.args){ + yield arg; + } + } +} +for (let k of new P('aa', 'bb')){ + console.log(k); +} +// 'aa' +// 'bb' +``` + +#### 1.15.9 Class的静态方法 +由于类相当于实例的原型,所有类中定义的方法都会被实例继承,若不想被继承,只要加上`static`关键字,只能通过类来调用,即“**静态方法**”。 +```js +class P (){ + static f1 (){ return 'aaa' }; +} +P.f1(); // 'aa' +let a = new P(); +a.f1(); // TypeError: a.f1 is not a function +``` +如果静态方法包含`this`关键字,则`this`指向类,而不是实例。 +```js +class P { + static f1 (){ + this.f2(); + } + static f2 (){ + console.log('aaa'); + } + f2(){ + console.log('bbb'); + } +} +P.f2(); // 'aaa' +``` +并且静态方法可以被子类继承,或者`super`对象中调用。 +```js +class P{ + static f1(){ return 'leo' }; +} +class Q extends P { ... }; +Q.f1(); // 'leo' + +class R extends P { + static f2(){ + return super.f1() + ',too'; + } +} +R.f2(); // 'leo , too' +``` + +#### 1.15.10 Class的静态属性和实例属性 +ES6中明确规定,Class内部只有静态方法没有静态属性,所以只能通过下面实现。 +```js +// 正确写法 +class P {} +P.a1 = 1; +P.a1; // 1 + +// 无效写法 +class P { + a1: 2, // 无效 + static a1 : 2, // 无效 +} +P.a1; // undefined +``` +**新提案来规定实例属性和静态属性的新写法** +* 1.类的实例属性 +类的实例属性可以用等式,写入类的定义中。 +```js +class P { + prop = 100; // prop为P的实例属性 可直接读取 + constructor(){ + console.log(this.prop); // 100 + } +} +``` +有了新写法后,就可以不再`contructor`方法里定义。 +为了可读性的目的,对于那些在`constructor`里面已经定义的实例属性,新写法允许**直接列出**。 +```js +// 之前写法: +class RouctCounter extends React.Component { + constructor(prop){ + super(prop); + this.state = { + count : 0 + } + } +} + +// 新写法 +class RouctCounter extends React.Component { + state; + constructor(prop){ + super(prop); + this.state = { + count : 0 + } + } + +} +``` +* 2.类的静态属性 +只要在实例属性前面加上`static`关键字就可以。 +```js +class P { + static prop = 100; + constructor(){console.log(this.prop)}; // 100 +} +``` +新写法方便静态属性的表达。 +```js +// old +class P { .... } +P.a = 1; + +// new +class P { + static a = 1; +} +``` + +#### 1.15.11 Class的继承 +主要通过`extends`关键字实现,继承父类的所有属性和方法,通过`super`关键字来新建父类构造函数的`this`对象。 +```js +class P { ... } +class Q extends P { ... } + +class P { + constructor(x, y){ + // ... + } + f1 (){ ... } +} +class Q extends P { + constructor(a, b, c){ + super(x, y); // 调用父类 constructor(x, y) + this.color = color ; + } + f2 (){ + return this.color + ' ' + super.f1(); + // 调用父类的f1()方法 + } +} +``` +**子类必须在`constructor()`调用`super()`否则报错**,并且只有`super`方法才能调用父类实例,还有就是,**父类的静态方法,子类也可以继承到**。 +```js +class P { + constructor(x, y){ + this.x = x; + this.y = y; + } + static fun(){ + console.log('hello leo') + } +} +// 关键点1 调用super +class Q extends P { + constructor(){ ... } +} +let a = new Q(); // ReferenceError 因为Q没有调用super + +// 关键点2 调用super +class R extends P { + constructor (x, y. z){ + this.z = z; // ReferenceError 没调用super不能使用 + super(x, y); + this.z = z; // 正确 + } +} + +// 关键点3 子类继承父类静态方法 +R.hello(); // 'hello leo' +``` + +**super关键字**: +既可以当函数使用,还可以当对象使用。 +* 1.当函数调用,代表父类的构造函数,但必须执行一次。 +```js +class P {... }; +class R extends P { + constructor(){ + super(); + } +} +``` +* 2.当对象调用,指向原型对象,在静态方法中指向父类。 +```js +class P { + f (){ return 2 }; +} +class R extends P { + constructor (){ + super(); + console.log(super.f()); // 2 + } +} +let a = new R() +``` +**注意**:`super`指向父类原型对象,所以定义在父类实例的方法和属性,是无法通过`super`调用的,但是通过调用`super`方法可以把内部`this`指向当前实例,就可以访问到。 +```js +class P { + constructor(){ + this.a = 1; + } + print(){ + console.log(this.a); + } +} +class R extends P { + get f (){ + return super.a; + } +} +let b = new R(); +b.a; // undefined 因为a是父类P实例的属性 + +// 先调用super就可以访问 +class Q extends P { + constructor(){ + super(); // 将内部this指向当前实例 + return super.a; + } +} +let c = new Q(); +c.a; // 1 + +// 情况3 +class J extends P { + constructor(){ + super(); + this.a = 3; + } + g(){ + super.print(); + } +} +let c = new J(); +c.g(); // 3 由于执行了super()后 this指向当前实例 +``` + +[⬆ 返回目录](#二目录) + +### 1.16 Module语法和加载实现 +#### 1.16.1 介绍 +ES6之前用于JavaScript的模块加载方案,是一些社区提供的,主要有`CommonJS`和`AMD`两种,前者用于**服务器**,后者用于**浏览器**。 +ES6提供了模块的实现,使用`export`命令对外暴露接口,使用`import`命令输入其他模块暴露的接口。 +```js +// CommonJS模块 +let { stat, exists, readFire } = require('fs'); + +// ES6模块 +import { stat, exists, readFire } = from 'fs'; +``` + +#### 1.16.2 严格模式 +ES6模块自动采用严格模式,无论模块头部是否有`"use strict"`。 +**严格模式有以下限制**: +* 变量必须**声明后再使用** +* 函数的参数**不能有同名属性**,否则报错 +* 不能使用`with`语句 +* 不能对只读属性赋值,否则报错 +* 不能使用前缀 0 表示八进制数,否则报错 +* 不能删除不可删除的属性,否则报错 +* 不能删除变量`delete prop`,会报错,只能删除属性`delete * global[prop]` +* `eval`不会在它的外层作用域引入变量 +* `eval`和`arguments`不能被重新赋值 +* `arguments`不会自动反映函数参数的变化 +* 不能使用`arguments.callee` +* 不能使用`arguments.caller` +* 禁止`this`指向全局对象 +* 不能使用`fn.caller`和`fn.arguments`获取函数调用的堆栈 +* 增加了保留字(比如`protected`、`static`和`interface`) + +特别是,ES6中顶层`this`指向`undefined`,即不应该在顶层代码使用`this`。 + +#### 1.16.3 export命令 +使用`export`向模块外暴露接口,可以是方法,也可以是变量。 +```js +// 1. 变量 +export let a = 'leo'; +export let b = 100; + +// 还可以 +let a = 'leo'; +let b = 100; +export {a, b}; + +// 2. 方法 +export function f(a,b){ + return a*b; +} + +// 还可以 +function f1 (){ ... } +function f2 (){ ... } +export { + a1 as f1, + a2 as f2 +} +``` +可以使用`as`重命名函数的对外接口。 +**特别注意**: +`export`暴露的必须是接口,不能是值。 +```js +// 错误 +export 1; // 报错 + +let a = 1; +export a; // 报错 + +// 正确 +export let a = 1; // 正确 + +let a = 1; +export {a}; // 正确 + +let a = 1; +export { a as b}; // 正确 +``` +暴露方法也是一样: +```js +// 错误 +function f(){...}; +export f; + +// 正确 +export function f () {...}; + +function f(){...}; +export {f}; +``` + +#### 1.16.4 import命令 +加载`export`暴露的接口,输出为变量。 +```js +import { a, b } from '/a.js'; +function f(){ + return a + b; +} +``` +`import`后大括号指定变量名,需要与`export`的模块暴露的名称一致。 +也可以使用`as`为输入的变量重命名。 +```js +import { a as leo } from './a.js'; +``` +`import`不能直接修改输入变量的值,因为输入变量只读只是个接口,但是如果是个对象,可以修改它的属性。 +```js +// 错误 +import {a} from './f.js'; +a = {}; // 报错 + +// 正确 +a.foo = 'leo'; // 不报错 +``` +`import`命令具有提升效果,会提升到整个模块头部最先执行,且多次执行相同`import`只会执行一次。 + +#### 1.16.5 模块的整体加载 +当一个模块暴露多个方法和变量,引用时可以用`*`整体加载。 +```js +// a.js +export function f(){...} +export function g(){...} + +// b.js +import * as obj from '/a.js'; +console.log(obj.f()); +console.log(obj.g()); +``` +但是,不允许运行时改变: +```js +import * as obj from '/a.js'; +// 不允许 +obj.a = 'leo'; +obj.b = function(){...}; +``` + +#### 1.16.6 export default 命令 +使用`export default`命令,为模块指定默认输出,引用的时候直接指定任意名称即可。 +```js +// a.js +export default function(){console.log('leo')}; + +// b.js +import leo from './a.js'; +leo(); // 'leo' +``` +`export defualt`暴露有函数名的函数时,在调用时相当于匿名函数。 +```js +// a.js +export default function f(){console.log('leo')}; +// 或者 +function f(){console.log('leo')}; +export default f; + +// b.js +import leo from './a.js'; +``` +`export defualt`其实是输出一个名字叫`default`的变量,所以后面不能跟变量赋值语句。 +```js +// 正确 +export let a= 1; + +let a = 1; +export defualt a; + +// 错误 +export default let a = 1; +``` +`export default`命令的本质是将后面的值,赋给`default`变量,所以可以直接将一个值写在`export default`之后。 +```js +// 正确 +export detault 1; +// 错误 +export 1; +``` + +#### 1.16.7 export 和 import 复合写法 +常常在先输入后输出同一个模块使用,即转发接口,将两者写在一起。 +```js +export {a, b} from './leo.js'; + +// 理解为 +import {a, b} from './leo.js'; +export {a, b} +``` +常见的写法还有: +```js +// 接口改名 +export { a as b} from './leo.js'; + +// 整体输出 +export * from './leo.js'; + +// 默认接口改名 +export { default as a } from './leo.js'; +``` +**常常用在模块继承**。 + +#### 1.16.8 浏览器中的加载规则 +ES6中,可以在浏览器使用` +``` +另外,ES6模块也可以内嵌到网页,语法与外部加载脚本一致: +```html + +``` +**注意点**: +* 代码是在模块作用域之中运行,而不是在全局作用域运行。模块内部的顶层变量,外部不可见。 +* 模块脚本自动采用严格模式,不管有没有声明`use strict`。 +* 模块之中,可以使用`import`命令加载其他模块(`.js`后缀不可省略,需要提供`绝对 UR`L 或`相对 UR`L),也可以使用`export`命令输出对外接口。 +* 模块之中,顶层的`this`关键字返回`undefined`,而不是指向`window`。也就是说,在模块顶层使用`this`关键字,是无意义的。 +* 同一个模块如果加载多次,将只执行一次。 + +[⬆ 返回目录](#二目录) + diff --git "a/Cute-Article/article/64-ES7\346\261\207\346\200\273.md" "b/Cute-Article/article/64-ES7\346\261\207\346\200\273.md" new file mode 100644 index 00000000..c2d17715 --- /dev/null +++ "b/Cute-Article/article/64-ES7\346\261\207\346\200\273.md" @@ -0,0 +1,44 @@ +## 2. ES7 +### 2.1 Array.prototype.includes()方法 +`includes()`用于查找一个值是否在数组中,如果在返回`true`,否则返回`false`。 +```js +['a', 'b', 'c'].includes('a'); // true +['a', 'b', 'c'].includes('d'); // false +``` +`includes()`方法接收两个参数,**搜索的内容**和**开始搜索的索引**,默认值为**0**,若搜索值在数组中则返回`true`否则返回`false`。 +```js +['a', 'b', 'c', 'd'].includes('b'); // true +['a', 'b', 'c', 'd'].includes('b', 1); // true +['a', 'b', 'c', 'd'].includes('b', 2); // false +``` +与`indexOf`方法对比,下面方法效果相同: +```js +['a', 'b', 'c', 'd'].indexOf('b'); // true +['a', 'b', 'c', 'd'].includes('b') > -1; // true +``` +**includes()与indexOf对比:** +* `includes`相比`indexOf`更具语义化,`includes`返回的是是否存在的具体结果,值为布尔值,而`indexOf`返回的是搜索值的下标。 +* `includes`相比`indexOf`更准确,`includes`认为两个`NaN`相等,而`indexOf`不会。 +```js +let a = [1, NaN, 3]; +a.indexOf(NaN); // -1 +a.includes(NaN); // true +``` +另外在判断`+0`与`-0`时,`includes`和`indexOf`的返回相同。 +```js +[1, +0, 3, 4].includes(-0); // true +[1, +0, 3, 4].indexOf(-0); // 1 +``` + +### 2.2 指数操作符(**) +基本用法: +```js +let a = 3 ** 2 ; // 9 +// 等效于 +Math.pow(3, 2); // 9 +``` +`**`是一个运算符,也可以满足类似假发的操作,如下: +```js +let a = 3; +a **= 2; // 9 +``` \ No newline at end of file diff --git "a/Cute-Article/article/65-ES8\346\261\207\346\200\273.md" "b/Cute-Article/article/65-ES8\346\261\207\346\200\273.md" new file mode 100644 index 00000000..ac16efe0 --- /dev/null +++ "b/Cute-Article/article/65-ES8\346\261\207\346\200\273.md" @@ -0,0 +1,371 @@ + +## 3. ES8 +### 3.1 async函数 +#### 3.1.1 介绍 +ES8引入`async`函数,是为了使异步操作更加方便,其实它就是**Generator**函数的语法糖。 +`async`函数使用起来,只要把**Generator**函数的**(*)**号换成`async`,`yield`换成`await`即可。对比如下: +```js +// Generator写法 +const fs = require('fs'); +const readFile = function (fileName) { + return new Promise(function (resolve, reject) { + fs.readFile(fileName, function(error, data) { + if (error) return reject(error); + resolve(data); + }); + }); +}; +const gen = function* () { + const f1 = yield readFile('/etc/fstab'); + const f2 = yield readFile('/etc/shells'); + console.log(f1.toString()); + console.log(f2.toString()); +}; + +// async await写法 +const asyncReadFile = async function () { + const f1 = await readFile('/etc/fstab'); + const f2 = await readFile('/etc/shells'); + console.log(f1.toString()); + console.log(f2.toString()); +}; +``` +**对比Genenrator有四个优点:** +* (1)内置执行器 +Generator函数执行需要有执行器,而`async`函数自带执行器,即`async`函数与普通函数一模一样: +```js +async f(); +``` +* (2)更好的语义 +`async`和`await`,比起`星号`和`yield`,语义更清楚了。`async`表示函数里有异步操作,`await`表示紧跟在后面的表达式需要等待结果。 +* (3)更广的适用性 +`yield`命令后面只能是 Thunk 函数或 Promise 对象,而`async`函数的`await`命令后面,可以是 Promise 对象和原始类型的值(数值、字符串和布尔值,但这时等同于同步操作)。 +* (4)返回值是Promise +`async`函数的返回值是 Promise 对象,这比 Generator 函数的返回值是 Iterator 对象方便多了。你可以用`then`方法指定下一步的操作。 + +进一步说,`async`函数完全可以看作多个异步操作,包装成的一个 Promise 对象,而`await`命令就是内部`then`命令的语法糖。 + +#### 3.1.2 基本用法 +`async`函数返回一个Promise对象,可以使用`then`方法添加回调函数,函数执行时,遇到`await`就会先返回,等到异步操作完成,在继续执行。 +```js +async function f(item){ + let a = await g(item); + let b = await h(item); + return b; +} +f('hello').then(res => { + console.log(res); +}) +``` +`async`表明该函数内部有异步操作,调用该函数时,会立即返回一个Promise对象。 +另外还有个定时的案例,指定时间后执行: +```js +function f (ms){ + return new Promise(res => { + setTimeout(res, ms); + }); +} +async function g(val, ms){ + await f(ms); + console.log(val); +} +g('leo', 50); +``` +`async`函数还有很多使用形式: +```js +// 函数声明 +async function f (){...} + +// 函数表达式 +let f = async function (){...} + +// 对象的方法 +let a = { + async f(){...} +} +a.f().then(...) + +// Class的方法 +class c { + constructor(){...} + async f(){...} +} + +// 箭头函数 +let f = async () => {...} +``` + +#### 3.1.3 返回Promise对象 +`async`内部`return`返回的值会作为`then`方法的参数,另外只有`async`函数内部的异步操作执行完,才会执行`then`方法指定的回调函数。 +```js +async function f(){ + return 'leo'; +} +f().then(res => { console.log (res) }); // 'leo' +``` +`async`内部抛出的错误,会被`catch`接收。 +```js +async function f(){ + throw new Error('err'); +} +f().then ( + v => console.log(v), + e => console.log(e) +) +// Error: err +``` + +#### 3.1.4 await命令 +通常`await`后面是一个Promise对象,如果不是就返回对应的值。 +```js +async function f(){ + return await 10; +} +f().then(v => console.log(v)); // 10 +``` +我们常常将`async await`和`try..catch`一起使用,并且可以放多个`await`命令,也是防止异步操作失败因为中断后续异步操作的情况。 +```js +async function f(){ + try{ + await Promise.reject('err'); + }catch(err){ ... } + return await Promise.resolve('leo'); +} +f().then(v => console.log(v)); // 'leo' +``` + +#### 3.1.5 使用注意 +* (1)`await`命令放在`try...catch`代码块中,防止Promise返回`rejected`。 +* (2)若多个`await`后面的异步操作不存在继发关系,最好让他们同时执行。 +```js +// 效率低 +let a = await f(); +let b = await g(); + +// 效率高 +let [a, b] = await Promise.all([f(), g()]); +// 或者 +let a = f(); +let b = g(); +let a1 = await a(); +let b1 = await b(); +``` +* (3)`await`命令只能用在`async`函数之中,如果用在普通函数,就会报错。 +```js +// 错误 +async function f(){ + let a = [{}, {}, {}]; + a.forEach(v =>{ // 报错,forEach是普通函数 + await post(v); + }); +} + +// 正确 +async function f(){ + let a = [{}, {}, {}]; + for(let k of a){ + await post(k); + } +} +``` + +### 3.2 Promise.prototype.finally() +`finally()`是ES8中**Promise**添加的一个新标准,用于指定不管**Promise**对象最后状态(是`fulfilled`还是`rejected`)如何,都会执行此操作,并且`finally`方法必须写在最后面,即在`then`和`catch`方法后面。 +```js +// 写法如下: +promise + .then(res => {...}) + .catch(err => {...}) + .finally(() => {...}) +``` +`finally`方法常用在处理**Promise**请求后关闭服务器连接: +```js +server.listen(port) + .then(() => {..}) + .finally(server.stop); +``` +**本质上,finally方法是then方法的特例:** +```js +promise.finally(() => {...}); + +// 等同于 +promise.then( + result => { + // ... + return result + }, + error => { + // ... + throw error + } +) +``` + +### 3.3 Object.values(),Object.entries() +ES7中新增加的 `Object.values()`和`Object.entries()`与之前的`Object.keys()`类似,返回数组类型。 +回顾下`Object.keys()`: +```js +var a = { f1: 'hi', f2: 'leo'}; +Object.keys(a); // ['f1', 'f2'] +``` +#### 3.3.1 Object.values() +返回一个数组,成员是参数对象自身的(不含继承的)所有**可遍历属性**的键值。 +```js +let a = { f1: 'hi', f2: 'leo'}; +Object.values(a); // ['hi', 'leo'] +``` +如果参数不是对象,则返回空数组: +```js +Object.values(10); // [] +Object.values(true); // [] +``` + +#### 3.3.2 Object.entries() +返回一个数组,成员是参数对象自身的(不含继承的)所有**可遍历属性**的键值对数组。 +```js +let a = { f1: 'hi', f2: 'leo'}; +Object.entries(a); // [['f1','hi'], ['f2', 'leo']] +``` +* **用途1**: +遍历对象属性。 +```js +let a = { f1: 'hi', f2: 'leo'}; +for (let [k, v] of Object.entries(a)){ + console.log( + `${JSON.stringfy(k)}:${JSON.stringfy(v)}` + ) +} +// 'f1':'hi' +// 'f2':'leo' +``` +* **用途2**: +将对象转为真正的Map结构。 +```js +let a = { f1: 'hi', f2: 'leo'}; +let map = new Map(Object.entries(a)); +``` +手动实现`Object.entries()`方法: +```js +// Generator函数实现: +function* entries(obj){ + for (let k of Object.keys(obj)){ + yield [k ,obj[k]]; + } +} + +// 非Generator函数实现: +function entries (obj){ + let arr = []; + for(let k of Object.keys(obj)){ + arr.push([k, obj[k]]); + } + return arr; +} +``` + +### 3.4 Object.getOwnPropertyDescriptors() +之前有`Object.getOwnPropertyDescriptor`方法会返回某个对象属性的描述对象,新增的`Object.getOwnPropertyDescriptors()`方法,返回指定对象所有自身属性(非继承属性)的描述对象,所有原对象的属性名都是该对象的属性名,对应的属性值就是该属性的描述对象 +```js +let a = { + a1:1, + get f1(){ return 100} +} +Object.getOwnPropetyDescriptors(a); +/* +{ + a:{ configurable:true, enumerable:true, value:1, writeable:true} + f1:{ configurable:true, enumerable:true, get:f, set:undefined} +} +*/ +``` +实现原理: +```js +function getOwnPropertyDescriptors(obj) { + const result = {}; + for (let key of Reflect.ownKeys(obj)) { + result[key] = Object.getOwnPropertyDescriptor(obj, key); + } + return result; +} +``` +引入这个方法,主要是为了解决`Object.assign()`无法正确拷贝`get`属性和`set`属性的问题。 +```js +let a = { + set f(v){ + console.log(v) + } +} +let b = {}; +Object.assign(b, a); +Object.a(b, 'f'); +/* +f = { + configurable: true, + enumable: true, + value: undefined, + writeable: true +} +*/ +``` +`value`为`undefined`是因为`Object.assign`方法不会拷贝其中的`get`和`set`方法,使用`getOwnPropertyDescriptors`配合`Object.defineProperties`方法来实现正确的拷贝: +```js +let a = { + set f(v){ + console.log(v) + } +} +let b = {}; +Object.defineProperties(b, Object.getOwnPropertyDescriptors(a)); +Object.getOwnPropertyDescriptor(b, 'f') +/* + configurable: true, + enumable: true, + get: undefined, + set: function(){...} +*/ +``` +`Object.getOwnPropertyDescriptors`方法的配合`Object.create`方法,将对象属性克隆到一个新对象,实现浅拷贝。 +```js +const clone = Object.create(Object.getPrototypeOf(obj), + Object.getOwnPropertyDescriptors(obj)); + +// 或者 +const shallowClone = (obj) => Object.create( + Object.getPrototypeOf(obj), + Object.getOwnPropertyDescriptors(obj) +); +``` + +### 3.5 字符串填充 padStart和padEnd +用来为字符串填充特定字符串,并且都有两个参数:**字符串目标长度**和**填充字段**,第二个参数可选,默认空格。 +```js +'es8'.padStart(2); // 'es8' +'es8'.padStart(5); // ' es8' +'es8'.padStart(6, 'woof'); // 'wooes8' +'es8'.padStart(14, 'wow'); // 'wowwowwowwoes8' +'es8'.padStart(7, '0'); // '0000es8' + +'es8'.padEnd(2); // 'es8' +'es8'.padEnd(5); // 'es8 ' +'es8'.padEnd(6, 'woof'); // 'es8woo' +'es8'.padEnd(14, 'wow'); // 'es8wowwowwowwo' +'es8'.padEnd(7, '6'); // 'es86666' +``` +从上面结果来看,填充函数只有在字符长度小于目标长度时才有效,若字符长度已经等于或小于目标长度时,填充字符不会起作用,而且目标长度如果小于字符串本身长度时,字符串也不会做截断处理,只会原样输出。 + +### 3.6 函数参数列表与调用中的尾部逗号 +该特性允许我们在定义或者调用函数时添加尾部逗号而不报错: +```js +function es8(var1, var2, var3,) { + // ... +} +es8(10, 20, 30,); +``` + +### 3.7 共享内存与原子操作 +当内存被共享时,多个线程可以并发读、写内存中相同的数据。原子操作可以确保那些被读、写的值都是可预期的,即新的事务是在旧的事务结束之后启动的,旧的事务在结束之前并不会被中断。这部分主要介绍了 ES8 中新的构造函数 `SharedArrayBuffer` 以及拥有许多静态方法的命名空间对象 `Atomic` 。 +`Atomic` 对象类似于 `Math` 对象,拥有许多静态方法,所以我们不能把它当做构造函数。 `Atomic` 对象有如下常用的静态方法: + +* add /sub :为某个指定的value值在某个特定的位置增加或者减去某个值 +* and / or /xor :进行位操作 +* load :获取特定位置的值 \ No newline at end of file diff --git "a/Cute-Article/article/66-ES9\346\261\207\346\200\273.md" "b/Cute-Article/article/66-ES9\346\261\207\346\200\273.md" new file mode 100644 index 00000000..991eb667 --- /dev/null +++ "b/Cute-Article/article/66-ES9\346\261\207\346\200\273.md" @@ -0,0 +1,324 @@ +## 4. ES9 +### 4.1 对象的拓展运算符 +#### 4.1.1 介绍 +对象的拓展运算符,即对象的Rest/Spread属性,可将对象解构赋值用于从一个对象取值,搜键值对分配到指定对象上,与数组的拓展运算符类似: +```js +let {x, y, ...z} = {x:1, y:2, a:3, b:4}; +x; // 1 +y; // 2 +z; // {a:3, b:4} +``` +对象的解构赋值要求等号右边必须是个对象,所以如果等号右边是`undefined`或`null`就会报错无法转成对象。 +```js +let {a, ...b} = null; // 运行时报错 +let {a, ...b} = undefined; // 运行时报错 +``` +解构赋值必须是最后一个参数,否则报错。 +```js +let {...a, b, c} = obj; // 语法错误 +let {a, ...b, c} = obj; // 语法错误 +``` +**注意**: +* 1.解构赋值是浅拷贝。 +```js +let a = {a1: {a2: 1}}; +let {...b} = a; +a.a1.a2 = 'leo'; +b.a1.a2 = 'leo'; +``` +* 2.拓展运算符的解构赋值,不能复制继承自原型对象的属性。 +```js +let o1 = { a: 1 }; +let o2 = { b: 2 }; +o2.__proto__ = o1; +let { ...o3 } = o2; +o3; // { b: 2 } +o3.a; // undefined +``` + +#### 4.1.2 使用场景 +* 1.取出参数对象所有可遍历属性,拷贝到当前对象中。 +```js +let a = { a1:1, a2:2 }; +let b = { ...a }; +b; // { a1:1, a2:2 } + +// 类似Object.assign方法 +``` +* 2.合并两个对象。 +```js +let a = { a1:1, a2:2 }; +let b = { b1:11, b2:22 }; +let ab = { ...a, ...b }; // {a1: 1, a2: 2, b1: 11, b2: 22} +// 等同于 +let ab = Object.assign({}, a, b); +``` +* 3.将自定义属性放在拓展运算符后面,覆盖对象原有的同名属性。 +```js +let a = { a1:1, a2:2, a3:3 }; +let r = { ...a, a3:666 }; +// r {a1: 1, a2: 2, a3: 666} + +// 等同于 +let r = { ...a, ...{ a3:666 }}; +// r {a1: 1, a2: 2, a3: 666} + +// 等同于 +let r = Object.assign({}, a, { a3:666 }); +// r {a1: 1, a2: 2, a3: 666} +``` +* 4.将自定义属性放在拓展运算符前面,就会成为设置新对象的默认值。 +```js +let a = { a1:1, a2:2 }; +let r = { a3:666, ...a }; +// r {a3: 666, a1: 1, a2: 2} + +// 等同于 +let r = Object.assign({}, {a3:666}, a); +// r {a3: 666, a1: 1, a2: 2} + +// 等同于 +let r = Object.assign({a3:666}, a); +// r {a3: 666, a1: 1, a2: 2} +``` +* 5.拓展运算符后面可以使用表达式。 +```js +let a = { + ...(x>1? {a:!:{}), + b:2 +} +``` +* 6.拓展运算符后面如果是个空对象,则没有任何效果。 +```js +{...{}, a:1}; // {a:1} +``` +* 7.若参数是`null`或`undefined`则忽略且不报错。 +```js +let a = { ...null, ...undefined }; // 不报错 +``` +* 8.若有取值函数`get`则会执行。 +```js +// 不会打印 因为f属性只是定义 而不没执行 +let a = { + ...a1, + get f(){console.log(1)} +} + +// 会打印 因为f执行了 +let a = { + ...a1, + ...{ + get f(){console.log(1)} + } +} +``` + +### 4.2 正则表达式 s 修饰符 +在正则表达式中,点(`.`)可以表示任意单个字符,除了两个:用`u`修饰符解决**四个字节的UTF-16字符**,另一个是行终止符。 +**终止符**即表示一行的结束,如下四个字符属于“行终止符”: +* U+000A 换行符(\n) +* U+000D 回车符(\r) +* U+2028 行分隔符(line separator) +* U+2029 段分隔符(paragraph separator) +```js +/foo.bar/.test('foo\nbar') +// false +``` +上面代码中,因为`.`不匹配`\n`,所以正则表达式返回`false`。 +换个醒,可以匹配任意单个字符: +```js +/foo[^]bar/.test('foo\nbar') +// true +``` +ES9引入`s`修饰符,使得`.`可以匹配任意单个字符: +```js +/foo.bar/s.test('foo\nbar') // true +``` +这被称为`dotAll`模式,即点(`dot`)代表一切字符。所以,正则表达式还引入了一个`dotAll`属性,返回一个布尔值,表示该正则表达式是否处在`dotAll`模式。 +```js +const re = /foo.bar/s; +// 另一种写法 +// const re = new RegExp('foo.bar', 's'); + +re.test('foo\nbar') // true +re.dotAll // true +re.flags // 's' +``` +`/s`修饰符和多行修饰符`/m`不冲突,两者一起使用的情况下,`.`匹配所有字符,而`^`和`$`匹配每一行的行首和行尾。 + +### 4.3 异步遍历器 +在前面ES6章节中,介绍了Iterator接口,而ES6引入了“异步遍历器”,是为异步操作提供原生的遍历器接口,即`value`和`done`这两个属性都是异步产生的。 + +#### 4.3.1 异步遍历的接口 +通过调用遍历器的`next`方法,返回一个Promise对象。 +```js +a.next().then( + ({value, done}) => { + //... + } +) +``` +上述`a`为异步遍历器,调用`next`后返回一个Promise对象,再调用`then`方法就可以指定Promise对象状态变为`resolve`后执行的回调函数,参数为`value`和`done`两个属性的对象,与同步遍历器一致。 +与同步遍历器一样,异步遍历器接口也是部署在`Symbol.asyncIterator`属性上,只要有这个属性,就都可以异步遍历。 +```js +let a = createAsyncIterable(['a', 'b']); +//createAsyncIterable方法用于构建一个iterator接口 +let b = a[Symbol.asyncInterator](); + +b.next().then( result1 => { + console.log(result1); // {value: 'a', done:false} + return b.next(); +}).then( result2 => { + console.log(result2); // {value: 'b', done:false} + return b.next(); +}).then( result3 => { + console.log(result3); // {value: undefined, done:true} +}) +``` +另外`next`方法返回的是一个Promise对象,所以可以放在`await`命令后。 +```js +async function f(){ + let a = createAsyncIterable(['a', 'b']); + let b = a[Symbol.asyncInterator](); + console.log(await b.next());// {value: 'a', done:false} + console.log(await b.next());// {value: 'b', done:false} + console.log(await b.next());// {value: undefined, done:true} +} +``` +还有一种情况,使用`Promise.all`方法,将所有的`next`按顺序连续调用: +```js +let a = createAsyncIterable(['a', 'b']); +let b = a[Symbol.asyncInterator](); +let {{value:v1}, {value:v2}} = await Promise.all([ + b.next(), b.next() +]) +``` +也可以一次调用所有`next`方法,再用`await`最后一步操作。 +```js +async function f(){ + let write = openFile('aaa.txt'); + write.next('hi'); + write.next('leo'); + await write.return(); +} +f(); +``` +#### 4.3.2 for await...of +`for...of`用于遍历同步的Iterator接口,而ES8引入`for await...of`遍历异步的Iterator接口。 +```js +async function f(){ + for await(let a of createAsyncIterable(['a', 'b'])) { + console.log(x); + } +} +// a +// b +``` +上面代码,`createAsyncIterable()`返回一个拥有异步遍历器接口的对象,`for...of`自动调用这个对象的`next`方法,得到一个Promise对象,`await`用来处理这个Promise,一但`resolve`就把得到的值`x`传到`for...of`里面。 +**用途** +直接把部署了asyncIteable操作的异步接口放入这个循环。 +```js +let a = ''; +async function f(){ + for await (let b of req) { + a += b; + } + let c = JSON.parse(a); + console.log('leo', c); +} +``` +当`next`返回的Promise对象被`reject`,`for await...of`就会保错,用`try...catch`捕获。 +```js +async function f(){ + try{ + for await (let a of iterableObj()){ + console.log(a); + } + }catch(e){ + console.error(e); + } +} +``` +注意,`for await...of`循环也可以用于同步遍历器。 +```js +(async function () { + for await (let a of ['a', 'b']) { + console.log(a); + } +})(); +// a +// b +``` +#### 4.3.3 异步Generator函数 +就像 Generator 函数返回一个同步遍历器对象一样,异步 Generator 函数的作用,是返回一个异步遍历器对象。 +在语法上,异步 Generator 函数就是`async`函数与 Generator 函数的结合。 +```js +async function* f() { + yield 'hi'; +} +const a = f(); +a.next().then(x => console.log(x)); +// { value: 'hello', done: false } +``` +设计异步遍历器的目的之一,就是为了让Generator函数能用同一套接口处理同步和异步操作。 +```js +// 同步Generator函数 +function * f(iterable, fun){ + let a = iterabl[Symbol.iterator](); + while(true){ + let {val, done} = a.next(); + if(done) break; + yield fun(val); + } +} + +// 异步Generator函数 +async function * f(iterable, fun){ + let a = iterabl[Symbol.iterator](); + while(true){ + let {val, done} = await a.next(); + if(done) break; + yield fun(val); + } +} +``` +同步和异步Generator函数相同点:在`yield`时用`next`方法停下,将后面表达式的值作为`next()`返回对象的`value`。 +在异步Generator函数中,同时使用`await`和`yield`,简单样理解,`await`命令用于将外部操作产生的值输入函数内部,`yield`命令用于将函数内部的值输出。 +```js +(async function () { + for await (const line of readLines(filePath)) { + console.log(line); + } +})() +``` +异步 Generator 函数可以与`for await...of`循环结合起来使用。 +```js +async function* f(asyncIterable) { + for await (const line of asyncIterable) { + yield '> ' + line; + } +} +``` + +#### 4.3.4 yield* 语句 +`yield*`语句跟一个异步遍历器。 +```js +async function * f(){ + yield 'a'; + yield 'b'; + return 'leo'; +} +async function * g(){ + const a = yield* f(); // a => 'leo' +} +``` +与同步 Generator 函数一样,`for await...of`循环会展开`yield*`。 +```js +(async function () { + for await (const x of gen2()) { + console.log(x); + } +})(); +// a +// b +``` diff --git "a/Cute-Article/article/67-JS\347\256\255\345\244\264\345\207\275\346\225\260\347\232\204\351\200\202\347\224\250\345\222\214\344\270\215\351\200\202\347\224\250\347\232\204\345\234\272\346\231\257.md" "b/Cute-Article/article/67-JS\347\256\255\345\244\264\345\207\275\346\225\260\347\232\204\351\200\202\347\224\250\345\222\214\344\270\215\351\200\202\347\224\250\347\232\204\345\234\272\346\231\257.md" new file mode 100644 index 00000000..700beac8 --- /dev/null +++ "b/Cute-Article/article/67-JS\347\256\255\345\244\264\345\207\275\346\225\260\347\232\204\351\200\202\347\224\250\345\222\214\344\270\215\351\200\202\347\224\250\347\232\204\345\234\272\346\231\257.md" @@ -0,0 +1,210 @@ +## JavaScript 箭头函数究竟是什么? +JavaScript 箭头函数大致相当于 `python` 中的 `lambda` 函数 或 `Ruby` 中的` blocks`。 + +这些是匿名函数,它们有自己的特殊语法,接受一定数量的参数,并在其封闭的作用域的上下文(即定义它们的函数或其他代码)中操作。 + +让我们依次分解这些部分。 + +### 箭头函数语法 +箭头函数具有单一的总体结构,然后在特殊情况下可以通过多种方式简化它们。 核心结构如下所示: +```js +(argument1, argument2, ... argumentN) => { + // function body +} +``` +括号内的是参数列表,后跟“胖箭头”(`=>`),最后是函数体。 + +这与传统函数非常相似,我们只是省略 `function` 关键字并在参数后添加一个胖箭头(`=>`)。 + +然而,有许多方法可以简化箭头函数。 + +首先,如果函数体是单个表达式,则可以不使用花括号并将其置于内联中(省略大括号直接将表达式写在一行中)。 表达式的结果将由函数返回。 例如: +```js +const add = (a, b) => a + b; +``` +其次,如果只有一个参数,你甚至可以省略参数的括号。例如: +```js +const getFirst = array => array[0]; +``` +正如您所看到的,这是一些非常简洁的语法,我们将重点介绍后面的好处。 + +### 高级语法 +有一些高级语法可以了解一下。 + +首先,如果您尝试使用内联单行表达式语法,但您返回的值是对象字面量。您可能会认为这看起来应该是这样的: +```js +(name, description) => {name: name, description: description}; +``` +问题是这种语法比较含糊不清,容易引起歧义 : 看起来好像你正试图创建一个传统的函数体。 如果你碰巧想要一个对象的单个表达式,请用括号包裹该对象: +```js +(name, description) => ({name: name, description: description}); +``` + +### 封闭的上下文作用域 +与其他形式的函数不同,箭头函数没有自己的 [执行期上下文](https://blog.bitsrc.io/understanding-execution-context-and-execution-stack-in-javascript-1c9ea8642dd0)。 + +实际上,这意味着 this 和 arguments 都是从它们的父函数继承而来的。 + +例如,使用和不使用箭头函数比较以下代码: +```js +const test = { + name: 'test object', + createAnonFunction: function() { + return function() { + console.log(this.name); + console.log(arguments); + }; + }, + + createArrowFunction: function() { + return () => { + console.log(this.name); + console.log(arguments); + }; + } +}; +``` +我们有一个简单的 `test` 对象,有两个方法 – 每个方法都返回一个匿名函数。 + +不同之处在于第一个方法使用传统函数表达式,而后者中使用箭头函数。 + +如果我们使用相同参数,在控制台中运行它们,我们会得到完全不一样的结果。 +```js +const anon = test.createAnonFunction('hello', 'world'); +const arrow = test.createArrowFunction('hello', 'world'); +anon(); // undefined {} +arrow(); //test object { '0': 'hello', '1': 'world' } +``` +第一个匿名函数有自己的函数上下文,因此当您调用它时,`test` 对象的 `this.name` 没有可用的引用,也没有创建它时调用的参数。 + +另一个,箭头函数具有与创建它的函数完全相同的函数上下文,使其可以访问 `argumetns` 和 `test` 对象。 + +### 使用箭头函数改进您的代码 +传统 `lambda` 函数的主要用例之一,就是用于遍历列表中的项,现在用 `JavaScript` 箭头函数实现。 + +比如你有一个有值的数组,你想去 `map` 遍历每一项,这时使用箭头函数非常理想: +```js +const words = ['hello', 'WORLD', 'Whatever']; +const downcasedWords = words.map(word => word.toLowerCase()); +``` +一个非常常见的例子是提取对象中的某个特定值: +```js +const names = objects.map(object => object.name); +``` +类似地,当用现代迭代样式取代传统的 for 循环,一般我们使用 forEach 循环,箭头函数能够保持 `this` 来自于父级,让他们非常直观 + +类似的,当用 forEach 来替换传统 for循环的时候,实际上箭头函数会直观的保持 `this`来自于父一级 + +```js +this.examples.forEach(example => { + this.runExample(example); +}); +``` + +### Promises 和 Promise 链 +箭头函数的另一个可以使代码更清晰,更直观的地方是管理异步代码。 + +[Promises](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_promises) 使得管理异步代码变得容易很多(即使你很欢快地使用 `async` / `await`,你仍然应该理解 [`async` / `await` 是建立在 Promises 之上的](https://medium.com/@bluepnume/learn-about-promises-before-you-start-using-async-await-eb148164a9c8) !) + +但是,虽然使用 `promises`仍然需要定义在异步代码或调用完成后运行的函数。 + +这是箭头函数的理想位置,特别是如果您生成的函数是有状态的,同时想引用对象中的某些内容。 例如: +```js +this.doSomethingAsync().then((result) => { + this.storeResult(result); +}); +``` + +### 对象转换 +箭头函数的另一个常见且极其强大的用途是封装对象转换。 + +例如,在 Vue.js 中,有一种通用模式,用于使用 `mapState` 将 Vuex 存储的各个部分直接包含到 Vue 组件中。 + +这涉及定义一组 “`mappers`” ,这些 “`mappers`” 将从原始的完整的 `state` 对象转换为提取所涉及组件所需的内容。 + +这些简单的转换使用箭头函数再合适不过了。比如: +```js +export default { + computed: { + ...mapState({ + results: state => state.results, + users: state => state.users, + }); + } +} +``` + +### 你不应该使用箭头函数的情景 +在许多情况下,使用箭头函数不是一个好主意。 他们不仅不会帮助你,而且会给你带来一些不必要的麻烦。 + +第一个是对象的方法。 这是一个函数上下文的例子,这对于我们理解很有帮助。 + +有一段时间使用 Class(类)属性语法和箭头函数的组合,作为创建“自动绑定方法”的方式,例如, 事件处理程序可以使用,但仍然绑定到类的方法。 + +这看起来像是这样的: + +```js +class Counter { + counter = 0; + + handleClick = () => { + this.counter++; + } +} +``` +这样,即使 `handleClick` 由事件处理程序调用,而不是在 `Counter` 实例的上下文中调用,它仍然可以访问实例的数据。 + +这种方法的缺点很多,在本文中很好地记录。 + +虽然使用这种方法确实为您提供了具有绑定函数的快捷方式,但该函数以多种不直观的方式运行,如果您尝试将此对象作为原型进行子类化/使用,则会不利于测试,同时也会产生很多问题。 + +相反,使用常规函数,如果需要,将其绑定到构造函数中的实例: + +```js +class Counter { + counter = 0; + + handleClick() { + this.counter++; + } + + constructor() { + this.handleClick = this.handleClick.bind(this); + } +} +``` + +### 深层的调用链 +箭头函数可能让你遇到麻烦的另一个地方是,它们被用于许多不同的组合,特别是在函数深层调用链中。 + +核心原因与匿名函数相同 – 它们给出了非常糟糕的堆栈跟踪。 + +如果你的函数只是向下一级,比如在迭代器里面,那也不是太糟糕,但是如果你把所有的函数定义为箭头函数,并在它们之间来回调用,你就会陷入困境 遇到一个错误的时候,只是收到错误消息,如: + +```js +{anonymous}() +{anonymous}() +{anonymous}() +{anonymous}() +{anonymous}() +``` + +### 有动态上下文的函数 +箭头函数可能让您遇到麻烦的最后一种情况就是吗, this 是动态绑定的时候。 + +如果您在这些位置使用箭头函数,那么动态绑定将不起作用,并且你(或稍后使用你的代码的其他人)可能会对事情未按预期执行的原因感到困惑。 + +一些典型的例子: + +* 事件处理程序是通过将 this 设置为事件的 currentTarget 属性来调用。 +* 如果您仍在使用 jQuery ,则大多数 jQuery 方法将 this 设置为已选择的 dom 元素。 +* 如果您正在使用 Vue.js ,则方法和计算函数通常将 this 设置为 Vue 组件。 +当然你可以故意使用箭头函数来覆盖这种行为,但特别是在 jQuery 和 Vue 的情况下,这通常会干扰正常运行,让你感到困惑的是为什么看起来与附近其他代码相同的代码不起作用。 + +## 总结 +箭头函数是 JavaScript 语言的一个非常有必要的补充,并且在许多情况下使代码更符合人们的阅读习惯。 + +然而,像所有其他特性一样,它们有优点和缺点。 我们应该将它们作为我们工具箱中的另一个工具,而不是作为所有函数的全面替代品。 + +英文原文:https://zendev.com/2018/10/01/javascript-arrow-functions-how-why-when.html +中文地址:https://www.css88.com/archives/10033 \ No newline at end of file diff --git "a/Cute-Article/article/68-\345\210\233\345\273\272\345\257\271\350\261\241\347\232\204\344\270\203\347\247\215\346\226\271\345\274\217.md" "b/Cute-Article/article/68-\345\210\233\345\273\272\345\257\271\350\261\241\347\232\204\344\270\203\347\247\215\346\226\271\345\274\217.md" new file mode 100644 index 00000000..548e09f3 --- /dev/null +++ "b/Cute-Article/article/68-\345\210\233\345\273\272\345\257\271\350\261\241\347\232\204\344\270\203\347\247\215\346\226\271\345\274\217.md" @@ -0,0 +1,212 @@ +JavaScript创建对象的方式有很多,通过Object构造函数或对象字面量的方式也可以创建单个对象,显然这两种方式会产生大量的重复代码,并不适合量产。接下来介绍七种非常经典的创建对象的方式,他们也各有优缺点。 + +## 1.工厂模式 +```js +function f(a, b) { + let a1 = new Object(); + a1.a = a; + a1.b = b; + a1.fun = function(){ + console.log(this.a); + } + return a1; +} + +let f1 = f('Leo','good'); +let f2 = f('Robin','nice'); +``` +可以无数次调用这个工厂函数,每次都会返回一个包含两个属性和一个方法的对象。 + +工厂模式虽然解决了**创建多个相似对象**的问题,但是没有解决对象识别问题,即不能知道一个对象的类型。 + +## 2.构造函数模式 +```js +function f (a, b){ + this.a = a; + this.b = b; + this.fun = function (){ + console.log(this.a) + } +} + +let f1 = new f('Leo','good'); +let f2 = new f('Robin','nice'); +``` +没有显示的创建对象,使用`new`来调用这个构造函数,使用`new`后会自动执行如下操作: +* 创建一个新对象 +* 这个新对象会被执行[[prototype]]链接 +* 这个新对象会绑定到函数调用的`this` +* 返回这个对象 + +使用这个方式创建对象可以检测对象类型 +```js +f1 instanceof Object; // true +f1 instanceof f; // true +``` +但是使用构造函数创建对象,每个方法都要在每个实例上**重新创建**一次。 + +## 3.原型模式 +```js +function f (){ + +} +f.prototype.a = 'Leo'; +f.prototype.b = 'good'; +f.prototype.fun = function (){ + console.log(this.a); +} +let f1 = new f(); +``` +将信息直接添加到原型对象上。使用原型的好处是可以让所有的实例对象共享它所包含的属性和方法,不必在构造函数中定义对象实例信息。 + +**更简单的写法** : +```js +function f (){ + +} +f.prototype = { + a : 'Leo'; + b : 'good'; + fun : function (){ + console.log(this.a); + } +} +let f1 = new f(); +``` +将`f.prototype`设置为等于一个以对象字面量形式创建的对象,但是会导致`.constructor`不在指向`f`了。 + +使用这种方式,完全重写了默认的`f.prototype`对象,因此 `.constructor`也不会存在这里。 +```js +f.prototype.constructor === f // false +``` +如果需要这个属性的话,可以手动添加: +```js +function f (){ + +} +f.prototype = { + constructor : f, + a : 'Leo'; + b : 'good'; + fun : function (){ + console.log(this.a); + } +} +let f1 = new f(); +``` +不过这种方式还是不够好,应为`constructor`属性默认是**不可枚举**的,这样直接设置,它将是可枚举的。所以可以时候,`Object.defineProperty`方法: +```js +Object.defineProperty(f.prototype, 'constructor', { + enumerable: false, + value: f +}) +``` +**缺点** + +使用原型,所有的属性都将被共享,这是个很大的优点,同样会带来一些缺点。 + +原型中所有属性实例是被很多实例共享的,这种共享对于函数非常合适。对于那些包含基本值的属性也勉强可以,毕竟实例属性可以屏蔽原型属性。但是引用类型值,就会出现问题了。 +```js +function f() { +} +f.prototype = { + a: 'leo', + b: ['good', 'nice'] +} +var f1 = new f() +var f2 = new f() +f1.b.push('hi') +console.log(f1.b) //["good", "nice", "hi"] +console.log(f2.b) //["good", "nice", "hi"] +console.log(f1.b === f2.b) // true +``` +`b`存在与原型中,实例f1和f2指向同一个原型,f1修改了引用的数组,也会反应到实例f2中。 + +## 4.组合使用构造函数模式和原型模式 +这是使用最为广泛、认同度最高的一种创建自定义类型的方法。它可以解决上面那些模式的缺点。 + +使用此模式可以让每个实例都会有自己的一份实例属性副本,但同时又共享着对方法的引用。 + +这样的话,即使实例属性修改引用类型的值,也不会影响其他实例的属性值了。 +```js +function f(a) { + this.a = a + this.friends = ['good', 'nice'] +} +f.prototype.fun = function() { + console.log(this.a) +} +var f1 = new f() +var f2 = new f() +f1.friends.push('hi') +console.log(f1.friends) //["good", "nice", "hi"] +console.log(f2.friends) // ["good", "nice"] +console.log(f1.friends === f2.friends) //false +``` + +## 5.动态原型模式 +动态原型模式将所有信息都封装在了构造函数中,初始化的时候,通过检测某个应该存在的方法时候有效,来决定是否需要初始化原型。 +```js +function f(a, b) { + // 属性 + this.a = a + this.b = b + + // 方法 + if(typeof this.fun !== 'function') { + f.prototype.fun = function() { + console.log(this.a) + } + } + +} +var f1 = new f('good', 'nice') +f1.fun() +``` +只有在`fun`方法不存在的时候,才会将它添加到原型中。这段代码只会初次调用构造函数的时候才会执行。 + +此后原型已经完成初始化,不需要在做什么修改了。 + +这里对原型所做的修改,能够立即在所有实例中得到反映。 + +其次,if语句检查的可以是初始化之后应该存在的任何属性或方法,所以不必用一大堆的if语句检查每一个属性和方法,只要检查一个就行。 + +## 6.寄生构造函数模式 +这种模式的基本思想就是创建一个函数,该函数的作用仅仅是封装创建对象的代码,然后再返回新建的对象 +```js +function f(a, b) { + var o = new Object() + o.a = a + o.b = b + o.fun = function() { + console.log(this.a) + } + return o +} +var f1 = new f('good', 'nice') +f1.fun() +``` +这个模式,除了使用`new`操作符并把使用的包装函数叫做构造函数之外,和工厂模式几乎一样。 + +构造函数如果不返回对象,默认也会返回一个新的对象,通过在构造函数的末尾添加一个`return`语句,可以重写调用构造函数时返回的值。 + +## 7.稳妥构造函数模式 +首先明白稳妥对象指的是没有公共属性,而且其方法也不引用`this`。 + +稳妥对象最适合在一些安全环境中(这些环境会禁止使用`this`和`new`),或防止数据被其他应用程序改动时使用。 + +稳妥构造函数模式和寄生模式类似,有两点不同:一是创建对象的实例方法不引用this,而是不使用new操作符调用构造函数。 +```js +function f(a, b) { + var o = new Object() + o.a = a + o.b = b + o.fun = function() { + console.log(a) + } + return o +} +var f1 = f('good', 'nice') +f1.fun() +``` +和寄生构造函数模式一样,这样创建出来的对象与构造函数之间没有什么关系,instanceof操作符对他们没有意义。 \ No newline at end of file diff --git "a/Cute-Article/article/69-\347\247\222\346\207\202this.md" "b/Cute-Article/article/69-\347\247\222\346\207\202this.md" new file mode 100644 index 00000000..79002c6e --- /dev/null +++ "b/Cute-Article/article/69-\347\247\222\346\207\202this.md" @@ -0,0 +1,317 @@ +> [打开原文](segmentfault.com/a/1190000017075730) + +日常开发中经常会遇到 `this` 指向的 bug,郁闷好久才猛然醒悟,痛定思痛,将 `this` 做个汇总,以便在日后的开发工作中少走弯路。 +注意:本文讲述只针对浏览器环境。 + +# 一:全局执行 +```js +console.log(this); +// Window +``` +可以看出在全局作用域中 `this` 指向当前的全局对象 `Window`。 + +# 二:函数中执行 +## 1. 非严格模式中 +```js +function func () { + console.log(this); +} +func(); +// Window +``` + +## 2. 严格模式中 +```js +"use strict"; +function func () { + console.log(this); +} +func(); +// undefined +``` + +# 三:作为对象的方法调用 +当一个函数被当作一个对象的方法调用的时候,`this` 指向当前的对象 `obj`: +```js +var obj = { + name: 'kk', + func: function () { + console.log(this.name); + } +} +obj.func(); +// kk +``` +如果把对象的方法赋值给一个变量,调用该方法时,`this` 指向` Window`: +```js +var obj = { + name: 'kk', + func: function () { + console.log(this); + } +} +var test = obj.func; +test(); +// Window +``` +# 四:作为一个构造函数使用 +在 JS 中,为了实现类,我们需要定义一些构造函数,在调用一个构造函数的时候加上 `new `这个关键字: +```js +function Person (name) { + this.name = name; + console.log(this); +} +var p1 = new Person('kk'); +// Person +``` +此时,`this` 指向这个构造函数调用的时候实例化出来的对象。 + +当然了,构造函数其实也是一个函数,若将构造函数当做普通函数来调用,`this` 指向 `Window`: +```js +function Person (name) { + this.name = name; + console.log(this); +} +var p2 = Person('MM'); +// Window +``` + +# 五:在定时器中使用 +```js +setInterval(function () { + console.log(this); +}, 2000) +// Window +setTimeout(function () { + console.log(this); +}, 0) +// Window +``` +如果没有特殊指向(指向更改请看下方:怎么改变 `this` 的指向),`setInterval` 和`setTimeout` 的回调函数中 `this` 的指向都是 `Window` 。这是因为 JS 的定时器方法是定义在` Window` 下的。 + +# 六:箭头函数 +在全局环境中调用: +```js +var func = () => { + console.log(this); +} +func(); +// Window +``` +作为对象的一个函数调用: +```js +var obj = { + name: 'hh', + func: function () { + console.log(this); + } +} +obj.func(); +// obj +var obj = { + name: 'hh', + func: () => { + console.log(this); + } +} +obj.func(); +// Window +``` +不难发现,普通函数作为对象的一个函数被调用,`this` 指向 `obj`,箭头函数作为对象的一个函数被调用,`this` 指向 `Window`。 + +特殊情况:结合定时器调用: +```js +var obj = { + name: 'hh', + func: function () { + setTimeout(function () { + console.log(this); + }, 0) + } +} +obj.func(); +// Window +var obj = { + name: 'hh', + func: function () { + setTimeout(() => { + console.log(this); + }, 0) + } +} +obj.func(); +// obj +``` +若在对象的函数中,普通函数作为定时器延时执行的函数调用,`this` 指向 `Window`;箭头函数作为定时器延时执行的函数调用, `this` 指向定义时所在的对象,也就是 `func `中的 `this`,即` obj`。 + +箭头函数中 `this `的值取决于该函数外部非箭头函数的`this `的值,且不能通过 `call()` 、` apply()` 和 `bind()` 方法来改变 `this` 的值。 +# 七:call、apply、bind +**1.call**: +```js +fun.call(thisArg[, arg1[, arg2[, ...]]]) +``` +它会立即执行函数,第一个参数是指定执行函数中 `this` 的上下文,后面的参数是执行函数需要传入的参数; + +**apply**: +```js +fun.apply(thisArg, [argsArray]) +``` +它也会立即执行函数,第一个参数是指定执行函数中 `this` 的上下文,第二个参数是一个数组,是传给执行函数的参数(与 `call` 的区别); + +**bind**: +```js +var foo = fun.bind(thisArg[, arg1[, arg2[, ...]]]); +``` +它不会执行函数,而是返回一个新的函数,这个新的函数被指定了 `this` 的上下文,后面的参数是执行函数需要传入的参数; + +我们来看个示例: +```js +function Person(name, age) { + this.name = name; + this.age = age; + console.log(this); +} +var obj = { + name: 'kk', + age: 6 +}; +Person.call(obj, 'mm', 10); +// obj,{name: "mm", age: 10} + +Person.apply(obj, ['mm', 10]); +// obj,{name: "mm", age: 10} + +var p1 = Person.bind(obj, 'mm', 10) +var p2 = new p1(); +// Person {name: "mm", age: 10} +``` +在这个示例中,`call`、`apply` 和 `bind` 的 `this` 都指向了` obj`,都能正常运行;`call`、`apply `会立即执行函数,`call` 和 `apply` 的区别就在于传递的参数,`call` 接收多个参数列表,`apply` 接收一个包含多个参数的数组;`bind` 不是立即执行函数,它返回一个函数,需要执行 `p2` 才能返回结果,`bind` 接收多个参数列表。 + +## 应用:怎么改变 this 的指向 +为什么讲这个模块呢,为了便于大家更加透彻的理解上面所讲述的 `this` 指向问题,以及更加彻底的理解 JS 函数中重要的三种方法:`call`、`apply`、`bind` 的使用;并且在实际的项目开发中,我们经常会遇到需要改变 this 指向的情况。 + +我们来看下都有哪些方法: + +## 1. 使用 es6 的箭头函数 +```js +var name = "hh"; +var obj = { + name : "kk", + func1: function () { + console.log(this.name) + }, + func2: function () { + setTimeout(function () { + this.func1() + }, 1000); + } +}; + +obj.func2(); +// Uncaught TypeError: this.func1 is not a function +``` +这时会报错,因为 `setTimeout` 里函数的 `thi`s 指向 `Window`,而 `Window` 对象上是没有 `func1` 这个函数的。下面我们来修改成箭头函数: +```js +var name = "hh"; +var obj = { + name : "kk", + func1: function () { + console.log(this.name) + }, + func2: function () { + setTimeout(() => { + this.func1() + }, 1000); + } +}; + +obj.func2(); +// kk +``` +这时候,没有报错,因为箭头函数的` this` 的值取决于该函数外部非箭头函数的 `this` 的值,也就是 `func2` 的 `this `的值, 即` obj`。 + +## 2. 在函数内部使用 _this = this +```js +var name = "hh"; +var obj = { + name : "kk", + func1: function () { + console.log(this.name) + }, + func2: function () { + let _this = this; + setTimeout(function () { + _this.func1() + }, 1000); + } +}; + +obj.func2(); +// kk +``` +此时,`func2` 也能正常运行。在` func`2 中,首先设置 `var _this = this`,这里的 `this` 是指向 `func2` 的对象 `obj`,为了防止在 `func2` 中的 `setTimeout` 被 `window` 调用而导致的在 `setTimeout` 中的 `this` 为 window。我们将 `this` (指向变量 `obj`) 赋值给一个变量 `_this`,这样,在 `func2` 中我们使用 `_this` 就是指向对象 `obj` 了。 + +## 3. 使用 call、apply、bind + +**call**: +```js +var name = "hh"; +var obj = { + name : "kk", + func1: function () {s + console.log(this.name) + }, + func2: function () { + setTimeout(function () { + this.func1() + }.call(obj), 1000); + } +}; + +obj.func2(); +// kk +``` + +**apply**: +```js +var name = "hh"; +var obj = { + name : "kk", + func1: function () { + console.log(this.name) + }, + func2: function () { + setTimeout(function () { + this.func1() + }.apply(obj), 1000); + } +}; + +obj.func2(); +// kk +``` + +**bind**: +```js +var name = "hh"; +var obj = { + name : "kk", + func1: function () { + console.log(this.name) + }, + func2: function () { + setTimeout(function () { + this.func1() + }.bind(obj)(), 1000); + } +}; + +obj.func2(); +// kk +``` +`call`、`apply`、`bind` 都能改变` this` 的上下文对象,所以也没有报错,可以正常执行。 + +具体原因可看上述第七点,`call`、`apply`、`bind`。 + +## 4. new 实例化一个对象 +如上:第四点,作为一个构造函数使用。 \ No newline at end of file diff --git "a/7-[\345\216\237\345\210\233]\347\274\251\345\260\217Vuejs\346\211\223\345\214\205\344\275\223\347\247\257\346\226\271\346\263\225.md" "b/Cute-Article/article/7-[\345\216\237\345\210\233]\347\274\251\345\260\217Vuejs\346\211\223\345\214\205\344\275\223\347\247\257\346\226\271\346\263\225.md" similarity index 100% rename from "7-[\345\216\237\345\210\233]\347\274\251\345\260\217Vuejs\346\211\223\345\214\205\344\275\223\347\247\257\346\226\271\346\263\225.md" rename to "Cute-Article/article/7-[\345\216\237\345\210\233]\347\274\251\345\260\217Vuejs\346\211\223\345\214\205\344\275\223\347\247\257\346\226\271\346\263\225.md" diff --git "a/Cute-Article/article/70-JS\345\244\215\346\235\202\345\210\244\346\226\255\347\232\204\346\233\264\344\274\230\351\233\205\345\206\231\346\263\225.md" "b/Cute-Article/article/70-JS\345\244\215\346\235\202\345\210\244\346\226\255\347\232\204\346\233\264\344\274\230\351\233\205\345\206\231\346\263\225.md" new file mode 100644 index 00000000..84ec490e --- /dev/null +++ "b/Cute-Article/article/70-JS\345\244\215\346\235\202\345\210\244\346\226\255\347\232\204\346\233\264\344\274\230\351\233\205\345\206\231\346\263\225.md" @@ -0,0 +1,303 @@ +> [阅读原文](https://juejin.im/post/5bdfef86e51d453bf8051bf8) + + +## 前提 +我们编写js代码时经常遇到复杂逻辑判断的情况,通常大家可以用`if/else`或者`switch`来实现多个条件判断,但这样会有个问题,随着逻辑复杂度的增加,代码中的`if/else/switch`会变得越来越臃肿,越来越看不懂,那么如何更优雅的写判断逻辑,本文带你试一下。 + +## 举个例子 +先看一段代码: +```js +/** + * 按钮点击事件 + * @param {number} status + * 活动状态:1 开团进行中 2 开团失败 3 商品售罄 4 开团成功 5 系统取消 + */ +const onButtonClick = (status)=>{ + if(status == 1){ + sendLog('processing') + jumpTo('IndexPage') + }else if(status == 2){ + sendLog('fail') + jumpTo('FailPage') + }else if(status == 3){ + sendLog('fail') + jumpTo('FailPage') + }else if(status == 4){ + sendLog('success') + jumpTo('SuccessPage') + }else if(status == 5){ + sendLog('cancel') + jumpTo('CancelPage') + }else { + sendLog('other') + jumpTo('Index') + } +} +``` +通过代码可以看到这个按钮的点击逻辑:根据不同活动状态做两件事情,发送日志埋点和跳转到对应页面,大家可以很轻易的提出这段代码的改写方案,`switch`出场: +```js +/** + * 按钮点击事件 + * @param {number} status + * 活动状态:1 开团进行中 2 开团失败 3 商品售罄 4 开团成功 5 系统取消 + */ +const onButtonClick = (status)=>{ + switch (status){ + case 1: + sendLog('processing') + jumpTo('IndexPage') + break + case 2: + case 3: + sendLog('fail') + jumpTo('FailPage') + break + case 4: + sendLog('success') + jumpTo('SuccessPage') + break + case 5: + sendLog('cancel') + jumpTo('CancelPage') + break + default: + sendLog('other') + jumpTo('Index') + break + } +} +``` +嗯,这样看起来比`if/else`清晰多了,细心的同学也发现了小技巧,`case 2`和`case 3`逻辑一样的时候,可以省去执行语句和`break`,则`case 2`的情况自动执行`case 3`的逻辑。 +这时有同学会说,还有更简单的写法: +```js +const actions = { + '1': ['processing','IndexPage'], + '2': ['fail','FailPage'], + '3': ['fail','FailPage'], + '4': ['success','SuccessPage'], + '5': ['cancel','CancelPage'], + 'default': ['other','Index'], +} +/** + * 按钮点击事件 + * @param {number} status + * 活动状态:1开团进行中 2开团失败 3 商品售罄 4 开团成功 5 系统取消 + */ +const onButtonClick = (status)=>{ + let action = actions[status] || actions['default'], + logName = action[0], + pageName = action[1] + sendLog(logName) + jumpTo(pageName) +} +``` +上面代码确实看起来更清爽了,这种方法的聪明之处在于:将判断条件作为对象的属性名,将处理逻辑作为对象的属性值,在按钮点击的时候,通过对象属性查找的方式来进行逻辑判断,这种写法特别适合一元条件判断的情况。 +是不是还有其他写法呢?有的: +```js +const actions = new Map([ + [1, ['processing','IndexPage']], + [2, ['fail','FailPage']], + [3, ['fail','FailPage']], + [4, ['success','SuccessPage']], + [5, ['cancel','CancelPage']], + ['default', ['other','Index']] +]) +/** + * 按钮点击事件 + * @param {number} status + * 活动状态:1 开团进行中 2 开团失败 3 商品售罄 4 开团成功 5 系统取消 + */ +const onButtonClick = (status)=>{ + let action = actions.get(status) || actions.get('default') + sendLog(action[0]) + jumpTo(action[1]) +} +``` +这样写用到了`es6`里的`Map`对象,是不是更爽了?`Map`对象和`Object`对象有什么区别呢? + +* 一个对象通常都有自己的原型,所以一个对象总有一个"`prototype`"键。 +* 一个对象的键只能是**字符串**或者**Symbols**,但一个`Map`的键可以是任意值。 +你可以通过`size`属性很容易地得到一个`Map`的键值对个数,而对象的键值对个数只能手动确认。 + +我们需要把问题升级一下,以前按钮点击时候只需要判断`status`,现在还需要判断用户的身份: +```js +/** + * 按钮点击事件 + * @param {number} status + * 活动状态:1开团进行中 2开团失败 3 开团成功 4 商品售罄 5 有库存未开团 + * @param {string} identity + * 身份标识:guest客态 master主态 + */ +const onButtonClick = (status,identity)=>{ + if(identity == 'guest'){ + if(status == 1){ + //do sth + }else if(status == 2){ + //do sth + }else if(status == 3){ + //do sth + }else if(status == 4){ + //do sth + }else if(status == 5){ + //do sth + }else { + //do sth + } + }else if(identity == 'master') { + if(status == 1){ + //do sth + }else if(status == 2){ + //do sth + }else if(status == 3){ + //do sth + }else if(status == 4){ + //do sth + }else if(status == 5){ + //do sth + }else { + //do sth + } + } +} +``` +原谅我不写每个判断里的具体逻辑了,因为代码太冗长了。 +原谅我又用了`if/else`,因为我看到很多人依然在用`if/else`写这种大段的逻辑判断。 +从上面的例子我们可以看到,当你的逻辑升级为二元判断时,你的判断量会加倍,你的代码量也会加倍,这时怎么写更清爽呢? +```js +const actions = new Map([ + ['guest_1', ()=>{/*do sth*/}], + ['guest_2', ()=>{/*do sth*/}], + ['guest_3', ()=>{/*do sth*/}], + ['guest_4', ()=>{/*do sth*/}], + ['guest_5', ()=>{/*do sth*/}], + ['master_1', ()=>{/*do sth*/}], + ['master_2', ()=>{/*do sth*/}], + ['master_3', ()=>{/*do sth*/}], + ['master_4', ()=>{/*do sth*/}], + ['master_5', ()=>{/*do sth*/}], + ['default', ()=>{/*do sth*/}], +]) + +/** + * 按钮点击事件 + * @param {string} identity 身份标识:guest客态 master主态 + * @param {number} status 活动状态:1 开团进行中 2 开团失败 3 开团成功 4 商品售罄 5 有库存未开团 + */ +const onButtonClick = (identity,status)=>{ + let action = actions.get(`${identity}_${status}`) || actions.get('default') + action.call(this) +} +``` +上述代码核心逻辑是:把两个条件拼接成字符串,并通过以条件拼接字符串作为键,以处理函数作为值的Map对象进行查找并执行,这种写法在多元条件判断时候尤其好用。 +当然上述代码如果用`Object`对象来实现也是类似的: +```js +const actions = { + 'guest_1':()=>{/*do sth*/}, + 'guest_2':()=>{/*do sth*/}, + //.... +} + +const onButtonClick = (identity,status)=>{ + let action = actions[`${identity}_${status}`] || actions['default'] + action.call(this) +} +``` +如果有些同学觉得把查询条件拼成字符串有点别扭,那还有一种方案,就是用`Map`对象,以`Object`对象作为`key`: +```js +const actions = new Map([ + [{identity:'guest',status:1},()=>{/*do sth*/}], + [{identity:'guest',status:2},()=>{/*do sth*/}], + //... +]) + +const onButtonClick = (identity,status)=>{ + let action = [...actions].filter(([key,value])=>(key.identity == identity && key.status == status)) + action.forEach(([key,value])=>value.call(this)) +} +``` +是不是又高级了一点点? +这里也看出来`Map`与`Object`的区别,`Map`可以用任何类型的数据作为`key`。 +我们现在再将难度升级一点点,假如`guest`情况下,`status1-4`的处理逻辑都一样怎么办,最差的情况是这样: +```js +const actions = new Map([ + [{identity:'guest',status:1},()=>{/* functionA */}], + [{identity:'guest',status:2},()=>{/* functionA */}], + [{identity:'guest',status:3},()=>{/* functionA */}], + [{identity:'guest',status:4},()=>{/* functionA */}], + [{identity:'guest',status:5},()=>{/* functionB */}], + //... +]) +``` +好一点的写法是将处理逻辑函数进行缓存: +```js +const actions = ()=>{ + const functionA = ()=>{/*do sth*/} + const functionB = ()=>{/*do sth*/} + return new Map([ + [{identity:'guest',status:1},functionA], + [{identity:'guest',status:2},functionA], + [{identity:'guest',status:3},functionA], + [{identity:'guest',status:4},functionA], + [{identity:'guest',status:5},functionB], + //... + ]) +} + +const onButtonClick = (identity,status)=>{ + let action = [...actions()].filter(([key,value])=>(key.identity == identity && key.status == status)) + action.forEach(([key,value])=>value.call(this)) +} +``` +这样写已经能满足日常需求了,但认真一点讲,上面重写了4次`functionA`还是有点不爽,假如判断条件变得特别复杂,比如`identity`有3种状态,`status`有10种状态,那你需要定义30条处理逻辑,而往往这些逻辑里面很多都是相同的,这似乎也是笔者不想接受的,那可以这样实现: +```js +const actions = ()=>{ + const functionA = ()=>{/*do sth*/} + const functionB = ()=>{/*do sth*/} + return new Map([ + [/^guest_[1-4]$/,functionA], + [/^guest_5$/,functionB], + //... + ]) +} + +const onButtonClick = (identity,status)=>{ + let action = [...actions()].filter(([key,value])=>(key.test(`${identity}_${status}`))) + action.forEach(([key,value])=>value.call(this)) + +} +``` +这里Map的优势更加凸显,可以用正则类型作为`key`了,这样就有了无限可能,假如需求变成,凡是`guest`情况都要发送一个日志埋点,不同`status`情况也需要单独的逻辑处理,那我们可以这样写: +```js +const actions = ()=>{ + const functionA = ()=>{/*do sth*/} + const functionB = ()=>{/*do sth*/} + const functionC = ()=>{/*send log*/} + return new Map([ + [/^guest_[1-4]$/,functionA], + [/^guest_5$/,functionB], + [/^guest_.*$/,functionC], + //... + ]) +} + +const onButtonClick = (identity,status)=>{ + let action = [...actions()].filter(([key,value])=>(key.test(`${identity}_${status}`))) + action.forEach(([key,value])=>value.call(this)) +} +``` +也就是说利用数组循环的特性,符合正则条件的逻辑都会被执行,那就可以同时执行公共逻辑和单独逻辑,因为正则的存在,你可以打开想象力解锁更多的玩法,本文就不赘述了。 + +## 总结 +本文已经教你了8种逻辑判断写法,包括: + +* `if/else` +* `switch` +* 一元判断时:存到`Object`里 +* 一元判断时:存到`Map`里 +* 多元判断时:将`condition`拼接成字符串存到`Object`里 +* 多元判断时:将`condition`拼接成字符串存到`Map`里 +* 多元判断时:将`condition`存为`Object`存到`Map`里 +* 多元判断时:将`condition`写作正则存到`Map`里 + +至此,本文也将告一段落,愿你未来的人生里,不只是有`if/else/switch`。 +如果你对本文感兴趣,请关注作者微信公众号:“大转转fe” \ No newline at end of file diff --git "a/Cute-Article/article/71-\345\244\215\344\271\240instanceof\350\277\220\347\256\227\347\254\246.md" "b/Cute-Article/article/71-\345\244\215\344\271\240instanceof\350\277\220\347\256\227\347\254\246.md" new file mode 100644 index 00000000..28c506fa --- /dev/null +++ "b/Cute-Article/article/71-\345\244\215\344\271\240instanceof\350\277\220\347\256\227\347\254\246.md" @@ -0,0 +1,142 @@ +最近开始在整理`ES6/ES7/ES8/ES9`的知识点(已经上传到 [我的博客](http://es.pingan8787.com) 上),碰到一些知识点是自己已经忘记(用得少的知识点),于是也重新复习了一遍。 +这篇文章要复习的 `instanceof` 是我在整理过程中遇到的,那就整理下来吧,不然容易忘记。 +要是哪里写得不妥,欢迎各位大佬指点。 + + +## 1.定义 +> `instanceof`运算符用于测试构造函数的`prototype`属性是否出现在对象的原型链中的任何位置。 —— MDN + +简单理解为:`instanceof`可以检测一个实例是否属于某种类型。 +比如: +```js +function F (){ + // ... +} +let a = new F (); + +a instanceof F; // true +a instanceof Object; // true 后面介绍原因 +``` +还可以在**继承关系**中用来判断一个实例是否属于它的父类型。 +比如: +```js +function F (){}; +function G (){}; +function Q (){}; +G.prototype = new F(); // 继承 + +let a = new G(); +a instanceof F; // true +a instanceof G; // true +a instanceof Q; // false +``` + +## 2.使用方法 +语法为: `object instanceof constructor`。 +* `object` : 需要测试的函数 +* `constructor` : 构造函数 + +即:用`instanceof`运算符来检测`constructor.prototype` 是否存在参数`object`的原型链。 +```js +function F (){}; +function G (){}; +let a = new F (); + +a instanceof F; // true 因为:Object.getPrototypeOf(a) === F.prototype +a instanceof Q; // false 因为:F.prototype不在a的原型链上 +a instanceof Object; // true 因为:Object.prototype.isPrototypeOf(a)返回true +F.prototype instanceof Object; // true,同上 +``` +**注意**: +1. `a instanceof F` 返回 `true` 以后,不一定永远都都返回为`true`,`F.prototype`属性的值有可能会改变。 +2. 原表达式`a`的值也会改变,比如 `a.__proto__ = {}`之后,`a instanceof F`就会返回`false`了。 + +**检测对象是不是特定构造函数的实例:** +```js +// 正确 +if (!(obj instanceof F)) { + // ... +} + +// 错误 因为 +if (!obj instanceof F); // 永远返回false +// 因为 !obj 在instanceof之前被处理 , 即一直使用一个布尔值检测是否是F的实例 +``` + +## 3.实现instanceof +```js +/** +* 实现instanceof +* @param obj{Object} 需要测试的对象 +* @param fun{Function} 构造函数 +*/ +function _instanceof(obj, fun) { + let f = fun.prototype; // 取B的显示原型 + obj = obj.__proto__; // 取A的隐式原型 + while (true) { + //Object.prototype.__proto__ === null + if (obj === null) + return false; + if (f === obj) // 这里重点:当 f 严格等于 obj 时,返回 true + return true; + obj = obj.__proto__; + } +} +``` + +## 4.instanceof 与 typeof 对比 +**相同**: +`instanceof`和`typeof`都能用来判断一个变量的类型。 + +**区别**: +`instanceof` 只能用来判断对象、函数和数组,不能用来判断字符串和数字等: +```js +let a = {}; +let b = function () {}; +let c = []; +let d = 'hi'; +let e = 123; + +a instanceof Object; // true +b instanceof Object; // true +c instanceof Array; // true +d instanceof String; // false +e instanceof Number; // false +``` + +`typeof` :用于判断一个表达式的原始值,返回一个字符串。 +```js +typeof 42; // "number" +typeof 'blubber'; // "string" +typeof true; // "boolean" +typeof aa; // "undefined" +``` +一般返回结果有: +* number +* boolean +* string +* function(函数) +* object(NULL,数组,对象) +* undefined。 + +**判断变量是否存在**: +不能使用: +```js +if(a){ + //变量存在 +} +// Uncaught ReferenceError: a is not defined +``` +原因是如果变量未定义,就会报**未定义**的错,而应该使用: +```js +if(typeof a != 'undefined'){ + //变量存在 +} +``` + +## 5.参考资料 +1. [MDN instanceof](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/instanceof) + +2. [IBM instanceof](https://www.ibm.com/developerworks/cn/web/1306_jiangjj_jsinstanceof/index.html) + +3. [js中typeof和instanceof用法区别](https://blog.csdn.net/qq_27626333/article/details/76146078) \ No newline at end of file diff --git "a/Cute-Article/article/72-\343\200\220\351\207\215\346\270\251\345\237\272\347\241\200\343\200\221JS\344\270\255\347\232\204\351\253\230\351\230\266\345\207\275\346\225\260.md" "b/Cute-Article/article/72-\343\200\220\351\207\215\346\270\251\345\237\272\347\241\200\343\200\221JS\344\270\255\347\232\204\351\253\230\351\230\266\345\207\275\346\225\260.md" new file mode 100644 index 00000000..76db077e --- /dev/null +++ "b/Cute-Article/article/72-\343\200\220\351\207\215\346\270\251\345\237\272\347\241\200\343\200\221JS\344\270\255\347\232\204\351\253\230\351\230\266\345\207\275\346\225\260.md" @@ -0,0 +1,206 @@ +Ps. 晚上加班到快十点,回来赶紧整理整理这篇文章,今天老大给我推荐了一篇文章,[我从写技术博客中收获到了什么?- J_Knight_](https://juejin.im/post/5c02b2bce51d4533253f2f43),感受也是很多,自己也需要慢慢养成记录博客的习惯,即使起步艰难,难以坚持,但还是要让自己加油加油。 +前两天把我整理的[【复习资料】ES6/ES7/ES8/ES9资料整理(个人整理)](https://juejin.im/post/5c02b106f265da61764aa0c1)要分享给大家啦。 + +正文内容开始: + +# 1.介绍 +个人简单理解为,一个函数可以接收其他函数作为参数,这种函数便称为**高阶函数**,而主要目的就是为了能接收其他函数作为参数。 +> Q: 为什么可以接收一个函数作为参数? +> A: 因为函数可以接收变量作为参数,而变量可以声明一个方法。 + +**简单实例:** +```js +function a (x){ + return 'hi ' + x; +} +function f (a, b){ + return a(b); +} +f(a, 'leo'); // "hi leo" +``` +**这段代码的意思**:定义方法`f`,接收两个参数,方法`a`和变量`b`,在方法`a`中返回一段字符串,当执行方法`f`并传入参数方法`a`和参数`b`的时候,返回`"hi leo"`。 + +也可以直接调用JS内置方法: +```js +let a = 3, b = -2; +function my_abs (val, fun){ + return fun(val); +} +my_abs(a, Math.abs); // 3 +my_abs(b, Math.abs); // 2 +``` + +# 2.常用高阶函数 + +## 2.1 map() +`map()`方法的作用是:接收一个函数作为参数,对数组中每个元素按顺序调用一次传入的函数并返回结果,**不改变原数组,返回一个新数组**。 +通常使用方式:`arr.map(callback())`,更多详细介绍可以参考 [MDN Array.map()](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/map)。 +**参数**: +* `arr` : 需要操作的数组; +* `callback(currentValue, index, array, thisArg)` : 处理的方法,四个参数; + * 1. `currentValue` 当前处理的元素的**值** + * 2. `index` 当前处理的元素的**索引**,可选 + * 3. `array` 调用`map()`方法的**数组**,可选 + * 4. `currentVthisArgalue` 执行 `callback` 函数时使用的 `this` 值,可选 + +**返回值**: +返回一个处理后的新数组。 + +**实例**: +```js +let arr = [1, 3, -5]; +let a1 = arr.map(Math.abs); +// a1 => [1, 3, 5]; + +let a2 = arr.map(String); +// a2 => ["1", "3", "-5"] + +let a3 = arr.map(function (x){ + return x + 1; +}) +// 等价于 a3=arr.map(x => x+1) +// a3 => [2, 4, -4] +``` +对比 `for...in` 循环,`map()`书写起来更加简洁: +```js +let arr = [1, 3, -5]; +let a1 = []; +for (var i=0; i [2, 4, -4] +``` +`map()`作为高阶函数,事实上它把运算规则抽象了。 + +## 2.2 reduce() +`reduce()`方法的作用是:接收一个函数,对数组进行累加操作,把累计结果和下一个值进行操作,最后返回最终的单个结果值。 + +通常使用方式:`arr.reduce(callback(), initValue)`,更多详细介绍可以参考 [MDN Array.reduce()](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce) + +**参数**: +* `callback(returnValue, currentValue, currentIndex, array)` : 累记器的方法,四个参数: + * 1. `returnValue` 上一次处理的返回值,或者初始值 + * 2. `currentValue` 当前处理的元素的**值**,可选 + * 3. `currentIndex` 当前处理的元素的**索引**,可选 + * 4. `array` 调用`reduce()`方法的**数组**,可选 +* `initValue` 初次调用`callback()`时候`returnValue`参数的初始值,默认数组第一个元素,可选 + +**返回值**: +返回一个最终的累计值。 + +**实例**: +1. 数组求和 +```js +let arr = [1, 3, -5]; +let sum1 = arr.reduce((res, cur) => res + cur); +// sum1 => -1 + +let sum2 = arr.reduce((res, cur) => res + cur , 1); +// sum1 => 0 +``` +2. 二维数组转化为一维 +```js +let arr = [[1, 2], [3, 4], [5, 6]]; +let con = arr.reduce((res, cur) => res.concat(cur)); +// con => [1, 2, 3, 4, 5, 6] +``` + +## 2.3 filter() +`filter()`方法的作用是:接收一个函数,依次作用数组每个元素,并过滤符合函数条件的元素,将剩下的数组作为一个新数组返回。 + +通常使用方式:`arr.filter(callback(), thisArg)`,更多详细介绍可以参考 [MDN Array.filter()](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/filter) + +**参数**: +* `callback(ele, index, array)` : 过滤条件的方法,当返回`true`则保存该元素,反之不保留,三个参数: + * 1. `ele` 当前处理的元素 + * 2. `index` 当前处理的元素的**索引**,可选 + * 3. `array` 调用`filter()`方法的**数组**,可选 +* `thisArg` 执行 `callback` 时的用于 `this` 的值,可选 + +**返回值**: +返回一个过滤剩下的元素组成的新数组。 + +**实例**: +1. 过滤奇数值 +```js +let arr = [1, 2, 3, 4, 5, 6]; +let res = arr.filter(x => x % 2 != 0); +// res => [1, 3, 5] +``` +2. 过滤不满足条件的值 +```js +let arr = [1, 2, 3, 4, 5, 6]; +let res = arr.filter(x => x > 3); +// res => [4, 5, 6] +``` +3. 过滤空字符串 +```js +let arr = ['a', '', null, undefined, 'b', '']; +let tri = arr.filter(x => x && x.trim()); +// tri => ["a", "b"] +``` +总结下: `filter()`主要作为**筛选功能**,因此核心就是正确实现一个“筛选”函数。 + +## 2.4 sort() +`sort()`方法的作用是:接收一个函数,对数组的元素进行排序,并返回排序后的新数组。**默认排序顺序是根据字符串Unicode码点**。 + +通常使用方式:`arr.sort(fun())`,更多详细介绍可以参考 [MDN Array.sort()](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/sort) +compareFunction 可选 +用来指定按某种顺序进行排列的函数。如果省略,元素按照转换为的字符串的各个字符的Unicode位点进行排序。 +**参数**: +* `fun(a, b)` : 指定按某种顺序进行排列的函数,若省略则按照转换为的字符串的各个字符的Unicode位点进行排序,两个可选参数: +`fun()` 返回 `a`和`b`两个值的大小的比较结果,`sort()`根据返回结果进行排序: +* 若 `fun(a, b)` 小于 0 ,则 `a` 排在 `b` 前面; +* 若 `fun(a, b)` 等于 0 ,则 `a` `b` 位置不变; +* 若 `fun(a, b)` 大于 0 ,则 `a` 排在 `b` 后面; + + +**返回值**: +返回排序后的新数组,并**修改原数组**。 + +**实例**: +1. 升序降序数组 +```js +let arr = [1,5,2,3]; +let sort1 = arr.sort(); +// 等同于 let sort1 = arr.sort((a, b) => a - b); +// sort1 => [1, 2, 3, 5] + +let sort2 = arr.sort((a, b) => b - a); +// sort2 => [5, 3, 2, 1] +``` +2. 字符串排序 +```js +let arr1 = ['AA', 'CC', 'BB']; +let sort1 = arr1.sort(); +// sort1 => ["AA", "BB", "CC"] + +let arr2 = ['AA', 'aa', 'BB']; +let sort2 = arr2.sort(); +// sort2 => ["AA", "BB", "aa"] + +let arr3 = ['AA', 'aa', 'BB']; +let sort3 = arr3.sort((a, b) => a.toLowerCase() > b.toLowerCase()); +// sort3 => ["AA", "aa", "BB"] +// 也可以写成: +let sort3 = arr3.sort((a, b) => { + let s1 = a.toLowerCase(); + let s2 = b.toLowerCase(); + return s1 > s2 ? 1 : s1 < s2 ? -1 : 0; +}) +``` +总结下: `sort()`主要作为**排序功能**,因此核心就是正确实现一个“排序”函数。 + +# 3. 参考文章 +* [阮一峰 JS高阶函数](https://www.liaoxuefeng.com/wiki/001434446689867b27157e896e74d51a89c25cc8b43bdb3000/001434499355829ead974e550644e2ebd9fd8bb1b0dd721000) + +分享的内容比较简单,但是还是希望能帮助到需要的人哈。~~感谢 + + +|Author|王平安| +|---|---| +|E-mail|pingan8787@qq.com| +|博 客|www.pingan8787.com| +|微 信|pingan8787| +|每日文章推荐|https://github.com/pingan8787/Leo_Reading/issues| +|JS小册|js.pingan8787.com| diff --git "a/Cute-Article/article/73-JavaScript \351\207\215\346\226\260\345\212\240\350\275\275\351\241\265\351\235\242\347\232\204 535 \347\247\215\346\226\271\346\263\225.md" "b/Cute-Article/article/73-JavaScript \351\207\215\346\226\260\345\212\240\350\275\275\351\241\265\351\235\242\347\232\204 535 \347\247\215\346\226\271\346\263\225.md" new file mode 100644 index 00000000..1631a778 --- /dev/null +++ "b/Cute-Article/article/73-JavaScript \351\207\215\346\226\260\345\212\240\350\275\275\351\241\265\351\235\242\347\232\204 535 \347\247\215\346\226\271\346\263\225.md" @@ -0,0 +1,539 @@ +使用 JavaScript 有多少种方式重新加载页面? + +```js +location = location +location = location.href +location = window.location +location = self.location +location = window.location.href +location = self.location.href +location = location['href'] +location = window['location'] +location = window['location'].href +location = window['location']['href'] +location = window.location['href'] +location = self['location'] +location = self['location'].href +location = self['location']['href'] +location = self.location['href'] +location.assign(location) +location.replace(location) +window.location.assign(location) +window.location.replace(location) +self.location.assign(location) +self.location.replace(location) +location['assign'](location) +location['replace'](location) +window.location['assign'](location) +window.location['replace'](location) +window['location'].assign(location) +window['location'].replace(location) +window['location']['assign'](location) +window['location']['replace'](location) +self.location['assign'](location) +self.location['replace'](location) +self['location'].assign(location) +self['location'].replace(location) +self['location']['assign'](location) +self['location']['replace'](location) +location.href = location +location.href = location.href +location.href = window.location +location.href = self.location +location.href = window.location.href +location.href = self.location.href +location.href = location['href'] +location.href = window['location'] +location.href = window['location'].href +location.href = window['location']['href'] +location.href = window.location['href'] +location.href = self['location'] +location.href = self['location'].href +location.href = self['location']['href'] +location.href = self.location['href'] +location.assign(location.href) +location.replace(location.href) +window.location.assign(location.href) +window.location.replace(location.href) +self.location.assign(location.href) +self.location.replace(location.href) +location['assign'](location.href) +location['replace'](location.href) +window.location['assign'](location.href) +window.location['replace'](location.href) +window['location'].assign(location.href) +window['location'].replace(location.href) +window['location']['assign'](location.href) +window['location']['replace'](location.href) +self.location['assign'](location.href) +self.location['replace'](location.href) +self['location'].assign(location.href) +self['location'].replace(location.href) +self['location']['assign'](location.href) +self['location']['replace'](location.href) +window.location = location +window.location = location.href +window.location = window.location +window.location = self.location +window.location = window.location.href +window.location = self.location.href +window.location = location['href'] +window.location = window['location'] +window.location = window['location'].href +window.location = window['location']['href'] +window.location = window.location['href'] +window.location = self['location'] +window.location = self['location'].href +window.location = self['location']['href'] +window.location = self.location['href'] +location.assign(window.location) +location.replace(window.location) +window.location.assign(window.location) +window.location.replace(window.location) +self.location.assign(window.location) +self.location.replace(window.location) +location['assign'](window.location) +location['replace'](window.location) +window.location['assign'](window.location) +window.location['replace'](window.location) +window['location'].assign(window.location) +window['location'].replace(window.location) +window['location']['assign'](window.location) +window['location']['replace'](window.location) +self.location['assign'](window.location) +self.location['replace'](window.location) +self['location'].assign(window.location) +self['location'].replace(window.location) +self['location']['assign'](window.location) +self['location']['replace'](window.location) +self.location = location +self.location = location.href +self.location = window.location +self.location = self.location +self.location = window.location.href +self.location = self.location.href +self.location = location['href'] +self.location = window['location'] +self.location = window['location'].href +self.location = window['location']['href'] +self.location = window.location['href'] +self.location = self['location'] +self.location = self['location'].href +self.location = self['location']['href'] +self.location = self.location['href'] +location.assign(self.location) +location.replace(self.location) +window.location.assign(self.location) +window.location.replace(self.location) +self.location.assign(self.location) +self.location.replace(self.location) +location['assign'](self.location) +location['replace'](self.location) +window.location['assign'](self.location) +window.location['replace'](self.location) +window['location'].assign(self.location) +window['location'].replace(self.location) +window['location']['assign'](self.location) +window['location']['replace'](self.location) +self.location['assign'](self.location) +self.location['replace'](self.location) +self['location'].assign(self.location) +self['location'].replace(self.location) +self['location']['assign'](self.location) +self['location']['replace'](self.location) +window.location.href = location +window.location.href = location.href +window.location.href = window.location +window.location.href = self.location +window.location.href = window.location.href +window.location.href = self.location.href +window.location.href = location['href'] +window.location.href = window['location'] +window.location.href = window['location'].href +window.location.href = window['location']['href'] +window.location.href = window.location['href'] +window.location.href = self['location'] +window.location.href = self['location'].href +window.location.href = self['location']['href'] +window.location.href = self.location['href'] +location.assign(window.location.href) +location.replace(window.location.href) +window.location.assign(window.location.href) +window.location.replace(window.location.href) +self.location.assign(window.location.href) +self.location.replace(window.location.href) +location['assign'](window.location.href) +location['replace'](window.location.href) +window.location['assign'](window.location.href) +window.location['replace'](window.location.href) +window['location'].assign(window.location.href) +window['location'].replace(window.location.href) +window['location']['assign'](window.location.href) +window['location']['replace'](window.location.href) +self.location['assign'](window.location.href) +self.location['replace'](window.location.href) +self['location'].assign(window.location.href) +self['location'].replace(window.location.href) +self['location']['assign'](window.location.href) +self['location']['replace'](window.location.href) +self.location.href = location +self.location.href = location.href +self.location.href = window.location +self.location.href = self.location +self.location.href = window.location.href +self.location.href = self.location.href +self.location.href = location['href'] +self.location.href = window['location'] +self.location.href = window['location'].href +self.location.href = window['location']['href'] +self.location.href = window.location['href'] +self.location.href = self['location'] +self.location.href = self['location'].href +self.location.href = self['location']['href'] +self.location.href = self.location['href'] +location.assign(self.location.href) +location.replace(self.location.href) +window.location.assign(self.location.href) +window.location.replace(self.location.href) +self.location.assign(self.location.href) +self.location.replace(self.location.href) +location['assign'](self.location.href) +location['replace'](self.location.href) +window.location['assign'](self.location.href) +window.location['replace'](self.location.href) +window['location'].assign(self.location.href) +window['location'].replace(self.location.href) +window['location']['assign'](self.location.href) +window['location']['replace'](self.location.href) +self.location['assign'](self.location.href) +self.location['replace'](self.location.href) +self['location'].assign(self.location.href) +self['location'].replace(self.location.href) +self['location']['assign'](self.location.href) +self['location']['replace'](self.location.href) +location['href'] = location +location['href'] = location.href +location['href'] = window.location +location['href'] = self.location +location['href'] = window.location.href +location['href'] = self.location.href +location['href'] = location['href'] +location['href'] = window['location'] +location['href'] = window['location'].href +location['href'] = window['location']['href'] +location['href'] = window.location['href'] +location['href'] = self['location'] +location['href'] = self['location'].href +location['href'] = self['location']['href'] +location['href'] = self.location['href'] +location.assign(location['href']) +location.replace(location['href']) +window.location.assign(location['href']) +window.location.replace(location['href']) +self.location.assign(location['href']) +self.location.replace(location['href']) +location['assign'](location['href']) +location['replace'](location['href']) +window.location['assign'](location['href']) +window.location['replace'](location['href']) +window['location'].assign(location['href']) +window['location'].replace(location['href']) +window['location']['assign'](location['href']) +window['location']['replace'](location['href']) +self.location['assign'](location['href']) +self.location['replace'](location['href']) +self['location'].assign(location['href']) +self['location'].replace(location['href']) +self['location']['assign'](location['href']) +self['location']['replace'](location['href']) +window['location'] = location +window['location'] = location.href +window['location'] = window.location +window['location'] = self.location +window['location'] = window.location.href +window['location'] = self.location.href +window['location'] = location['href'] +window['location'] = window['location'] +window['location'] = window['location'].href +window['location'] = window['location']['href'] +window['location'] = window.location['href'] +window['location'] = self['location'] +window['location'] = self['location'].href +window['location'] = self['location']['href'] +window['location'] = self.location['href'] +location.assign(window['location']) +location.replace(window['location']) +window.location.assign(window['location']) +window.location.replace(window['location']) +self.location.assign(window['location']) +self.location.replace(window['location']) +location['assign'](window['location']) +location['replace'](window['location']) +window.location['assign'](window['location']) +window.location['replace'](window['location']) +window['location'].assign(window['location']) +window['location'].replace(window['location']) +window['location']['assign'](window['location']) +window['location']['replace'](window['location']) +self.location['assign'](window['location']) +self.location['replace'](window['location']) +self['location'].assign(window['location']) +self['location'].replace(window['location']) +self['location']['assign'](window['location']) +self['location']['replace'](window['location']) +window['location'].href = location +window['location'].href = location.href +window['location'].href = window.location +window['location'].href = self.location +window['location'].href = window.location.href +window['location'].href = self.location.href +window['location'].href = location['href'] +window['location'].href = window['location'] +window['location'].href = window['location'].href +window['location'].href = window['location']['href'] +window['location'].href = window.location['href'] +window['location'].href = self['location'] +window['location'].href = self['location'].href +window['location'].href = self['location']['href'] +window['location'].href = self.location['href'] +location.assign(window['location'].href) +location.replace(window['location'].href) +window.location.assign(window['location'].href) +window.location.replace(window['location'].href) +self.location.assign(window['location'].href) +self.location.replace(window['location'].href) +location['assign'](window['location'].href) +location['replace'](window['location'].href) +window.location['assign'](window['location'].href) +window.location['replace'](window['location'].href) +window['location'].assign(window['location'].href) +window['location'].replace(window['location'].href) +window['location']['assign'](window['location'].href) +window['location']['replace'](window['location'].href) +self.location['assign'](window['location'].href) +self.location['replace'](window['location'].href) +self['location'].assign(window['location'].href) +self['location'].replace(window['location'].href) +self['location']['assign'](window['location'].href) +self['location']['replace'](window['location'].href) +window['location']['href'] = location +window['location']['href'] = location.href +window['location']['href'] = window.location +window['location']['href'] = self.location +window['location']['href'] = window.location.href +window['location']['href'] = self.location.href +window['location']['href'] = location['href'] +window['location']['href'] = window['location'] +window['location']['href'] = window['location'].href +window['location']['href'] = window['location']['href'] +window['location']['href'] = window.location['href'] +window['location']['href'] = self['location'] +window['location']['href'] = self['location'].href +window['location']['href'] = self['location']['href'] +window['location']['href'] = self.location['href'] +location.assign(window['location']['href']) +location.replace(window['location']['href']) +window.location.assign(window['location']['href']) +window.location.replace(window['location']['href']) +self.location.assign(window['location']['href']) +self.location.replace(window['location']['href']) +location['assign'](window['location']['href']) +location['replace'](window['location']['href']) +window.location['assign'](window['location']['href']) +window.location['replace'](window['location']['href']) +window['location'].assign(window['location']['href']) +window['location'].replace(window['location']['href']) +window['location']['assign'](window['location']['href']) +window['location']['replace'](window['location']['href']) +self.location['assign'](window['location']['href']) +self.location['replace'](window['location']['href']) +self['location'].assign(window['location']['href']) +self['location'].replace(window['location']['href']) +self['location']['assign'](window['location']['href']) +self['location']['replace'](window['location']['href']) +window.location['href'] = location +window.location['href'] = location.href +window.location['href'] = window.location +window.location['href'] = self.location +window.location['href'] = window.location.href +window.location['href'] = self.location.href +window.location['href'] = location['href'] +window.location['href'] = window['location'] +window.location['href'] = window['location'].href +window.location['href'] = window['location']['href'] +window.location['href'] = window.location['href'] +window.location['href'] = self['location'] +window.location['href'] = self['location'].href +window.location['href'] = self['location']['href'] +window.location['href'] = self.location['href'] +location.assign(window.location['href']) +location.replace(window.location['href']) +window.location.assign(window.location['href']) +window.location.replace(window.location['href']) +self.location.assign(window.location['href']) +self.location.replace(window.location['href']) +location['assign'](window.location['href']) +location['replace'](window.location['href']) +window.location['assign'](window.location['href']) +window.location['replace'](window.location['href']) +window['location'].assign(window.location['href']) +window['location'].replace(window.location['href']) +window['location']['assign'](window.location['href']) +window['location']['replace'](window.location['href']) +self.location['assign'](window.location['href']) +self.location['replace'](window.location['href']) +self['location'].assign(window.location['href']) +self['location'].replace(window.location['href']) +self['location']['assign'](window.location['href']) +self['location']['replace'](window.location['href']) +self['location'] = location +self['location'] = location.href +self['location'] = window.location +self['location'] = self.location +self['location'] = window.location.href +self['location'] = self.location.href +self['location'] = location['href'] +self['location'] = window['location'] +self['location'] = window['location'].href +self['location'] = window['location']['href'] +self['location'] = window.location['href'] +self['location'] = self['location'] +self['location'] = self['location'].href +self['location'] = self['location']['href'] +self['location'] = self.location['href'] +location.assign(self['location']) +location.replace(self['location']) +window.location.assign(self['location']) +window.location.replace(self['location']) +self.location.assign(self['location']) +self.location.replace(self['location']) +location['assign'](self['location']) +location['replace'](self['location']) +window.location['assign'](self['location']) +window.location['replace'](self['location']) +window['location'].assign(self['location']) +window['location'].replace(self['location']) +window['location']['assign'](self['location']) +window['location']['replace'](self['location']) +self.location['assign'](self['location']) +self.location['replace'](self['location']) +self['location'].assign(self['location']) +self['location'].replace(self['location']) +self['location']['assign'](self['location']) +self['location']['replace'](self['location']) +self['location'].href = location +self['location'].href = location.href +self['location'].href = window.location +self['location'].href = self.location +self['location'].href = window.location.href +self['location'].href = self.location.href +self['location'].href = location['href'] +self['location'].href = window['location'] +self['location'].href = window['location'].href +self['location'].href = window['location']['href'] +self['location'].href = window.location['href'] +self['location'].href = self['location'] +self['location'].href = self['location'].href +self['location'].href = self['location']['href'] +self['location'].href = self.location['href'] +location.assign(self['location'].href) +location.replace(self['location'].href) +window.location.assign(self['location'].href) +window.location.replace(self['location'].href) +self.location.assign(self['location'].href) +self.location.replace(self['location'].href) +location['assign'](self['location'].href) +location['replace'](self['location'].href) +window.location['assign'](self['location'].href) +window.location['replace'](self['location'].href) +window['location'].assign(self['location'].href) +window['location'].replace(self['location'].href) +window['location']['assign'](self['location'].href) +window['location']['replace'](self['location'].href) +self.location['assign'](self['location'].href) +self.location['replace'](self['location'].href) +self['location'].assign(self['location'].href) +self['location'].replace(self['location'].href) +self['location']['assign'](self['location'].href) +self['location']['replace'](self['location'].href) +self['location']['href'] = location +self['location']['href'] = location.href +self['location']['href'] = window.location +self['location']['href'] = self.location +self['location']['href'] = window.location.href +self['location']['href'] = self.location.href +self['location']['href'] = location['href'] +self['location']['href'] = window['location'] +self['location']['href'] = window['location'].href +self['location']['href'] = window['location']['href'] +self['location']['href'] = window.location['href'] +self['location']['href'] = self['location'] +self['location']['href'] = self['location'].href +self['location']['href'] = self['location']['href'] +self['location']['href'] = self.location['href'] +location.assign(self['location']['href']) +location.replace(self['location']['href']) +window.location.assign(self['location']['href']) +window.location.replace(self['location']['href']) +self.location.assign(self['location']['href']) +self.location.replace(self['location']['href']) +location['assign'](self['location']['href']) +location['replace'](self['location']['href']) +window.location['assign'](self['location']['href']) +window.location['replace'](self['location']['href']) +window['location'].assign(self['location']['href']) +window['location'].replace(self['location']['href']) +window['location']['assign'](self['location']['href']) +window['location']['replace'](self['location']['href']) +self.location['assign'](self['location']['href']) +self.location['replace'](self['location']['href']) +self['location'].assign(self['location']['href']) +self['location'].replace(self['location']['href']) +self['location']['assign'](self['location']['href']) +self['location']['replace'](self['location']['href']) +self.location['href'] = location +self.location['href'] = location.href +self.location['href'] = window.location +self.location['href'] = self.location +self.location['href'] = window.location.href +self.location['href'] = self.location.href +self.location['href'] = location['href'] +self.location['href'] = window['location'] +self.location['href'] = window['location'].href +self.location['href'] = window['location']['href'] +self.location['href'] = window.location['href'] +self.location['href'] = self['location'] +self.location['href'] = self['location'].href +self.location['href'] = self['location']['href'] +self.location['href'] = self.location['href'] +location.assign(self.location['href']) +location.replace(self.location['href']) +window.location.assign(self.location['href']) +window.location.replace(self.location['href']) +self.location.assign(self.location['href']) +self.location.replace(self.location['href']) +location['assign'](self.location['href']) +location['replace'](self.location['href']) +window.location['assign'](self.location['href']) +window.location['replace'](self.location['href']) +window['location'].assign(self.location['href']) +window['location'].replace(self.location['href']) +window['location']['assign'](self.location['href']) +window['location']['replace'](self.location['href']) +self.location['assign'](self.location['href']) +self.location['replace'](self.location['href']) +self['location'].assign(self.location['href']) +self['location'].replace(self.location['href']) +self['location']['assign'](self.location['href']) +self['location']['replace'](self.location['href']) +location.reload() +location['reload']() +window.location.reload() +window['location'].reload() +window.location['reload']() +window['location']['reload']() +self.location.reload() +self['location'].reload() +self.location['reload']() +self['location']['reload']() +``` \ No newline at end of file diff --git "a/Cute-Article/article/74-\343\200\212\345\244\247\345\211\215\347\253\257\345\267\245\347\250\213\345\214\226\347\232\204\346\227\266\345\256\236\350\267\265\345\222\214\346\200\235\350\200\203\343\200\213\347\213\274\345\217\224\345\210\206\344\272\253.md" "b/Cute-Article/article/74-\343\200\212\345\244\247\345\211\215\347\253\257\345\267\245\347\250\213\345\214\226\347\232\204\346\227\266\345\256\236\350\267\265\345\222\214\346\200\235\350\200\203\343\200\213\347\213\274\345\217\224\345\210\206\344\272\253.md" new file mode 100644 index 00000000..952deec2 --- /dev/null +++ "b/Cute-Article/article/74-\343\200\212\345\244\247\345\211\215\347\253\257\345\267\245\347\250\213\345\214\226\347\232\204\346\227\266\345\256\236\350\267\265\345\222\214\346\200\235\350\200\203\343\200\213\347\213\274\345\217\224\345\210\206\344\272\253.md" @@ -0,0 +1,350 @@ +> 本文来自 狼叔分享的《大前端工程化的时实践和思考》,如有侵权,联系删除。 + +### 一、前端发展历程 + +前端发展太快了,在2004年之前,大概只要会网页三剑客(一套强大的网页编辑工具,最初是 Macromedia 公司开发的,由 Dreamweaver 、 Fireworks 、 Flash 三个软件组成)就很牛了,那时候前端还比较“纯洁”。 + +在进入以 Ajax 为代表的**异步刷新改进用户体验的 Web 2.0 时代**后,开始涌现出大量的库,比如 `Prototype`、 `jQuery`、 `MooTools`、 `YUI`、 `Ext JS`、 `kissy `等,它们都还只是**对浏览器兼容性和工具类函数的封装**,可是从 `Backbone`、 `Angular 1.x` 相继出现后,前端就开始热闹起来了,出现 `MVC` 、 `MVVM` 、 `IOC` (控制反转,Java著名框架 `Spring` 里的概念)、前端路由(类似于 `Express` 、 `Koa` 等框架的路由)、`Virtual DOM`(虚拟DOM,通过 `DOM Diff` 算法,减少对DOM操作)、JSON API(接口规范)、JavaScript Runtime(Coffee、Babel、TypeScrpt)等等,各种框架格式如雨后春笋一样冒出来,以前可能半年甚至更长时间才出一个框架,现在可能几周就有新的框架诞生,前端进入了空前的爆发阶段。 + +![langshu1](http://images.pingan8787.com/20190710langshu01.png) + +目前整个大前端还属于发展期,没有形成固定模式,所以从趋势上看是上升期,复杂、混乱、多样性的结果也导致现在的前端比较吃香,被大家戏称为“钱端”。 + +很多同学会问,学习前端技术是采用渐进式方式,还是上来就直接学 `React`/`Vue.js` 等框架呢? + +实际上,我认为这两种方式都存在,如果有经济压力就向“钱“看,可以直接学习 `React`/`Vue.js` 等框架,但学会了之后,一定要把其他3个阶段补充学习完;如果没有经济压力,又有时间和耐心的话,循序渐进的学习是最好的方式。编程没有捷径,无论是哪种,都需要脚踏实地多多练习。 + +当你代码写多了,就会发现: + +当大家HTML写烦了,开始引入模板引擎。 +当大家CSS写烦了,比如不支持嵌套,开始引入CSS预处理器SASS、Less、Stylus、PostCSS等。 +当大家JavaScript写烦了,开始引入更友好的语言,熟悉Ruby的使用CoffeeScript、熟悉Java/C#的使用TypeScript。于是,前端开发变得复杂,并进入现代Web开发时代。 + + +![langshu1](http://images.pingan8787.com/20190710langshu02.png) + + +对于 `Node.js` 来说,所有前端框架都是视图层(View)的展现技术而已,可以非常方便地和各种框架集成,并按照业务需求予以更好的实现。另外,所有的前端框架都采用 `Node.js` 和 `NPM` 作为辅助开发工具,使用了大量 `Node.js` 模块,但前端框架使用的模块大同小异,如果熟练掌握了 `Node.js` 和 `NPM` ,对于学习前端技术来说,你要学的只是纯前端的部分而已,复用价值非常高。 + +和 `Node.js` 相关的前端开发模块实在是太多了,这里简单列举一些: + +![langshu1](http://images.pingan8787.com/20190710langshu03.png) + + +由这些模块,也就引出了我们今天要聊得话题——大前端工程化实践与思考。 + +先来说说构建工具。预处理器是前端高级玩法。 + +![langshu1](http://images.pingan8787.com/20190710langshu04.png) + +我经常开玩笑说以前 `HTML` / `JavaScript` / `CSS` 的时代太纯洁了,现在随便写哪样都要编译/转译。 + +好处是可以借助高级特性,提高开发效率;缺点也是极其明显的,就是人脑要有转换思维。 + +这其实是蛮痛苦的,本来 `HTML` / `JavaScript` / `CSS` 就不够熟练,再转一次,对于新手来说需要一个适应过程。所以这件事还是要辩证地看,福祸相依。 + +说起构建工具,大概都会想到 `Make` 、 `Ant` 、 `Rake` 、 `Gradle` 等,其实 `Node.js` 世界里有更多实现。 + + +![langshu1](http://images.pingan8787.com/20190710langshu05.png) + +构建工具的源码都不是特别复杂,所以 `Node.js` 世界里有非常多的实现,还有人写过`Node.js` 版本的 `Make` 呢,玩得很嗨! + +选用构建工具最主要的目的是为了自动化。对于需要反复重复的任务,例如**压缩(minification)、编译、单元测试、linting**等,自动化工具可以减轻你的劳动,简化你的工作。尤其是工程越复杂,自动化的价值就越大。 + +这里的编译包含**模板**、**CSS预处理语言**、**JavaScript友好语言**等编译,在源码编写时用的是高级玩法。 + + +除了编码编译外,还有测试、代码风格检查、上线前优化(合并、压缩、混淆),可以说,构建系统在整个软件工程里无处不在。 + +`Grunt` 是前端领域第一个流行的 `DSL` (领域定义语言)风格的构建工具,它的出现对于前端工程化起到了非常好的引导作用。它通过 `Gruntfile` 来描述 `task` ,同时通过插件机制来扩展各种工程能力。 + +当你在 `Gruntfile` 文件正确配置好了任务,任务运行器就会自动帮你或你的小组完成大部分无聊的工作。 + +`Grunt` 除了配置复杂外,还有一个问题就性能,可能很多同学遇不到性能问题。`Grunt` 是读写文件的,所以在多文件、大文件处理时是有性能瓶颈的。 + +`Grunt` 的设计还是挺精巧的,但配置起来让人抓狂,自然会有很多不爽的人,于是 `Gulp` 就慢慢崛起了。 + +简单来讲, `Gulp `是一个 `Node.js `写的构建工具,基于Stream的流式构建工具,它包含大量插件。 `Orchestrator `是 `Gulp `底层依赖的 `task `相关的核心库,它定义了 `task `执行方式和依赖,而且支持最大可能的并发, `Gulp `的高效即来源于此。本身Stream对大文件读写就非常棒,再加上上面说的种种特性,使得 `Gulp `流行成为必然。 + + `Gulp `的应用场景非常广,前端项目或Node项目都可以使用,哪怕是 `webpack `也可以和 `Gulp `搭配使用,通过 `gulp-webpack `模块即可。如果想深入学习 `Gulp `,可以看一下 `stuq-gulp `(https://github.com/i5ting/stuq-gulp),文中以 `WeUI `里 `Gulp `用法为例,由浅入深,从用法到原理进行了阐述。 + + +### 二、前端工程化的发展 + +解耦是软件开发领域永恒的主题,而模块化是目前最好的解耦方式,所以从无到有、从有到成熟的演化,必然要经历很长的路。 + +如今的发展,源于1993年HTML创建、1995年诞生 `JavaScript `、1996年发布 `CSS1 `,之后就进入了原始而野蛮的开发阶段。 + +从互联网诞生到2000年泡沫破灭,Web技术才算真正崛起。 + +那时候特别纯洁,会写的人就很牛了,之后 `Flash `也曾超级火爆。在之后就到了Web 2.0时代,开始出现 `Ajax `,开始有了各种兼容浏览器的库,然后开始模块化,在2009年诞生了 `Node.js `,彻底改变了 `JavaScript `以及前端开发领域的开发方式,从浏览器端的 `Backbone `支持 `MVC模式 `,到 `AngluarJS `支持的 `MVVM `,到现在的 `React `/ `Vue `和 `webpack `等。 + +这里我尝试从更宏观的视角来进行归类,大致分5个阶段,分别是原始阶段、包管理器、模块规范、模块加载器、模块打包器。下面我来一一说明。 + +#### 1.原始阶段 +脚本加载还都比较原始,方式如下: +* 使用多个script标签加载 +* 手动管理顺序 +* 手动管理加载哪些 + +在Web开发里经过了很多尝试,也做过很多龌蹉的事儿,比如: + +* 动态创建Script标签 +* XHR Eval、XHR Injection、$.getScript() +* Script in Iframe +* Script DOM Element +* Script Defer + +如果说加载比较恶心,那么脚本顺序更恶心,而且JavaScript有个“特性”,一处报错,所有后面的都会崩溃,所以开发会很苦的维护脚本加载的顺序。 + +#### 2.包管理器 + +如果代码存在一个问题,更新jQuery UI版本怎么做呢?jQuery UI依赖jQuery,先下载jQuery UI代码,然后找到依赖的jQuery版本,再替换已有文件,之后测试,如果没问题就直接替换了。 + +很明显,直接进行文件操作是非常低效率的做法,对于版本、依赖都没法做更好的处理。于是就出现了Bower、NPM等包管理器,所有模块升级,依赖都有包管理负责,可以说很大程度上省去了前端的重复性工作。 + +#### 3.模块规范 +**模块化加载的本质是按需加载**。 + +比较常见的规范有3个: + +1. AMD、CommonJS和ES6 Modules。 +2. 使用标准的模块系统来处理依赖和导出,每个文件是一个模块。 +3. 使用模块加载器或打包器进行处理。 + +#### 4.模块加载器 +模块加载器需要实现两个基本功能: + +* 实现模块定义规范,这是模块系统的基础 +* 模块系统的启动与运行 + +常见的比如 `RequireJS `、 `Sea.js `和 `SystemJS `。以 `AMD `的模块加载器 `RequireJS `为例,通常使用 `RequireJS `的话,我们只需要导入 `RequireJS `即可,不需要显式导入其他的JS库,因为这个工作会交给 `RequireJS `来做。 + +#### 5.模块打包器 + +模块加载器提供运行环境,能够让遵守模块规范的代码在上面跑,所以模块化的好处是很明显的。但对于真实项目来说,你还需要构建、打包等操作。 + +对于开发来说,只需要关注业务模块,不需要了解模块加载器和构建过程,很明显这是非常理想的,也因此产生了 `webpack `这样的模块打包器。 + + `Gulp `作为通用构建工具,它已经非常完美了。但技术变革太快了,应用各种预处理器、前端组件化,导致前端无比复杂,而 `webpack `的出现刚刚好,完美解决了前端工程化的问题。 + + `webpack `是 `Node `编写的著名模块,是打包器(`bundler`),不只是支持 `CommonJS `模块,而且还支持更潮的ES6模块,是目前使用极其广泛的打包器,像前端组件化的框架( `React `、 `Vue `)大多都是使用 `webpack `的。 + +它提供了两个极其好的机制: `loaders `和 `plugins `。 + + +* `loaders`:`webpack`认为每个文件都是资源模块,针对打包构建过程中用来处理源文件的(JSX、SCSS、Less..)统称为 `loader `。 + +* `plugins`:插件可以完成更多 `loader `不能完成的功能。插件并不直接操作单个文件,它直接对整个构建过程起作用,大多数内容功能都是基于这个插件系统运行的;当然还可以开发和使用开源的 `webpack `插件,来满足各式各样的需求。比如知名插件 `autoprefixer `、 `html-webpack-plugin `、 `webpack-dev-middleware `、 `webpack-hot-middleware `。 + +**下面来看一下webpack打包过程**: + +1. 从配置文件里找到`entry point` +1. 解析模块系统 +1. 解决依赖 +1. 解决依赖管理(读取、解析、解决) +1. 合并所有使用的模块 +1. 合并模块系统的运行时环境 +1. 产生打包后的文件 + +**浏览器加载过程:** + +1. 通过 ` +``` + +当我们写完以后,点击 **获取数据** 就会在控制台打印下面的数据: +```js +{ + "data":{ + "getSuperHero":{ + "name":"钢铁侠", + "age":46, + "doSomething": "I'm 钢铁侠, I'm watching TV now" + } + } +} +``` + +### 3. 注意点 + +* 请求中的 `query` 参数需要对照好有 `$` 符号的变量。 + +查询语句 `query GetSuperHero($heroName: String)` 里参数 `$heroName` 中的 `heroName` ; +查询语句 `getSuperHero(heroName: $heroName)` 里类型 `$heroName` 中的 `heroName` ; +变量 `variables` 中的 `heroName` 属性; + +**这三个名称需要一样**。 + +* 请求中需要将数据**序列化操作**。 + +```js +body: JSON.stringify({ query, variables }) +``` + +## 六、使用Mutations修改数据 + +### 1. Mutation 使用 + +根据前面的学习,我们知道,要做查询操作,需要使用 `Query` 来声明: +```JS +type Query { + queryHero(heroName: String): String +} +``` +当我们要做**修改操作**,需要用到的是 `Mutation` : +```js +type Mutation { + createHero(heroName: String): String +} +``` +如果 `Mutation` 中字段的形参是**自定义类型**,则类型需要用 `input` 标识: +```js +const schema = buildSchema(` + # 输入类型 用 input 标识 + input HeroInput { + name: String + age: Int + } + # 查询类型 + type Hero { + name: String + age: Int + } + type Mutation { + createHero(heroName: String): Hero + updateHero(heroName: String, hero: HeroInput): Hero + } +`) +``` +注意下:这里需要至少定义一个 `Query` 不然` GraphiQL` 会不显示查询: +```js +type Query { + hero: [Hero] +} +``` + +### 2. Mutation 使用案例 + +先创建一个 `schema` ,内容为上一步【1. Mutation 使用】中定义的内容,这里不重复写。 +然后模拟创建一个本地数据库 `localDb`, 用于模拟存放添加的超级英雄数据: +```js +const localDb = {} +``` +接下来声明 `root` 实现 `schema` 中的字段方法: +```js +const root = { + hero() { + // 这里需要转成数组 因为前面定义了返回值是 [Hero] 类型 + let arr = [] + for(const key in localDb){ + arr.push(localDb[key]) + } + return arr + }, + createHero({ input }) { + // 相当于数据库的添加操作 + localDb[input.name] = input + return localDb[input.name] + }, + updateHero({ id, input }) { + // 相当于数据库的更新操作 + const update = Object.assign({}, localDb[id], input) + localDb[id] = update + return update + } +} +``` + +最后配置 `graphqlHTTP` 方法和启动服务器,这里就不多重复咯。 + +最终代码: +```js +//...省略其他 +const schema = buildSchema(` + # 输入类型 用 input 标识 + input HeroInput { + name: String + age: Int + } + # 查询类型 + type Hero { + name: String + age: Int + } + type Mutation { + createHero(input: HeroInput): Hero + updateHero(id: ID!, input: HeroInput): Hero + } + # 需要至少定义一个 Query 不要GraphiQL会不显示查询 + type Query { + hero: [Hero] + } +`) + +const localDb = {} + +const root = { + hero() { + // 这里需要转成数组 因为前面定义了返回值是 [Hero] 类型 + let arr = [] + for(const key in localDb){ + arr.push(localDb[key]) + } + return arr + }, + createHero({ input }) { + // 相当于数据库的添加操作 + localDb[input.name] = input + return localDb[input.name] + }, + updateHero({ id, input }) { + // 相当于数据库的更新操作 + const update = Object.assign({}, localDb[id], input) + localDb[id] = update + return update + } +} +//...省略其他 +``` + +现在我们可以启动服务器,在 `GraphiQL` 上测试下效果了。 + +我们是使用 `mutation` 的 `createHero` 字段添加两条数据: +```js +mutation { + createHero(input: { + name: "钢铁侠" + age: 40 + }){ + name + age + } +} +``` +```js +mutation { + createHero(input: { + name: "美国队长" + age: 41 + }){ + name + age + } +} +``` +然后使用 `query` 的 `hero` 字段查询添加的结果: +```js +query { + hero { + name + age + } +} +``` +这样我们就获取到刚才的添加结果: +```js +{ + "data": { + "hero": [ + { + "name": "钢铁侠", + "age": 40 + }, + { + "name": "美国队长", + "age": 41 + } + ] + } +} +``` +然后我们开始更新数据,使用 `mutation` 的 `updateHero` 字段将 **美国队长** 的 `age` 值修改为 18: +```js +mutation { + updateHero(id: "美国队长", input: { + age: 18 + }){ + age + } +} +``` +再使用 `query` 的 `hero` 字段查询下新的数据,会发现 **美国队长** 的 `age` 值已经更新为 18: +```js +{ + "data": { + "hero": [ + { + "name": "钢铁侠", + "age": 40 + }, + { + "name": "美国队长", + "age": 18 + } + ] + } +} +``` + +## 七、认证和中间件 +我们知道,修改数据的接口不能让所有人随意访问,所以需要添加权限认证,让有权限的人才可以访问。 +在 `express` 中,可以很简单的使用**中间件**来将请求进行拦截,将没有权限的请求过滤并返回错误提示。 + +中间件实际上是一个函数,在接口执行之前,先拦截请求,再决定我们是否接着往下走,还是返回错误提示。 + +这在【六、使用Mutations修改数据】的最终代码上,在添加这个中间件: + +```js +//... 省略其他 +const app = express() +const middleWare = (req, res, next) => { + // 这里是简单模拟权限 + // 实际开发中 更多的是和后端进行 token 交换来判断权限 + if(req.url.indexOf('/graphql') !== -1 && req.headers.cookie.indexOf('auth') === -1){ + // 向客户端返回一个错误信息 + res.send(JSON.stringify({ + err: '暂无权限' + })) + return + } + next() // 正常下一步 +} +// 注册中间件 +app.use(middleWare) + +//... 省略其他 +``` + +这里的权限判断,只是简单模拟,实际开发中,更多的是和后端进行 `token` 交换来判断权限(或者其他形式)。 +我们重启服务器,打开 `http://localhost:3000/graphql` ,发现页面提示错误了,因为 `cookies` 中没有含有 `auth` 字符串。 + +如果这里提示 `TypeError: Cannot read property 'indexOf' of undefined` ,可以先不用管,因为浏览器中没有 `cookies` 的原因,其实前面的权限判断逻辑需要根据具体业务场景判断。 + + +为了方便测试,我们在 chrome 浏览器控制台的 `application` 下,手动设置一个含有 `auth` 字符串的一个 `cookies` ,只是测试使用哦。 + +设置完成后,我们就能正常进入页面。 + +## 八、ConstructingTypes + +在前面的介绍中,我们要创建一个 `schema` 都是使用 `buildSchema` 方法来定义,但我们也可以使用另外一种定义方式。 +就是这里要学习使用的构造函数 `graphql.GraphQLObjectType` 定义,它有这么几个优点和缺点: + +* **优点**:报错提醒更直观,结构更清晰,更便于维护。 +* **缺点**:代码量上升。 + + +### 1. 定义type(类型) + +这里先将前面定义的 `Hero` 类型进行改造: +```js +const graphql = require('graphql') // 需要引入 +const HeroType = new graphql.GraphQLObjectType({ + name: 'Hero', + fields: { + name:{ type: graphql.GraphQLString }, + age:{ type: graphql.GraphQLInt }, + } +}) +``` + +![graphql6](http://images.pingan8787.com/graphql_6.png) + +两者区别在于: + +|区别|`buildSchema`|`graphql.GraphQLObjectType`| +|:--:|:--:|:--:| +|参数类型|字符串|对象| +|类名|跟在 `type` 字符后面,这里是 `type Hero`|在参数对象的 `name` 属性上| +|属性定义|定义在类型后,**键值对**形式|定义在参数对象 `fields` 属性中,值为对象,每个属性名为键名,值也是对象,其中 `type` 属性的值为 `graphql` 中的属性,下面会补充| + +补充: +`fields` 属性中的子属性的类型通常有: +* `graphql.GraphQLString` +* `graphql.GraphQLInt` +* `graphql.GraphQLBoolean` +.... + +即在 `GraphQL`后面跟上基本类型名称。 + + +### 2. 定义query(查询) + +定义查询的时候,跟之前类似,可以参照下面对比图理解,这里比较不同的是,多了个 `resolve` 的方法,这个方法是用来执行处理查询的逻辑,其实就是之前的 `root` 中的方法。 + +```js +const QueryType = new graphql.GraphQLObjectType({ + name: 'Query', + fields: { + // 一个个查询方法 + getSuperHero: { + type: HeroType, + args: { + heroName: { type: graphql.GraphQLString } + }, + // 方法实现 查询的处理函数 + resolve: function(_, { heroName }){ + const name = heroName + const age = 18 + return { name, age } + } + } + } +}) +``` + +![graphql8](http://images.pingan8787.com/graphql_8.png) + +### 3. 创建 schema + +创建的时候只需实例化并且将参数传入即可: + +```js +// step3 构造 schema +const schema = new graphql.GraphQLSchema({ query: QueryType}) +``` + +最后使用也是和前面一样: +```js +const app = express() + +app.use('/graphql', graphqlHTTP({ + schema, + graphiql: true +})) +app.listen(3000) +``` + + +## 九、与数据库结合实战 + +我们试着使用前面所学的内容,开发一个简单的实践项目: +通过 `GraphiQL` 页面,往 `Mongodb` 中插入和更新数据,主要用到【六、使用Mutations修改数据】章节的操作。 + +### 1. 搭建并启动本地 Mongodb 数据库 +首先我们可以到 [Mongodb 官网](https://www.mongodb.com/download-center) 选择对应平台和版本下载安装。 +> 下载安装步骤,可以参考 [mongoDB下载、安装和配置](https://blog.csdn.net/qq_43288085/article/details/83187440),这里就不多介绍哟~~ + +安装完成后,我们打开两个终端,分别执行下面两行命令: +```bash +// 终端1 启动数据库 +mongod --dbpath c:\leo\app\mongodb\data\db + +// 终端2 进入数据库命令行操作模式 +mongo +``` + +### 2. 连接数据库,创建 Schema 和 Model +首先我们新建一个文件 `db.js` ,并 `npm install mongoose` 安装 `mongoose` ,然后写入下面代码,实现**连接数据库**: +```js +const express = require('express') +const { buildSchema } = require('graphql') +const graphqlHTTP = require('express-graphql') +const mongoose = require('mongoose') + +const DB_PATH = 'mongodb://127.0.0.1:27017/hero_table' +const connect = () => { + // 连接数据库 + mongoose.connect(DB_PATH) + // 连接断开 + mongoose.connection.on('disconnected', () => { + mongoose.connect(DB_PATH) + }) + // 连接失败 + mongoose.connection.on('error', err => { + console.error(err) + }) + // 连接成功 + mongoose.connection.on('connected', async () => { + console.log('Connected to MongoDB connected', DB_PATH) + }) +} +connect() +``` + +然后创建 `Schema` 和 `Model`: +```js +let HeroSchema = new mongoose.Schema({ + name: String, + age: Number +}) +let HeroModel = mongoose.model('hero',HeroSchema, 'hero_table') +``` + +### 3. 声明查询语句 + +这一步,还是先使用【六、使用Mutations修改数据】章节的操作逻辑,也就是先用**字符串创建查询**,而不使用 `GraphQLObjectType` 创建: +```js +const schema = buildSchema(` + # 输入类型 用 input 标识 + input HeroInput { + name: String + age: Int + } + # 查询类型 + type Hero { + name: String + age: Int + } + type Mutation { + createHero(input: HeroInput): Hero + updateHero(hero: String!, input: HeroInput): Hero + } + # 需要至少定义一个 Query 不要GraphiQL会不显示查询 + type Query { + hero: [Hero] + } +`) +``` +**这边案例有稍作修改** + +### 4. 实现添加数据和更新数据的逻辑 + +这边处理添加数据和更新数据的逻辑,就要修改之前声明的 `root` 的操作内容了: + +```js +const root = { + hero() { + return new Promise( (resolve, reject) => { + HeroModel.find((err, res) => { + if(err) { + reject(err) + return + } + resolve(res) + }) + }) + }, + createHero({ input }) { + // 实例化一个Model + const { name, age } = input + const params = new HeroModel({ name, age }) + return new Promise( (resolve, reject) => { + params.save((err, res) => { + if(err) { + reject(err) + return + } + resolve(res) + }) + }) + }, + updateHero({ hero, input }) { + const { age } = input + return new Promise ((resolve, reject) => { + HeroModel.update({name: hero}, {age}, (err, res) => { + if(err) { + reject(err) + return + } + resolve(res) + }) + }) + } +} +``` + +### 5. 模拟测试 + +最后我们在 `GraphiQL` 页面上模拟测试一下,首先添加两个英雄,钢铁侠和美国队长,并设置他们的 `age / name` 属性: +```js +mutation { + createHero(input: { + name: "钢铁侠" + age: 40 + }){ + name + age + } +} +``` +```js +mutation { + createHero(input: { + name: "美国队长" + age: 20 + }){ + name + age + } +} +``` + +页面和接口没有报错,说明我们添加成功,数据库中也有这两条数据了: + + +![graphql10](http://images.pingan8787.com/graphql_10.png) + +在测试下查询: + +```js +query { + hero { + name + age + } +} +``` +![graphql11](http://images.pingan8787.com/graphql_11.png) + +查询也正常,接下来测试下更新,将美国队长的 `age` 修改为 60: +```js +mutation { + updateHero(hero: "美国队长", input: { + age: 60 + }){ + age + } +} +``` + +![graphql12](http://images.pingan8787.com/graphql_12.png) + + +到这一步,我们也算是将这个练习做完了。 + +## 总结 + +* `GraphQL` 是一种 **API 的查询语言**,是 REST API 的替代品。 + +* `GraphQL` 可以使用一个请求,获取所有想要的数据。 + +* 创建查询的方式有两种:使用 `buildSchema` 或者 `GraphQLObjectType`。 + +* 查询操作用 `Query`,修改操作用 `Mutations`。 + +* 查询类型用 `type` ,输入类型用 `input`。 + +其实 `GraphQL` 还是很简单好用的呢~~~ + +--- + +> 本文首发在 [pingan8787个人博客](http://www.pingan8787.com),如需转载请保留个人介绍 + +|Author|王平安| +|---|---| +|E-mail|pingan8787@qq.com| +|博 客|www.pingan8787.com| +|微 信|pingan8787| +|每日文章推荐|https://github.com/pingan8787/Leo_Reading/issues| +|ES小册|js.pingan8787.com| + +## 微信公众号 +![bg](http://images.pingan8787.com/2019_07_12guild_page.png) \ No newline at end of file diff --git a/Cute-HTTP/README.md b/Cute-HTTP/README.md new file mode 100644 index 00000000..60232389 --- /dev/null +++ b/Cute-HTTP/README.md @@ -0,0 +1,16 @@ +## 💌仓库介绍 +**Cute-Hybrid 系列**主要分享我学习和实战**混合应用开发**的相关笔记和代码,目前分享主要以题目为主,其中涉及知识点内容较多,需要仔细学习,喜欢的朋友欢迎 👉star。 + +### 关于作者 +[![博客](http://images.pingan8787.com/icon_my1.png)](http://www.pingan8787.com) +[![知乎](http://images.pingan8787.com/icon_zhihu1.png)](https://zhuanlan.zhihu.com/cute-javascript) +[![掘金](http://images.pingan8787.com/icon_juejin2.png)](https://juejin.im/user/586fc337a22b9d0058807d53/posts) +[![思否](http://images.pingan8787.com/icon_sf1.png)](https://segmentfault.com/blog/pingan8787) +[![CSDN](http://images.pingan8787.com/icon_csdn1.png)](https://blog.csdn.net/qq_36380426) +[![简书](http://images.pingan8787.com/icon_jianshu1.png)](https://www.jianshu.com/u/2ec5d94afd60) + + +## 💌文章目录 + +1. [《HTTP 的15个常见知识点复习》](https://github.com/pingan8787/Leo-JavaScript/blob/master/Cute-HTTP/http_knowledge_point.md) + diff --git a/Cute-HTTP/http_knowledge_point.md b/Cute-HTTP/http_knowledge_point.md new file mode 100644 index 00000000..9f97c0bb --- /dev/null +++ b/Cute-HTTP/http_knowledge_point.md @@ -0,0 +1,693 @@ +### 前言 + +自从入职新公司到现在,我们前端团队内部一直在做 📖**每周一练** 的知识复习计划,我之前整理了一个 **[每周一练 之 数据结构与算法](https://juejin.im/post/5ce2a20e6fb9a07ebb05061c)** 学习内容,大家也快去看看~~ + +最近三周,主要复习 **网络基础** 相关的知识,今天我把这三周复习的知识和参考答案,整理成本文,欢迎各位朋友互相学习和指点,觉得本文不错,也欢迎点赞哈💕💕。 + +特别喜欢现在的每周学习和分享,哈哈哈~~😄 + +📅推荐一个 github 仓库 —— [《awesome-http》](https://github.com/semlinker/awesome-http),内容挺棒的。 + +> 注:本文整理资料来源网络,有些图片/段落找不到原文出处,如有侵权,联系删除。 + +### 一. 简述浏览器输入 URL 地址后发生的事情 + +#### 1.1 描述 +1. 浏览器向 DNS 服务器查找输入 URL 对应的 IP 地址。 +1. NS 服务器返回网站的 IP 地址。 +1. 浏览器根据 IP 地址与目标 web 服务器在 80 端口上建立 TCP 连接。 +1. 浏览器获取请求页面的 HTML 代码。 +1. 浏览器在显示窗口内渲染 HTML 。 +1. 窗口关闭时,浏览器终止与服务器的连接。 + + +#### 1.2 TCP 知识点补充 + +参考文章:[《TCP三次握手和四次挥手协议》](http://swiftlet.net/archives/1082) + +建立 TCP 需要三次握手才能建立,而断开连接则需要四次握手。整个过程如下图所示: + +![http3](http://images.pingan8787.com/20190622http3.jpg) + + +**TCP三次握手**: + +所谓的三次握手,是指建立一个 TCP 连接时,需要客户端和服务器端总共发送三个包,三次握手的目的是连接服务器的指定端口,建立 TCP 连接,并同步连接双方的序列号和确认号并交换 TCP 窗口大小信息,在 SOCKET 编程中,客户端执行 connect() 时,将会触发三次握手: + + +![http1](http://images.pingan8787.com/20190622http1.png) + +**TCP四次挥手:** + +TCP连接的拆除需要发送四个包,客户端或者服务器端均可主动发起挥手动作,在SOCKET编程中,任何一方执行close()即可产生挥手操作。 + +![http2](http://images.pingan8787.com/20190622http2.jpg) + + +### 2. 请介绍常见的 HTTP 状态码(至少五个) + +状态码是由 3 位数组成,第一个数字定义了响应的类别,且有五种可能取值: + +**1xx:指示信息–表示请求已接收,继续处理。** + +* 100 客户必须继续发出请求 + +* 101 客户要求服务器根据请求转换HTTP协议版本 + +**2xx:成功–表示请求已被成功接收、理解、接受。** + +* 200 (成功) 服务器已成功处理了请求。 通常,这表示服务器提供了请求的网页。 + +* 201 (已创建) 请求成功并且服务器创建了新的资源。 + +* 202 (已接受) 服务器已接受请求,但尚未处理。 + +**3xx:重定向–要完成请求必须进行更进一步的操作。** + +* 300 (多种选择) 针对请求,服务器可执行多种操作。 服务器可根据请求者 (user agent) 选择一项操作,或提供操作列表供请求者选择。 + +* 301 (永久移动) 请求的网页已永久移动到新位置。 服务器返回此响应(对 GET 或 HEAD 请求的响应)时,会自动将请求者转到新位置。 + +* 302 (临时移动) 服务器目前从不同位置的网页响应请求,但请求者应继续使用原有位置来进行以后的请求。 + +**4xx:客户端错误–请求有语法错误或请求无法实现。** + +* 400 (错误请求) 服务器不理解请求的语法。 + +* 401 (未授权) 请求要求身份验证。 对于需要登录的网页,服务器可能返回此响应。 + +* 403 (禁止) 服务器拒绝请求。 + +**5xx:服务器端错误–服务器未能实现合法的请求。** + +* 500 (服务器内部错误) 服务器遇到错误,无法完成请求。 + +* 501 (尚未实施) 服务器不具备完成请求的功能。 例如,服务器无法识别请求方法时可能会返回此代码。 + +* 502 (错误网关) 服务器作为网关或代理,从上游服务器收到无效响应。 + +* 503 (服务不可用) 服务器目前无法使用(由于超载或停机维护)。 通常,这只是暂时状态。 + +* 504 (网关超时) 服务器作为网关或代理,但是没有及时从上游服务器收到请求。 + +* 505 (HTTP 版本不受支持) 服务器不支持请求中所用的 HTTP 协议版本。 + +### 3. 请介绍常见的 HTTP 头部(至少五个) + +#### 3.1 HTTP 头部 + +更多完整内容,可以查看 [《HTTP响应头和请求头信息对照表》](tools.jb51.net/table/http_header) + +|首部字段名 | 说明| +|:---:|---| +|`Accept`|告诉服务器,客户端支持的数据类型。| +|`Accept-Charset`|告诉服务器,客户端采用的编码。| +|`Accept-Encoding`|告诉服务器,客户机支持的数据压缩格式。| +|`Accept-Language`|告诉服务器,客户机的语言环境。| +|`Host`|客户机通过这个头告诉服务器,想访问的主机名。| +|`If-Modified-Since`|客户机通过这个头告诉服务器,资源的缓存时间。| +|`Referer`|客户机通过这个头告诉服务器,它是从哪个资源来访问服务器的。(一般用于防盗链)| +|`User-Agent`|客户机通过这个头告诉服务器,客户机的软件环境。| +|`Cookie`|客户机通过这个头告诉服务器,可以向服务器带数据。| +|`Connection`|客户机通过这个头告诉服务器,请求完后是关闭还是保持链接。| +|`Date`|客户机通过这个头告诉服务器,客户机当前请求时间| + +#### 3.2 Request Header + +参考文章:[《HTTP常用头部信息》](https://www.cnblogs.com/amiezhang/p/9389840.html) + +举例: +| Request Header |描述| +|---|---| +|`GET /sample.Jsp HTTP/1.1` |请求行| +|`Host: www.uuid.online/` |请求的目标域名和端口号| +|`Origin: http://localhost:8081/` |请求的来源域名和端口号 (跨域请求时,浏览器会自动带上这个头信息)| +|`Referer: https:/localhost:8081/link?query=xxxxx` |请求资源的完整URI| +|`User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36` |浏览器信息| +|`Cookie: BAIDUID=FA89F036:FG=1; BD_HOME=1; sugstore=0` |当前域名下的Cookie| +|`Accept: text/html,image/apng` |代表客户端希望接受的数据类型是html或者是png图片类型 | +|`Accept-Encoding: gzip, deflate` |代表客户端能支持 gzip 和 deflate 格式的压缩| +|`Accept-Language: zh-CN,zh;q=0.9` |代表客户端可以支持语言 zh-CN 或者 zh (值得一提的是q(0~1)是优先级权重的意思,不写默认为1,这里 zh-CN 是1, zh 是0.9)| +|`Connection: keep-alive` |告诉服务器,客户端需要的 tcp 连接是一个长连接| + +#### 3.3 Response Header + +参考文章:[《HTTP常用头部信息》](https://www.cnblogs.com/amiezhang/p/9389840.html) + +举例: + +| Response Header |描述| +|---|---| +|`HTTP/1.1 200 OK`| 响应状态行| +|`Date: Mon, 30 Jul 2018 02:50:55 GMT`|服务端发送资源时的服务器时间| +|`Expires: Wed, 31 Dec 1969 23:59:59 GMT`|较过时的一种验证缓存的方式,与浏览器(客户端)的时间比较,超过这个时间就不用缓存(不和服务器进行验证),适合版本比较稳定的网页| +|`Cache-Control: no-cache`| 现在最多使用的控制缓存的方式,会和服务器进行缓存验|证,具体见博文 “Cache-Control” +|`etag: "fb8ba2f80b1d324bb997cbe188f28187-ssl-df"`| 一般是Nginx静态服务器发来的静态文件签名,浏览在没有 “Disabled cache” 情况下,接收到 etag 后,同一个 url 第二次请求就会自动带上 “If-None-Match”| +|`Last-Modified: Fri, 27 Jul 2018 11:04:55 GMT`|服务器发来的当前资源最后一次修改的时间,下次请求时,如果服务器上当前资源的修改时间大于这个时间,就返回新的资源内容| +|`Content-Type: text/html; charset=utf-8`|如果返回是流式的数据,我们就必须告诉浏览器这个头,不然浏览器会下载这个页面,同时告诉浏览器是utf8编码,否则可能出现乱码| +|`Content-Encoding: gzip`|告诉客户端,应该采用gzip对资源进行解码| +|`Connection: keep-alive`|告诉客户端服务器的tcp连接也是一个长连接| + +### 4. 请列举常用的 HTTP 方法,并介绍 GET 与 POST 请求之间的区别 + +#### 4.1 HTTP Request Method + +参考文章:[《HTTP请求方法对照表》](http://tools.jb51.net/table/http_request_method/) + +根据 HTTP 标准,HTTP 请求可以使用多种请求方法。 +HTTP1.0 定义了三种请求方法: GET, POST 和 HEAD方法。 +HTTP/1.1 新增了五种请求方法:OPTIONS, PUT, DELETE, TRACE 和 CONNECT 方法。 + +|序号|方法|描述| +|:---:|:---:|---| +|1|GET|请求指定的页面信息,并返回实体主体。| +|2|HEAD|类似于get请求,只不过返回的响应中没有具体的内容,用于获取报头| +|3|POST|向指定资源提交数据进行处理请求(例如提交表单或者上传文件)。数据被包含在请求体中。POST请求可能会导致新的资源的建立和/或已有资源的修改。| +|4|PUT|从客户端向服务器传送的数据取代指定的文档的内容。| +|5|DELETE|请求服务器删除指定的页面。| +|6|CONNECT|HTTP/1.1协议中预留给能够将连接改为管道方式的代理服务器。| +|7|OPTIONS|允许客户端查看服务器的性能。| +|8|TRACE|回显服务器收到的请求,主要用于测试或诊断。| +|9|PATCH|实体中包含一个表,表中说明与该URI所表示的原内容的区别。| +|10|MOVE|请求服务器将指定的页面移至另一个网络地址。| +|11|COPY|请求服务器将指定的页面拷贝至另一个网络地址。| +|12|LINK|请求服务器建立链接关系。| +|13|UNLINK|断开链接关系。| +|14|WRAPPED|允许客户端发送经过封装的请求。| +|15|Extension-mothed|在不改动协议的前提下,可增加另外的方法。| + +#### 4.2 GET 与 POST 请求之间的区别 + +|区别内容|GET|POST| +|:---:|---|---| +|点击返回/刷新按钮|没有影响|数据会重新发送(浏览器将会提示“数据被重新提交”)| +|添加书签|可以|不可以| +|缓存|可以|不可以| +|编码类型(Encoding type)|`application/x-www-form-rulencoded`|`application/x-www-form-rulencoded` or `multipart/form-data` 请为二进制数据使用 `multipart` 编码| +|历史记录|有|没有| +|长度限制|有|没有| +|数据类型限制|只允许 ASCLll 字符类型|没有限制,允许二进制数据| +|安全性|查询字符串会显示在地址栏的 URL 上,不安全,**请不要使用 GET 请求提交敏感数据**|因为数据不会显示在地址栏中,也不会缓存下来或保存在浏览记录中,所以 POST 请求比 GET 请求安全,但也不是最安全的方式,如需要传送敏感数据,**请使用数据加密**。| +|可见性|查询字符串在地址栏的 URL 中可见|查询字符串在地址栏的 URL 中不可见| + + +### 5. 请分别介绍 Cookie 和 Session 的作用及它们之间的区别 + +参考文章: [《3分钟搞懂Cookie与Session》](https://juejin.im/post/5cbf0749e51d456e781f2023) + +#### 5.1 Cookie简单介绍 + +Cookie是存储在用户本地计算机上,用于保存一些用户操作的历史信息,当用户再次访问我们的服务器的时候,浏览器通过HTTP协议,将他们本地的Cookie内容也发到咱们服务器上,从而完成验证。 + + +![http4](http://images.pingan8787.com/20190622http4.jpg) + +* `Cookie` 是存储在浏览器客户的一小片数据; +* `Cookie` 可以同时被前台与后台操作; +* `Cookie` 可以跨页面存取; +* `Cookie` 是不可以跨服务器访问的; +* `Cookie` 有限制; 每个浏览器存储的个数不能超过300个,每个服务器不能超过20个,数据量不能超过4K; +* `Cookie` 是有生命周期的,默认与浏览器相同,如果进程退出,cookie会被销毁 + + +#### 5.2 Session + +Session 存储在我们的服务器上,就是在我们的服务器上保存用户的操作信息。 + +当用户访问我们的网站时,我们的服务器会成一个 `Session ID`,然后把 `Session ID` 存储起来,再把这个 `Session ID `发给我们的用户,用户再次访问我们的服务器的时候,拿着这个 `Session ID就 `能验证了,当这个ID能与我们服务器上存储的ID对应起来时,我们就可以认为是自己人。 + +![http5](http://images.pingan8787.com/20190622http5.jpg) + +* `seesion` 数据存储在服务器端; +* 每一个会话分配一个单独的 `session_id`; +* 该 `session_id` 通过 `cookie` 传送到前台,默认的 `session_id` 名称是`PHPSESSIONID`; +* 前台只能看到 `Session` 的 `ID`,而不能修改 `Session` 值; +* 使用 `Session `之前需要先开启会话; +* `Session `存储在 `Session `数组 `$_SESSION `; +* `Session `存储方式比较安全,但是如果 `Session `数量过多,会导致服务器性能下降; + +#### 5.3 两者区别 + +||Cookie|session| +|---|---|---| +|定义|浏览器保存用户信息的文件,存储的数量和字符数都有限制|服务器把`sessionID` 和用户信息、用户操作,记录在服务器上,这些记录就称为`session` | +|相同点|都是为了存储用户相关的信息| +|存储|客户端|服务器| +|安全性|安全性不高,任何人都能直接查看|安全性高| + +#### 5.4 两者结合使用 + +* 存储在服务端:通过 `cookie `存储一个 `session_id `,然后具体的数据则是保存在 `session `中。如果用户已经登录,则服务器会在 `cookie `中保存一个 `session_id `,下次再次请求的时候,会把该 `session_id `携带上来,服务器根据 `session_id `在 `session `库中获取用户的 `session `数据。就能知道该用户到底是谁,以及之前保存的一些状态信息。这种专业术语叫做 `server side session `。 + +* 将 `session `数据加密,然后存储在 `cookie `中。这种专业术语叫做 `client side session `。 + +### 6. 请介绍 HTTP 请求报文与响应报文格式 + +#### 6.1 请求报文 + +![http6](http://images.pingan8787.com/20190622http6.png) + +请求报文由**请求行**、**请求头部**和**请求正文**组成: + +* **请求行** + +格式为: + +``` +请求方法 + 空格 + URL + 空格 + 协议版本 + 回车符 + 换行符 +``` + +例如: + +``` +GET www.baidu.com HTTP/1.1 +``` + +常见的请求方法有:GET,HEAD,PUT,POST,TRACE,OPTIONS,DELETE以及扩展方法。 + +* **请求头部** + +格式为: + +``` +头部字段名 + 冒号(:) + 值 + 回车符 + 换行符 +``` + +请求头部为请求报文添加了一些附加信息,由“**名/值**”对组成,每行一对,名和值之间使用**冒号**分隔。 + +并且,在**请求头部的最后会有一个空行**,表示请求头部结束,这一行必不可少。 + +典型的请求头部有: + +|请求头部|说明| +|---|---| +|Host|接受请求的服务器地址,可以是IP:端口号,也可以是域名| +|User-Agent|发送请求的应用程序名称| +|Connection|指定与连接相关的属性,如Connection:Keep-Alive| +|Accept-Charset|通知服务端可以发送的编码格式| +|Accept-Encoding|通知服务端可以发送的数据压缩格式| +|Accept-Language|通知服务端可以发送的语言| + +* **请求正文** + +一般使用在 `POST` 方法中, `GET` 方法不存在请求正文。 +`POST` 方法适用于需要客户填写表单的场合。与请求数据相关的最常使用的请求头是 `Content-Type` 和 `Content-Length` 。 + + +#### 6.2 响应报文 + +![http7](http://images.pingan8787.com/20190622http7.png) + +响应报文由**状态行**、**响应头部**和**响应正文**组成: + + +* **状态行** + +格式为: + +``` +协议版本 + 空格 + 状态码 + 空格 + 状态码描述 + 回车符 + 换行符 +``` + +状态码划分: + +100〜199的状态码是 HTTP / 1.1 向协议中引入了**信息性状态码**; +200〜299的状态码表示**成功**; +300〜399的状态码指**资源重定向**; +400〜499的状态码指**客户端请求出错**; +500〜599的状态码指**服务端出错**; + + +常见的状态码: + +|状态码|说明| +|---|---| +|200|响应成功| +|302|跳转,跳转地址通过响应头中的位置属性指定(JSP中Forward和Redirect之间的区别)| +|400|客户端请求有语法错误,不能被服务器识别| +|403|服务器接收到请求,但是拒绝提供服务(认证失败)| +|404|请求资源不存在| +|500|服务器内部错误| + +* **响应头部** + +格式为: + +``` +头部字段名 + 冒号(:) + 值 + 回车符 + 换行符 +``` + +常见响应头部: + +|响应头部|说明| +|---|---| +|`Server`|服务器应用程序软件的名称和版本| +|`Content-Type`|响应正文的类型(是图片还是二进制字符串)| +|`Content-Length`|响应正文长度| +|`Content-Charset`|响应正文使用的编码| +|`Content-Encoding`|响应正文使用的数据压缩格式| +|`Content-Language`|响应正文使用的语言| + + +### 7. HTTP/1.1 有什么优缺点 + +参考文章:[《HTTP/1.0 HTTP/1.1 HTTP/2.0 主要特性对比》](https://segmentfault.com/a/1190000013028798?utm_source=tag-newest) + +对于 `HTTP/1.1 `,不仅继承了 `HTTP1.0 `简单的特点,还克服了诸多 `HTTP1.0 `性能上的问题。 + + +#### 7.1 HTTP/1.1 优点 + +1. 增加持久性连接 + +也就是多个请求和响应可以利用同一个 TCP 连接,而不是每一次请求响应都要新建一个TCP连接,减少了建立和关闭连接的消耗和延迟。 +``` +Connection: keep-alive +``` + +2. 增加管道机制 + +增加了管道机制,请求可以同时发出,但是响应必须按照请求发出的顺序依次返回,性能在一定程度上得到了改善。 + +![管道](http://images.pingan8787.com/20190609http01.png) + +3. 分块传输 + +在 HTTP/1.1 版本中,可以不必等待数据完全处理完毕再返回,服务器产生部分数据,那么就发送部分数据,很明此种方式更加优秀一些,可以节省很多等待时间。 + +4. 增加 `host` 字段 + +使得一个服务器能够用来创建多个 Web 站点。 + + +5. 错误提示 + +HTTP/1.1 引入了一个 `Warning` 头域,增加对错误或警告信息的描述,此外,在 HTTP/1.1 中新增了24个状态响应码(100,101,203,205,206,301,305… )。 + + + +6. 带宽优化 + +HTTP/1.1 中在请求消息中引入了 `range `头域,它允许只请求资源的某个部分。 + +在响应消息中 `Content-Range `头域声明了返回的这部分对象的偏移值和长度。如果服务器相应地返回了对象所请求范围的内容,则响应码为 `206(Partial Content) `,它可以防止 `Cache `将响应误以为是完整的一个对象,HTTP/1.1 加入了一个新的状态码 `100(Continue)`。客户端事先发送一个只带头域的请求,如果服务器因为权限拒绝了请求,就回送响应码 `401(Unauthorized`);如果服务器接收此请求就回送响应码 `100 `,客户端就可以继续发送带实体的完整请求了。 + +注意,HTTP/1.0 的客户端不支持 100 响应码。但可以让客户端在请求消息中加入 `Expect `头域,并将它的值设置为 `100-continue`。 + + +#### 7.2 HTTP/1.1 缺点 + +1. 队头阻塞 + +此版本的网络延迟问题主要由于队头堵塞导致,虽然通过持久性连接得到改善,但是每一个请求的响应依然需要按照顺序排队,如果前面的响应处理较为耗费时间,那么同样非常耗费性能。 + +2. 技术不成熟 + +还有此版本虽然引进了管道机制,但是当前存在诸多问题,且默认处于关闭状态。 + +3. 浪费资源 + +http/1.1 请求会携带大量冗余的头信息,浪费了很多宽带资源。 + +### 8. 相比 HTTP/1.1,HTTP/2.0 有哪些新特性 + +参考文章:[《HTTP1.0 HTTP/1.1 HTTP2.0 主要特性对比》](https://segmentfault.com/a/1190000013028798?utm_source=tag-newest) + +* **二进制分帧** + +在应用层(HTTP/2.0)和传输层(TCP or UDP)之间增加一个二进制分帧层,从而突破 HTTP1.1 的性能限制,改进**传输性能**,**实现低延迟**和**高吞吐量**。 + +![http12](http://images.pingan8787.com/20190622http12.jpg) + +可见,虽然 HTTP/2.0 的协议和 HTTP1.x 协议之间的规范完全不同了,但是实际上 HTTP/2.0并 没有改变 HTTP1.x 的语义。 + +简单来说,HTTP/2.0 只是把原来 HTTP1.x 的 `header` 和 `body` 部分用 `frame` 重新封装了一层而已。 + +* **多路复用(连接共享)** + +允许同时通过单一的 HTTP/2 连接发起多重的请求-响应消息,这个强大的功能则是基于“二进制分帧”的特性。 + +![http11](http://images.pingan8787.com/20190622http11.jpg) + +从图中可见,所有的 HTTP/2.0 通信都在一个 TCP 连接上完成,这个连接可以承载任意数量的双向数据流。 + +每个数据流以消息的形式发送,而消息由一或多个帧组成。这些帧可以乱序发送,然后再根据每个帧头部的流标识符(`stream id`)重新组装。 + +* **首部压缩** + +HTTP1.1 不支持 `header` 数据的压缩,HTTP/2.0 使用 `HPACK` 算法对 `header` 的数据进行压缩,这样数据体积小了,在网络上传输就会更快。高效的压缩算法可以很大的压缩 `header` ,减少发送包的数量从而降低延迟。 + +* **服务器推送** + +在 HTTP/2 中,服务器可以对客户端的一个请求发送多个响应,即服务器可以额外的向客户端推送资源,而无需客户端明确的请求。 + +### 9. 请简述 HTTPS 工作原理 + +#### 9.1 HTTPS 简介 + +参考文章:[《深入浅出讲解HTTPS工作原理》](developer.51cto.com/art/201812/589283.htm) + +HTTPS 并非是应用层的一种新协议。只是 HTTP 通信接口部分用 SSL(Secure Socket Layer)和 TLS(Transport Layer Security)协议代替而已。 + +通常,HTTP 直接和 TCP 通信。当使用 SSL 时,则演变成先和 SSL 通信,再由 SSL 和 TCP 通信了。简言之,所谓 HTTPS,其实就是身披 SSL 协议这层外壳的 HTTP。 + + +![http13](http://images.pingan8787.com/20190622http13.jpg) + +在采用 SSL 后,HTTP 就拥有了 HTTPS 的加密、证书和完整性保护这些功能。也就是说HTTP 加上加密处理和认证以及完整性保护后即是 HTTPS。 + +![http14](http://images.pingan8787.com/20190622http14.jpg) + +HTTPS 协议的主要功能基本都依赖于 TLS/SSL 协议,TLS/SSL 的功能实现主要依赖于三类基本算法:**散列函数** 、**对称加密**和**非对称加密**,其利用非对称加密实现身份认证和密钥协商,对称加密算法采用协商的密钥对数据加密,基于散列函数验证信息的完整性。 + +![http15](http://images.pingan8787.com/20190622http15.jpg) + +#### 9.2 HTTPS 工作原理 + +HTTPS 其实是有两部分组成:**HTTP** + **SSL / TLS**,也就是在 HTTP 上又加了一层处理加密信息的模块。服务端和客户端的信息传输都会通过 TLS 进行加密,所以**传输的数据都是加密后的数据**。 + +![http16](http://images.pingan8787.com/20190622http16.jpeg) + + +1. 客户端发起HTTPS请求 + +浏览器里面输入一个HTTPS网址,然后连接到服务端的443端口上。注意这个过程中客户端会发送一个密文族给服务端,密文族是浏览器所支持的加密算法的清单。 + +2. 服务端配置 + +采用HTTPS协议的服务器必须要有一套数字证书,可以自己制作,也可以向组织申请。区别就是自己颁发的证书需要客户端验证通过才可以继续访问,而使用受信任的公司申请的证书则不会弹出提示页面。 + +这套证书其实就是一对公钥和私钥,可以这么理解,公钥就是一把锁头,私钥就是这把锁的钥匙,锁头可以给别人对某个东西进行加锁,但是加锁完毕之后,只有持有这把锁的钥匙才可以解锁看到加锁的内容。 + +3. 传送证书 + +这个证书其实就是公钥,只是包含了很多信息,如证书的颁发机构、过期时间等等。 + +4. 客户端解析证书 + +这部分工作是由客户端的TLS来完成的,首先会验证公钥是否有效,如颁发机构、过期时间等等,如果发现异常则会弹出一个警告框,提示证书存在问题。如果证书没有问题,那么就生成一个随机值,然后用证书对该随机值进行加密。 + +注意一下上面提到的"发现异常"。证书中会包含数字签名,该数字签名是加密过的,是用颁发机构的私钥对本证书的公钥、名称及其他信息做hash散列加密而生成的。客户端浏览器会首先找到该证书的根证书颁发机构,如果有,则用该根证书的公钥解密服务器下发的证书,如果不能正常解密,则就是"发现异常",说明该证书是伪造的。 + +5. 传送加密信息 + +这部分传送的是用证书加密后的随机值,目的就是让服务端得到这个随机值,然后客户端和服务端的通信就可以通过这个随机值来进行加密和解密了。 + +6. 服务端解密信息 + +服务端用私钥解密后,得到了客户端传过来的随机值,至此一个非对称加密的过程结束,看到TLS利用非对称加密实现了身份认证和密钥协商。然后把内容通过该值进行对称加密。 + +7. 传输加密后的信息 + +这部分是服务端用随机值加密后的信息,可以在客户端被还原。 + +8. 客户端解密信息 + +客户端用之前生成的随机值解密服务端传送过来的信息,于是获取了解密后的内容,至此一个对称加密的过程结束,看到对称加密是用于对服务器待传送给客户端的数据进行加密用的。整个过程即使第三方监听了数据,也束手无策。 + + +### 10. HTTP 和 HTTPS 的共同点和区别 + +1. https 协议需要到 ca 申请证书,一般免费证书较少,因而需要一定费用。 + +2. http 是超文本传输协议,信息是明文传输, https 则是具有安全性的ssl加密传输协议。 + +3. http 和 https 使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443。 + +4. http 的连接很简单,是无状态的; HTTPS 协议是由 SSL+HTTP 协议构建的可进行加密传输、身份认证的网络协议,比 http 协议安全。 + + +### 11. 什么是跨域,如何解决跨域 + +参考文章:[《前端常见跨域解决方案(全)》](https://segmentfault.com/a/1190000011145364) + +#### 11.1 什么是跨域 + +跨域,指的是浏览器不能执行其他网站的脚本。它是由**浏览器的同源策略**造成的,是浏览器对JavaScript施加的安全限制。 + +* **什么是同源策略?** + +同源策略/SOP(Same origin policy)是一种约定,由Netscape公司1995年引入浏览器,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,浏览器很容易受到 XSS 、 CSFR 等攻击。所谓同源是指"协议+域名+端口"三者相同,即便两个不同的域名指向同一个ip地址,也非同源。 + +* **同源策略**限制了以下行为: + +1. `Cookie`、`LocalStorage` 和 `IndexDB` 无法读取 +1. `DOM` 和 `JS `对象无法获取 +1. `Ajax`请求发送不出去 + + +* **常见跨域场景:** + +所谓的同源是指:**域名**、**协议**、**端口**均为相同。 + +* `http://www.a.cn/index.html` 调用 ` http://www.a.cn/server.php` 非跨域。 + +* `http://www.a.cn/index.html` 调用 `http://www.b.cn/server.php` 跨域,主域不同。 + +* `http://abc.a.cn/index.html` 调用 `http://def.b.cn/server.php` 跨域,子域名不同。 + +* `http://www.a.cn:8080/index.html` 调用 `http://www.a.cn/server.php` 跨域,端口不同。 + +* `https://www.a.cn/index.html` 调用 `http://www.a.cn/server.php` 跨域,协议不同。 + +* `localhost` 调用 `127.0.0.1` 跨域。 + +#### 11.2 解决跨域 + +1. `jsonp` 跨域 +2. `document.domain` + `iframe` 跨域 +3. `window.name` + `iframe` 跨域 +4. `location.hash` + `iframe` 跨域 +5. `postMessage` 跨域 +6. 跨域资源共享` CORS` +7. `withCredentials` 属性 +8. `WebSocket` 协议跨域 +9. `node` 代理跨域 +10. `nginx` 代理跨域 + +具体每一种解决方法,可以参考:[《前端常见跨域解决方案(全)》](https://segmentfault.com/a/1190000011145364) + +### 12. HTTP 中与缓存相关的头部有哪些,它们有什么区别 + +|头部|优势和特点|劣势和问题| +|---|---|---| +|`Expires`|1、`HTTP 1.0` 产物,可以在`HTTP 1.0`和`1.1`中使用,简单易用。2、以时刻标识失效时间。|1、时间是由服务器发送的(`UTC`),如果服务器时间和客户端时间存在不一致,可能会出现问题。2、存在版本问题,到期之前的修改客户端是不可知的。| +|`Cache-Control`|1、`HTTP 1.1` 产物,以时间间隔标识失效时间,解决了`Expires`服务器和客户端相对时间的问题。2、比`Expires`多了很多选项设置。|1、`HTTP 1.1 `才有的内容,不适用于`HTTP 1.0` 。2、存在版本问题,到期之前的修改客户端是不可知的。| +|`Last-Modified`|1、不存在版本问题,每次请求都会去服务器进行校验。服务器对比最后修改时间如果相同则返回304,不同返回200以及资源内容。|1、只要资源修改,无论内容是否发生实质性的变化,都会将该资源返回客户端。例如周期性重写,这种情况下该资源包含的数据实际上一样的。2、以时刻作为标识,无法识别一秒内进行多次修改的情况。3、某些服务器不能精确的得到文件的最后修改时间。| +|`ETag`|1、可以更加精确的判断资源是否被修改,可以识别一秒内多次修改的情况。2、不存在版本问题,每次请求都回去服务器进行校验。| 1、计算`ETag`值需要性能损耗。2、分布式服务器存储的情况下,计算`ETag`的算法如果不一样,会导致浏览器从一台服务器上获得页面内容后到另外一台服务器上进行验证时发现`ETag`不匹配的情况。| + + + +### 13. 分别介绍强缓存和协商缓存 + +浏览器缓存主要分为**强缓存**(也称**本地缓存**)和**协商缓存**(也称**弱缓存**)。 + +#### 13.1 强缓存 + +**强缓存**是利用 `http` 头中的 `Expires` 和 `Cache-Control` 两个字段来控制的,用来表示资源的缓存时间。 + +强缓存中,普通刷新会忽略它,但不会清除它,需要强制刷新。浏览器强制刷新,请求会带上 `Cache-Control:no-cache` 和 `Pragma:no-cache`。 + +通常,**强缓存**不会向服务器发送请求,直接从缓存中读取资源,在chrome控制台的network选项中可以看到该请求返回200的状态码。分为 `from disk cache` 和 `from memory cache`。 + +* `from disk cache` :一般非脚本会存在内存当中,如css,html等 + +* `from memory cache` :资源在内存当中,一般脚本、字体、图片会存在内存当 + +有**缓存命中**和**缓存未命中**状态: + +![http17](http://images.pingan8787.com/20190622http17.png) + +![http18](http://images.pingan8787.com/20190622http18.png) + + +#### 13.2 协商缓存 + +**协商缓存**就是由服务器来确定缓存资源是否可用,所以客户端与服务器端要通过某种标识来进行通信,从而让服务器判断请求资源是否可以缓存访问。 + +普通刷新会启用弱缓存,忽略强缓存。只有在地址栏或收藏夹输入网址、通过链接引用资源等情况下,浏览器才会启用强缓存,这也是为什么有时候我们更新一张图片、一个js文件,页面内容依然是旧的,但是直接浏览器访问那个图片或文件,看到的内容却是新的。 + +这个主要涉及到两组 `header` 字段: `Etag` 和 `If-None-Match `、 `Last-Modified `和 `If-Modified-Since `。 + +向服务器发送请求,服务器会根据这个请求的request header的一些参数来判断是否命中协商缓存。 + +有**缓存命中**和**缓存未命中**状态: + +![http19](http://images.pingan8787.com/20190622http19.png) + +![http20](http://images.pingan8787.com/20190622http20.png) + +#### 13.3 流程 + +浏览器第一次发起请求,本地有缓存情况: 在浏览器第一次发起请求时,本地无缓存,向web服务器发送请求,服务器起端响应请求,浏览器端缓存。过程如下: + +![1](http://images.pingan8787.com/20190615http1.png) + +在第一次请求时,服务器会将页面最后修改时间通过 `Last-Modified `标识由服务器发送给客户端,客户端记录修改时间;服务器还会生成一个Etag,并发送给客户端。 + +浏览器后续再次进行请求时: + +![2](http://images.pingan8787.com/20190615http2.png) + + +### 14. 请简单介绍一下 LRU (Least recently used)算法 + +参考文章:[《LRU算法》](https://www.jianshu.com/p/d533d8a66795) + + +#### 14.1 原理 + +LRU(Least recently used,最近最少使用)算法根据数据的历史访问记录来进行淘汰数据,其核心思想是“如果数据最近被访问过,那么将来被访问的几率也更高”。 + +![http21](http://images.pingan8787.com/20190622http21.png) + +这里有一张比较卡通的图片: + +![http22](http://images.pingan8787.com/20190622http22.png) + +#### 14.2 实现 + +最常见的实现是使用一个链表保存缓存数据,详细算法实现如下: + +1. 新数据插入到链表头部; + +2. 每当缓存命中(即缓存数据被访问),则将数据移到链表头部; + +3. 当链表满的时候,将链表尾部的数据丢弃。 + +#### 14.3 分析 + +* 命中率 + +当存在热点数据时,LRU的效率很好,但偶发性的、周期性的批量操作会导致LRU命中率急剧下降,缓存污染情况比较严重。 + +* 复杂度 + +实现简单。 + +* 代价 + +命中时需要遍历链表,找到命中的数据块索引,然后需要将数据移到头部。 + + +### 结语 + +本文主要复习了 HTTP/HTTPS 的一些基础知识,还有 HTTP 的其他版本的知识,对于面试也好,知识沉淀也好,这些也是我们作为开发者必须懂的。 + +作为一名前端开发者,说实话对 HTTP/HTTPS 了解还是太少,可能和平常工作内容有关。 + +### 关于我 + +> 本文首发在 [pingan8787个人博客](http://www.pingan8787.com),如需转载请保留个人介绍 + + +|Author|王平安| +|---|---| +|E-mail|pingan8787@qq.com| +|博 客|www.pingan8787.com| +|微 信|pingan8787| +|每日文章推荐|https://github.com/pingan8787/Leo_Reading/issues| +|ES小册|js.pingan8787.com| + +## 微信公众号 +![bg](http://images.pingan8787.com/2019_07_12guild_page.png) \ No newline at end of file diff --git a/Cute-Hybrid/Cute-Hybrid-01.md b/Cute-Hybrid/Cute-Hybrid-01.md new file mode 100644 index 00000000..52512eaa --- /dev/null +++ b/Cute-Hybrid/Cute-Hybrid-01.md @@ -0,0 +1,667 @@ +### 前言 + +我们大前端团队内部 📖**每周一练** 的知识复习计划还在继续,本周主题是 **《Hybrid APP 混合应用专题》** ,这期内容比较多,篇幅也相对较长,每个知识点内容也比较多。 + +之前分享的每周内容,我都整理到掘金收藏集 [📔《EFT每周一练》](https://juejin.im/collection/5cd11b0af265da0346227e24) 上啦,欢迎点赞收藏咯💕💕。 + +> 注:本文整理资料来源网络,有些图片/段落找不到原文出处,如有侵权,联系删除。 + + +### 一、什么是 Hybrid App,与 Native App 及 Web App 有什么区别 + +参考文章: + +1. [《Web App Hybrid App和 Native App的区别》](http://www.ionic.wang/article-index-id-58.html) + +2. [《Hybrid APP基础篇(二) -> Native、Hybrid、React Native、Web App方案的分析比较》](https://www.cnblogs.com/dailc/p/5930238.html) + +#### 1.1 主流应用类型 + +随着现在移动互联网的快速发展,市面上目前主流移动应用程序主要分三类:**Web App**、 **Native App** 和 **Hybrid App**。 + +三者大致关系如下: + +![关系图](http://images.pingan8787.com/20190623HybridApp1.png) + +#### 1.2 Web App + +**Web App**,即**移动端网站**,一般指的是基于 Web 的应用,基于**浏览器运行**,**无需下载安装**,基本上可以说是触屏版的网页应用。这类应用基本上是一个网页或一系列网页,旨在在移动屏幕上工作。 + +**Web 网站**一般分为两种: + +1. **MPA**(Multi-page Application) + +2. **SPA**(Single-page Application) + +一般的 **Web App** 是指 **SPA** 形式开发的网站。 + + +**优点:** + +* **开发和维护成本低,可以跨平台,调试方便;** + +前端人员开发的代码,可应用于各大主流浏览器(特殊情况可以代码进行下兼容),没有新的学习成本,而且可以直接在浏览器中调试。 + +* **更新最为快速;** + +由于web app资源是直接部署在服务器端的,所以只需替换服务器端文件,用户访问是就已经更新了(当然需要解决一些缓存问题)。 + +* **无需安装App,不会占用手机内存;** + +通过浏览器即可访问,无需安装,用户使用成本更低。 + +**缺点:** + +* **性能低,用户体验差;** + +由于是直接通过的浏览器访问,所以无法使用原生的API,操作体验不好。 + +* **依赖于网络,页面访问速度慢,耗费流量;** + +Web App每次访问都必须依赖网络,从服务端加载资源,当网速慢时访问速度很不理想,特别是在移动端,对网站性能优化要求比较高。 + +* **功能受限,大量功能无法实现;** + +只能使用 HTML5 的一些特殊 API ,无法调用原生 API ,所以很多功能存在无法实现情况。 + +* **临时性入口,用户留存率低;** + +这既是它的优点,也是缺点,优点是无需安装,确定是用完后有时候很难再找到,或者说很难专门为某个web app留存一个入口,导致用户很难再次使用。 + +#### 1.3 Native App + +**Native APP** 指的是原生程序,需要用户下载安装使用,一般依托于操作系统,有很强的交互,是一个完整的App,可拓展性强,能发布应用商店。 + +目前市面上主流的平台有:**Android** 和 **iOS**。 + + +**优点:** + +* 直接依托于操作系统,**用户体验好**,**操作流畅**,**性能稳定**; + +* 用户留存率高; + +* 功能最为强大,特别是在与系统交互中,几乎所有功能都能实现; + +由于 **Native APP** 是直接依托于系统,所以可以直接调用官方提供的API,功能最为全面(比如本地资源操作,通知,动画等)。 + +**缺点:** + +* 开发和维护成本高,无法跨平台,需要各平台各自独立开发; + +**Android** 上基于 **Java** 开发,**iOS** 上基 **OC** 或 **Swift** 开发,相互之间独立,必须要有各自的开发人员。 + +* 门槛较高,原生人员有一定的入门门槛,人才较少; + +原生的一个很大特点就是独立,所以不太容易入门,而且 **Android**, **iOS**都需要独立学习。 + +* 分发成本高,更新缓慢,特别是发布应用商店后,需要等到审核周期; + +原生应用更新是一个很大的问题, **Android**中还能直接下载整包APK进行更新,但是 **iOS**中,如果是发布 **AppStore** ,必须通过 **AppStore**地址更新,而每次更新都需要审核,所以无法达到及时更新。 + +#### 1.4 Hybrid App + +**Hybrid App** 指的是**混合开发**,也就是半原生半 Web 的开发模式,有跨平台效果,当然了,实质最终发布的仍然是独立的原生APP(各种的平台有各种的SDK)。 + +**优点:** + +* **学习和开发成本较低,可以跨平台,调试方便**; + +Hybrid 开发模式下,由原生提供统一的 API 给 JS 调用,实际的主要逻辑由 HTML 和 JS 完成,最终放在 webview 中显示,这样只需要写一套代码即可,达到跨平台效果,另外也可以直接在浏览器中调试,很方便。 + +一般 Hybrid 中的跨平台最少可以跨三个平台: Android App ,iOS App ,普通 webkit 浏览器。 + +需要前端人员关注一些原生提供的API,具体的实现无需关心,没有新的学习内容。 + +* **维护成本低**,功能可复用,并且更容易更新; + +虽然没有 web app 更新那么快速,但是 Hybrid 中也可以通过原生提供 api ,进行资源主动下载,达到只更新资源文件,不更新 apk(ipa) 的效果。 + + +* 功能更加完善,性能和体验要比起 web app 好太多; + +因为可以调用原生api,所以很多功能只要原生提供出就可以实现,另外性能也比较接近原生。 + +* 部分性能要求的页面可用原生实现; + +这种模式是原生混合 web ,所以我们完全可以将交互强,性能要求高的页面用原生写,然后一些其它页面用 JS 写,嵌入 webview 中,达到最佳体验。 + +**缺点:** + +* 相比原生,性能仍然有较大损耗; + +这种模式受限于 webview 的性能,相比原生而言有不少损耗,体验无法和原生相比。 + +* 不适用于交互性较强的app; + +这种模式的主要适用:一些新闻阅读类,信息展示类的 app ,不适用于一些交互较强或者性能要求较高的 app (比如动画较多就不适合)。 + + +#### 1.5 三者区别 + +**三者使用场景对比:** + +![对比图](http://images.pingan8787.com/20190623HybridApp7.png) + +**三者技术特征对比:** + +![对比图](http://images.pingan8787.com/20190623HybridApp2.png) + +**另外增加 ReactNative 一起放入作对比。** + +||NativeApp|WebApp|HybridApp|ReactNativeApp| +|:---|:---|:----|:---|---| +|原生功能体验|优秀|差|良好|接近优秀| +|渲染性能|非常快|慢|接近快|快| +|是否支持设备底层访问|支持|不支持|支持|支持| +|网络要求|支持离线|依赖网络|支持离线(资源存本地情况)|支持离线| +|更新复杂度|高(几乎总是通过应用商店更新)|低(服务器端直接更新)|较低(可以进行资源包更新)|较低(可以进行资源包更新)| +|编程语言|Android(Java),iOS(OC/Swift)|js+html+css3|js+html+css3|主要使用JS编写,语法规则JSX| +|社区资源|丰富(Android,iOS单独学习)|丰富(大量前端资源)|有局限(不同的Hybrid相互独立)|丰富(统一的活跃社区)| +|上手难度|难(不同平台需要单独学习)|简单(写一次,支持不同平台访问)|简单(写一次,运行任何平台)|中等(学习一次,写任何平台)| +|开发周期|长|短|较短|中等| +|开发成本|昂贵|便宜|较为便宜|中等| +|跨平台|不跨平台|所有H5浏览器|Android,iOS,h5浏览器|Android,iOS| +|APP发布|AppStore|Web服务器|AppStore|AppStore| + +#### 1.6 三者如何选择 + +这里简单介绍几种情况,具体还是要以实际项目技术评估结果为主。 + +* 选择纯 Native App 模式的情况: + +**性能要求极高,体验要求极好,不追求开发效率**。 + +* 选择 Web App 模式的情况: + +不追求用户体验和性能,对离线访问没要求,正常来说,如果追求性能和体验,都不会选用web app。 + +* 选择 Hybrid App 模式的情况 + +**大部分情况下的App都推荐采用这种模式**,这种模式可以用原生来实现要求高的界面,对于一些比较通用型,展示型的页面完全可以用web来实现,达到跨平台效果,**提升效率**。一般好一点的Hybrid方案,都会**把资源放在本地**的,可以**减少网络流量消耗**。 + +* 选择React Native App模式的情况 + +追求性能,体验,同时追求开发效率,而且有一定的技术资本,舍得前期投入。 + +React Native这种模式学习成本较高,所以需要前期投入不少时间才能达到较好水平,但是有了一定水准后,开发起来它的优势就体现出来了,性能不逊色原生,而且开发速度也很快 + +### 二、什么是 Cordova,它的优缺点是什么 + +参考文章: [《浅谈Cordova框架》](https://blog.csdn.net/weixin_37730482/article/details/73920722) + +#### 2.1 Cordova 简介 + +Cordova 是一个用基于 HTML、CSS 和 JavaScript 的,**用于创建跨平台移动应用程序的快速开发平台**。它使开发者能够利用iPhone、Android、Palm、Symbian、WP7、Bada和Blackberry等智能手机的核心功能——包括地理定位、加速器、联系人、声音和振动等,此外 Cordova 拥有丰富的插件,可以调用。 + +也可以用来开发**原生和WebView组件之间的插件接口**。 + +**来源:** +Cordova 是 PhoneGap 贡献给 Apache 后的开源项目,是从 PhoneGap 中抽出的核心代码,是驱动 PhoneGap 的核心引擎。可以把它们的关系想象成类似于 Webkit 和 Google Chrome 的关系。 + +#### 2.2 Cordova 架构图 + +![Cordova架构图](http://images.pingan8787.com/20190623HybridApp3.jpg) + +**架构图介绍:** + +* **Web App** + +用于存放我们程序的代码,包括业务逻辑,还有一些运行需要的资源(如:CSS,JavaScript,图片,媒体文件等)。 +应用的实现是通过 web 页面,默认的本地文件名称是 `index.html` ,应用执行在原生应用包装的 WebView 中,这个原生应用是你分发到应用商店中的。 + +* **WebView** + +Cordova 用的 WebView 可以给应用提供完整用户访问界面,使得应用混合了 Webview 和原生的应用组件。 + +* **Cordova Plugins** + +插件是 Cordova 生态系统的重要组成部分。它**提供了 Cordova 和原生组件相互通信的接口**,并绑定到了标准的设备API上,这使你能够**通过 JavaScript 调用原生代码**。 + + +#### 2.3 优缺点 + +**优点:** + +* 跨平台,开发简单,学习成本低; +* 框架多,插件多,可自定义插件; +* 发展最早,社区资源丰富; + +**缺点:** + +* WebView性能低下时,用户体验差,反应慢; +* 中文文档资源少; +* 调试不方便,既不像原生那么好调试,也不像纯web那种调试; + + +### 三、Cordova 插件的原理是什么 + +Cordova 插件就是一些附加代码用来提供原生组件的 JavaScript 接口,它允许你的 App 可以使用原生设备的能力,超越了纯粹的 Web App。 + +**Cordova 在 iOS 上的实现原理:** +![cordova](http://images.pingan8787.com/20190623HybridApp6.png) + +#### 3.1 工作流程 + +1. Cordova 发起对原生的请求: + +```js +cordova.exec(successCallback, failCallback, service, action, actionArgs); +// successCallback: 成功回调方法 +// failCallback: 失败回调方法 +// server: 所要请求的服务名字 +// action: 所要请求的服务具体操作 +// actionArgs: 请求操作所带的参数 +``` + +2. 这五个参数并不是直接传给原生,Cordova JS 端会做以下处理: + + * 为每个请求生成一个唯一标识( `callbackId` ),并传给原生端,原生端处理完后,会把 `callbackId` 连同处理结果一起返回给 JS 端; + * 以 `callbackId` 为 `key`,`{success:successCallback, fail:failCallback}` 为 `value`,把这个键值对保存在 JS 端的字典里,`successCallback` 与 `failCallback` 这两个参数不需要传给原生,原生返回结果时带上` callbackId`,JS 端就可以根据 `callbackId` 找到回调方法; + * 每次 JS 请求,最后发到原生的数据包括:`callbackId`, `service`, `action`, `actionArgs`; + +![iOS](http://images.pingan8787.com/20190623HybridApp12.png) + +3. 原生代码拿到 `callbackId`、`service`、`action` 及 `actionArgs` 后,会做以下处理: + + * 根据 `service` 参数找到对应插件类; + * 根据 `action` 参数找到插件类中对应的处理方法,并把 `actionArgs` 作为处理方法请求参数的一部分传给处理方法; + * 处理完成后,把处理结果及 `callbackId` 返回给 JS 端,JS 端收到后会根据 `callbackId` 找到回调方法,并把处理结果传给回调方法; + +![iOS](http://images.pingan8787.com/20190623HybridApp13.png) + +4. JS 端根据 `callbackId` 回调 `cordova.js` + + +```js +// 根据 callbackId 及是否成功标识,找到回调方法,并把处理结果传给回调方法 +callbackFromNative: function(callbackId, success, status, args, keepCallback) { + var callback = cordova.callbacks[callbackId]; + if (callback) { + if (success && status == cordova.callbackStatus.OK) { + callback.success && callback.success.apply(null, args); + } else if (!success) { + callback.fail && callback.fail.apply(null, args); + } + + // Clear callback if not expecting any more results + if (!keepCallback) { + delete cordova.callbacks[callbackId]; + } + } +} +``` + +### 四、什么是 JS Bridge,它的作用是什么 + +参考文章:[《JSBridge的原理》](https://juejin.im/post/5abca877f265da238155b6bc) + +#### 4.1 JS Bridge 介绍 + +JSBridge 简单来讲,主要是 **给 JavaScript 提供调用 Native 功能的接口**,让混合开发中的**前端部分**可以方便地使用地址位置、摄像头甚至支付等 Native 功能。 + +JSBridge 就像其名称中的 **“Bridge”** 的意义一样,**是 Native 和非 Native 之间的桥梁**,它的核心是 **构建 Native 和非 Native 间消息通信的通道,而且是 双向通信的通道**。 + +JSBridge 另一个叫法及大家熟知的 Hybrid app 技术。 + +![brige](http://images.pingan8787.com/20190623HybridApp4.png) + +所谓 **双向通信的通道**: + +* JS 向 Native 发送消息 : + +调用相关功能、通知 Native 当前 JS 的相关状态等。 + +* Native 向 JS 发送消息 : + +回溯调用结果、消息推送、通知 JS 当前 Native 的状态等。 + +#### 4.2. JS Bridge 实现原理 + +参考文章:[《Hybrid APP基础篇(四)->JSBridge的原理》](https://www.cnblogs.com/dailc/p/5931324.html) + +**Android 和 iOS 的 JSBridge 实现方式:** + +![brige原理](http://images.pingan8787.com/20190623HybridApp9.jpeg) + +##### 4.2.1 基本流程 + +![brige原理](http://images.pingan8787.com/20190623HybridApp5.png) + +* H5 页面通过某种方式触发一个 `url scheme `; +* Native 捕获到 `url scheme`,并进行分析和处理; +* Native 调用 H5 的 JSBridge 对象传递回调; + +原生的 WebView/UIWebView 控件已经能够和 JS 实现数据通信了,那为什么还要 JSBridge呢? + +其实使用JSBridge有很多方面的考虑: + +* Android4.2以下,`addJavascriptInterface` 方式有安全漏掉。 +* iOS7以下,JS 无法调用 Native。 +* `url scheme` 交互方式是一套现有的成熟方案,可以完美兼容各种版本,对以前老版本技术的兼容。 + +##### 4.2.1 实现流程(Android 为例) + +![brige原理](http://images.pingan8787.com/20190623HybridApp10.png) + +1. 拟定协议,参考 http 制定的协议为:`jsbridge://className:port/methodName?jsonObj`; + +``` +className // Android端实现暴露给前端的类名 +port // Android返回结果给前端的端口 +methodName // 前端需要调用的函数 +jsonObj // 前端给Android传递的参数 +``` + +2. 新建 HTML 文件命名为 `index.html`, 编写一个 `button` 绑定 `click` 事件; + +```html + +``` + +3. 新建 JS 文件命名为 `JSBridge.js`, 第2步中的 `JSBridge.call` 即为调用 `JSBridge.js `中的 `call` 方法,后面带了四个参数; + +```js +call: function (obj, method, params, callback) { + console.log(obj+" "+method+" "+params+" "+callback); + var port = Util.getPort(); + console.log(port); + this.callbacks[port] = callback; + var uri=Util.getUri(obj,method,params,port); + console.log(uri); + window.prompt(uri, ""); +}, +``` + +`JSBridge.js` 中的 `call` 方法,最后调用了` window.prompt` 方法,这个方法就是触发 Android 端 `webChromeClient `的回调函数用的。 + +4. `window.prompt` 触发了 `WebChromeClient`(这个需要使用函数`WebView.setWebChromeClient`( `new WebChromeClietn()` )进行设定); + +类中的如下回调 `onJsPrompt`。这时就完成了前端与 Android端 的通信了,因为前端的信息都顺利通过这个函数传递给Android了。 + +```java +@Override +public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) { + result.confirm(JSBridge.callJava(view,message)); + return true; +} +``` + +5. Android 中会定义一个类 `JSBridge.java` 来管理暴露给前端使用的函数; + +这个类有两个功能: + +* 暴露给前端的函数的动态注册功能。 +* 解析前端信息,调用了 Android 端对应的函数,这个示例中是:`showToast` 函数。 + +解析前端的信息,获取前端调用的函数名: + +```java +Uri uri = Uri.parse(uriString); +className = uri.getHost(); +param = uri.getQuery(); +port = uri.getPort() + ""; +String path = uri.getPath(); +HashMap< String, Method> methodHashMap = exposedMethod.get(className); +Method method = methodHashMap.get(methodName); +``` +通过获取的函数名,这里是 `showToast `,调用 Android 端的 `showToast `函数。 + +```java +method.invoke(null,webView,new JSONObject(param),new Callback(webView,port)); +``` + +6. 定义类 `BridgeImpl.java` 来具体的实现暴露给前端的所有函数。这里的 `showToast `函数如下: + +```js +public static void showToast(WebView webView, JSONObject param, final JSBridge.Callback callback){ + String message = param.optString("msg"); + Toast.makeText(webView.getContext(),message,Toast.LENGTH_LONG).show(); + if(null != callback){ + try { + JSONObject object = new JSONObject(); + object.put("key","value"); + object.put("key1","vaule1"); + callback.apply(getJSONObject(0,"ok",object)); + }catch (Exception e){ + e.printStackTrace(); + } + } +} +``` + +### 五、请列举 Android 与 iOS 平台下 JS Bridge 的实现方式 + +> 这边代码比较多,我使用图片来展示,大家可以放大来查看。 + +#### 5.1 Android 实现方式 + +##### 5.1.1 Android 调用 JS 的 2 种方式 + +1. **通过** `WebView` **的** `loadUrl()`: + +JS 代码调用一定要在 `onPageFinished()` 回调之后才能调用,否则不会调用。 + +**Web 端代码:** +```html + + + + + 前端代码 + + + +``` + +**Android 端代码:** + +![Android](http://images.pingan8787.com/20190623HybridApp11.png) + + +2. **通过** `WebView` **的** `evaluateJavascript()`: + +```js +// 只需要将第一种方法的loadUrl()换成下面该方法即可 +mWebView.evaluateJavascript( + "javascript:callJS()", + new ValueCallback() { + @Override + public void onReceiveValue(String value) { + //此处为 js 返回的结果 + } + }); +} +``` + + +##### 5.1.2 JS 调用 Android 的 3 种方式 + +1. **通过 `WebView` 的 `addJavascriptInterface()` 进行对象映射**: + +**Android 映射:** +```java +// 继承自Object类 +public class AndroidtoJs extends Object { + // 定义JS需要调用的方法 + // 被JS调用的方法必须加入@JavascriptInterface注解 + @JavascriptInterface + public void hello(String msg) { + System.out.println("JS调用了Android的hello方法"); + } +} +``` + +**Web:** + +```js + + + + + 前端代码 + + + + //点击按钮则调用callAndroid函数 + + + +``` +**Android 端:** + +![Android](http://images.pingan8787.com/20190623HybridApp15.png) + + +2. **通过 `WebViewClient` 的 `shouldOverrideUrlLoading ()` 方法回调拦截 `url`:** + +**Web 端:** + +```html + + + + + 前端代码 + + + + + + + +``` + +**Android 端:** + +![Android](http://images.pingan8787.com/20190623HybridApp16.png) + +3. **通过 WebChromeClient 的方法回调拦截JS对话框方法:** + +通过 WebChromeClient 的 `onJsAlert()` 、`onJsConfirm()` 、`onJsPrompt()`方法回调拦截JS对话框 `alert()` 、`confirm()`、`prompt()` 消息。 + +**Web 端:** + +```html + + + + + 前端代码 + + + + + + + +``` + +**Android 端:** + +![Android](http://images.pingan8787.com/20190623HybridApp17.png) + + +#### 5.2 iOS 实现方式 + +##### 5.2.1 JS 调用 iOS 的 2 种方式 + +1. **使用 `XMLHttpRequest` 发起请求的方式:** + +**Web 端:** + +![iOS](http://images.pingan8787.com/20190623HybridApp18.png) + +**XMLHttpRequest bridge:** + +JS 端使用 `XMLHttpRequest` 发起了一个请求:`execXhr.open('HEAD', "/!gap_exec?" + (+new Date()), true);`,请求的地址是 `/!gap_exec`;并把请求的数据放在了请求的 header 里面,见这句代码:`execXhr.setRequestHeader('cmds', iOSExec.nativeFetchMessages());`。 + +而在 Objective-C 端使用一个 `NSURLProtocol` 的子类来检查每个请求,如果地址是 `/!gap_exec` 的话,则认为是 Cordova 通信的请求,直接拦截,拦截后就可以通过分析请求的数据,分发到不同的插件类(CDVPlugin 类的子类)的方法中: + + +![iOS](http://images.pingan8787.com/20190623HybridApp19.png) + +Cordova 中优先使用这种方式,` Cordova.js` 中的注释有提及为什么优先使用 ` XMLHttpRequest` 的方式,及为什么保留第二种 ` iframe bridge ` 的通信方式: + +``` +// XHR mode does not work on iOS 4.2, so default to IFRAME_NAV for such devices. +// XHR mode’s main advantage is working around a bug in -webkit-scroll, which +// doesn’t exist in 4.X devices anyways123 +``` + +**iframe bridge:** + +在 JS 端创建一个透明的 `iframe`,设置这个 `ifame` 的 `src` 为自定义的协议,而 `ifame `的 `src` 更改时,`UIWebView` 会先回调其 `delegate` 的 `webView:shouldStartLoadWithRequest:navigationType:` 方法,关键代码如下: + +![iOS](http://images.pingan8787.com/20190623HybridApp20.png) + +2. **通过设置透明的 `iframe` 的 `src` 属性:** + + +##### 5.2.2 iOS 调用 JS 的方式 + +`UIWebView` 有一个这样的方法 `stringByEvaluatingJavaScriptFromString:`,这个方法可以让一个 `UIWebView` 对象执行一段 JS 代码,这样就可以达到 Objective-C 跟 JS 通信的效果,在 Cordova 的代码中多处用到了这个方法,其中最重要的两处如下: + +1. **获取 JS 的请求数据:** + + +![iOS](http://images.pingan8787.com/20190623HybridApp21.png) + +2. **把 JS 请求的结果返回给 JS 端:** + +![iOS](http://images.pingan8787.com/20190623HybridApp22.png) + +### 结语 + +对于初入混合应用开发的小伙伴,这些会有点难度,但是好好理解下那几张流程图,再理一理思路,相信会有帮助😁 + +给大家加加油~~ + +### 关于我 + +> 本文首发在 [pingan8787个人博客](http://www.pingan8787.com),如需转载请保留个人介绍。 + +|Author|王平安| +|---|---| +|E-mail|pingan8787@qq.com| +|博 客|www.pingan8787.com| +|微 信|pingan8787| +|每日文章推荐|https://github.com/pingan8787/Leo_Reading/issues| +|ES小册|js.pingan8787.com| + +## 微信公众号 +![bg](http://images.pingan8787.com/2019_07_12guild_page.png) \ No newline at end of file diff --git a/Cute-Hybrid/Cute-Hybrid-02.md b/Cute-Hybrid/Cute-Hybrid-02.md new file mode 100644 index 00000000..669a0b3f --- /dev/null +++ b/Cute-Hybrid/Cute-Hybrid-02.md @@ -0,0 +1,624 @@ + +![bg](http://images.pingan8787.com/Hybrid%E5%B0%81%E9%9D%A22.png) + + +### 前言 + +我们大前端团队内部 📖**每周一练** 的知识复习计划继续加油,本篇文章是 **《Hybrid APP 混合应用专题》** 主题的第二期和第三期的合集。 + +这一期共整理了 10 个问题,和相应的参考答案,文字和图片较多,**建议大家可以收藏,根据文章目录来阅读**。 + +之前分享的每周内容,我都整理到掘金收藏集 [📔《EFT每周一练》] (https://juejin.im/collection/5cd11b0af265da0346227e24) 上啦,欢迎点赞收藏咯💕💕。 + +**内容回顾:** + +1. [《EFT 每周分享 —— Hybrid App 应用开发中 5 个必备知识点复习》] (https://juejin.im/post/5d125306e51d45773e418aab) + +2. [《EFT 每周分享 —— HTTP 的15个常见知识点复习》] (https://juejin.im/post/5d0de954e51d4556be5b3a6f) + +3. [《EFT 每周分享 —— 数据结构与算法合集》] (https://juejin.im/collection/5cd11b0af265da0346227e24) + +**文章收录:** + +本系列所有文章,都将收录在 Github 上,[欢迎点击查阅] (https://github.com/pingan8787/Leo-EveryWeek)。 + +> 注:本文整理部分资料来源网络,有些图片/段落找不到原文出处,如有侵权,联系删除。 + + +### 一、iOS 平台中 UIWebView 与 WKWebView 有什么区别? + +> 参考文章:[《UIWebView与WKWebView》] (http://www.cocoachina.com/articles/17337) + +`UIWebView` 是苹果继承于 `UIView` 封装的一个加载 web 内容的类,它可以加载任何远端的web数据展示在你的页面上,你可以像浏览器一样前进后退刷新等操作。不过苹果在 iOS8 以后推出了 `WKWebView` 来加载 Web,并应用于 iOS 和 OSX 中,它取代了 `UIWebView` 和 `WebView` ,在两个平台上支持同一套 API。 + +它脱离于 `UIWebView` 的设计,将原本的设计拆分成14个类,和3个代理协议,虽然是这样但是了解之后其实用法比较简单,依照职责单一的原则,每个协议做的事情根据功能分类。 + +![UIWebView](http://cdn.cocimg.com/ueditor/upload/image/20160810/1470807807124957.png) + +`WKWebView` 与 `UIWebView` 的区别: + +* `WKWebView` 的内存远远没有 `UIWebView` 的开销大,而且没有缓存; + +* `WKWebView` 拥有高达 60FPS 滚动刷新率及内置手势; + +* `WKWebView` 支持了更多的 HTML5 特性; + +* `WKWebView` 高效的 app 和 web 信息交换通道; + +* `WKWebView` 允许 `JavaScript` 的 `Nitro` 库加载并使用, `UIWebView` 中限制了; + +* `WKWebView` 目前缺少关于页码相关的 API; + +* `WKWebView` 提供加载网页进度的属性; + +* `WKWebView` 使用 Safari 相同的 JavaScript 引擎; + +* `WKWebView` 增加加载进度属性: `estimatedProgress` ; + +* `WKWebView` 不支持页面缓存,需要自己注入 `cookie` , 而 `UIWebView` 是自动注入 `cookie` ; + +* `WKWebView` 无法发送 `POST` 参数问题; + +* `WKWebView` 可以和js直接互调函数,不像 `UIWebView` 需要第三方库 `WebViewJavascriptBridge` 来协助处理和 js 的交互; + + +**注意:** + +大多数App需要支持 `iOS7` 以上的版本,而 `WKWebView` 只在 `iOS8` 后才能用,所以需要一个兼容性方案,既 `iOS7` 下用 `UIWebView` ,`iOS8` 后用 `WKWebView` 。但是目前 `IOS10` 以下的系统以及很少了, + +**小结:** + +`WKWebView` 相较于 `UIWebView` 在整体上有较大的提升,满足 iOS 上面使用同一套控件的功能,同时对整个内存的开销以及滚动刷新率和 JS 交互做了优化的处理。 + +依据**职责单一原则**,拆分成了三个协议去实现 `WebView` 的响应,解耦了 JS 交互和加载进度的响应处理。 + +`WKWebView` 没有做缓存处理,所以**对网页需要缓存的加载性能要求没那么高**的还是可以考虑 `UIWebView` 。 + +### 二、WKWebView 有哪一些坑? + +> 参考文章:[《WKWebView 那些坑》] (https://kknews.cc/tech/x2rzg3g.html) + + +#### 1. WKWebView 白屏问题 + +`WKWebView` 实际上是个多进程组件,这也是它加载速度更快,内存暂用更低的原因。 + +在 `UIWebView` 上当内存占用太大的时候,App Process 会 crash;而在 `WKWebView` 上当总体的内存占用比较大的时候,WebContent Process 会 crash,从而出现白屏现象。 + +**解决办法:** + +1. **借助 WKNavigtionDelegate** + +当 `WKWebView` 总体内存占用过大,页面即将白屏的时候,系统会调用上面的回调函数,我们在该函数里执行`[webView reload]`(这个时候 `webView.URL` 取值尚不为` nil`)解决白屏问题。在一些高内存消耗的页面可能会频繁刷新当前页面,H5侧也要做相应的适配操作。 + +2. **检测 webView.title 是否为空** + +并不是所有 H5 页面白屏的时候都会调用上面的回调函数,比如,最近遇到在一个高内存消耗的 H5 页面上 present 系统相机,拍照完毕后返回原来页面的时候出现白屏现象(拍照过程消耗了大量内存,导致内存紧张,WebContent Process 被系统挂起),但上面的回调函数并没有被调用。在 `WKWebView` 白屏的时候,另一种现象是 `webView.titile` 会被置空, 因此,可以在 `viewWillAppear` 的时候检测 `webView.title` 是否为空来 `reload` 页面。 + + +#### 2. WKWebView Cookie 问题 + +`WKWebView` `Cookie` 问题在于 `WKWebView` 发起的请求不会自动带上存储于 `NSHTTPCookieStorage` 容器中的 `Cookie`,而在 `UIWebView` 会自动带上 `Cookie`。 + +**原因是:** + +`WKWebView` 拥有自己的私有存储,不会将 `Cookie` 存入到标准的 `Cookie` 容器`NSHTTPCookieStorage` 中。 + +实践发现 `WKWebView` 实例其实也会将 `Cookie` 存储于 `NSHTTPCookieStorage` 中,但存储时机有延迟,在 `iOS 8 `上,当页面跳转的时候,当前页面的 `Cookie` 会写入 `NSHTTPCookieStorage` 中,而在 `iOS 10` 上,JS 执行 `document.cookie` 或服务器 `set-cookie` 注入的 `Cookie` 会很快同步到 `NSHTTPCookieStorage` 中,FireFox 工程师曾建议通过 `reset WKProcessPool` 来触发 `Cookie` 同步到 `NSHTTPCookieStorage` 中,实践发现不起作用,并可能会引发当前页面 `session cookie `丢失等问题。 + +**解决办法1:** + +`WKWebView loadRequest` 前,在 `request header` 中设置 `Cookie`, 解决首个请求 `Cookie` 带不上的问题; + +**解决办法2:** + +通过 `document.cookie` 设置 `Cookie` 解决后续页面(同域)`Ajax``、iframe` 请求的 `Cookie` 问题;(注意:`document.cookie()` 无法跨域设置 `cookie`)。 + +#### 3. WKWebView loadRequest 问题 + +在 `WKWebView` 上通过 `loadRequest` 发起的 `post` 请求 `body` 数据会丢失,同样是由于**进程间通信性能问题**, `HTTPBody` 字段被丢弃。 + +#### 4. WKWebView NSURLProtocol问题 + +`WKWebView` 在独立于 app 进程之外的进程中执行网络请求,请求数据不经过主进程,因此,在 `WKWebView` 上直接使用 `NSURLProtocol` 无法拦截请求。 + +**解决办法:** + +由于 `WKWebView` 在独立进程里执行网络请求。一旦注册 `http(s) scheme` 后,网络请求将从 `Network Process` 发送到 `App Process`,这样 `NSURLProtocol` 才能拦截网络请求。在 `webkit2` 的设计里使用 `MessageQueue` 进行进程之间的通信,Network Process 会将请求 `encode` 成一个 `Message`,然后通过 IPC 发送给` App Process`。出于性能的原因,`encode` 的时候 `HTTPBody` 和 `HTTPBodyStream` 这两个字段会被丢弃掉了。 + +如果是用正常 `HTTP` 和 `HTTPs` 就是用 `WKWebView` 自带的拦截器,自定义协议用 `NSURLProtocol` 拦截。 + +> 详细可以参考:[《NSURLProtocol处理WKWebView的http和https的请求》](https://blog.csdn.net/liuyinghui523/article/details/89550083) + +#### 5. WKWebView 页面样式问题 + +在 `WKWebView` 适配过程中,我们发现部分 H5 页面元素位置向下偏移或被拉伸变形,追踪后发现主要是 H5 页面高度值异常导致。 + +**解决办法:** + +调整 `WKWebView` 布局方式,避免调整` webView.scrollView.contentInset` 。实际上,即便在 `UIWebView` 上也不建议直接调整 `webView.scrollView.contentInset` 的值,这确实会带来一些奇怪的问题。如果某些特殊情况下非得调整 `contentInset` 不可的话,可以通过下面方式让H5页面恢复正常显示。 + +#### 6. WKWebView 截屏问题 + +WKWebView 下通过 -[CALayer renderInContext:]实现截屏的方式失效,需要通过以下方式实现截屏功能: + +``` +@implementation UIView (ImageSnapshot) +- (UIImage*)imageSnapshot { + UIGraphicsBeginImageContextWithOptions(self.bounds.size,YES,self.contentScaleFactor); + [self drawViewHierarchyInRect:self.bounds afterScreenUpdates:YES]; + UIImage* newImage = UIGraphicsGetImageFromCurrentImageContext(); + UIGraphicsEndImageContext(); + return newImage; +} +@end +``` + +然而这种方式依然解决不了 `webGL` 页面的截屏问题,截屏结果不是空白就是纯黑图片。 + +**解决办法:** + +无奈之下,我们只能约定一个JS接口,让游戏开发商实现该接口,具体是通过 `canvas` `getImageData()`方法取得图片数据后返回 `base64` 格式的数据,客户端在需要截图的时候,调用这个JS接口获取` base64 String `并转换成 `UIImage`。 + +#### 7. WKWebView crash问题 + +如果 `WKWebView` 退出的时候,JS刚好执行了 `window.alert()`, alert 框可能弹不出来,`completionHandler` 最后没有被执行,导致` crash`; + +另一种情况是在 `WKWebView` 一打开,JS就执行 `window.alert()`,这个时候由于 `WKWebView` 所在的 `UIViewController` 出现( `push` 或 `present` )的动画尚未结束,alert 框可能弹不出来,`completionHandler `最后没有被执行,导致 `crash`。 + +#### 8. 视频自动播放 + +`WKWebView` 需要通过 `WKWebViewConfiguration.mediaPlaybackRequiresUserAction` 设置是否允许自动播放,但一定要在 `WKWebView` 初始化之前设置,在 `WKWebView` 初始化之后设置无效。 + + +#### 9. goBack API问题 + +`WKWebView` 上调用 `-[WKWebView goBack]`, 回退到上一个页面后不会触发`window.onload()` 函数、不会执行JS。 + +#### 10. 页面滚动速率 + +`WKWebView` 需要通过 `scrollView delegate` 调整滚动速率: + +``` +- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView { + scrollView.decelerationRate = UIScrollViewDecelerationRateNormal; +} +``` + +### 三、Crosswalk 是什么,它有什么作用? + +> 参考网站: [《Crosswalk Github》] (https://Crosswalk-project.org/) +> 参考文章: [《Crosswalk入门》] (https://www.mobibrw.com/2015/1934) + +**Crosswalk** 是一款开源的 web 引擎。目前 **Crosswalk** 正式支持的移动操作系统包括 Android 和 Tizen ,在 Android 4.0 及以上的系统中使用 **Crosswalk** 的 Web 应用程序在 HTML5 方面可以有一致的体验,**同时和系统的整合交互方面(比如启动画面、权限管理、应用切换、社交分享等等)可以做到类似原生应用**。 + +现在 **Crosswalk** 已经成为众多知名 HTML5 平台和应用的推荐引擎,包括 Google Mobile Chrome App 、 Intel XDK 、 Famo.us 和 Construct2 等等,未来的 Cordova 4.0 也计划集成 **Crosswalk** 。 + + + +### 四、常见的 WebView 性能优化方案有哪一些? + +#### 0. 问题分析 + +首先需要了解,对于一个普通用户来讲,打开一个 WebView 通常会经历哪几个阶段,一般有这些: + +1. 交互无反馈; + +2. 到达新的页面,页面白屏; + +3. 页面基本框架出现,但是没有数据;页面处于loading状态; + +4. 出现所需的数据; + +![webview1](http://images.pingan8787.com/20190712Hybrid01.jpeg) + +当 App 首次打开时,默认是并不初始化浏览器内核的;只有当创建 `WebView` 实例的时候,才会创建 `WebView` 的基础框架。 + +所以与浏览器不同,App 中打开 `WebView` 的第一步并不是建立连接,而是启动浏览器内核。 + +于是我们找到了“**为什么WebView总是很慢**”的原因之一: + +* 在浏览器中,我们输入地址时(甚至在之前),浏览器就可以开始加载页面。 + +* 而在客户端中,客户端需要先花费时间初始化 `WebView` 完成后,才开始加载。 + + +而这段时间,由于WebView还不存在,所有后续的过程是完全阻塞的。因此由于这段过程发生在 native 的代码中,单纯靠前端代码是无法优化的;大部分的方案都是前端和客户端协作完成,以下是几个业界采用过的方案: + +#### 1. 全局 WebView + +在客户端刚启动时,就初始化一个全局的 `WebView` 待用,并隐藏,当用户访问了 `WebView` 时,直接使用这个 `WebView` 加载对应网页,并展示。 + +这种方法可以**比较有效的减少 WebView 在App中的首次打开时间**。当用户访问页面时,不需要初始化 WebView 的时间。 + + +当然这也带来了一些问题,包括: + +* 额外的内存消耗。 + +* 页面间跳转需要清空上一个页面的痕迹,更容易内存泄露。 + +#### 2. WebView 动态加载 + +> 参考文章:[《WebView常用优化方案》] (https://www.jianshu.com/p/f64e1b1c90d9) + +`WebView` 动态加载。就是不在 `xml` 中写 `WebView` ,写一个 `layout` ,然后把 `WebView` add 进去。 + +```java +WebView mWebView = new WebView(getApplicationgContext()); +LinearLayout mll = findViewById(R.id.xxx); +mll.addView(mWebView); + +protected void onDestroy() { + super.onDestroy(); + mWebView.removeAllViews(); + mWebView.destroy() +} +``` + +这里用的 `getApplicationContext()` 也是防止内存溢出,这种方法有一个问题。如果你需要在 `WebView` 中打开链接或者你打开的页面带有 flash,获得你的 `WebView` 想弹出一个 `dialog` ,都会导致从 `ApplicationContext` 到 `ActivityContext` 的强制类型转换错误,从而导致你应用崩溃。 + +这是因为在加载 flash 的时候,系统会首先把你的 `WebView` 作为父控件,然后在该控件上绘制 flash ,他想找一个 `Activity` 的 `Context` 来绘制他,但是你传入的是 `ApplicationContext` 。然后就崩溃了。 + +#### 3. 独立的web进程,与主进程隔开 + +> 参考文章:[《WebView常用优化方案》] (https://www.jianshu.com/p/f64e1b1c90d9) + +这个方法被运用于类似 qq ,微信这样的超级 app 中,这也是解决任何 `WebView` 内存问题屡试不爽的方法 对于封装的 `webactivity` ,在` manifest.xml` 中设置。 + +```html + +``` + +然后在关闭 `webactivity` 时销毁进程: + +```java +@Overrideprotected void onDestroy() { +super.onDestroy(); + System.exit(0); +} +``` +关闭浏览器后便销毁整个进程,这样一般 95% 的情况下不会造成内存泄漏之类的问题,但这就涉及到 android 进程间通讯,比较不方便处理,优劣参半,也是可选的一个方案。 + +#### 4. WebView 释放 + +> 参考文章:[《WebView常用优化方案》] (https://www.jianshu.com/p/f64e1b1c90d9) + +```java +public void destroy() { + if (mWebView != null) { + // 如果先调用destroy()方法,则会命中if (isDestroyed()) return;这一行代码,需要先onDetachedFromWindow(),再 + // destory() + ViewParent parent = mWebView.getParent(); + if (parent != null) { + ((ViewGroup) parent).removeView(mWebView); + } +​ + mWebView.stopLoading(); + // 退出时调用此方法,移除绑定的服务,否则某些特定系统会报错 + mWebView.getSettings().setJavaScriptEnabled(false); + mWebView.clearHistory(); + mWebView.clearView(); + mWebView.removeAllViews(); +​ + try { + mWebView.destroy(); + } catch (Throwable ex) { +​ + } + } +} +``` + +### 五、在 Android 平台下如何调试 WebView? + +#### 1. 在 Chrome 浏览器上调试 +> 参考文章:[《Android调试webview》] (https://www.jianshu.com/p/3591eebbe797) + + +**1.1 条件:** +* 在 Android 设备或模拟器运行 Android4.4 或更高版本,Android 设备上启用 **USB调试模式**。 + +* Chrome 30 或更高版本。更强大的 WebView 界面调试功能需要 Chrome31 或更高版本。 + +* Android 应用程序中的 WebView 配置为**可调试模式**。 + +**1.2 Android 代码中配置 WebView 为可调试:** + +```java +if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + WebView.setWebContentsDebuggingEnabled(true); +} +``` + +注意 web 调测不受 `app manifest` 文件中 `debuggable` 标记状态的影响,如果希望仅 `debuggable` 为 `true` 时才能使用 web 调测,那么运行时检测此标记,如下: + +```java +if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + if ( 0 != ( getApplcationInfo().flags &= ApplicationInfo.FLAG_DEBUGGABLE ) ) { + WebView.setWebContentsDebuggingEnabled(true); + } +} +``` + +**1.3 手机开启 USB 调试选项,并用 USB 连接电脑:** + +开启 Android 手机的开发者选项,一般在 **系统设置** - **Android版本** 进行多次点击会触发开启开发者选项,然后进入开发者选项页面,开启USB调试。 + +为了避免每次调试时看到此警告,勾选“**总是允许从这台计算机**”,并单击“**确定**”。 + +**1.4 在 Chrome 中启用设置“USB web debugging”(不会影响WebView):** + + +在 Chrome 上访问 `chrome://inspect/#devices` 或 `about:inspect` 访问已启用调试的 WebView 列表,**需要翻墙**。 + +然后在 WebView 列表中选择你要调试的页面,点击“ `Inspect` ”选项,跟调试 PC 网页一样,使用 Chrome 控制台进行调试。 + +![Chrome 控制台](http://images.pingan8787.com/20190707hybrid01.png) + +![Chrome 控制台](http://images.pingan8787.com/20190707hybrid02.png) + +**1.5 小技巧:** + +(1)访问 `chrome://inspect/#devices` 如果 chrome 没有检测到 `Remote Target` 中的页面,可能需要安装一下 Chrome 的 ADB 插件,也可以在 Chrome 翻墙访问 `https://chrome-devtools-frontend.appspot.com`; + +(2)对于腾讯系的 APP,默认采用 X5内核 ,我们可以在 APP 内部打开 `https://debugx5.qq.com/` 来使用它的**Vconsole调试工具**进行调试。 + + +#### 2. 使用 DebugGap 调试 + +> 参考文章:[《Android下的webview调试》] (https://segmentfault.com/a/1190000009240637) + +**2.1 Windows 下载 DebugGap 并配置:** + +在电脑上面下载 Windows 版本的 `DebugGap` 软件包(下载链接:[DebugGap](http://www.debuggap.com/)),下载完成后解压下来。 + +安装完成后,运行 `DebugGap` ,开始配置: + +* 通常情况下,`DebugGap` 可以自动获取IP,并设置默认的端口,如果没有,你可以手动设置; +* 点击“**连接**”按钮启动各种客户端的侦听器; + +![DebugGap](http://images.pingan8787.com/20190707hybrid03.png) + +**2.2 在客户端上配置:** + +在调试项目中要进行测试的 HTML 界面中引入 `debuggap.js`。 +```html + +``` + +当调试项目的加载时,您的应用程序将会有一个蓝色的地方,点击会出现一个四叶三叶草的东西,点击“**配置**”,显示配置页面。输入与远程 DebugGap 上的主机和端口相同的主机和端口,例如 `192.168.1.4:11111`,然后点击“**连接**”按钮。 + +![DebugGap](http://images.pingan8787.com/20190707hybrid04.png) + +1.4电脑端远程 DebugGap 将检测即将到来的客户端,开发人员可以单击每个客户端进行调试。 + +![DebugGap](http://images.pingan8787.com/20190707hybrid05.png) + +### 六、在 iOS 平台下如何调试 WebView? + +> 参考文章:[《iOS之Safari调试webView/H5页面》] (https://www.cnblogs.com/dianming/p/6902442.html) + +一般我们通过 Mac 的 Safari浏览器 来调试,但是要注意两点: + +* 如果调试的是 APP 中 WebView 的页面,则需要这个 APP 的包支持调试,如果不能调试,需要让 iOS 开发人员重签名 APP(可能需要将我们 iOS 设备的 ID 写入到可信任设备列表中,然后使用 iTunes 安装客户端提供的测试包即可)。 + +* 如果调试的是 H5 页面,可以直接在手机的 Safari浏览器 打开直接调试。 + +下面开始说说在 Mac 上如何调试: + +#### 1. 开启 Safari 开发菜单 + +先将 iPhone 连接到 Mac,在 Mac 的 Safari 偏好设置中,开启开发菜单。具体步骤为:Safari -> 偏好设置… -> 高级 -> 勾选在菜单栏显示“**开发**”菜单。 + +![Safari](http://images.pingan8787.com/20190707hybrid06.jpg) + + +#### 2. iPhone 开启 Web检查器 + +具体步骤为:设置 -> Safari -> 高级 -> Web 检查器。 + +![Safari](http://images.pingan8787.com/20190707hybrid07.jpg) + +#### 3. 调试 APP 内的 WebView + +> 参考文章:[《前端 WEBVIEW 指南之 IOS 调试篇》] (https://imnerd.org/ios-webview-debug.html) + +在 Safari-> 开发中,看到自己的设备以及 WebView 中网页,点击后即可开启对应页面的 `Inspector` ,可以用来进行断点调试。 + +![Safari](http://images.pingan8787.com/20190707hybrid09.png) + +![Safari](http://images.pingan8787.com/20190707hybrid10.png) + + + +### 七、在内嵌版调试过程中,Fiddler 或 Charles 能起到什么作用? + +这两者都是强大的抓包工具,原理是以**web代理服务器**的形式进行工作的,使用的代理地址是:`127.0.0.1`,端口默认为`8888`,我们也可以通过设置进行修改。 + +**代理**就是在客户端和服务器之间设置一道关卡,客户端先将请求数据发送出去后,代理服务器会将数据包进行拦截,代理服务器再冒充客户端发送数据到服务器;同理,服务器将响应数据返回,代理服务器也会将数据拦截,再返回给客户端。 + +Fiddler 或 Charles 的主要作用有: + +* 可以代理请求,用来查看页面发送的请求和接收的响应; +* 可以修改请求的响应,用来模拟自己想要的数据; +* 可以模拟网络请求的速度; +* 可以代理服务器的静态资源请求,指向本地文件,省去频繁发布 H5 包,达到快速调试目的; + +> 补充链接:[《Fiddler工具使用介绍一》] (https://www.cnblogs.com/miantest/p/7289694.html) + +### 八、调试企业微信、微信和钉钉版时,可以使用哪些工具? + +#### 1. 调试企业微信、微信 + +* **[微信开发者工具](https://developers.weixin.qq.com/miniprogram/dev/devtools/download.html)**,可以用来调试页面基本功能; +* **[企业微信接口调试工具](https://work.weixin.qq.com/api/devtools/devtool.php)**,可以用来调试企业微信的接口; + +#### 2. 调试钉钉 + +* **[钉钉Android开发版](https://files.alicdn.com/tpsservice/76ac853fc96075414a837d8bbae89030.pdf)**,用来调试Android上的钉钉应用; + +#### 3. 通用 + +* **Fiddler 或 Charles**,可以拦截接口替换文件,来调试应用; + +### 九、常见的调试技巧有哪些? + +#### 1. Chrome 控制台调试 + +> 参考文章:[《前端常见调试技巧篇总结(持续更新...)》] (https://www.cnblogs.com/chenbeibei520/p/9959555.html) + +**1.1 Source 面板断点调试 JS** +从左到右,各个图标表示的功能分别为: + +* `Pause/Resume script execution`:暂停/恢复脚本执行(程序执行到下一断点停止)。 +* `Step over next function call`:执行到下一步的函数调用(跳到下一行)。 +* `Step into next function call`:进入当前函数。 +* `Step out of current function`:跳出当前执行函数。 +* `Deactive/Active all breakpoints`:关闭/开启所有断点(不会取消)。 +* `Pause on exceptions`:异常情况自动断点设置。 + +**1.2 Element 面板调试 DOM:** + +右击元素,选择 `break on` 选项: + +![break on](http://images.pingan8787.com/20190707hybrid14.png) + +* `Subtree modifications` 选项,是指**当节点内部子节点变化时断点**; + +![break on](http://images.pingan8787.com/20190707hybrid11.gif) + +* `Attributes modifications` 选项,是指**当节点属性发生变化时断点**; + +![break on](http://images.pingan8787.com/20190707hybrid12.gif) + +* `node removal` 选项,是指**当节点被移除时断点**; + +![break on](http://images.pingan8787.com/20190707hybrid13.png) + + +#### 2. console 调试 + +> 参考文章:[《Console调试常用用法》] (https://blog.csdn.net/a0405221/article/details/85248433) + +**2.1 显示信息的命令:** + +```js +console.log("normal"); // 用于输出普通信息 +console.info("information"); // 用于输出提示性信息 +console.error("error"); // 用于输出错误信息 +console.warn("warn"); // 用于输出警示信息 +console.clear(); // 清空控制台信息 +``` + +**2.2 计时功能:** + +`console.time()` 和 `console.timeEnd()` : + +```js +console.time("控制台计时器"); +for(var i = 0; i < 10000; i++){ + for(var j = 0; j < 10000; j++){} +} +console.timeEnd("控制台计时器") +``` + +**2.3 信息分组:** + +`console.group()` 和 `console.groupEnd()`: + +```js +console.group("第一组信息"); + console.log("第一组第一条:我的博客"); + console.log("第一组第二条:CSDN"); +console.groupEnd(); + +console.group("第二组信息"); + console.log("第二组第一条:程序爱好者QQ群"); + console.log("第二组第二条:欢迎你加入"); +console.groupEnd(); +``` + +**2.4 将对象以树状结构展现:** + +`console.dir()` 可以显示一个对象所有的属性和方法: + +```js +var info = { + name : "Alan", + age : "27", + grilFriend : "nothing", + getName : function(){ + return this.name; + } +} +console.dir(info); +``` + +**2.5 显示某个节点的内容:** + +`console.dirxml()` 用来显示网页的某个节点( `node`) 所包含的 `html/xml` 代码: + +```js +var node = document.getElementById("info"); +node.innerHTML += "

    追加的元素显示吗

    "; +console.dirxml(node); +``` + +**2.5 统计代码被执行的次数:** + +使用 `console.count()`: + +```js +function myFunction(){ + console.count("myFunction 被执行的次数"); +} +myFunction(); //myFunction 被执行的次数: 1 +myFunction(); //myFunction 被执行的次数: 2 +myFunction(); //myFunction 被执行的次数: 3 +``` + +**2.6 输出表格:** + +```js +console.table(mytable); +``` + +#### 3. 调试各种页面尺寸 + +虽然把各种各样的手机都摆在桌子上看起来很酷,但却很不现实。但是,浏览器内却提供了你所需要的一切。进入检查面板点击“**切换设备模式**”按钮。这样,就可以在窗口内调整视窗的大小。 + + +#### 4. debugger 断点 + +具体的说就是通过在代码中添加" `debugger; `"语句,当代码执行到该语句的时候就会自动断点。 + +![debugger](http://images.pingan8787.com/20190712Hybrid02.gif) + + + +### 结语 + +对于初入混合应用开发的小伙伴,还有经常需要调试混合应用的小伙伴,相信会有帮助😁 + +大家加油~ + +### 关于我 + +> 本文首发在 [pingan8787个人博客](http://www.pingan8787.com),如需转载请保留个人介绍。 + +|Author|王平安| +|---|---| +|E-mail|pingan8787@qq.com| +|博 客|www.pingan8787.com| +|微 信|pingan8787| +|每日文章推荐|https://github.com/pingan8787/Leo_Reading/issues| +|ES小册|js.pingan8787.com| + +### 微信公众号 +![bg](http://images.pingan8787.com/2019_07_12guild_page.png) \ No newline at end of file diff --git a/Cute-Hybrid/README.md b/Cute-Hybrid/README.md new file mode 100644 index 00000000..cb4395b2 --- /dev/null +++ b/Cute-Hybrid/README.md @@ -0,0 +1,18 @@ +## 💌仓库介绍 +**Cute-Hybrid 系列**主要分享我学习和实战**混合应用开发**的相关笔记和代码,目前分享主要以题目为主,其中涉及知识点内容较多,需要仔细学习,喜欢的朋友欢迎 👉star。 + +### 关于作者 +[![博客](http://images.pingan8787.com/icon_my1.png)](http://www.pingan8787.com) +[![知乎](http://images.pingan8787.com/icon_zhihu1.png)](https://zhuanlan.zhihu.com/cute-javascript) +[![掘金](http://images.pingan8787.com/icon_juejin2.png)](https://juejin.im/user/586fc337a22b9d0058807d53/posts) +[![思否](http://images.pingan8787.com/icon_sf1.png)](https://segmentfault.com/blog/pingan8787) +[![CSDN](http://images.pingan8787.com/icon_csdn1.png)](https://blog.csdn.net/qq_36380426) +[![简书](http://images.pingan8787.com/icon_jianshu1.png)](https://www.jianshu.com/u/2ec5d94afd60) + + +## 💌文章目录 + +1. [《Hybrid App 应用开发中必备知识点复习 - 1》](https://github.com/pingan8787/Leo-JavaScript/blob/master/Cute-Hybrid/Cute-Hybrid-01.md) + +2. [《Hybrid App 应用开发中必备知识点复习 - 2》](https://github.com/pingan8787/Leo-JavaScript/blob/master/Cute-Hybrid/Cute-Hybrid-02.md) + diff --git a/Cute-Hybrid/inAppBrowser_to_Cordova_core.md b/Cute-Hybrid/inAppBrowser_to_Cordova_core.md new file mode 100644 index 00000000..ead1b796 --- /dev/null +++ b/Cute-Hybrid/inAppBrowser_to_Cordova_core.md @@ -0,0 +1,213 @@ +## 一、流程简介 + +`InAppBrowser` 调用 `Cordova` 插件的流程共分为 9 个步骤: + +1. 前端进入页面,加载 JSSDK(`jexe.js`); + +2. 动态加载对应平台的 `Cordova.js` 及 `Cordova` 插件; + +3. 用户触发请求事件, 前端调用 `Cordova` 插件; + +4. 前端传递 `command` 参数与 Native 层通信(如 iOS 端使用 `postMessage`); + +5. Native 层接收并解析前端传入的 参数,调用对应 `Cordova` 插件,将插件和主 `WebView` 绑定; + +6. 主 `WebView` 中调用 `callbackFromNative`; + +7. 执行 `callbackFromNative` ,调用 `InAppBrowser` 实例的 `executeScript` 方法进行结果回传; + +8. 将调用结果进行回传到 `InAppBorwser` 层中; + +9. 调用 `InAppBorwser` 层的 `callbackFromNative`,调用前端回调函数; + +## 二、流程分析 + +接下来的介绍,将以 iOS 端调用 `Cordova` 的 `ImagePicker` 插件为例,介绍整个从 `InAppBrowser` 调用 `Cordova` 插件的流程。 + +### 1. 前端页面加载 JSSDK + +当用户进入页面时,会开始加载 JSSDK 文件(`jexe.js`)。 + +```html + +``` + +### 2. 动态添加不同平台 Cordova 及插件 + +在 `jexe.js` 文件中,主要有两部分逻辑: + +1. 我们原本的 JSSDK 文件,及 `exe.app.js`; + +2. 判断设备平台的逻辑,并动态添加 `Cordova.js` 及插件; + +```js +// 判断平台 动态加载 + +// .... 省略其他 有精简 +var u = $window.navigator.userAgent; +var isIOS = !!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/); +var flatform = isIOS ? 'cdvi' : 'cdva'; +var cordovaPath = `./dist/js/${flatform}.min.js`; +var script = $window.document.createElement('script'); +script.src = cordovaPath; +script.type = 'text/javascript'; +script.crossorigin="anonymous"; +$window.document.head.appendChild(script); +``` + +这段代码中,当我们加载 `jexe.js` 时,会判断当前设备平台( iOS / Android ),并**动态创建 script 标签**去加载对应平台的 `Cordova.js` 及插件。 + +- iOS 平台会加载 `cdvi.min.js` 文件; + +- Android 平台会加载 `cdva.min.js` 文件; + +```html + +``` + +### 3. 用户触发请求,前端调用插件 + +用户通过前端页面,触发事件,来实现调用 JSSDK 方法。 + +这里通过约定好的命名空间 `exe` 去调用 `Cordova` 中的 `ImagePicker` 插件,并执行插件的 `getPictures` 方法。 + +`getPictures` 方法需要传入 `options` 参数,具体可以查看**《EXE开放平台》**上的参数介绍。 + +```js +var options = { + maximumImagesCount: 9, + width: 360, + height: 360, + quality: 100, + outputType: 1, + chosePicture: true, + choseVideo: true, +} +exe.ImagePicker.getPictures(options) + .then( res => { + // do success something + console.log(res) + }) + .catch( err => { + // do error something + console.log(err) + }) +``` + +### 4. 与 Native 层通信 + +在调用 `exe.ImagePicker.getPictures` 方法时,iOS 端需要传入一个 `command` 作为参数,再调用 `postMessage` 方法,才能与 `Cordova.js` 通信,最终返回一个 `Promise` 对象。 + +`command` 参数中,包含如下值: + +- `successCallback` : 执行成功的回调方法; +- `failCallback` : 执行失败的回调方法; +- `service` : 插件的服务; +- `action` : 调用的插件名称; +- `actionArgs` : 调用的插件参数; + +这里所说的 `postMessage` 方法,是用来与 Native 层做通信的桥梁,iOS 端通过监听下面方法来接收并解析前面传递过来的 `command` 参数: + +`window.webkit.meeageHandlers.cordova.postMessage(command);`; + +这样便实现了前端到 iOS 端的单向通信。 + +### 5. 解析参数和插件绑定 + +在 iOS 端接收后,开始解析 `command` 参数,将解析结果回传到下一步。 + +另外在此之前, `InAppBrowser.js` 中声明全局变量 `__globalBrowser`,保存当前使用的 `InAppBrowser` 实例: + +```js +module.exports = function(strUrl, strWindowName, strWindowFeatures, callbacks) { + // ... 省略其他 + exec(cb, cb, "InAppBrowser", "open", [strUrl, strWindowName, strWindowFeatures]); + // 声明全局变量__globalBrowser,保存当前使用的InAppBrowser实例 + window.__globalBrowser = iab; + return iab; +} +``` + +之后在调用过程中,会将 `Cordova` 插件与主 `WebView` 绑定,实现主 `WebView` 和 `InAppBrowser` 共享实例。 + +### 6. 调起 Native 层的 Cordova 插件 + +接下来开始调用 Native 层的对应 Cordova 插件。 + +当主 `WebView` 和 `InAppBrowser` 共享实例后,Native 层就可以获取到 `InAppBorwser` 中的整个 `window` 对象(即 `window.__globalBrowser`)。 + +再执行 Native 层的 `callbackFromNative` 方法。 + + +### 7. 执行前端传入的回调 + +在执行的 `callbackFromNative` 方法中,会执行到 `else` 语句中,随后判断主 `WebView` 和 `InAppBrowser` 是否已经共享实例(即 `__globalBrowser` 是否指向当前使用的 `InAppBrowser` 实例)。 + +如果已经共享实例,则会通过拼接字符串实现 `cordova.callbackFromNative` 方法调用,并将字符串作为 `InAppBrowser` 实例的 `executeScript` 方法的参数对象中 `code` 的值,进行结构的回传。 + +``` +callbackFromNative: function(callbackId, isSuccess, status, args, keepCallback) { + try { + // ... 省略 + } else { + // __globalBrowser指向当前使用的InAppBrowser实例 + if(window.__globalBrowser) { + var message = 'cordova.callbackFromNative("'+callbackId+'", + '+isSuccess+',' + status +',' +JSON.stringify(args) + ',' + + keepCallback + ')'; + // 调用InAppBrowser实例的executeScript方法进行结果回传 + window.__globalBrowser.executeScript({code: message}); + } + } +} +``` + +### 8. 回传调用结果 + +将 `window.__globalBrowser.executeScript` 调用结果进行回传到 `InAppBorwser` 层中。 + +### 9. 调用前端回调函数 + +最后一步,`window.__globalBrowser.executeScript` 中会在 `InAppBorwser` 层中执行参数 `code` 值中的 `cordova.callbackFromNative` 方法,此时已经是在 `InAppBorwser` 层,因此这个 `cordova.callbackFromNative` 方法是 `InAppBorwser` 层中的。 + +在 `code` 参数中,包含: + +- `callbackId`; +- `isSuccess`; +- `status`; +- `args`; +- `keepCallback`; + +对应 `cordova.callbackFromNative` 方法中的参数。 + +此时在 `try` 语句中,就会通过 `callbackId` 拿到对应整个 `callback` 对象,最终执行 `callback.success` 方法等。 + +```js +callbackFromNative: function(callbackId, isSuccess, status, args, keepCallback) { + try { + var callback = cordova.callbacks[callbackId]; + if (callback) { + if (isSuccess && status == cordova.callbackStatus.OK) { + callback.success && callback.success.apply(null, args); + } else if (!isSuccess) { + callback.fail && callback.fail.apply(null, args); + } + if (!keepCallback) { + delete cordova.callbacks[callbackId]; + } + } else { + // __globalBrowser指向当前使用的InAppBrowser实例 + if(window.__globalBrowser) { + var message = 'cordova.callbackFromNative("'+callbackId+'", + '+isSuccess+',' + status +',' +JSON.stringify(args) + ',' + + keepCallback + ')'; + // 调用InAppBrowser实例的executeScript方法进行结果回传 + window.__globalBrowser.executeScript({code: message}); + } + } +} +``` + +这样便实现了 iOS 端到前端的单向通信。 + +于是,两端通信边完成,整个 `InAppBrowser` 调用 `Cordova` 插件的流程便完成了。 diff --git a/Cute-JavaScript/Cute-ES/1.ES6.md b/Cute-JavaScript/Cute-ES/1.ES6.md new file mode 100644 index 00000000..6f6ae6cf --- /dev/null +++ b/Cute-JavaScript/Cute-ES/1.ES6.md @@ -0,0 +1,3809 @@ + + +- [1 let 和 const命令](#1-let-和-const命令) + - [1.1 let 命令](#11-let-命令) + - [1.2 const 命令](#12-const-命令) +- [2 变量的解构赋值](#2-变量的解构赋值) + - [2.1 数组](#21-数组) + - [2.2 对象的解构赋值](#22-对象的解构赋值) + - [2.3 字符串的解构赋值](#23-字符串的解构赋值) + - [2.4 数值和布尔值的解构赋值](#24-数值和布尔值的解构赋值) + - [2.5 函数参数的解构赋值](#25-函数参数的解构赋值) + - [2.6 应用](#26-应用) +- [3 字符串的拓展](#3-字符串的拓展) + - [3.1 includes(),startsWith(),endsWith()](#31-includesstartswithendswith) + - [3.2 repeat()](#32-repeat) + - [3.3 padStart(),padEnd()](#33-padstartpadend) + - [3.4 模版字符串](#34-模版字符串) +- [4 正则的拓展](#4-正则的拓展) + - [4.1 介绍](#41-介绍) + - [4.2 字符串的正则方法](#42-字符串的正则方法) + - [4.3 u修饰符](#43-u修饰符) + - [4.4 y修饰符](#44-y修饰符) + - [4.5 flags属性](#45-flags属性) +- [5 数值的拓展](#5-数值的拓展) + - [5.1 Number.isFinite(), Number.isNaN()](#51-numberisfinite-numberisnan) + - [5.2 Number.parseInt(), Number.parseFloat()](#52-numberparseint-numberparsefloat) + - [5.3 Number.isInteger()](#53-numberisinteger) + - [5.4 Math对象的拓展](#54-math对象的拓展) + - [5.5 指数运算符](#55-指数运算符) +- [6 函数的拓展](#6-函数的拓展) + - [6.1 参数默认值](#61-参数默认值) + - [6.2 rest 参数](#62-rest-参数) + - [6.3 name 属性](#63-name-属性) + - [6.4 箭头函数](#64-箭头函数) + - [6.5 双冒号运算符](#65-双冒号运算符) +- [7 数组的拓展](#7-数组的拓展) + - [7.1 拓展运算符](#71-拓展运算符) + - [7.2 Array.from()](#72-arrayfrom) + - [7.3 Array.of()](#73-arrayof) + - [7.4 find()和findIndex()](#74-find和findindex) + - [7.5 fill()](#75-fill) + - [7.6 entries(),keys(),values()](#76-entrieskeysvalues) + - [7.7 includes()](#77-includes) + - [7.8 flat(),flatMap()](#78-flatflatmap) +- [8 对象的拓展](#8-对象的拓展) + - [8.1 属性的简洁表示](#81-属性的简洁表示) + - [8.2 属性名表达式](#82-属性名表达式) + - [8.3 Object.is()](#83-objectis) + - [8.4 Object.assign()](#84-objectassign) +- [9 Symbol](#9-symbol) + - [9.1 介绍](#91-介绍) + - [9.2 Symbol作为属性名](#92-symbol作为属性名) + - [9.3 应用:消除魔术字符串](#93-应用消除魔术字符串) + - [9.4 属性名遍历](#94-属性名遍历) + - [9.5 Symbol.for()、Symbol.keyFor()](#95-symbolforsymbolkeyfor) + - [9.6 内置的Symbol值](#96-内置的symbol值) +- [10 Set和Map数据结构](#10-set和map数据结构) + - [10.1 Set](#101-set) + - [10.2 Set的应用](#102-set的应用) + - [10.3 Map](#103-map) + - [10.4 Map与其他数据结构互相转换](#104-map与其他数据结构互相转换) +- [11 Proxy](#11-proxy) + - [11.1 基础使用](#111-基础使用) + - [11.2 取消Proxy实例](#112-取消proxy实例) + - [11.3 实现 Web服务的客户端](#113-实现-web服务的客户端) +- [12 Promise对象](#12-promise对象) + - [12.1 概念](#121-概念) + - [12.2 基本使用](#122-基本使用) + - [12.3 Promise.prototype.then()](#123-promiseprototypethen) + - [12.4 Promise.prototype.catch()](#124-promiseprototypecatch) + - [12.5 Promise.prototype.finally()](#125-promiseprototypefinally) + - [12.6 Promise.all()](#126-promiseall) + - [12.7 Promise.race()](#127-promiserace) + - [12.8 Promise.resolve()](#128-promiseresolve) + - [12.9 Promise.reject()](#129-promisereject) +- [13 Iterator和 for...of循环](#13-iterator和-forof循环) + - [13.1 Iterator遍历器概念](#131-iterator遍历器概念) + - [13.2 Iterator遍历过程](#132-iterator遍历过程) + - [13.3 默认Iterator接口](#133-默认iterator接口) + - [13.4 Iterator使用场景](#134-iterator使用场景) + - [13.5 for...of循环](#135-forof循环) + - [13.6 跳出for...of](#136-跳出forof) +- [14 Generator函数和应用](#14-generator函数和应用) + - [14.1 基本概念](#141-基本概念) + - [14.2 yield表达式](#142-yield表达式) + - [14.3 next方法](#143-next方法) + - [14.4 for...of循环](#144-forof循环) + - [14.5 Generator.prototype.throw()](#145-generatorprototypethrow) + - [14.6 Generator.prototype.return()](#146-generatorprototypereturn) + - [14.7 next()/throw()/return()共同点](#147-nextthrowreturn共同点) + - [14.8 yield* 表达式](#148-yield-表达式) + - [14.9 应用场景](#149-应用场景) +- [15 Class语法和继承](#15-class语法和继承) + - [15.1 介绍](#151-介绍) + - [15.2 constructor()方法](#152-constructor方法) + - [15.3 类的实例对象](#153-类的实例对象) + - [15.4 Class表达式](#154-class表达式) + - [15.5 私有方法和私有属性](#155-私有方法和私有属性) + - [15.6 this指向问题](#156-this指向问题) + - [15.7 Class的getter和setter](#157-class的getter和setter) + - [15.8 Class的generator方法](#158-class的generator方法) + - [15.9 Class的静态方法](#159-class的静态方法) + - [15.10 Class的静态属性和实例属性](#1510-class的静态属性和实例属性) + - [15.11 Class的继承](#1511-class的继承) +- [16 Module语法和加载实现](#16-module语法和加载实现) + - [16.1 介绍](#161-介绍) + - [16.2 严格模式](#162-严格模式) + - [16.3 export命令](#163-export命令) + - [16.4 import命令](#164-import命令) + - [16.5 模块的整体加载](#165-模块的整体加载) + - [16.6 export default 命令](#166-export-default-命令) + - [16.7 export 和 import 复合写法](#167-export-和-import-复合写法) + - [16.8 浏览器中的加载规则](#168-浏览器中的加载规则) + + +## 1 let 和 const命令 + +在ES6中,我们通常实用 `let` 表示**变量**,`const` 表示**常量**,并且 `let` 和 `const` 都是**块级作用域**,且在**当前作用域有效**不能重复声明。 + +### 1.1 let 命令 +`let` 命令的用法和 `var` 相似,但是 `let` 只在所在代码块内有效。 +**基础用法**: +```js +{ + let a = 1; + let b = 2; +} +``` + +并且 `let` 有以下特点: + +* **不存在变量提升:** +在ES6之前,我们 `var` 声明一个**变量**一个**函数**,都会伴随着变量提升的问题,导致实际开发过程经常出现一些逻辑上的疑惑,按照一般思维习惯,变量都是需要先声明后使用。 +```js +// var +console.log(v1); // undefined +var v1 = 2; +// 由于变量提升 代码实际如下 +var v1; +console.log(v1) +v1 = 2; + +// let +console.log(v2); // ReferenceError +let v2 = 2; +``` + +* **不允许重复声明:** +`let` 和 `const` 在**相同作用域下**,都**不能重复声明同一变量**,并且**不能在函数内重新声明参数**。 +```js +// 1. 不能重复声明同一变量 +// 报错 +function f1 (){ + let a = 1; + var a = 2; +} +// 报错 +function f2 (){ + let a = 1; + let a = 2; +} + +// 2. 不能在函数内重新声明参数 +// 报错 +function f3 (a1){ + let a1; +} +// 不报错 +function f4 (a2){ + { + let a2 + } +} +``` + +### 1.2 const 命令 +`const` 声明一个**只读**的**常量**。 +**基础用法**: +```js +const PI = 3.1415926; +console.log(PI); // 3.1415926 + +``` +**注意点**: +* `const` 声明后,无法修改值; +```js +const PI = 3.1415926; +PI = 3; +// TypeError: Assignment to constant variable. +``` +* `const` 声明时,必须赋值; +```js +const a ; +// SyntaxError: Missing initializer in const declaration. +``` +* `const` 声明的常量,`let` 不能重复声明; +```js +const PI = 3.1415926; +let PI = 0; +// Uncaught SyntaxError: Identifier 'PI' has already been declared +``` + +[⬆ 返回目录](#二目录) + +## 2 变量的解构赋值 +**解构赋值概念**:在ES6中,直接从数组和对象中取值,按照对应位置,赋值给变量的操作。 + +### 2.1 数组 +**基础用法**: +```js +// ES6 之前 +let a = 1; +let b = 2; + +// ES6 之后 +let [a, b] = [1, 2]; +``` + +本质上,只要等号两边模式一致,左边变量即可获取右边对应位置的值,更多用法: + +```js +let [a, [[b], c]] = [1, [[2], 3]]; +console.log(a, b, c); // 1, 2, 3 + +let [ , , c] = [1, 2, 3]; +console.log(c); // 3 + +let [a, , c] = [1, 2, 3]; +console.log(a,c); // 1, 3 + +let [a, ...b] = [1, 2, 3]; +console.log(a,b); // 1, [2,3] + +let [a, b, ..c.] = [1]; +console.log(a, b, c); // 1, undefined, [] +``` + +**注意点**: +* 如果解构不成功,变量的值就等于`undefined`。 +```js +let [a] = []; // a => undefined +let [a, b] = [1]; // a => 1 , b => undefined +``` +* 当左边模式多于右边,也可以解构成功。 +```js +let [a, b] = [1, 2, 3]; +console.log(a, b); // 1, 2 +``` +* 两边模式不同,报错。 +```js +let [a] = 1; +let [a] = false; +let [a] = NaN; +let [a] = undefined; +let [a] = null; +let [a] = {}; +``` + +**指定解构的默认值**: +**基础用法**: +```js +let [a = 1] = []; // a => 1 +let [a, b = 2] = [a]; // a => 1 , b => 2 +``` +特殊情况: +```js +let [a = 1] = [undefined]; // a => 1 +let [a = 1] = [null]; // a => null +``` +右边模式对应的值,必须严格等于`undefined`,默认值才能生效,而`null`不严格等于`undefined`。 + +### 2.2 对象的解构赋值 +与数组解构不同的是,对象解构**不需要严格按照顺序取值**,而只要按照**变量名**去取对应**属性名**的值,若取不到对应**属性名**的值,则为`undefined` 。 + +**基础用法**: +```js +let {a, b} = {a:1, b:2}; // a => 1 , b => 2 +let {a, b} = {a:2, b:1}; // a => 2 , b => 1 +let {a} = {a:3, b:2, c:1};// a => 3 +let {a} = {b:2, c:1}; // a => undefined +``` + +**注意点**: +* 若**变量名**和**属性名**不一致,则需要修改名称。 +```js +let {a:b} = {a:1, c:2}; +// error: a is not defined +// b => 1 +``` +对象的解构赋值的内部机制,是先找到同名属性,然后再赋给对应的变量。真正被赋值的是后者,而不是前者。 +上面代码中,`a` 是匹配的模式,`b`才是变量。真正被赋值的是变量`b`,而不是模式`a`。 + +* 对象解构也支持**嵌套解构**。 +```js +let obj = { + a:[ 1, { b: 2}] +}; +let {a, a: [c, {b}]} = obj; +// a=>[1, {b: 2}], b => 2, c => 1 +``` + +**指定解构的默认值**: +```js +let {a=1} = {}; // a => 1 +let {a, b=1} = {a:2}; // a => 2, b => 1 + +let {a:b=3} = {}; // b => 3 +let {a:b=3} = {a:4}; // b = >4 +// a是模式,b是变量 牢记 + +let {a=1} = {a:undefined}; // a => 1 +let {a=1} = {a:null}; // a => null +// 因为null与undefined不严格相等,所以赋值有效 +// 导致默认值1不会生效。 +``` + +### 2.3 字符串的解构赋值 +字符串的解构赋值中,字符串被转换成了一个**类似数组的对象**。 +**基础用法**: +```js +const [a, b, c, d, e] = 'hello'; +a // "h" +b // "e" +c // "l" +d // "l" +e // "o" + +let {length:len} = 'hello';// len => 5 +``` + +### 2.4 数值和布尔值的解构赋值 +解构赋值的规则是,**只要等号右边的值不是对象或数组,就先将其转为对象**。由于`undefined`和`null`**无法转为对象**,所以对它们进行解构赋值,都会报错。 +```js +// 数值和布尔值的包装对象都有toString属性 +let {toString: s} = 123; +s === Number.prototype.toString // true +let {toString: s} = true; +s === Boolean.prototype.toString // true + +let { prop: x } = undefined; // TypeError +let { prop: y } = null; // TypeError +``` + +### 2.5 函数参数的解构赋值 +**基础用法**: +```js +function fun ([a, b]){ + return a + b; +} +fun ([1, 2]); // 3 +``` +**指定默认值的解构**: +```js +function fun ({a=0, b=0} = {}){ + return [a, b]; +} +fun ({a:1, b:2}); // [1, 2] +fun ({a:1}); // [1, 0] +fun ({}); // [0, 0] +fun (); // [0, 0] + +function fun ({a, b} = {a:0, b:0}){ + return [a, b]; +} +fun ({a:1, b:2}); // [1, 2] +fun ({a:1}); // [1, undefined] +fun ({}); // [undefined, undefined] +fun (); // [0, 0] +``` + +### 2.6 应用 +* **交换变量的值**: +```js +let a = 1,b = 2; +[a, b] = [b, a]; // a =>2 , b => 1 +``` + +* **函数返回多个值**: +```js +// 返回一个数组 +function f (){ + return [1, 2, 3]; +} +let [a, b, c] = f(); // a=>1, b=>2, c=>3 + +// 返回一个对象 +function f (){ + return {a:1, b:2}; +} +let {a, b} = f(); // a=>1, b=>2 +``` + +* **快速对应参数**: +快速的将一组参数与变量名对应。 +```js +function f([a, b, c]) {...} +f([1, 2, 3]); + +function f({a, b, c}) {...} +f({b:2, c:3, a:1}); +``` + +* **提取JSON数据**: +```js +let json = { + name : 'leo', + age: 18 +} +let {name, age} = json; +console.log(name,age); // leo, 18 +``` + +* **遍历Map结构**: +```js +const m = new Map(); +m.set('a', 1); +m.set('b', 2); +for (let [k, v] of m){ + console.log(k + ' : ' + v); +} +// 获取键名 +for (let [k] of m){...} +// 获取键值 +for (let [,k] of m){...} +``` + +* **输入模块的指定方法**: +用于**按需加载**模块中需要用到的方法。 +```js +const {log, sin, cos} = require('math'); + +``` + +[⬆ 返回目录](#二目录) + + +## 3 字符串的拓展 +### 3.1 includes(),startsWith(),endsWith() +在我们判断字符串是否包含另一个字符串时,ES6之前,我们只有`typeof`方法,ES6之后我们又多了三种方法: +* **includes()**:返回**布尔值**,表示**是否找到参数字符串**。 +* **startsWith()**:返回**布尔值**,表示参数字符串是否在原字符串的**头部**。 +* **endsWith()**:返回**布尔值**,表示参数字符串是否在原字符串的**尾部**。 +```js +let a = 'hello leo'; +a.startsWith('leo'); // false +a.endsWith('o'); // true +a.includes('lo'); // true +``` +并且这三个方法都支持第二个参数,表示起始搜索的位置。 +```js +let a = 'hello leo'; +a.startsWith('leo',1); // false +a.endsWith('o',5); // true +a.includes('lo',6); // false +``` +`endsWith` 是针对前 `n` 个字符,而其他两个是针对从第`n`个位置直到结束。 + +### 3.2 repeat() +`repeat`方法返回一个新字符串,表示将原字符串重复`n`次。 +**基础用法**: +```js +'ab'.repeat(3); // 'ababab' +'ab'.repeat(0); // '' +``` +**特殊用法**: +* 参数为`小数`,则取整 +```js +'ab'.repeat(2.3); // 'abab' +``` +* 参数为`负数`或`Infinity`,则报错 +```js +'ab'.repeat(-1); // RangeError +'ab'.repeat(Infinity); // RangeError +``` +* 参数为`0到-1的小数`或`NaN`,则取0 +```js +'ab'.repeat(-0.5); // '' +'ab'.repeat(NaN); // '' +``` +* 参数为`字符串`,则转成`数字` +```js +'ab'.repeat('ab'); // '' +'ab'.repeat('3'); // 'ababab' +``` + +### 3.3 padStart(),padEnd() +用于将字符串**头部**或**尾部**补全长度,`padStart()`为**头部补全**,`padEnd()`为**尾部补全**。 +这两个方法接收**2个**参数,第一个指定**字符串最小长度**,第二个**用于补全的字符串**。 +**基础用法** : +```js +'x'.padStart(5, 'ab'); // 'ababx' +'x'.padEnd(5, 'ab'); // 'xabab' +``` +**特殊用法**: +* 原字符串长度,大于或等于指定最小长度,则返回原字符串。 +```js +'xyzabc'.padStart(5, 'ab'); // 'xyzabc' +``` +* 用来补全的字符串长度和原字符串长度之和,超过指定最小长度,则截去超出部分的补全字符串。 +```js +'ab'.padStart(5,'012345'); // "012ab" +``` +* 省略第二个参数,则用`空格`补全。 +```js +'x'.padStart(4); // ' x' +'x'.padEnd(4); // 'x ' +``` +### 3.4 模版字符串 +用于拼接字符串,ES6之前: +```js +let a = 'abc' + + 'def' + + 'ghi'; +``` +ES6之后: +```js +let a = ` + abc + def + ghi +` +``` +**拼接变量**: +在**反引号(\`)**中使用`${}`包裹变量或方法。 +```js +// ES6之前 +let a = 'abc' + v1 + 'def'; + +// ES6之后 +let a = `abc${v1}def` +``` + +[⬆ 返回目录](#二目录) + + +## 4 正则的拓展 +### 4.1 介绍 +在ES5中有两种情况。 +* 参数是**字符串**,则第二个参数为正则表达式的修饰符。 +```js +let a = new RegExp('abc', 'i'); +// 等价于 +let a = /abx/i; +``` +* 参数是**正则表达式**,返回一个原表达式的拷贝,且不能有第二个参数,否则报错。 +```js +let a = new RegExp(/abc/i); +//等价于 +let a = /abx/i; + +let a = new RegExp(/abc/, 'i'); +// Uncaught TypeError +``` +ES6中使用: +第一个参数是正则对象,第二个是指定修饰符,如果第一个参数已经有修饰符,则会被第二个参数覆盖。 +```js +new RegExp(/abc/ig, 'i'); +``` + +### 4.2 字符串的正则方法 +常用的四种方法:`match()`、`replace()`、`search()`和`split()`。 + +### 4.3 u修饰符 +添加`u`修饰符,是为了处理大于`uFFFF`的Unicode字符,即正确处理四个字节的UTF-16编码。 +```js +/^\uD83D/u.test('\uD83D\uDC2A'); // false +/^\uD83D/.test('\uD83D\uDC2A'); // true +``` +由于ES5之前不支持四个字节UTF-16编码,会识别为两个字符,导致第二行输出`true`,加入`u`修饰符后ES6就会识别为一个字符,所以输出`false`。 + +**注意:** +加上`u`修饰符后,会改变下面正则表达式的行为: +* (1)点字符 +点字符(`.`)在正则中表示除了**换行符**以外的任意单个字符。对于码点大于`0xFFFF`的Unicode字符,点字符不能识别,必须加上`u`修饰符。 +```js +var a = "𠮷"; +/^.$/.test(a); // false +/^.$/u.test(a); // true +``` +* (2)Unicode字符表示法 +使用ES6新增的大括号表示Unicode字符时,必须在表达式添加`u`修饰符,才能识别大括号。 +```js +/\u{61}/.test('a'); // false +/\u{61}/u.test('a'); // true +/\u{20BB7}/u.test('𠮷'); // true +``` +* (3)量词 +使用`u`修饰符后,所有量词都会正确识别码点大于`0xFFFF`的 Unicode 字符。 +```js +/a{2}/.test('aa'); // true +/a{2}/u.test('aa'); // true +/𠮷{2}/.test('𠮷𠮷'); // false +/𠮷{2}/u.test('𠮷𠮷'); // true +``` +* (4)i修饰符 +不加`u`修饰符,就无法识别非规范的`K`字符。 +```js +/[a-z]/i.test('\u212A') // false +/[a-z]/iu.test('\u212A') // true +``` + +**检查是否设置`u`修饰符:** +使用`unicode`属性。 +```js +const a = /hello/; +const b = /hello/u; + +a.unicode // false +b.unicode // true +``` + +### 4.4 y修饰符 +`y`修饰符与`g`修饰符类似,也是全局匹配,后一次匹配都是从上一次匹配成功的下一个位置开始。区别在于,`g`修饰符**只要**剩余位置中存在匹配即可,而`y`修饰符是必须从**剩余第一个**开始。 +```js +var s = 'aaa_aa_a'; +var r1 = /a+/g; +var r2 = /a+/y; + +r1.exec(s) // ["aaa"] +r2.exec(s) // ["aaa"] + +r1.exec(s) // ["aa"] 剩余 '_aa_a' +r2.exec(s) // null +``` +**`lastIndex`属性**: +指定匹配的开始位置: +```js +const a = /a/y; +a.lastIndex = 2; // 从2号位置开始匹配 +a.exec('wahaha'); // null +a.lastIndex = 3; // 从3号位置开始匹配 +let c = a.exec('wahaha'); +c.index; // 3 +a.lastIndex; // 4 +``` +**返回多个匹配**: +一个`y`修饰符对`match`方法只能返回第一个匹配,与`g`修饰符搭配能返回所有匹配。 +```js +'a1a2a3'.match(/a\d/y); // ["a1"] +'a1a2a3'.match(/a\d/gy); // ["a1", "a2", "a3"] +``` +**检查是否使用`y`修饰符**: +使用`sticky`属性检查。 +```js +const a = /hello\d/y; +a.sticky; // true +``` + +### 4.5 flags属性 +`flags`属性返回所有正则表达式的修饰符。 +```js +/abc/ig.flags; // 'gi' +``` + + +[⬆ 返回目录](#二目录) + + +## 5 数值的拓展 +### 5.1 Number.isFinite(), Number.isNaN() +`Number.isFinite()` 用于检查一个数值是否是有限的,即不是`Infinity`,若参数不是`Number`类型,则一律返回`false` 。 +```js +Number.isFinite(10); // true +Number.isFinite(0.5); // true +Number.isFinite(NaN); // false +Number.isFinite(Infinity); // false +Number.isFinite(-Infinity); // false +Number.isFinite('leo'); // false +Number.isFinite('15'); // false +Number.isFinite(true); // false +Number.isFinite(Math.random()); // true +``` + +`Number.isNaN()`用于检查是否是`NaN`,若参数不是`NaN`,则一律返回`false`。 +```js +Number.isNaN(NaN); // true +Number.isNaN(10); // false +Number.isNaN('10'); // false +Number.isNaN(true); // false +Number.isNaN(5/NaN); // true +Number.isNaN('true' / 0); // true +Number.isNaN('true' / 'true'); // true +``` + +**区别**: +与传统全局的`isFinite()`和`isNaN()`方法的区别,传统的这两个方法,是先将参数转换成**数值**,再判断。 +而ES6新增的这两个方法则只对**数值**有效, `Number.isFinite()`对于**非数值**一律返回`false`,` Number.isNaN()`只有对于`NaN`才返回`true`,其他一律返回`false`。 +```js +isFinite(25); // true +isFinite("25"); // true +Number.isFinite(25); // true +Number.isFinite("25"); // false + +isNaN(NaN); // true +isNaN("NaN"); // true +Number.isNaN(NaN); // true +Number.isNaN("NaN"); // false +``` + +### 5.2 Number.parseInt(), Number.parseFloat() +这两个方法与全局方法`parseInt()`和`parseFloat()`一致,目的是逐步**减少全局性的方法**,让**语言更模块化**。 +```js +parseInt('12.34'); // 12 +parseFloat('123.45#'); // 123.45 + +Number.parseInt('12.34'); // 12 +Number.parseFloat('123.45#'); // 123.45 + +Number.parseInt === parseInt; // true +Number.parseFloat === parseFloat; // true +``` + +### 5.3 Number.isInteger() +用来判断一个数值是否是整数,若参数不是数值,则返回`false`。 +```js +Number.isInteger(10); // true +Number.isInteger(10.0); // true +Number.isInteger(10.1); // false +``` + +### 5.4 Math对象的拓展 +ES6新增17个数学相关的**静态方法**,只能在**Math对象**上调用。 +* **Math.trunc**: +用来去除小数的小数部分,**返回整数部分**。 +若参数为**非数值**,则**先转为数值**。 +若参数为**空值**或**无法截取整数的值**,则返回**NaN**。 +```js +// 正常使用 +Math.trunc(1.1); // 1 +Math.trunc(1.9); // 1 +Math.trunc(-1.1); // -1 +Math.trunc(-1.9); // -1 +Math.trunc(-0.1234); // -0 + +// 参数为非数值 +Math.trunc('11.22'); // 11 +Math.trunc(true); // 1 +Math.trunc(false); // 0 +Math.trunc(null); // 0 + +// 参数为空和无法取整 +Math.trunc(NaN); // NaN +Math.trunc('leo'); // NaN +Math.trunc(); // NaN +Math.trunc(undefined); // NaN +``` +**ES5实现**: +```js +Math.trunc = Math.trunc || function(x){ + return x < 0 ? Math.ceil(x) : Math.floor(x); +} +``` + +* **Math.sign()**: +判断一个数是**正数**、**负数**还**是零**,对于非数值,会先转成**数值**。 +返回值: + * 参数为正数, 返回 +1 + * 参数为负数, 返回 -1 + * 参数为0, 返回 0 + * 参数为-0, 返回 -0 + * 参数为其他值, 返回 NaN +```js +Math.sign(-1); // -1 +Math.sign(1); // +1 +Math.sign(0); // 0 +Math.sign(-0); // -0 +Math.sign(NaN); // NaN + +Math.sign(''); // 0 +Math.sign(true); // +1 +Math.sign(false);// 0 +Math.sign(null); // 0 +Math.sign('9'); // +1 +Math.sign('leo');// NaN +Math.sign(); // NaN +Math.sign(undefined); // NaN +``` + +**ES5实现** +```js +Math.sign = Math.sign || function (x){ + x = +x; + if (x === 0 || isNaN(x)){ + return x; + } + return x > 0 ? 1: -1; +} +``` + +* **Math.cbrt()**: +用来计算一个数的立方根,若参数为非数值则先转成数值。 +```js +Math.cbrt(-1); // -1 +Math.cbrt(0); // 0 +Math.cbrt(1); // 1 +Math.cbrt(2); // 1.2599210498 + +Math.cbrt('1'); // 1 +Math.cbrt('leo'); // NaN +``` +**ES5实现** +```js +Math.cbrt = Math.cbrt || function (x){ + var a = Math.pow(Math.abs(x), 1/3); + return x < 0 ? -y : y; +} +``` + +* **Math.clz32()**: +用于返回一个数的 32 位无符号整数形式有多少个前导 0。 +```js +Math.clz32(0) // 32 +Math.clz32(1) // 31 +Math.clz32(1000) // 22 +Math.clz32(0b01000000000000000000000000000000) // 1 +Math.clz32(0b00100000000000000000000000000000) // 2 +``` + +* **Math.imul()**: +用于返回两个数以 32 位带符号整数形式相乘的结果,返回的也是一个 32 位的带符号整数。 +```js +Math.imul(2, 4) // 8 +Math.imul(-1, 8) // -8 +Math.imul(-2, -2) // 4 +``` + +* **Math.fround()**: +用来返回一个数的**2位单精度浮点数**形式。 +```js +Math.fround(0) // 0 +Math.fround(1) // 1 +Math.fround(2 ** 24 - 1) // 16777215 +``` + +* **Math.hypot()**: +用来返回所有参数的平方和的**平方根**。 +```js +Math.hypot(3, 4); // 5 +Math.hypot(3, 4, 5); // 7.0710678118654755 +Math.hypot(); // 0 +Math.hypot(NaN); // NaN +Math.hypot(3, 4, 'foo'); // NaN +Math.hypot(3, 4, '5'); // 7.0710678118654755 +Math.hypot(-3); // 3 +``` + +* **Math.expm1()**: +用来返回` ex - 1`,即`Math.exp(x) - 1`。 +```js +Math.expm1(-1) // -0.6321205588285577 +Math.expm1(0) // 0 +Math.expm1(1) // 1.718281828459045 +``` +**ES5实现** +```js +Math.expm1 = Math.expm1 || function(x) { + return Math.exp(x) - 1; +}; +``` + +* **Math.log1p()**: +用来返回`1 + x`的自然对数,即`Math.log(1 + x)`。如果x小于`-1`,返回`NaN`。 +```js +Math.log1p(1) // 0.6931471805599453 +Math.log1p(0) // 0 +Math.log1p(-1) // -Infinity +Math.log1p(-2) // NaN +``` +**ES5实现** +```js +Math.log1p = Math.log1p || function(x) { + return Math.log(1 + x); +}; +``` + +* **Math.log10()**: +用来返回以 `10 `为底的`x的对数`。如果x小于 0,则返回 `NaN`。 +```js +Math.log10(2) // 0.3010299956639812 +Math.log10(1) // 0 +Math.log10(0) // -Infinity +Math.log10(-2) // NaN +Math.log10(100000) // 5 +``` +**ES5实现** +```js +Math.log10 = Math.log10 || function(x) { + return Math.log(x) / Math.LN10; +}; +``` + +* **Math.log2()**: +用来返回以 `2` 为底的`x的对数`。如果`x`小于` 0`,则返回 `NaN`。 +```js +Math.log2(3) // 1.584962500721156 +Math.log2(2) // 1 +Math.log2(1) // 0 +Math.log2(0) // -Infinity +Math.log2(-2) // NaN +Math.log2(1024) // 10 +Math.log2(1 << 29) // 29 +``` +**ES5实现** +```js +Math.log2 = Math.log2 || function(x) { + return Math.log(x) / Math.LN2; +}; +``` +* **双曲函数方法**: + * `Math.sinh(x)` 返回x的**双曲正弦**(hyperbolic sine) + * `Math.cosh(x)` 返回x的**双曲余弦**(hyperbolic cosine) + * `Math.tanh(x)` 返回x的**双曲正切**(hyperbolic tangent) + * `Math.asinh(x)` 返回x的**反双曲正弦**(inverse hyperbolic sine) + * `Math.acosh(x)` 返回x的**反双曲余弦**(inverse hyperbolic cosine) + * `Math.atanh(x)` 返回x的**反双曲正切**(inverse hyperbolic tangent) + +### 5.5 指数运算符 +新增的指数运算符(`**`): +```js +2 ** 2; // 4 +2 ** 3; // 8 + +2 ** 3 ** 2; // 相当于 2 ** (3 ** 2); 返回 512 +``` +指数运算符(`**`)与`Math.pow`的实现不相同,对于特别大的运算结果,两者会有细微的差异。 +```js +Math.pow(99, 99) +// 3.697296376497263e+197 + +99 ** 99 +// 3.697296376497268e+197 +``` + +[⬆ 返回目录](#二目录) + + +## 6 函数的拓展 +### 6.1 参数默认值 +```js +// ES6 之前 +function f(a, b){ + b = b || 'leo'; + console.log(a, b); +} + +// ES6 之后 +function f(a, b='leo'){ + console.log(a, b); +} + +f('hi'); // hi leo +f('hi', 'jack'); // hi jack +f('hi', ''); // hi leo +``` +**注意**: +* 参数变量是默认声明的,不能用`let`和`const`再次声明: +```js +function f (a = 1){ + let a = 2; // error +} +``` +* 使用参数默认值时,参数名不能相同: +```js +function f (a, a, b){ ... }; // 不报错 +function f (a, a, b = 1){ ... }; // 报错 +``` + +**与解构赋值默认值结合使用**: +```js +function f ({a, b=1}){ + console.log(a,b) +}; +f({}); // undefined 1 +f({a:2}); // 2 1 +f({a:2, b:3}); // 2 3 +f(); // 报错 + +function f ({a, b = 1} = {}){ + console.log(a, b) +} +f(); // undefined 1 +``` + +**尾参数定义默认值**: +通常在尾参数定义默认值,便于观察参数,并且非尾参数无法省略。 +```js +function f (a=1,b){ + return [a, b]; +} +f(); // [1, undefined] +f(2); // [2, undefined] +f(,2); // 报错 + +f(undefined, 2); // [1, 2] + +function f (a, b=1, c){ + return [a, b, c]; +} +f(); // [undefined, 1, undefined] +f(1); // [1,1,undefined] +f(1, ,2); // 报错 +f(1,undefined,2); // [1,1,2] +``` +在给参数传递默认值时,传入`undefined`会触发默认值,传入`null`不会触发。 +```js +function f (a = 1, b = 2){ + console.log(a, b); +} +f(undefined, null); // 1 null +``` + +**函数的length属性**: +`length`属性将返回,没有指定默认值的参数数量,并且rest参数不计入`length`属性。 +```js +function f1 (a){...}; +function f2 (a=1){...}; +function f3 (a, b=2){...}; +function f4 (...a){...}; +function f5 (a,b,...c){...}; + +f1.length; // 1 +f2.length; // 0 +f3.length; // 1 +f4.length; // 0 +f5.length; // 2 +``` + +### 6.2 rest 参数 +`rest`参数形式为(`...变量名`),其值为一个数组,用于获取函数多余参数。 +```js +function f (a, ...b){ + console.log(a, b); +} +f(1,2,3,4); // 1 [2, 3, 4] +``` +**注意**: +* `rest`参数只能放在最后一个,否则报错: +```js +function f(a, ...b, c){...}; // 报错 +``` +* 函数的`length`属性不包含`rest`参数。 +```js +function f1 (a){...}; +function f2 (a,...b){...}; +f1(1); // 1 +f2(1,2); // 1 +``` + +### 6.3 name 属性 +用于返回该函数的函数名。 +```js +function f (){...}; +f.name; // f + +const f = function g(){...}; +f.name; // g +``` + +### 6.4 箭头函数 +使用“箭头”(`=>`)定义函数。 +**基础使用**: +```js +// 有1个参数 +let f = v => v; +// 等同于 +let f = function (v){return v}; + +// 有多个参数 +let f = (v, i) => {return v + i}; +// 等同于 +let f = function (v, i){return v + i}; + +// 没参数 +let f = () => 1; +// 等同于 +let f = function (){return 1}; +``` + +**箭头函数与变量结构结合使用**: +```js +// 正常函数写法 +function f (p) { + return p.a + ':' + p.b; +} + +// 箭头函数写法 +let f = ({a, b}) => a + ':' + b; +``` + +**简化回调函数**: +```js +// 正常函数写法 +[1, 2, 3].map(function (x){ + return x * x; +}) + + +// 箭头函数写法 +[1, 2, 3].map(x => x * x); +``` + +**箭头函数与rest参数结合**: +```js +let f = (...n) => n; +f(1, 2, 3); // [1, 2, 3] +``` + +**注意点**: +* 1.箭头函数内的`this`**总是**指向**定义时所在的对象**,而不是调用时。 +* 2.箭头函数不能当做**构造函数**,即不能用`new`命令,否则报错。 +* 3.箭头函数不存在`arguments`对象,即不能使用,可以使用`rest`参数代替。 +* 4.箭头函数不能使用`yield`命令,即不能用作**Generator**函数。 + +**不适用场景**: +* 1.在定义函数方法,且该方法内部包含`this`。 +```js +const obj = { + a:9, + b: () => { + this.a --; + } +} +``` +上述`b`如果是**普通函数**,函数内部的`this`指向`obj`,但是如果是箭头函数,则`this`会指向**全局**,不是预期结果。 + +* 2.需要动态`this`时。 +```js +let b = document.getElementById('myID'); +b.addEventListener('click', ()=>{ + this.classList.toggle('on'); +}) +``` +上诉按钮点击会报错,因为`b`监听的箭头函数中,`this`是全局对象,若改成**普通函数**,`this`就会指向被点击的按钮对象。 + +### 6.5 双冒号运算符 +双冒号暂时是一个提案,用于解决一些不适用的场合,取代`call`、`apply`、`bind`调用。 +双冒号运算符(`::`)的左边是一个**对象**,右边是一个**函数**。该运算符会自动将左边的对象,作为上下文环境(即`this`对象),绑定到右边函数上。 +```js +f::b; +// 等同于 +b.bind(f); + +f::b(...arguments); +// 等同于 +b.apply(f, arguments); +``` +若双冒号左边为空,右边是一个对象的方法,则等于将该方法绑定到该对象上。 +```js +let f = a::a.b; +// 等同于 +let f = ::a.b; +``` + +[⬆ 返回目录](#二目录) + + +## 7 数组的拓展 +### 7.1 拓展运算符 +拓展运算符使用(`...`),类似`rest`参数的逆运算,将数组转为用(`,`)分隔的参数序列。 +```js +console.log(...[1, 2, 3]); // 1 2 3 +console.log(1, ...[2,3], 4); // 1 2 3 4 +``` +拓展运算符主要使用在函数调用。 +```js +function f (a, b){ + console.log(a, b); +} +f(...[1, 2]); // 1 2 + +function g (a, b, c, d, e){ + console.log(a, b, c, d, e); +} +g(0, ...[1, 2], 3, ...[4]); // 0 1 2 3 4 +``` +**若拓展运算符后面是个空数组,则不产生效果**。 +```js +[...[], 1]; // [1] +``` + +**替代apply方法** +```js +// ES6之前 +function f(a, b, c){...}; +var a = [1, 2, 3]; +f.apply(null, a); + +// ES6之后 +function f(a, b, c){...}; +let a = [1, 2, 3]; +f(...a); + +// ES6之前 +Math.max.apply(null, [3,2,6]); + +// ES6之后 +Math.max(...[3,2,6]); +``` + +**拓展运算符的运用** +* **(1)复制数组**: +通常我们直接复制数组时,只是浅拷贝,如果要实现深拷贝,可以使用拓展运算符。 +```js +// 通常情况 浅拷贝 +let a1 = [1, 2]; +let a2 = a1; +a2[0] = 3; +console.log(a1,a2); // [3,2] [3,2] + +// 拓展运算符 深拷贝 +let a1 = [1, 2]; +let a2 = [...a1]; +// let [...a2] = a1; // 作用相同 +a2[0] = 3; +console.log(a1,a2); // [1,2] [3,2] +``` +* **(2)合并数组**: +注意,这里合并数组,只是浅拷贝。 +```js +let a1 = [1,2]; +let a2 = [3]; +let a3 = [4,5]; + +// ES5 +let a4 = a1.concat(a2, a3); + +// ES6 +let a5 = [...a1, ...a2, ...a3]; + +a4[0] === a1[0]; // true +a5[0] === a1[0]; // true +``` +* **(3)与解构赋值结合**: +与解构赋值结合生成数组,但是使用拓展运算符需要放到参数最后一个,否则报错。 +```js +let [a, ...b] = [1, 2, 3, 4]; +// a => 1 b => [2,3,4] + +let [a, ...b] = []; +// a => undefined b => [] + +let [a, ...b] = ["abc"]; +// a => "abc" b => [] +``` + +### 7.2 Array.from() +将 **类数组对象** 和 **可遍历的对象**,转换成真正的数组。 +```js +// 类数组对象 +let a = { + '0':'a', + '1':'b', + length:2 +} +let arr = Array.from(a); + +// 可遍历的对象 +let a = Array.from([1,2,3]); +let b = Array.from({length: 3}); +let c = Array.from([1,2,3]).map(x => x * x); +let d = Array.from([1,2,3].map(x => x * x)); +``` + +### 7.3 Array.of() +将一组数值,转换成**数组**,弥补`Array`方法参数不同导致的差异。 +```js +Array.of(1,2,3); // [1,2,3] +Array.of(1).length; // 1 + +Array(); // [] +Array(2); // [,] 1个参数时,为指定数组长度 +Array(1,2,3); // [1,2,3] 多于2个参数,组成新数组 +``` + +### 7.4 find()和findIndex() +`find()`方法用于找出第一个符合条件的数组成员,参数为一个回调函数,所有成员依次执行该回调函数,返回第一个返回值为`true`的成员,如果没有一个符合则返回`undefined`。 +```js +[1,2,3,4,5].find( a => a < 3 ); // 1 +``` +回调函数接收三个参数,当前值、当前位置和原数组。 +```js +[1,2,3,4,5].find((value, index, arr) => { + // ... +}); +``` +`findIndex()`方法与`find()`类似,返回第一个符合条件的数组成员的**位置**,如果都不符合则返回`-1`。 +```js +[1,2,3,4].findIndex((v,i,a)=>{ + return v>2; +}); // 2 +``` + +### 7.5 fill() +用于用指定值**填充**一个数组,通常用来**初始化空数组**,并抹去数组中已有的元素。 +```js +new Array(3).fill('a'); // ['a','a','a'] +[1,2,3].fill('a'); // ['a','a','a'] +``` +并且`fill()`的第二个和第三个参数指定填充的**起始位置**和**结束位置**。 +```js +[1,2,3].fill('a',1,2);// [1, "a", 3] +``` + +### 7.6 entries(),keys(),values() +主要用于遍历数组,`entries()`对键值对遍历,`keys()`对键名遍历,`values()`对键值遍历。 +```js +for (let i of ['a', 'b'].keys()){ + console.log(i) +} +// 0 +// 1 + +for (let e of ['a', 'b'].values()){ + console.log(e) +} +// 'a' +// 'b' + +for (let e of ['a', 'b'].entries()){ + console.log(e) +} +// 0 'a' +// 1 'b' +``` + +### 7.7 includes() +用于表示数组是否包含给定的值,与字符串的`includes`方法类似。 +```js +[1,2,3].includes(2); // true +[1,2,3].includes(4); // false +[1,2,NaN].includes(NaN); // true +``` +第二个参数为**起始位置**,默认为`0`,如果负数,则表示倒数的位置,如果大于数组长度,则重置为`0`开始。 +```js +[1,2,3].includes(3,3); // false +[1,2,3].includes(3,4); // false +[1,2,3].includes(3,-1); // true +[1,2,3].includes(3,-4); // true +``` + +### 7.8 flat(),flatMap() +`flat()`用于将数组一维化,返回一个新数组,不影响原数组。 +默认一次只一维化一层数组,若需多层,则传入一个整数参数指定层数。 +若要一维化所有层的数组,则传入`Infinity`作为参数。 +```js +[1, 2, [2,3]].flat(); // [1,2,2,3] +[1,2,[3,[4,[5,6]]]].flat(3); // [1,2,3,4,5,6] +[1,2,[3,[4,[5,6]]]].flat('Infinity'); // [1,2,3,4,5,6] +``` +`flatMap()`是将原数组每个对象先执行一个函数,在对返回值组成的数组执行`flat()`方法,返回一个新数组,不改变原数组。 + `flatMap()`只能展开一层。 +```js +[2, 3, 4].flatMap((x) => [x, x * 2]); +// [2, 4, 3, 6, 4, 8] +``` + +[⬆ 返回目录](#二目录) + + +## 8 对象的拓展 +### 8.1 属性的简洁表示 +```js +let a = 'a1'; +let b = { a }; // b => { a : 'a1' } +// 等同于 +let b = { a : a }; + +function f(a, b){ + return {a, b}; +} +// 等同于 +function f (a, b){ + return {a:a ,b:b}; +} + +let a = { + fun () { + return 'leo'; + } +} +// 等同于 +let a = { + fun : function(){ + return 'leo'; + } +} +``` + +### 8.2 属性名表达式 +`JavaScript`提供2种方法**定义对象的属性**。 +```js +// 方法1 标识符作为属性名 +a.f = true; + +// 方法2 字符串作为属性名 +a['f' + 'un'] = true; +``` +延伸出来的还有: +```js +let a = 'hi leo'; +let b = { + [a]: true, + ['a'+'bc']: 123, + ['my' + 'fun'] (){ + return 'hi'; + } +}; +// b.a => undefined ; b.abc => 123 ; b.myfun() => 'hi' +// b[a] => true ; b['abc'] => 123 ; b['myfun'] => ƒ ['my' + 'fun'] (){ return 'hi'; } +``` +**注意**: +属性名表达式不能与简洁表示法同时使用,否则报错。 +```js +// 报错 +let a1 = 'aa'; +let a2 = 'bb'; +let b1 = {[a1]}; + +// 正确 +let a1 = 'aa'; +let b1 = { [a1] : 'bb'}; +``` + +### 8.3 Object.is() +`Object.is()` 用于比较两个值是否严格相等,在ES5时候只要使用**相等运算符**(`==`)和**严格相等运算符**(`===`)就可以做比较,但是它们都有缺点,前者会**自动转换数据类型**,后者的`NaN`不等于自身,以及`+0`等于`-0`。 +```js +Object.is('a','a'); // true +Object.is({}, {}); // false + +// ES5 ++0 === -0 ; // true +NaN === NaN; // false + +// ES6 +Object.is(+0,-0); // false +Object.is(NaN,NaN); // true +``` + +### 8.4 Object.assign() +`Object.assign()`方法用于对象的合并,将原对象的所有可枚举属性复制到目标对象。 +**基础用法**: +第一个参数是**目标对象**,后面参数都是**源对象**。 +```js +let a = {a:1}; +let b = {b:2}; +Object.assign(a,b); // a=> {a:1,b:2} +``` +**注意**: +* 若目标对象与源对象有同名属性,则后面属性会覆盖前面属性。 +```js +let a = {a:1, b:2}; +let b = {b:3, c:4}; +Object.assign(a, b); // a => {a:1, b:3, c:4} +``` +* 若只有**一个**参数,则返回该参数。 +```js +let a = {a:1}; +Object.assign(a) === a; // true +``` +* 若参数**不是对象**,则先转成对象后返回。 +```js +typeof Object.assign(2); // 'object' +``` +* 由于`undefined`或`NaN`无法转成对象,所以做为参数会报错。 +```js +Object.assign(undefined) // 报错 +Object.assign(NaN); // 报错 +``` +* `Object.assign()`实现的是浅拷贝。 + +`Object.assign()`拷贝得到的是这个对象的引用。这个对象的任何变化,都会反映到目标对象上面。 +```js +let a = {a: {b:1}}; +let b = Object.assign({},a); +a.a.b = 2; +console.log(b.a.b); // 2 +``` +* 将数组当做对象处理,键名为数组下标,键值为数组下标对应的值。 +```js +Object.assign([1, 2, 3], [4, 5]); // [4, 5, 3] +``` + +[⬆ 返回目录](#二目录) + + +## 9 Symbol +### 9.1 介绍 +ES6引入`Symbol`作为一种新的**原始数据类型**,表示**独一无二**的值,主要是为了**防止属性名冲突**。 +ES6之后,JavaScript一共有其中数据类型:`Symbol`、`undefined`、`null`、`Boolean`、`String`、`Number`、`Object`。 +简单实用: +```js +let a = Symbol(); +typeof a; // "symbol" +``` +**注意:** +* `Symbol`函数不能用`new`,会报错。由于`Symbol`是一个原始类型,不是对象,所以不能添加属性,它是类似于字符串的数据类型。 +* `Symbol`都是不相等的,即使参数相同。 +```js +// 没有参数 +let a1 = Symbol(); +let a2 = Symbol(); +a1 === a2; // false + +// 有参数 +let a1 = Symbol('abc'); +let a2 = Symbol('abc'); +a1 === a2; // false +``` +* `Symbol`不能与其他类型的值计算,会报错。 +```js +let a = Symbol('hello'); +a + " world!"; // 报错 +`${a} world!`; // 报错 +``` +Symbol可以显式转换为字符串: +```js +let a1 = Symbol('hello'); + +String(a1); // "Symbol(hello)" +a1.toString(); // "Symbol(hello)" +``` +Symbol可以转换为布尔值,但不能转为数值: +```js +let a1 = Symbol(); +Boolean(a1); +!a1; // false + +Number(a1); // TypeError +a1 + 1 ; // TypeError +``` + +### 9.2 Symbol作为属性名 +好处:防止同名属性,还有防止键被改写或覆盖。 +```js +let a1 = Symbol(); + +// 写法1 +let b = {}; +b[a1] = 'hello'; + +// 写法2 +let b = { + [a1] : 'hello' +} + +// 写法3 +let b = {}; +Object.defineProperty(b, a1, {value : 'hello' }); + +// 3种写法 结果相同 +b[a1]; // 'hello' +``` +**需要注意:** Symbol作为对象属性名时,不能用点运算符,并且必须放在方括号内。 +```js +let a = Symbol(); +let b = {}; + +// 不能用点运算 +b.a = 'hello'; +b[a] ; // undefined +b['a'] ; // 'hello' + +// 必须放在方括号内 +let c = { + [a] : function (text){ + console.log(text); + } +} +c[a]('leo'); // 'leo' + +// 上面等价于 更简洁 +let c = { + [a](text){ + console.log(text); + } +} +``` + +**常常还用于创建一组常量,保证所有值不相等:** +```js +let a = {}; +a.a1 = { + AAA: Symbol('aaa'), + BBB: Symbol('bbb'), + CCC: Symbol('ccc') +} +``` + +### 9.3 应用:消除魔术字符串 +魔术字符串:指代码中多次出现,强耦合的字符串或数值,应该避免,而使用含义清晰的变量代替。 +```js +function f(a){ + if(a == 'leo') { + console.log('hello'); + } +} +f('leo'); // 'leo' 为魔术字符串 +``` +常使用变量,消除魔术字符串: +```js +let obj = { + name: 'leo' +}; +function f (a){ + if(a == obj.name){ + console.log('hello'); + } +} +f(obj.name); // 'leo' +``` +使用Symbol消除强耦合,使得不需关系具体的值: +```js +let obj = { + name: Symbol() +}; +function f (a){ + if(a == obj.name){ + console.log('hello'); + } +} +f(obj.name); +``` + +### 9.4 属性名遍历 +Symbol作为属性名遍历,不出现在`for...in`、`for...of`循环,也不被`Object.keys()`、`Object.getOwnPropertyNames()`、`JSON.stringify()`返回。 +```js +let a = Symbol('aa'),b= Symbol('bb'); +let obj = { + [a]:'11', [b]:'22' +} +for(let k of Object.values(obj)){console.log(k)} +// 无输出 + +let obj = {}; +let aa = Symbol('leo'); +Object.defineProperty(obj, aa, {value: 'hi'}); + +for(let k in obj){ + console.log(k); // 无输出 +} + +Object.getOwnPropertyNames(obj); // [] +Object.getOwnPropertySymbols(obj); // [Symbol(leo)] +``` + +`Object.getOwnPropertySymbols`方法返回一个数组,包含当前对象所有用做属性名的Symbol值。 +```js +let a = {}; +let a1 = Symbol('a'); +let a2 = Symbol('b'); +a[a1] = 'hi'; +a[a2] = 'oi'; + +let obj = Object.getOwnPropertySymbols(a); +obj; //  [Symbol(a), Symbol(b)] +``` + +另外可以使用`Reflect.ownKeys`方法可以返回所有类型的键名,包括常规键名和 Symbol 键名。 +```js +let a = { + [Symbol('leo')]: 1, + aa : 2, + bb : 3, +} +Reflect.ownKeys(a); // ['aa', 'bb',Symbol('leo')] +``` + +由于Symbol值作为名称的属性不被常规方法遍历获取,因此常用于定义对象的一些非私有,且内部使用的方法。 + +### 9.5 Symbol.for()、Symbol.keyFor() +* Symbol.for() +**用于重复使用一个Symbol值**,接收一个**字符串**作为参数,若存在用此参数作为名称的Symbol值,返回这个Symbol,否则新建并返回以这个参数为名称的Symbol值。 +```js +let a = Symbol.for('aaa'); +let b = Symbol.for('aaa'); + +a === b; // true +``` +`Symbol()` 和 `Symbol.for()`区别: +```js +Symbol.for('aa') === Symbol.for('aa'); // true +Symbol('aa') === Symbol('aa'); // false +``` + +* Symbol.keyFor() +**用于返回一个已使用的Symbol类型的key**: +```js +let a = Symbol.for('aa'); +Symbol.keyFor(a); // 'aa' + +let b = Symbol('aa'); +Symbol.keyFor(b); // undefined +``` + +### 9.6 内置的Symbol值 +ES6提供11个内置的Symbol值,指向语言内部使用的方法: +* **1.Symbol.hasInstance** +当其他对象使用`instanceof`运算符,判断是否为该对象的实例时,会调用这个方法。比如,`foo instanceof Foo`在语言内部,实际调用的是`Foo[Symbol.hasInstance](foo)`。 +```js +class P { + [Symbol.hasInstance](a){ + return a instanceof Array; + } +} +[1, 2, 3] instanceof new P(); // true +``` +P是一个类,new P()会返回一个实例,该实例的`Symbol.hasInstance`方法,会在进行`instanceof`运算时自动调用,判断左侧的运算子是否为`Array`的实例。 + +* **2.Symbol.isConcatSpreadable** +值为布尔值,表示该对象用于`Array.prototype.concat()`时,是否可以展开。 +```js +let a = ['aa','bb']; +['cc','dd'].concat(a, 'ee'); +// ['cc', 'dd', 'aa', 'bb', 'ee'] +a[Symbol.isConcatSpreadable]; // undefined + +let b = ['aa','bb']; +b[Symbol.isConcatSpreadable] = false; +['cc','dd'].concat(b, 'ee'); +// ['cc', 'dd',[ 'aa', 'bb'], 'ee'] +``` + +* **3.Symbol.species** +指向一个构造函数,在创建衍生对象时会使用,使用时需要用`get`取值器。 +```js +class P extends Array { + static get [Symbol.species](){ + return this; + } +} +``` +解决下面问题: +```js +// 问题: b应该是 Array 的实例,实际上是 P 的实例 +class P extends Array{} + +let a = new P(1,2,3); +let b = a.map(x => x); + +b instanceof Array; // true +b instanceof P; // true + +// 解决: 通过使用 Symbol.species +class P extends Array { + static get [Symbol.species]() { return Array; } +} +let a = new P(); +let b = a.map(x => x); +b instanceof P; // false +b instanceof Array; // true +``` + +* **4.Symbol.match** +当执行`str.match(myObject)`,传入的属性存在时会调用,并返回该方法的返回值。 +```js +class P { + [Symbol.match](string){ + return 'hello world'.indexOf(string); + } +} +'h'.match(new P()); // 0 +``` + +* **5.Symbol.replace** +当该对象被`String.prototype.replace`方法调用时,会返回该方法的返回值。 +```js +let a = {}; +a[Symbol.replace] = (...s) => console.log(s); +'Hello'.replace(a , 'World') // ["Hello", "World"] +``` + +* **6.Symbol.hasInstance** +当该对象被`String.prototype.search`方法调用时,会返回该方法的返回值。 +```js +class P { + constructor(val) { + this.val = val; + } + [Symbol.search](s){ + return s.indexOf(this.val); + } +} +'hileo'.search(new P('leo')); // 2 +``` + +* **7.Symbol.split** +当该对象被`String.prototype.split`方法调用时,会返回该方法的返回值。 +```js +// 重新定义了字符串对象的split方法的行为 +class P { + constructor(val) { + this.val = val; + } + [Symbol.split](s) { + let i = s.indexOf(this.val); + if(i == -1) return s; + return [ + s.substr(0, i), + s.substr(i + this.val.length) + ] + } +} + +'helloworld'.split(new P('hello')); // ["hello", ""] +'helloworld'.split(new P('world')); // ["", "world"] +'helloworld'.split(new P('leo')); // "helloworld" +``` + +* **8.Symbol.iterator** +对象进行`for...of`循环时,会调用`Symbol.iterator`方法,返回该对象的默认遍历器。 +```js +class P { + *[Symbol.interator]() { + let i = 0; + while(this[i] !== undefined ) { + yield this[i]; + ++i; + } + } +} +let a = new P(); +a[0] = 1; +a[1] = 2; + +for (let k of a){ + console.log(k); +} +``` + +* **9.Symbol.toPrimitive** +该对象被转为原始类型的值时,会调用这个方法,返回该对象对应的原始类型值。调用时,需要接收一个字符串参数,表示当前运算模式,运算模式有: + * Number : 此时需要转换成数值 + * String : 此时需要转换成字符串 + * Default : 此时可以转换成数值或字符串 +```js +let obj = { + [Symbol.toPrimitive](hint) { + switch (hint) { + case 'number': + return 123; + case 'string': + return 'str'; + case 'default': + return 'default'; + default: + throw new Error(); + } + } +}; + +2 * obj // 246 +3 + obj // '3default' +obj == 'default' // true +String(obj) // 'str' +``` + +* **10.Symbol.toStringTag** +在该对象上面调用`Object.prototype.toString`方法时,如果这个属性存在,它的返回值会出现在`toString`方法返回的字符串之中,表示对象的类型。也就是说,这个属性可以用来定制`[object Object`]或`[object Array]`中`object`后面的那个字符串。 +```js +// 例一 +({[Symbol.toStringTag]: 'Foo'}.toString()) +// "[object Foo]" + +// 例二 +class Collection { + get [Symbol.toStringTag]() { + return 'xxx'; + } +} +let x = new Collection(); +Object.prototype.toString.call(x) // "[object xxx]" +``` + +* **11.Symbol.unscopables** +该对象指定了使用with关键字时,哪些属性会被with环境排除。 +```js +// 没有 unscopables 时 +class MyClass { + foo() { return 1; } +} + +var foo = function () { return 2; }; + +with (MyClass.prototype) { + foo(); // 1 +} + +// 有 unscopables 时 +class MyClass { + foo() { return 1; } + get [Symbol.unscopables]() { + return { foo: true }; + } +} + +var foo = function () { return 2; }; + +with (MyClass.prototype) { + foo(); // 2 +} +``` +上面代码通过指定`Symbol.unscopables`属性,使得`with`语法块不会在当前作用域寻找`foo`属性,即`foo`将指向外层作用域的变量。 + + +[⬆ 返回目录](#二目录) + + +## 10 Set和Map数据结构 +### 10.1 Set +**介绍**: +`Set`数据结构类似数组,但所有成员的值**唯一**。 +`Set`本身为一个构造函数,用来生成`Set`数据结构,使用`add`方法来添加新成员。 +```js +let a = new Set(); +[1,2,2,1,3,4,5,4,5].forEach(x=>a.add(x)); +for(let k of a){ + console.log(k) +}; +// 1 2 3 4 5 +``` +**基础使用**: +```js +let a = new Set([1,2,3,3,4]); +[...a]; // [1,2,3,4] +a.size; // 4 + +// 数组去重 +[...new Set([1,2,3,4,4,4])];// [1,2,3,4] +``` + +**注意**: +* 向`Set`中添加值的时候,不会类型转换,即`5`和`'5'`是不同的。 +```js +[...new Set([5,'5'])]; // [5, "5"] +``` + +**属性和方法**: +* 属性: + * `Set.prototype.constructor`:构造函数,默认就是`Set`函数。 + * `Set.prototype.size`:返回`Set`实例的成员总数。 + +* 操作方法: + * `add(value)`:添加某个值,返回 Set 结构本身。 + * `delete(value)`:删除某个值,返回一个布尔值,表示删除是否成功。 + * `has(value)`:返回一个布尔值,表示该值是否为Set的成员。 + * `clear()`:清除所有成员,没有返回值。 + +```js +let a = new Set(); +a.add(1).add(2); // a => Set(2) {1, 2} +a.has(2); // true +a.has(3); // false +a.delete(2); // true a => Set(1) {1} +a.clear(); // a => Set(0) {} +``` +**数组去重**: +```js +let a = new Set([1,2,3,3,3,3]); +``` +### 10.2 Set的应用 +**数组去重**: +```js +// 方法1 +[...new Set([1,2,3,4,4,4])]; // [1,2,3,4] +// 方法2 +Array.from(new Set([1,2,3,4,4,4])); // [1,2,3,4] +``` +**遍历和过滤**: +```js +let a = new Set([1,2,3,4]); + +// map 遍历操作 +let b = new Set([...a].map(x =>x*2));// b => Set(4) {2,4,6,8} + +// filter 过滤操作 +let c = new Set([...a].filter(x =>(x%2) == 0)); // b => Set(2) {2,4} +``` +**获取并集、交集和差集**: +```js +let a = new Set([1,2,3]); +let b = new Set([4,3,2]); + +// 并集 +let c1 = new Set([...a, ...b]); // Set {1,2,3,4} + +// 交集 +let c2 = new Set([...a].filter(x => b.has(x))); // set {2,3} + +// 差集 +let c3 = new Set([...a].filter(x => !b.has(x))); // set {1} +``` + +* 遍历方法: + * `keys()`:返回**键名**的遍历器。 + * `values()`:返回**键值**的遍历器。 + * `entries()`:返回**键值对**的遍历器。 + * `forEach()`:使用回调函数遍历**每个成员**。 + +`Set`遍历顺序是**插入顺序**,当保存多个回调函数,只需按照顺序调用。但由于`Set`结构**没有键名只有键值**,所以`keys()`和`values()`是返回结果相同。 +```js +let a = new Set(['a','b','c']); +for(let i of a.keys()){console.log(i)}; // 'a' 'b' 'c' +for(let i of a.values()){console.log(i)}; // 'a' 'b' 'c' +for(let i of a.entries()){console.log(i)}; +// ['a','a'] ['b','b'] ['c','c'] +``` +并且 还可以使用`for...of`直接遍历`Set`。 +```js +let a = new Set(['a','b','c']); +for(let k of a){console.log(k)}; // 'a' 'b' 'c' +``` +`forEach`与数组相同,对每个成员执行操作,且无返回值。 +```js +let a = new Set(['a','b','c']); +a.forEach((v,k) => console.log(k + ' : ' + v)); +``` + + +### 10.3 Map +由于传统的`JavaScript`对象只能用字符串当做键,给开发带来很大限制,ES6增加`Map`数据结构,使得**各种类型的值**(包括对象)都可以作为键。 +`Map`结构提供了“**值—值**”的对应,是一种更完善的 Hash 结构实现。 +**基础使用**: +```js +let a = new Map(); +let b = {name: 'leo' }; +a.set(b,'my name'); // 添加值 +a.get(b); // 获取值 +a.size; // 获取总数 +a.has(b); // 查询是否存在 +a.delete(b); // 删除一个值 +a.clear(); // 清空所有成员 无返回 +``` +**注意**: +* 传入数组作为参数,**指定键值对的数组**。 +```js +let a = new Map([ + ['name','leo'], + ['age',18] +]) +``` +* 如果对同一个键**多次赋值**,后面的值将**覆盖前面的值**。 +```js +let a = new Map(); +a.set(1,'aaa').set(1,'bbb'); +a.get(1); // 'bbb' +``` +* 如果读取一个未知的键,则返回`undefined`。 +```js +new Map().get('abcdef'); // undefined +``` +* **同样的值**的两个实例,在 Map 结构中被视为两个键。 +```js +let a = new Map(); +let a1 = ['aaa']; +let a2 = ['aaa']; +a.set(a1,111).set(a2,222); +a.get(a1); // 111 +a.get(a2); // 222 +``` +**遍历方法**: +Map 的遍历顺序就是插入顺序。 +* `keys()`:返回键名的遍历器。 +* `values()`:返回键值的遍历器。 +* `entries()`:返回所有成员的遍历器。 +* `forEach()`:遍历 Map 的所有成员。 +```js +let a = new Map([ + ['name','leo'], + ['age',18] +]) + +for (let i of a.keys()){...}; +for (let i of a.values()){...}; +for (let i of a.entries()){...}; +a.forEach((v,k,m)=>{ + console.log(`key:${k},value:${v},map:${m}`) +}) +``` +**将Map结构转成数组结构**: +```js +let a = new Map([ + ['name','leo'], + ['age',18] +]) + +let a1 = [...a.keys()]; // a1 => ["name", "age"] +let a2 = [...a.values()]; // a2 =>  ["leo", 18] +let a3 = [...a.entries()];// a3 => [['name','leo'], ['age',18]] +``` + +### 10.4 Map与其他数据结构互相转换 +* Map 转 数组 +```js +let a = new Map().set(true,1).set({f:2},['abc']); +[...a]; // [[true:1], [ {f:2},['abc'] ]] +``` +* 数组 转 Map +```js +let a = [ ['name','leo'], [1, 'hi' ]] +let b = new Map(a); +``` +* Map 转 对象 +如果所有 Map 的键都是字符串,它可以无损地转为对象。 +如果有非字符串的键名,那么这个键名会被转成字符串,再作为对象的键名。 +```js +function fun(s) { + let obj = Object.create(null); + for (let [k,v] of s) { + obj[k] = v; + } + return obj; +} + +const a = new Map().set('yes', true).set('no', false); +fun(a) +// { yes: true, no: false } +``` +* 对象 转 Map +```js +function fun(obj) { + let a = new Map(); + for (let k of Object.keys(obj)) { + a.set(k, obj[k]); + } + return a; +} + +fun({yes: true, no: false}) +// Map {"yes" => true, "no" => false} +``` + +* Map 转 JSON +**(1)Map键名都是字符串,转为对象JSON:** +```js +function fun (s) { + let obj = Object.create(null); + for (let [k,v] of s) { + obj[k] = v; + } + return JSON.stringify(obj) +} +let a = new Map().set('yes', true).set('no', false); +fun(a); +// '{"yes":true,"no":false}' +``` +**(2)Map键名有非字符串,转为数组JSON:** +```js +function fun (map) { + return JSON.stringify([...map]); +} + +let a = new Map().set(true, 7).set({foo: 3}, ['abc']); +fun(a) +// '[[true,7],[{"foo":3},["abc"]]]' +``` +* JSON 转 Map +**(1)所有键名都是字符串:** +```js +function fun (s) { + let strMap = new Map(); + for (let k of Object.keys(s)) { + strMap.set(k, s[k]); + } + return strMap; + return JSON.parse(strMap); +} +fun('{"yes": true, "no": false}') +// Map {'yes' => true, 'no' => false} +``` +**(2)整个 JSON 就是一个数组,且每个数组成员本身,又是一个有两个成员的数组**: +```js +function fun2(s) { + return new Map(JSON.parse(s)); +} +fun2('[[true,7],[{"foo":3},["abc"]]]') +// Map {true => 7, Object {foo: 3} => ['abc']} +``` + +[⬆ 返回目录](#二目录) + + +## 11 Proxy +`proxy` 用于修改某些操作的**默认行为**,可以理解为一种拦截外界对目标对象访问的一种机制,从而对外界的访问进行过滤和修改,即代理某些操作,也称“**代理器**”。 +### 11.1 基础使用 +`proxy`实例化需要传入两个参数,`target`参数表示所要拦截的目标对象,`handler`参数也是一个对象,用来定制拦截行为。 +```js +let p = new Proxy(target, handler); + +let a = new Proxy({}, { + get: function (target, handler){ + return 'leo'; + } +}) +a.name; // leo +a.age; // leo +a.abcd; // leo +``` +上述`a`实例中,在第二个参数中定义了`get`方法,来拦截外界访问,并且`get`方法接收两个参数,分别是**目标对象**和**所要访问的属性**,所以不管外部访问对象中任何属性都会执行`get`方法返回`leo`。 +**注意**: +* 只能使用`Proxy`实例的对象才能使用这些操作。 +* 如果`handler`没有设置拦截,则直接返回原对象。 +```js +let target = {}; +let handler = {}; +let p = new Proxy(target, handler); +p.a = 'leo'; +target.a; // 'leo' +``` +**同个拦截器函数,设置多个拦截操作**: +```js +let p = new Proxy(function(a, b){ + return a + b; +},{ + get:function(){ + return 'get方法'; + }, + apply:function(){ + return 'apply方法'; + } +}) +``` +这里还有一个简单的案例: +```js +let handler = { + get : function (target, name){ + return name in target ? target[name] : 16; + } +} + +let p = new Proxy ({}, handler); +p.a = 1; +console.log(p.a , p.b); +// 1 16 +``` +这里因为 `p.a = 1` 定义了`p`中的`a`属性,值为`1`,而没有定义`b`属性,所以`p.a`会得到`1`,而`p.b`会得到`undefined`从而使用`name in target ? target[name] : 16;`返回的默认值`16`; + +**`Proxy`支持的13种拦截操作**: +13种拦截操作的详细介绍:[打开阮一峰老师的链接](http://es6.ruanyifeng.com/#docs/proxy)。 +* `get(target, propKey, receiver)`: +拦截对象属性的读取,比如proxy.foo和proxy['foo']。 + +* `set(target, propKey, value, receiver)`: +拦截对象属性的设置,比如proxy.foo = v或proxy['foo'] = v,返回一个布尔值。 + +* `has(target, propKey)`: +拦截propKey in proxy的操作,返回一个布尔值。 + +* `deleteProperty(target, propKey)`: +拦截delete proxy[propKey]的操作,返回一个布尔值。 + +* `ownKeys(target)`: +拦截Object.getOwnPropertyNames(proxy)、Object.getOwnPropertySymbols(proxy)、Object.keys(proxy)、for...in循环,返回一个数组。该方法返回目标对象所有自身的属性的属性名,而Object.keys()的返回结果仅包括目标对象自身的可遍历属性。 + +* `getOwnPropertyDescriptor(target, propKey)`: +拦截Object.getOwnPropertyDescriptor(proxy, propKey),返回属性的描述对象。 + +* `defineProperty(target, propKey, propDesc)`: +拦截Object.defineProperty(proxy, propKey, propDesc)、Object.defineProperties(proxy, propDescs),返回一个布尔值。 + +* `preventExtensions(target)`: +拦截Object.preventExtensions(proxy),返回一个布尔值。 + +* `getPrototypeOf(target)`: +拦截Object.getPrototypeOf(proxy),返回一个对象。 + +* `isExtensible(target)`: +拦截Object.isExtensible(proxy),返回一个布尔值。 + +* `setPrototypeOf(target, proto)`: +拦截Object.setPrototypeOf(proxy, proto),返回一个布尔值。如果目标对象是函数,那么还有两种额外操作可以拦截。 + +* `apply(target, object, args)`: +拦截 Proxy 实例作为函数调用的操作,比如proxy(...args)、proxy.call(object, ...args)、proxy.apply(...)。 + +* `construct(target, args)`: +拦截 Proxy 实例作为构造函数调用的操作,比如new proxy(...args)。 + +### 11.2 取消Proxy实例 +使用`Proxy.revocale`方法取消`Proxy`实例。 +```js +let a = {}; +let b = {}; +let {proxy, revoke} = Proxy.revocale(a, b); + +proxy.name = 'leo'; // 'leo' +revoke(); +proxy.name; // TypeError: Revoked +``` + +### 11.3 实现 Web服务的客户端 +```js +const service = createWebService('http://le.com/data'); +service.employees().than(json =>{ + const employees = JSON.parse(json); +}) + +function createWebService(url){ + return new Proxy({}, { + get(target, propKey, receiver{ + return () => httpGet(url+'/'+propKey); + }) + }) +} +``` + +## 12 Promise对象 +### 12.1 概念 +主要用途:**解决异步编程带来的回调地狱问题**。 +把`Promise`简单理解一个容器,存放着某个未来才会结束的事件(通常是一个异步操作)的结果。通过`Promise`对象来获取异步操作消息,处理各种异步操作。 + +**`Promise`对象2特点**: +* **对象的状态不受外界影响**。 +> `Promise`对象代表一个异步操作,有三种状态:**pending(进行中)**、**fulfilled(已成功)**和**rejected(已失败)**。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。这也是`Promise`这个名字的由来,它的英语意思就是“承诺”,表示其他手段无法改变。 + +* **一旦状态改变,就不会再变,任何时候都可以得到这个结果**。 +> Promise对象的状态改变,只有两种可能:从**pending**变为**fulfilled**和从**pending**变为**rejected**。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果,这时就称为 **resolved**(已定型)。如果改变已经发生了,你再对**Promise**对象添加回调函数,也会立即得到这个结果。这与事件(Event)完全不同,事件的特点是,如果你错过了它,再去监听,是得不到结果的。 + +注意,为了行文方便,本章后面的`resolve`d统一只指`fulfilled`状态,不包含`rejected`状态。 + +**`Promise`缺点** +* **无法取消**Promise,一旦新建它就会立即执行,无法中途取消。 +* 如果不设置回调函数,Promise内部抛出的错误,不会反应到外部。 +* 当处于pending状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。 + +### 12.2 基本使用 +`Promise`为一个构造函数,需要用`new`来实例化。 +```js +let p = new Promise(function (resolve, reject){ + if(/*异步操作成功*/){ + resolve(value); + } else { + reject(error); + } +}) +``` +`Promise`接收一个函数作为参数,该函数两个参数`resolve`和`reject`,有JS引擎提供。 +* `resolve`作用是将`Promise`的状态从pending变成resolved,在异步操作成功时调用,返回异步操作的结果,作为参数传递出去。 +* `reject`作用是将`Promise`的状态从pending变成rejected,在异步操作失败时报错,作为参数传递出去。 + +`Promise`实例生成以后,可以用`then`方法分别指定`resolved`状态和`rejected`状态的回调函数。 +```js +p.then(function(val){ + // success... +},function(err){ + // error... +}) +``` + +**几个例子来理解** : +* 当一段时间过后,`Promise`状态便成为`resolved`触发`then`方法绑定的回调函数。 +```js +function timeout (s){ + return new Promise((resolve, reject){ + setTimeout(result,ms, 'done'); + }) +} +timeout(100).then(val => { + console.log(val); +}) +``` + +* `Promise`新建后立刻执行。 +```js +let p = new Promise(function(resolve, reject){ + console.log(1); + resolve(); +}) +p.then(()=>{ + console.log(2); +}) +console.log(3); +// 1 +// 3 +// 2 +``` + +**异步加载图片**: +```js +function f(url){ + return new Promise(function(resolve, reject){ + const img = new Image (); + img.onload = function(){ + resolve(img); + } + img.onerror = function(){ + reject(new Error( + 'Could not load image at ' + url + )); + } + img.src = url; + }) +} +``` + +**`resolve`函数和`reject`函数的参数为`resolve`函数或`reject`函数**: +`p1`的状态决定了`p2`的状态,所以`p2`要等待`p1`的结果再执行回调函数。 +```js +const p1 = new Promise(function (resolve, reject) { + setTimeout(() => reject(new Error('fail')), 3000) +}) + +const p2 = new Promise(function (resolve, reject) { + setTimeout(() => resolve(p1), 1000) +}) + +p2 + .then(result => console.log(result)) + .catch(error => console.log(error)) +// Error: fail +``` + +**调用`resolve`或`reject`不会结束`Promise`参数函数的执行,除了`return`**: +```js +new Promise((resolve, reject){ + resolve(1); + console.log(2); +}).then(r => { + console.log(3); +}) +// 2 +// 1 + +new Promise((resolve, reject){ + return resolve(1); + console.log(2); +}) +// 1 +``` + +### 12.3 Promise.prototype.then() +作用是为`Promise`添加状态改变时的回调函数,`then`方法的第一个参数是`resolved`状态的回调函数,第二个参数(可选)是`rejected`状态的回调函数。 +`then`方法返回一个新`Promise`实例,与原来`Promise`实例不同,因此可以使用链式写法,上一个`then`的结果作为下一个`then`的参数。 +```js +getJSON("/posts.json").then(function(json) { + return json.post; +}).then(function(post) { + // ... +}); +``` + +### 12.4 Promise.prototype.catch() +`Promise.prototype.catch`方法是`.then(null, rejection)`的别名,用于指定发生错误时的回调函数。 +```js +getJSON('/posts.json').then(function(posts) { + // ... +}).catch(function(error) { + // 处理 getJSON 和 前一个回调函数运行时发生的错误 + console.log('发生错误!', error); +}); +``` +如果 `Promise` 状态已经变成`resolved`,再抛出错误是无效的。 +```js +const p = new Promise(function(resolve, reject) { + resolve('ok'); + throw new Error('test'); +}); +p + .then(function(value) { console.log(value) }) + .catch(function(error) { console.log(error) }); +// ok +``` +当`promise`抛出一个错误,就被`catch`方法指定的回调函数捕获,下面三种写法相同。 +```js +// 写法一 +const p = new Promise(function(resolve, reject) { + throw new Error('test'); +}); +p.catch(function(error) { + console.log(error); +}); +// Error: test + +// 写法二 +const p = new Promise(function(resolve, reject) { + try { + throw new Error('test'); + } catch(e) { + reject(e); + } +}); +p.catch(function(error) { + console.log(error); +}); + +// 写法三 +const p = new Promise(function(resolve, reject) { + reject(new Error('test')); +}); +p.catch(function(error) { + console.log(error); +}); +``` +一般来说,不要在`then`方法里面定义` Reject` 状态的回调函数(即`then`的第二个参数),总是使用`catch`方法。 +```js +// bad +promise + .then(function(data) { + // success + }, function(err) { + // error + }); + +// good +promise + .then(function(data) { //cb + // success + }) + .catch(function(err) { + // error + }); +``` + +### 12.5 Promise.prototype.finally() +`finally`方法用于指定不管 `Promise` 对象最后状态如何,都会执行的操作。该方法是 ES2018 引入标准的。 +```js +promise +.then(result => {···}) +.catch(error => {···}) +.finally(() => {···}); +``` +`finally`不接收任何参数,与状态无关,本质上是`then`方法的特例。 +```js +promise +.finally(() => { + // 语句 +}); + +// 等同于 +promise +.then( + result => { + // 语句 + return result; + }, + error => { + // 语句 + throw error; + } +); +``` +上面代码中,如果不使用`finally`方法,同样的语句需要为成功和失败两种情况各写一次。有了`finally`方法,则只需要写一次。 +`finally`方法总是会返回原来的值。 +```js +// resolve 的值是 undefined +Promise.resolve(2).then(() => {}, () => {}) + +// resolve 的值是 2 +Promise.resolve(2).finally(() => {}) + +// reject 的值是 undefined +Promise.reject(3).then(() => {}, () => {}) + +// reject 的值是 3 +Promise.reject(3).finally(() => {}) +``` + +### 12.6 Promise.all() +用于将多个 `Promise` 实例,包装成一个新的 `Promise` 实例,参数可以不是数组,但必须是Iterator接口,且返回的每个成员都是`Promise`实例。 +```js +const p = Promise.all([p1, p2, p3]); +``` +`p`的状态由`p1`、`p2`、`p3`决定,分成**两种**情况。 +1. 只有p1、p2、p3的状态都变成fulfilled,p的状态才会变成fulfilled,此时p1、p2、p3的返回值组成一个数组,传递给p的回调函数。 +2. 只要p1、p2、p3之中有一个被rejected,p的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数。 + +```js +// 生成一个Promise对象的数组 +const promises = [2, 3, 5, 7, 11, 13].map(function (id) { + return getJSON('/post/' + id + ".json"); +}); + +Promise.all(promises).then(function (posts) { + // ... +}).catch(function(reason){ + // ... +}); +``` +上面代码中,`promises`是包含 6 个 Promise 实例的数组,只有这 6 个实例的状态都变成`fulfilled`,或者其中有一个变为`rejected`,才会调用`Promise.all`方法后面的回调函数。 + +**注意**:如果`Promise`的参数中定义了`catch`方法,则`rejected`后不会触发`Promise.all()`的`catch`方法,因为参数中的`catch`方法执行完后也会变成`resolved`,当`Promise.all()`方法参数的实例都是`resolved`时就会调用`Promise.all()`的`then`方法。 +```js +const p1 = new Promise((resolve, reject) => { + resolve('hello'); +}) +.then(result => result) +.catch(e => e); + +const p2 = new Promise((resolve, reject) => { + throw new Error('报错了'); +}) +.then(result => result) +.catch(e => e); + +Promise.all([p1, p2]) +.then(result => console.log(result)) +.catch(e => console.log(e)); +// ["hello", Error: 报错了] +``` + +**如果参数里面都没有catch方法,就会调用Promise.all()的catch方法。** +```js +const p1 = new Promise((resolve, reject) => { + resolve('hello'); +}) +.then(result => result); + +const p2 = new Promise((resolve, reject) => { + throw new Error('报错了'); +}) +.then(result => result); + +Promise.all([p1, p2]) +.then(result => console.log(result)) +.catch(e => console.log(e)); +// Error: 报错了 +``` + +### 12.7 Promise.race() +与`Promise.all`方法类似,也是将多个`Promise`实例包装成一个新的`Promise`实例。 +```js +const p = Promise.race([p1, p2, p3]); +``` +与`Promise.all`方法区别在于,`Promise.race`方法是`p1`, `p2`, `p3`中只要一个参数先改变状态,就会把这个参数的返回值传给`p`的回调函数。 + +### 12.8 Promise.resolve() +将现有对象转换成 `Promise` 对象。 +```js +const p = Promise.resolve($.ajax('/whatever.json')); +``` + +### 12.9 Promise.reject() +返回一个`rejected`状态的`Promise`实例。 +```js +const p = Promise.reject('出错了'); +// 等同于 +const p = new Promise((resolve, reject) => reject('出错了')) + +p.then(null, function (s) { + console.log(s) +}); +// 出错了 +``` +注意,`Promise.reject()`方法的参数,会原封不动地作为`reject`的理由,变成后续方法的参数。这一点与`Promise.resolve`方法不一致。 +```js +const thenable = { + then(resolve, reject) { + reject('出错了'); + } +}; + +Promise.reject(thenable) +.catch(e => { + console.log(e === thenable) +}) +// true +``` + + +[⬆ 返回目录](#二目录) + +## 13 Iterator和 for...of循环 +### 13.1 Iterator遍历器概念 +> **Iterator**是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署 **Iterator** 接口,就可以完成遍历操作(即依次处理该数据结构的所有成员)。 + +**Iterator三个作用**: +* 为各种数据结构,提供一个**统一**的、**简便**的访问接口; +* 使得数据结构的成员能够按某种次序排列; +* **Iterator** 接口主要供ES6新增的`for...of`消费; + +### 13.2 Iterator遍历过程 +1. 创建一个指针对象,指向当前数据结构的起始位置。也就是说,遍历器对象本质上,就是一个指针对象。 +2. 第一次调用指针对象的`next`方法,可以将指针指向数据结构的第一个成员。 +3. 第二次调用指针对象的`next`方法,指针就指向数据结构的第二个成员。 +4. 不断调用指针对象的`next`方法,直到它指向数据结构的结束位置。 + +每一次调用`next`方法,都会返回数据结构的当前成员的信息。具体来说,就是返回一个包含`value`和`done`两个属性的对象。 + +* `value`属性是当前成员的值; +* `done`属性是一个布尔值,表示遍历是否结束; + +模拟`next`方法返回值: +```js +let f = function (arr){ + var nextIndex = 0; + return { + next:function(){ + return nextIndex < arr.length ? + {value: arr[nextIndex++], done: false}: + {value: undefined, done: true} + } + } +} + +let a = f(['a', 'b']); +a.next(); // { value: "a", done: false } +a.next(); // { value: "b", done: false } +a.next(); // { value: undefined, done: true } +``` + +### 13.3 默认Iterator接口 +若数据**可遍历**,即一种数据部署了Iterator接口。 +ES6中默认的Iterator接口部署在数据结构的`Symbol.iterator`属性,即如果一个数据结构具有`Symbol.iterator`属性,就可以认为是**可遍历**。 +`Symbol.iterator`属性本身是函数,是当前数据结构默认的遍历器生成函数。执行这个函数,就会返回一个遍历器。至于属性名`Symbol.iterator`,它是一个表达式,返回`Symbol`对象的`iterator`属性,这是一个预定义好的、类型为 Symbol 的特殊值,所以要放在方括号内(参见《Symbol》一章)。 + +**原生具有Iterator接口的数据结构有**: +* Array +* Map +* Set +* String +* TypedArray +* 函数的 arguments 对象 +* NodeList 对象 + +### 13.4 Iterator使用场景 +* **(1)解构赋值** +对数组和 `Set` 结构进行解构赋值时,会默认调用`Symbol.iterator`方法。 +```js +let a = new Set().add('a').add('b').add('c'); +let [x, y] = a; // x = 'a' y = 'b' +let [a1, ...a2] = a; // a1 = 'a' a2 = ['b','c'] +``` + +* **(2)扩展运算符** +扩展运算符(`...`)也会调用默认的 Iterator 接口。 +```js +let a = 'hello'; +[...a]; // ['h','e','l','l','o'] + +let a = ['b', 'c']; +['a', ...a, 'd']; // ['a', 'b', 'c', 'd'] +``` + +* **(2)yield*** +`yield*`后面跟的是一个可遍历的结构,它会调用该结构的遍历器接口。 +```js +let a = function*(){ + yield 1; + yield* [2,3,4]; + yield 5; +} + +let b = a(); +b.next() // { value: 1, done: false } +b.next() // { value: 2, done: false } +b.next() // { value: 3, done: false } +b.next() // { value: 4, done: false } +b.next() // { value: 5, done: false } +b.next() // { value: undefined, done: true } +``` + +* **(4)其他场合** +由于数组的遍历会调用遍历器接口,所以任何接受数组作为参数的场合,其实都调用了遍历器接口。下面是一些例子。 + +* for...of +* Array.from() +* Map(), Set(), WeakMap(), WeakSet()(比如`new Map([['a',1],['b',2]])`) +* Promise.all() +* Promise.race() + +### 13.5 for...of循环 +只要数据结构部署了`Symbol.iterator`属性,即具有 iterator 接口,可以用`for...of`循环遍历它的成员。也就是说,`for...of`循环内部调用的是数据结构的`Symbol.iterato`方法。 +**使用场景**: +`for...of`可以使用在**数组**,**`Set`和`Map`结构**,**类数组对象**,**Genetator对象**和**字符串**。 + +* **数组** +`for...of`循环可以代替数组实例的`forEach`方法。 +```js +let a = ['a', 'b', 'c']; +for (let k of a){console.log(k)}; // a b c + +a.forEach((ele, index)=>{ + console.log(ele); // a b c + console.log(index); // 0 1 2 +}) +``` +与`for...in`对比,`for...in`只能获取对象键名,不能直接获取键值,而`for...of`允许直接获取键值。 +```js +let a = ['a', 'b', 'c']; +for (let k of a){console.log(k)}; // a b c +for (let k in a){console.log(k)}; // 0 1 2 +``` + +* **Set和Map** +可以使用数组作为变量,如`for (let [k,v] of b){...}`。 +```js +let a = new Set(['a', 'b', 'c']); +for (let k of a){console.log(k)}; // a b c + +let b = new Map(); +b.set('name','leo'); +b.set('age', 18); +b.set('aaa','bbb'); +for (let [k,v] of b){console.log(k + ":" + v)}; +// name:leo +// age:18 +// aaa:bbb +``` + +* **类数组对象** +```js +// 字符串 +let a = 'hello'; +for (let k of a ){console.log(k)}; // h e l l o + +// DOM NodeList对象 +let b = document.querySelectorAll('p'); +for (let k of b ){ + k.classList.add('test'); +} + +// arguments对象 +function f(){ + for (let k of arguments){ + console.log(k); + } +} +f('a','b'); // a b +``` + +* **对象** +普通对象不能直接使用`for...of`会报错,要部署Iterator才能使用。 +```js +let a = {a:'aa',b:'bb',c:'cc'}; +for (let k in a){console.log(k)}; // a b c +for (let k of a){console>log(k)}; // TypeError +``` + +### 13.6 跳出for...of +使用`break`来实现。 +```js +for (let k of a){ + if(k>100) + break; + console.log(k); +} +``` + +[⬆ 返回目录](#二目录) + + + +## 14 Generator函数和应用 +### 14.1 基本概念 +`Generator`函数是一种异步编程解决方案。 +**原理**: +执行`Genenrator`函数会返回一个遍历器对象,依次遍历`Generator`函数内部的每一个状态。 +`Generator`函数是一个普通函数,有以下两个特征: +* `function`关键字与函数名之间有个星号; +* 函数体内使用`yield`表达式,定义不同状态; + +通过调用`next`方法,将指针移向下一个状态,直到遇到下一个`yield`表达式(或`return`语句)为止。简单理解,`Generator`函数分段执行,`yield`表达式是暂停执行的标记,而`next`恢复执行。 +```js +function * f (){ + yield 'hi'; + yield 'leo'; + return 'ending'; +} +let a = f(); +a.next(); // {value: 'hi', done : false} +a.next(); // {value: 'leo', done : false} +a.next(); // {value: 'ending', done : true} +a.next(); // {value: undefined, done : false} +``` + +### 14.2 yield表达式 +`yield`表达式是暂停标志,遍历器对象的`next`方法的运行逻辑如下: +1. 遇到`yield`就暂停执行,将这个`yield`后的表达式的值,作为返回对象的`value`属性值。 +2. 下次调用`next`往下执行,直到遇到下一个`yield`。 +3. 直到函数结束或者`return`为止,并返回`return`语句后面表达式的值,作为返回对象的`value`属性值。 +4. 如果该函数没有`return`语句,则返回对象的`value`为`undefined` 。 + +**注意:** +* `yield`只能用在`Generator`函数里使用,其他地方使用会报错。 +```js +// 错误1 +(function(){ + yiled 1; // SyntaxError: Unexpected number +})() + +// 错误2 forEach参数是个普通函数 +let a = [1, [[2, 3], 4], [5, 6]]; +let f = function * (i){ + i.forEach(function(m){ + if(typeof m !== 'number'){ + yield * f (m); + }else{ + yield m; + } + }) +} +for (let k of f(a)){ + console.log(k) +} +``` + +* `yield`表达式如果用于另一个表达式之中,必须放在**圆括号**内。 +```js +function * a (){ + console.log('a' + yield); // SyntaxErro + console.log('a' + yield 123); // SyntaxErro + console.log('a' + (yield)); // ok + console.log('a' + (yield 123)); // ok +} +``` + +* `yield`表达式用做函数参数或放在表达式右边,可以**不加括号**。 +```js +function * a (){ + f(yield 'a', yield 'b'); // ok + lei i = yield; // ok +} +``` + +### 14.3 next方法 +`yield`本身没有返回值,或者是总返回`undefined`,`next`方法可带一个参数,作为上一个`yield`表达式的返回值。 +```js +function * f (){ + for (let k = 0; true; k++){ + let a = yield k; + if(a){k = -1}; + } +} +let g =f(); +g.next(); // {value: 0, done: false} +g.next(); // {value: 1, done: false} +g.next(true); // {value: 0, done: false} +``` +这一特点,可以让`Generator`函数开始执行之后,可以从外部向内部注入不同值,从而调整函数行为。 +```js +function * f(x){ + let y = 2 * (yield (x+1)); + let z = yield (y/3); + return (x + y + z); +} +let a = f(5); +a.next(); // {value : 6 ,done : false} +a.next(); // {value : NaN ,done : false} +a.next(); // {value : NaN ,done : true} +// NaN因为yeild返回的是对象 和数字计算会NaN + +let b = f(5); +b.next(); // {value : 6 ,done : false} +b.next(12); // {value : 8 ,done : false} +b.next(13); // {value : 42 ,done : false} +// x 5 y 24 z 13 +``` + +### 14.4 for...of循环 +`for...of`循环会自动遍历,不用调用`next`方法,需要注意的是,`for...of`遇到`next`返回值的`done`属性为`true`就会终止,`return`返回的不包括在`for...of`循环中。 +```js +function * f(){ + yield 1; + yield 2; + yield 3; + yield 4; + return 5; +} +for (let k of f()){ + console.log(k); +} +// 1 2 3 4 没有 5 +``` + +### 14.5 Generator.prototype.throw() +`throw`方法用来向函数外抛出错误,并且在Generator函数体内捕获。 +```js +let f = function * (){ + try { yield } + catch (e) { console.log('内部捕获', e) } +} + +let a = f(); +a.next(); + +try{ + a.throw('a'); + a.throw('b'); +}catch(e){ + console.log('外部捕获',e); +} +// 内部捕获 a +// 外部捕获 b +``` + +### 14.6 Generator.prototype.return() +`return`方法用来返回给定的值,并结束遍历Generator函数,如果`return`方法没有参数,则返回值的`value`属性为`undefined`。 +```js +function * f(){ + yield 1; + yield 2; + yield 3; +} +let g = f(); +g.next(); // {value : 1, done : false} +g.return('leo'); // {value : 'leo', done " true} +g.next(); // {value : undefined, done : true} +``` + +### 14.7 next()/throw()/return()共同点 +相同点就是都是用来恢复Generator函数的执行,并且使用不同语句替换`yield`表达式。 +* `next()`将`yield`表达式替换成一个值。 +```js +let f = function * (x,y){ + let r = yield x + y; + return r; +} +let g = f(1, 2); +g.next(); // {value : 3, done : false} +g.next(1); // {value : 1, done : true} +// 相当于把 let r = yield x + y; +// 替换成 let r = 1; +``` +* `throw()`将`yield`表达式替换成一个`throw`语句。 +```js +g.throw(new Error('报错')); // Uncaught Error:报错 +// 相当于将 let r = yield x + y +// 替换成 let r = throw(new Error('报错')); +``` +* `next()`将`yield`表达式替换成一个`return`语句。 +```js +g.return(2); // {value: 2, done: true} +// 相当于将 let r = yield x + y +// 替换成 let r = return 2; +``` + +### 14.8 yield* 表达式 +用于在一个Generator中执行另一个Generator函数,如果没有使用`yield*`会没有效果。 +```js +function * a(){ + yield 1; + yield 2; +} +function * b(){ + yield 3; + yield * a(); + yield 4; +} +// 等同于 +function * b(){ + yield 3; + yield 1; + yield 2; + yield 4; +} +for(let k of b()){console.log(k)} +// 3 +// 1 +// 2 +// 4 +``` + +### 14.9 应用场景 +1. **控制流管理** +解决回调地狱: +```js +// 使用前 +f1(function(v1){ + f2(function(v2){ + f3(function(v3){ + // ... more and more + }) + }) +}) + +// 使用Promise +Promise.resolve(f1) + .then(f2) + .then(f3) + .then(function(v4){ + // ... + },function (err){ + // ... + }).done(); + +// 使用Generator +function * f (v1){ + try{ + let v2 = yield f1(v1); + let v3 = yield f1(v2); + let v4 = yield f1(v3); + // ... + }catch(err){ + // console.log(err) + } +} +function g (task){ + let obj = task.next(task.value); + // 如果Generator函数未结束,就继续调用 + if(!obj.done){ + task.value = obj.value; + g(task); + } +} +g( f(initValue) ); +``` + +2. **异步编程的使用** +在真实的异步任务封装的情况: +```js +let fetch = require('node-fetch'); +function * f(){ + let url = 'http://www.baidu.com'; + let res = yield fetch(url); + console.log(res.bio); +} +// 执行该函数 +let g = f(); +let result = g.next(); +// 由于fetch返回的是Promise对象,所以用then +result.value.then(function(data){ + return data.json(); +}).then(function(data){ + g.next(data); +}) +``` + +[⬆ 返回目录](#二目录) + + +## 15 Class语法和继承 +### 15.1 介绍 +ES6中的`class`可以看作只是一个语法糖,绝大部分功能都可以用ES5实现,并且,**类和模块的内部,默认就是严格模式,所以不需要使用use strict指定运行模式**。 +```js +// ES5 +function P (x,y){ + this.x = x; + this.y = y; +} +P.prototype.toString = function () { + return '(' + this.x + ', ' + this.y + ')'; +}; +var a = new P(1, 2); + +// ES6 +class P { + constructor(x, y){ + this.x = x; + this.y = y; + } + toString(){ + return '(' + this.x + ', ' + this.y + ')'; + } +} +let a = new P(1, 2); +``` +**值得注意**: +ES6的**类**的所有方法都是定义在`prototype`属性上,调用类的实例的方法,其实就是调用原型上的方法。 +```js +class P { + constructor(){ ... } + toString(){ ... } + toNumber(){ ... } +} +// 等同于 +P.prototyoe = { + constructor(){ ... }, + toString(){ ... }, + toNumber(){ ... } +} + +let a = new P(); +a.constructor === P.prototype.constructor; // true +``` +类的属性名可以使用**表达式**: +```js +let name = 'leo'; +class P { + constructor (){ ... } + [name](){ ... } +} +``` + +**Class不存在变量提升**: +ES6中的类不存在变量提升,与ES5完全不同: +```js +new P (); // ReferenceError +class P{...}; +``` +**Class的name属性**: +`name`属性总是返回紧跟在`class`后的类名。 +```js +class P {} +P.name; // 'P' +``` + +### 15.2 constructor()方法 +`constructor()`是类的默认方法,通过`new`实例化时自动调用执行,一个类必须有`constructor()`方法,否则一个空的`constructor()`会默认添加。 +`constructor()`方法默认返回实例对象(即`this`)。 +```js +class P { ... } +// 等同于 +class P { + constructor(){ ... } +} +``` + +### 15.3 类的实例对象 +与ES5一样,ES6的类必须使用`new`命令实例化,否则报错。 +```js +class P { ... } +let a = P (1,2); // 报错 +let b = new P(1, 2); // 正确 +``` +与 ES5 一样,实例的属性除非显式定义在其本身(即定义在`this`对象上),否则都是定义在原型上(即定义在`class`上)。 +```js +class P { + constructor(x, y){ + this.x = x; + this.y = y; + } + toString(){ + return '(' + this.x + ', ' + this.y + ')'; + } +} +var point = new Point(2, 3); + +point.toString() // (2, 3) + +point.hasOwnProperty('x') // true +point.hasOwnProperty('y') // true +point.hasOwnProperty('toString') // false +point.__proto__.hasOwnProperty('toString') // true +// toString是原型对象的属性(因为定义在Point类上) +``` + +### 15.4 Class表达式 +与函数一样,类也可以使用表达式来定义,使用表达式来作为类的名字,而`class`后跟的名字,用来指代当前类,只能再Class内部使用。 +```js +let a = class P{ + get(){ + return P.name; + } +} + +let b = new a(); +b.get(); // P +P.name; // ReferenceError: P is not defined +``` +如果类的内部没用到的话,可以省略`P`,也就是可以写成下面的形式。 +```js +let a = class { ... } +``` + +### 15.5 私有方法和私有属性 +由于ES6不提供,只能变通来实现: +* 1.使用命名加以区别,如变量名前添加`_`,但是不保险,外面也可以调用到。 +```js +class P { + // 公有方法 + f1 (x) { + this._x(x); + } + // 私有方法 + _x (x){ + return this.y = x; + } +} +``` +* 2.将私有方法移除模块,再在类内部调用`call`方法。 +```js +class P { + f1 (x){ + f2.call(this, x); + } +} +function f2 (x){ + return this.y = x; +} +``` +* 3.使用`Symbol`为私有方法命名。 +```js +const a1 = Symbol('a1'); +const a2 = Symbol('a2'); +export default class P{ + // 公有方法 + f1 (x){ + this[a1](x); + } + // 私有方法 + [a1](x){ + return this[a2] = x; + } +} +``` + + +### 15.6 this指向问题 +类内部方法的`this`默认指向类的实例,但单独使用该方法可能报错,因为`this`指向的问题。 +```js +class P{ + leoDo(thing = 'any'){ + this.print(`Leo do ${thing}`) + } + print(text){ + console.log(text); + } +} +let a = new P(); +let { leoDo } = a; +leoDo(); // TypeError: Cannot read property 'print' of undefined +// 问题出在 单独使用leoDo时,this指向调用的环境, +// 但是leoDo中的this是指向P类的实例,所以报错 +``` +**解决方法**: +* 1.在类里面绑定`this` +```js +class P { + constructor(){ + this.name = this.name.bind(this); + } +} +``` +* 2.使用箭头函数 +```js +class P{ + constructor(){ + this.name = (name = 'leo' )=>{ + this.print(`my name is ${name}`) + } + } +} +``` + +### 15.7 Class的getter和setter +使用`get`和`set`关键词对属性设置取值函数和存值函数,拦截属性的存取行为。 +```js +class P { + constructor (){ ... } + get f (){ + return 'getter'; + } + set f (val) { + console.log('setter: ' + val); + } +} + +let a = new P(); +a.f = 100; // setter : 100 +a.f; // getter +``` + +### 15.8 Class的generator方法 +只要在方法之前加个(`*`)即可。 +```js +class P { + constructor (...args){ + this.args = args; + } + *[Symbol.iterator](){ + for (let arg of this.args){ + yield arg; + } + } +} +for (let k of new P('aa', 'bb')){ + console.log(k); +} +// 'aa' +// 'bb' +``` + +### 15.9 Class的静态方法 +由于类相当于实例的原型,所有类中定义的方法都会被实例继承,若不想被继承,只要加上`static`关键字,只能通过类来调用,即“**静态方法**”。 +```js +class P (){ + static f1 (){ return 'aaa' }; +} +P.f1(); // 'aa' +let a = new P(); +a.f1(); // TypeError: a.f1 is not a function +``` +如果静态方法包含`this`关键字,则`this`指向类,而不是实例。 +```js +class P { + static f1 (){ + this.f2(); + } + static f2 (){ + console.log('aaa'); + } + f2(){ + console.log('bbb'); + } +} +P.f2(); // 'aaa' +``` +并且静态方法可以被子类继承,或者`super`对象中调用。 +```js +class P{ + static f1(){ return 'leo' }; +} +class Q extends P { ... }; +Q.f1(); // 'leo' + +class R extends P { + static f2(){ + return super.f1() + ',too'; + } +} +R.f2(); // 'leo , too' +``` + +### 15.10 Class的静态属性和实例属性 +ES6中明确规定,Class内部只有静态方法没有静态属性,所以只能通过下面实现。 +```js +// 正确写法 +class P {} +P.a1 = 1; +P.a1; // 1 + +// 无效写法 +class P { + a1: 2, // 无效 + static a1 : 2, // 无效 +} +P.a1; // undefined +``` +**新提案来规定实例属性和静态属性的新写法** +* 1.类的实例属性 +类的实例属性可以用等式,写入类的定义中。 +```js +class P { + prop = 100; // prop为P的实例属性 可直接读取 + constructor(){ + console.log(this.prop); // 100 + } +} +``` +有了新写法后,就可以不再`contructor`方法里定义。 +为了可读性的目的,对于那些在`constructor`里面已经定义的实例属性,新写法允许**直接列出**。 +```js +// 之前写法: +class RouctCounter extends React.Component { + constructor(prop){ + super(prop); + this.state = { + count : 0 + } + } +} + +// 新写法 +class RouctCounter extends React.Component { + state; + constructor(prop){ + super(prop); + this.state = { + count : 0 + } + } + +} +``` +* 2.类的静态属性 +只要在实例属性前面加上`static`关键字就可以。 +```js +class P { + static prop = 100; + constructor(){console.log(this.prop)}; // 100 +} +``` +新写法方便静态属性的表达。 +```js +// old +class P { .... } +P.a = 1; + +// new +class P { + static a = 1; +} +``` + +### 15.11 Class的继承 +主要通过`extends`关键字实现,继承父类的所有属性和方法,通过`super`关键字来新建父类构造函数的`this`对象。 +```js +class P { ... } +class Q extends P { ... } + +class P { + constructor(x, y){ + // ... + } + f1 (){ ... } +} +class Q extends P { + constructor(a, b, c){ + super(x, y); // 调用父类 constructor(x, y) + this.color = color ; + } + f2 (){ + return this.color + ' ' + super.f1(); + // 调用父类的f1()方法 + } +} +``` +**子类必须在`constructor()`调用`super()`否则报错**,并且只有`super`方法才能调用父类实例,还有就是,**父类的静态方法,子类也可以继承到**。 +```js +class P { + constructor(x, y){ + this.x = x; + this.y = y; + } + static fun(){ + console.log('hello leo') + } +} +// 关键点1 调用super +class Q extends P { + constructor(){ ... } +} +let a = new Q(); // ReferenceError 因为Q没有调用super + +// 关键点2 调用super +class R extends P { + constructor (x, y. z){ + this.z = z; // ReferenceError 没调用super不能使用 + super(x, y); + this.z = z; // 正确 + } +} + +// 关键点3 子类继承父类静态方法 +R.hello(); // 'hello leo' +``` + +**super关键字**: +既可以当函数使用,还可以当对象使用。 +* 1.当函数调用,代表父类的构造函数,但必须执行一次。 +```js +class P {... }; +class R extends P { + constructor(){ + super(); + } +} +``` +* 2.当对象调用,指向原型对象,在静态方法中指向父类。 +```js +class P { + f (){ return 2 }; +} +class R extends P { + constructor (){ + super(); + console.log(super.f()); // 2 + } +} +let a = new R() +``` +**注意**:`super`指向父类原型对象,所以定义在父类实例的方法和属性,是无法通过`super`调用的,但是通过调用`super`方法可以把内部`this`指向当前实例,就可以访问到。 +```js +class P { + constructor(){ + this.a = 1; + } + print(){ + console.log(this.a); + } +} +class R extends P { + get f (){ + return super.a; + } +} +let b = new R(); +b.a; // undefined 因为a是父类P实例的属性 + +// 先调用super就可以访问 +class Q extends P { + constructor(){ + super(); // 将内部this指向当前实例 + return super.a; + } +} +let c = new Q(); +c.a; // 1 + +// 情况3 +class J extends P { + constructor(){ + super(); + this.a = 3; + } + g(){ + super.print(); + } +} +let c = new J(); +c.g(); // 3 由于执行了super()后 this指向当前实例 +``` + +[⬆ 返回目录](#二目录) + +## 16 Module语法和加载实现 +### 16.1 介绍 +ES6之前用于JavaScript的模块加载方案,是一些社区提供的,主要有`CommonJS`和`AMD`两种,前者用于**服务器**,后者用于**浏览器**。 +ES6提供了模块的实现,使用`export`命令对外暴露接口,使用`import`命令输入其他模块暴露的接口。 +```js +// CommonJS模块 +let { stat, exists, readFire } = require('fs'); + +// ES6模块 +import { stat, exists, readFire } = from 'fs'; +``` + +### 16.2 严格模式 +ES6模块自动采用严格模式,无论模块头部是否有`"use strict"`。 +**严格模式有以下限制**: +* 变量必须**声明后再使用** +* 函数的参数**不能有同名属性**,否则报错 +* 不能使用`with`语句 +* 不能对只读属性赋值,否则报错 +* 不能使用前缀 0 表示八进制数,否则报错 +* 不能删除不可删除的属性,否则报错 +* 不能删除变量`delete prop`,会报错,只能删除属性`delete * global[prop]` +* `eval`不会在它的外层作用域引入变量 +* `eval`和`arguments`不能被重新赋值 +* `arguments`不会自动反映函数参数的变化 +* 不能使用`arguments.callee` +* 不能使用`arguments.caller` +* 禁止`this`指向全局对象 +* 不能使用`fn.caller`和`fn.arguments`获取函数调用的堆栈 +* 增加了保留字(比如`protected`、`static`和`interface`) + +特别是,ES6中顶层`this`指向`undefined`,即不应该在顶层代码使用`this`。 + +### 16.3 export命令 +使用`export`向模块外暴露接口,可以是方法,也可以是变量。 +```js +// 1. 变量 +export let a = 'leo'; +export let b = 100; + +// 还可以 +let a = 'leo'; +let b = 100; +export {a, b}; + +// 2. 方法 +export function f(a,b){ + return a*b; +} + +// 还可以 +function f1 (){ ... } +function f2 (){ ... } +export { + a1 as f1, + a2 as f2 +} +``` +可以使用`as`重命名函数的对外接口。 +**特别注意**: +`export`暴露的必须是接口,不能是值。 +```js +// 错误 +export 1; // 报错 + +let a = 1; +export a; // 报错 + +// 正确 +export let a = 1; // 正确 + +let a = 1; +export {a}; // 正确 + +let a = 1; +export { a as b}; // 正确 +``` +暴露方法也是一样: +```js +// 错误 +function f(){...}; +export f; + +// 正确 +export function f () {...}; + +function f(){...}; +export {f}; +``` + +### 16.4 import命令 +加载`export`暴露的接口,输出为变量。 +```js +import { a, b } from '/a.js'; +function f(){ + return a + b; +} +``` +`import`后大括号指定变量名,需要与`export`的模块暴露的名称一致。 +也可以使用`as`为输入的变量重命名。 +```js +import { a as leo } from './a.js'; +``` +`import`不能直接修改输入变量的值,因为输入变量只读只是个接口,但是如果是个对象,可以修改它的属性。 +```js +// 错误 +import {a} from './f.js'; +a = {}; // 报错 + +// 正确 +a.foo = 'leo'; // 不报错 +``` +`import`命令具有提升效果,会提升到整个模块头部最先执行,且多次执行相同`import`只会执行一次。 + +### 16.5 模块的整体加载 +当一个模块暴露多个方法和变量,引用时可以用`*`整体加载。 +```js +// a.js +export function f(){...} +export function g(){...} + +// b.js +import * as obj from '/a.js'; +console.log(obj.f()); +console.log(obj.g()); +``` +但是,不允许运行时改变: +```js +import * as obj from '/a.js'; +// 不允许 +obj.a = 'leo'; +obj.b = function(){...}; +``` + +### 16.6 export default 命令 +使用`export default`命令,为模块指定默认输出,引用的时候直接指定任意名称即可。 +```js +// a.js +export default function(){console.log('leo')}; + +// b.js +import leo from './a.js'; +leo(); // 'leo' +``` +`export default`暴露有函数名的函数时,在调用时相当于匿名函数。 +```js +// a.js +export default function f(){console.log('leo')}; +// 或者 +function f(){console.log('leo')}; +export default f; + +// b.js +import leo from './a.js'; +``` +`export default`其实是输出一个名字叫`default`的变量,所以后面不能跟变量赋值语句。 +```js +// 正确 +export let a= 1; + +let a = 1; +export default a; + +// 错误 +export default let a = 1; +``` +`export default`命令的本质是将后面的值,赋给`default`变量,所以可以直接将一个值写在`export default`之后。 +```js +// 正确 +export detault 1; +// 错误 +export 1; +``` + +### 16.7 export 和 import 复合写法 +常常在先输入后输出同一个模块使用,即转发接口,将两者写在一起。 +```js +export {a, b} from './leo.js'; + +// 理解为 +import {a, b} from './leo.js'; +export {a, b} +``` +常见的写法还有: +```js +// 接口改名 +export { a as b} from './leo.js'; + +// 整体输出 +export * from './leo.js'; + +// 默认接口改名 +export { default as a } from './leo.js'; +``` +**常常用在模块继承**。 + +### 16.8 浏览器中的加载规则 +ES6中,可以在浏览器使用` +``` +另外,ES6模块也可以内嵌到网页,语法与外部加载脚本一致: +```html + +``` +**注意点**: +* 代码是在模块作用域之中运行,而不是在全局作用域运行。模块内部的顶层变量,外部不可见。 +* 模块脚本自动采用严格模式,不管有没有声明`use strict`。 +* 模块之中,可以使用`import`命令加载其他模块(`.js`后缀不可省略,需要提供`绝对 UR`L 或`相对 UR`L),也可以使用`export`命令输出对外接口。 +* 模块之中,顶层的`this`关键字返回`undefined`,而不是指向`window`。也就是说,在模块顶层使用`this`关键字,是无意义的。 +* 同一个模块如果加载多次,将只执行一次。 + +[⬆ 返回目录](#二目录) \ No newline at end of file diff --git a/Cute-JavaScript/Cute-ES/2.ES7.md b/Cute-JavaScript/Cute-ES/2.ES7.md new file mode 100644 index 00000000..74426085 --- /dev/null +++ b/Cute-JavaScript/Cute-ES/2.ES7.md @@ -0,0 +1,51 @@ + + +- [1 Array.prototype.includes()方法](#1-arrayprototypeincludes方法) +- [2 指数操作符(**)](#2-指数操作符) + + +## 1 Array.prototype.includes()方法 +`includes()`用于查找一个值是否在数组中,如果在返回`true`,否则返回`false`。 +```js +['a', 'b', 'c'].includes('a'); // true +['a', 'b', 'c'].includes('d'); // false +``` +`includes()`方法接收两个参数,**搜索的内容**和**开始搜索的索引**,默认值为**0**,若搜索值在数组中则返回`true`否则返回`false`。 +```js +['a', 'b', 'c', 'd'].includes('b'); // true +['a', 'b', 'c', 'd'].includes('b', 1); // true +['a', 'b', 'c', 'd'].includes('b', 2); // false +``` +与`indexOf`方法对比,下面方法效果相同: +```js +['a', 'b', 'c', 'd'].indexOf('b') > -1; // true +['a', 'b', 'c', 'd'].includes('b'); // true +``` +**includes()与indexOf对比:** +* `includes`相比`indexOf`更具语义化,`includes`返回的是是否存在的具体结果,值为布尔值,而`indexOf`返回的是搜索值的下标。 +* `includes`相比`indexOf`更准确,`includes`认为两个`NaN`相等,而`indexOf`不会。 +```js +let a = [1, NaN, 3]; +a.indexOf(NaN); // -1 +a.includes(NaN); // true +``` +另外在判断`+0`与`-0`时,`includes`和`indexOf`的返回相同。 +```js +[1, +0, 3, 4].includes(-0); // true +[1, +0, 3, 4].indexOf(-0); // 1 +``` + +## 2 指数操作符(**) +基本用法: +```js +let a = 3 ** 2 ; // 9 +// 等效于 +Math.pow(3, 2); // 9 +``` +`**`是一个运算符,也可以满足类似假发的操作,如下: +```js +let a = 3; +a **= 2; // 9 +``` + +[⬆ 返回目录](#二目录) \ No newline at end of file diff --git a/Cute-JavaScript/Cute-ES/3.ES8.md b/Cute-JavaScript/Cute-ES/3.ES8.md new file mode 100644 index 00000000..adb7b192 --- /dev/null +++ b/Cute-JavaScript/Cute-ES/3.ES8.md @@ -0,0 +1,396 @@ + + +- [1 async函数](#1-async函数) + - [1.1 介绍](#11-介绍) + - [1.2 基本用法](#12-基本用法) + - [1.3 返回Promise对象](#13-返回promise对象) + - [1.4 await命令](#14-await命令) + - [1.5 使用注意](#15-使用注意) +- [2 Promise.prototype.finally()](#2-promiseprototypefinally) +- [3 Object.values(),Object.entries()](#3-objectvaluesobjectentries) + - [3.1 Object.values()](#31-objectvalues) + - [3.2 Object.entries()](#32-objectentries) +- [4 Object.getOwnPropertyDescriptors()](#4-objectgetownpropertydescriptors) +- [5 字符串填充 padStart和padEnd](#5-字符串填充-padstart和padend) +- [6 函数参数列表与调用中的尾部逗号](#6-函数参数列表与调用中的尾部逗号) +- [7 共享内存与原子操作](#7-共享内存与原子操作) + + +## 1 async函数 +### 1.1 介绍 +ES8引入`async`函数,是为了使异步操作更加方便,其实它就是**Generator**函数的语法糖。 +`async`函数使用起来,只要把**Generator**函数的(*)号换成`async`,`yield`换成`await`即可。对比如下: +```js +// Generator写法 +const fs = require('fs'); +const readFile = function (fileName) { + return new Promise(function (resolve, reject) { + fs.readFile(fileName, function(error, data) { + if (error) return reject(error); + resolve(data); + }); + }); +}; +const gen = function* () { + const f1 = yield readFile('/etc/fstab'); + const f2 = yield readFile('/etc/shells'); + console.log(f1.toString()); + console.log(f2.toString()); +}; + +// async await写法 +const asyncReadFile = async function () { + const f1 = await readFile('/etc/fstab'); + const f2 = await readFile('/etc/shells'); + console.log(f1.toString()); + console.log(f2.toString()); +}; +``` +**对比Genenrator有四个优点:** +* (1)内置执行器 +Generator函数执行需要有执行器,而`async`函数自带执行器,即`async`函数与普通函数一模一样: +```js +async f(); +``` +* (2)更好的语义 +`async`和`await`,比起`星号`和`yield`,语义更清楚了。`async`表示函数里有异步操作,`await`表示紧跟在后面的表达式需要等待结果。 +* (3)更广的适用性 +`yield`命令后面只能是 Thunk 函数或 Promise 对象,而`async`函数的`await`命令后面,可以是 Promise 对象和原始类型的值(数值、字符串和布尔值,但这时等同于同步操作)。 +* (4)返回值是Promise +`async`函数的返回值是 Promise 对象,这比 Generator 函数的返回值是 Iterator 对象方便多了。你可以用`then`方法指定下一步的操作。 + +进一步说,`async`函数完全可以看作多个异步操作,包装成的一个 Promise 对象,而`await`命令就是内部`then`命令的语法糖。 + +### 1.2 基本用法 +`async`函数返回一个Promise对象,可以使用`then`方法添加回调函数,函数执行时,遇到`await`就会先返回,等到异步操作完成,在继续执行。 +```js +async function f(item){ + let a = await g(item); + let b = await h(item); + return b; +} +f('hello').then(res => { + console.log(res); +}) +``` +`async`表明该函数内部有异步操作,调用该函数时,会立即返回一个Promise对象。 +另外还有个定时的案例,指定时间后执行: +```js +function f (ms){ + return new Promise(res => { + setTimeout(res, ms); + }); +} +async function g(val, ms){ + await f(ms); + console.log(val); +} +g('leo', 50); +``` +`async`函数还有很多使用形式: +```js +// 函数声明 +async function f (){...} + +// 函数表达式 +let f = async function (){...} + +// 对象的方法 +let a = { + async f(){...} +} +a.f().then(...) + +// Class的方法 +class c { + constructor(){...} + async f(){...} +} + +// 箭头函数 +let f = async () => {...} +``` + +### 1.3 返回Promise对象 +`async`内部`return`返回的值会作为`then`方法的参数,另外只有`async`函数内部的异步操作执行完,才会执行`then`方法指定的回调函数。 +```js +async function f(){ + return 'leo'; +} +f().then(res => { console.log (res) }); // 'leo' +``` +`async`内部抛出的错误,会被`catch`接收。 +```js +async function f(){ + throw new Error('err'); +} +f().then ( + v => console.log(v), + e => console.log(e) +) +// Error: err +``` + +### 1.4 await命令 +通常`await`后面是一个Promise对象,如果不是就返回对应的值。 +```js +async function f(){ + return await 10; +} +f().then(v => console.log(v)); // 10 +``` +我们常常将`async await`和`try..catch`一起使用,并且可以放多个`await`命令,也是防止异步操作失败因为中断后续异步操作的情况。 +```js +async function f(){ + try{ + await Promise.reject('err'); + }catch(err){ ... } + return await Promise.resolve('leo'); +} +f().then(v => console.log(v)); // 'leo' +``` + +### 1.5 使用注意 +* (1)`await`命令放在`try...catch`代码块中,防止Promise返回`rejected`。 +* (2)若多个`await`后面的异步操作不存在继发关系,最好让他们同时执行。 +```js +// 效率低 +let a = await f(); +let b = await g(); + +// 效率高 +let [a, b] = await Promise.all([f(), g()]); +// 或者 +let a = f(); +let b = g(); +let a1 = await a(); +let b1 = await b(); +``` +* (3)`await`命令只能用在`async`函数之中,如果用在普通函数,就会报错。 +```js +// 错误 +async function f(){ + let a = [{}, {}, {}]; + a.forEach(v =>{ // 报错,forEach是普通函数 + await post(v); + }); +} + +// 正确 +async function f(){ + let a = [{}, {}, {}]; + for(let k of a){ + await post(k); + } +} +``` + +[⬆ 返回目录](#二目录) + +## 2 Promise.prototype.finally() +`finally()`是ES8中**Promise**添加的一个新标准,用于指定不管**Promise**对象最后状态(是`fulfilled`还是`rejected`)如何,都会执行此操作,并且`finally`方法必须写在最后面,即在`then`和`catch`方法后面。 +```js +// 写法如下: +promise + .then(res => {...}) + .catch(err => {...}) + .finally(() => {...}) +``` +`finally`方法常用在处理**Promise**请求后关闭服务器连接: +```js +server.listen(port) + .then(() => {..}) + .finally(server.stop); +``` +**本质上,finally方法是then方法的特例:** +```js +promise.finally(() => {...}); + +// 等同于 +promise.then( + result => { + // ... + return result + }, + error => { + // ... + throw error + } +) +``` +[⬆ 返回目录](#二目录) + +## 3 Object.values(),Object.entries() +ES7中新增加的 `Object.values()`和`Object.entries()`与之前的`Object.keys()`类似,返回数组类型。 +回顾下`Object.keys()`: +```js +var a = { f1: 'hi', f2: 'leo'}; +Object.keys(a); // ['f1', 'f2'] +``` +### 3.1 Object.values() +返回一个数组,成员是参数对象自身的(不含继承的)所有**可遍历属性**的键值。 +```js +let a = { f1: 'hi', f2: 'leo'}; +Object.values(a); // ['hi', 'leo'] +``` +如果参数不是对象,则返回空数组: +```js +Object.values(10); // [] +Object.values(true); // [] +``` + +### 3.2 Object.entries() +返回一个数组,成员是参数对象自身的(不含继承的)所有**可遍历属性**的键值对数组。 +```js +let a = { f1: 'hi', f2: 'leo'}; +Object.entries(a); // [['f1','hi'], ['f2', 'leo']] +``` +* **用途1**: +遍历对象属性。 +```js +let a = { f1: 'hi', f2: 'leo'}; +for (let [k, v] of Object.entries(a)){ + console.log( + `${JSON.stringfy(k)}:${JSON.stringfy(v)}` + ) +} +// 'f1':'hi' +// 'f2':'leo' +``` +* **用途2**: +将对象转为真正的Map结构。 +```js +let a = { f1: 'hi', f2: 'leo'}; +let map = new Map(Object.entries(a)); +``` +手动实现`Object.entries()`方法: +```js +// Generator函数实现: +function* entries(obj){ + for (let k of Object.keys(obj)){ + yield [k ,obj[k]]; + } +} + +// 非Generator函数实现: +function entries (obj){ + let arr = []; + for(let k of Object.keys(obj)){ + arr.push([k, obj[k]]); + } + return arr; +} +``` + +[⬆ 返回目录](#二目录) + +## 4 Object.getOwnPropertyDescriptors() +之前有`Object.getOwnPropertyDescriptor`方法会返回某个对象属性的描述对象,新增的`Object.getOwnPropertyDescriptors()`方法,返回指定对象所有自身属性(非继承属性)的描述对象,所有原对象的属性名都是该对象的属性名,对应的属性值就是该属性的描述对象 +```js +let a = { + a1:1, + get f1(){ return 100} +} +Object.getOwnPropetyDescriptors(a); +/* +{ + a:{ configurable:true, enumerable:true, value:1, writeable:true} + f1:{ configurable:true, enumerable:true, get:f, set:undefined} +} +*/ +``` +实现原理: +```js +function getOwnPropertyDescriptors(obj) { + const result = {}; + for (let key of Reflect.ownKeys(obj)) { + result[key] = Object.getOwnPropertyDescriptor(obj, key); + } + return result; +} +``` +引入这个方法,主要是为了解决`Object.assign()`无法正确拷贝`get`属性和`set`属性的问题。 +```js +let a = { + set f(v){ + console.log(v) + } +} +let b = {}; +Object.assign(b, a); +Object.a(b, 'f'); +/* +f = { + configurable: true, + enumable: true, + value: undefined, + writeable: true +} +*/ +``` +`value`为`undefined`是因为`Object.assign`方法不会拷贝其中的`get`和`set`方法,使用`getOwnPropertyDescriptors`配合`Object.defineProperties`方法来实现正确的拷贝: +```js +let a = { + set f(v){ + console.log(v) + } +} +let b = {}; +Object.defineProperties(b, Object.getOwnPropertyDescriptors(a)); +Object.getOwnPropertyDescriptor(b, 'f') +/* + configurable: true, + enumable: true, + get: undefined, + set: function(){...} +*/ +``` +`Object.getOwnPropertyDescriptors`方法的配合`Object.create`方法,将对象属性克隆到一个新对象,实现浅拷贝。 +```js +const clone = Object.create(Object.getPrototypeOf(obj), + Object.getOwnPropertyDescriptors(obj)); + +// 或者 +const shallowClone = (obj) => Object.create( + Object.getPrototypeOf(obj), + Object.getOwnPropertyDescriptors(obj) +); +``` + +[⬆ 返回目录](#二目录) + +## 5 字符串填充 padStart和padEnd +用来为字符串填充特定字符串,并且都有两个参数:**字符串目标长度**和**填充字段**,第二个参数可选,默认空格。 +```js +'es8'.padStart(2); // 'es8' +'es8'.padStart(5); // ' es8' +'es8'.padStart(6, 'woof'); // 'wooes8' +'es8'.padStart(14, 'wow'); // 'wowwowwowwoes8' +'es8'.padStart(7, '0'); // '0000es8' + +'es8'.padEnd(2); // 'es8' +'es8'.padEnd(5); // 'es8 ' +'es8'.padEnd(6, 'woof'); // 'es8woo' +'es8'.padEnd(14, 'wow'); // 'es8wowwowwowwo' +'es8'.padEnd(7, '6'); // 'es86666' +``` +从上面结果来看,填充函数只有在字符长度小于目标长度时才有效,若字符长度已经等于或小于目标长度时,填充字符不会起作用,而且目标长度如果小于字符串本身长度时,字符串也不会做截断处理,只会原样输出。 + +## 6 函数参数列表与调用中的尾部逗号 +该特性允许我们在定义或者调用函数时添加尾部逗号而不报错: +```js +function es8(var1, var2, var3,) { + // ... +} +es8(10, 20, 30,); +``` + +## 7 共享内存与原子操作 +当内存被共享时,多个线程可以并发读、写内存中相同的数据。原子操作可以确保那些被读、写的值都是可预期的,即新的事务是在旧的事务结束之后启动的,旧的事务在结束之前并不会被中断。这部分主要介绍了 ES8 中新的构造函数 `SharedArrayBuffer` 以及拥有许多静态方法的命名空间对象 `Atomic` 。 +`Atomic` 对象类似于 `Math` 对象,拥有许多静态方法,所以我们不能把它当做构造函数。 `Atomic` 对象有如下常用的静态方法: + +* add /sub :为某个指定的value值在某个特定的位置增加或者减去某个值 +* and / or /xor :进行位操作 +* load :获取特定位置的值 + +[⬆ 返回目录](#二目录) diff --git a/Cute-JavaScript/Cute-ES/4.ES9.md b/Cute-JavaScript/Cute-ES/4.ES9.md new file mode 100644 index 00000000..ab057f83 --- /dev/null +++ b/Cute-JavaScript/Cute-ES/4.ES9.md @@ -0,0 +1,344 @@ + + +- [1 对象的拓展运算符](#1-对象的拓展运算符) + - [1.1 介绍](#11-介绍) + - [1.2 使用场景](#12-使用场景) +- [2 正则表达式 s 修饰符](#2-正则表达式-s-修饰符) +- [3 异步遍历器](#3-异步遍历器) + - [3.1 异步遍历的接口](#31-异步遍历的接口) + - [3.2 for await...of](#32-for-awaitof) + - [3.3 异步Generator函数](#33-异步generator函数) + - [3.4 yield* 语句](#34-yield-语句) + + +## 1 对象的拓展运算符 +### 1.1 介绍 +对象的拓展运算符,即对象的Rest/Spread属性,可将对象解构赋值用于从一个对象取值,搜键值对分配到指定对象上,与数组的拓展运算符类似: +```js +let {x, y, ...z} = {x:1, y:2, a:3, b:4}; +x; // 1 +y; // 2 +z; // {a:3, b:4} +``` +对象的解构赋值要求等号右边必须是个对象,所以如果等号右边是`undefined`或`null`就会报错无法转成对象。 +```js +let {a, ...b} = null; // 运行时报错 +let {a, ...b} = undefined; // 运行时报错 +``` +解构赋值必须是最后一个参数,否则报错。 +```js +let {...a, b, c} = obj; // 语法错误 +let {a, ...b, c} = obj; // 语法错误 +``` +**注意**: +* 1.解构赋值是浅拷贝。 +```js +let a = {a1: {a2: 'leo'}}; +let {...b} = a; +a.a1.a2 = 'leo'; +b.a1.a2 = 'leo'; +``` +* 2.拓展运算符的解构赋值,不能复制继承自原型对象的属性。 +```js +let o1 = { a: 1 }; +let o2 = { b: 2 }; +o2.__proto__ = o1; +let { ...o3 } = o2; +o3; // { b: 2 } +o3.a; // undefined +``` + +### 1.2 使用场景 +* 1.取出参数对象所有可遍历属性,拷贝到当前对象中。 +```js +let a = { a1:1, a2:2 }; +let b = { ...a }; +b; // { a1:1, a2:2 } + +// 类似Object.assign方法 +``` +* 2.合并两个对象。 +```js +let a = { a1:1, a2:2 }; +let b = { b1:11, b2:22 }; +let ab = { ...a, ...b }; // {a1: 1, a2: 2, b1: 11, b2: 22} +// 等同于 +let ab = Object.assign({}, a, b); +``` +* 3.将自定义属性放在拓展运算符后面,覆盖对象原有的同名属性。 +```js +let a = { a1:1, a2:2, a3:3 }; +let r = { ...a, a3:666 }; +// r {a1: 1, a2: 2, a3: 666} + +// 等同于 +let r = { ...a, ...{ a3:666 }}; +// r {a1: 1, a2: 2, a3: 666} + +// 等同于 +let r = Object.assign({}, a, { a3:666 }); +// r {a1: 1, a2: 2, a3: 666} +``` +* 4.将自定义属性放在拓展运算符前面,就会成为设置新对象的默认值。 +```js +let a = { a1:1, a2:2 }; +let r = { a3:666, ...a }; +// r {a3: 666, a1: 1, a2: 2} + +// 等同于 +let r = Object.assign({}, {a3:666}, a); +// r {a3: 666, a1: 1, a2: 2} + +// 等同于 +let r = Object.assign({a3:666}, a); +// r {a3: 666, a1: 1, a2: 2} +``` +* 5.拓展运算符后面可以使用表达式。 +```js +let a = { + ...(x>1? {a:!:{}), + b:2 +} +``` +* 6.拓展运算符后面如果是个空对象,则没有任何效果。 +```js +{...{}, a:1}; // {a:1} +``` +* 7.若参数是`null`或`undefined`则忽略且不报错。 +```js +let a = { ...null, ...undefined }; // 不报错 +``` +* 8.若有取值函数`get`则会执行。 +```js +// 不会打印 因为f属性只是定义 而不没执行 +let a = { + ...a1, + get f(){console.log(1)} +} + +// 会打印 因为f执行了 +let a = { + ...a1, + ...{ + get f(){console.log(1)} + } +} +``` + +[⬆ 返回目录](#二目录) + + +## 2 正则表达式 s 修饰符 +在正则表达式中,点(`.`)可以表示任意单个字符,除了两个:用`u`修饰符解决**四个字节的UTF-16字符**,另一个是行终止符。 +**终止符**即表示一行的结束,如下四个字符属于“行终止符”: +* U+000A 换行符(\n) +* U+000D 回车符(\r) +* U+2028 行分隔符(line separator) +* U+2029 段分隔符(paragraph separator) +```js +/foo.bar/.test('foo\nbar') +// false +``` +上面代码中,因为`.`不匹配`\n`,所以正则表达式返回`false`。 +换个醒,可以匹配任意单个字符: +```js +/foo[^]bar/.test('foo\nbar') +// true +``` +ES9引入`s`修饰符,使得`.`可以匹配任意单个字符: +```js +/foo.bar/s.test('foo\nbar') // true +``` +这被称为`dotAll`模式,即点(`dot`)代表一切字符。所以,正则表达式还引入了一个`dotAll`属性,返回一个布尔值,表示该正则表达式是否处在`dotAll`模式。 +```js +const re = /foo.bar/s; +// 另一种写法 +// const re = new RegExp('foo.bar', 's'); + +re.test('foo\nbar') // true +re.dotAll // true +re.flags // 's' +``` +`/s`修饰符和多行修饰符`/m`不冲突,两者一起使用的情况下,`.`匹配所有字符,而`^`和`$`匹配每一行的行首和行尾。 + +[⬆ 返回目录](#二目录) + + +## 3 异步遍历器 +在前面ES6章节中,介绍了Iterator接口,而ES6引入了“异步遍历器”,是为异步操作提供原生的遍历器接口,即`value`和`done`这两个属性都是异步产生的。 + +### 3.1 异步遍历的接口 +通过调用遍历器的`next`方法,返回一个Promise对象。 +```js +a.next().then( + ({value, done}) => { + //... + } +) +``` +上述`a`为异步遍历器,调用`next`后返回一个Promise对象,再调用`then`方法就可以指定Promise对象状态变为`resolve`后执行的回调函数,参数为`value`和`done`两个属性的对象,与同步遍历器一致。 +与同步遍历器一样,异步遍历器接口也是部署在`Symbol.asyncIterator`属性上,只要有这个属性,就都可以异步遍历。 +```js +let a = createAsyncIterable(['a', 'b']); +//createAsyncIterable方法用于构建一个iterator接口 +let b = a[Symbol.asyncInterator](); + +b.next().then( result1 => { + console.log(result1); // {value: 'a', done:false} + return b.next(); +}).then( result2 => { + console.log(result2); // {value: 'b', done:false} + return b.next(); +}).then( result3 => { + console.log(result3); // {value: undefined, done:true} +}) +``` +另外`next`方法返回的是一个Promise对象,所以可以放在`await`命令后。 +```js +async function f(){ + let a = createAsyncIterable(['a', 'b']); + let b = a[Symbol.asyncInterator](); + console.log(await b.next());// {value: 'a', done:false} + console.log(await b.next());// {value: 'b', done:false} + console.log(await b.next());// {value: undefined, done:true} +} +``` +还有一种情况,使用`Promise.all`方法,将所有的`next`按顺序连续调用: +```js +let a = createAsyncIterable(['a', 'b']); +let b = a[Symbol.asyncInterator](); +let {{value:v1}, {value:v2}} = await Promise.all([ + b.next(), b.next() +]) +``` +也可以一次调用所有`next`方法,再用`await`最后一步操作。 +```js +async function f(){ + let write = openFile('aaa.txt'); + write.next('hi'); + write.next('leo'); + await write.return(); +} +f(); +``` +### 3.2 for await...of +`for...of`用于遍历同步的Iterator接口,而ES8引入`for await...of`遍历异步的Iterator接口。 +```js +async function f(){ + for await(let a of createAsyncIterable(['a', 'b'])) { + console.log(x); + } +} +// a +// b +``` +上面代码,`createAsyncIterable()`返回一个拥有异步遍历器接口的对象,`for...of`自动调用这个对象的`next`方法,得到一个Promise对象,`await`用来处理这个Promise,一但`resolve`就把得到的值`x`传到`for...of`里面。 +**用途** +直接把部署了asyncIteable操作的异步接口放入这个循环。 +```js +let a = ''; +async function f(){ + for await (let b of req) { + a += b; + } + let c = JSON.parse(a); + console.log('leo', c); +} +``` +当`next`返回的Promise对象被`reject`,`for await...of`就会保错,用`try...catch`捕获。 +```js +async function f(){ + try{ + for await (let a of iterableObj()){ + console.log(a); + } + }catch(e){ + console.error(e); + } +} +``` +注意,`for await...of`循环也可以用于同步遍历器。 +```js +(async function () { + for await (let a of ['a', 'b']) { + console.log(a); + } +})(); +// a +// b +``` +### 3.3 异步Generator函数 +就像 Generator 函数返回一个同步遍历器对象一样,异步 Generator 函数的作用,是返回一个异步遍历器对象。 +在语法上,异步 Generator 函数就是`async`函数与 Generator 函数的结合。 +```js +async function* f() { + yield 'hi'; +} +const a = f(); +a.next().then(x => console.log(x)); +// { value: 'hello', done: false } +``` +设计异步遍历器的目的之一,就是为了让Generator函数能用同一套接口处理同步和异步操作。 +```js +// 同步Generator函数 +function * f(iterable, fun){ + let a = iterabl[Symbol.iterator](); + while(true){ + let {val, done} = a.next(); + if(done) break; + yield fun(val); + } +} + +// 异步Generator函数 +async function * f(iterable, fun){ + let a = iterabl[Symbol.iterator](); + while(true){ + let {val, done} = await a.next(); + if(done) break; + yield fun(val); + } +} +``` +同步和异步Generator函数相同点:在`yield`时用`next`方法停下,将后面表达式的值作为`next()`返回对象的`value`。 +在异步Generator函数中,同时使用`await`和`yield`,简单样理解,`await`命令用于将外部操作产生的值输入函数内部,`yield`命令用于将函数内部的值输出。 +```js +(async function () { + for await (const line of readLines(filePath)) { + console.log(line); + } +})() +``` +异步 Generator 函数可以与`for await...of`循环结合起来使用。 +```js +async function* f(asyncIterable) { + for await (const line of asyncIterable) { + yield '> ' + line; + } +} +``` + +### 3.4 yield* 语句 +`yield*`语句跟一个异步遍历器。 +```js +async function * f(){ + yield 'a'; + yield 'b'; + return 'leo'; +} +async function * g(){ + const a = yield* f(); // a => 'leo' +} +``` +与同步 Generator 函数一样,`for await...of`循环会展开`yield*`。 +```js +(async function () { + for await (const x of gen2()) { + console.log(x); + } +})(); +// a +// b +``` + +[⬆ 返回目录](#二目录) \ No newline at end of file diff --git a/Cute-JavaScript/Cute-ES/5.ES10.md b/Cute-JavaScript/Cute-ES/5.ES10.md new file mode 100644 index 00000000..0714e889 --- /dev/null +++ b/Cute-JavaScript/Cute-ES/5.ES10.md @@ -0,0 +1,258 @@ +最近 ECMAScript2019,最新提案完成:[tc39 Finished Proposals](https://github.com/tc39/proposals/blob/master/finished-proposals.md),我这里也是按照官方介绍的顺序进行整理,如有疑问,可以查看官方介绍啦~ + +另外之前也整理了 [《ES6/ES7/ES8/ES9系列》](https://juejin.im/post/5c02b106f265da61764aa0c1),可以一起看哈。 + +![20190901_es2019.png](http://images.pingan8787.com/20190901_es2019.png) + +## 1. 可选的 catch 绑定 + +### 1.1 介绍 + +在 ECMAScript2019 最新提案中,支持我们在使用 `try catch` 错误异常处理时,选择性的给 `catch` 传入参数。 + +即我们可以不传入 `catch` 参数。 + +正常使用 `try catch`: + +```js +try { + // todo +} catch (err){ + console.log('err:',err) +} +``` + +在 ES10 中可以这么使用: + +```js +try { + // todo +} catch { + // todo +} +``` + +### 1.2 使用场景 + +当我们不需要对 `chtch` 返回的错误信息进行处理时,比如:我们对于一些数据处理,经常会出现格式报错,但是我们并不关心这个错误,我们只需要继续处理,或重新请求数据等。 + +这种情况,我们就可以使用这个新特性,当然,还是需要根据实际情况考虑。 + + +## 2. JSON.superset + +### 2.1 介绍 + +* **来源背景**: + +由于在 ES2019 之前不支持转义**行分隔符** (`\u2028`) 和**段落分隔符** (`\u2029`) 字符,并且在解析过程中会报错: `SyntaxError: Invalid or unexpected token`。 + +```js +const LS = ""; +const PS = eval("'\u2029'");// SyntaxError: Invalid or unexpected token +``` + +* **解决方案**: + +JSON 语法由** ECMA-404** 定义并由 **RFC 7159** 永久修复,允许**行分隔符** (`\u2028`) 和**段落分隔符** (`\u2029`) 字符,直接出现在字符串中。 + + +### 2.2 使用 + +在 ES10 中,我们就可以直接使用 `eval("'\u2029'");` 而不会再提示错误。 + +## 3. Symbol.prototype.description + +### 3.1 介绍 + +在 ES6 中引入 Symbol 这个**基本数据类型**,可以实现一些数据内省等高级功能。 + +这次 ES10 中,为 Symbol 类型增加 `Symbol.prototype.description` 的一个访问器属性,用来获取 `Symbol` 类型数据的描述信息(description)。 + +### 3.2 使用 + +MDN 上的案例介绍: + +```js +console.log(Symbol('pingan8787').description); +// expected output: "pingan8787" + +console.log(Symbol.iterator.description); +// expected output: "Symbol.iterator" + +console.log(Symbol.for('leo').description); +// expected output: "leo" + +console.log(Symbol('pingan8787').description + ' and leo!'); +// expected output: "pingan8787 and leo!" +``` + +另外我们也可以这么使用: + +```js +let pingan = Symbol('pingan8787').description; +console.log(pingan === 'pingan8787'); // true +``` + +## 4. Function.prototype.toString + +### 4.1 介绍 + +在 ES10 之前,我们对一个函数调用 `toString()` 方法,返回的结果中会将注释信息去除。 + +在 ES10 之后,函数再调用 `toString()` 方法,将准确返回原有内容,包括**空格**和**注释**等: + +```js +let pingan8787 = function(){ + // do something + console.log('leo') +} +pingan8787.toString(); +/** +"function(){ + // do something + console.log('leo') +}" +*/ +``` + +## 5. Object.fromEntries + +### 5.1 介绍 + +`Object.fromEntries` 是 ES10 中新的静态方法,用于**将键值对列表转换为对象**。 + +`Object.fromEntries()` 方法接收一个**键值对的列表参数**,并返回一个带有这些键值对的**新对象**。 + +这个迭代参数应该是一个能够实现 `@iterator` 方法的的对象,返回一个迭代器对象。它生成一个具有两个元素的类数组的对象,第一个元素是**将用作属性键的值**,第二个元素是**与该属性键关联的值**。 + +`Object.fromEntries()` 是 `Object.entries` 的反转。 + +### 5.2 使用 + +* `Object.entries` 和 `Object.fromEntries()` 互转 + +```js +let leo = { name: 'pingan8787', age: 10}; +let arr = Object.entries(leo); +console.log(arr);// [["name", "pingan8787"],["age", 10]] + +let obj = Object.fromEntries(arr); +console.log(obj);// {name: "pingan8787", age: 10} +``` + +* `Map` 转化为 `Object` + +```js +const map = new Map([ ['name', 'pingan8787'], ['age', 10] ]); +const obj = Object.fromEntries(map); +console.log(obj); // {name: "pingan8787", age: 10} +``` + +* `Array` 转化为 `Object` + +```js +const arr = [ ['name', 'pingan8787'], ['age', 10] ]; +const obj = Object.fromEntries(arr); +console.log(obj); // {name: "pingan8787", age: 10} +``` + +## 6. 更友好的 JSON.stringify + +### 6.1 介绍 + +更友好的 `JSON.stringify`,对于一些超出范围的 Unicode 字符串,为其输出转义序列,使其成为有效 Unicode 字符串。 + + +### 6.2 使用 + +```js +// Non-BMP characters still serialize to surrogate pairs. +JSON.stringify('𝌆') +// → '"𝌆"' +JSON.stringify('\uD834\uDF06') +// → '"𝌆"' + +// Unpaired surrogate code units will serialize to escape sequences. +JSON.stringify('\uDF06\uD834') +// → '"\\udf06\\ud834"' +JSON.stringify('\uDEAD') +// → '"\\udead"' +``` + +## 7. String.prototype.{trimStart,trimEnd} + +### 7.1 String.prototype.trimStart + +`trimStart()` 方法从字符串的开头删除空格,返回一个**新字符串**,表示从其开头(左端)剥离空格的调用字符串,**不会直接修改原字符串本身**。 + +`trimLeft()`是此方法的别名。 + +```js +let pingan8787 = ' Hello pingan8787! '; +console.log(pingan8787); // " Hello pingan8787! "; +console.log(pingan8787.length); // 23; + +console.log(pingan8787.trimStart()); // "Hello pingan8787! "; +console.log(pingan8787.trimStart().length); // 20; + +``` + +### 7.2 String.prototype.trimEnd + +`trimEnd()` 方法从一个字符串的右端移除空白字符,返回一个**新字符串**,表示从其(右)端剥去空白的调用字符串,**不会直接修改原字符串本身**。 + +`trimRight()`是此方法的别名。 + +```js +let pingan8787 = ' Hello pingan8787! '; +console.log(pingan8787); // " Hello pingan8787! "; +console.log(pingan8787.length); // 23; + +console.log(pingan8787.trimEnd()); // " Hello pingan8787!"; +console.log(pingan8787.trimEnd().length); // 20; + +``` + +## 8. Array.prototype.{flat,flatMap} + +在 ES10 之前,我们要将一个数组打平,由于官方没有对应 API,我们可能需要 lodash +活着手写循环去操作。 + +### 8.1 Array.prototype.flat + +在 ES10 中,官方新增一个 `Array.prototype.flat` 方法,将数组第一层数据打平,也仅限第一层。如果我们需要将多层递归,则需要显式传入参数: + +```js +[1,2,3,[1,2,[3, [4]]]].flat(2); +// [1, 2, 3, 1, 2, 3, [4]] +``` + +### 8.2 Array.prototype.flatMap + +在 ES10 中,官方还增加了 `Array.prototype.flatMap` 方法,其实就是 `flat` 和 `map` 一起组合操作: + +```js +[1,3,5].map(x => [x * x]); // [[1],[9],[25]] + +[1,3,5].flatMap(x => [x * x]); // [1,9,25] +``` + + +## 参考文章: + +1.[ES2019 新特性简介](https://juejin.im/post/5c52a645f265da2ddc3c4bd8) +2.[ES2019 新特性简介](http://www.imooc.com/article/290813) + + + +|Author|王平安| +|---|---| +|E-mail|pingan8787@qq.com| +|博 客|www.pingan8787.com| +|微 信|pingan8787| +|每日文章推荐|https://github.com/pingan8787/Leo_Reading/issues| +|ES小册|js.pingan8787.com| + +## 微信公众号 +![bg](http://images.pingan8787.com/2019_07_12guild_page.png) \ No newline at end of file diff --git a/Cute-JavaScript/Cute-ES/ES_ALL.md b/Cute-JavaScript/Cute-ES/ES_ALL.md new file mode 100644 index 00000000..3d33dbff --- /dev/null +++ b/Cute-JavaScript/Cute-ES/ES_ALL.md @@ -0,0 +1,4778 @@ +**整理进度**: +- [x] 介绍 +- [x] 目录 +- [x] ES6 +- [x] ES7 +- [x] ES8 +- [x] ES9 +- [ ] 知识补充 +> 在思考,如何整理好这一份资料,让不同阶段的人,能有不同的收获。 + +**更新记录**: +...前面几天没有写记录。 +* 2018.10.24 添加**ES6**《**Symbol**》和《**Set和Map数据结构**》章节。 +* 2018.10.26 添加**ES6**《**Proxy**》章节。 +* 2018.10.29 添加**ES6**《**正则表达式**》《**Promise**》章节。 +* 2018.10.30 添加**ES6**《**Iterator**》章节。 +* 2018.10.31 添加**ES6**《**Generator**,**Module**》章节。 +* 2018.11.1 更新**ES6**《**Class**》章节。 +* 2018.11.2 完成**ES6**所有章节内容。 +* 2018.11.4 完成**ES7**章节内容。 +* 2018.11.5 完成**ES8**章节内容。 +* 2018.11.6 更新**ES9**《**对象的拓展运算符**》章节内容。 +* 2018.11.7 更新**ES9**《**正则表达式s修饰符**》章节。 +* 2018.11.8 更新**ES9**《**异步遍历器**》章节。 +* 2018.11.9 更新**ES9**完毕,转战vuepress整理一套。 +* 2018.11.29 修改《**Set和Map数据结构**》章节错误 书写,更新《Symbol》章节。 +* 2018.11.30 完成《Symbol》章节。 + +**未来规划**: +* [x] 1.将内容按不同模块拆分不同文件,方便README文件的阅读。 +* [ ] 2.添加案例项目。 + +# 一、介绍 +现在的网络上已经有各样关于 **ECMAScript** 规范介绍和分析的文章,而我自己重新学习一遍这些规范,整理出这么一份笔记,比较精简,主要内容涵盖**ES6**、**ES7**、**ES8**、**ES9**,后续会增加**面试题**,**框架入门**等笔记,欢迎吐槽交流。 +这份资料的**ES6部分**将会参考阮一峰老师的 [ECMAScript6入门](http://es6.ruanyifeng.com/) ,精简和整理出快速实用的内容。 +另外**ES7/ES8/ES9**则会从网络综合参考和整理。 + +**ES全称ECMAScript**: +目前JavaScript使用的ECMAScript版本为[ECMAScript-262](https://www.ecma-international.org/ecma-262/)。 + +| ECMAScript版本 | 发布时间 | 新增特性 | +| ------ | ------ | ------ | +| ECMAScript 2009(ES5) | 2009年11月 | 扩展了Object、Array、Function的功能等 | +| ECMAScript 2015(ES6) | 2015年6月 | 类,模块化,箭头函数,函数参数默认值等 | +| ECMAScript 2016(ES7) | 2016年3月 | includes,指数操作符 | +| ECMAScript 2017(ES8) | 2017年6月 | sync/await,Object.values(),Object.entries(),String padding等 | + +> 本文博客 [Cute-JavaScript](http://js.pingan8787.com) +> 本文开源地址 [Cute-JavaScript](https://github.com/pingan8787/Leo-JavaScript/tree/master/EXEFE-es6book) +> 个人博客 [ping'anの博客](http://www.pingan8787.com) +> Github地址 [pingan8787的github](https://github.com/pingan8787) +> 掘金主页 [pingan8787](https://juejin.im/user/586fc337a22b9d0058807d53) + +# 二、目录 + + +- [一、介绍](#一介绍) +- [二、目录](#二目录) +- [三、正文](#三正文) + - [1. ES6](#1-es6) + - [1.1 let 和 const命令](#11-let-和-const命令) + - [1.1.1 let 命令](#111-let-命令) + - [1.1.2 const 命令](#112-const-命令) + - [1.2 变量的解构赋值](#12-变量的解构赋值) + - [1.2.1 数组](#121-数组) + - [1.2.2 对象的解构赋值](#122-对象的解构赋值) + - [1.2.3 字符串的解构赋值](#123-字符串的解构赋值) + - [1.2.4 数值和布尔值的解构赋值](#124-数值和布尔值的解构赋值) + - [1.2.5 函数参数的解构赋值](#125-函数参数的解构赋值) + - [1.2.6 应用](#126-应用) + - [1.3 字符串的拓展](#13-字符串的拓展) + - [1.3.1 includes(),startsWith(),endsWith()](#131-includesstartswithendswith) + - [1.3.2 repeat()](#132-repeat) + - [1.3.3 padStart(),padEnd()](#133-padstartpadend) + - [1.3.4 模版字符串](#134-模版字符串) + - [1.4 正则的拓展](#14-正则的拓展) + - [1.4.1 介绍](#141-介绍) + - [1.4.2 字符串的正则方法](#142-字符串的正则方法) + - [1.4.3 u修饰符](#143-u修饰符) + - [1.4.4 y修饰符](#144-y修饰符) + - [1.4.5 flags属性](#145-flags属性) + - [1.5 数值的拓展](#15-数值的拓展) + - [1.5.1 Number.isFinite(), Number.isNaN()](#151-numberisfinite-numberisnan) + - [1.5.2 Number.parseInt(), Number.parseFloat()](#152-numberparseint-numberparsefloat) + - [1.5.3 Number.isInteger()](#153-numberisinteger) + - [1.5.4 Math对象的拓展](#154-math对象的拓展) + - [1.5.5 指数运算符](#155-指数运算符) + - [1.6 函数的拓展](#16-函数的拓展) + - [1.6.1 参数默认值](#161-参数默认值) + - [1.6.2 rest 参数](#162-rest-参数) + - [1.6.3 name 属性](#163-name-属性) + - [1.6.4 箭头函数](#164-箭头函数) + - [1.6.5 双冒号运算符](#165-双冒号运算符) + - [1.7 数组的拓展](#17-数组的拓展) + - [1.7.1 拓展运算符](#171-拓展运算符) + - [1.7.2 Array.from()](#172-arrayfrom) + - [1.7.3 Array.of()](#173-arrayof) + - [1.7.4 find()和findIndex()](#174-find和findindex) + - [1.7.5 fill()](#175-fill) + - [1.7.6 entries(),keys(),values()](#176-entrieskeysvalues) + - [1.7.7 includes()](#177-includes) + - [1.7.8 flat(),flatMap()](#178-flatflatmap) + - [1.8 对象的拓展](#18-对象的拓展) + - [1.8.1 属性的简洁表示](#181-属性的简洁表示) + - [1.8.2 属性名表达式](#182-属性名表达式) + - [1.8.3 Object.is()](#183-objectis) + - [1.8.4 Object.assign()](#184-objectassign) + - [1.9 Symbol](#19-symbol) + - [1.9.1 介绍](#191-介绍) + - [1.9.2 Symbol作为属性名](#192-symbol作为属性名) + - [1.9.3 应用:消除魔术字符串](#193-应用消除魔术字符串) + - [1.9.4 属性名遍历](#194-属性名遍历) + - [1.9.5 Symbol.for()、Symbol.keyFor()](#195-symbolforsymbolkeyfor) + - [1.9.6 内置的Symbol值](#196-内置的symbol值) + - [1.10 Set和Map数据结构](#110-set和map数据结构) + - [1.10.1 Set](#1101-set) + - [1.10.2 Set的应用](#1102-set的应用) + - [1.10.3 Map](#1103-map) + - [1.10.4 Map与其他数据结构互相转换](#1104-map与其他数据结构互相转换) + - [1.11 Proxy](#111-proxy) + - [1.11.1 基础使用](#1111-基础使用) + - [1.11.2 取消Proxy实例](#1112-取消proxy实例) + - [1.11.3 实现 Web服务的客户端](#1113-实现-web服务的客户端) + - [1.12 Promise对象](#112-promise对象) + - [1.12.1 概念](#1121-概念) + - [1.12.2 基本使用](#1122-基本使用) + - [1.12.3 Promise.prototype.then()](#1123-promiseprototypethen) + - [1.12.4 Promise.prototype.catch()](#1124-promiseprototypecatch) + - [1.12.5 Promise.prototype.finally()](#1125-promiseprototypefinally) + - [1.12.6 Promise.all()](#1126-promiseall) + - [1.12.7 Promise.race()](#1127-promiserace) + - [1.12.8 Promise.resolve()](#1128-promiseresolve) + - [1.12.9 Promise.reject()](#1129-promisereject) + - [1.13 Iterator和 for...of循环](#113-iterator和-forof循环) + - [1.13.1 Iterator遍历器概念](#1131-iterator遍历器概念) + - [1.13.2 Iterator遍历过程](#1132-iterator遍历过程) + - [1.13.3 默认Iterator接口](#1133-默认iterator接口) + - [1.13.4 Iterator使用场景](#1134-iterator使用场景) + - [1.13.5 for...of循环](#1135-forof循环) + - [1.13.6 跳出for...of](#1136-跳出forof) + - [1.14 Generator函数和应用](#114-generator函数和应用) + - [1.14.1 基本概念](#1141-基本概念) + - [1.14.2 yield表达式](#1142-yield表达式) + - [1.14.3 next方法](#1143-next方法) + - [1.14.4 for...of循环](#1144-forof循环) + - [1.14.5 Generator.prototype.throw()](#1145-generatorprototypethrow) + - [1.14.6 Generator.prototype.return()](#1146-generatorprototypereturn) + - [1.14.7 next()/throw()/return()共同点](#1147-nextthrowreturn共同点) + - [1.14.8 yield* 表达式](#1148-yield-表达式) + - [1.14.9 应用场景](#1149-应用场景) + - [1.15 Class语法和继承](#115-class语法和继承) + - [1.15.1 介绍](#1151-介绍) + - [1.15.2 constructor()方法](#1152-constructor方法) + - [1.15.3 类的实例对象](#1153-类的实例对象) + - [1.15.4 Class表达式](#1154-class表达式) + - [1.15.5 私有方法和私有属性](#1155-私有方法和私有属性) + - [1.15.6 this指向问题](#1156-this指向问题) + - [1.15.7 Class的getter和setter](#1157-class的getter和setter) + - [1.15.8 Class的generator方法](#1158-class的generator方法) + - [1.15.9 Class的静态方法](#1159-class的静态方法) + - [1.15.10 Class的静态属性和实例属性](#11510-class的静态属性和实例属性) + - [1.15.11 Class的继承](#11511-class的继承) + - [1.16 Module语法和加载实现](#116-module语法和加载实现) + - [1.16.1 介绍](#1161-介绍) + - [1.16.2 严格模式](#1162-严格模式) + - [1.16.3 export命令](#1163-export命令) + - [1.16.4 import命令](#1164-import命令) + - [1.16.5 模块的整体加载](#1165-模块的整体加载) + - [1.16.6 export default 命令](#1166-export-default-命令) + - [1.16.7 export 和 import 复合写法](#1167-export-和-import-复合写法) + - [1.16.8 浏览器中的加载规则](#1168-浏览器中的加载规则) + - [2. ES7](#2-es7) + - [2.1 Array.prototype.includes()方法](#21-arrayprototypeincludes方法) + - [2.2 指数操作符(**)](#22-指数操作符) + - [3. ES8](#3-es8) + - [3.1 async函数](#31-async函数) + - [3.1.1 介绍](#311-介绍) + - [3.1.2 基本用法](#312-基本用法) + - [3.1.3 返回Promise对象](#313-返回promise对象) + - [3.1.4 await命令](#314-await命令) + - [3.1.5 使用注意](#315-使用注意) + - [3.2 Promise.prototype.finally()](#32-promiseprototypefinally) + - [3.3 Object.values(),Object.entries()](#33-objectvaluesobjectentries) + - [3.3.1 Object.values()](#331-objectvalues) + - [3.3.2 Object.entries()](#332-objectentries) + - [3.4 Object.getOwnPropertyDescriptors()](#34-objectgetownpropertydescriptors) + - [3.5 字符串填充 padStart和padEnd](#35-字符串填充-padstart和padend) + - [3.6 函数参数列表与调用中的尾部逗号](#36-函数参数列表与调用中的尾部逗号) + - [3.7 共享内存与原子操作](#37-共享内存与原子操作) + - [4. ES9](#4-es9) + - [4.1 对象的拓展运算符](#41-对象的拓展运算符) + - [4.1.1 介绍](#411-介绍) + - [4.1.2 使用场景](#412-使用场景) + - [4.2 正则表达式 s 修饰符](#42-正则表达式-s-修饰符) + - [4.3 异步遍历器](#43-异步遍历器) + - [4.3.1 异步遍历的接口](#431-异步遍历的接口) + - [4.3.2 for await...of](#432-for-awaitof) + - [4.3.3 异步Generator函数](#433-异步generator函数) + - [4.3.4 yield* 语句](#434-yield-语句) + - [5. 知识补充](#5-知识补充) + - [5.1 块级作用域](#51-块级作用域) + - [5.2 ES5/6对数组空位的处理](#52-es56对数组空位的处理) +- [四、结语](#四结语) + - [参考文章](#参考文章) + - [推荐文章](#推荐文章) + - [微信公众号](#微信公众号) + + + +# 三、正文 +## 1. ES6 + +### 1.1 let 和 const命令 + +在ES6中,我们通常实用 `let` 表示**变量**,`const` 表示**常量**,并且 `let` 和 `const` 都是**块级作用域**,且在**当前作用域有效**不能重复声明。 + +#### 1.1.1 let 命令 +`let` 命令的用法和 `var` 相似,但是 `let` 只在所在代码块内有效。 +**基础用法**: +```js +{ + let a = 1; + let b = 2; +} +``` + +并且 `let` 有以下特点: + +* **不存在变量提升:** +在ES6之前,我们 `var` 声明一个**变量**一个**函数**,都会伴随着变量提升的问题,导致实际开发过程经常出现一些逻辑上的疑惑,按照一般思维习惯,变量都是需要先声明后使用。 +```js +// var +console.log(v1); // undefined +var v1 = 2; +// 由于变量提升 代码实际如下 +var v1; +console.log(v1) +v1 = 2; + +// let +console.log(v2); // ReferenceError +let v2 = 2; +``` + +* **不允许重复声明:** +`let` 和 `const` 在**相同作用域下**,都**不能重复声明同一变量**,并且**不能在函数内重新声明参数**。 +```js +// 1. 不能重复声明同一变量 +// 报错 +function f1 (){ + let a = 1; + var a = 2; +} +// 报错 +function f2 (){ + let a = 1; + let a = 2; +} + +// 2. 不能在函数内重新声明参数 +// 报错 +function f3 (a1){ + let a1; +} +// 不报错 +function f4 (a2){ + { + let a2 + } +} +``` + +#### 1.1.2 const 命令 +`const` 声明一个**只读**的**常量**。 +**基础用法**: +```js +const PI = 3.1415926; +console.log(PI); // 3.1415926 + +``` +**注意点**: +* `const` 声明后,无法修改值; +```js +const PI = 3.1415926; +PI = 3; +// TypeError: Assignment to constant variable. +``` +* `const` 声明时,必须赋值; +```js +const a ; +// SyntaxError: Missing initializer in const declaration. +``` +* `const` 声明的常量,`let` 不能重复声明; +```js +const PI = 3.1415926; +let PI = 0; +// Uncaught SyntaxError: Identifier 'PI' has already been declared +``` + +[⬆ 返回目录](#二目录) + +### 1.2 变量的解构赋值 +**解构赋值概念**:在ES6中,直接从数组和对象中取值,按照对应位置,赋值给变量的操作。 + +#### 1.2.1 数组 +**基础用法**: +```js +// ES6 之前 +let a = 1; +let b = 2; + +// ES6 之后 +let [a, b] = [1, 2]; +``` + +本质上,只要等号两边模式一致,左边变量即可获取右边对应位置的值,更多用法: + +```js +let [a, [[b], c]] = [1, [[2], 3]]; +console.log(a, b, c); // 1, 2, 3 + +let [ , , c] = [1, 2, 3]; +console.log(c); // 3 + +let [a, , c] = [1, 2, 3]; +console.log(a,c); // 1, 3 + +let [a, ...b] = [1, 2, 3]; +console.log(a,b); // 1, [2,3] + +let [a, b, ..c.] = [1]; +console.log(a, b, c); // 1, undefined, [] +``` + +**注意点**: +* 如果解构不成功,变量的值就等于`undefined`。 +```js +let [a] = []; // a => undefined +let [a, b] = [1]; // a => 1 , b => undefined +``` +* 当左边模式多于右边,也可以解构成功。 +```js +let [a, b] = [1, 2, 3]; +console.log(a, b); // 1, 2 +``` +* 两边模式不同,报错。 +```js +let [a] = 1; +let [a] = false; +let [a] = NaN; +let [a] = undefined; +let [a] = null; +let [a] = {}; +``` + +**指定解构的默认值**: +**基础用法**: +```js +let [a = 1] = []; // a => 1 +let [a, b = 2] = [a]; // a => 1 , b => 2 +``` +特殊情况: +```js +let [a = 1] = [undefined]; // a => 1 +let [a = 1] = [null]; // a => null +``` +右边模式对应的值,必须严格等于`undefined`,默认值才能生效,而`null`不严格等于`undefined`。 + +#### 1.2.2 对象的解构赋值 +与数组解构不同的是,对象解构**不需要严格按照顺序取值**,而只要按照**变量名**去取对应**属性名**的值,若取不到对应**属性名**的值,则为`undefined` 。 + +**基础用法**: +```js +let {a, b} = {a:1, b:2}; // a => 1 , b => 2 +let {a, b} = {a:2, b:1}; // a => 2 , b => 1 +let {a} = {a:3, b:2, c:1};// a => 3 +let {a} = {b:2, c:1}; // a => undefined +``` + +**注意点**: +* 若**变量名**和**属性名**不一致,则需要修改名称。 +```js +let {a:b} = {a:1, c:2}; +// error: a is not defined +// b => 1 +``` +对象的解构赋值的内部机制,是先找到同名属性,然后再赋给对应的变量。真正被赋值的是后者,而不是前者。 +上面代码中,`a` 是匹配的模式,`b`才是变量。真正被赋值的是变量`b`,而不是模式`a`。 + +* 对象解构也支持**嵌套解构**。 +```js +let obj = { + a:[ 1, { b: 2}] +}; +let {a, a: [c, {b}]} = obj; +// a=>[1, {b: 2}], b => 2, c => 1 +``` + +**指定解构的默认值**: +```js +let {a=1} = {}; // a => 1 +let {a, b=1} = {a:2}; // a => 2, b => 1 + +let {a:b=3} = {}; // b => 3 +let {a:b=3} = {a:4}; // b = >4 +// a是模式,b是变量 牢记 + +let {a=1} = {a:undefined}; // a => 1 +let {a=1} = {a:null}; // a => null +// 因为null与undefined不严格相等,所以赋值有效 +// 导致默认值1不会生效。 +``` + +#### 1.2.3 字符串的解构赋值 +字符串的解构赋值中,字符串被转换成了一个**类似数组的对象**。 +**基础用法**: +```js +const [a, b, c, d, e] = 'hello'; +a // "h" +b // "e" +c // "l" +d // "l" +e // "o" + +let {length:len} = 'hello';// len => 5 +``` + +#### 1.2.4 数值和布尔值的解构赋值 +解构赋值的规则是,**只要等号右边的值不是对象或数组,就先将其转为对象**。由于`undefined`和`null`**无法转为对象**,所以对它们进行解构赋值,都会报错。 +```js +// 数值和布尔值的包装对象都有toString属性 +let {toString: s} = 123; +s === Number.prototype.toString // true +let {toString: s} = true; +s === Boolean.prototype.toString // true + +let { prop: x } = undefined; // TypeError +let { prop: y } = null; // TypeError +``` + +#### 1.2.5 函数参数的解构赋值 +**基础用法**: +```js +function fun ([a, b]){ + return a + b; +} +fun ([1, 2]); // 3 +``` +**指定默认值的解构**: +```js +function fun ({a=0, b=0} = {}){ + return [a, b]; +} +fun ({a:1, b:2}); // [1, 2] +fun ({a:1}); // [1, 0] +fun ({}); // [0, 0] +fun (); // [0, 0] + +function fun ({a, b} = {a:0, b:0}){ + return [a, b]; +} +fun ({a:1, b:2}); // [1, 2] +fun ({a:1}); // [1, undefined] +fun ({}); // [undefined, undefined] +fun (); // [0, 0] +``` + +#### 1.2.6 应用 +* **交换变量的值**: +```js +let a = 1,b = 2; +[a, b] = [b, a]; // a =>2 , b => 1 +``` + +* **函数返回多个值**: +```js +// 返回一个数组 +function f (){ + return [1, 2, 3]; +} +let [a, b, c] = f(); // a=>1, b=>2, c=>3 + +// 返回一个对象 +function f (){ + return {a:1, b:2}; +} +let {a, b} = f(); // a=>1, b=>2 +``` + +* **快速对应参数**: +快速的将一组参数与变量名对应。 +```js +function f([a, b, c]) {...} +f([1, 2, 3]); + +function f({a, b, c}) {...} +f({b:2, c:3, a:1}); +``` + +* **提取JSON数据**: +```js +let json = { + name : 'leo', + age: 18 +} +let {name, age} = json; +console.log(name,age); // leo, 18 +``` + +* **遍历Map结构**: +```js +const m = new Map(); +m.set('a', 1); +m.set('b', 2); +for (let [k, v] of m){ + console.log(k + ' : ' + v); +} +// 获取键名 +for (let [k] of m){...} +// 获取键值 +for (let [,k] of m){...} +``` + +* **输入模块的指定方法**: +用于**按需加载**模块中需要用到的方法。 +```js +const {log, sin, cos} = require('math'); + +``` + +[⬆ 返回目录](#二目录) + + +### 1.3 字符串的拓展 +#### 1.3.1 includes(),startsWith(),endsWith() +在我们判断字符串是否包含另一个字符串时,ES6之前,我们只有`typeof`方法,ES6之后我们又多了三种方法: +* **includes()**:返回**布尔值**,表示**是否找到参数字符串**。 +* **startsWith()**:返回**布尔值**,表示参数字符串是否在原字符串的**头部**。 +* **endsWith()**:返回**布尔值**,表示参数字符串是否在原字符串的**尾部**。 +```js +let a = 'hello leo'; +a.startsWith('leo'); // false +a.endsWith('o'); // true +a.includes('lo'); // true +``` +并且这三个方法都支持第二个参数,表示起始搜索的位置。 +```js +let a = 'hello leo'; +a.startsWith('leo',1); // false +a.endsWith('o',5); // true +a.includes('lo',6); // false +``` +`endsWith` 是针对前 `n` 个字符,而其他两个是针对从第`n`个位置直到结束。 + +#### 1.3.2 repeat() +`repeat`方法返回一个新字符串,表示将原字符串重复`n`次。 +**基础用法**: +```js +'ab'.repeat(3); // 'ababab' +'ab'.repeat(0); // '' +``` +**特殊用法**: +* 参数为`小数`,则取整 +```js +'ab'.repeat(2.3); // 'abab' +``` +* 参数为`负数`或`Infinity`,则报错 +```js +'ab'.repeat(-1); // RangeError +'ab'.repeat(Infinity); // RangeError +``` +* 参数为`0到-1的小数`或`NaN`,则取0 +```js +'ab'.repeat(-0.5); // '' +'ab'.repeat(NaN); // '' +``` +* 参数为`字符串`,则转成`数字` +```js +'ab'.repeat('ab'); // '' +'ab'.repeat('3'); // 'ababab' +``` + +#### 1.3.3 padStart(),padEnd() +用于将字符串**头部**或**尾部**补全长度,`padStart()`为**头部补全**,`padEnd()`为**尾部补全**。 +这两个方法接收**2个**参数,第一个指定**字符串最小长度**,第二个**用于补全的字符串**。 +**基础用法** : +```js +'x'.padStart(5, 'ab'); // 'ababx' +'x'.padEnd(5, 'ab'); // 'xabab' +``` +**特殊用法**: +* 原字符串长度,大于或等于指定最小长度,则返回原字符串。 +```js +'xyzabc'.padStart(5, 'ab'); // 'xyzabc' +``` +* 用来补全的字符串长度和原字符串长度之和,超过指定最小长度,则截去超出部分的补全字符串。 +```js +'ab'.padStart(5,'012345'); // "012ab" +``` +* 省略第二个参数,则用`空格`补全。 +```js +'x'.padStart(4); // ' x' +'x'.padEnd(4); // 'x ' +``` +#### 1.3.4 模版字符串 +用于拼接字符串,ES6之前: +```js +let a = 'abc' + + 'def' + + 'ghi'; +``` +ES6之后: +```js +let a = ` + abc + def + ghi +` +``` +**拼接变量**: +在**反引号(\`)**中使用`${}`包裹变量或方法。 +```js +// ES6之前 +let a = 'abc' + v1 + 'def'; + +// ES6之后 +let a = `abc${v1}def` +``` + +[⬆ 返回目录](#二目录) + + +### 1.4 正则的拓展 +#### 1.4.1 介绍 +在ES5中有两种情况。 +* 参数是**字符串**,则第二个参数为正则表达式的修饰符。 +```js +let a = new RegExp('abc', 'i'); +// 等价于 +let a = /abx/i; +``` +* 参数是**正则表达式**,返回一个原表达式的拷贝,且不能有第二个参数,否则报错。 +```js +let a = new RegExp(/abc/i); +//等价于 +let a = /abx/i; + +let a = new RegExp(/abc/, 'i'); +// Uncaught TypeError +``` +ES6中使用: +第一个参数是正则对象,第二个是指定修饰符,如果第一个参数已经有修饰符,则会被第二个参数覆盖。 +```js +new RegExp(/abc/ig, 'i'); +``` + +#### 1.4.2 字符串的正则方法 +常用的四种方法:`match()`、`replace()`、`search()`和`split()`。 + +#### 1.4.3 u修饰符 +添加`u`修饰符,是为了处理大于`uFFFF`的Unicode字符,即正确处理四个字节的UTF-16编码。 +```js +/^\uD83D/u.test('\uD83D\uDC2A'); // false +/^\uD83D/.test('\uD83D\uDC2A'); // true +``` +由于ES5之前不支持四个字节UTF-16编码,会识别为两个字符,导致第二行输出`true`,加入`u`修饰符后ES6就会识别为一个字符,所以输出`false`。 + +**注意:** +加上`u`修饰符后,会改变下面正则表达式的行为: +* (1)点字符 +点字符(`.`)在正则中表示除了**换行符**以外的任意单个字符。对于码点大于`0xFFFF`的Unicode字符,点字符不能识别,必须加上`u`修饰符。 +```js +var a = "𠮷"; +/^.$/.test(a); // false +/^.$/u.test(a); // true +``` +* (2)Unicode字符表示法 +使用ES6新增的大括号表示Unicode字符时,必须在表达式添加`u`修饰符,才能识别大括号。 +```js +/\u{61}/.test('a'); // false +/\u{61}/u.test('a'); // true +/\u{20BB7}/u.test('𠮷'); // true +``` +* (3)量词 +使用`u`修饰符后,所有量词都会正确识别码点大于`0xFFFF`的 Unicode 字符。 +```js +/a{2}/.test('aa'); // true +/a{2}/u.test('aa'); // true +/𠮷{2}/.test('𠮷𠮷'); // false +/𠮷{2}/u.test('𠮷𠮷'); // true +``` +* (4)i修饰符 +不加`u`修饰符,就无法识别非规范的`K`字符。 +```js +/[a-z]/i.test('\u212A') // false +/[a-z]/iu.test('\u212A') // true +``` + +**检查是否设置`u`修饰符:** +使用`unicode`属性。 +```js +const a = /hello/; +const b = /hello/u; + +a.unicode // false +b.unicode // true +``` + +#### 1.4.4 y修饰符 +`y`修饰符与`g`修饰符类似,也是全局匹配,后一次匹配都是从上一次匹配成功的下一个位置开始。区别在于,`g`修饰符**只要**剩余位置中存在匹配即可,而`y`修饰符是必须从**剩余第一个**开始。 +```js +var s = 'aaa_aa_a'; +var r1 = /a+/g; +var r2 = /a+/y; + +r1.exec(s) // ["aaa"] +r2.exec(s) // ["aaa"] + +r1.exec(s) // ["aa"] 剩余 '_aa_a' +r2.exec(s) // null +``` +**`lastIndex`属性**: +指定匹配的开始位置: +```js +const a = /a/y; +a.lastIndex = 2; // 从2号位置开始匹配 +a.exec('wahaha'); // null +a.lastIndex = 3; // 从3号位置开始匹配 +let c = a.exec('wahaha'); +c.index; // 3 +a.lastIndex; // 4 +``` +**返回多个匹配**: +一个`y`修饰符对`match`方法只能返回第一个匹配,与`g`修饰符搭配能返回所有匹配。 +```js +'a1a2a3'.match(/a\d/y); // ["a1"] +'a1a2a3'.match(/a\d/gy); // ["a1", "a2", "a3"] +``` +**检查是否使用`y`修饰符**: +使用`sticky`属性检查。 +```js +const a = /hello\d/y; +a.sticky; // true +``` + +#### 1.4.5 flags属性 +`flags`属性返回所有正则表达式的修饰符。 +```js +/abc/ig.flags; // 'gi' +``` + + +[⬆ 返回目录](#二目录) + + +### 1.5 数值的拓展 +#### 1.5.1 Number.isFinite(), Number.isNaN() +`Number.isFinite()` 用于检查一个数值是否是有限的,即不是`Infinity`,若参数不是`Number`类型,则一律返回`false` 。 +```js +Number.isFinite(10); // true +Number.isFinite(0.5); // true +Number.isFinite(NaN); // false +Number.isFinite(Infinity); // false +Number.isFinite(-Infinity); // false +Number.isFinite('leo'); // false +Number.isFinite('15'); // false +Number.isFinite(true); // false +Number.isFinite(Math.random()); // true +``` + +`Number.isNaN()`用于检查是否是`NaN`,若参数不是`NaN`,则一律返回`false`。 +```js +Number.isNaN(NaN); // true +Number.isNaN(10); // false +Number.isNaN('10'); // false +Number.isNaN(true); // false +Number.isNaN(5/NaN); // true +Number.isNaN('true' / 0); // true +Number.isNaN('true' / 'true'); // true +``` + +**区别**: +与传统全局的`isFinite()`和`isNaN()`方法的区别,传统的这两个方法,是先将参数转换成**数值**,再判断。 +而ES6新增的这两个方法则只对**数值**有效, `Number.isFinite()`对于**非数值**一律返回`false`,` Number.isNaN()`只有对于`NaN`才返回`true`,其他一律返回`false`。 +```js +isFinite(25); // true +isFinite("25"); // true +Number.isFinite(25); // true +Number.isFinite("25"); // false + +isNaN(NaN); // true +isNaN("NaN"); // true +Number.isNaN(NaN); // true +Number.isNaN("NaN"); // false +``` + +#### 1.5.2 Number.parseInt(), Number.parseFloat() +这两个方法与全局方法`parseInt()`和`parseFloat()`一致,目的是逐步**减少全局性的方法**,让**语言更模块化**。 +```js +parseInt('12.34'); // 12 +parseFloat('123.45#'); // 123.45 + +Number.parseInt('12.34'); // 12 +Number.parseFloat('123.45#'); // 123.45 + +Number.parseInt === parseInt; // true +Number.parseFloat === parseFloat; // true +``` + +#### 1.5.3 Number.isInteger() +用来判断一个数值是否是整数,若参数不是数值,则返回`false`。 +```js +Number.isInteger(10); // true +Number.isInteger(10.0); // true +Number.isInteger(10.1); // false +``` + +#### 1.5.4 Math对象的拓展 +ES6新增17个数学相关的**静态方法**,只能在**Math对象**上调用。 +* **Math.trunc**: +用来去除小数的小数部分,**返回整数部分**。 +若参数为**非数值**,则**先转为数值**。 +若参数为**空值**或**无法截取整数的值**,则返回**NaN**。 +```js +// 正常使用 +Math.trunc(1.1); // 1 +Math.trunc(1.9); // 1 +Math.trunc(-1.1); // -1 +Math.trunc(-1.9); // -1 +Math.trunc(-0.1234); // -0 + +// 参数为非数值 +Math.trunc('11.22'); // 11 +Math.trunc(true); // 1 +Math.trunc(false); // 0 +Math.trunc(null); // 0 + +// 参数为空和无法取整 +Math.trunc(NaN); // NaN +Math.trunc('leo'); // NaN +Math.trunc(); // NaN +Math.trunc(undefined); // NaN +``` +**ES5实现**: +```js +Math.trunc = Math.trunc || function(x){ + return x < 0 ? Math.ceil(x) : Math.floor(x); +} +``` + +* **Math.sign()**: +判断一个数是**正数**、**负数**还**是零**,对于非数值,会先转成**数值**。 +返回值: + * 参数为正数, 返回 +1 + * 参数为负数, 返回 -1 + * 参数为0, 返回 0 + * 参数为-0, 返回 -0 + * 参数为其他值, 返回 NaN +```js +Math.sign(-1); // -1 +Math.sign(1); // +1 +Math.sign(0); // 0 +Math.sign(-0); // -0 +Math.sign(NaN); // NaN + +Math.sign(''); // 0 +Math.sign(true); // +1 +Math.sign(false);// 0 +Math.sign(null); // 0 +Math.sign('9'); // +1 +Math.sign('leo');// NaN +Math.sign(); // NaN +Math.sign(undefined); // NaN +``` + +**ES5实现** +```js +Math.sign = Math.sign || function (x){ + x = +x; + if (x === 0 || isNaN(x)){ + return x; + } + return x > 0 ? 1: -1; +} +``` + +* **Math.cbrt()**: +用来计算一个数的立方根,若参数为非数值则先转成数值。 +```js +Math.cbrt(-1); // -1 +Math.cbrt(0); // 0 +Math.cbrt(1); // 1 +Math.cbrt(2); // 1.2599210498 + +Math.cbrt('1'); // 1 +Math.cbrt('leo'); // NaN +``` +**ES5实现** +```js +Math.cbrt = Math.cbrt || function (x){ + var a = Math.pow(Math.abs(x), 1/3); + return x < 0 ? -y : y; +} +``` + +* **Math.clz32()**: +用于返回一个数的 32 位无符号整数形式有多少个前导 0。 +```js +Math.clz32(0) // 32 +Math.clz32(1) // 31 +Math.clz32(1000) // 22 +Math.clz32(0b01000000000000000000000000000000) // 1 +Math.clz32(0b00100000000000000000000000000000) // 2 +``` + +* **Math.imul()**: +用于返回两个数以 32 位带符号整数形式相乘的结果,返回的也是一个 32 位的带符号整数。 +```js +Math.imul(2, 4) // 8 +Math.imul(-1, 8) // -8 +Math.imul(-2, -2) // 4 +``` + +* **Math.fround()**: +用来返回一个数的**2位单精度浮点数**形式。 +```js +Math.fround(0) // 0 +Math.fround(1) // 1 +Math.fround(2 ** 24 - 1) // 16777215 +``` + +* **Math.hypot()**: +用来返回所有参数的平方和的**平方根**。 +```js +Math.hypot(3, 4); // 5 +Math.hypot(3, 4, 5); // 7.0710678118654755 +Math.hypot(); // 0 +Math.hypot(NaN); // NaN +Math.hypot(3, 4, 'foo'); // NaN +Math.hypot(3, 4, '5'); // 7.0710678118654755 +Math.hypot(-3); // 3 +``` + +* **Math.expm1()**: +用来返回` ex - 1`,即`Math.exp(x) - 1`。 +```js +Math.expm1(-1) // -0.6321205588285577 +Math.expm1(0) // 0 +Math.expm1(1) // 1.718281828459045 +``` +**ES5实现** +```js +Math.expm1 = Math.expm1 || function(x) { + return Math.exp(x) - 1; +}; +``` + +* **Math.log1p()**: +用来返回`1 + x`的自然对数,即`Math.log(1 + x)`。如果x小于`-1`,返回`NaN`。 +```js +Math.log1p(1) // 0.6931471805599453 +Math.log1p(0) // 0 +Math.log1p(-1) // -Infinity +Math.log1p(-2) // NaN +``` +**ES5实现** +```js +Math.log1p = Math.log1p || function(x) { + return Math.log(1 + x); +}; +``` + +* **Math.log10()**: +用来返回以 `10 `为底的`x的对数`。如果x小于 0,则返回 `NaN`。 +```js +Math.log10(2) // 0.3010299956639812 +Math.log10(1) // 0 +Math.log10(0) // -Infinity +Math.log10(-2) // NaN +Math.log10(100000) // 5 +``` +**ES5实现** +```js +Math.log10 = Math.log10 || function(x) { + return Math.log(x) / Math.LN10; +}; +``` + +* **Math.log2()**: +用来返回以 `2` 为底的`x的对数`。如果`x`小于` 0`,则返回 `NaN`。 +```js +Math.log2(3) // 1.584962500721156 +Math.log2(2) // 1 +Math.log2(1) // 0 +Math.log2(0) // -Infinity +Math.log2(-2) // NaN +Math.log2(1024) // 10 +Math.log2(1 << 29) // 29 +``` +**ES5实现** +```js +Math.log2 = Math.log2 || function(x) { + return Math.log(x) / Math.LN2; +}; +``` +* **双曲函数方法**: + * `Math.sinh(x)` 返回x的**双曲正弦**(hyperbolic sine) + * `Math.cosh(x)` 返回x的**双曲余弦**(hyperbolic cosine) + * `Math.tanh(x)` 返回x的**双曲正切**(hyperbolic tangent) + * `Math.asinh(x)` 返回x的**反双曲正弦**(inverse hyperbolic sine) + * `Math.acosh(x)` 返回x的**反双曲余弦**(inverse hyperbolic cosine) + * `Math.atanh(x)` 返回x的**反双曲正切**(inverse hyperbolic tangent) + +#### 1.5.5 指数运算符 +新增的指数运算符(`**`): +```js +2 ** 2; // 4 +2 ** 3; // 8 + +2 ** 3 ** 2; // 相当于 2 ** (3 ** 2); 返回 512 +``` +指数运算符(`**`)与`Math.pow`的实现不相同,对于特别大的运算结果,两者会有细微的差异。 +```js +Math.pow(99, 99) +// 3.697296376497263e+197 + +99 ** 99 +// 3.697296376497268e+197 +``` + +[⬆ 返回目录](#二目录) + + +### 1.6 函数的拓展 +#### 1.6.1 参数默认值 +```js +// ES6 之前 +function f(a, b){ + b = b || 'leo'; + console.log(a, b); +} + +// ES6 之后 +function f(a, b='leo'){ + console.log(a, b); +} + +f('hi'); // hi leo +f('hi', 'jack'); // hi jack +f('hi', ''); // hi leo +``` +**注意**: +* 参数变量是默认声明的,不能用`let`和`const`再次声明: +```js +function f (a = 1){ + let a = 2; // error +} +``` +* 使用参数默认值时,参数名不能相同: +```js +function f (a, a, b){ ... }; // 不报错 +function f (a, a, b = 1){ ... }; // 报错 +``` + +**与解构赋值默认值结合使用**: +```js +function f ({a, b=1}){ + console.log(a,b) +}; +f({}); // undefined 1 +f({a:2}); // 2 1 +f({a:2, b:3}); // 2 3 +f(); // 报错 + +function f ({a, b = 1} = {}){ + console.log(a, b) +} +f(); // undefined 1 +``` + +**尾参数定义默认值**: +通常在尾参数定义默认值,便于观察参数,并且非尾参数无法省略。 +```js +function f (a=1,b){ + return [a, b]; +} +f(); // [1, undefined] +f(2); // [2, undefined] +f(,2); // 报错 + +f(undefined, 2); // [1, 2] + +function f (a, b=1, c){ + return [a, b, c]; +} +f(); // [undefined, 1, undefined] +f(1); // [1,1,undefined] +f(1, ,2); // 报错 +f(1,undefined,2); // [1,1,2] +``` +在给参数传递默认值时,传入`undefined`会触发默认值,传入`null`不会触发。 +```js +function f (a = 1, b = 2){ + console.log(a, b); +} +f(undefined, null); // 1 null +``` + +**函数的length属性**: +`length`属性将返回,没有指定默认值的参数数量,并且rest参数不计入`length`属性。 +```js +function f1 (a){...}; +function f2 (a=1){...}; +function f3 (a, b=2){...}; +function f4 (...a){...}; +function f5 (a,b,...c){...}; + +f1.length; // 1 +f2.length; // 0 +f3.length; // 1 +f4.length; // 0 +f5.length; // 2 +``` + +#### 1.6.2 rest 参数 +`rest`参数形式为(`...变量名`),其值为一个数组,用于获取函数多余参数。 +```js +function f (a, ...b){ + console.log(a, b); +} +f(1,2,3,4); // 1 [2, 3, 4] +``` +**注意**: +* `rest`参数只能放在最后一个,否则报错: +```js +function f(a, ...b, c){...}; // 报错 +``` +* 函数的`length`属性不包含`rest`参数。 +```js +function f1 (a){...}; +function f2 (a,...b){...}; +f1(1); // 1 +f2(1,2); // 1 +``` + +#### 1.6.3 name 属性 +用于返回该函数的函数名。 +```js +function f (){...}; +f.name; // f + +const f = function g(){...}; +f.name; // g +``` + +#### 1.6.4 箭头函数 +使用“箭头”(`=>`)定义函数。 +**基础使用**: +```js +// 有1个参数 +let f = v => v; +// 等同于 +let f = function (v){return v}; + +// 有多个参数 +let f = (v, i) => {return v + i}; +// 等同于 +let f = function (v, i){return v + i}; + +// 没参数 +let f = () => 1; +// 等同于 +let f = function (){return 1}; +``` + +**箭头函数与变量结构结合使用**: +```js +// 正常函数写法 +function f (p) { + return p.a + ':' + p.b; +} + +// 箭头函数写法 +let f = ({a, b}) => a + ':' + b; +``` + +**简化回调函数**: +```js +// 正常函数写法 +[1, 2, 3].map(function (x){ + return x * x; +}) + + +// 箭头函数写法 +[1, 2, 3].map(x => x * x); +``` + +**箭头函数与rest参数结合**: +```js +let f = (...n) => n; +f(1, 2, 3); // [1, 2, 3] +``` + +**注意点**: +* 1.箭头函数内的`this`**总是**指向**定义时所在的对象**,而不是调用时。 +* 2.箭头函数不能当做**构造函数**,即不能用`new`命令,否则报错。 +* 3.箭头函数不存在`arguments`对象,即不能使用,可以使用`rest`参数代替。 +* 4.箭头函数不能使用`yield`命令,即不能用作**Generator**函数。 + +**不适用场景**: +* 1.在定义函数方法,且该方法内部包含`this`。 +```js +const obj = { + a:9, + b: () => { + this.a --; + } +} +``` +上述`b`如果是**普通函数**,函数内部的`this`指向`obj`,但是如果是箭头函数,则`this`会指向**全局**,不是预期结果。 + +* 2.需要动态`this`时。 +```js +let b = document.getElementById('myID'); +b.addEventListener('click', ()=>{ + this.classList.toggle('on'); +}) +``` +上诉按钮点击会报错,因为`b`监听的箭头函数中,`this`是全局对象,若改成**普通函数**,`this`就会指向被点击的按钮对象。 + +#### 1.6.5 双冒号运算符 +双冒号暂时是一个提案,用于解决一些不适用的场合,取代`call`、`apply`、`bind`调用。 +双冒号运算符(`::`)的左边是一个**对象**,右边是一个**函数**。该运算符会自动将左边的对象,作为上下文环境(即`this`对象),绑定到右边函数上。 +```js +f::b; +// 等同于 +b.bind(f); + +f::b(...arguments); +// 等同于 +b.apply(f, arguments); +``` +若双冒号左边为空,右边是一个对象的方法,则等于将该方法绑定到该对象上。 +```js +let f = a::a.b; +// 等同于 +let f = ::a.b; +``` + +[⬆ 返回目录](#二目录) + + +### 1.7 数组的拓展 +#### 1.7.1 拓展运算符 +拓展运算符使用(`...`),类似`rest`参数的逆运算,将数组转为用(`,`)分隔的参数序列。 +```js +console.log(...[1, 2, 3]); // 1 2 3 +console.log(1, ...[2,3], 4); // 1 2 3 4 +``` +拓展运算符主要使用在函数调用。 +```js +function f (a, b){ + console.log(a, b); +} +f(...[1, 2]); // 1 2 + +function g (a, b, c, d, e){ + console.log(a, b, c, d, e); +} +g(0, ...[1, 2], 3, ...[4]); // 0 1 2 3 4 +``` +**若拓展运算符后面是个空数组,则不产生效果**。 +```js +[...[], 1]; // [1] +``` + +**替代apply方法** +```js +// ES6之前 +function f(a, b, c){...}; +var a = [1, 2, 3]; +f.apply(null, a); + +// ES6之后 +function f(a, b, c){...}; +let a = [1, 2, 3]; +f(...a); + +// ES6之前 +Math.max.apply(null, [3,2,6]); + +// ES6之后 +Math.max(...[3,2,6]); +``` + +**拓展运算符的运用** +* **(1)复制数组**: +通常我们直接复制数组时,只是浅拷贝,如果要实现深拷贝,可以使用拓展运算符。 +```js +// 通常情况 浅拷贝 +let a1 = [1, 2]; +let a2 = a1; +a2[0] = 3; +console.log(a1,a2); // [3,2] [3,2] + +// 拓展运算符 深拷贝 +let a1 = [1, 2]; +let a2 = [...a1]; +// let [...a2] = a1; // 作用相同 +a2[0] = 3; +console.log(a1,a2); // [1,2] [3,2] +``` +* **(2)合并数组**: +注意,这里合并数组,只是浅拷贝。 +```js +let a1 = [1,2]; +let a2 = [3]; +let a3 = [4,5]; + +// ES5 +let a4 = a1.concat(a2, a3); + +// ES6 +let a5 = [...a1, ...a2, ...a3]; + +a4[0] === a1[0]; // true +a5[0] === a1[0]; // true +``` +* **(3)与解构赋值结合**: +与解构赋值结合生成数组,但是使用拓展运算符需要放到参数最后一个,否则报错。 +```js +let [a, ...b] = [1, 2, 3, 4]; +// a => 1 b => [2,3,4] + +let [a, ...b] = []; +// a => undefined b => [] + +let [a, ...b] = ["abc"]; +// a => "abc" b => [] +``` + +#### 1.7.2 Array.from() +将 **类数组对象** 和 **可遍历的对象**,转换成真正的数组。 +```js +// 类数组对象 +let a = { + '0':'a', + '1':'b', + length:2 +} +let arr = Array.from(a); + +// 可遍历的对象 +let a = Array.from([1,2,3]); +let b = Array.from({length: 3}); +let c = Array.from([1,2,3]).map(x => x * x); +let d = Array.from([1,2,3].map(x => x * x)); +``` + +#### 1.7.3 Array.of() +将一组数值,转换成**数组**,弥补`Array`方法参数不同导致的差异。 +```js +Array.of(1,2,3); // [1,2,3] +Array.of(1).length; // 1 + +Array(); // [] +Array(2); // [,] 1个参数时,为指定数组长度 +Array(1,2,3); // [1,2,3] 多于2个参数,组成新数组 +``` + +#### 1.7.4 find()和findIndex() +`find()`方法用于找出第一个符合条件的数组成员,参数为一个回调函数,所有成员依次执行该回调函数,返回第一个返回值为`true`的成员,如果没有一个符合则返回`undefined`。 +```js +[1,2,3,4,5].find( a => a < 3 ); // 1 +``` +回调函数接收三个参数,当前值、当前位置和原数组。 +```js +[1,2,3,4,5].find((value, index, arr) => { + // ... +}); +``` +`findIndex()`方法与`find()`类似,返回第一个符合条件的数组成员的**位置**,如果都不符合则返回`-1`。 +```js +[1,2,3,4].findIndex((v,i,a)=>{ + return v>2; +}); // 2 +``` + +#### 1.7.5 fill() +用于用指定值**填充**一个数组,通常用来**初始化空数组**,并抹去数组中已有的元素。 +```js +new Array(3).fill('a'); // ['a','a','a'] +[1,2,3].fill('a'); // ['a','a','a'] +``` +并且`fill()`的第二个和第三个参数指定填充的**起始位置**和**结束位置**。 +```js +[1,2,3].fill('a',1,2);// [1, "a", 3] +``` + +#### 1.7.6 entries(),keys(),values() +主要用于遍历数组,`entries()`对键值对遍历,`keys()`对键名遍历,`values()`对键值遍历。 +```js +for (let i of ['a', 'b'].keys()){ + console.log(i) +} +// 0 +// 1 + +for (let e of ['a', 'b'].values()){ + console.log(e) +} +// 'a' +// 'b' + +for (let e of ['a', 'b'].entries()){ + console.log(e) +} +// 0 'a' +// 1 'b' +``` + +#### 1.7.7 includes() +用于表示数组是否包含给定的值,与字符串的`includes`方法类似。 +```js +[1,2,3].includes(2); // true +[1,2,3].includes(4); // false +[1,2,NaN].includes(NaN); // true +``` +第二个参数为**起始位置**,默认为`0`,如果负数,则表示倒数的位置,如果大于数组长度,则重置为`0`开始。 +```js +[1,2,3].includes(3,3); // false +[1,2,3].includes(3,4); // false +[1,2,3].includes(3,-1); // true +[1,2,3].includes(3,-4); // true +``` + +#### 1.7.8 flat(),flatMap() +`flat()`用于将数组一维化,返回一个新数组,不影响原数组。 +默认一次只一维化一层数组,若需多层,则传入一个整数参数指定层数。 +若要一维化所有层的数组,则传入`Infinity`作为参数。 +```js +[1, 2, [2,3]].flat(); // [1,2,2,3] +[1,2,[3,[4,[5,6]]]].flat(3); // [1,2,3,4,5,6] +[1,2,[3,[4,[5,6]]]].flat('Infinity'); // [1,2,3,4,5,6] +``` +`flatMap()`是将原数组每个对象先执行一个函数,在对返回值组成的数组执行`flat()`方法,返回一个新数组,不改变原数组。 + `flatMap()`只能展开一层。 +```js +[2, 3, 4].flatMap((x) => [x, x * 2]); +// [2, 4, 3, 6, 4, 8] +``` + +[⬆ 返回目录](#二目录) + + +### 1.8 对象的拓展 +#### 1.8.1 属性的简洁表示 +```js +let a = 'a1'; +let b = { a }; // b => { a : 'a1' } +// 等同于 +let b = { a : a }; + +function f(a, b){ + return {a, b}; +} +// 等同于 +function f (a, b){ + return {a:a ,b:b}; +} + +let a = { + fun () { + return 'leo'; + } +} +// 等同于 +let a = { + fun : function(){ + return 'leo'; + } +} +``` + +#### 1.8.2 属性名表达式 +`JavaScript`提供2种方法**定义对象的属性**。 +```js +// 方法1 标识符作为属性名 +a.f = true; + +// 方法2 字符串作为属性名 +a['f' + 'un'] = true; +``` +延伸出来的还有: +```js +let a = 'hi leo'; +let b = { + [a]: true, + ['a'+'bc']: 123, + ['my' + 'fun'] (){ + return 'hi'; + } +}; +// b.a => undefined ; b.abc => 123 ; b.myfun() => 'hi' +// b[a] => true ; b['abc'] => 123 ; b['myfun'] => ƒ ['my' + 'fun'] (){ return 'hi'; } +``` +**注意**: +属性名表达式不能与简洁表示法同时使用,否则报错。 +```js +// 报错 +let a1 = 'aa'; +let a2 = 'bb'; +let b1 = {[a1]}; + +// 正确 +let a1 = 'aa'; +let b1 = { [a1] : 'bb'}; +``` + +#### 1.8.3 Object.is() +`Object.is()` 用于比较两个值是否严格相等,在ES5时候只要使用**相等运算符**(`==`)和**严格相等运算符**(`===`)就可以做比较,但是它们都有缺点,前者会**自动转换数据类型**,后者的`NaN`不等于自身,以及`+0`等于`-0`。 +```js +Object.is('a','a'); // true +Object.is({}, {}); // false + +// ES5 ++0 === -0 ; // true +NaN === NaN; // false + +// ES6 +Object.is(+0,-0); // false +Object.is(NaN,NaN); // true +``` + +#### 1.8.4 Object.assign() +`Object.assign()`方法用于对象的合并,将原对象的所有可枚举属性复制到目标对象。 +**基础用法**: +第一个参数是**目标对象**,后面参数都是**源对象**。 +```js +let a = {a:1}; +let b = {b:2}; +Object.assign(a,b); // a=> {a:1,b:2} +``` +**注意**: +* 若目标对象与源对象有同名属性,则后面属性会覆盖前面属性。 +```js +let a = {a:1, b:2}; +let b = {b:3, c:4}; +Object.assign(a, b); // a => {a:1, b:3, c:4} +``` +* 若只有**一个**参数,则返回该参数。 +```js +let a = {a:1}; +Object.assign(a) === a; // true +``` +* 若参数**不是对象**,则先转成对象后返回。 +```js +typeof Object.assign(2); // 'object' +``` +* 由于`undefined`或`NaN`无法转成对象,所以做为参数会报错。 +```js +Object.assign(undefined) // 报错 +Object.assign(NaN); // 报错 +``` +* `Object.assign()`实现的是浅拷贝。 + +`Object.assign()`拷贝得到的是这个对象的引用。这个对象的任何变化,都会反映到目标对象上面。 +```js +let a = {a: {b:1}}; +let b = Object.assign({},a); +a.a.b = 2; +console.log(b.a.b); // 2 +``` +* 将数组当做对象处理,键名为数组下标,键值为数组下标对应的值。 +```js +Object.assign([1, 2, 3], [4, 5]); // [4, 5, 3] +``` + +[⬆ 返回目录](#二目录) + + +### 1.9 Symbol +#### 1.9.1 介绍 +ES6引入`Symbol`作为一种新的**原始数据类型**,表示**独一无二**的值,主要是为了**防止属性名冲突**。 +ES6之后,JavaScript一共有其中数据类型:`Symbol`、`undefined`、`null`、`Boolean`、`String`、`Number`、`Object`。 +简单实用: +```js +let a = Symbol(); +typeof a; // "symbol" +``` +**注意:** +* `Symbol`函数不能用`new`,会报错。由于`Symbol`是一个原始类型,不是对象,所以不能添加属性,它是类似于字符串的数据类型。 +* `Symbol`都是不相等的,即使参数相同。 +```js +// 没有参数 +let a1 = Symbol(); +let a2 = Symbol(); +a1 === a2; // false + +// 有参数 +let a1 = Symbol('abc'); +let a2 = Symbol('abc'); +a1 === a2; // false +``` +* `Symbol`不能与其他类型的值计算,会报错。 +```js +let a = Symbol('hello'); +a + " world!"; // 报错 +`${a} world!`; // 报错 +``` +Symbol可以显式转换为字符串: +```js +let a1 = Symbol('hello'); + +String(a1); // "Symbol(hello)" +a1.toString(); // "Symbol(hello)" +``` +Symbol可以转换为布尔值,但不能转为数值: +```js +let a1 = Symbol(); +Boolean(a1); +!a1; // false + +Number(a1); // TypeError +a1 + 1 ; // TypeError +``` + +#### 1.9.2 Symbol作为属性名 +好处:防止同名属性,还有防止键被改写或覆盖。 +```js +let a1 = Symbol(); + +// 写法1 +let b = {}; +b[a1] = 'hello'; + +// 写法2 +let b = { + [a1] : 'hello' +} + +// 写法3 +let b = {}; +Object.defineProperty(b, a1, {value : 'hello' }); + +// 3种写法 结果相同 +b[a1]; // 'hello' +``` +**需要注意:** Symbol作为对象属性名时,不能用点运算符,并且必须放在方括号内。 +```js +let a = Symbol(); +let b = {}; + +// 不能用点运算 +b.a = 'hello'; +b[a] ; // undefined +b['a'] ; // 'hello' + +// 必须放在方括号内 +let c = { + [a] : function (text){ + console.log(text); + } +} +c[a]('leo'); // 'leo' + +// 上面等价于 更简洁 +let c = { + [a](text){ + console.log(text); + } +} +``` + +**常常还用于创建一组常量,保证所有值不相等:** +```js +let a = {}; +a.a1 = { + AAA: Symbol('aaa'), + BBB: Symbol('bbb'), + CCC: Symbol('ccc') +} +``` + +#### 1.9.3 应用:消除魔术字符串 +魔术字符串:指代码中多次出现,强耦合的字符串或数值,应该避免,而使用含义清晰的变量代替。 +```js +function f(a){ + if(a == 'leo') { + console.log('hello'); + } +} +f('leo'); // 'leo' 为魔术字符串 +``` +常使用变量,消除魔术字符串: +```js +let obj = { + name: 'leo' +}; +function f (a){ + if(a == obj.name){ + console.log('hello'); + } +} +f(obj.name); // 'leo' +``` +使用Symbol消除强耦合,使得不需关系具体的值: +```js +let obj = { + name: Symbol() +}; +function f (a){ + if(a == obj.name){ + console.log('hello'); + } +} +f(obj.name); +``` + +#### 1.9.4 属性名遍历 +Symbol作为属性名遍历,不出现在`for...in`、`for...of`循环,也不被`Object.keys()`、`Object.getOwnPropertyNames()`、`JSON.stringify()`返回。 +```js +let a = Symbol('aa'),b= Symbol('bb'); +let obj = { + [a]:'11', [b]:'22' +} +for(let k of Object.values(obj)){console.log(k)} +// 无输出 + +let obj = {}; +let aa = Symbol('leo'); +Object.defineProperty(obj, aa, {value: 'hi'}); + +for(let k in obj){ + console.log(k); // 无输出 +} + +Object.getOwnPropertyNames(obj); // [] +Object.getOwnPropertySymbols(obj); // [Symbol(leo)] +``` + +`Object.getOwnPropertySymbols`方法返回一个数组,包含当前对象所有用做属性名的Symbol值。 +```js +let a = {}; +let a1 = Symbol('a'); +let a2 = Symbol('b'); +a[a1] = 'hi'; +a[a2] = 'oi'; + +let obj = Object.getOwnPropertySymbols(a); +obj; //  [Symbol(a), Symbol(b)] +``` + +另外可以使用`Reflect.ownKeys`方法可以返回所有类型的键名,包括常规键名和 Symbol 键名。 +```js +let a = { + [Symbol('leo')]: 1, + aa : 2, + bb : 3, +} +Reflect.ownKeys(a); // ['aa', 'bb',Symbol('leo')] +``` + +由于Symbol值作为名称的属性不被常规方法遍历获取,因此常用于定义对象的一些非私有,且内部使用的方法。 + +#### 1.9.5 Symbol.for()、Symbol.keyFor() +* Symbol.for() +**用于重复使用一个Symbol值**,接收一个**字符串**作为参数,若存在用此参数作为名称的Symbol值,返回这个Symbol,否则新建并返回以这个参数为名称的Symbol值。 +```js +let a = Symbol.for('aaa'); +let b = Symbol.for('aaa'); + +a === b; // true +``` +`Symbol()` 和 `Symbol.for()`区别: +```js +Symbol.for('aa') === Symbol.for('aa'); // true +Symbol('aa') === Symbol('aa'); // false +``` + +* Symbol.keyFor() +**用于返回一个已使用的Symbol类型的key**: +```js +let a = Symbol.for('aa'); +Symbol.keyFor(a); // 'aa' + +let b = Symbol('aa'); +Symbol.keyFor(b); // undefined +``` + +#### 1.9.6 内置的Symbol值 +ES6提供11个内置的Symbol值,指向语言内部使用的方法: +* **1.Symbol.hasInstance** +当其他对象使用`instanceof`运算符,判断是否为该对象的实例时,会调用这个方法。比如,`foo instanceof Foo`在语言内部,实际调用的是`Foo[Symbol.hasInstance](foo)`。 +```js +class P { + [Symbol.hasInstance](a){ + return a instanceof Array; + } +} +[1, 2, 3] instanceof new P(); // true +``` +P是一个类,new P()会返回一个实例,该实例的`Symbol.hasInstance`方法,会在进行`instanceof`运算时自动调用,判断左侧的运算子是否为`Array`的实例。 + +* **2.Symbol.isConcatSpreadable** +值为布尔值,表示该对象用于`Array.prototype.concat()`时,是否可以展开。 +```js +let a = ['aa','bb']; +['cc','dd'].concat(a, 'ee'); +// ['cc', 'dd', 'aa', 'bb', 'ee'] +a[Symbol.isConcatSpreadable]; // undefined + +let b = ['aa','bb']; +b[Symbol.isConcatSpreadable] = false; +['cc','dd'].concat(b, 'ee'); +// ['cc', 'dd',[ 'aa', 'bb'], 'ee'] +``` + +* **3.Symbol.species** +指向一个构造函数,在创建衍生对象时会使用,使用时需要用`get`取值器。 +```js +class P extends Array { + static get [Symbol.species](){ + return this; + } +} +``` +解决下面问题: +```js +// 问题: b应该是 Array 的实例,实际上是 P 的实例 +class P extends Array{} + +let a = new P(1,2,3); +let b = a.map(x => x); + +b instanceof Array; // true +b instanceof P; // true + +// 解决: 通过使用 Symbol.species +class P extends Array { + static get [Symbol.species]() { return Array; } +} +let a = new P(); +let b = a.map(x => x); +b instanceof P; // false +b instanceof Array; // true +``` + +* **4.Symbol.match** +当执行`str.match(myObject)`,传入的属性存在时会调用,并返回该方法的返回值。 +```js +class P { + [Symbol.match](string){ + return 'hello world'.indexOf(string); + } +} +'h'.match(new P()); // 0 +``` + +* **5.Symbol.replace** +当该对象被`String.prototype.replace`方法调用时,会返回该方法的返回值。 +```js +let a = {}; +a[Symbol.replace] = (...s) => console.log(s); +'Hello'.replace(a , 'World') // ["Hello", "World"] +``` + +* **6.Symbol.hasInstance** +当该对象被`String.prototype.search`方法调用时,会返回该方法的返回值。 +```js +class P { + constructor(val) { + this.val = val; + } + [Symbol.search](s){ + return s.indexOf(this.val); + } +} +'hileo'.search(new P('leo')); // 2 +``` + +* **7.Symbol.split** +当该对象被`String.prototype.split`方法调用时,会返回该方法的返回值。 +```js +// 重新定义了字符串对象的split方法的行为 +class P { + constructor(val) { + this.val = val; + } + [Symbol.split](s) { + let i = s.indexOf(this.val); + if(i == -1) return s; + return [ + s.substr(0, i), + s.substr(i + this.val.length) + ] + } +} + +'helloworld'.split(new P('hello')); // ["hello", ""] +'helloworld'.split(new P('world')); // ["", "world"] +'helloworld'.split(new P('leo')); // "helloworld" +``` + +* **8.Symbol.iterator** +对象进行`for...of`循环时,会调用`Symbol.iterator`方法,返回该对象的默认遍历器。 +```js +class P { + *[Symbol.interator]() { + let i = 0; + while(this[i] !== undefined ) { + yield this[i]; + ++i; + } + } +} +let a = new P(); +a[0] = 1; +a[1] = 2; + +for (let k of a){ + console.log(k); +} +``` + +* **9.Symbol.toPrimitive** +该对象被转为原始类型的值时,会调用这个方法,返回该对象对应的原始类型值。调用时,需要接收一个字符串参数,表示当前运算模式,运算模式有: + * Number : 此时需要转换成数值 + * String : 此时需要转换成字符串 + * Default : 此时可以转换成数值或字符串 +```js +let obj = { + [Symbol.toPrimitive](hint) { + switch (hint) { + case 'number': + return 123; + case 'string': + return 'str'; + case 'default': + return 'default'; + default: + throw new Error(); + } + } +}; + +2 * obj // 246 +3 + obj // '3default' +obj == 'default' // true +String(obj) // 'str' +``` + +* **10.Symbol.toStringTag** +在该对象上面调用`Object.prototype.toString`方法时,如果这个属性存在,它的返回值会出现在`toString`方法返回的字符串之中,表示对象的类型。也就是说,这个属性可以用来定制`[object Object`]或`[object Array]`中`object`后面的那个字符串。 +```js +// 例一 +({[Symbol.toStringTag]: 'Foo'}.toString()) +// "[object Foo]" + +// 例二 +class Collection { + get [Symbol.toStringTag]() { + return 'xxx'; + } +} +let x = new Collection(); +Object.prototype.toString.call(x) // "[object xxx]" +``` + +* **11.Symbol.unscopables** +该对象指定了使用with关键字时,哪些属性会被with环境排除。 +```js +// 没有 unscopables 时 +class MyClass { + foo() { return 1; } +} + +var foo = function () { return 2; }; + +with (MyClass.prototype) { + foo(); // 1 +} + +// 有 unscopables 时 +class MyClass { + foo() { return 1; } + get [Symbol.unscopables]() { + return { foo: true }; + } +} + +var foo = function () { return 2; }; + +with (MyClass.prototype) { + foo(); // 2 +} +``` +上面代码通过指定`Symbol.unscopables`属性,使得`with`语法块不会在当前作用域寻找`foo`属性,即`foo`将指向外层作用域的变量。 + + +[⬆ 返回目录](#二目录) + + +### 1.10 Set和Map数据结构 +#### 1.10.1 Set +**介绍**: +`Set`数据结构类似数组,但所有成员的值**唯一**。 +`Set`本身为一个构造函数,用来生成`Set`数据结构,使用`add`方法来添加新成员。 +```js +let a = new Set(); +[1,2,2,1,3,4,5,4,5].forEach(x=>a.add(x)); +for(let k of a){ + console.log(k) +}; +// 1 2 3 4 5 +``` +**基础使用**: +```js +let a = new Set([1,2,3,3,4]); +[...a]; // [1,2,3,4] +a.size; // 4 + +// 数组去重 +[...new Set([1,2,3,4,4,4])];// [1,2,3,4] +``` + +**注意**: +* 向`Set`中添加值的时候,不会类型转换,即`5`和`'5'`是不同的。 +```js +[...new Set([5,'5'])]; // [5, "5"] +``` + +**属性和方法**: +* 属性: + * `Set.prototype.constructor`:构造函数,默认就是`Set`函数。 + * `Set.prototype.size`:返回`Set`实例的成员总数。 + +* 操作方法: + * `add(value)`:添加某个值,返回 Set 结构本身。 + * `delete(value)`:删除某个值,返回一个布尔值,表示删除是否成功。 + * `has(value)`:返回一个布尔值,表示该值是否为Set的成员。 + * `clear()`:清除所有成员,没有返回值。 + +```js +let a = new Set(); +a.add(1).add(2); // a => Set(2) {1, 2} +a.has(2); // true +a.has(3); // false +a.delete(2); // true a => Set(1) {1} +a.clear(); // a => Set(0) {} +``` +**数组去重**: +```js +let a = new Set([1,2,3,3,3,3]); +``` +#### 1.10.2 Set的应用 +**数组去重**: +```js +// 方法1 +[...new Set([1,2,3,4,4,4])]; // [1,2,3,4] +// 方法2 +Array.from(new Set([1,2,3,4,4,4])); // [1,2,3,4] +``` +**遍历和过滤**: +```js +let a = new Set([1,2,3,4]); + +// map 遍历操作 +let b = new Set([...a].map(x =>x*2));// b => Set(4) {2,4,6,8} + +// filter 过滤操作 +let c = new Set([...a].filter(x =>(x%2) == 0)); // b => Set(2) {2,4} +``` +**获取并集、交集和差集**: +```js +let a = new Set([1,2,3]); +let b = new Set([4,3,2]); + +// 并集 +let c1 = new Set([...a, ...b]); // Set {1,2,3,4} + +// 交集 +let c2 = new Set([...a].filter(x => b.has(x))); // set {2,3} + +// 差集 +let c3 = new Set([...a].filter(x => !b.has(x))); // set {1} +``` + +* 遍历方法: + * `keys()`:返回**键名**的遍历器。 + * `values()`:返回**键值**的遍历器。 + * `entries()`:返回**键值对**的遍历器。 + * `forEach()`:使用回调函数遍历**每个成员**。 + +`Set`遍历顺序是**插入顺序**,当保存多个回调函数,只需按照顺序调用。但由于`Set`结构**没有键名只有键值**,所以`keys()`和`values()`是返回结果相同。 +```js +let a = new Set(['a','b','c']); +for(let i of a.keys()){console.log(i)}; // 'a' 'b' 'c' +for(let i of a.values()){console.log(i)}; // 'a' 'b' 'c' +for(let i of a.entries()){console.log(i)}; +// ['a','a'] ['b','b'] ['c','c'] +``` +并且 还可以使用`for...of`直接遍历`Set`。 +```js +let a = new Set(['a','b','c']); +for(let k of a){console.log(k)}; // 'a' 'b' 'c' +``` +`forEach`与数组相同,对每个成员执行操作,且无返回值。 +```js +let a = new Set(['a','b','c']); +a.forEach((v,k) => console.log(k + ' : ' + v)); +``` + + +#### 1.10.3 Map +由于传统的`JavaScript`对象只能用字符串当做键,给开发带来很大限制,ES6增加`Map`数据结构,使得**各种类型的值**(包括对象)都可以作为键。 +`Map`结构提供了“**值—值**”的对应,是一种更完善的 Hash 结构实现。 +**基础使用**: +```js +let a = new Map(); +let b = {name: 'leo' }; +a.set(b,'my name'); // 添加值 +a.get(b); // 获取值 +a.size; // 获取总数 +a.has(b); // 查询是否存在 +a.delete(b); // 删除一个值 +a.clear(); // 清空所有成员 无返回 +``` +**注意**: +* 传入数组作为参数,**指定键值对的数组**。 +```js +let a = new Map([ + ['name','leo'], + ['age',18] +]) +``` +* 如果对同一个键**多次赋值**,后面的值将**覆盖前面的值**。 +```js +let a = new Map(); +a.set(1,'aaa').set(1,'bbb'); +a.get(1); // 'bbb' +``` +* 如果读取一个未知的键,则返回`undefined`。 +```js +new Map().get('abcdef'); // undefined +``` +* **同样的值**的两个实例,在 Map 结构中被视为两个键。 +```js +let a = new Map(); +let a1 = ['aaa']; +let a2 = ['aaa']; +a.set(a1,111).set(a2,222); +a.get(a1); // 111 +a.get(a2); // 222 +``` +**遍历方法**: +Map 的遍历顺序就是插入顺序。 +* `keys()`:返回键名的遍历器。 +* `values()`:返回键值的遍历器。 +* `entries()`:返回所有成员的遍历器。 +* `forEach()`:遍历 Map 的所有成员。 +```js +let a = new Map([ + ['name','leo'], + ['age',18] +]) + +for (let i of a.keys()){...}; +for (let i of a.values()){...}; +for (let i of a.entries()){...}; +a.forEach((v,k,m)=>{ + console.log(`key:${k},value:${v},map:${m}`) +}) +``` +**将Map结构转成数组结构**: +```js +let a = new Map([ + ['name','leo'], + ['age',18] +]) + +let a1 = [...a.keys()]; // a1 => ["name", "age"] +let a2 = [...a.values()]; // a2 =>  ["leo", 18] +let a3 = [...a.entries()];// a3 => [['name','leo'], ['age',18]] +``` + +#### 1.10.4 Map与其他数据结构互相转换 +* Map 转 数组 +```js +let a = new Map().set(true,1).set({f:2},['abc']); +[...a]; // [[true:1], [ {f:2},['abc'] ]] +``` +* 数组 转 Map +```js +let a = [ ['name','leo'], [1, 'hi' ]] +let b = new Map(a); +``` +* Map 转 对象 +如果所有 Map 的键都是字符串,它可以无损地转为对象。 +如果有非字符串的键名,那么这个键名会被转成字符串,再作为对象的键名。 +```js +function fun(s) { + let obj = Object.create(null); + for (let [k,v] of s) { + obj[k] = v; + } + return obj; +} + +const a = new Map().set('yes', true).set('no', false); +fun(a) +// { yes: true, no: false } +``` +* 对象 转 Map +```js +function fun(obj) { + let a = new Map(); + for (let k of Object.keys(obj)) { + a.set(k, obj[k]); + } + return a; +} + +fun({yes: true, no: false}) +// Map {"yes" => true, "no" => false} +``` + +* Map 转 JSON +**(1)Map键名都是字符串,转为对象JSON:** +```js +function fun (s) { + let obj = Object.create(null); + for (let [k,v] of s) { + obj[k] = v; + } + return JSON.stringify(obj) +} +let a = new Map().set('yes', true).set('no', false); +fun(a); +// '{"yes":true,"no":false}' +``` +**(2)Map键名有非字符串,转为数组JSON:** +```js +function fun (map) { + return JSON.stringify([...map]); +} + +let a = new Map().set(true, 7).set({foo: 3}, ['abc']); +fun(a) +// '[[true,7],[{"foo":3},["abc"]]]' +``` +* JSON 转 Map +**(1)所有键名都是字符串:** +```js +function fun (s) { + let strMap = new Map(); + for (let k of Object.keys(s)) { + strMap.set(k, s[k]); + } + return strMap; + return JSON.parse(strMap); +} +fun('{"yes": true, "no": false}') +// Map {'yes' => true, 'no' => false} +``` +**(2)整个 JSON 就是一个数组,且每个数组成员本身,又是一个有两个成员的数组**: +```js +function fun2(s) { + return new Map(JSON.parse(s)); +} +fun2('[[true,7],[{"foo":3},["abc"]]]') +// Map {true => 7, Object {foo: 3} => ['abc']} +``` + +[⬆ 返回目录](#二目录) + + +### 1.11 Proxy +`proxy` 用于修改某些操作的**默认行为**,可以理解为一种拦截外界对目标对象访问的一种机制,从而对外界的访问进行过滤和修改,即代理某些操作,也称“**代理器**”。 +#### 1.11.1 基础使用 +`proxy`实例化需要传入两个参数,`target`参数表示所要拦截的目标对象,`handler`参数也是一个对象,用来定制拦截行为。 +```js +let p = new Proxy(target, handler); + +let a = new Proxy({}, { + get: function (target, handler){ + return 'leo'; + } +}) +a.name; // leo +a.age; // leo +a.abcd; // leo +``` +上述`a`实例中,在第二个参数中定义了`get`方法,来拦截外界访问,并且`get`方法接收两个参数,分别是**目标对象**和**所要访问的属性**,所以不管外部访问对象中任何属性都会执行`get`方法返回`leo`。 +**注意**: +* 只能使用`Proxy`实例的对象才能使用这些操作。 +* 如果`handler`没有设置拦截,则直接返回原对象。 +```js +let target = {}; +let handler = {}; +let p = new Proxy(target, handler); +p.a = 'leo'; +target.a; // 'leo' +``` +**同个拦截器函数,设置多个拦截操作**: +```js +let p = new Proxy(function(a, b){ + return a + b; +},{ + get:function(){ + return 'get方法'; + }, + apply:function(){ + return 'apply方法'; + } +}) +``` +这里还有一个简单的案例: +```js +let handler = { + get : function (target, name){ + return name in target ? target[name] : 16; + } +} + +let p = new Proxy ({}, handler); +p.a = 1; +console.log(p.a , p.b); +// 1 16 +``` +这里因为 `p.a = 1` 定义了`p`中的`a`属性,值为`1`,而没有定义`b`属性,所以`p.a`会得到`1`,而`p.b`会得到`undefined`从而使用`name in target ? target[name] : 16;`返回的默认值`16`; + +**`Proxy`支持的13种拦截操作**: +13种拦截操作的详细介绍:[打开阮一峰老师的链接](http://es6.ruanyifeng.com/#docs/proxy)。 +* `get(target, propKey, receiver)`: +拦截对象属性的读取,比如proxy.foo和proxy['foo']。 + +* `set(target, propKey, value, receiver)`: +拦截对象属性的设置,比如proxy.foo = v或proxy['foo'] = v,返回一个布尔值。 + +* `has(target, propKey)`: +拦截propKey in proxy的操作,返回一个布尔值。 + +* `deleteProperty(target, propKey)`: +拦截delete proxy[propKey]的操作,返回一个布尔值。 + +* `ownKeys(target)`: +拦截Object.getOwnPropertyNames(proxy)、Object.getOwnPropertySymbols(proxy)、Object.keys(proxy)、for...in循环,返回一个数组。该方法返回目标对象所有自身的属性的属性名,而Object.keys()的返回结果仅包括目标对象自身的可遍历属性。 + +* `getOwnPropertyDescriptor(target, propKey)`: +拦截Object.getOwnPropertyDescriptor(proxy, propKey),返回属性的描述对象。 + +* `defineProperty(target, propKey, propDesc)`: +拦截Object.defineProperty(proxy, propKey, propDesc)、Object.defineProperties(proxy, propDescs),返回一个布尔值。 + +* `preventExtensions(target)`: +拦截Object.preventExtensions(proxy),返回一个布尔值。 + +* `getPrototypeOf(target)`: +拦截Object.getPrototypeOf(proxy),返回一个对象。 + +* `isExtensible(target)`: +拦截Object.isExtensible(proxy),返回一个布尔值。 + +* `setPrototypeOf(target, proto)`: +拦截Object.setPrototypeOf(proxy, proto),返回一个布尔值。如果目标对象是函数,那么还有两种额外操作可以拦截。 + +* `apply(target, object, args)`: +拦截 Proxy 实例作为函数调用的操作,比如proxy(...args)、proxy.call(object, ...args)、proxy.apply(...)。 + +* `construct(target, args)`: +拦截 Proxy 实例作为构造函数调用的操作,比如new proxy(...args)。 + +#### 1.11.2 取消Proxy实例 +使用`Proxy.revocale`方法取消`Proxy`实例。 +```js +let a = {}; +let b = {}; +let {proxy, revoke} = Proxy.revocale(a, b); + +proxy.name = 'leo'; // 'leo' +revoke(); +proxy.name; // TypeError: Revoked +``` + +#### 1.11.3 实现 Web服务的客户端 +```js +const service = createWebService('http://le.com/data'); +service.employees().than(json =>{ + const employees = JSON.parse(json); +}) + +function createWebService(url){ + return new Proxy({}, { + get(target, propKey, receiver{ + return () => httpGet(url+'/'+propKey); + }) + }) +} +``` + +### 1.12 Promise对象 +#### 1.12.1 概念 +主要用途:**解决异步编程带来的回调地狱问题**。 +把`Promise`简单理解一个容器,存放着某个未来才会结束的事件(通常是一个异步操作)的结果。通过`Promise`对象来获取异步操作消息,处理各种异步操作。 + +**`Promise`对象2特点**: +* **对象的状态不受外界影响**。 +> `Promise`对象代表一个异步操作,有三种状态:**pending(进行中)**、**fulfilled(已成功)**和**rejected(已失败)**。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。这也是`Promise`这个名字的由来,它的英语意思就是“承诺”,表示其他手段无法改变。 + +* **一旦状态改变,就不会再变,任何时候都可以得到这个结果**。 +> Promise对象的状态改变,只有两种可能:从**pending**变为**fulfilled**和从**pending**变为**rejected**。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果,这时就称为 **resolved**(已定型)。如果改变已经发生了,你再对**Promise**对象添加回调函数,也会立即得到这个结果。这与事件(Event)完全不同,事件的特点是,如果你错过了它,再去监听,是得不到结果的。 + +注意,为了行文方便,本章后面的`resolve`d统一只指`fulfilled`状态,不包含`rejected`状态。 + +**`Promise`缺点** +* **无法取消**Promise,一旦新建它就会立即执行,无法中途取消。 +* 如果不设置回调函数,Promise内部抛出的错误,不会反应到外部。 +* 当处于pending状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。 + +#### 1.12.2 基本使用 +`Promise`为一个构造函数,需要用`new`来实例化。 +```js +let p = new Promise(function (resolve, reject){ + if(/*异步操作成功*/){ + resolve(value); + } else { + reject(error); + } +}) +``` +`Promise`接收一个函数作为参数,该函数两个参数`resolve`和`reject`,有JS引擎提供。 +* `resolve`作用是将`Promise`的状态从pending变成resolved,在异步操作成功时调用,返回异步操作的结果,作为参数传递出去。 +* `reject`作用是将`Promise`的状态从pending变成rejected,在异步操作失败时报错,作为参数传递出去。 + +`Promise`实例生成以后,可以用`then`方法分别指定`resolved`状态和`rejected`状态的回调函数。 +```js +p.then(function(val){ + // success... +},function(err){ + // error... +}) +``` + +**几个例子来理解** : +* 当一段时间过后,`Promise`状态便成为`resolved`触发`then`方法绑定的回调函数。 +```js +function timeout (s){ + return new Promise((resolve, reject){ + setTimeout(result,ms, 'done'); + }) +} +timeout(100).then(val => { + console.log(val); +}) +``` + +* `Promise`新建后立刻执行。 +```js +let p = new Promise(function(resolve, reject){ + console.log(1); + resolve(); +}) +p.then(()=>{ + console.log(2); +}) +console.log(3); +// 1 +// 3 +// 2 +``` + +**异步加载图片**: +```js +function f(url){ + return new Promise(function(resolve, reject){ + const img = new Image (); + img.onload = function(){ + resolve(img); + } + img.onerror = function(){ + reject(new Error( + 'Could not load image at ' + url + )); + } + img.src = url; + }) +} +``` + +**`resolve`函数和`reject`函数的参数为`resolve`函数或`reject`函数**: +`p1`的状态决定了`p2`的状态,所以`p2`要等待`p1`的结果再执行回调函数。 +```js +const p1 = new Promise(function (resolve, reject) { + setTimeout(() => reject(new Error('fail')), 3000) +}) + +const p2 = new Promise(function (resolve, reject) { + setTimeout(() => resolve(p1), 1000) +}) + +p2 + .then(result => console.log(result)) + .catch(error => console.log(error)) +// Error: fail +``` + +**调用`resolve`或`reject`不会结束`Promise`参数函数的执行,除了`return`**: +```js +new Promise((resolve, reject){ + resolve(1); + console.log(2); +}).then(r => { + console.log(3); +}) +// 2 +// 1 + +new Promise((resolve, reject){ + return resolve(1); + console.log(2); +}) +// 1 +``` + +#### 1.12.3 Promise.prototype.then() +作用是为`Promise`添加状态改变时的回调函数,`then`方法的第一个参数是`resolved`状态的回调函数,第二个参数(可选)是`rejected`状态的回调函数。 +`then`方法返回一个新`Promise`实例,与原来`Promise`实例不同,因此可以使用链式写法,上一个`then`的结果作为下一个`then`的参数。 +```js +getJSON("/posts.json").then(function(json) { + return json.post; +}).then(function(post) { + // ... +}); +``` + +#### 1.12.4 Promise.prototype.catch() +`Promise.prototype.catch`方法是`.then(null, rejection)`的别名,用于指定发生错误时的回调函数。 +```js +getJSON('/posts.json').then(function(posts) { + // ... +}).catch(function(error) { + // 处理 getJSON 和 前一个回调函数运行时发生的错误 + console.log('发生错误!', error); +}); +``` +如果 `Promise` 状态已经变成`resolved`,再抛出错误是无效的。 +```js +const p = new Promise(function(resolve, reject) { + resolve('ok'); + throw new Error('test'); +}); +p + .then(function(value) { console.log(value) }) + .catch(function(error) { console.log(error) }); +// ok +``` +当`promise`抛出一个错误,就被`catch`方法指定的回调函数捕获,下面三种写法相同。 +```js +// 写法一 +const p = new Promise(function(resolve, reject) { + throw new Error('test'); +}); +p.catch(function(error) { + console.log(error); +}); +// Error: test + +// 写法二 +const p = new Promise(function(resolve, reject) { + try { + throw new Error('test'); + } catch(e) { + reject(e); + } +}); +p.catch(function(error) { + console.log(error); +}); + +// 写法三 +const p = new Promise(function(resolve, reject) { + reject(new Error('test')); +}); +p.catch(function(error) { + console.log(error); +}); +``` +一般来说,不要在`then`方法里面定义` Reject` 状态的回调函数(即`then`的第二个参数),总是使用`catch`方法。 +```js +// bad +promise + .then(function(data) { + // success + }, function(err) { + // error + }); + +// good +promise + .then(function(data) { //cb + // success + }) + .catch(function(err) { + // error + }); +``` + +#### 1.12.5 Promise.prototype.finally() +`finally`方法用于指定不管 `Promise` 对象最后状态如何,都会执行的操作。该方法是 ES2018 引入标准的。 +```js +promise +.then(result => {···}) +.catch(error => {···}) +.finally(() => {···}); +``` +`finally`不接收任何参数,与状态无关,本质上是`then`方法的特例。 +```js +promise +.finally(() => { + // 语句 +}); + +// 等同于 +promise +.then( + result => { + // 语句 + return result; + }, + error => { + // 语句 + throw error; + } +); +``` +上面代码中,如果不使用`finally`方法,同样的语句需要为成功和失败两种情况各写一次。有了`finally`方法,则只需要写一次。 +`finally`方法总是会返回原来的值。 +```js +// resolve 的值是 undefined +Promise.resolve(2).then(() => {}, () => {}) + +// resolve 的值是 2 +Promise.resolve(2).finally(() => {}) + +// reject 的值是 undefined +Promise.reject(3).then(() => {}, () => {}) + +// reject 的值是 3 +Promise.reject(3).finally(() => {}) +``` + +#### 1.12.6 Promise.all() +用于将多个 `Promise` 实例,包装成一个新的 `Promise` 实例,参数可以不是数组,但必须是Iterator接口,且返回的每个成员都是`Promise`实例。 +```js +const p = Promise.all([p1, p2, p3]); +``` +`p`的状态由`p1`、`p2`、`p3`决定,分成**两种**情况。 +1. 只有p1、p2、p3的状态都变成fulfilled,p的状态才会变成fulfilled,此时p1、p2、p3的返回值组成一个数组,传递给p的回调函数。 +2. 只要p1、p2、p3之中有一个被rejected,p的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数。 + +```js +// 生成一个Promise对象的数组 +const promises = [2, 3, 5, 7, 11, 13].map(function (id) { + return getJSON('/post/' + id + ".json"); +}); + +Promise.all(promises).then(function (posts) { + // ... +}).catch(function(reason){ + // ... +}); +``` +上面代码中,`promises`是包含 6 个 Promise 实例的数组,只有这 6 个实例的状态都变成`fulfilled`,或者其中有一个变为`rejected`,才会调用`Promise.all`方法后面的回调函数。 + +**注意**:如果`Promise`的参数中定义了`catch`方法,则`rejected`后不会触发`Promise.all()`的`catch`方法,因为参数中的`catch`方法执行完后也会变成`resolved`,当`Promise.all()`方法参数的实例都是`resolved`时就会调用`Promise.all()`的`then`方法。 +```js +const p1 = new Promise((resolve, reject) => { + resolve('hello'); +}) +.then(result => result) +.catch(e => e); + +const p2 = new Promise((resolve, reject) => { + throw new Error('报错了'); +}) +.then(result => result) +.catch(e => e); + +Promise.all([p1, p2]) +.then(result => console.log(result)) +.catch(e => console.log(e)); +// ["hello", Error: 报错了] +``` + +**如果参数里面都没有catch方法,就会调用Promise.all()的catch方法。** +```js +const p1 = new Promise((resolve, reject) => { + resolve('hello'); +}) +.then(result => result); + +const p2 = new Promise((resolve, reject) => { + throw new Error('报错了'); +}) +.then(result => result); + +Promise.all([p1, p2]) +.then(result => console.log(result)) +.catch(e => console.log(e)); +// Error: 报错了 +``` + +#### 1.12.7 Promise.race() +与`Promise.all`方法类似,也是将多个`Promise`实例包装成一个新的`Promise`实例。 +```js +const p = Promise.race([p1, p2, p3]); +``` +与`Promise.all`方法区别在于,`Promise.race`方法是`p1`, `p2`, `p3`中只要一个参数先改变状态,就会把这个参数的返回值传给`p`的回调函数。 + +#### 1.12.8 Promise.resolve() +将现有对象转换成 `Promise` 对象。 +```js +const p = Promise.resolve($.ajax('/whatever.json')); +``` + +#### 1.12.9 Promise.reject() +返回一个`rejected`状态的`Promise`实例。 +```js +const p = Promise.reject('出错了'); +// 等同于 +const p = new Promise((resolve, reject) => reject('出错了')) + +p.then(null, function (s) { + console.log(s) +}); +// 出错了 +``` +注意,`Promise.reject()`方法的参数,会原封不动地作为`reject`的理由,变成后续方法的参数。这一点与`Promise.resolve`方法不一致。 +```js +const thenable = { + then(resolve, reject) { + reject('出错了'); + } +}; + +Promise.reject(thenable) +.catch(e => { + console.log(e === thenable) +}) +// true +``` + + +[⬆ 返回目录](#二目录) + +### 1.13 Iterator和 for...of循环 +#### 1.13.1 Iterator遍历器概念 +> **Iterator**是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署 **Iterator** 接口,就可以完成遍历操作(即依次处理该数据结构的所有成员)。 + +**Iterator三个作用**: +* 为各种数据结构,提供一个**统一**的、**简便**的访问接口; +* 使得数据结构的成员能够按某种次序排列; +* **Iterator** 接口主要供ES6新增的`for...of`消费; + +#### 1.13.2 Iterator遍历过程 +1. 创建一个指针对象,指向当前数据结构的起始位置。也就是说,遍历器对象本质上,就是一个指针对象。 +2. 第一次调用指针对象的`next`方法,可以将指针指向数据结构的第一个成员。 +3. 第二次调用指针对象的`next`方法,指针就指向数据结构的第二个成员。 +4. 不断调用指针对象的`next`方法,直到它指向数据结构的结束位置。 + +每一次调用`next`方法,都会返回数据结构的当前成员的信息。具体来说,就是返回一个包含`value`和`done`两个属性的对象。 + +* `value`属性是当前成员的值; +* `done`属性是一个布尔值,表示遍历是否结束; + +模拟`next`方法返回值: +```js +let f = function (arr){ + var nextIndex = 0; + return { + next:function(){ + return nextIndex < arr.length ? + {value: arr[nextIndex++], done: false}: + {value: undefined, done: true} + } + } +} + +let a = f(['a', 'b']); +a.next(); // { value: "a", done: false } +a.next(); // { value: "b", done: false } +a.next(); // { value: undefined, done: true } +``` + +#### 1.13.3 默认Iterator接口 +若数据**可遍历**,即一种数据部署了Iterator接口。 +ES6中默认的Iterator接口部署在数据结构的`Symbol.iterator`属性,即如果一个数据结构具有`Symbol.iterator`属性,就可以认为是**可遍历**。 +`Symbol.iterator`属性本身是函数,是当前数据结构默认的遍历器生成函数。执行这个函数,就会返回一个遍历器。至于属性名`Symbol.iterator`,它是一个表达式,返回`Symbol`对象的`iterator`属性,这是一个预定义好的、类型为 Symbol 的特殊值,所以要放在方括号内(参见《Symbol》一章)。 + +**原生具有Iterator接口的数据结构有**: +* Array +* Map +* Set +* String +* TypedArray +* 函数的 arguments 对象 +* NodeList 对象 + +#### 1.13.4 Iterator使用场景 +* **(1)解构赋值** +对数组和 `Set` 结构进行解构赋值时,会默认调用`Symbol.iterator`方法。 +```js +let a = new Set().add('a').add('b').add('c'); +let [x, y] = a; // x = 'a' y = 'b' +let [a1, ...a2] = a; // a1 = 'a' a2 = ['b','c'] +``` + +* **(2)扩展运算符** +扩展运算符(`...`)也会调用默认的 Iterator 接口。 +```js +let a = 'hello'; +[...a]; // ['h','e','l','l','o'] + +let a = ['b', 'c']; +['a', ...a, 'd']; // ['a', 'b', 'c', 'd'] +``` + +* **(2)yield*** +`yield*`后面跟的是一个可遍历的结构,它会调用该结构的遍历器接口。 +```js +let a = function*(){ + yield 1; + yield* [2,3,4]; + yield 5; +} + +let b = a(); +b.next() // { value: 1, done: false } +b.next() // { value: 2, done: false } +b.next() // { value: 3, done: false } +b.next() // { value: 4, done: false } +b.next() // { value: 5, done: false } +b.next() // { value: undefined, done: true } +``` + +* **(4)其他场合** +由于数组的遍历会调用遍历器接口,所以任何接受数组作为参数的场合,其实都调用了遍历器接口。下面是一些例子。 + +* for...of +* Array.from() +* Map(), Set(), WeakMap(), WeakSet()(比如`new Map([['a',1],['b',2]])`) +* Promise.all() +* Promise.race() + +#### 1.13.5 for...of循环 +只要数据结构部署了`Symbol.iterator`属性,即具有 iterator 接口,可以用`for...of`循环遍历它的成员。也就是说,`for...of`循环内部调用的是数据结构的`Symbol.iterato`方法。 +**使用场景**: +`for...of`可以使用在**数组**,**`Set`和`Map`结构**,**类数组对象**,**Genetator对象**和**字符串**。 + +* **数组** +`for...of`循环可以代替数组实例的`forEach`方法。 +```js +let a = ['a', 'b', 'c']; +for (let k of a){console.log(k)}; // a b c + +a.forEach((ele, index)=>{ + console.log(ele); // a b c + console.log(index); // 0 1 2 +}) +``` +与`for...in`对比,`for...in`只能获取对象键名,不能直接获取键值,而`for...of`允许直接获取键值。 +```js +let a = ['a', 'b', 'c']; +for (let k of a){console.log(k)}; // a b c +for (let k in a){console.log(k)}; // 0 1 2 +``` + +* **Set和Map** +可以使用数组作为变量,如`for (let [k,v] of b){...}`。 +```js +let a = new Set(['a', 'b', 'c']); +for (let k of a){console.log(k)}; // a b c + +let b = new Map(); +b.set('name','leo'); +b.set('age', 18); +b.set('aaa','bbb'); +for (let [k,v] of b){console.log(k + ":" + v)}; +// name:leo +// age:18 +// aaa:bbb +``` + +* **类数组对象** +```js +// 字符串 +let a = 'hello'; +for (let k of a ){console.log(k)}; // h e l l o + +// DOM NodeList对象 +let b = document.querySelectorAll('p'); +for (let k of b ){ + k.classList.add('test'); +} + +// arguments对象 +function f(){ + for (let k of arguments){ + console.log(k); + } +} +f('a','b'); // a b +``` + +* **对象** +普通对象不能直接使用`for...of`会报错,要部署Iterator才能使用。 +```js +let a = {a:'aa',b:'bb',c:'cc'}; +for (let k in a){console.log(k)}; // a b c +for (let k of a){console>log(k)}; // TypeError +``` + +#### 1.13.6 跳出for...of +使用`break`来实现。 +```js +for (let k of a){ + if(k>100) + break; + console.log(k); +} +``` + +[⬆ 返回目录](#二目录) + + + +### 1.14 Generator函数和应用 +#### 1.14.1 基本概念 +`Generator`函数是一种异步编程解决方案。 +**原理**: +执行`Genenrator`函数会返回一个遍历器对象,依次遍历`Generator`函数内部的每一个状态。 +`Generator`函数是一个普通函数,有以下两个特征: +* `function`关键字与函数名之间有个星号; +* 函数体内使用`yield`表达式,定义不同状态; + +通过调用`next`方法,将指针移向下一个状态,直到遇到下一个`yield`表达式(或`return`语句)为止。简单理解,`Generator`函数分段执行,`yield`表达式是暂停执行的标记,而`next`恢复执行。 +```js +function * f (){ + yield 'hi'; + yield 'leo'; + return 'ending'; +} +let a = f(); +a.next(); // {value: 'hi', done : false} +a.next(); // {value: 'leo', done : false} +a.next(); // {value: 'ending', done : true} +a.next(); // {value: undefined, done : false} +``` + +#### 1.14.2 yield表达式 +`yield`表达式是暂停标志,遍历器对象的`next`方法的运行逻辑如下: +1. 遇到`yield`就暂停执行,将这个`yield`后的表达式的值,作为返回对象的`value`属性值。 +2. 下次调用`next`往下执行,直到遇到下一个`yield`。 +3. 直到函数结束或者`return`为止,并返回`return`语句后面表达式的值,作为返回对象的`value`属性值。 +4. 如果该函数没有`return`语句,则返回对象的`value`为`undefined` 。 + +**注意:** +* `yield`只能用在`Generator`函数里使用,其他地方使用会报错。 +```js +// 错误1 +(function(){ + yiled 1; // SyntaxError: Unexpected number +})() + +// 错误2 forEach参数是个普通函数 +let a = [1, [[2, 3], 4], [5, 6]]; +let f = function * (i){ + i.forEach(function(m){ + if(typeof m !== 'number'){ + yield * f (m); + }else{ + yield m; + } + }) +} +for (let k of f(a)){ + console.log(k) +} +``` + +* `yield`表达式如果用于另一个表达式之中,必须放在**圆括号**内。 +```js +function * a (){ + console.log('a' + yield); // SyntaxErro + console.log('a' + yield 123); // SyntaxErro + console.log('a' + (yield)); // ok + console.log('a' + (yield 123)); // ok +} +``` + +* `yield`表达式用做函数参数或放在表达式右边,可以**不加括号**。 +```js +function * a (){ + f(yield 'a', yield 'b'); // ok + lei i = yield; // ok +} +``` + +#### 1.14.3 next方法 +`yield`本身没有返回值,或者是总返回`undefined`,`next`方法可带一个参数,作为上一个`yield`表达式的返回值。 +```js +function * f (){ + for (let k = 0; true; k++){ + let a = yield k; + if(a){k = -1}; + } +} +let g =f(); +g.next(); // {value: 0, done: false} +g.next(); // {value: 1, done: false} +g.next(true); // {value: 0, done: false} +``` +这一特点,可以让`Generator`函数开始执行之后,可以从外部向内部注入不同值,从而调整函数行为。 +```js +function * f(x){ + let y = 2 * (yield (x+1)); + let z = yield (y/3); + return (x + y + z); +} +let a = f(5); +a.next(); // {value : 6 ,done : false} +a.next(); // {value : NaN ,done : false} +a.next(); // {value : NaN ,done : true} +// NaN因为yeild返回的是对象 和数字计算会NaN + +let b = f(5); +b.next(); // {value : 6 ,done : false} +b.next(12); // {value : 8 ,done : false} +b.next(13); // {value : 42 ,done : false} +// x 5 y 24 z 13 +``` + +#### 1.14.4 for...of循环 +`for...of`循环会自动遍历,不用调用`next`方法,需要注意的是,`for...of`遇到`next`返回值的`done`属性为`true`就会终止,`return`返回的不包括在`for...of`循环中。 +```js +function * f(){ + yield 1; + yield 2; + yield 3; + yield 4; + return 5; +} +for (let k of f()){ + console.log(k); +} +// 1 2 3 4 没有 5 +``` + +#### 1.14.5 Generator.prototype.throw() +`throw`方法用来向函数外抛出错误,并且在Generator函数体内捕获。 +```js +let f = function * (){ + try { yield } + catch (e) { console.log('内部捕获', e) } +} + +let a = f(); +a.next(); + +try{ + a.throw('a'); + a.throw('b'); +}catch(e){ + console.log('外部捕获',e); +} +// 内部捕获 a +// 外部捕获 b +``` + +#### 1.14.6 Generator.prototype.return() +`return`方法用来返回给定的值,并结束遍历Generator函数,如果`return`方法没有参数,则返回值的`value`属性为`undefined`。 +```js +function * f(){ + yield 1; + yield 2; + yield 3; +} +let g = f(); +g.next(); // {value : 1, done : false} +g.return('leo'); // {value : 'leo', done " true} +g.next(); // {value : undefined, done : true} +``` + +#### 1.14.7 next()/throw()/return()共同点 +相同点就是都是用来恢复Generator函数的执行,并且使用不同语句替换`yield`表达式。 +* `next()`将`yield`表达式替换成一个值。 +```js +let f = function * (x,y){ + let r = yield x + y; + return r; +} +let g = f(1, 2); +g.next(); // {value : 3, done : false} +g.next(1); // {value : 1, done : true} +// 相当于把 let r = yield x + y; +// 替换成 let r = 1; +``` +* `throw()`将`yield`表达式替换成一个`throw`语句。 +```js +g.throw(new Error('报错')); // Uncaught Error:报错 +// 相当于将 let r = yield x + y +// 替换成 let r = throw(new Error('报错')); +``` +* `next()`将`yield`表达式替换成一个`return`语句。 +```js +g.return(2); // {value: 2, done: true} +// 相当于将 let r = yield x + y +// 替换成 let r = return 2; +``` + +#### 1.14.8 yield* 表达式 +用于在一个Generator中执行另一个Generator函数,如果没有使用`yield*`会没有效果。 +```js +function * a(){ + yield 1; + yield 2; +} +function * b(){ + yield 3; + yield * a(); + yield 4; +} +// 等同于 +function * b(){ + yield 3; + yield 1; + yield 2; + yield 4; +} +for(let k of b()){console.log(k)} +// 3 +// 1 +// 2 +// 4 +``` + +#### 1.14.9 应用场景 +1. **控制流管理** +解决回调地狱: +```js +// 使用前 +f1(function(v1){ + f2(function(v2){ + f3(function(v3){ + // ... more and more + }) + }) +}) + +// 使用Promise +Promise.resolve(f1) + .then(f2) + .then(f3) + .then(function(v4){ + // ... + },function (err){ + // ... + }).done(); + +// 使用Generator +function * f (v1){ + try{ + let v2 = yield f1(v1); + let v3 = yield f1(v2); + let v4 = yield f1(v3); + // ... + }catch(err){ + // console.log(err) + } +} +function g (task){ + let obj = task.next(task.value); + // 如果Generator函数未结束,就继续调用 + if(!obj.done){ + task.value = obj.value; + g(task); + } +} +g( f(initValue) ); +``` + +2. **异步编程的使用** +在真实的异步任务封装的情况: +```js +let fetch = require('node-fetch'); +function * f(){ + let url = 'http://www.baidu.com'; + let res = yield fetch(url); + console.log(res.bio); +} +// 执行该函数 +let g = f(); +let result = g.next(); +// 由于fetch返回的是Promise对象,所以用then +result.value.then(function(data){ + return data.json(); +}).then(function(data){ + g.next(data); +}) +``` + +[⬆ 返回目录](#二目录) + + +### 1.15 Class语法和继承 +#### 1.15.1 介绍 +ES6中的`class`可以看作只是一个语法糖,绝大部分功能都可以用ES5实现,并且,**类和模块的内部,默认就是严格模式,所以不需要使用use strict指定运行模式**。 +```js +// ES5 +function P (x,y){ + this.x = x; + this.y = y; +} +P.prototype.toString = function () { + return '(' + this.x + ', ' + this.y + ')'; +}; +var a = new P(1, 2); + +// ES6 +class P { + constructor(x, y){ + this.x = x; + this.y = y; + } + toString(){ + return '(' + this.x + ', ' + this.y + ')'; + } +} +let a = new P(1, 2); +``` +**值得注意**: +ES6的**类**的所有方法都是定义在`prototype`属性上,调用类的实例的方法,其实就是调用原型上的方法。 +```js +class P { + constructor(){ ... } + toString(){ ... } + toNumber(){ ... } +} +// 等同于 +P.prototyoe = { + constructor(){ ... }, + toString(){ ... }, + toNumber(){ ... } +} + +let a = new P(); +a.constructor === P.prototype.constructor; // true +``` +类的属性名可以使用**表达式**: +```js +let name = 'leo'; +class P { + constructor (){ ... } + [name](){ ... } +} +``` + +**Class不存在变量提升**: +ES6中的类不存在变量提升,与ES5完全不同: +```js +new P (); // ReferenceError +class P{...}; +``` +**Class的name属性**: +`name`属性总是返回紧跟在`class`后的类名。 +```js +class P {} +P.name; // 'P' +``` + +#### 1.15.2 constructor()方法 +`constructor()`是类的默认方法,通过`new`实例化时自动调用执行,一个类必须有`constructor()`方法,否则一个空的`constructor()`会默认添加。 +`constructor()`方法默认返回实例对象(即`this`)。 +```js +class P { ... } +// 等同于 +class P { + constructor(){ ... } +} +``` + +#### 1.15.3 类的实例对象 +与ES5一样,ES6的类必须使用`new`命令实例化,否则报错。 +```js +class P { ... } +let a = P (1,2); // 报错 +let b = new P(1, 2); // 正确 +``` +与 ES5 一样,实例的属性除非显式定义在其本身(即定义在`this`对象上),否则都是定义在原型上(即定义在`class`上)。 +```js +class P { + constructor(x, y){ + this.x = x; + this.y = y; + } + toString(){ + return '(' + this.x + ', ' + this.y + ')'; + } +} +var point = new Point(2, 3); + +point.toString() // (2, 3) + +point.hasOwnProperty('x') // true +point.hasOwnProperty('y') // true +point.hasOwnProperty('toString') // false +point.__proto__.hasOwnProperty('toString') // true +// toString是原型对象的属性(因为定义在Point类上) +``` + +#### 1.15.4 Class表达式 +与函数一样,类也可以使用表达式来定义,使用表达式来作为类的名字,而`class`后跟的名字,用来指代当前类,只能再Class内部使用。 +```js +let a = class P{ + get(){ + return P.name; + } +} + +let b = new a(); +b.get(); // P +P.name; // ReferenceError: P is not defined +``` +如果类的内部没用到的话,可以省略`P`,也就是可以写成下面的形式。 +```js +let a = class { ... } +``` + +#### 1.15.5 私有方法和私有属性 +由于ES6不提供,只能变通来实现: +* 1.使用命名加以区别,如变量名前添加`_`,但是不保险,外面也可以调用到。 +```js +class P { + // 公有方法 + f1 (x) { + this._x(x); + } + // 私有方法 + _x (x){ + return this.y = x; + } +} +``` +* 2.将私有方法移除模块,再在类内部调用`call`方法。 +```js +class P { + f1 (x){ + f2.call(this, x); + } +} +function f2 (x){ + return this.y = x; +} +``` +* 3.使用`Symbol`为私有方法命名。 +```js +const a1 = Symbol('a1'); +const a2 = Symbol('a2'); +export default class P{ + // 公有方法 + f1 (x){ + this[a1](x); + } + // 私有方法 + [a1](x){ + return this[a2] = x; + } +} +``` + + +#### 1.15.6 this指向问题 +类内部方法的`this`默认指向类的实例,但单独使用该方法可能报错,因为`this`指向的问题。 +```js +class P{ + leoDo(thing = 'any'){ + this.print(`Leo do ${thing}`) + } + print(text){ + console.log(text); + } +} +let a = new P(); +let { leoDo } = a; +leoDo(); // TypeError: Cannot read property 'print' of undefined +// 问题出在 单独使用leoDo时,this指向调用的环境, +// 但是leoDo中的this是指向P类的实例,所以报错 +``` +**解决方法**: +* 1.在类里面绑定`this` +```js +class P { + constructor(){ + this.name = this.name.bind(this); + } +} +``` +* 2.使用箭头函数 +```js +class P{ + constructor(){ + this.name = (name = 'leo' )=>{ + this.print(`my name is ${name}`) + } + } +} +``` + +#### 1.15.7 Class的getter和setter +使用`get`和`set`关键词对属性设置取值函数和存值函数,拦截属性的存取行为。 +```js +class P { + constructor (){ ... } + get f (){ + return 'getter'; + } + set f (val) { + console.log('setter: ' + val); + } +} + +let a = new P(); +a.f = 100; // setter : 100 +a.f; // getter +``` + +#### 1.15.8 Class的generator方法 +只要在方法之前加个(`*`)即可。 +```js +class P { + constructor (...args){ + this.args = args; + } + *[Symbol.iterator](){ + for (let arg of this.args){ + yield arg; + } + } +} +for (let k of new P('aa', 'bb')){ + console.log(k); +} +// 'aa' +// 'bb' +``` + +#### 1.15.9 Class的静态方法 +由于类相当于实例的原型,所有类中定义的方法都会被实例继承,若不想被继承,只要加上`static`关键字,只能通过类来调用,即“**静态方法**”。 +```js +class P (){ + static f1 (){ return 'aaa' }; +} +P.f1(); // 'aa' +let a = new P(); +a.f1(); // TypeError: a.f1 is not a function +``` +如果静态方法包含`this`关键字,则`this`指向类,而不是实例。 +```js +class P { + static f1 (){ + this.f2(); + } + static f2 (){ + console.log('aaa'); + } + f2(){ + console.log('bbb'); + } +} +P.f2(); // 'aaa' +``` +并且静态方法可以被子类继承,或者`super`对象中调用。 +```js +class P{ + static f1(){ return 'leo' }; +} +class Q extends P { ... }; +Q.f1(); // 'leo' + +class R extends P { + static f2(){ + return super.f1() + ',too'; + } +} +R.f2(); // 'leo , too' +``` + +#### 1.15.10 Class的静态属性和实例属性 +ES6中明确规定,Class内部只有静态方法没有静态属性,所以只能通过下面实现。 +```js +// 正确写法 +class P {} +P.a1 = 1; +P.a1; // 1 + +// 无效写法 +class P { + a1: 2, // 无效 + static a1 : 2, // 无效 +} +P.a1; // undefined +``` +**新提案来规定实例属性和静态属性的新写法** +* 1.类的实例属性 +类的实例属性可以用等式,写入类的定义中。 +```js +class P { + prop = 100; // prop为P的实例属性 可直接读取 + constructor(){ + console.log(this.prop); // 100 + } +} +``` +有了新写法后,就可以不再`contructor`方法里定义。 +为了可读性的目的,对于那些在`constructor`里面已经定义的实例属性,新写法允许**直接列出**。 +```js +// 之前写法: +class RouctCounter extends React.Component { + constructor(prop){ + super(prop); + this.state = { + count : 0 + } + } +} + +// 新写法 +class RouctCounter extends React.Component { + state; + constructor(prop){ + super(prop); + this.state = { + count : 0 + } + } + +} +``` +* 2.类的静态属性 +只要在实例属性前面加上`static`关键字就可以。 +```js +class P { + static prop = 100; + constructor(){console.log(this.prop)}; // 100 +} +``` +新写法方便静态属性的表达。 +```js +// old +class P { .... } +P.a = 1; + +// new +class P { + static a = 1; +} +``` + +#### 1.15.11 Class的继承 +主要通过`extends`关键字实现,继承父类的所有属性和方法,通过`super`关键字来新建父类构造函数的`this`对象。 +```js +class P { ... } +class Q extends P { ... } + +class P { + constructor(x, y){ + // ... + } + f1 (){ ... } +} +class Q extends P { + constructor(a, b, c){ + super(x, y); // 调用父类 constructor(x, y) + this.color = color ; + } + f2 (){ + return this.color + ' ' + super.f1(); + // 调用父类的f1()方法 + } +} +``` +**子类必须在`constructor()`调用`super()`否则报错**,并且只有`super`方法才能调用父类实例,还有就是,**父类的静态方法,子类也可以继承到**。 +```js +class P { + constructor(x, y){ + this.x = x; + this.y = y; + } + static fun(){ + console.log('hello leo') + } +} +// 关键点1 调用super +class Q extends P { + constructor(){ ... } +} +let a = new Q(); // ReferenceError 因为Q没有调用super + +// 关键点2 调用super +class R extends P { + constructor (x, y. z){ + this.z = z; // ReferenceError 没调用super不能使用 + super(x, y); + this.z = z; // 正确 + } +} + +// 关键点3 子类继承父类静态方法 +R.hello(); // 'hello leo' +``` + +**super关键字**: +既可以当函数使用,还可以当对象使用。 +* 1.当函数调用,代表父类的构造函数,但必须执行一次。 +```js +class P {... }; +class R extends P { + constructor(){ + super(); + } +} +``` +* 2.当对象调用,指向原型对象,在静态方法中指向父类。 +```js +class P { + f (){ return 2 }; +} +class R extends P { + constructor (){ + super(); + console.log(super.f()); // 2 + } +} +let a = new R() +``` +**注意**:`super`指向父类原型对象,所以定义在父类实例的方法和属性,是无法通过`super`调用的,但是通过调用`super`方法可以把内部`this`指向当前实例,就可以访问到。 +```js +class P { + constructor(){ + this.a = 1; + } + print(){ + console.log(this.a); + } +} +class R extends P { + get f (){ + return super.a; + } +} +let b = new R(); +b.a; // undefined 因为a是父类P实例的属性 + +// 先调用super就可以访问 +class Q extends P { + constructor(){ + super(); // 将内部this指向当前实例 + return super.a; + } +} +let c = new Q(); +c.a; // 1 + +// 情况3 +class J extends P { + constructor(){ + super(); + this.a = 3; + } + g(){ + super.print(); + } +} +let c = new J(); +c.g(); // 3 由于执行了super()后 this指向当前实例 +``` + +[⬆ 返回目录](#二目录) + +### 1.16 Module语法和加载实现 +#### 1.16.1 介绍 +ES6之前用于JavaScript的模块加载方案,是一些社区提供的,主要有`CommonJS`和`AMD`两种,前者用于**服务器**,后者用于**浏览器**。 +ES6提供了模块的实现,使用`export`命令对外暴露接口,使用`import`命令输入其他模块暴露的接口。 +```js +// CommonJS模块 +let { stat, exists, readFire } = require('fs'); + +// ES6模块 +import { stat, exists, readFire } = from 'fs'; +``` + +#### 1.16.2 严格模式 +ES6模块自动采用严格模式,无论模块头部是否有`"use strict"`。 +**严格模式有以下限制**: +* 变量必须**声明后再使用** +* 函数的参数**不能有同名属性**,否则报错 +* 不能使用`with`语句 +* 不能对只读属性赋值,否则报错 +* 不能使用前缀 0 表示八进制数,否则报错 +* 不能删除不可删除的属性,否则报错 +* 不能删除变量`delete prop`,会报错,只能删除属性`delete * global[prop]` +* `eval`不会在它的外层作用域引入变量 +* `eval`和`arguments`不能被重新赋值 +* `arguments`不会自动反映函数参数的变化 +* 不能使用`arguments.callee` +* 不能使用`arguments.caller` +* 禁止`this`指向全局对象 +* 不能使用`fn.caller`和`fn.arguments`获取函数调用的堆栈 +* 增加了保留字(比如`protected`、`static`和`interface`) + +特别是,ES6中顶层`this`指向`undefined`,即不应该在顶层代码使用`this`。 + +#### 1.16.3 export命令 +使用`export`向模块外暴露接口,可以是方法,也可以是变量。 +```js +// 1. 变量 +export let a = 'leo'; +export let b = 100; + +// 还可以 +let a = 'leo'; +let b = 100; +export {a, b}; + +// 2. 方法 +export function f(a,b){ + return a*b; +} + +// 还可以 +function f1 (){ ... } +function f2 (){ ... } +export { + a1 as f1, + a2 as f2 +} +``` +可以使用`as`重命名函数的对外接口。 +**特别注意**: +`export`暴露的必须是接口,不能是值。 +```js +// 错误 +export 1; // 报错 + +let a = 1; +export a; // 报错 + +// 正确 +export let a = 1; // 正确 + +let a = 1; +export {a}; // 正确 + +let a = 1; +export { a as b}; // 正确 +``` +暴露方法也是一样: +```js +// 错误 +function f(){...}; +export f; + +// 正确 +export function f () {...}; + +function f(){...}; +export {f}; +``` + +#### 1.16.4 import命令 +加载`export`暴露的接口,输出为变量。 +```js +import { a, b } from '/a.js'; +function f(){ + return a + b; +} +``` +`import`后大括号指定变量名,需要与`export`的模块暴露的名称一致。 +也可以使用`as`为输入的变量重命名。 +```js +import { a as leo } from './a.js'; +``` +`import`不能直接修改输入变量的值,因为输入变量只读只是个接口,但是如果是个对象,可以修改它的属性。 +```js +// 错误 +import {a} from './f.js'; +a = {}; // 报错 + +// 正确 +a.foo = 'leo'; // 不报错 +``` +`import`命令具有提升效果,会提升到整个模块头部最先执行,且多次执行相同`import`只会执行一次。 + +#### 1.16.5 模块的整体加载 +当一个模块暴露多个方法和变量,引用时可以用`*`整体加载。 +```js +// a.js +export function f(){...} +export function g(){...} + +// b.js +import * as obj from '/a.js'; +console.log(obj.f()); +console.log(obj.g()); +``` +但是,不允许运行时改变: +```js +import * as obj from '/a.js'; +// 不允许 +obj.a = 'leo'; +obj.b = function(){...}; +``` + +#### 1.16.6 export default 命令 +使用`export default`命令,为模块指定默认输出,引用的时候直接指定任意名称即可。 +```js +// a.js +export default function(){console.log('leo')}; + +// b.js +import leo from './a.js'; +leo(); // 'leo' +``` +`export default`暴露有函数名的函数时,在调用时相当于匿名函数。 +```js +// a.js +export default function f(){console.log('leo')}; +// 或者 +function f(){console.log('leo')}; +export default f; + +// b.js +import leo from './a.js'; +``` +`export default`其实是输出一个名字叫`default`的变量,所以后面不能跟变量赋值语句。 +```js +// 正确 +export let a= 1; + +let a = 1; +export default a; + +// 错误 +export default let a = 1; +``` +`export default`命令的本质是将后面的值,赋给`default`变量,所以可以直接将一个值写在`export default`之后。 +```js +// 正确 +export detault 1; +// 错误 +export 1; +``` + +#### 1.16.7 export 和 import 复合写法 +常常在先输入后输出同一个模块使用,即转发接口,将两者写在一起。 +```js +export {a, b} from './leo.js'; + +// 理解为 +import {a, b} from './leo.js'; +export {a, b} +``` +常见的写法还有: +```js +// 接口改名 +export { a as b} from './leo.js'; + +// 整体输出 +export * from './leo.js'; + +// 默认接口改名 +export { default as a } from './leo.js'; +``` +**常常用在模块继承**。 + +#### 1.16.8 浏览器中的加载规则 +ES6中,可以在浏览器使用` +``` +另外,ES6模块也可以内嵌到网页,语法与外部加载脚本一致: +```html + +``` +**注意点**: +* 代码是在模块作用域之中运行,而不是在全局作用域运行。模块内部的顶层变量,外部不可见。 +* 模块脚本自动采用严格模式,不管有没有声明`use strict`。 +* 模块之中,可以使用`import`命令加载其他模块(`.js`后缀不可省略,需要提供`绝对 UR`L 或`相对 UR`L),也可以使用`export`命令输出对外接口。 +* 模块之中,顶层的`this`关键字返回`undefined`,而不是指向`window`。也就是说,在模块顶层使用`this`关键字,是无意义的。 +* 同一个模块如果加载多次,将只执行一次。 + +[⬆ 返回目录](#二目录) + + +## 2. ES7 +### 2.1 Array.prototype.includes()方法 +`includes()`用于查找一个值是否在数组中,如果在返回`true`,否则返回`false`。 +```js +['a', 'b', 'c'].includes('a'); // true +['a', 'b', 'c'].includes('d'); // false +``` +`includes()`方法接收两个参数,**搜索的内容**和**开始搜索的索引**,默认值为**0**,若搜索值在数组中则返回`true`否则返回`false`。 +```js +['a', 'b', 'c', 'd'].includes('b'); // true +['a', 'b', 'c', 'd'].includes('b', 1); // true +['a', 'b', 'c', 'd'].includes('b', 2); // false +``` +与`indexOf`方法对比,下面方法效果相同: +```js +['a', 'b', 'c', 'd'].indexOf('b') > -1; // true +['a', 'b', 'c', 'd'].includes('b'); // true +``` +**includes()与indexOf对比:** +* `includes`相比`indexOf`更具语义化,`includes`返回的是是否存在的具体结果,值为布尔值,而`indexOf`返回的是搜索值的下标。 +* `includes`相比`indexOf`更准确,`includes`认为两个`NaN`相等,而`indexOf`不会。 +```js +let a = [1, NaN, 3]; +a.indexOf(NaN); // -1 +a.includes(NaN); // true +``` +另外在判断`+0`与`-0`时,`includes`和`indexOf`的返回相同。 +```js +[1, +0, 3, 4].includes(-0); // true +[1, +0, 3, 4].indexOf(-0); // 1 +``` + +### 2.2 指数操作符(**) +基本用法: +```js +let a = 3 ** 2 ; // 9 +// 等效于 +Math.pow(3, 2); // 9 +``` +`**`是一个运算符,也可以满足类似假发的操作,如下: +```js +let a = 3; +a **= 2; // 9 +``` + +[⬆ 返回目录](#二目录) + + +## 3. ES8 +### 3.1 async函数 +#### 3.1.1 介绍 +ES8引入`async`函数,是为了使异步操作更加方便,其实它就是**Generator**函数的语法糖。 +`async`函数使用起来,只要把**Generator**函数的(*)号换成`async`,`yield`换成`await`即可。对比如下: +```js +// Generator写法 +const fs = require('fs'); +const readFile = function (fileName) { + return new Promise(function (resolve, reject) { + fs.readFile(fileName, function(error, data) { + if (error) return reject(error); + resolve(data); + }); + }); +}; +const gen = function* () { + const f1 = yield readFile('/etc/fstab'); + const f2 = yield readFile('/etc/shells'); + console.log(f1.toString()); + console.log(f2.toString()); +}; + +// async await写法 +const asyncReadFile = async function () { + const f1 = await readFile('/etc/fstab'); + const f2 = await readFile('/etc/shells'); + console.log(f1.toString()); + console.log(f2.toString()); +}; +``` +**对比Genenrator有四个优点:** +* (1)内置执行器 +Generator函数执行需要有执行器,而`async`函数自带执行器,即`async`函数与普通函数一模一样: +```js +async f(); +``` +* (2)更好的语义 +`async`和`await`,比起`星号`和`yield`,语义更清楚了。`async`表示函数里有异步操作,`await`表示紧跟在后面的表达式需要等待结果。 +* (3)更广的适用性 +`yield`命令后面只能是 Thunk 函数或 Promise 对象,而`async`函数的`await`命令后面,可以是 Promise 对象和原始类型的值(数值、字符串和布尔值,但这时等同于同步操作)。 +* (4)返回值是Promise +`async`函数的返回值是 Promise 对象,这比 Generator 函数的返回值是 Iterator 对象方便多了。你可以用`then`方法指定下一步的操作。 + +进一步说,`async`函数完全可以看作多个异步操作,包装成的一个 Promise 对象,而`await`命令就是内部`then`命令的语法糖。 + +#### 3.1.2 基本用法 +`async`函数返回一个Promise对象,可以使用`then`方法添加回调函数,函数执行时,遇到`await`就会先返回,等到异步操作完成,在继续执行。 +```js +async function f(item){ + let a = await g(item); + let b = await h(item); + return b; +} +f('hello').then(res => { + console.log(res); +}) +``` +`async`表明该函数内部有异步操作,调用该函数时,会立即返回一个Promise对象。 +另外还有个定时的案例,指定时间后执行: +```js +function f (ms){ + return new Promise(res => { + setTimeout(res, ms); + }); +} +async function g(val, ms){ + await f(ms); + console.log(val); +} +g('leo', 50); +``` +`async`函数还有很多使用形式: +```js +// 函数声明 +async function f (){...} + +// 函数表达式 +let f = async function (){...} + +// 对象的方法 +let a = { + async f(){...} +} +a.f().then(...) + +// Class的方法 +class c { + constructor(){...} + async f(){...} +} + +// 箭头函数 +let f = async () => {...} +``` + +#### 3.1.3 返回Promise对象 +`async`内部`return`返回的值会作为`then`方法的参数,另外只有`async`函数内部的异步操作执行完,才会执行`then`方法指定的回调函数。 +```js +async function f(){ + return 'leo'; +} +f().then(res => { console.log (res) }); // 'leo' +``` +`async`内部抛出的错误,会被`catch`接收。 +```js +async function f(){ + throw new Error('err'); +} +f().then ( + v => console.log(v), + e => console.log(e) +) +// Error: err +``` + +#### 3.1.4 await命令 +通常`await`后面是一个Promise对象,如果不是就返回对应的值。 +```js +async function f(){ + return await 10; +} +f().then(v => console.log(v)); // 10 +``` +我们常常将`async await`和`try..catch`一起使用,并且可以放多个`await`命令,也是防止异步操作失败因为中断后续异步操作的情况。 +```js +async function f(){ + try{ + await Promise.reject('err'); + }catch(err){ ... } + return await Promise.resolve('leo'); +} +f().then(v => console.log(v)); // 'leo' +``` + +#### 3.1.5 使用注意 +* (1)`await`命令放在`try...catch`代码块中,防止Promise返回`rejected`。 +* (2)若多个`await`后面的异步操作不存在继发关系,最好让他们同时执行。 +```js +// 效率低 +let a = await f(); +let b = await g(); + +// 效率高 +let [a, b] = await Promise.all([f(), g()]); +// 或者 +let a = f(); +let b = g(); +let a1 = await a(); +let b1 = await b(); +``` +* (3)`await`命令只能用在`async`函数之中,如果用在普通函数,就会报错。 +```js +// 错误 +async function f(){ + let a = [{}, {}, {}]; + a.forEach(v =>{ // 报错,forEach是普通函数 + await post(v); + }); +} + +// 正确 +async function f(){ + let a = [{}, {}, {}]; + for(let k of a){ + await post(k); + } +} +``` + +[⬆ 返回目录](#二目录) + +### 3.2 Promise.prototype.finally() +`finally()`是ES8中**Promise**添加的一个新标准,用于指定不管**Promise**对象最后状态(是`fulfilled`还是`rejected`)如何,都会执行此操作,并且`finally`方法必须写在最后面,即在`then`和`catch`方法后面。 +```js +// 写法如下: +promise + .then(res => {...}) + .catch(err => {...}) + .finally(() => {...}) +``` +`finally`方法常用在处理**Promise**请求后关闭服务器连接: +```js +server.listen(port) + .then(() => {..}) + .finally(server.stop); +``` +**本质上,finally方法是then方法的特例:** +```js +promise.finally(() => {...}); + +// 等同于 +promise.then( + result => { + // ... + return result + }, + error => { + // ... + throw error + } +) +``` +[⬆ 返回目录](#二目录) + +### 3.3 Object.values(),Object.entries() +ES7中新增加的 `Object.values()`和`Object.entries()`与之前的`Object.keys()`类似,返回数组类型。 +回顾下`Object.keys()`: +```js +var a = { f1: 'hi', f2: 'leo'}; +Object.keys(a); // ['f1', 'f2'] +``` +#### 3.3.1 Object.values() +返回一个数组,成员是参数对象自身的(不含继承的)所有**可遍历属性**的键值。 +```js +let a = { f1: 'hi', f2: 'leo'}; +Object.values(a); // ['hi', 'leo'] +``` +如果参数不是对象,则返回空数组: +```js +Object.values(10); // [] +Object.values(true); // [] +``` + +#### 3.3.2 Object.entries() +返回一个数组,成员是参数对象自身的(不含继承的)所有**可遍历属性**的键值对数组。 +```js +let a = { f1: 'hi', f2: 'leo'}; +Object.entries(a); // [['f1','hi'], ['f2', 'leo']] +``` +* **用途1**: +遍历对象属性。 +```js +let a = { f1: 'hi', f2: 'leo'}; +for (let [k, v] of Object.entries(a)){ + console.log( + `${JSON.stringfy(k)}:${JSON.stringfy(v)}` + ) +} +// 'f1':'hi' +// 'f2':'leo' +``` +* **用途2**: +将对象转为真正的Map结构。 +```js +let a = { f1: 'hi', f2: 'leo'}; +let map = new Map(Object.entries(a)); +``` +手动实现`Object.entries()`方法: +```js +// Generator函数实现: +function* entries(obj){ + for (let k of Object.keys(obj)){ + yield [k ,obj[k]]; + } +} + +// 非Generator函数实现: +function entries (obj){ + let arr = []; + for(let k of Object.keys(obj)){ + arr.push([k, obj[k]]); + } + return arr; +} +``` + +[⬆ 返回目录](#二目录) + +### 3.4 Object.getOwnPropertyDescriptors() +之前有`Object.getOwnPropertyDescriptor`方法会返回某个对象属性的描述对象,新增的`Object.getOwnPropertyDescriptors()`方法,返回指定对象所有自身属性(非继承属性)的描述对象,所有原对象的属性名都是该对象的属性名,对应的属性值就是该属性的描述对象 +```js +let a = { + a1:1, + get f1(){ return 100} +} +Object.getOwnPropetyDescriptors(a); +/* +{ + a:{ configurable:true, enumerable:true, value:1, writeable:true} + f1:{ configurable:true, enumerable:true, get:f, set:undefined} +} +*/ +``` +实现原理: +```js +function getOwnPropertyDescriptors(obj) { + const result = {}; + for (let key of Reflect.ownKeys(obj)) { + result[key] = Object.getOwnPropertyDescriptor(obj, key); + } + return result; +} +``` +引入这个方法,主要是为了解决`Object.assign()`无法正确拷贝`get`属性和`set`属性的问题。 +```js +let a = { + set f(v){ + console.log(v) + } +} +let b = {}; +Object.assign(b, a); +Object.a(b, 'f'); +/* +f = { + configurable: true, + enumable: true, + value: undefined, + writeable: true +} +*/ +``` +`value`为`undefined`是因为`Object.assign`方法不会拷贝其中的`get`和`set`方法,使用`getOwnPropertyDescriptors`配合`Object.defineProperties`方法来实现正确的拷贝: +```js +let a = { + set f(v){ + console.log(v) + } +} +let b = {}; +Object.defineProperties(b, Object.getOwnPropertyDescriptors(a)); +Object.getOwnPropertyDescriptor(b, 'f') +/* + configurable: true, + enumable: true, + get: undefined, + set: function(){...} +*/ +``` +`Object.getOwnPropertyDescriptors`方法的配合`Object.create`方法,将对象属性克隆到一个新对象,实现浅拷贝。 +```js +const clone = Object.create(Object.getPrototypeOf(obj), + Object.getOwnPropertyDescriptors(obj)); + +// 或者 +const shallowClone = (obj) => Object.create( + Object.getPrototypeOf(obj), + Object.getOwnPropertyDescriptors(obj) +); +``` + +[⬆ 返回目录](#二目录) + +### 3.5 字符串填充 padStart和padEnd +用来为字符串填充特定字符串,并且都有两个参数:**字符串目标长度**和**填充字段**,第二个参数可选,默认空格。 +```js +'es8'.padStart(2); // 'es8' +'es8'.padStart(5); // ' es8' +'es8'.padStart(6, 'woof'); // 'wooes8' +'es8'.padStart(14, 'wow'); // 'wowwowwowwoes8' +'es8'.padStart(7, '0'); // '0000es8' + +'es8'.padEnd(2); // 'es8' +'es8'.padEnd(5); // 'es8 ' +'es8'.padEnd(6, 'woof'); // 'es8woo' +'es8'.padEnd(14, 'wow'); // 'es8wowwowwowwo' +'es8'.padEnd(7, '6'); // 'es86666' +``` +从上面结果来看,填充函数只有在字符长度小于目标长度时才有效,若字符长度已经等于或小于目标长度时,填充字符不会起作用,而且目标长度如果小于字符串本身长度时,字符串也不会做截断处理,只会原样输出。 + +### 3.6 函数参数列表与调用中的尾部逗号 +该特性允许我们在定义或者调用函数时添加尾部逗号而不报错: +```js +function es8(var1, var2, var3,) { + // ... +} +es8(10, 20, 30,); +``` + +### 3.7 共享内存与原子操作 +当内存被共享时,多个线程可以并发读、写内存中相同的数据。原子操作可以确保那些被读、写的值都是可预期的,即新的事务是在旧的事务结束之后启动的,旧的事务在结束之前并不会被中断。这部分主要介绍了 ES8 中新的构造函数 `SharedArrayBuffer` 以及拥有许多静态方法的命名空间对象 `Atomic` 。 +`Atomic` 对象类似于 `Math` 对象,拥有许多静态方法,所以我们不能把它当做构造函数。 `Atomic` 对象有如下常用的静态方法: + +* add /sub :为某个指定的value值在某个特定的位置增加或者减去某个值 +* and / or /xor :进行位操作 +* load :获取特定位置的值 + +[⬆ 返回目录](#二目录) + +## 4. ES9 +### 4.1 对象的拓展运算符 +#### 4.1.1 介绍 +对象的拓展运算符,即对象的Rest/Spread属性,可将对象解构赋值用于从一个对象取值,搜键值对分配到指定对象上,与数组的拓展运算符类似: +```js +let {x, y, ...z} = {x:1, y:2, a:3, b:4}; +x; // 1 +y; // 2 +z; // {a:3, b:4} +``` +对象的解构赋值要求等号右边必须是个对象,所以如果等号右边是`undefined`或`null`就会报错无法转成对象。 +```js +let {a, ...b} = null; // 运行时报错 +let {a, ...b} = undefined; // 运行时报错 +``` +解构赋值必须是最后一个参数,否则报错。 +```js +let {...a, b, c} = obj; // 语法错误 +let {a, ...b, c} = obj; // 语法错误 +``` +**注意**: +* 1.解构赋值是浅拷贝。 +```js +let a = {a1: {a2: 'leo'}}; +let {...b} = a; +a.a1.a2 = 'leo'; +b.a1.a2 = 'leo'; +``` +* 2.拓展运算符的解构赋值,不能复制继承自原型对象的属性。 +```js +let o1 = { a: 1 }; +let o2 = { b: 2 }; +o2.__proto__ = o1; +let { ...o3 } = o2; +o3; // { b: 2 } +o3.a; // undefined +``` + +#### 4.1.2 使用场景 +* 1.取出参数对象所有可遍历属性,拷贝到当前对象中。 +```js +let a = { a1:1, a2:2 }; +let b = { ...a }; +b; // { a1:1, a2:2 } + +// 类似Object.assign方法 +``` +* 2.合并两个对象。 +```js +let a = { a1:1, a2:2 }; +let b = { b1:11, b2:22 }; +let ab = { ...a, ...b }; // {a1: 1, a2: 2, b1: 11, b2: 22} +// 等同于 +let ab = Object.assign({}, a, b); +``` +* 3.将自定义属性放在拓展运算符后面,覆盖对象原有的同名属性。 +```js +let a = { a1:1, a2:2, a3:3 }; +let r = { ...a, a3:666 }; +// r {a1: 1, a2: 2, a3: 666} + +// 等同于 +let r = { ...a, ...{ a3:666 }}; +// r {a1: 1, a2: 2, a3: 666} + +// 等同于 +let r = Object.assign({}, a, { a3:666 }); +// r {a1: 1, a2: 2, a3: 666} +``` +* 4.将自定义属性放在拓展运算符前面,就会成为设置新对象的默认值。 +```js +let a = { a1:1, a2:2 }; +let r = { a3:666, ...a }; +// r {a3: 666, a1: 1, a2: 2} + +// 等同于 +let r = Object.assign({}, {a3:666}, a); +// r {a3: 666, a1: 1, a2: 2} + +// 等同于 +let r = Object.assign({a3:666}, a); +// r {a3: 666, a1: 1, a2: 2} +``` +* 5.拓展运算符后面可以使用表达式。 +```js +let a = { + ...(x>1? {a:!:{}), + b:2 +} +``` +* 6.拓展运算符后面如果是个空对象,则没有任何效果。 +```js +{...{}, a:1}; // {a:1} +``` +* 7.若参数是`null`或`undefined`则忽略且不报错。 +```js +let a = { ...null, ...undefined }; // 不报错 +``` +* 8.若有取值函数`get`则会执行。 +```js +// 不会打印 因为f属性只是定义 而不没执行 +let a = { + ...a1, + get f(){console.log(1)} +} + +// 会打印 因为f执行了 +let a = { + ...a1, + ...{ + get f(){console.log(1)} + } +} +``` + +[⬆ 返回目录](#二目录) + + +### 4.2 正则表达式 s 修饰符 +在正则表达式中,点(`.`)可以表示任意单个字符,除了两个:用`u`修饰符解决**四个字节的UTF-16字符**,另一个是行终止符。 +**终止符**即表示一行的结束,如下四个字符属于“行终止符”: +* U+000A 换行符(\n) +* U+000D 回车符(\r) +* U+2028 行分隔符(line separator) +* U+2029 段分隔符(paragraph separator) +```js +/foo.bar/.test('foo\nbar') +// false +``` +上面代码中,因为`.`不匹配`\n`,所以正则表达式返回`false`。 +换个醒,可以匹配任意单个字符: +```js +/foo[^]bar/.test('foo\nbar') +// true +``` +ES9引入`s`修饰符,使得`.`可以匹配任意单个字符: +```js +/foo.bar/s.test('foo\nbar') // true +``` +这被称为`dotAll`模式,即点(`dot`)代表一切字符。所以,正则表达式还引入了一个`dotAll`属性,返回一个布尔值,表示该正则表达式是否处在`dotAll`模式。 +```js +const re = /foo.bar/s; +// 另一种写法 +// const re = new RegExp('foo.bar', 's'); + +re.test('foo\nbar') // true +re.dotAll // true +re.flags // 's' +``` +`/s`修饰符和多行修饰符`/m`不冲突,两者一起使用的情况下,`.`匹配所有字符,而`^`和`$`匹配每一行的行首和行尾。 + +[⬆ 返回目录](#二目录) + + +### 4.3 异步遍历器 +在前面ES6章节中,介绍了Iterator接口,而ES6引入了“异步遍历器”,是为异步操作提供原生的遍历器接口,即`value`和`done`这两个属性都是异步产生的。 + +#### 4.3.1 异步遍历的接口 +通过调用遍历器的`next`方法,返回一个Promise对象。 +```js +a.next().then( + ({value, done}) => { + //... + } +) +``` +上述`a`为异步遍历器,调用`next`后返回一个Promise对象,再调用`then`方法就可以指定Promise对象状态变为`resolve`后执行的回调函数,参数为`value`和`done`两个属性的对象,与同步遍历器一致。 +与同步遍历器一样,异步遍历器接口也是部署在`Symbol.asyncIterator`属性上,只要有这个属性,就都可以异步遍历。 +```js +let a = createAsyncIterable(['a', 'b']); +//createAsyncIterable方法用于构建一个iterator接口 +let b = a[Symbol.asyncInterator](); + +b.next().then( result1 => { + console.log(result1); // {value: 'a', done:false} + return b.next(); +}).then( result2 => { + console.log(result2); // {value: 'b', done:false} + return b.next(); +}).then( result3 => { + console.log(result3); // {value: undefined, done:true} +}) +``` +另外`next`方法返回的是一个Promise对象,所以可以放在`await`命令后。 +```js +async function f(){ + let a = createAsyncIterable(['a', 'b']); + let b = a[Symbol.asyncInterator](); + console.log(await b.next());// {value: 'a', done:false} + console.log(await b.next());// {value: 'b', done:false} + console.log(await b.next());// {value: undefined, done:true} +} +``` +还有一种情况,使用`Promise.all`方法,将所有的`next`按顺序连续调用: +```js +let a = createAsyncIterable(['a', 'b']); +let b = a[Symbol.asyncInterator](); +let {{value:v1}, {value:v2}} = await Promise.all([ + b.next(), b.next() +]) +``` +也可以一次调用所有`next`方法,再用`await`最后一步操作。 +```js +async function f(){ + let write = openFile('aaa.txt'); + write.next('hi'); + write.next('leo'); + await write.return(); +} +f(); +``` +#### 4.3.2 for await...of +`for...of`用于遍历同步的Iterator接口,而ES8引入`for await...of`遍历异步的Iterator接口。 +```js +async function f(){ + for await(let a of createAsyncIterable(['a', 'b'])) { + console.log(x); + } +} +// a +// b +``` +上面代码,`createAsyncIterable()`返回一个拥有异步遍历器接口的对象,`for...of`自动调用这个对象的`next`方法,得到一个Promise对象,`await`用来处理这个Promise,一但`resolve`就把得到的值`x`传到`for...of`里面。 +**用途** +直接把部署了asyncIteable操作的异步接口放入这个循环。 +```js +let a = ''; +async function f(){ + for await (let b of req) { + a += b; + } + let c = JSON.parse(a); + console.log('leo', c); +} +``` +当`next`返回的Promise对象被`reject`,`for await...of`就会保错,用`try...catch`捕获。 +```js +async function f(){ + try{ + for await (let a of iterableObj()){ + console.log(a); + } + }catch(e){ + console.error(e); + } +} +``` +注意,`for await...of`循环也可以用于同步遍历器。 +```js +(async function () { + for await (let a of ['a', 'b']) { + console.log(a); + } +})(); +// a +// b +``` +#### 4.3.3 异步Generator函数 +就像 Generator 函数返回一个同步遍历器对象一样,异步 Generator 函数的作用,是返回一个异步遍历器对象。 +在语法上,异步 Generator 函数就是`async`函数与 Generator 函数的结合。 +```js +async function* f() { + yield 'hi'; +} +const a = f(); +a.next().then(x => console.log(x)); +// { value: 'hello', done: false } +``` +设计异步遍历器的目的之一,就是为了让Generator函数能用同一套接口处理同步和异步操作。 +```js +// 同步Generator函数 +function * f(iterable, fun){ + let a = iterabl[Symbol.iterator](); + while(true){ + let {val, done} = a.next(); + if(done) break; + yield fun(val); + } +} + +// 异步Generator函数 +async function * f(iterable, fun){ + let a = iterabl[Symbol.iterator](); + while(true){ + let {val, done} = await a.next(); + if(done) break; + yield fun(val); + } +} +``` +同步和异步Generator函数相同点:在`yield`时用`next`方法停下,将后面表达式的值作为`next()`返回对象的`value`。 +在异步Generator函数中,同时使用`await`和`yield`,简单样理解,`await`命令用于将外部操作产生的值输入函数内部,`yield`命令用于将函数内部的值输出。 +```js +(async function () { + for await (const line of readLines(filePath)) { + console.log(line); + } +})() +``` +异步 Generator 函数可以与`for await...of`循环结合起来使用。 +```js +async function* f(asyncIterable) { + for await (const line of asyncIterable) { + yield '> ' + line; + } +} +``` + +#### 4.3.4 yield* 语句 +`yield*`语句跟一个异步遍历器。 +```js +async function * f(){ + yield 'a'; + yield 'b'; + return 'leo'; +} +async function * g(){ + const a = yield* f(); // a => 'leo' +} +``` +与同步 Generator 函数一样,`for await...of`循环会展开`yield*`。 +```js +(async function () { + for await (const x of gen2()) { + console.log(x); + } +})(); +// a +// b +``` + +[⬆ 返回目录](#二目录) + +## 5. 知识补充 +### 5.1 块级作用域 + +通常指一个**函数内部**,或者一个**代码块内部**。 +比如: +```js +function fun1 () { + // 块级作用域 + if (true) { + // 块级作用域 + } +} +``` +**缺点**: +1.导致内层变量覆盖外层变量 +```js +var a1 = new Date(); +function f1 (){ + console.log(a1); // undefined + if (false) { + var a1 = 'hello' + } +} +``` +输出 `undefined` 是因为 `if` 内的 `a1` 变量声明的变量提升,导致内部的 `a1` 覆盖外部的 `a1`,所以输出为 `undefined` 。 + +2.变量的全局污染 +```js +var a = 'hello'; +for (var i = 0; i< a.length; i++) { + //... +} +console.log(i); // 5 +``` +循环结束后,变量 `i` 的值依然存在,造成变量的全局污染。 + + +[⬆ 返回目录](#二目录) + + +### 5.2 ES5/6对数组空位的处理 + +数组的空位不是`undefined`,而是没有任何值,数组的`undefined`也是有值。 +```js +0 in [undefined,undefined,undefined] // true +0 in [,,,] // false +``` +**ES5对空位的处理**: +* `forEach()`, `filter()`, `reduce()`, `every()` 和`some()`都会跳过空位。 +* `map()`会跳过空位,但会保留这个值。 +* `join()`和`toString()`会将空位视为`undefined`,而`undefined`和`null`会被处理成空字符串。 +```js +[,'a'].forEach((x,i) => console.log(i)); // 1 +['a',,'b'].filter(x => true); // ['a','b'] +[,'a'].every(x => x==='a'); // true +[1,,2].reduce((x,y) => x+y); // 3 +[,'a'].some(x => x !== 'a'); // false +[,'a'].map(x => 1); // [,1] +[,'a',undefined,null].join('#'); // "#a##" +[,'a',undefined,null].toString(); // ",a,," +``` +**ES6对空位的处理**: +将空位视为正常值,转成`undefined`。 +```js +Array.from(['a',,'b']);// [ "a", undefined, "b" ] +[...['a',,'b']]; // [ "a", undefined, "b" ] + +//copyWithin() 会连空位一起拷贝。 +[,'a','b',,].copyWithin(2,0) // [,"a",,"a"] + +//fill()会将空位视为正常的数组位置。 +new Array(3).fill('a') // ["a","a","a"] + +//for...of循环也会遍历空位。 +let arr = [, ,]; +for (let i of arr) { + console.log(1); +} // 1 1 +``` +`entries()`、`keys()`、`values()`、`find()`和`findIndex()`会将空位处理成`undefined`。 +```js +[...[,'a'].entries()] // [[0,undefined], [1,"a"]] + +[...[,'a'].keys()] // [0,1] + +[...[,'a'].values()] // [undefined,"a"] + +[,'a'].find(x => true) // undefined + +[,'a'].findIndex(x => true) // 0 +``` +**由于空位的处理规则非常不统一,所以建议避免出现空位。** + + +# 四、结语 + +[回到顶部](#一介绍) + +### 参考文章 +* [ECMAScript 6 入门](http://es6.ruanyifeng.com/) +* [ES8 新特性一览](https://github.com/xitu/gold-miner/blob/master/TODO/es8-was-released-and-here-are-its-main-new-features.md) +* [ECMAScript 2017(ES8)特性概述](https://zhuanlan.zhihu.com/p/27844393) +* [ECMAScript 2018(ES2018)有哪些新功能?](https://www.imooc.com/article/44423) + +### 推荐文章 +* [ECMAScript 2018 标准导读](https://zhuanlan.zhihu.com/p/27537439) + +## 微信公众号 +![bg](http://images.pingan8787.com/2019_07_12guild_page.png) \ No newline at end of file diff --git a/Cute-JavaScript/Cute-ES/README.md b/Cute-JavaScript/Cute-ES/README.md new file mode 100644 index 00000000..8d050b0c --- /dev/null +++ b/Cute-JavaScript/Cute-ES/README.md @@ -0,0 +1,60 @@ +## 一、介绍 +现在的网络上已经有各样关于 **ECMAScript** 规范介绍和分析的文章,而我自己重新学习一遍这些规范,整理出这么一份笔记,比较精简,主要内容涵盖**ES6**、**ES7**、**ES8**、**ES9**,后续会增加**面试题**,**框架入门**等笔记,欢迎吐槽交流。 +这份资料的**ES6部分**将会参考阮一峰老师的 [ECMAScript6入门](http://es6.ruanyifeng.com/) ,精简和整理出快速实用的内容。 +另外**ES7/ES8/ES9**则会从网络综合参考和整理。 + +**ES全称ECMAScript**: +目前JavaScript使用的ECMAScript版本为[ECMAScript-262](https://www.ecma-international.org/ecma-262/)。 + +| ECMAScript版本 | 发布时间 | 新增特性 | +| ------ | ------ | ------ | +| ECMAScript 2009(ES5) | 2009年11月 | 扩展了Object、Array、Function的功能等 | +| ECMAScript 2015(ES6) | 2015年6月 | 类,模块化,箭头函数,函数参数默认值等 | +| ECMAScript 2016(ES7) | 2016年3月 | includes,指数操作符 | +| ECMAScript 2017(ES8) | 2017年6月 | sync/await,Object.values(),Object.entries(),String padding等 | + + +## 三、关于作者 +[![博客](http://images.pingan8787.com/icon_my1.png)](http://www.pingan8787.com) +[![知乎](http://images.pingan8787.com/icon_zhihu1.png)](https://zhuanlan.zhihu.com/cute-javascript) +[![掘金](http://images.pingan8787.com/icon_juejin2.png)](https://juejin.im/user/586fc337a22b9d0058807d53/posts) +[![思否](http://images.pingan8787.com/icon_sf1.png)](https://segmentfault.com/blog/pingan8787) +[![CSDN](http://images.pingan8787.com/icon_csdn1.png)](https://blog.csdn.net/qq_36380426) +[![简书](http://images.pingan8787.com/icon_jianshu1.png)](https://www.jianshu.com/u/2ec5d94afd60) + +## 二、目录 +- [1. ES6](https://github.com/pingan8787/Leo-JavaScript/blob/master/Cute-JavaScript/Cute-ES/1.ES6.md) + - [1.1 let 和 const命令](https://github.com/pingan8787/Leo-JavaScript/blob/master/Cute-JavaScript/Cute-ES/1.ES6.md) + - [1.2 变量的解构赋值](https://github.com/pingan8787/Leo-JavaScript/blob/master/Cute-JavaScript/Cute-ES/1.ES6.md) + - [1.3 字符串的拓展](https://github.com/pingan8787/Leo-JavaScript/blob/master/Cute-JavaScript/Cute-ES/1.ES6.md) + - [1.4 正则的拓展](https://github.com/pingan8787/Leo-JavaScript/blob/master/Cute-JavaScript/Cute-ES/1.ES6.md) + - [1.5 数值的拓展](https://github.com/pingan8787/Leo-JavaScript/blob/master/Cute-JavaScript/Cute-ES/1.ES6.md) + - [1.6 函数的拓展](https://github.com/pingan8787/Leo-JavaScript/blob/master/Cute-JavaScript/Cute-ES/1.ES6.md) + - [1.7 数组的拓展](https://github.com/pingan8787/Leo-JavaScript/blob/master/Cute-JavaScript/Cute-ES/1.ES6.md) + - [1.8 对象的拓展](https://github.com/pingan8787/Leo-JavaScript/blob/master/Cute-JavaScript/Cute-ES/1.ES6.md) + - [1.9 Symbol](https://github.com/pingan8787/Leo-JavaScript/blob/master/Cute-JavaScript/Cute-ES/1.ES6.md) + - [1.10 Set和Map数据结构](https://github.com/pingan8787/Leo-JavaScript/blob/master/Cute-JavaScript/Cute-ES/1.ES6.md) + - [1.11 Proxy](https://github.com/pingan8787/Leo-JavaScript/blob/master/Cute-JavaScript/Cute-ES/1.ES6.md) + - [1.12 Promise对象](https://github.com/pingan8787/Leo-JavaScript/blob/master/Cute-JavaScript/Cute-ES/1.ES6.md) + - [1.13 Iterator和 for...of循环](https://github.com/pingan8787/Leo-JavaScript/blob/master/Cute-JavaScript/Cute-ES/1.ES6.md) + - [1.14 Generator函数和应用](https://github.com/pingan8787/Leo-JavaScript/blob/master/Cute-JavaScript/Cute-ES/1.ES6.md) + - [1.15 Class语法和继承](https://github.com/pingan8787/Leo-JavaScript/blob/master/Cute-JavaScript/Cute-ES/1.ES6.md) + - [1.16 Module语法和加载实现](https://github.com/pingan8787/Leo-JavaScript/blob/master/Cute-JavaScript/Cute-ES/1.ES6.md) +- [2. ES7](https://github.com/pingan8787/Leo-JavaScript/blob/master/Cute-JavaScript/Cute-ES/2.ES7.md) + - [2.1 Array.prototype.includes()方法](https://github.com/pingan8787/Leo-JavaScript/blob/master/Cute-JavaScript/Cute-ES/2.ES7.md) + - [2.2 指数操作符(**)](https://github.com/pingan8787/Leo-JavaScript/blob/master/Cute-JavaScript/Cute-ES/2.ES7.md) +- [3. ES8](https://github.com/pingan8787/Leo-JavaScript/blob/master/Cute-JavaScript/Cute-ES/3.ES8.md) + - [3.1 async函数](https://github.com/pingan8787/Leo-JavaScript/blob/master/Cute-JavaScript/Cute-ES/3.ES8.md) + - [3.2 Promise.prototype.finally()](https://github.com/pingan8787/Leo-JavaScript/blob/master/Cute-JavaScript/Cute-ES/3.ES8.md) + - [3.3 Object.values(),Object.entries()](https://github.com/pingan8787/Leo-JavaScript/blob/master/Cute-JavaScript/Cute-ES/3.ES8.md) + - [3.4 Object.getOwnPropertyDescriptors()](https://github.com/pingan8787/Leo-JavaScript/blob/master/Cute-JavaScript/Cute-ES/3.ES8.md) + - [3.5 字符串填充 padStart和padEnd](https://github.com/pingan8787/Leo-JavaScript/blob/master/Cute-JavaScript/Cute-ES/3.ES8.md) + - [3.6 函数参数列表与调用中的尾部逗号](https://github.com/pingan8787/Leo-JavaScript/blob/master/Cute-JavaScript/Cute-ES/3.ES8.md) + - [3.7 共享内存与原子操作](https://github.com/pingan8787/Leo-JavaScript/blob/master/Cute-JavaScript/Cute-ES/3.ES8.md) +- [4. ES9](https://github.com/pingan8787/Leo-JavaScript/blob/master/Cute-JavaScript/Cute-ES/4.ES9.md) + - [4.1 对象的拓展运算符](https://github.com/pingan8787/Leo-JavaScript/blob/master/Cute-JavaScript/Cute-ES/4.ES9.md) + - [4.2 正则表达式 s 修饰符](https://github.com/pingan8787/Leo-JavaScript/blob/master/Cute-JavaScript/Cute-ES/4.ES9.md) + - [4.3 异步遍历器](https://github.com/pingan8787/Leo-JavaScript/blob/master/Cute-JavaScript/Cute-ES/4.ES9.md) + + +![bg](http://images.pingan8787.com/2019_07_12guild_page.png) \ No newline at end of file diff --git a/Cute-JavaScript/Cute-JS/README.md b/Cute-JavaScript/Cute-JS/README.md new file mode 100644 index 00000000..1baf0f5f --- /dev/null +++ b/Cute-JavaScript/Cute-JS/README.md @@ -0,0 +1,65 @@ +最近开始把精力放在重新复习JavaScript的基础知识上面,不再太追求各种花枝招展的前端框架,框架再多,适合实际项目才是最重要。 + + +上星期在掘金发布了几篇文章,其中最大块算是 [【复习资料】ES6/ES7/ES8/ES9资料整理(个人整理)](https://juejin.im/post/5c02b106f265da61764aa0c1),也是让我好好把这些规范复习了一遍,虽然不是完全的原创,而是自己的一些复习笔记,但是还是让我感觉还是挺有用的,在项目开发过程中,有有意识的去使用到这些新的规范。 + + +这次开始复习 [MDN](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide) 这个系列的文章,巩固好自己的基础,也让自己养成记录学习的习惯,当然这些文章我也会同步到自己的博客。 + +## 关于作者 +[![博客](http://images.pingan8787.com/icon_my1.png)](http://www.pingan8787.com) +[![知乎](http://images.pingan8787.com/icon_zhihu1.png)](https://zhuanlan.zhihu.com/cute-javascript) +[![掘金](http://images.pingan8787.com/icon_juejin2.png)](https://juejin.im/user/586fc337a22b9d0058807d53/posts) +[![思否](http://images.pingan8787.com/icon_sf1.png)](https://segmentfault.com/blog/pingan8787) +[![CSDN](http://images.pingan8787.com/icon_csdn1.png)](https://blog.csdn.net/qq_36380426) +[![简书](http://images.pingan8787.com/icon_jianshu1.png)](https://www.jianshu.com/u/2ec5d94afd60) + +## 本文目录 +### JavaScript初级 +1. [语法和数据类型](https://github.com/pingan8787/Leo-JavaScript/tree/master/Cute-JavaScript/Cute-JS/level1/1.%E8%AF%AD%E6%B3%95%E5%92%8C%E6%95%B0%E6%8D%AE%E7%B1%BB%E5%9E%8B.md) +2. [流程控制和错误处理](https://github.com/pingan8787/Leo-JavaScript/tree/master/Cute-JavaScript/Cute-JS/level1/2.%E6%B5%81%E7%A8%8B%E6%8E%A7%E5%88%B6%E5%92%8C%E9%94%99%E8%AF%AF%E5%A4%84%E7%90%86.md) +3. [循环和迭代](https://github.com/pingan8787/Leo-JavaScript/tree/master/Cute-JavaScript/Cute-JS/level1/3.%E5%BE%AA%E7%8E%AF%E5%92%8C%E8%BF%AD%E4%BB%A3.md) +4. [函数](https://github.com/pingan8787/Leo-JavaScript/tree/master/Cute-JavaScript/Cute-JS/level1/4.%E5%87%BD%E6%95%B0.md) +5. [表达式和运算符](https://github.com/pingan8787/Leo-JavaScript/tree/master/Cute-JavaScript/Cute-JS/level1/5.%E8%A1%A8%E8%BE%BE%E5%BC%8F%E5%92%8C%E8%BF%90%E7%AE%97%E7%AC%A6.md) +6. [数字](https://github.com/pingan8787/Leo-JavaScript/tree/master/Cute-JavaScript/Cute-JS/level1/6.数字.md) +7. [时间对象](https://github.com/pingan8787/Leo-JavaScript/tree/master/Cute-JavaScript/Cute-JS/level1/7.时间对象.md) +8. [字符串](https://github.com/pingan8787/Leo-JavaScript/tree/master/Cute-JavaScript/Cute-JS/level1/8.字符串.md) +9. [正则表达式](https://github.com/pingan8787/Leo-JavaScript/tree/master/Cute-JavaScript/Cute-JS/level1/9.正则表达式.md) +10. [数组](https://github.com/pingan8787/Leo-JavaScript/tree/master/Cute-JavaScript/Cute-JS/level1/10.数组.md) +11. [Map和Set对象](https://github.com/pingan8787/Leo-JavaScript/tree/master/Cute-JavaScript/Cute-JS/level1/11.Map和Set对象.md) +12. [使用对象](https://github.com/pingan8787/Leo-JavaScript/tree/master/Cute-JavaScript/Cute-JS/level1/12.使用对象.md) +13. [迭代器和生成器](https://github.com/pingan8787/Leo-JavaScript/tree/master/Cute-JavaScript/Cute-JS/level1/13.迭代器和生成器.md) +14. [元编程](https://github.com/pingan8787/Leo-JavaScript/tree/master/Cute-JavaScript/Cute-JS/level1/14.元编程.md) + +### JavaScript中级 +15. [JS对象介绍](https://github.com/pingan8787/Leo-JavaScript/tree/master/Cute-JavaScript/Cute-JS/level2/1.JS对象介绍.md) +16. [JSON对象介绍](https://github.com/pingan8787/Leo-JavaScript/tree/master/Cute-JavaScript/Cute-JS/level2/2.JSON对象介绍.md) +17. [WebAPI介绍](https://github.com/pingan8787/Leo-JavaScript/tree/master/Cute-JavaScript/Cute-JS/level2/3.WebAPI介绍.md) +18. [相等性判断](https://github.com/pingan8787/Leo-JavaScript/tree/master/Cute-JavaScript/Cute-JS/level2/4.相等性判断.md) +19. [闭包](https://github.com/pingan8787/Leo-JavaScript/tree/master/Cute-JavaScript/Cute-JS/level2/5.闭包.md) + +### JavaScript高级 +20. [事件](https://github.com/pingan8787/Leo-JavaScript/tree/master/Cute-JavaScript/Cute-JS/level3/1.事件.md) +21. [高阶函数](https://github.com/pingan8787/Leo-JavaScript/tree/master/Cute-JavaScript/Cute-JS/level3/2.高阶函数.md) +22. [内存管理](https://github.com/pingan8787/Leo-JavaScript/tree/master/Cute-JavaScript/Cute-JS/level3/3.内存管理.md) + +更新中 + +### 其他细节知识 +1. [JS中的高阶函数](https://github.com/pingan8787/Leo-JavaScript/blob/master/base-javascript/other/1-JS中的高阶函数.md) +2. [instanceof运算符](https://github.com/pingan8787/Leo-JavaScript/blob/master/base-javascript/other/2-instanceof运算符.md) + + +希望自己的文章会对各位有所帮助,也欢迎各位大佬指点。 + + +|Author|王平安| +|---|---| +|E-mail|pingan8787@qq.com| +|博 客|www.pingan8787.com| +|微 信|pingan8787| +|每日文章推荐|https://github.com/pingan8787/Leo_Reading/issues| +|ES小册|js.pingan8787.com| + +## 微信公众号 +![bg](http://images.pingan8787.com/2019_07_12guild_page.png) \ No newline at end of file diff --git "a/Cute-JavaScript/Cute-JS/level1/1.\350\257\255\346\263\225\345\222\214\346\225\260\346\215\256\347\261\273\345\236\213.md" "b/Cute-JavaScript/Cute-JS/level1/1.\350\257\255\346\263\225\345\222\214\346\225\260\346\215\256\347\261\273\345\236\213.md" new file mode 100644 index 00000000..70b85c6b --- /dev/null +++ "b/Cute-JavaScript/Cute-JS/level1/1.\350\257\255\346\263\225\345\222\214\346\225\260\346\215\256\347\261\273\345\236\213.md" @@ -0,0 +1,330 @@ +最近开始把精力放在重新复习JavaScript的基础知识上面,不再太追求各种花枝招展的前端框架,框架再多,适合实际项目才是最重要。 +上星期在掘金发布了几篇文章,其中最大块算是 [【复习资料】ES6/ES7/ES8/ES9资料整理(个人整理)](https://juejin.im/post/5c02b106f265da61764aa0c1),也是让我好好把这些规范复习了一遍,虽然不是完全的原创,而是自己的一些复习笔记,但是还是让我感觉还是挺有用的,在项目开发过程中,有有意识的去使用到这些新的规范。 +这次开始复习 [MDN](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide) 这个系列的文章,巩固好自己的基础,也让自己养成记录学习的习惯,当然这些文章我也会同步到自己的博客。 +希望自己的文章会对各位有所帮助,也欢迎各位大佬指点。 + + +---- +正文开始 + +**本章节复习的是JS中的基本语法,变量声明,数据类型和字面量。** + +首先要记住:JavaScript 对大小写敏感,即`var att;` 和 `var Att` 是两个不同变量。 + +# 1.注释 +```js +// 单行注释 +/* + 多行注释 +*/ +``` + +# 2. 声明 +JavaScript有三种声明方式: +* `var` 声明一个变量,可赋一个初始值。 +* `let` 声明一个块作用域的局部变量,可赋一个初始值。 +* `const` 声明一个块作用域的只读命名的常量。 + +## 声明变量 +变量的名字又叫做“**标识符**”,必须以字母、下划线(`_`)或者美元符号(`$`)开头,并且区分大小写。 +变量声明有三种方式: +* 如`var a = 1`,声明局部变量和全局变量。 +* 如`a = 1`,声明一个全局变量,且在严格模式报错,不应该使用。 +* 如`let a = 1`,声明一个块作用域的局部变量。 + +**注意**: +* 若没有为变量赋初始值,则值默认为`undefined`; +```js +let a; +console.log(a); // undefined +``` +* 若没有声明变量而直接使用,会抛出`ReferenceError`错误; +```js +console.log(b); // Uncaught ReferenceError: b is not defined +``` +* 当变量值为`undefined`时,布尔值环境会当做`false`,数值环境会当做`NaN`; +```js +var a; +if(!a){ + console.log('a为undefined'); // a为undefined +} +a + 1; // NaN +``` +* 当变量值为`null`时,布尔值环境会当做`false`,数值环境会当做`0`; +```js +let a = null; +if(!a){ + console.log('a为unll'); // a为unll +} +a + 1; // 1 +``` + +## 变量作用域 +**全局变量**:即声明在函数之外,当前文档所有地方都可以访问; +**局部遍历**:即声明在函数内部,仅在当前函数内可以访问; +在ES5之前没有语句**块作用域**的概念,并只能使用`var`进行声明,用`var`声明的变量,在函数内和全局都可以访问,而在ES6开始,将只能在声明的作用域中使用: +```js +if(true){ + var a = 1; +} +a; // 1 + +if(true){ + let b = 2; +} +b; // ReferenceError: b is not defined +``` + +## 变量声明提前 +即将变量的声明提升到函数或语句的顶部,并返回`undefined`直到变量被初始化操作。 +**千万注意**: +ES5以及之前,才有变量声明提前,在ES6开始就不存在变量提升。 +```js +// ES5及之前 +console.log(a); // undefined +var a = 1; +console.log(a); // 1 + +// ES6开始 +console.log(b); // Uncaught ReferenceError: b1 is not defined +let b = 2; +console.log(b); // 2 +``` + +## 函数提升 +函数声明有两种方式:**函数声明**和**函数表达式**两种方式: +```js +// 函数声明 +f(); // 'hi leo' +function(){ + console.log('hi leo'); +}; + +// 函数表达式 +g(); // Uncaught TypeError: g is not a function +var g = function(){ // 换成 let 声明也一样 + console.log('hi leo'); +} +``` + +## 全局变量 +全局变量默认是全局对象(`window`)的属性,常常使用`window.variable`语法来设置和访问全局变量。 +这边还需要记住: +* **ES5之中**,顶层对象的属性等价于全局变量(浏览器环境中顶层对象是`window`,Node中是`global`对象); +* **ES6之中**,`var`/`function`声明的全局变量,依然是顶层对象的属性,但是`let`/`const`/`class`声明的全局变量不属于顶层对象的属性,即ES6开始,全局变量和顶层对象的属性是分开的。 +```js +// ES5 +var a = 'leo'; +window.a; // 'leo' + +// ES6 +let b = 'leo'; +window.b; // undefined +``` + +## 常量 +ES6之后我们可以使用`const`来声明一个只读的常量,并且在**声明时必须赋值**,之后在相同作用域中**不能赋值**也**不能重新声明**,否则报错。 +```js +const a; +// Uncaught SyntaxError: Missing initializer in const declaration + +const b = 'leo'; +b = 'hi'; +// Uncaught TypeError: Assignment to constant variable. + +function f(){ + const a1 = 'hi'; + console.log(a1); +} +f(); // 'hi' +const a1 = 'hi leo'; +a1; // "hi leo" +``` +尽管`const`声明的变量不能直接修改值,但是对于对象和数组,却是不受保护可以修改的: +```js +const a = {name:'leo',age:25}; +a.name = 'pingan'; // a => {name: "pingan", age: 25} + +const b = ['hi', 'leo']; +b[1] = 'pingan'; // b => ["hi", "pingan"] +``` + +# 3.数据结构和类型 +## 数据类型 +JavaScript中一共分为**7**中不同类型: +* 六种**原型**数据类型: + * 1.Boolean : 布尔值,true和false; + * 2.null : 对大小写敏感(即`null`/`Null`/`NULL`完全不同); + * 3.undefined : 空类型,变量未定义时候的值; + * 4.Number : 数值类型,如`100`; + * 5.String : 字符串类型,如'hi pingan'; + * 6.Symbol(ES6新增) : 表示一种唯一且不可变的数据; +* 以及Object对象类型 + +## 数据类型转换 +由于JavaScript是门动态类型语言,因此在开发过程可以不需要指定数据类型,在执行时会自动转换: +```js +var a = 100; +a = 'hi leo'; // 这样不报错 +``` +另外还有: +```js +let a1 = '10'; +let b1 = 20; +a1 + b1; // 30 + +let a2 = 'leo ' + 10 + ' age'; // 'leo 10 age' + +'10' - 5; // 5 +'10' + 5; // 105 +``` +**转换字符串为数字小技巧** +小技巧很多,这里说个最简单的: +```js +// 这样不会使两个数字计算总和: +'1.1' + '1.2'; // '1.11.2' + +// 实际上要这样: ++'1.1' + +'1.2'; // 2.3 +``` + +# 4.字面量 +字面量是用来表示**如何表达这个值**,简单理解就是**变量赋值时右边的都是字面量**。比如: +```js +let a = 'hi leo'; +``` +`hi leo`为字符串字面量,`a`为变量名。 +字面量分为七种: +* 1.数组字面量 +* 2.布尔字面量 +* 3.浮点数字面量 +* 4.整数字面量 +* 5.对象字面量 +* 6.正则字面量 +* 7.字符串字面量 + +## 数组字面量 +使用数组字面量创建数组的时,指定元素的值,并作为数组的初始化,从而确定数组长度。 +```js +let a = ['hi','leo','hello','pingan']; +a[1]; // 'leo' +a.length; // 4 +``` +若使用多余逗号,作为数组字面量,则值为`undefined`,并且数组长度也会正常计算: +```js +let a = ['hi', ,'leo']; +a[0]; // 'hi' +a[1]; // undefined +a.length; // 3 +``` + +## 布尔字面量 +只有`true`和`false`: +```js +let a = true; +``` + +## 整数字面量 +整数可以用十进制(基数为10)、十六进制(基数为16)、八进制(基数为8)以及二进制(基数为2)表示。 + +## 浮点数字面量 +浮点数字面量组成: +* 一个十进制的整数,可以带正负号; +* 小数点 +* 小数部分(只能十进制) +* 指数部分 +```js +let a = 3.14; // 3.14 +let b = -.001; // -0.001 +let c = -3.14e+12; // -3.14*1012 +let d = .1e-23;// 0.1*10 - 23 = 10-24 = 1e-24 +``` + +## 对象字面量 +对象字面量是由`{}`包含一个或多个 `键:值` 对的列表: +```js +let a1 = 'hi leo'; +let a2 = function(){ return 'my name is pingan' }; + +let obj = { + n1 : 'pingan', + n2 : a1, + n3 : a2() +} +obj; // {n1: "pingan", n2: "hi leo", n3: "my name is pingan"} +``` +也可以使用任意数字或字符串作为对象属性的名字,但必须用`''`引号包裹: +```js +let obj = { + "" : "hi leo", + "!" : "hi pingan", + 2 : 'hi number' +} +obj; // {2: "hi number", "": "hi leo", !: "hi pingan"} +obj[""]; // "hi leo" +obj[2]; // "hi number" +``` + +## 正则字面量 +使用字符被正斜杠“`/`”围起来的表达式: +```js +var a = /ab+c/; +``` + +## 字符串字面量 +使用单引号(`''`)或者双引号(`""`)括起来的字符串: +```js +let a = 'leo'; +a.length; // 3 +``` +ES6中新增了**模板字符串**,作用于: +* 方便拼接字符串 +* 有效实现字符串换行 +* 防止注入攻击 +* 建立基于字符串的高级数据抽象 +```js +// 拼接字符串 +let name = 'leo'; +let a = ` + hi ${name} +`; +a; // 'hi leo' + +// 换行 +let b = ` + hi + leo +`; +b; +// " +// hi +// leo +// " +``` +**常用特殊字符**: +|字符|含义| +|---|---| +|\b|退格符| +|\f|换页符| +|\n|换行符| +|\r|回车符| +|\t|Tab (制表符)| +|\v|垂直制表符| +|\'|单引号| +|\"|双引号| +|\\|反斜杠字符(\)| + + +--- +# 参考文章: +[1.MDN 语法和数据类型](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/Grammar_and_Types) + +**本部分内容到这结束** + +|Author|王平安| +|---|---| +|E-mail|pingan8787@qq.com| +|博 客|www.pingan8787.com| +|微 信|pingan8787| +|每日文章推荐|https://github.com/pingan8787/Leo_Reading/issues| +|ES小册|js.pingan8787.com| \ No newline at end of file diff --git "a/Cute-JavaScript/Cute-JS/level1/10.\346\225\260\347\273\204.md" "b/Cute-JavaScript/Cute-JS/level1/10.\346\225\260\347\273\204.md" new file mode 100644 index 00000000..da2f63ff --- /dev/null +++ "b/Cute-JavaScript/Cute-JS/level1/10.\346\225\260\347\273\204.md" @@ -0,0 +1,657 @@ +本文是 **重温基础** 系列文章的第十篇。 +今日感受:平安夜,多棒。 + +**本章节复习的是JS中的数组,以索引进行排序。** + +前置知识: +数组是一个有序的数据集合,使用数组名称和索引进行访问。 +```js +let arr = [1,2,3]; +arr[0] = 1; +``` +在JavaScript中数组没有明确数据类型。 +```js +let arr = [1, 'hi', undefined, fun()]; +``` + +# 1.创建数组 +创建数组方法有3种: +```js +let arr = new Array(ele1, ele2, ele3, ..., eleN); +let arr = Array(ele1, ele2, ele3, ..., eleN); +let arr = [ele1, ele2, ele3, ..., eleN]; +``` +上面是已知数组元素,另外一种还有**创建一个长度不为0,且有没有任何元素的数组**: +```js +let len = 5; + +let arr = new Array(len); // 方法1 +let arr = Array(len); // 方法2 +let arr = []; // 方法3 +arr.length = len; +``` +若传入的数组长度不是整数,则报错: +```js +let arr = new Array(3.5); +let arr = Array(3.5); +let arr = []; +arr.length = 3.5; +//Uncaught RangeError: Invalid array length +``` +其中要注意这两种创建方法是不同的: +```js +let arr1 = new Array(4); // [empty × 4] +let arr2 = [4]; // [4] +for(let k in arr1){ + console.log(k); +} // undefined +for(let k in arr2){ + console.log(k); +} // 0 +``` + +# 2.使用数组 +## 2.1 简单使用 +获取数组指定位置的值: +```js +let a = [1,2,5]; +a[0]; // 1 +a[2]; // 5 +a[3]; // undefined +``` + +获取数组长度: +```js +let a = [1,2,5]; +a.length; // 3 +a["length"]; // 3 +``` +设置数组指定位置的值: +```js +let a = [1,2,5]; +a[0] = 9; +a[2] = 99; +a[3] = 999; +``` + +## 2.2 理解数组length +* 数组的索引值是从`0`开始,即上面数组索引`0`的是`1`,索引`1`的值是`2`,依次下去。 +* 数组`length`永远返回的是数组最后一个元素的索引加1。 +* 可通过`arr.length = 0`来清空数组。 +* 可通过`arr.length = len`来设置数组长度。 + +## 2.3 遍历数组 +遍历数组就是以某种方法处理数组的每个元素,简单如下: +* 使用`for`循环: +```js +let arr = ["pingan", "leo", "robin"]; +for (let i = 0; i  [2, 3] +// arr => [1, "hi", "leo", 4] +``` + +## 3.8 sort() +对数组的元素进行排序,**改变原数组**。 +可接受一个回调方法作为比较函数,来决定排序方式。 +比较函数应该具有两个参数 `a` 和 `b`,返回值如下: +若 `a` 小于 `b`,在排序后的数组中 a 应该出现在 b 之前,则返回一个小于 0 的值。 +若 `a` 等于 `b`,则返回 0。 +若 `a` 大于 `b`,则返回一个大于 0 的值。 +```js +let a1 = [1,3,6,9,10]; +a1.sort(); // [1, 10, 3, 6, 9] +a1.sort(function(a,b){ + return a > b ? 1 : a < b ? -1 : 0; +}) // [1, 3, 6, 9, 10] +``` + +## 3.9 indexOf()和lastIndexOf() +两者都是在数组搜索指定元素,只是`indexOf()`返回的是搜索到的**第一个元素**的索引,而`lastIndexOf()`返回的是搜索到的**最后一个元素**的索引。 +语法: +`indexOf(ele[,start])`和`lastIndexOf(ele[,start])`; +参数: +* `ele`: 需要搜索的元素。 +* `start`: 开始搜索的索引。 +```js +let arr = ["hh1", "hh2", "hh2", "hh2", "hh3", "hh4"]; +let a1 = arr.indexOf("hh2"); // 1 +let a2 = arr.lastIndexOf("hh2"); // 3 +let a3 = arr.indexOf("hh2",2); // 2 +``` + +# 4. 数组方法(迭代) + +|方法名称|方法介绍| +|---|---| +|`forEach()`|为数组中的每个元素执行一次回调函数。| +|`every()`|如果数组中的每个元素都满足测试函数,则返回 true,否则返回 false。| +|`some()`|如果数组中至少有一个元素满足测试函数,则返回 true,否则返回 false。| +|`filter()`|将所有在过滤函数中返回 true 的数组元素放进一个新数组中并返回。| +|`map()`|返回一个由回调函数的返回值组成的新数组。| +|`reduce()`|从左到右为每个数组元素执行一次回调函数,并把上次回调函数的返回值放在一个暂存器中传给下次回调函数,并返回最后一次回调函数的返回值。| +|`reduceRight()`|从右到左为每个数组元素执行一次回调函数,并把上次回调函数的返回值放在一个暂存器中传给下次回调函数,并返回最后一次回调函数的返回值。| + +**以下是ES6规范新增的数组方法:** + +|方法名称|方法介绍| +|---|---| +|`keys() `|返回一个数组迭代器对象,该迭代器会包含所有数组元素的键。| +|`values() `|返回一个数组迭代器对象,该迭代器会包含所有数组元素的值。| +|`entries() `|返回一个数组迭代器对象,该迭代器会包含所有数组元素的键值对。| +|`find() `|找到第一个满足测试函数的元素并返回那个元素的值,如果找不到,则返回 undefined。| +|`findIndex() `|找到第一个满足测试函数的元素并返回那个元素的索引,如果找不到,则返回 -1。| + +可参考[MDN Array](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array) 的详细介绍。 + +## 4.1 forEach() +对数组的每个元素执行一次提供的函数。 + +语法: +`arr.forEach(callback)`。 + +参数: +`callback(val, index, arr)` : 需要执行的函数,接收三个参数: +* `val` : 正在处理的当前元素; +* `index` : 可选,正在处理的当前元素的索引; +* `arr` : 可选,正在操作的数组; + +```js +let a = [1,3,5,7]; +a.forEach(function(val, index, arr){ + arr[index] = val * 2 +}) +a ; // [2, 6, 10, 14] +``` + +## 4.2 every() +测试数组的所有元素是否**都通过**了指定函数的测试。 +语法: +`arr.every(callback)`。 + +参数: +`callback(val, index, arr)` : 需要执行的函数,接收三个参数: +* `val` : 正在处理的当前元素; +* `index` : 可选,正在处理的当前元素的索引; +* `arr` : 可选,正在操作的数组; + +返回值: +若都通过返回`true`,否则返回`false`。 + +```js +let a = [1, "", "aa", 13, 6]; +let res = a.every(function(val, index, arr){ + return typeof val == "number"; +}) +res;// false + +let b = [1, 2, 3]; +let r = b.every(function(val, index, arr){ + return typeof val == "number"; +}) +r; // true +``` + +## 4.3 some() +测试数组中的某些元素是否通过由提供的函数实现的测试。 +语法: +`arr.some(callback)`。 + +参数: +`callback(val, index, arr)` : 需要执行的函数,接收三个参数: +* `val` : 正在处理的当前元素; +* `index` : 可选,正在处理的当前元素的索引; +* `arr` : 可选,正在操作的数组; + +返回值: +若有一个通过返回`true`,否则返回`false`。 + +```js +let a = [1, "", "aa", 13, 6]; +let res = a.some(function(val, index, arr){ + return typeof val == "number"; +}) +res;// true + +let b = [1, 2, 3]; +let r = b.some(function(val, index, arr){ + return typeof val == "number"; +}) +r; // true +``` + +## 4.4 filter() +将所有在过滤函数中返回 `true` 的数组元素放进一个新数组中并返回。 + +语法: +`arr.filter(callback)`。 + +参数: +`callback(val, index, arr)` : 需要执行的函数,接收三个参数: +* `val` : 正在处理的当前元素; +* `index` : 可选,正在处理的当前元素的索引; +* `arr` : 可选,正在操作的数组; + +返回值: +一个返回通过测试的元素的数组,若都没有则返回空数组。 + +```js +let a = [1, "", "aa", 13, 6]; +let res = a.filter(function(val, index, arr){ + return typeof val == "number"; +}) +res;//[1, 13, 6] +``` + +## 4.5 map() +传入一个操作函数,对每个元素执行此方法,并返回一个执行后的数组。 + +语法: +`arr.map(callback)`。 + +参数: +`callback(val, index, arr)` : 需要执行的函数,接收三个参数: +* `val` : 正在处理的当前元素; +* `index` : 可选,正在处理的当前元素的索引; +* `arr` : 可选,正在操作的数组; + +返回值: +一个新数组,每个元素都是回调函数的结果。 + +```js +let a = [1, 3, 5]; +let b = a.map(function(val, index, arr){ + return val + 2; +}) +b; //[3, 5, 7] +``` + + +# 5. 数组的拓展(ES6) +## 5.1 拓展运算符 +拓展运算符使用(`...`),类似`rest`参数的逆运算,将数组转为用(`,`)分隔的参数序列。 +```js +console.log(...[1, 2, 3]); // 1 2 3 +console.log(1, ...[2,3], 4); // 1 2 3 4 +``` +拓展运算符主要使用在函数调用。 +```js +function f (a, b){ + console.log(a, b); +} +f(...[1, 2]); // 1 2 + +function g (a, b, c, d, e){ + console.log(a, b, c, d, e); +} +g(0, ...[1, 2], 3, ...[4]); // 0 1 2 3 4 +``` +**若拓展运算符后面是个空数组,则不产生效果**。 +```js +[...[], 1]; // [1] +``` + +**替代apply方法** +```js +// ES6之前 +function f(a, b, c){...}; +var a = [1, 2, 3]; +f.apply(null, a); + +// ES6之后 +function f(a, b, c){...}; +let a = [1, 2, 3]; +f(...a); + +// ES6之前 +Math.max.apply(null, [3,2,6]); + +// ES6之后 +Math.max(...[3,2,6]); +``` + +**拓展运算符的运用** +* **(1)复制数组**: +通常我们直接复制数组时,只是浅拷贝,如果要实现深拷贝,可以使用拓展运算符。 +```js +// 通常情况 浅拷贝 +let a1 = [1, 2]; +let a2 = a1; +a2[0] = 3; +console.log(a1,a2); // [3,2] [3,2] + +// 拓展运算符 深拷贝 +let a1 = [1, 2]; +let a2 = [...a1]; +// let [...a2] = a1; // 作用相同 +a2[0] = 3; +console.log(a1,a2); // [1,2] [3,2] +``` +* **(2)合并数组**: +注意,这里合并数组,只是浅拷贝。 +```js +let a1 = [1,2]; +let a2 = [3]; +let a3 = [4,5]; + +// ES5 +let a4 = a1.concat(a2, a3); + +// ES6 +let a5 = [...a1, ...a2, ...a3]; + +a4[0] === a1[0]; // true +a5[0] === a1[0]; // true +``` +* **(3)与解构赋值结合**: +与解构赋值结合生成数组,但是使用拓展运算符需要放到参数最后一个,否则报错。 +```js +let [a, ...b] = [1, 2, 3, 4]; +// a => 1 b => [2,3,4] + +let [a, ...b] = []; +// a => undefined b => [] + +let [a, ...b] = ["abc"]; +// a => "abc" b => [] +``` + +## 5.2 Array.from() +将 **类数组对象** 和 **可遍历的对象**,转换成真正的数组。 +```js +// 类数组对象 +let a = { + '0':'a', + '1':'b', + length:2 +} +let arr = Array.from(a); + +// 可遍历的对象 +let a = Array.from([1,2,3]); +let b = Array.from({length: 3}); +let c = Array.from([1,2,3]).map(x => x * x); +let d = Array.from([1,2,3].map(x => x * x)); +``` + +## 5.3 Array.of() +将一组数值,转换成**数组**,弥补`Array`方法参数不同导致的差异。 +```js +Array.of(1,2,3); // [1,2,3] +Array.of(1).length; // 1 + +Array(); // [] +Array(2); // [,] 1个参数时,为指定数组长度 +Array(1,2,3); // [1,2,3] 多于2个参数,组成新数组 +``` + +## 5.4 find()和findIndex() +`find()`方法用于找出第一个符合条件的数组成员,参数为一个回调函数,所有成员依次执行该回调函数,返回第一个返回值为`true`的成员,如果没有一个符合则返回`undefined`。 +```js +[1,2,3,4,5].find( a => a < 3 ); // 1 +``` +回调函数接收三个参数,当前值、当前位置和原数组。 +```js +[1,2,3,4,5].find((value, index, arr) => { + // ... +}); +``` +`findIndex()`方法与`find()`类似,返回第一个符合条件的数组成员的**位置**,如果都不符合则返回`-1`。 +```js +[1,2,3,4].findIndex((v,i,a)=>{ + return v>2; +}); // 2 +``` + +## 5.5 fill() +用于用指定值**填充**一个数组,通常用来**初始化空数组**,并抹去数组中已有的元素。 +```js +new Array(3).fill('a'); // ['a','a','a'] +[1,2,3].fill('a'); // ['a','a','a'] +``` +并且`fill()`的第二个和第三个参数指定填充的**起始位置**和**结束位置**。 +```js +[1,2,3].fill('a',1,2);// [1, "a", 3] +``` + +## 5.6 entries(),keys(),values() +主要用于遍历数组,`entries()`对键值对遍历,`keys()`对键名遍历,`values()`对键值遍历。 +```js +for (let i of ['a', 'b'].keys()){ + console.log(i) +} +// 0 +// 1 + +for (let e of ['a', 'b'].values()){ + console.log(e) +} +// 'a' +// 'b' + +for (let e of ['a', 'b'].entries()){ + console.log(e) +} +// 0 'a' +// 1 'b' +``` + +## 5.7 includes() +用于表示数组是否包含给定的值,与字符串的`includes`方法类似。 +```js +[1,2,3].includes(2); // true +[1,2,3].includes(4); // false +[1,2,NaN].includes(NaN); // true +``` +第二个参数为**起始位置**,默认为`0`,如果负数,则表示倒数的位置,如果大于数组长度,则重置为`0`开始。 +```js +[1,2,3].includes(3,3); // false +[1,2,3].includes(3,4); // false +[1,2,3].includes(3,-1); // true +[1,2,3].includes(3,-4); // true +``` + +## 5.8 flat(),flatMap() +`flat()`用于将数组一维化,返回一个新数组,不影响原数组。 +默认一次只一维化一层数组,若需多层,则传入一个整数参数指定层数。 +若要一维化所有层的数组,则传入`Infinity`作为参数。 +```js +[1, 2, [2,3]].flat(); // [1,2,2,3] +[1,2,[3,[4,[5,6]]]].flat(3); // [1,2,3,4,5,6] +[1,2,[3,[4,[5,6]]]].flat('Infinity'); // [1,2,3,4,5,6] +``` +`flatMap()`是将原数组每个对象先执行一个函数,在对返回值组成的数组执行`flat()`方法,返回一个新数组,不改变原数组。 + `flatMap()`只能展开一层。 +```js +[2, 3, 4].flatMap((x) => [x, x * 2]); +// [2, 4, 3, 6, 4, 8] +``` + + +# 6. 数组的拓展(ES7) +## 6.1 Array.prototype.includes()方法 +`includes()`用于查找一个值是否在数组中,如果在返回`true`,否则返回`false`。 +```js +['a', 'b', 'c'].includes('a'); // true +['a', 'b', 'c'].includes('d'); // false +``` +`includes()`方法接收两个参数,**搜索的内容**和**开始搜索的索引**,默认值为**0**,若搜索值在数组中则返回`true`否则返回`false`。 +```js +['a', 'b', 'c', 'd'].includes('b'); // true +['a', 'b', 'c', 'd'].includes('b', 1); // true +['a', 'b', 'c', 'd'].includes('b', 2); // false +``` +与`indexOf`方法对比,下面方法效果相同: +```js +['a', 'b', 'c', 'd'].indexOf('b') > -1; // true +['a', 'b', 'c', 'd'].includes('b'); // true +``` +**includes()与indexOf对比:** +* `includes`相比`indexOf`更具语义化,`includes`返回的是是否存在的具体结果,值为布尔值,而`indexOf`返回的是搜索值的下标。 +* `includes`相比`indexOf`更准确,`includes`认为两个`NaN`相等,而`indexOf`不会。 +```js +let a = [1, NaN, 3]; +a.indexOf(NaN); // -1 +a.includes(NaN); // true +``` +另外在判断`+0`与`-0`时,`includes`和`indexOf`的返回相同。 +```js +[1, +0, 3, 4].includes(-0); // true +[1, +0, 3, 4].indexOf(-0); // 1 +``` + +# 参考资料 +1.[MDN 索引集合类](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/Indexed_collections) +2.[MDN 数组对象](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array) +3.[W3school JavaScript Array 对象](http://www.w3school.com.cn/jsref/jsref_obj_array.asp) + +--- +**本部分内容到这结束** + +|Author|王平安| +|---|---| +|E-mail|pingan8787@qq.com| +|博 客|www.pingan8787.com| +|微 信|pingan8787| +|每日文章推荐|https://github.com/pingan8787/Leo_Reading/issues| +|ES小册|js.pingan8787.com| \ No newline at end of file diff --git "a/Cute-JavaScript/Cute-JS/level1/11.Map\345\222\214Set\345\257\271\350\261\241.md" "b/Cute-JavaScript/Cute-JS/level1/11.Map\345\222\214Set\345\257\271\350\261\241.md" new file mode 100644 index 00000000..7389a057 --- /dev/null +++ "b/Cute-JavaScript/Cute-JS/level1/11.Map\345\222\214Set\345\257\271\350\261\241.md" @@ -0,0 +1,294 @@ +本文是 **重温基础** 系列文章的第十一篇。 +今日感受:注意身体,生病花钱又难受。 + +**本章节复习的是JS中的Map和Set对象,是个集合。** + +前置知识: +Map和Set对象是在ES6中被引入的,作为一种由`key`值标记的数据容器。 +Map和Set对象承载的数据元素可以按照插入时的顺序,被迭代遍历。 + +# 1 Set对象 +**介绍**: +`Set`数据结构类似数组,但所有成员的值**唯一**。 +`Set`本身为一个构造函数,用来生成`Set`数据结构,使用`add`方法来添加新成员。 +```js +let a = new Set(); +[1,2,2,1,3,4,5,4,5].forEach(x=>a.add(x)); +for(let k of a){ + console.log(k) +}; +// 1 2 3 4 5 +``` +**基础使用**: +```js +let a = new Set([1,2,3,3,4]); +[...a]; // [1,2,3,4] +a.size; // 4 + +// 数组去重 +[...new Set([1,2,3,4,4,4])];// [1,2,3,4] +``` + +**注意**: +* 向`Set`中添加值的时候,不会类型转换,即`5`和`'5'`是不同的。 +```js +[...new Set([5,'5'])]; // [5, "5"] +``` + +**属性和方法**: +* 属性: + * `Set.prototype.constructor`:构造函数,默认就是`Set`函数。 + * `Set.prototype.size`:返回`Set`实例的成员总数。 + +* 操作方法: + * `add(value)`:添加某个值,返回 Set 结构本身。 + * `delete(value)`:删除某个值,返回一个布尔值,表示删除是否成功。 + * `has(value)`:返回一个布尔值,表示该值是否为Set的成员。 + * `clear()`:清除所有成员,没有返回值。 + +```js +let a = new Set(); +a.add(1).add(2); // a => Set(2) {1, 2} +a.has(2); // true +a.has(3); // false +a.delete(2); // true a => Set(1) {1} +a.clear(); // a => Set(0) {} +``` +**数组去重**: +```js +let a = new Set([1,2,3,3,3,3]); +``` +# 2 Set的应用 +**数组去重**: +```js +// 方法1 +[...new Set([1,2,3,4,4,4])]; // [1,2,3,4] +// 方法2 +Array.from(new Set([1,2,3,4,4,4])); // [1,2,3,4] +``` +**遍历和过滤**: +```js +let a = new Set([1,2,3,4]); + +// map 遍历操作 +let b = new Set([...a].map(x =>x*2));// b => Set(4) {2,4,6,8} + +// filter 过滤操作 +let c = new Set([...a].filter(x =>(x%2) == 0)); // b => Set(2) {2,4} +``` +**获取并集、交集和差集**: +```js +let a = new Set([1,2,3]); +let b = new Set([4,3,2]); + +// 并集 +let c1 = new Set([...a, ...b]); // Set {1,2,3,4} + +// 交集 +let c2 = new Set([...a].filter(x => b.has(x))); // set {2,3} + +// 差集 +let c3 = new Set([...a].filter(x => !b.has(x))); // set {1} +``` + +* 遍历方法: + * `keys()`:返回**键名**的遍历器。 + * `values()`:返回**键值**的遍历器。 + * `entries()`:返回**键值对**的遍历器。 + * `forEach()`:使用回调函数遍历**每个成员**。 + +`Set`遍历顺序是**插入顺序**,当保存多个回调函数,只需按照顺序调用。但由于`Set`结构**没有键名只有键值**,所以`keys()`和`values()`是返回结果相同。 +```js +let a = new Set(['a','b','c']); +for(let i of a.keys()){console.log(i)}; // 'a' 'b' 'c' +for(let i of a.values()){console.log(i)}; // 'a' 'b' 'c' +for(let i of a.entries()){console.log(i)}; +// ['a','a'] ['b','b'] ['c','c'] +``` +并且 还可以使用`for...of`直接遍历`Set`。 +```js +let a = new Set(['a','b','c']); +for(let k of a){console.log(k)}; // 'a' 'b' 'c' +``` +`forEach`与数组相同,对每个成员执行操作,且无返回值。 +```js +let a = new Set(['a','b','c']); +a.forEach((v,k) => console.log(k + ' : ' + v)); +``` + + +# 3 Map对象 +由于传统的`JavaScript`对象只能用字符串当做键,给开发带来很大限制,ES6增加`Map`数据结构,使得**各种类型的值**(包括对象)都可以作为键。 +`Map`结构提供了“**值—值**”的对应,是一种更完善的 Hash 结构实现。 +**基础使用**: +```js +let a = new Map(); +let b = {name: 'leo' }; +a.set(b,'my name'); // 添加值 +a.get(b); // 获取值 +a.size; // 获取总数 +a.has(b); // 查询是否存在 +a.delete(b); // 删除一个值 +a.clear(); // 清空所有成员 无返回 +``` +**注意**: +* 传入数组作为参数,**指定键值对的数组**。 +```js +let a = new Map([ + ['name','leo'], + ['age',18] +]) +``` +* 如果对同一个键**多次赋值**,后面的值将**覆盖前面的值**。 +```js +let a = new Map(); +a.set(1,'aaa').set(1,'bbb'); +a.get(1); // 'bbb' +``` +* 如果读取一个未知的键,则返回`undefined`。 +```js +new Map().get('abcdef'); // undefined +``` +* **同样的值**的两个实例,在 Map 结构中被视为两个键。 +```js +let a = new Map(); +let a1 = ['aaa']; +let a2 = ['aaa']; +a.set(a1,111).set(a2,222); +a.get(a1); // 111 +a.get(a2); // 222 +``` +**遍历方法**: +Map 的遍历顺序就是插入顺序。 +* `keys()`:返回键名的遍历器。 +* `values()`:返回键值的遍历器。 +* `entries()`:返回所有成员的遍历器。 +* `forEach()`:遍历 Map 的所有成员。 +```js +let a = new Map([ + ['name','leo'], + ['age',18] +]) + +for (let i of a.keys()){...}; +for (let i of a.values()){...}; +for (let i of a.entries()){...}; +a.forEach((v,k,m)=>{ + console.log(`key:${k},value:${v},map:${m}`) +}) +``` +**将Map结构转成数组结构**: +```js +let a = new Map([ + ['name','leo'], + ['age',18] +]) + +let a1 = [...a.keys()]; // a1 => ["name", "age"] +let a2 = [...a.values()]; // a2 =>  ["leo", 18] +let a3 = [...a.entries()];// a3 => [['name','leo'], ['age',18]] +``` + +# 4 Map与其他数据结构互相转换 +* Map 转 数组 +```js +let a = new Map().set(true,1).set({f:2},['abc']); +[...a]; // [[true:1], [ {f:2},['abc'] ]] +``` +* 数组 转 Map +```js +let a = [ ['name','leo'], [1, 'hi' ]] +let b = new Map(a); +``` +* Map 转 对象 +如果所有 Map 的键都是字符串,它可以无损地转为对象。 +如果有非字符串的键名,那么这个键名会被转成字符串,再作为对象的键名。 +```js +function fun(s) { + let obj = Object.create(null); + for (let [k,v] of s) { + obj[k] = v; + } + return obj; +} + +const a = new Map().set('yes', true).set('no', false); +fun(a) +// { yes: true, no: false } +``` +* 对象 转 Map +```js +function fun(obj) { + let a = new Map(); + for (let k of Object.keys(obj)) { + a.set(k, obj[k]); + } + return a; +} + +fun({yes: true, no: false}) +// Map {"yes" => true, "no" => false} +``` + +* Map 转 JSON +**(1)Map键名都是字符串,转为对象JSON:** +```js +function fun (s) { + let obj = Object.create(null); + for (let [k,v] of s) { + obj[k] = v; + } + return JSON.stringify(obj) +} +let a = new Map().set('yes', true).set('no', false); +fun(a); +// '{"yes":true,"no":false}' +``` +**(2)Map键名有非字符串,转为数组JSON:** +```js +function fun (map) { + return JSON.stringify([...map]); +} + +let a = new Map().set(true, 7).set({foo: 3}, ['abc']); +fun(a) +// '[[true,7],[{"foo":3},["abc"]]]' +``` +* JSON 转 Map +**(1)所有键名都是字符串:** +```js +function fun (s) { + let strMap = new Map(); + for (let k of Object.keys(s)) { + strMap.set(k, s[k]); + } + return strMap; + return JSON.parse(strMap); +} +fun('{"yes": true, "no": false}') +// Map {'yes' => true, 'no' => false} +``` +**(2)整个 JSON 就是一个数组,且每个数组成员本身,又是一个有两个成员的数组**: +```js +function fun2(s) { + return new Map(JSON.parse(s)); +} +fun2('[[true,7],[{"foo":3},["abc"]]]') +// Map {true => 7, Object {foo: 3} => ['abc']} +``` + + + +# 参考资料 +[1.阮一峰ES6入门](http://es6.ruanyifeng.com/#docs/set-map) + +--- +**本部分内容到这结束** + +|Author|王平安| +|---|---| +|E-mail|pingan8787@qq.com| +|博 客|www.pingan8787.com| +|微 信|pingan8787| +|每日文章推荐|https://github.com/pingan8787/Leo_Reading/issues| +|ES小册|js.pingan8787.com| \ No newline at end of file diff --git "a/Cute-JavaScript/Cute-JS/level1/12.\344\275\277\347\224\250\345\257\271\350\261\241.md" "b/Cute-JavaScript/Cute-JS/level1/12.\344\275\277\347\224\250\345\257\271\350\261\241.md" new file mode 100644 index 00000000..08df6f3d --- /dev/null +++ "b/Cute-JavaScript/Cute-JS/level1/12.\344\275\277\347\224\250\345\257\271\350\261\241.md" @@ -0,0 +1,527 @@ +本文是 **重温基础** 系列文章的第十二篇。 +今日感受:需要总结下2018。 + +这几天,重重的感冒发烧,在家休息好几天,伤···。 + + +**本章节复习的是JS中对象的使用,这是重点。** + +前置知识: +JavaScrip的设计是一个简单的基于对象的范式。 +对象是一系列属性的集合,一个属性包含一个键和一个值,也叫**键值对**。 +若一个属性的值,是一个函数,则称这个属性为方法。 + +一个对象,可以有很多方法,就像一个杯子,可以有颜色,重量,形状等属性。 + +**注意:** +对象的名称,对大小写敏感。 + +# 1.创建对象 +本文主要是复习对象的使用,至于对象的创建,下面这里简单介绍一下常见的创建对象的方法: + +**创建方法1**: +```js +let obj = new Object(); // 声明一个空对象 +``` + +**创建方法2**: +```js +let obj = {}; // 声明一个空对象 +``` +上面的`name`是对象`obj`中的一个属性,对应的值为`"leo"`。 + + +# 2.设置对象属性和访问属性 +设置对象属性,也有两种方法: +**直接设置**: +```js +let obj = {}; +obj.name = "leo"; // 为对象添加属性和值 +``` + +**创建时设置**: +```js +let obj = {name : 'leo'}; +obj.name = "leo"; // 为对象添加属性和值 +``` + +当一个对象定义了一个属性,我们可以用点符号(`.`)来访问它。 +```js +obj.name; // "leo" +``` + +**注意**: +* 属性的另外一种写法: +```js +let obj = {}; +let n = "name"; +obj[n] = "leo"; // 属性名使用变量 +obj["height"] = 188; // 字符串 +obj["age" + "Is"] = 15; // 字符串拼接 +console.log(obj); +/* +obj = { + ageIs: 15 + height: 188 + name: "leo" +} +*/ +``` + +* 属性的值也可以是个方法: +```js +let obj = {}; +obj.getTime = function(){ + return new Date(); +} +obj.getTime(); // 访问属性的方法 +//Wed Jan 02 2019 21:07:59 GMT+0800 (中国标准时间) +``` + +# 3.枚举对象的所有属性 +从 [ECMAScript 5](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/New_in_JavaScript/ECMAScript_5_support_in_Mozilla) 开始,有三种原生的方法用于列出或枚举对象的属性: +## 3.1 for...in 循环 + +该方法依次访问一个对象及其**原型链**中所有可枚举的属性。 +```js +let obj = {a:1, b:2, c:3}; +for (let i in obj) { + console.log("obj." + i + " = " + obj[i]); +} +/* +"obj.a = 1" +"obj.b = 2" +"obj.c = 3" +*/ +``` + +## 3.2 Object.keys(o) + +该方法返回一个对象 `o` 自身包含(**不包括原型中**)的所有属性的名称的数组。 +```js +let obj = {a:1, b:2, c:3}; +let arr = Object.keys(obj); +console.log(arr); +/* +["a", "b", "c"] +*/ +arr.forEach(function(val,index,array){ + console.log("key:" + val+",val:"+obj[val]) +}) +/* +key:a,val:1 +key:b,val:2 +key:c,val:3 +*/ +``` + + +## 3.3 Object.getOwnPropertyNames(o) + +该方法返回一个数组,它包含了对象 `o` 所有拥有的属性(**无论是否可枚举**)的名称。 + + +```js +let obj = {a:1, b:2, c:3}; +let arr = Object.getOwnPropertyNames(obj); +console.log(arr); +/* +["a", "b", "c"] +*/ +arr.forEach(function(val,index,array){ + console.log("key:" + val+",val:"+obj[val]) +}) +/* +key:a,val:1 +key:b,val:2 +key:c,val:3 +*/ +``` + + +# 4.ES6新增:对象的拓展 +## 4.1 属性的简洁表示 +```js +let a = 'a1'; +let b = { a }; // b => { a : 'a1' } +// 等同于 +let b = { a : a }; + +function f(a, b){ + return {a, b}; +} +// 等同于 +function f (a, b){ + return {a:a ,b:b}; +} + +let a = { + fun () { + return 'leo'; + } +} +// 等同于 +let a = { + fun : function(){ + return 'leo'; + } +} +``` + +## 4.2 属性名表达式 +`JavaScript`提供2种方法**定义对象的属性**。 +```js +// 方法1 标识符作为属性名 +a.f = true; + +// 方法2 字符串作为属性名 +a['f' + 'un'] = true; +``` +延伸出来的还有: +```js +let a = 'hi leo'; +let b = { + [a]: true, + ['a'+'bc']: 123, + ['my' + 'fun'] (){ + return 'hi'; + } +}; +// b.a => undefined ; b.abc => 123 ; b.myfun() => 'hi' +// b[a] => true ; b['abc'] => 123 ; b['myfun'] => ƒ ['my' + 'fun'] (){ return 'hi'; } +``` +**注意**: +属性名表达式不能与简洁表示法同时使用,否则报错。 +```js +// 报错 +let a1 = 'aa'; +let a2 = 'bb'; +let b1 = {[a1]}; + +// 正确 +let a1 = 'aa'; +let b1 = { [a1] : 'bb'}; +``` + +## 4.3 Object.is() +`Object.is()` 用于比较两个值是否严格相等,在ES5时候只要使用**相等运算符**(`==`)和**严格相等运算符**(`===`)就可以做比较,但是它们都有缺点,前者会**自动转换数据类型**,后者的`NaN`不等于自身,以及`+0`等于`-0`。 +```js +Object.is('a','a'); // true +Object.is({}, {}); // false + +// ES5 ++0 === -0 ; // true +NaN === NaN; // false + +// ES6 +Object.is(+0,-0); // false +Object.is(NaN,NaN); // true +``` + +## 4.4 Object.assign() +`Object.assign()`方法用于对象的合并,将原对象的所有可枚举属性复制到目标对象。 +**基础用法**: +第一个参数是**目标对象**,后面参数都是**源对象**。 +```js +let a = {a:1}; +let b = {b:2}; +Object.assign(a,b); // a=> {a:1,b:2} +``` +**注意**: +* 若目标对象与源对象有同名属性,则后面属性会覆盖前面属性。 +```js +let a = {a:1, b:2}; +let b = {b:3, c:4}; +Object.assign(a, b); // a => {a:1, b:3, c:4} +``` +* 若只有**一个**参数,则返回该参数。 +```js +let a = {a:1}; +Object.assign(a) === a; // true +``` +* 若参数**不是对象**,则先转成对象后返回。 +```js +typeof Object.assign(2); // 'object' +``` +* 由于`undefined`或`NaN`无法转成对象,所以做为参数会报错。 +```js +Object.assign(undefined) // 报错 +Object.assign(NaN); // 报错 +``` +* `Object.assign()`实现的是浅拷贝。 + +`Object.assign()`拷贝得到的是这个对象的引用。这个对象的任何变化,都会反映到目标对象上面。 +```js +let a = {a: {b:1}}; +let b = Object.assign({},a); +a.a.b = 2; +console.log(b.a.b); // 2 +``` +* 将数组当做对象处理,键名为数组下标,键值为数组下标对应的值。 +```js +Object.assign([1, 2, 3], [4, 5]); // [4, 5, 3] +``` + +# 5.ES8新增:Object.values(),Object.entries() +ES7中新增加的 `Object.values()`和`Object.entries()`与之前的`Object.keys()`类似,返回数组类型。 +回顾下`Object.keys()`: +```js +var a = { f1: 'hi', f2: 'leo'}; +Object.keys(a); // ['f1', 'f2'] +``` +## 5.1 Object.values() +返回一个数组,成员是参数对象自身的(不含继承的)所有**可遍历属性**的键值。 +```js +let a = { f1: 'hi', f2: 'leo'}; +Object.values(a); // ['hi', 'leo'] +``` +如果参数不是对象,则返回空数组: +```js +Object.values(10); // [] +Object.values(true); // [] +``` + +## 5.2 Object.entries() +返回一个数组,成员是参数对象自身的(不含继承的)所有**可遍历属性**的键值对数组。 +```js +let a = { f1: 'hi', f2: 'leo'}; +Object.entries(a); // [['f1','hi'], ['f2', 'leo']] +``` +* **用途1**: +遍历对象属性。 +```js +let a = { f1: 'hi', f2: 'leo'}; +for (let [k, v] of Object.entries(a)){ + console.log( + `${JSON.stringfy(k)}:${JSON.stringfy(v)}` + ) +} +// 'f1':'hi' +// 'f2':'leo' +``` +* **用途2**: +将对象转为真正的Map结构。 +```js +let a = { f1: 'hi', f2: 'leo'}; +let map = new Map(Object.entries(a)); +``` +手动实现`Object.entries()`方法: +```js +// Generator函数实现: +function* entries(obj){ + for (let k of Object.keys(obj)){ + yield [k ,obj[k]]; + } +} + +// 非Generator函数实现: +function entries (obj){ + let arr = []; + for(let k of Object.keys(obj)){ + arr.push([k, obj[k]]); + } + return arr; +} +``` + +# 6.ES8新增:Object.getOwnPropertyDescriptors() +之前有`Object.getOwnPropertyDescriptor`方法会返回某个对象属性的描述对象,新增的`Object.getOwnPropertyDescriptors()`方法,返回指定对象所有自身属性(非继承属性)的描述对象,所有原对象的属性名都是该对象的属性名,对应的属性值就是该属性的描述对象 +```js +let a = { + a1:1, + get f1(){ return 100} +} +Object.getOwnPropetyDescriptors(a); +/* +{ + a:{ configurable:true, enumerable:true, value:1, writeable:true} + f1:{ configurable:true, enumerable:true, get:f, set:undefined} +} +*/ +``` +实现原理: +```js +function getOwnPropertyDescriptors(obj) { + const result = {}; + for (let key of Reflect.ownKeys(obj)) { + result[key] = Object.getOwnPropertyDescriptor(obj, key); + } + return result; +} +``` +引入这个方法,主要是为了解决`Object.assign()`无法正确拷贝`get`属性和`set`属性的问题。 +```js +let a = { + set f(v){ + console.log(v) + } +} +let b = {}; +Object.assign(b, a); +Object.a(b, 'f'); +/* +f = { + configurable: true, + enumable: true, + value: undefined, + writeable: true +} +*/ +``` +`value`为`undefined`是因为`Object.assign`方法不会拷贝其中的`get`和`set`方法,使用`getOwnPropertyDescriptors`配合`Object.defineProperties`方法来实现正确的拷贝: +```js +let a = { + set f(v){ + console.log(v) + } +} +let b = {}; +Object.defineProperties(b, Object.getOwnPropertyDescriptors(a)); +Object.getOwnPropertyDescriptor(b, 'f') +/* + configurable: true, + enumable: true, + get: undefined, + set: function(){...} +*/ +``` +`Object.getOwnPropertyDescriptors`方法的配合`Object.create`方法,将对象属性克隆到一个新对象,实现浅拷贝。 +```js +const clone = Object.create(Object.getPrototypeOf(obj), + Object.getOwnPropertyDescriptors(obj)); + +// 或者 +const shallowClone = (obj) => Object.create( + Object.getPrototypeOf(obj), + Object.getOwnPropertyDescriptors(obj) +); +``` + +# 7.ES9新增:对象的拓展运算符 +## 7.1 介绍 +对象的拓展运算符,即对象的Rest/Spread属性,可将对象解构赋值用于从一个对象取值,搜键值对分配到指定对象上,与数组的拓展运算符类似: +```js +let {x, y, ...z} = {x:1, y:2, a:3, b:4}; +x; // 1 +y; // 2 +z; // {a:3, b:4} +``` +对象的解构赋值要求等号右边必须是个对象,所以如果等号右边是`undefined`或`null`就会报错无法转成对象。 +```js +let {a, ...b} = null; // 运行时报错 +let {a, ...b} = undefined; // 运行时报错 +``` +解构赋值必须是最后一个参数,否则报错。 +```js +let {...a, b, c} = obj; // 语法错误 +let {a, ...b, c} = obj; // 语法错误 +``` +**注意**: +* 1.解构赋值是浅拷贝。 +```js +let a = {a1: {a2: 'leo'}}; +let {...b} = a; +a.a1.a2 = 'leo'; +b.a1.a2 = 'leo'; +``` +* 2.拓展运算符的解构赋值,不能复制继承自原型对象的属性。 +```js +let o1 = { a: 1 }; +let o2 = { b: 2 }; +o2.__proto__ = o1; +let { ...o3 } = o2; +o3; // { b: 2 } +o3.a; // undefined +``` + +## 7.2 使用场景 +* 1.取出参数对象所有可遍历属性,拷贝到当前对象中。 +```js +let a = { a1:1, a2:2 }; +let b = { ...a }; +b; // { a1:1, a2:2 } + +// 类似Object.assign方法 +``` +* 2.合并两个对象。 +```js +let a = { a1:1, a2:2 }; +let b = { b1:11, b2:22 }; +let ab = { ...a, ...b }; // {a1: 1, a2: 2, b1: 11, b2: 22} +// 等同于 +let ab = Object.assign({}, a, b); +``` +* 3.将自定义属性放在拓展运算符后面,覆盖对象原有的同名属性。 +```js +let a = { a1:1, a2:2, a3:3 }; +let r = { ...a, a3:666 }; +// r {a1: 1, a2: 2, a3: 666} + +// 等同于 +let r = { ...a, ...{ a3:666 }}; +// r {a1: 1, a2: 2, a3: 666} + +// 等同于 +let r = Object.assign({}, a, { a3:666 }); +// r {a1: 1, a2: 2, a3: 666} +``` +* 4.将自定义属性放在拓展运算符前面,就会成为设置新对象的默认值。 +```js +let a = { a1:1, a2:2 }; +let r = { a3:666, ...a }; +// r {a3: 666, a1: 1, a2: 2} + +// 等同于 +let r = Object.assign({}, {a3:666}, a); +// r {a3: 666, a1: 1, a2: 2} + +// 等同于 +let r = Object.assign({a3:666}, a); +// r {a3: 666, a1: 1, a2: 2} +``` +* 5.拓展运算符后面可以使用表达式。 +```js +let a = { + ...(x>1? {a:!:{}), + b:2 +} +``` +* 6.拓展运算符后面如果是个空对象,则没有任何效果。 +```js +{...{}, a:1}; // {a:1} +``` +* 7.若参数是`null`或`undefined`则忽略且不报错。 +```js +let a = { ...null, ...undefined }; // 不报错 +``` +* 8.若有取值函数`get`则会执行。 +```js +// 不会打印 因为f属性只是定义 而不没执行 +let a = { + ...a1, + get f(){console.log(1)} +} + +// 会打印 因为f执行了 +let a = { + ...a1, + ...{ + get f(){console.log(1)} + } +} +``` + +# 参考资料 +1.[MDN 使用对象](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/Working_with_Objects) +2.[W3school JavaScript 对象](http://www.w3school.com.cn/js/js_obj_intro.asp) + +--- +**本部分内容到这结束** + +|Author|王平安| +|---|---| +|E-mail|pingan8787@qq.com| +|博 客|www.pingan8787.com| +|微 信|pingan8787| +|每日文章推荐|https://github.com/pingan8787/Leo_Reading/issues| +|ES小册|js.pingan8787.com| \ No newline at end of file diff --git "a/Cute-JavaScript/Cute-JS/level1/13.\350\277\255\344\273\243\345\231\250\345\222\214\347\224\237\346\210\220\345\231\250.md" "b/Cute-JavaScript/Cute-JS/level1/13.\350\277\255\344\273\243\345\231\250\345\222\214\347\224\237\346\210\220\345\231\250.md" new file mode 100644 index 00000000..b4cbc8de --- /dev/null +++ "b/Cute-JavaScript/Cute-JS/level1/13.\350\277\255\344\273\243\345\231\250\345\222\214\347\224\237\346\210\220\345\231\250.md" @@ -0,0 +1,565 @@ +本文是 **重温基础** 系列文章的第十三篇。 +今日感受:每次自我年终总结,都会有各种情绪和收获。 + +**本章节复习的是JS中的迭代器和生成器,常常用来处理集合。** + +前置知识: +JavaScrip已经提供多个迭代集合的方法,从简单的`for`循环到`map()`和`filter()`。 +迭代器和生成器将迭代的概念直接带入核心语言,并提供一种机制来自定义`for...of`循环的行为。 + +**本文会将知识点分为两大部分,简单介绍和详细介绍**: +简单介绍,适合基础入门会使用的目标; +详细介绍,会更加深入的做介绍,适合理解原理; + +# 1. 概述 +当我们使用循环语句迭代数据时,需初始化一个变量来记录每一次迭代在数据集合中的位置: +```js +let a = ["aaa","bbb","ccc"]; +for (let i = 0; i< a.length; i++){ + console.log(a[i]); +} +``` +这边的`i`就是我们用来记录迭代位置的变量,但是在**ES6**开始,JavaScrip引入了迭代器这个特性,并且**新的数组方法**和**新的集合类型**(如`Set集合`与`Map集合`)都依赖迭代器的实现,这个新特性对于**高效的数据处理**而言是不可或缺的,在语言的其他特性中也都有迭代器的身影:新的`for-of循环`、展开运算符(`...`),甚至连**异步编程**都可以使用迭代器。 + +本文主要会介绍ES6中新增的迭代器(Iterator)和生成器(Generator)。 + +# 2. 迭代器(简单介绍) +迭代器是一种特殊对象,它具有一些专门为迭代过程设计的专有接口,所有的迭代器对象都有一个`next()`方法,每次调用都会返回一个结果对象。 +这个结果对象,有两个属性: +* `value`: 表示下一个将要返回的值。 +* `done`: 一个布尔值,若没有更多可返回的数据时,值为`true`,否则`false`。 + +如果最后一个值返回后,再调用`next()`,则返回的对象的`done`值为`true`,而`value`值如果没有值的话,返回的为`undefined`。 + +**ES5**实现一个迭代器: +```js +function myIterator(list){ + var i = 0; + return { + next: function(){ + var done = i >= list.length; + var value = !done ? list[i++] : undefined; + return { + done : done, + value : value + } + } + } +} + +var iterator = myIterator([1,2,3]); +iterator.next(); // "{done: false, value: 1}" +iterator.next(); // "{done: false, value: 2}" +iterator.next(); // "{done: false, value: 3}" +iterator.next(); // "{done: true, value: undefined}" +// 以后的调用都一样 +iterator.next(); // "{done: true, value: undefined}" +``` + +从上面代码可以看出,ES5的实现还是比较麻烦,而ES6新增的生成器,可以使得创建迭代器对象的过程更加简单。 + +# 3. 生成器(简单介绍) +生成器是一种返回迭代器的函数,通过`function`关键字后的星号(`*`)来表示,函数中会用到新的关键字`yield`。星号可以紧挨着`function`关键字,也可以在中间添加一个空格。 + +```js +function *myIterator(){ + yield 1; + yield 2; + yield 3; +} +let iterator = myIterator(); +iterator.next(); // "{done: false, value: 1}" +iterator.next(); // "{done: false, value: 2}" +iterator.next(); // "{done: false, value: 3}" +iterator.next(); // "{done: true, value: undefined}" +// 以后的调用都一样 +iterator.next(); // "{done: true, value: undefined}" +``` + +生成器函数最有趣的部分是,每当执行完一条`yield`语句后函数就会自动停止执行,比如上面代码,当`yield 1;`执行完后,便不会执行任何语句,而是等到再调用迭代器的`next()`方法才会执行下一个语句,即`yield 2;`. +使用`yield`关键字可以返回任何值和表达式,因为可以通过生成器函数批量给迭代器添加元素: +```js +function *myIterator(list){ + for(let i = 0; i< list.length ; i ++){ + yield list[i]; + } +} + +var iterator = myIterator([1,2,3]); +iterator.next(); // "{done: false, value: 1}" +iterator.next(); // "{done: false, value: 2}" +iterator.next(); // "{done: false, value: 3}" +iterator.next(); // "{done: true, value: undefined}" +// 以后的调用都一样 +iterator.next(); // "{done: true, value: undefined}" +``` + +生成器的适用返回很广,可以将它用于所有支持函数使用的地方。 + +# 4. 迭代器(详细介绍) +## 4.1 Iterator迭代器概念 +> **Iterator**是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署 **Iterator** 接口,就可以完成迭代操作(即依次处理该数据结构的所有成员)。 + +**Iterator三个作用**: +* 为各种数据结构,提供一个**统一**的、**简便**的访问接口; +* 使得数据结构的成员能够按某种次序排列; +* **Iterator** 接口主要供ES6新增的`for...of`消费; + +## 4.2 Iterator迭代过程 +1. 创建一个指针对象,指向当前数据结构的起始位置。也就是说,迭代器对象本质上,就是一个指针对象。 +2. 第一次调用指针对象的`next`方法,可以将指针指向数据结构的第一个成员。 +3. 第二次调用指针对象的`next`方法,指针就指向数据结构的第二个成员。 +4. 不断调用指针对象的`next`方法,直到它指向数据结构的结束位置。 + +每一次调用`next`方法,都会返回数据结构的当前成员的信息。具体来说,就是返回一个包含`value`和`done`两个属性的对象。 + +* `value`属性是当前成员的值; +* `done`属性是一个布尔值,表示迭代是否结束; + +模拟`next`方法返回值: +```js +let f = function (arr){ + var nextIndex = 0; + return { + next:function(){ + return nextIndex < arr.length ? + {value: arr[nextIndex++], done: false}: + {value: undefined, done: true} + } + } +} + +let a = f(['a', 'b']); +a.next(); // { value: "a", done: false } +a.next(); // { value: "b", done: false } +a.next(); // { value: undefined, done: true } +``` + +## 4.3 默认Iterator接口 +若数据**可迭代**,即一种数据部署了Iterator接口。 +ES6中默认的Iterator接口部署在数据结构的`Symbol.iterator`属性,即如果一个数据结构具有`Symbol.iterator`属性,就可以认为是**可迭代**。 +`Symbol.iterator`属性本身是函数,是当前数据结构默认的迭代器生成函数。执行这个函数,就会返回一个迭代器。至于属性名`Symbol.iterator`,它是一个表达式,返回`Symbol`对象的`iterator`属性,这是一个预定义好的、类型为 Symbol 的特殊值,所以要放在方括号内(参见《Symbol》一章)。 + +**原生具有Iterator接口的数据结构有**: +* Array +* Map +* Set +* String +* TypedArray +* 函数的 arguments 对象 +* NodeList 对象 + +## 4.4 Iterator使用场景 +* **(1)解构赋值** +对数组和 `Set` 结构进行解构赋值时,会默认调用`Symbol.iterator`方法。 +```js +let a = new Set().add('a').add('b').add('c'); +let [x, y] = a; // x = 'a' y = 'b' +let [a1, ...a2] = a; // a1 = 'a' a2 = ['b','c'] +``` + +* **(2)扩展运算符** +扩展运算符(`...`)也会调用默认的 Iterator 接口。 +```js +let a = 'hello'; +[...a]; // ['h','e','l','l','o'] + +let a = ['b', 'c']; +['a', ...a, 'd']; // ['a', 'b', 'c', 'd'] +``` + +* **(2)yield*** +`yield*`后面跟的是一个可迭代的结构,它会调用该结构的迭代器接口。 +```js +let a = function*(){ + yield 1; + yield* [2,3,4]; + yield 5; +} + +let b = a(); +b.next() // { value: 1, done: false } +b.next() // { value: 2, done: false } +b.next() // { value: 3, done: false } +b.next() // { value: 4, done: false } +b.next() // { value: 5, done: false } +b.next() // { value: undefined, done: true } +``` + +* **(4)其他场合** +由于数组的迭代会调用迭代器接口,所以任何接受数组作为参数的场合,其实都调用了迭代器接口。下面是一些例子。 + +* for...of +* Array.from() +* Map(), Set(), WeakMap(), WeakSet()(比如`new Map([['a',1],['b',2]])`) +* Promise.all() +* Promise.race() + +## 4.5 for...of循环 +只要数据结构部署了`Symbol.iterator`属性,即具有 iterator 接口,可以用`for...of`循环迭代它的成员。也就是说,`for...of`循环内部调用的是数据结构的`Symbol.iterato`方法。 +**使用场景**: +`for...of`可以使用在**数组**,**`Set`和`Map`结构**,**类数组对象**,**Genetator对象**和**字符串**。 + +* **数组** +`for...of`循环可以代替数组实例的`forEach`方法。 +```js +let a = ['a', 'b', 'c']; +for (let k of a){console.log(k)}; // a b c + +a.forEach((ele, index)=>{ + console.log(ele); // a b c + console.log(index); // 0 1 2 +}) +``` +与`for...in`对比,`for...in`只能获取对象键名,不能直接获取键值,而`for...of`允许直接获取键值。 +```js +let a = ['a', 'b', 'c']; +for (let k of a){console.log(k)}; // a b c +for (let k in a){console.log(k)}; // 0 1 2 +``` + +* **Set和Map** +可以使用数组作为变量,如`for (let [k,v] of b){...}`。 +```js +let a = new Set(['a', 'b', 'c']); +for (let k of a){console.log(k)}; // a b c + +let b = new Map(); +b.set('name','leo'); +b.set('age', 18); +b.set('aaa','bbb'); +for (let [k,v] of b){console.log(k + ":" + v)}; +// name:leo +// age:18 +// aaa:bbb +``` + +* **类数组对象** +```js +// 字符串 +let a = 'hello'; +for (let k of a ){console.log(k)}; // h e l l o + +// DOM NodeList对象 +let b = document.querySelectorAll('p'); +for (let k of b ){ + k.classList.add('test'); +} + +// arguments对象 +function f(){ + for (let k of arguments){ + console.log(k); + } +} +f('a','b'); // a b +``` + +* **对象** +普通对象不能直接使用`for...of`会报错,要部署Iterator才能使用。 +```js +let a = {a:'aa',b:'bb',c:'cc'}; +for (let k in a){console.log(k)}; // a b c +for (let k of a){console>log(k)}; // TypeError +``` + +## 4.6 跳出for...of +使用`break`来实现。 +```js +for (let k of a){ + if(k>100) + break; + console.log(k); +} +``` + +# 5. 生成器(详细介绍) +## 5.1 基本概念 +`Generator`生成器函数是一种异步编程解决方案。 +**原理**: +执行`Genenrator`函数会返回一个遍历器对象,依次遍历`Generator`函数内部的每一个状态。 +`Generator`函数是一个普通函数,有以下两个特征: +* `function`关键字与函数名之间有个星号; +* 函数体内使用`yield`表达式,定义不同状态; + +通过调用`next`方法,将指针移向下一个状态,直到遇到下一个`yield`表达式(或`return`语句)为止。简单理解,`Generator`函数分段执行,`yield`表达式是暂停执行的标记,而`next`恢复执行。 +```js +function * f (){ + yield 'hi'; + yield 'leo'; + return 'ending'; +} +let a = f(); +a.next(); // {value: 'hi', done : false} +a.next(); // {value: 'leo', done : false} +a.next(); // {value: 'ending', done : true} +a.next(); // {value: undefined, done : false} +``` + +## 5.2 yield表达式 +`yield`表达式是暂停标志,遍历器对象的`next`方法的运行逻辑如下: +1. 遇到`yield`就暂停执行,将这个`yield`后的表达式的值,作为返回对象的`value`属性值。 +2. 下次调用`next`往下执行,直到遇到下一个`yield`。 +3. 直到函数结束或者`return`为止,并返回`return`语句后面表达式的值,作为返回对象的`value`属性值。 +4. 如果该函数没有`return`语句,则返回对象的`value`为`undefined` 。 + +**注意:** +* `yield`只能用在`Generator`函数里使用,其他地方使用会报错。 +```js +// 错误1 +(function(){ + yiled 1; // SyntaxError: Unexpected number +})() + +// 错误2 forEach参数是个普通函数 +let a = [1, [[2, 3], 4], [5, 6]]; +let f = function * (i){ + i.forEach(function(m){ + if(typeof m !== 'number'){ + yield * f (m); + }else{ + yield m; + } + }) +} +for (let k of f(a)){ + console.log(k) +} +``` + +* `yield`表达式如果用于另一个表达式之中,必须放在**圆括号**内。 +```js +function * a (){ + console.log('a' + yield); // SyntaxErro + console.log('a' + yield 123); // SyntaxErro + console.log('a' + (yield)); // ok + console.log('a' + (yield 123)); // ok +} +``` + +* `yield`表达式用做函数参数或放在表达式右边,可以**不加括号**。 +```js +function * a (){ + f(yield 'a', yield 'b'); // ok + lei i = yield; // ok +} +``` + +## 5.3 next方法 +`yield`本身没有返回值,或者是总返回`undefined`,`next`方法可带一个参数,作为上一个`yield`表达式的返回值。 +```js +function * f (){ + for (let k = 0; true; k++){ + let a = yield k; + if(a){k = -1}; + } +} +let g =f(); +g.next(); // {value: 0, done: false} +g.next(); // {value: 1, done: false} +g.next(true); // {value: 0, done: false} +``` +这一特点,可以让`Generator`函数开始执行之后,可以从外部向内部注入不同值,从而调整函数行为。 +```js +function * f(x){ + let y = 2 * (yield (x+1)); + let z = yield (y/3); + return (x + y + z); +} +let a = f(5); +a.next(); // {value : 6 ,done : false} +a.next(); // {value : NaN ,done : false} +a.next(); // {value : NaN ,done : true} +// NaN因为yeild返回的是对象 和数字计算会NaN + +let b = f(5); +b.next(); // {value : 6 ,done : false} +b.next(12); // {value : 8 ,done : false} +b.next(13); // {value : 42 ,done : false} +// x 5 y 24 z 13 +``` + +## 5.4 for...of循环 +`for...of`循环会自动遍历,不用调用`next`方法,需要注意的是,`for...of`遇到`next`返回值的`done`属性为`true`就会终止,`return`返回的不包括在`for...of`循环中。 +```js +function * f(){ + yield 1; + yield 2; + yield 3; + yield 4; + return 5; +} +for (let k of f()){ + console.log(k); +} +// 1 2 3 4 没有 5 +``` + +## 5.5 Generator.prototype.throw() +`throw`方法用来向函数外抛出错误,并且在Generator函数体内捕获。 +```js +let f = function * (){ + try { yield } + catch (e) { console.log('内部捕获', e) } +} + +let a = f(); +a.next(); + +try{ + a.throw('a'); + a.throw('b'); +}catch(e){ + console.log('外部捕获',e); +} +// 内部捕获 a +// 外部捕获 b +``` + +## 5.6 Generator.prototype.return() +`return`方法用来返回给定的值,并结束遍历Generator函数,如果`return`方法没有参数,则返回值的`value`属性为`undefined`。 +```js +function * f(){ + yield 1; + yield 2; + yield 3; +} +let g = f(); +g.next(); // {value : 1, done : false} +g.return('leo'); // {value : 'leo', done " true} +g.next(); // {value : undefined, done : true} +``` + +## 5.7 next()/throw()/return()共同点 +相同点就是都是用来恢复Generator函数的执行,并且使用不同语句替换`yield`表达式。 +* `next()`将`yield`表达式替换成一个值。 +```js +let f = function * (x,y){ + let r = yield x + y; + return r; +} +let g = f(1, 2); +g.next(); // {value : 3, done : false} +g.next(1); // {value : 1, done : true} +// 相当于把 let r = yield x + y; +// 替换成 let r = 1; +``` +* `throw()`将`yield`表达式替换成一个`throw`语句。 +```js +g.throw(new Error('报错')); // Uncaught Error:报错 +// 相当于将 let r = yield x + y +// 替换成 let r = throw(new Error('报错')); +``` +* `next()`将`yield`表达式替换成一个`return`语句。 +```js +g.return(2); // {value: 2, done: true} +// 相当于将 let r = yield x + y +// 替换成 let r = return 2; +``` + +## 5.8 yield* 表达式 +用于在一个Generator中执行另一个Generator函数,如果没有使用`yield*`会没有效果。 +```js +function * a(){ + yield 1; + yield 2; +} +function * b(){ + yield 3; + yield * a(); + yield 4; +} +// 等同于 +function * b(){ + yield 3; + yield 1; + yield 2; + yield 4; +} +for(let k of b()){console.log(k)} +// 3 +// 1 +// 2 +// 4 +``` + +## 5.9 应用场景 +1. **控制流管理** +解决回调地狱: +```js +// 使用前 +f1(function(v1){ + f2(function(v2){ + f3(function(v3){ + // ... more and more + }) + }) +}) + +// 使用Promise +Promise.resolve(f1) + .then(f2) + .then(f3) + .then(function(v4){ + // ... + },function (err){ + // ... + }).done(); + +// 使用Generator +function * f (v1){ + try{ + let v2 = yield f1(v1); + let v3 = yield f1(v2); + let v4 = yield f1(v3); + // ... + }catch(err){ + // console.log(err) + } +} +function g (task){ + let obj = task.next(task.value); + // 如果Generator函数未结束,就继续调用 + if(!obj.done){ + task.value = obj.value; + g(task); + } +} +g( f(initValue) ); +``` + +2. **异步编程的使用** +在真实的异步任务封装的情况: +```js +let fetch = require('node-fetch'); +function * f(){ + let url = 'http://www.baidu.com'; + let res = yield fetch(url); + console.log(res.bio); +} +// 执行该函数 +let g = f(); +let result = g.next(); +// 由于fetch返回的是Promise对象,所以用then +result.value.then(function(data){ + return data.json(); +}).then(function(data){ + g.next(data); +}) +``` + + +# 参考资料 +[1.MDN 迭代器和生成器](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/Iterators_and_generators) +[2.ES6中的迭代器(Iterator)和生成器(Generator)](https://www.cnblogs.com/xiaohuochai/p/7253466.html) + +--- +**本部分内容到这结束** + +|Author|王平安| +|---|---| +|E-mail|pingan8787@qq.com| +|博 客|www.pingan8787.com| +|微 信|pingan8787| +|每日文章推荐|https://github.com/pingan8787/Leo_Reading/issues| +|ES小册|js.pingan8787.com| \ No newline at end of file diff --git "a/Cute-JavaScript/Cute-JS/level1/14.\345\205\203\347\274\226\347\250\213.md" "b/Cute-JavaScript/Cute-JS/level1/14.\345\205\203\347\274\226\347\250\213.md" new file mode 100644 index 00000000..87e231d2 --- /dev/null +++ "b/Cute-JavaScript/Cute-JS/level1/14.\345\205\203\347\274\226\347\250\213.md" @@ -0,0 +1,281 @@ +本文是 **重温基础** 系列文章的第十四篇。 +**这是第一个基础系列的最后一篇,后面会开始复习一些中级的知识了,欢迎持续关注呀** +接下来会统一整理到我的[【Cute-JavaScript】](http://js.pingan8787.com/)的**JavaScript基础系列**中。 + +今日感受:独乐乐不如众乐乐。 + +**本章节复习的是JS中的元编程,涉及的更多的是ES6的新特性。** + +# 1. 概述 +元编程,其实我是这么理解的:**让代码自动写代码,可以更改源码底层的功能**。 +元,是指程序本身。 +有理解不到位,还请指点,具体详细的介绍,可以查看[维基百科 元编程](https://zh.wikipedia.org/wiki/%E5%85%83%E7%BC%96%E7%A8%8B) 。 +从ES6开始,JavaScrip添加了对`Proxy`和`Reflect`对象的支持,允许我们连接并定义基本语言操作的自定义行为(如属性查找,赋值,枚举和函数调用等),从而实现JavaScrip的元级别编程。 + +* `Reflect`: 用于替换直接调用`Object`的方法,并不是一个函数对象,也没有`constructor`方法,所以不能用`new`操作符。 +* `Proxy`: 用于自定义对象的行为,如修改`set`和`get`方法,可以说是ES5中`Object.defineProperty()`方法的ES6升级版。 +* 两者联系: API完全一致,但`Reflect`一般在`Proxy`需要处理默认行为的时候使用。 + +**参考资料**: + +|名称|地址| +|---|---| +|`Reflect`|[MDN Reflect](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Reflect)| +|`Proxy`|[MDN Proxy](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Proxy)| +|元编程|[MDN 元编程](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/Meta_programming)| + + +**本文主要从Proxy介绍,还会有几个案例,实际看下怎么使用。** + +# 2. Proxy介绍 +`proxy` 用于修改某些操作的**默认行为**,可以理解为一种拦截外界对目标对象访问的一种机制,从而对外界的访问进行过滤和修改,即代理某些操作,也称“**代理器**”。 +## 2.1 基础使用 +基本语法: +```js +let p = new Proxy(target, handler); +``` +`proxy`实例化需要传入两个参数,`target`参数表示所要拦截的目标对象,`handler`参数也是一个对象,用来定制拦截行为。 +```js +let p = new Proxy({}, { + get: function (target, handler){ + return 'leo'; + } +}) +p.name; // leo +p.age; // leo +p.abcd; // leo +``` +上述`a`实例中,在第二个参数中定义了`get`方法,来拦截外界访问,并且`get`方法接收两个参数,分别是**目标对象**和**所要访问的属性**,所以不管外部访问对象中任何属性都会执行`get`方法返回`leo`。 + +**注意**: +* 只能使用`Proxy`实例的对象才能使用这些操作。 +* 如果`handler`没有设置拦截,则直接返回原对象。 +```js +let target = {}; +let handler = {}; +let p = new Proxy(target, handler); +p.a = 'leo'; +target.a; // 'leo' +``` +**同个拦截器函数,设置多个拦截操作**: +```js +let p = new Proxy(function(a, b){ + return a + b; +},{ + get:function(){ + return 'get方法'; + }, + apply:function(){ + return 'apply方法'; + } +}) +``` +这里还有一个简单的案例: +```js +let handler = { + get : function (target, name){ + return name in target ? target[name] : 16; + } +} + +let p = new Proxy ({}, handler); +p.a = 1; +console.log(p.a , p.b); +// 1 16 +``` +这里因为 `p.a = 1` 定义了`p`中的`a`属性,值为`1`,而没有定义`b`属性,所以`p.a`会得到`1`,而`p.b`会得到`undefined`从而使用`name in target ? target[name] : 16;`返回的默认值`16`; + + +**`Proxy`支持的13种拦截操作**: +13种拦截操作的详细介绍:[打开阮一峰老师的链接](http://es6.ruanyifeng.com/#docs/proxy)。 +* `get(target, propKey, receiver)`: +拦截对象属性的读取,比如proxy.foo和proxy['foo']。 + +* `set(target, propKey, value, receiver)`: +拦截对象属性的设置,比如proxy.foo = v或proxy['foo'] = v,返回一个布尔值。 + +* `has(target, propKey)`: +拦截propKey in proxy的操作,返回一个布尔值。 + +* `deleteProperty(target, propKey)`: +拦截delete proxy[propKey]的操作,返回一个布尔值。 + +* `ownKeys(target)`: +拦截Object.getOwnPropertyNames(proxy)、Object.getOwnPropertySymbols(proxy)、Object.keys(proxy)、for...in循环,返回一个数组。该方法返回目标对象所有自身的属性的属性名,而Object.keys()的返回结果仅包括目标对象自身的可遍历属性。 + +* `getOwnPropertyDescriptor(target, propKey)`: +拦截Object.getOwnPropertyDescriptor(proxy, propKey),返回属性的描述对象。 + +* `defineProperty(target, propKey, propDesc)`: +拦截Object.defineProperty(proxy, propKey, propDesc)、Object.defineProperties(proxy, propDescs),返回一个布尔值。 + +* `preventExtensions(target)`: +拦截Object.preventExtensions(proxy),返回一个布尔值。 + +* `getPrototypeOf(target)`: +拦截Object.getPrototypeOf(proxy),返回一个对象。 + +* `isExtensible(target)`: +拦截Object.isExtensible(proxy),返回一个布尔值。 + +* `setPrototypeOf(target, proto)`: +拦截Object.setPrototypeOf(proxy, proto),返回一个布尔值。如果目标对象是函数,那么还有两种额外操作可以拦截。 + +* `apply(target, object, args)`: +拦截 Proxy 实例作为函数调用的操作,比如proxy(...args)、proxy.call(object, ...args)、proxy.apply(...)。 + +* `construct(target, args)`: +拦截 Proxy 实例作为构造函数调用的操作,比如new proxy(...args)。 + +## 2.2 取消Proxy实例 +使用`Proxy.revocable`方法取消`Proxy`实例。 +```js +let a = {}; +let b = { + get: function(target, name) { + return "[[" + name + "]]"; + } +}; +let revoke = Proxy.revocable(a, b); +let proxy = revoke.proxy; + +proxy.age; // "[[age]]" +revoke.revoke(); +proxy.age; // Uncaught TypeError: Cannot perform 'get' on a proxy that has been revoked + +proxy.age = 10; // Uncaught TypeError: Cannot perform 'set' on a proxy that has been revoked +delete proxy.age; // Uncaught TypeError: Cannot perform 'deleteProperty' on a proxy that has been revoked +typeof proxy; // "object" +``` + +## 2.3 实现 Web服务的客户端 +```js +const service = createWebService('http://le.com/data'); +service.employees().than(json =>{ + const employees = JSON.parse(json); +}) + +function createWebService(url){ + return new Proxy({}, { + get(target, propKey, receiver{ + return () => httpGet(url+'/'+propKey); + }) + }) +} +``` + +# 3. Proxy实践 +## 3.1 数据拦截验证 +通过`Proxy`代理对象的`set`和`get`方法来进行拦截数据,像`Vue`就是用数据拦截来实现数据绑定。 +```js +let handler = { + // 拦截并处理get方法 + // 可以理解为设置get方法返回的默认值 + get : function (target, key){ + return key in target ? target[key] : 30; + }, + + // 拦截并处理set方法 + // 可以理解为设置set方法的默认行为 + set : function (target, key, value){ + if(key === "age"){ + if (!Number.isInteger(value)){ + throw new TypeError("age不是一个整数!"); + } + if (value > 200){ + throw new TypeError("age不能大于200!"); + } + } + // 保存默认行为 + target[key] = value; + } +} + +let p = new Proxy({}, handler); +p.a = 10; // p.a => 10 +p.b = undefined; // p.b => undefined +p.c; // 默认值 30 +p.age = 100; // p.age => 100 +p.age = 300; // Uncaught TypeError: age不能大于200! +p.age = "leo"; // Uncaught TypeError: age不是一个整数! +``` + +## 3.2 函数节流 +通过拦截`handler.apply()`方法的调用,实现函数只能在1秒之后才能再次被调用,经常可以用在防止重复事件的触发。 +```js +let p = (fun, time) => { + // 获取最后点击时间 + let last = Date.now() - time; + return new Proxy (fun, { + apply(target, context, args){ + if(Date.now() - last >= time){ + fun.bind(target)(args); + // 重复设置当前时间 + last = Date.now(); + } + } + }) +} + +let p1 = () => { + console.log("点击触发"); +} +let time = 1000; // 设置时间 +let proxyObj = p(p1, time); +// 监听滚动事件 +document.addEventListener('scroll', proxyObj); +``` + +## 3.3 实现单例模式 +通过拦截`construct`方法,让不同实例指向相同的`constructer`,实现单例模式。 +```js +let p = function(fun){ + let instance; + let handler = { + // 拦截construct方法 + construct: function(targer, args){ + if(!instance){ + instance = new fun(); + } + return instance; + } + } + return new Proxy(fun, handler); +} + +// 创建一个construct案例 +function Cons (){ + this.value = 0; +} + +// 创建实例 +let p1 = new Cons(); +let p2 = new Cons(); + +// 操作 +p1.value = 100; +// p1.value => 100 , p2.value => 0 +// 因为不是相同实例 + +// 通过Proxy实现单例 +let singleton = p(Cons); +let p3 = new singleton(); +let p4 = new singleton(); +p3.value = 130; +// p1.value => 130 , p2.value => 130 +// 现在是相同实例 +``` + + +# 参考资料 +[1.MDN 元编程](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/Meta_programming) +[2. ES6中的元编程-Proxy & Reflect](https://www.cnblogs.com/buzhiqianduan/p/9687804.html) +**本部分内容到这结束** + +|Author|王平安| +|---|---| +|E-mail|pingan8787@qq.com| +|博 客|www.pingan8787.com| +|微 信|pingan8787| +|每日文章推荐|https://github.com/pingan8787/Leo_Reading/issues| +|ES小册|js.pingan8787.com| \ No newline at end of file diff --git "a/Cute-JavaScript/Cute-JS/level1/2.\346\265\201\347\250\213\346\216\247\345\210\266\345\222\214\351\224\231\350\257\257\345\244\204\347\220\206.md" "b/Cute-JavaScript/Cute-JS/level1/2.\346\265\201\347\250\213\346\216\247\345\210\266\345\222\214\351\224\231\350\257\257\345\244\204\347\220\206.md" new file mode 100644 index 00000000..295009a1 --- /dev/null +++ "b/Cute-JavaScript/Cute-JS/level1/2.\346\265\201\347\250\213\346\216\247\345\210\266\345\222\214\351\224\231\350\257\257\345\244\204\347\220\206.md" @@ -0,0 +1,129 @@ +本文是 **重温基础** 系列文章的第二篇,需要让自己静下心来,学习,养成好习惯。 + +**本章节复习的是JS中的控制流语句,让我们能实现更多的交互功能。** + +注意一点:在ES6之前,JS是没有**块作用域**的,如果在语句块外部声明的变量,如果在块内部声明一个相同名称的变量,那么程序将取后声明的这个变量的值: +```js +var a = 1; +{ + var a = 2; +} +a; // 2 +``` + +但是ES6开始,用`let`声明的变量是块作用域的: +```js +let a = 1; +{ + let a = 2; +} +a; // 1 +``` + +# 1.条件判断语句 +用于根据指定条件返回结果,常见的是`if...else`和`switch`: + +## if...else语句 +若条件为**真**,则执行`if`后面的语句,若条件为**假**,则执行`else`后面的语句: +```js +if(condition){ + // do something +}else{ + // else 为可选 + // do something +} +``` +**False等效值**: +在JS中下面的值常常被计算为`false` +* false +* undefined +* null +* 0 +* NaN +* 空字符串("") + +**注意**: +不要使用原始布尔值`true`和`false` 与 Boolean对象的真和假混淆: +```js +let a = new Boolean(false); // Boolean {true} +if (a); // 永真 +if (a == true); // 永假 +``` + +# 2.switch语句 +通过匹配表达式的值到每个`case`标签,若匹配成功则执行相关语句: +```js +switch ( 'leo' ){ + case 'pingan': + // do something + break; // 可选 + case 'leo': + // do something + break; + default: // 都不匹配 则执行默认 + // do something + break; +} +``` +`break`为可选,目的用于保证在正确匹配后,能跳出程序的`switch`语句,并继续执行其他代码,若没有`break`则程序会继续执行下一个`case`语句。 + +# 3.异常处理语句 +当`throw`语句抛出的异常,我们可以使用`try...catch`捕获并处理,这里需要先介绍两个概念: +## throw语句: +用于抛出异常,后面可以是任何表达式: +```js +throw "error !"; +throw 404; +throw {msg: 'err'}; +``` +## try...catch语句: +用于捕获异常,`try`后面是程序正常时候执行的程序,`catch`后面是当前面有错误抛出的时候执行,并且捕获错误信息作为参数,并且在`catch`块执行完成,参数不可再用。 +```js +function f(){ + throw 'test error!' +} +try{ + f(); + console.log('success'); +}catch (err){ // err 为前面返回的错误信息 + console.log(err); + console.log('failed'); +} +// test error! +// failed +``` +通常在`try...catch`后还有一个`finally`语句块,用于不论前面是否有报错,都会执行`finally`语句: +```js +try{ + // do something +}catch(err){ + // do something +}finally{ + // do something +} +``` +`try...catch`常常也用在做网络请求的情况下: +```js +function getData (){ + try{ + let a = fetch(url); + }catch(err){ + console.log(err); + } +} +``` + + +# 参考资料 +[1.MDN 流程控制与错误处理](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/Control_flow_and_error_handling) + +--- +**本部分内容到这结束** + +|Author|王平安| +|---|---| +|E-mail|pingan8787@qq.com| +|博 客|www.pingan8787.com| +|微 信|pingan8787| +|每日文章推荐|https://github.com/pingan8787/Leo_Reading/issues| +|ES小册|js.pingan8787.com| \ No newline at end of file diff --git "a/Cute-JavaScript/Cute-JS/level1/3.\345\276\252\347\216\257\345\222\214\350\277\255\344\273\243.md" "b/Cute-JavaScript/Cute-JS/level1/3.\345\276\252\347\216\257\345\222\214\350\277\255\344\273\243.md" new file mode 100644 index 00000000..edca26c8 --- /dev/null +++ "b/Cute-JavaScript/Cute-JS/level1/3.\345\276\252\347\216\257\345\222\214\350\277\255\344\273\243.md" @@ -0,0 +1,170 @@ +本文是 **重温基础** 系列文章的第三篇,今天想起鬼脚七的一句话:人不一定自由,但思想一定是自由的。思想没有对和错,也没有高和低,只有不同。了解一个人可以去了解他的思想。 +。 + +**本章节复习的是JS中的循环语句,让我们能更快速且简单的完成一些需求。** + +首先我们要知道:循环实际上就是把一个事情重复n次执行,也有可能是0次,JS中有以下几种循环语句类型: +* `for`语句 +* `do...while`语句 +* `while`语句 +* `labeled`语句 +* `break`语句 +* `continue`语句 +* `for...in`语句 +* `for...of`语句 + +# 1.`for`语句 +重复执行`for`的条件语句,直到循环条件为`false`退出循环: +```js +var a = 1; +for ( a < 3){ + a ++; +} +``` +这个语句大概执行了以下操作: +* 1.定义并初始化一个变量`a`的值为`1`; +* 2.执行循环语句,条件为当`a < 3`的时候,`a`每次递增`1`; +* 3.返回第2步继续循环; +* 4.当`a`在等于4的时候,不满足循环条件`a < 3`,然后退出循环; +其他例子: +```js +// 求数组中所有值的和 +var a = [ 1, 3, 4, 6]; +var sum = 0; +for (var i = 0; i < a.length; i++){ + sum += a[i]; +} +sum; // 14 +``` + +# 2.`do...while`语句 +重复执行`do`的条件,直到不符合`while`的条件,退出循环: +```js +do { + a += 1; + console.log(a); +} while (a < 5); +``` + +# 3.`while`语句 +重复执行`while`的条件,直到`while`的条件为`false`,退出循环: +```js +var a = 0; +var b = 0; +while (a < 5){ + a ++; + b += a; +} +// a => 5; b => 15 +``` + +# 4.`labeled`语句 +用来标识一个程序位置的标识符,如标识一个循环,并在`break`或`continue`中指出终止标识符,来停止这个循环。 +```js +var a = [1, 2, 3, 4, 5]; +labelName: +for(var i = 0 ;i< a.length; i++){ + console.log(i); + if(i > 1){ + break labelName; + } +} +// 0 +// 1 +// 2 +``` + +# 5.`break`语句 +用于终止一个循环,还可以在`switch`中终止,通常这么使用: +* 终止一个循环: +```js +for(var i = 0 ;i< a.length; i++){ + if(a[i] == 1){ + break; + } +} +``` +* 终止一个label +```js +leo: +for(var i = 0 ;i< a.length; i++){ + for(var j = 0 ;j< a.length; j++){ + if(a[i] == a[j]){ + break leo; + } + } +} +``` + +# 6.`continue`语句 +用来跳过当前循环,进入下个循环,可以使用在`while`、`do...while`、`for`或者`label`语句: +* 有使用`continue` +```js +var a = 0; +var b = 0; +while (a < 5 ){ + a ++; + if(a == 2){ + continue; + } + b += a; + console.log(b); +} +// 1 4 8 13 +``` +* 没有使用`continue` +```js +var a = 0; +var b = 0; +while (a < 5 ){ + a ++; + if(a == 2){ + // continue; + } + b += a; + console.log(b); +} +// 1,3,6,10,15 +``` + +# 7.`for...in`语句 +通常用于遍历一个对象的所有可枚举的属性,执行指定方法: +```js +var a = [ + {name : 'leo'}, + {name : 'pingan'}, + {name : '平安'}, +] +for(var k in a){ + // a为循环对象 k为当前指针位置 + console.log(a[k]); +} +``` + +# 8.`for...of`语句 +通常用于遍历一个可迭代对象(包括`Array`,`Map`,`Set`和参数对象`arguments`等),执行指定方法: +```js +var a = [1, 2, 5]; +for(var k in a){ + console.log(k); // k 为当前元素的下标 +} +for(var m of a){ + console.log(m); // m 为当前元素的值 +} +``` + + + +# 参考资料 +[1.MDN 循环和迭代](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/Loops_and_iteration) + +--- +**本部分内容到这结束** + +|Author|王平安| +|---|---| +|E-mail|pingan8787@qq.com| +|博 客|www.pingan8787.com| +|微 信|pingan8787| +|每日文章推荐|https://github.com/pingan8787/Leo_Reading/issues| +|ES小册|js.pingan8787.com| \ No newline at end of file diff --git "a/Cute-JavaScript/Cute-JS/level1/4.\345\207\275\346\225\260.md" "b/Cute-JavaScript/Cute-JS/level1/4.\345\207\275\346\225\260.md" new file mode 100644 index 00000000..ff2285f3 --- /dev/null +++ "b/Cute-JavaScript/Cute-JS/level1/4.\345\207\275\346\225\260.md" @@ -0,0 +1,273 @@ +本文是 **重温基础** 系列文章的第四篇。 +今日感受:常怀感恩之心,对人对己。 + + +**本章节复习的是JS中的基础组件之一,函数,用来复用特定执行逻辑。** + +# 1.定义函数 +定义函数有两种方法:**函数声明** 和 **函数表达式** : +## 1.1 函数声明 +也成为**函数声明**,通常格式为: +```js +function f (a){ + return a + 1; +} +``` +**解释**:这里声明一个函数 `f` ,并传入一个参数 `a` ,当函数执行以后,通过 `return` 关键字返回了 `a+1`的值。 + +**参数**: +当传入的参数是一个数字/字符串等具体的值的时候,若参数的值被改变,不会影响到全局或调用函数。 +但如果参数是个对象,若函数内改变的这个参数的属性,则函数外部的这个参数原始的值会被修改。 +```js +var leo = { + age:20 +} +function f(obj){ + obj.age = 15; + obj.name = 'leo'; +} +f(leo); +console.log(leo); //{age: 15, name: "leo"} +``` + +## 1.2函数表达式 +通过定义一个匿名的函数,来赋值给一个变量,通过这个变量来调用这个函数。 +```js +var f = function (a){ + return a + 1; +} +``` +但是函数表达式也可以提供函数名,用于函数内部调用,并指代本身,也可以作为调试器堆栈跟踪中识别该函数。 +```js +var f = function g(a){ + return n < 2 ? 1 : a*g(a-1); +} +``` +另外,函数表达式声明可以用来根据不同条件,来定义一个函数: +```js +var f; +if(a == 1){ + f = function (){ + return 'when a == 1'; + } +}else { + f = function (){ + return 'when a != 1'; + } +} +``` + +# 2.函数调用 +函数定义完成后不会自动执行,需要我们通过函数名称来调用,才能真正执行: +```js +var f = function (){ + console.log('ok'); +} +f(); // 'ok' +``` +另外,函数也可以调用自身,这就是递归过程: +```js +function f (n){ + if( n==0 || n==1) { + return 1; + }else { + return n * f(n-1); + } +} +// 三目运算 +function f (n){ + return (n==0 || n==1)?1: n*f(n-1); +} +``` + +# 3.函数作用域 +由于函数只在函数的内部有定义,所以函数内部定义的变量在函数外部不能访问,函数内部就是这个函数的作用域。 +当一个父级函数内,还定义了一个子级函数,则这个子级函数可以访问父级函数定义的变量。 +```js +// 全局作用域 global scope +var a = 1, b = 2; +function f (){ + return a + b; +} +f(); // 3 + +function g(){ + var a1 = 'leo', b1 = 'pingan'; + function hi (){ + return a1 + '和' + b1 + } + return hi(); +} +g(); // 'leo和pingan' +``` +## 3.1 闭包 +闭包是 JavaScript 中最强大的特性之一,并且JS允许函数嵌套。 +在一个函数内部在嵌套一个函数,而嵌套的这个函数对外面的函数是私有的,则形成一个闭包,闭包是一个可以自己拥有独立的环境和变量的表达式,通常是函数。 +理解一下,前面说的内部函数可以调用外部函数的变量和方法,那么可以这么理解:**闭包的函数继承了父级容器函数的参数和变量**,即**内部函数包含外部函数的作用域**。 +总结一下: +* 内部函数只能在外部函数中访问; +* 内部函数形成闭包:可以访问外部函数的参数和变量,但外部函数却不能使用这个内部函数的参数和变量; +```js +function f(a) { + function g(b){ + return a + b; + } + return g; +} +var a1 = f(5); // ƒ g(b){ return a + b; } +var a2 = a1(6); // 11 +var a3 = f(5)(6); // 11 +``` +闭包可以给内部函数的变量提供一定的**安全保障**。 +另外,闭包还有复杂的用法: +```js +var f = function (name){ + var age ; + return { + setName : function (newName){ + name = newName; + }, + + getName : function (){ + return name; + }, + + getAge : function (){ + return age; + }, + setAge : function (newAge){ + age = newAge; + } + } +} + +var leo = f('leo'); +leo.setName('pingan'); +leo.setAge(20); +leo.getName(); // 'pingan' +leo.getAge(); // 20 +``` + +## 3.2命名冲突 +在同一个闭包作用域下若参数或变量名相同,产生冲突,则**优先使用作用域最近**: +```js +function f(){ + var a = 1; + function g(a){ + return a + 1; + } + return g; +} +f()(3); // 4 +``` + +# 4.arguments对象 +函数的实际参数会被保存在一个类数组对象 `arguments` 对象中,通过索引访问具体的参数: +```js +var a = arguments[i] +``` +`arguments`的索引从0开始,也有`arguments.length`属性获取长度。 +当我们不知道参数的数量的时候,可以使用`arguments.length`来获取实际传入参数的数量,再用`arguments`对象来获取每个参数。 +例如: +```js +// 拼接所有参数为一个字符串 +// 参数 s 为分隔符 +function f( s ){ + var text = ''; + for(var i = 0;i<= arguments.length; i++){ + text += arguments[i] + s ; + } + return text; +} + +f('--','leo','pingan','robin'); +// "----leo--pingan--robin--undefined--" +f('**','leo','pingan','robin'); +// "****leo**pingan**robin**undefined**" +``` + +# 5.函数参数 +ES6开始,新增两个类型的参数:**默认参数**和**剩余参数**: +## 5.1默认参数 +若函数没有传入参数,则参数默认值为`undefined`,通常设置参数默认值是这样做的: +```js +// 没有设置默认值 +function f(a, b){ + b = b ? b : 1; + return a * b; +} +f(2,3); // 6 +f(2); // 2 + +// 设置默认值 +function f(a, b = 1){ + return a * b; +} +f(2,3); // 6 +f(2); // 2 +``` + +## 5.2剩余参数 +可以将参数中不确定数量的参数表示成数组,如下: +```js +function f (a, ...b){ + console.log(a, b); +} +f(1,2,3,4); // a => 1 b => [2, 3, 4] +``` + +# 6.箭头函数 +**函数箭头表达式**是ES6新增的函数表达式的语法,也叫**胖箭头函数**,变化:更简洁的函数和`this`。 +* 更简洁的函数 +```js +// 有1个参数 +let f = v => v; +// 等同于 +let f = function (v){return v}; + +// 有多个参数 +let f = (v, i) => {return v + i}; +// 等同于 +let f = function (v, i){return v + i}; + +// 没参数 +let f = () => 1; +// 等同于 +let f = function (){return 1}; + +let arr = [1,2,3,4]; +arr.map(ele => ele + 1); // [2, 3, 4, 5] +``` + +* this +注意这几点: + 1. 箭头函数内的`this`总是指向**定义时所在的对象**,而不是调用时。 + 2. 箭头函数不能当做**构造函数**,即不能用`new`命令,否则报错。 + 3. 箭头函数不存在`arguments`对象,即不能使用,可以使用`rest`参数代替。 + 4. 箭头函数不能使用`yield`命令,即不能用作Generator函数。 + +一个简单的例子: +```js +function Person(){ + this.age = 0; + + setInterval(() => { + this.age++; + }, 1000); +} +var p = new Person(); // 定时器一直在执行 p的值一直变化 +``` + +# 参考资料 +[1.MDN 函数](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/Functions) + +--- +**本部分内容到这结束** + +|Author|王平安| +|---|---| +|E-mail|pingan8787@qq.com| +|博 客|www.pingan8787.com| +|微 信|pingan8787| +|每日文章推荐|https://github.com/pingan8787/Leo_Reading/issues| +|ES小册|js.pingan8787.com| \ No newline at end of file diff --git "a/Cute-JavaScript/Cute-JS/level1/5.\350\241\250\350\276\276\345\274\217\345\222\214\350\277\220\347\256\227\347\254\246.md" "b/Cute-JavaScript/Cute-JS/level1/5.\350\241\250\350\276\276\345\274\217\345\222\214\350\277\220\347\256\227\347\254\246.md" new file mode 100644 index 00000000..2ef06d6a --- /dev/null +++ "b/Cute-JavaScript/Cute-JS/level1/5.\350\241\250\350\276\276\345\274\217\345\222\214\350\277\220\347\256\227\347\254\246.md" @@ -0,0 +1,304 @@ +本文是 **重温基础** 系列文章的第五篇。 +今日感受:家的意义。 + + +**本章节复习的是JS中的表达式和运算符,用好这些可以大大提高开发效率。** + +一些基础:JavaScript中运算符有一元、二元和三元(条件)运算符,常见写法: +```js +// 操作数 + 运算符 + 操作数 +1 + 2 ; + +// 运算符 + 操作数 +x ++; + +// 操作数 + 运算符 +++ x; +``` + + +本文将介绍一下几类运算符: +* 赋值运算符(Assignment operators) +* 比较运算符(Comparison operators) +* 算数运算符(Arithmetic operators) +* 位运算符(Bitwise operators) +* 逻辑运算符(Logical operators) +* 字符串运算符(String operators) +* 条件(三元)运算符(Conditional operator) +* 逗号运算符(Comma operator) +* 一元运算符(Unary operators) +* 关系运算符(Relational operator) + +# 1.赋值运算符 +最简单的赋值运算符是 `=` ,它将右边操作数的值赋值给左边的操作数,如 `a = b`。 +另外常见的复合赋值运算符有如下: +|名称|简写的操作符|含义| +|---|---|---| +|赋值| `x = y`| `x = y`| +|加法赋值| `x += y` | `x = x + y`| +|减法赋值| `x -= y` | `x = x - y`| +|乘法赋值| `x *= y` | `x = x * y`| +|除法赋值| `x /= y` | `x = x / y`| +|求余赋值| `x %= y` | `x = x % y`| +|求幂赋值| `x **= y`| `x = x ** y`| +|左移位赋值| `x <<= y`| `x = x << y`| +|右移位赋值| `x >>= y`| `x = x >> y`| +|无符号右移位赋值| `x >>>= y`| `x = x >>> y`| +|按位与赋值| `x &= y`| `x = x & y`| +|按位异或赋值| `x ^= y`| `x = x ^ y`| +|按位或赋值| `x |= y`| `x = x | y`| + +另外在ES6中,新增一类**解构赋值**: +```js +let a = ['aa', 'bb', 'cc']; + +// 不使用解构赋值 +let a1 = a[0]; +let a2 = a[1]; + +// 使用解构赋值 +let [a1, a2] = a; +``` + +# 2.比较运算符 +通过比较两个比较对象来返回一个是否为真的布尔值,当两个比较的对象不是相同类型,JavaScript会尝试将两个比较对象转换成相同类型进行比较: +```js +let a = 10; +let b = '12'; +a > b; // false +``` + +常用的比较运算符有: +|名称|描述|返回true的示例| +|---|---|---| +|等于 `==` | 操作符两边数据相等 | `3 == '3'` | +|不等于 `!==` | 操作符两边数据不相等 | `3 != '4'` | +|全等 `===` | 操作符两边数据相等且类型相同 | `3 === 3` | +|不全等 `!==` | 操作符两边数据不相等或类型不相同 | `3 !== '3'` | +|大于 `>` | 判断操作符左边大于右边 | `3 > 2` | +|大于等于 `>=` | 判断操作符左边大于或等于右边 | `3 >= 2` | +|小于 `<` | 判断操作符左边小于右边 | `3 < 4` | +|小于等于 `<=` | 判断操作符左边小于或等于右边 | `3 <= 4` | + +**注意:** +`=>`不是运算符,而是ES6中新增的**箭头函数**的标记符号。 + +# 3.算数运算符 +除了标准的加减乘除这些基本运算符,JavaScript还提供一些新的算数运算符: +|名称|描述|示例| +|---|---|---| +|求余 `%` | 返回相除之后的余数 | `11 % 5` 返回 1 | +|自增 `++` | `++N`返回加一以后的值,`N++`返回原数值然后加一 | `++3`返回4,`3++`返回3 | +|自减 `--` | `--N`返回减一以后的值,`N--`返回原数值然后减一 | `--3`返回2,`3--`返回3 | +|一元负值符 `-` | 返回操作数的负数,若不是`Number`则试图转换为`Number`再取负值 |`-'-2'` 返回`2`;`-2`返回`2` | +|一元正值符 `+` | 若操作数不是`Number`类型则试图转换为`Number`|`+'-2'` 返回`-2`;`+'2'`返回`2` | +|指数运算符 `**` | 计算底数`a`的指数`n`次方 | `2 ** 3` 返回 8 | + +# 4.位运算符 +位运算符是在数字底层(即表示数字的 32 个数位)进行操作的。 +[复习数字32位数的表示](http://www.w3school.com.cn/js/pro_js_operators_bitwise.asp) +|名称|描述|示例| +|---|---|---| +|按位与 `AND` `&`| 在`a`和`b`的位表示中,每一个对应的位都为1则返回1,否则0 | `a & b`| +|按位或 `OR` `|` | 在`a`和`b`的位表示中,每一个对应的位,只要有一个为1则返回1,否则0 | `a | b`| +|按位异或 `XOR` `^` | 在`a`和`b`的位表示中,每一个对应的位,两个不相同则返回1,否则0 | `a ^ b`| +|按位非 `NOT` `~` | 反转被操作数的位 | `~a`| +|左移 `shift` `<<` | 将`a`的二进制串向左移动`b`位,右边移入0 | `a << b`| +|算术右移 `>>`| 译注:算术右移左边空出的位是根据最高位是0和1来进行填充的 | `a >> b`| +|无符号右移(左边空出位用0填充) `>>>` | 把`a`的二进制表示向右移动`b`位,丢弃被移出的所有位,并把左边空出的位都填充为0 | `a >>> b`| + +示例解释: +1的二进制表示为 `0 0 0 0 0 0 1` +3的二进制表示为 `0 0 0 0 0 1 1` +* 1.按位与 `&` +```js +1 & 3 ; // 1 +1 | 3 ; // 3 +1 ^ 3 ; // 2 +~1 ; // -2 +1>>1 ; // 0 +``` + +## 使用案例 +* 1.16进制颜色值转RGB: +```js +function hexToRGB(hex){ + let h = hex.replace('#','0x'), + r = h >> 16, + g = h >> 8 & 0xff, + b = h & 0xff; + return `rgb(${r},${g},${b})` +} +hexToRGB('#eeddff'); // "rgb(238,221,255)" +``` + +* 2.RGB转16进制: +```js +function RGBToHex(rgb){ + let r = rgb.split(/[^\d]+/), + c = r[1]<<16 | r[2]<<8 | r[3]; + return `#${c.toString(16)}`; +} +RGBToHex('rgb(238,221,255)'); // "#eeddff" +``` + +# 5.逻辑运算符 +常用来处理布尔值,但是当处理非布尔值的时候,往往返回非布尔值: +|运算符|描述|示例| +|---|---|---| +|逻辑与 `&&` |若`a`和`b`都能转为`true`则返回`true`| `1+1==2 && 1-1==0` 返回 `true` | +|逻辑或 `||` |若`a`和`b`其中一个能转为`true`则返回`true`,若都是`false`则返回`false`| `1+1==3 || 1-1==0` 返回 `true` | +|逻辑非 `!` |若`a`能转为`true`则返回`false`| `!1+1==2` 返回 `false` | + +**注意**: 能被转成`false`的值有`null`,`0`,`NaN`,空字符串`""`和`undefined`。 +几个示例: +```js +let a1 = true && true; // true +let a2 = true && false; // false +let a3 = false && true; // false +let a4 = false && false; // false +let a5 = false && "leo"; // false +let a6 = true && "leo"; // "leo" +let a7 = "leo" && "robin";// "robin" + + +let b1 = true || true; // true +let b2 = true || false; // true +let b3 = false || true; // true +let b4 = false || false; // false +let b5 = false || "leo"; // "leo" +let b6 = true || "leo"; // true +let b7 = "leo" || "robin";// "leo" + +let c1 = !true; // false +let c2 = !false; // true +let c3 = !"leo"; // false +``` +常常还使用**短路求值**: +```js +false && anything ; // 被短路求值为false +true || anything ; // 被短路求值为true +``` + +# 6.字符串运算符 +在拼接字符串中,由 `+` 来连接两个字符串: +```js +let a = 'leo ' + 'cute~'; // 'leo cute~' + +let b = 'ha'; +a += b; // "leo cute~ha" +``` + +# 7.条件(三元)运算符 +可以使用三个操作数的运算符,运算结果为根据给定条件在两个值中取一个: +```js +// 当条件为真 则取 值1 ,否则取 值2 +// 条件 ? 值1 : 值2 +let a = 10; +let b = a > 5 ? 'yes' : 'no'; // 'yes' +``` + +# 8.逗号运算符 +对两个操作数求值并且返回最终操作数的值,通常用于`for`循环中,在每次循环时对多个变量进行更新: +```js +let a1 = [1,2,3,9,6,6]; +for(let i = 0,j = 5; i<=j; i++, j--){ + console.log(`i:${i},j:${j},i值:${a1[i]},j值:${a1[j]}`) +} +// i:0,j:5,i值:1,j值:6 +// i:1,j:4,i值:2,j值:6 +// i:2,j:3,i值:3,j值:9 +``` + +# 9.一元运算符 +一元操作符仅对应一个操作数。 +## delete +删除一个对象或一个对象的属性或者一个数组中某一个键值,返回一个布尔值,删除成功返回`true`,否则返回`false`: +```js +let a = {name : 'leo',age : '15'}; +delete a.name; // true +a; // {age: "15"} + +let b = [1,3,5]; +delete b[0]; // true +b; // [empty, 3, 5] +b[0]; // undefined +``` +## typeof +返回一个参数的类型的字符串值,参数可以输字符串,变量,关键词或者对象: +```js +typeof new Function(); // "function" +typeof "leo" ; // "string" +typeof 11 ; // "number" +typeof undefined ; // "undefined" + +typeof true ; // "boolean" +typeof null ; // "object" +``` +参数也可以是表达式,`typeof`会根据其返回结果返回所包含的类型: +```js +typeof (1+1) ; // "number" +typeof (1+1==2 ? 'yes' : 'no') ; // "string" +``` +## void +表示一个运算没有返回值,常常用在创建一个超链接文本,但是点击的时候没有任何效果: +```html +点击 +点击 +``` + +# 10.关系运算符 +比较两个操作数: +## in +判断指定属性是否在指定对象中,若是则返回`true`: +```js +// 对象 +let a = {name:'leo',age:'15'}; +'name' in a; // true + +// 数组 +let b = ['leo', 'pingan', 'robin']; +0 in b; // true +'length' in b;// true +``` + +## instanceof +判断一个对象是否是指定类型,若是则返回`true`: +```js +let d = new Date(); +d instanceof Date; // true +``` + +# 11.运算符优先级 +当我们需要调整表达式计算顺序,就需要用到运算符的优先级,通过括号来实现排序,常见优先级从高到低: +|运算符|描述| +|---|---| +|`. [] ()` |字段访问、数组下标、函数调用以及表达式分组| +|`++ -- - ~ ! delete new typeof void` |一元运算符、返回数据类型、对象创建、未定义值| +|`* / %`|乘法、除法、取模| +|`+ - +`|加法、减法、字符串连接| +|`<< >> >>>`|移位| +|`< <= > >= instanceof`|小于、小于等于、大于、大于等于、instanceof| +|`== != === !==`|等于、不等于、严格相等、非严格相等| +|`&`|按位与| +|`^`|按位异或| +|`|`|按位或| +|`&&`|逻辑与| +|`||`|逻辑或| +|`?:`|条件| +|`= oP=`|赋值、运算赋值| +|`,`|多重求值| + +# 参考资料 +[1.MDN 表达式和运算符](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/Expressions_and_Operators) + +--- +**本部分内容到这结束** + +|Author|王平安| +|---|---| +|E-mail|pingan8787@qq.com| +|博 客|www.pingan8787.com| +|微 信|pingan8787| +|每日文章推荐|https://github.com/pingan8787/Leo_Reading/issues| +|ES小册|js.pingan8787.com| \ No newline at end of file diff --git "a/Cute-JavaScript/Cute-JS/level1/6.\346\225\260\345\255\227.md" "b/Cute-JavaScript/Cute-JS/level1/6.\346\225\260\345\255\227.md" new file mode 100644 index 00000000..a1f79587 --- /dev/null +++ "b/Cute-JavaScript/Cute-JS/level1/6.\346\225\260\345\255\227.md" @@ -0,0 +1,248 @@ +本文是 **重温基础** 系列文章的第六篇。 +今日感受:自己需要多总结,会有不同收获(比如今晚我做的转正总结)。 + +**本章节复习的是JS中的数字类型,涉及的API比较多。** + +前置基础: +在JavaScript中,数字为双精度浮点类型(即一个数字范围只能在-(253-1)和(253-1)之间),整数类型也一样。 +另外数字类型也可以是以下三种符号值: +* `+Infinity` : 正无穷; +* `-Infinity` : 负无穷; +* `NaN` : 非数字(not a number); + +# 1.数字对象 +JS中内置了`Number`对象的一些常量属性: + +|属性|描述| +|---|---| +|`Number.MAX_VALUE`|可表示的最大值| +|`Number.MIN_VALUE`|可表示的最小值| +|`Number.NaN`|特指“非数字”| +|`Number.NEGATIVE_INFINITY`|特指“负无穷”;在溢出时返回| +|`Number.POSITIVE_INFINITY`|特指“正无穷”;在溢出时返回| +|`Number.EPSILON`|表示1和比最接近1且大于1的最小Number之间的差别| +|`Number.MIN_SAFE_INTEGER`|JavaScript最小安全整数.| +|`Number.MAX_SAFE_INTEGER`|JavaScript最大安全整数.| + +注意:以上所有属性都是**不可写,不可枚举,也不可配置**。 + +* `Number.MAX_VALUE` + +`Number.MAX_VALUE`是 `Number` 对象的一个**静态属性**,值接近于 `1.79E+308`。大于 `Number.MAX_VALUE` 的值代表 "`Infinity`"。 +```js +let a = 100; +if(a < Number.MAX_VALUE){ + console.log('success'); +} +// success +``` + +* `Number.MIN_VALUE` + +`Number.MIN_VALUE`是 `Number` 对象的一个**静态属性**,值接近于 `5e-324`,是 JavaScript 里最接近 0 的正值,而不是最小的负值。 +```js +let a = 100; +if(a > Number.MIN_VALUE){ + console.log('success'); +} +// success +``` + +* `Number.NaN` + +`Number.NaN` 表示“非数字”,和 `NaN` 一样。 + +* `Number.POSITIVE_INFINITY` + +`Number.POSITIVE_INFINITY` 属性表示正无穷大,值同全局对象 `Infinity` 属性的值相同。 +```js +let a = Number.MAX_VALUE * 2; +if(a == Number.POSITIVE_INFINITY){ + console.log('success'); +} +// success +``` + +* `Number.NEGATIVE_INFINITY` + +`Number.NEGATIVE_INFINITY` 属性表示负无穷大,值和全局对象的 `Infinity` 属性的**负值**相同。 +```js +let a = - Number.MAX_VALUE * 2; +if(a == Number.NEGATIVE_INFINITY){ + console.log('success'); +} +// success +``` + +* `Number.EPSILON` + +`Number.EPSILON`属性表示 1 与Number可表示的大于 1 的最小的浮点数之间的差值,值接近于 2-52 。 +```js +let a = 0.1, b = 0.2, c = 0.3; +let d = (Math.abs(a + b - c) < Number.EPSILON); +d; // true +``` + +* `Number.MIN_SAFE_INTEGER` + +JS中最小的安全的integer型数字 (-(253 - 1))。 + +* `Number.MAX_SAFE_INTEGER` + +JS中最大的安全的integer型数字 (253 - 1)。 + + +# 2.数字方法 +常见的方法有: + +|方法|描述| +|---|---| +|`Number.parseFloat()`|把字符串参数解析成浮点数,和全局方法 `parseFloat()` 作用一致.| +|`Number.parseInt()`|把字符串解析成特定基数对应的整型数字,和全局方法 `parseInt()` 作用一致.| +|`Number.isFinite()`|判断传递的值是否为有限数字。| +|`Number.isInteger()`|判断传递的值是否为整数。| +|`Number.isNaN()`|判断传递的值是否为 `NaN`.| +|`Number.isSafeInteger()`|判断传递的值是否为安全整数。| + +使用方法: +* Number.parseFloat() +```js +let a1 = 3.1415, a2 = '3.1114'; +Number.parseFloat(a1); // 3.1415; +Number.parseFloat(a2); // 3.1111; +parseFloat(a1); // 3.1415; +parseFloat(a2); // 3.1111; +Number.parseFloat == parseFloat;// true +``` +* Number.parseInt() +```js +let a1 = '0110'; +Number.parseInt(a1, 2); // 6 +Number.parseInt(a1, 10); // 110 +Number.parseInt = parseInt; // true +``` +* Number.isFinite() +```js +Number.isFinite(Infinity); // false +Number.isFinite(NaN); // false +Number.isFinite(-Infinity); // false +Number.isFinite(0); // true +Number.isFinite(2e64); // true +Number.isFinite('0'); // false, 全局函数 isFinite('0') 会返回 true +``` +* Number.isInteger() +```js +Number.isInteger(0); // true +Number.isInteger(1); // true +Number.isInteger(-100000); // true +Number.isInteger(0.1); // false +Number.isInteger(Math.PI); // false +Number.isInteger(Infinity); // false +Number.isInteger(-Infinity); // false +Number.isInteger("10"); // false +Number.isInteger(true); // false +Number.isInteger(false); // false +Number.isInteger([1]); // false +``` +* Number.isNaN() +```js +Number.isNaN(NaN); // true +Number.isNaN(Number.NaN); // true +Number.isNaN(0 / 0) // true +// 下面这几个如果使用全局的 isNaN() 时,会返回 true。 +Number.isNaN("NaN"); // false,字符串 "NaN" 不会被隐式转换成数字 NaN。 +Number.isNaN(undefined); // false +Number.isNaN({}); // false +Number.isNaN("blabla"); // false +// 下面的都返回 false +Number.isNaN(true); +Number.isNaN(null); +Number.isNaN(37); +Number.isNaN("37"); +Number.isNaN("37.37"); +Number.isNaN(""); +Number.isNaN(" "); +``` +* Number.isSafeInteger() +```js +Number.isSafeInteger(3); // true +Number.isSafeInteger(Math.pow(2, 53)) // false +Number.isSafeInteger(Math.pow(2, 53) - 1) // true +Number.isSafeInteger(NaN); // false +Number.isSafeInteger(Infinity); // false +Number.isSafeInteger("3"); // false +Number.isSafeInteger(3.1); // false +Number.isSafeInteger(3.0); // true +``` + +数字类型原型上的方法: + +|方法|描述|案例| +|---|---|---| +|`toExponential()`|返回一个数字的指数形式的字符串|1.23e+2| +|`toFixed()`|返回指定小数位数的表示形式|var a=123,b=a.toFixed(2)//b="123.00"| +|`toPrecision()`|返回一个指定精度的数字。|a=123中,3会由于精度限制消失var a=123,b=a.toPrecision(2)//b="1.2e+2"| + +* `toExponential()` + +以**指数表示法**返回该数值**字符串**表示形式,可接收一个参数指定小数点后几位数字。 +```js +let a = 99.6633; +let a1 = '字符串:' + a.toExponential(); // "字符串:9.96633e+1" +let a2 = '字符串:' + a.toExponential(2);// "字符串:9.97e+1" +``` + +* `toFixed()` + +使用定点表示法来格式化一个数,可接收一个参数指定保留小数点后几位,取值为0-20之间。 +注意: 返回的数据类型是字符串类型。 +```js +let a = 1.2345; +a.toFixed(); // "1" +a.toFixed(2); // "1.23" +``` +* `toPrecision()` + +以**指定的精度返**回该数值对象的字符串表示,可接收一个参数,用来指定有效数个数的整数。 +```js +let a = 1.2345; +let a1 = '字符串:' + a.toPrecision(); // "字符串:1.2345" +let a2 = '字符串:' + a.toPrecision(1);// "字符串:1" +let a2 = '字符串:' + a.toPrecision(2);// "字符串:1.2" +``` + +# 3.数学对象 +JS内置的数学对象`Math`,有很多属性和方法,这里需要注意的是`Math`对象中的三角函数参数都是**弧度制**的。 + +|方法|描述| +|---|---| +|`abs()`|绝对值| +|`sin()`, `cos()`, `tan()`|标准三角函数;参数为弧度| +|`asin()`, `acos()`, `atan()`, `atan2()`|反三角函数; 返回值为弧度| +|`sinh()`, `cosh()`, `tanh()`|双曲三角函数; 返回值为弧度.| +|`asinh()`, `acosh()`, `atanh()`|反双曲三角函数;返回值为弧度.| +|`pow()`, `exp()`, `expm1()`, `log10()`, `log1p()`, `log2()`|指数与对数函数| +|`floor()`, `ceil()`|返回最大/最小整数小于/大于或等于参数| +|`min()`,` max()`|返回一个以逗号间隔的数字参数列表中的较小或较大值(分别地)| +|`random()`|返回0和1之间的随机数。| +|`round()`,`fround()`, `trunc()`|四舍五入和截断函数| +|`sqrt()`, `cbrt()`, `hypot()`|平方根,立方根,平方参数的和的平方根| +|`sign()`|数字的符号, 说明数字是否为正、负、零。| +|`clz32()`,`imul()`|在32位2进制表示中,开头的0的数量.返回传入的两个参数相乘结果的类C的32位表现形式| + +完整的描述和使用方法,建议查看 [MDN Math](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Math) + + +# 参考资料 +[1.MDN 数字和日期](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/Numbers_and_dates) + +--- +**本部分内容到这结束** + +|Author|王平安| +|---|---| +|E-mail|pingan8787@qq.com| +|博 客|www.pingan8787.com| +|微 信|pingan8787| +|每日文章推荐|https://github.com/pingan8787/Leo_Reading/issues| +|ES小册|js.pingan8787.com| \ No newline at end of file diff --git "a/Cute-JavaScript/Cute-JS/level1/7.\346\227\266\351\227\264\345\257\271\350\261\241.md" "b/Cute-JavaScript/Cute-JS/level1/7.\346\227\266\351\227\264\345\257\271\350\261\241.md" new file mode 100644 index 00000000..4ba86ff5 --- /dev/null +++ "b/Cute-JavaScript/Cute-JS/level1/7.\346\227\266\351\227\264\345\257\271\350\261\241.md" @@ -0,0 +1,268 @@ +本文是 **重温基础** 系列文章的第七篇。 +今日感受:做好自律。 + +**本章节复习的是JS中的时间对象,一些处理的方法。** + +**前置知识**: +JavaScript中的时间是以1970年1月1日00:00:00以来的毫秒数来储存数据类型。 +`Data`对象的范围是相对距离UTC1970年1月1日的前后100,000,000天。 +创建一个时间对象: +```js +let d = new Date([params]); +``` +参数`params`可以是: +* 无参数:默认创建今天的日期和时间。 +* 一个符合以下格式的表示日期的字符串: + +"月 日, 年 时:分:秒."或者"年月日 时分秒" +```js +let d = new Date("2018-12-20"); +``` +如果你省略时、分、秒,那么他们的值将被设置为0。 +* 一个年,月,日的整型值的集合: +```js +let d = new Date(2018, 12, 20); +``` +* 一个年,月,日,时,分,秒的集合: +```js +let d = new Date(2018, 12, 20, 23, 20, 10); +``` + +这里Date对象涉及到的方法特别多,请移步[W3school JavaScript Date 对象](http://www.w3school.com.cn/jsref/jsref_obj_date.asp) + + +# 1.Date对象的方法 +常用处理的方法有以下几类: +* "`set`":用于设置Date对象的日期和时间的值。 +* "`get`":用去获取Date对象的日期和时间的值。 +* "`to`":用于返回Date对象的字符串格式的值。 +* "`parse`和`UTC`":用于解析Date字符串。 + +**需要注意的Date对象的一些数值问题:** +* 秒/分: 0 - 59; +* 时: 0 - 23; +* 星期: 0(周日) - 6(周六) +* 日期: 1 - 31 +* 月份: 0(一月) - 11(十二月) +* 年份: 从1900开始的年数 + +例如: +```js +let d = new Date('2018-12-10'); +let d1 = d.getMonth(); // 11 +let d2 = d.getFullYear(); // 2018 +``` +获取今年剩下的天数: +```js +let d = new Date(); +let e = new Date(2018, 11, 31, 23, 59, 59, 999); // 设置年月日时分秒 +e.setFullYear(d.getFullYear); // 设置为今年 +let m = 24 * 60 * 60 * 1000; // 每日毫秒数 +let result = (e.getTime() - d.getTime()) / m; +result = Math.round(result); // 返回今年剩余天数 +``` + +这里Date对象涉及到的方法特别多,请移步[W3school JavaScript Date 对象](http://www.w3school.com.cn/jsref/jsref_obj_date.asp) + +**注意:** +> 格林尼治标准时间(GMT)英国、爱尔兰、冰岛和葡萄牙属于该时区。这个时区与中国北京时间的时差是8个小时,也就是说比北京时间晚8个小时。 + +# 2.使用Date对象 +## 2.1 **设置日期** +为一个时间对象设置指定日期(2018年12月20日),注意这里:和前面说的一样,12月在JS的Date对象中,是用`11`表示。 +```js +let d = new Date(); +d.setFullYear(2018,11,20); +``` +设置时间对象`10`天以后: +```js +let d = new Date(); +d.setDate(d.getDate() + 10); // 先获取当天的日期,再设置到指定天数以后 +``` + +## 2.2 **比较时间** +通常情况下,像下面这样简单比较: +```js +let d = new Date(); +let e = new Date(); +d.setFullYear(2018,10,10); +let r = d > e ? 'good' : 'nice' ; // nice +``` +还可以比较两个日期相差多少天: +```js +let d1 = new Date('2018-10-10'); +let d2 = new Date('2018-11-11'); +let d3 = (d2 - d1) / (1000 * 60 * 60 * 24); // 32 +``` + +## 2.3 **计算N天后星期几** +```js +function d (num){ + if(typeof Number(num) === 'number'){ + let d1 = new Date(); + let d2 = d1.setDate(d1.getDate() + Number(num)); + let n = new Date(d2).getDay(); + let s = ''; + switch (n){ + case 0 : + s = "星期天"; + break; + case 1 : + s = "星期一"; + break; + case 2 : + s = "星期二"; + break; + case 3 : + s = "星期三"; + break; + case 4 : + s = "星期四"; + break; + case 5 : + s = "星期五"; + break; + case 6 : + s = "星期六"; + break; + } + return s; + }else { + alert('请输入正确数字!'); + } +} +``` + +## 2.4 格式化日期 +常见的日期格式化为字符串的方法有这些: + +* `toDateString()`——以特定于实现的格式显示星期几、月、日和年; + +* `toTimeString()`——以特定于实现的格式显示时、分、秒和时区; + +* `toLocaleDateString()`——以特定与地区的格式显示星期几、月、日和年; + +* `toLocaleTimeString()`——以特定于实现的格式显示时、分、秒; + +* `toUTCString()`——以特定于实现的格式完整的UTC日期。 + +**获取并格式化日期:年-月-日**: + +```js +function d (date){ + return date.getFullYear() + '-' + (date.getMonth() + 1) + '-' + date.getDate(); +} +d(new Date()); // "2018-12-20" +``` + +**日期字符串转为 年-月-日**: + +```js +function d (str){ + return new Date(Date.parse(str.replace(/-/g, '/'))); + // 或者 + // return new Date(str.replace(/-/g, '/')); +} +``` + +**获取当前星期几**: + +```js +let d = "今天是星期" + "日一二三四五六".charat(new Date().getDay()); +``` + +## 2.5 获取某年某月的天数 +这里有个小技巧,若给`new Date()`传入一个如`aaaa/aa/0`参数时,可以得到`aa`月的**前一个月的最后一天**,如传入`2018/12/0`会得到`2018/11/30`。 + +**值得注意的是:** 在Chrome浏览器上并不支持,会返回`Invalid Date`导致结果为`NaN`,但是我们可以使用`aaaa,aa,0`形式作为参数,下面分别写出这两种: + +```js +// aaaa/aa/0形式 只要传入年和月 +function d (y, m){ + m = parseInt(m, 10) + 1; + let r = new Date(y + '/' + m + '/0'); + return r.getDate(); +} + +// aaaa,aa,0形式 只要传入年和月 +function d (y, m){ + m = parseInt(m, 10) + 1; + let r = new Date(y, m, 0); + return r.getDate(); +} +``` + +## 2.6 获取上个月/下个月日期("yyyy-mm-dd") +传入参数的格式"yyyy-mm-dd",其实也可以是Date()对象,大家可以自行尝试。 +```js +// 上个月 date格式"yyyy-mm-dd" +function my_date (date){ + let arr = date.split('-'); + let y = arr[0] , m = arr[1], d = arr[2]; // 获取当前的年月日 + // ES6语法 let [y,m,d] = arr; + let day = new Date(y,m,0); + day = day.getDate(); // 获取当前月份的天数 + + let y2 = y, m2 = parseInt(m) - 1; + if(m2 == 0){ + y2 = parseInt(y2) -1; + m2 = 12; + } + + let d2 = d, day2 = new Date(y2, m2, 0); + day2 = day2.getDate(); + if(d2 > day2){ + d2 = day2; + } + if(m2 < 10){ + m2 = '0' + m2; + } + + return y2 + '-' + m2 + '-' + d2; +} +my_date('2018-1-20'); //"2017-12-20" +``` +下个月的计算方法也是相似: +```js +// 上个月 date格式"yyyy-mm-dd" +function my_date (date){ + let arr = date.split('-'); + let y = arr[0] , m = arr[1], d = arr[2]; // 获取当前的年月日 + let day = new Date(y,m,0); + day = day.getDate(); // 获取当前月份的天数 + + // 和计算上个月的区别 + let y2 = y, m2 = parseInt(m) + 1; + if(m2 == 13){ + y2 = parseInt(y2) + 1; + m2 = 1; + } + + let d2 = d, day2 = new Date(y2, m2, 0); + day2 = day2.getDate(); + if(d2 > day2){ + d2 = day2; + } + if(m2 < 10){ + m2 = '0' + m2; + } + + return y2 + '-' + m2 + '-' + d2; +} +my_date('2018-12-20'); // "2019-01-20" +``` + +# 参考资料 +[1.MDN 数字和日期](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/Numbers_and_dates) +[2. JS日期Date详解与实例扩展](https://www.cnblogs.com/moqiutao/p/4875946.html) + +--- +**本部分内容到这结束** + +|Author|王平安| +|---|---| +|E-mail|pingan8787@qq.com| +|博 客|www.pingan8787.com| +|微 信|pingan8787| +|每日文章推荐|https://github.com/pingan8787/Leo_Reading/issues| +|ES小册|js.pingan8787.com| \ No newline at end of file diff --git "a/Cute-JavaScript/Cute-JS/level1/8.\345\255\227\347\254\246\344\270\262.md" "b/Cute-JavaScript/Cute-JS/level1/8.\345\255\227\347\254\246\344\270\262.md" new file mode 100644 index 00000000..8548a7c1 --- /dev/null +++ "b/Cute-JavaScript/Cute-JS/level1/8.\345\255\227\347\254\246\344\270\262.md" @@ -0,0 +1,271 @@ +本文是 **重温基础** 系列文章的第八篇。 +今日感受:人在异乡,也不能忘记汤圆。 + +**本章节复习的是JS中的字符串,还有字符串的相关属性和方法。** + +前置知识: +JavaScript中的字符串的每个元素,在字符串中都占据一个位置,第一个元素的索引值为0,往后累加,另外创建字符串有2个方法: +* 1.字面量创建: +```js +let a = 'hello'; // "hello" +typeof a; // "string" +``` +* 2.字符串对象创建: +```js +let a = new String('hello'); //String {"hello"} +typeof a; // "object" +``` +实际开发中,除非必要,建议使用**字面量创建**,因为两种创建方法会有差异: +```js +let a1 = "1+1"; +let a2 = new String("1+1"); +eval(a1); // number 2 +eval(a2); // string "1+1" +``` +**String**有一个`length`属性,表示字符串中字符个数: +```js +let a = "hello"; +a.length; // 5 +``` + +# 1.String对象方法: +String对象的方法非常多,建议大家可以到 [W3school JavaScript String 对象](www.w3school.com.cn/jsref/jsref_obj_string.asp) 学习完整的内容。 + +|方法|描述| +|---|---| +|`charAt`, `charCodeAt`, `codePointAt`|返回字符串指定位置的字符或者字符编码。| +|`indexOf`, `lastIndexOf`|分别返回字符串中指定子串的位置或最后位置。| +|`startsWith`, `endsWith`,` includes`|返回字符串是否以指定字符串开始、结束或包含指定字符串。| +|`concat`|连接两个字符串并返回新的字符串。| +|`fromCharCode`, `fromCodePoint`|从指定的Unicode值序列构造一个字符串。这是一个String类方法,不是实例方法。| +|`split`|通过将字符串分离成一个个子串来把一个String对象分裂到一个字符串数组中。| +|`slice`|从一个字符串提取片段并作为新字符串返回。| +|`substring`, `substr`|分别通过指定起始和结束位置,起始位置和长度来返回字符串的指定子集。| +|`match`, `replace`,` search`|通过正则表达式来工作.| +|`toLowerCase`, `toUpperCase`|分别返回字符串的小写表示和大写表示。| +|`normalize`|按照指定的一种 Unicode 正规形式将当前字符串正规化。| +|`repeat`|将字符串内容重复指定次数后返回。| +|`trim`|去掉字符串开头和结尾的空白字符。| + +## 1.1 charAt +作用:查找字符串中**指定位置**的**内容**。 +`str.charAt(index)` index : 查找的字符的下标(大于等于0,若小于0则返回空字符串),若没传则表示1。 +```js +let a = "hello leo!" +a.charAt(); // "h" +a.charAt(1); // "e" +a.charAt(-1);// "" +``` + +## 1.2.indexOf和lastIndexOf +作用:查找**指定字符串**的**位置**。 +`indexOf`和`lastIndexOf`相同点: + 两者接收的参数一致,没有查到内容,返回`-1`。 +`indexOf`和`lastIndexOf`不同点: + 若查找到内容,则`indexOf`返回**第一次出现的索引**而`lastIndexOf`返回**最后一次出现的索引**。 + +`str.indexOf(value[, fromIndex])`接收2个参数: +* `value` : 需要查找的字符串内容; +* `fromIndex` : 可选,开始查找的位置,默认0; + +```js +let a = 'hello leo'; +let b = a.indexOf('lo'); // 3 +let c = a.indexOf('lo',4);// -1 +let e = a.lastIndexOf('l'); // 6 +``` +**一定要注意:** +1. 当`fromIndex > a.length`,则`fromIndex`被视为`a.length`。 +```js +let a = 'hello leo'; +let b = a.indexOf('lo',99);// -1 +``` +2. 当`fromIndex < 0`,则`fromIndex`被视为`0`。 +```js +let a = 'hello leo'; +let b = a.indexOf('lo',-1);// 3 +``` +3. `indexOf`和`lastIndexOf`区分大小写。 +```js +let a = 'hello leo'; +let b = a.indexOf('Lo'); // -1 +``` +## 1.3 concat +作用:连接字符串。 +`concat()`接收任意个参数作为连接的字符串,返回一个合并后的新字符串。 +```js +let a = 'hello'; +let b = ' leo '; +let c = '!' +a.concat(b,c); // "hello leo !" +``` + +## 1.4 split +作用:把字符串分割为字符串数组,并可以指定分隔符。 +`split(separator[,limit])`可以接收2个参数: +* `separator`:必需,字符串或者正则表达式,作为分割的内容; +* `limit`:可选,作为指定返回的数组的最大长度; + +若`separator`为`""`,则字符串会在每个字符之间分割; +```js +let a = 'How are you doing today?'; +a.split(); +// ["How are you doing today?"] +a.split(""); +// ["H", "o", "w", " ", "a", "r", "e", " ", "y", "o", "u", " ", "d", "o", "i", "n", "g", " ", "t", "o", "d", "a", "y", "?"] +a.split(" "); +// ["How", "are", "you", "doing", "today?"] +a.split("",4); +// ["H", "o", "w", " "] +a.split(" ",4); +// ["How", "are", "you", "doing"] +``` +使用其他分割符号: +```js +let a = "ni,hao,a!"; +a.split(","); // ["ni", "hao", "a!"] +``` + +## 1.5 slice +作用:提取并返回字符串的片断。 +`slice([start,end])` 可以接收2个参数: +* `start`:要提取的片段的起始下标,若小于零,则从字符串尾部开始算起,如-1表示字符串最后一个字符,-2为倒数第二个字符等等。 +* `end`:要提取的片段的结束下标,若没有传入,则表示从start到字符串结尾,若为负数则从字符串尾部开始算起。 +```js +let a = 'How are you doing today?'; +a.slice(); // "How are you doing today?" +a.slice(1); // "ow are you doing today?" +a.slice(-1); // '?' +a.slice(1,5); // "ow a" +a.slice(1,-1); // "ow are you doing today" +a.slice(-2,-1); // "y" +``` + +# 2.字符串拓展(ES6) +## 2.1 includes(),startsWith(),endsWith() +在我们判断字符串是否包含另一个字符串时,ES6之前,我们只有`typeof`方法,ES6之后我们又多了三种方法: +* **includes()**:返回**布尔值**,表示**是否找到参数字符串**。 +* **startsWith()**:返回**布尔值**,表示参数字符串是否在原字符串的**头部**。 +* **endsWith()**:返回**布尔值**,表示参数字符串是否在原字符串的**尾部**。 +```js +let a = 'hello leo'; +a.startsWith('leo'); // false +a.endsWith('o'); // true +a.includes('lo'); // true +``` +并且这三个方法都支持第二个参数,表示起始搜索的位置。 +```js +let a = 'hello leo'; +a.startsWith('leo',1); // false +a.endsWith('o',5); // true +a.includes('lo',6); // false +``` +`endsWith` 是针对前 `n` 个字符,而其他两个是针对从第`n`个位置直到结束。 + +## 2.2 repeat() +`repeat`方法返回一个新字符串,表示将原字符串重复`n`次。 +**基础用法**: +```js +'ab'.repeat(3); // 'ababab' +'ab'.repeat(0); // '' +``` +**特殊用法**: +* 参数为`小数`,则取整 +```js +'ab'.repeat(2.3); // 'abab' +``` +* 参数为`负数`或`Infinity`,则报错 +```js +'ab'.repeat(-1); // RangeError +'ab'.repeat(Infinity); // RangeError +``` +* 参数为`0到-1的小数`或`NaN`,则取0 +```js +'ab'.repeat(-0.5); // '' +'ab'.repeat(NaN); // '' +``` +* 参数为`字符串`,则转成`数字` +```js +'ab'.repeat('ab'); // '' +'ab'.repeat('3'); // 'ababab' +``` + +## 2.3 padStart(),padEnd() +用于将字符串**头部**或**尾部**补全长度,`padStart()`为**头部补全**,`padEnd()`为**尾部补全**。 +这两个方法接收**2个**参数,第一个指定**字符串最小长度**,第二个**用于补全的字符串**。 +**基础用法** : +```js +'x'.padStart(5, 'ab'); // 'ababx' +'x'.padEnd(5, 'ab'); // 'xabab' +``` +**特殊用法**: +* 原字符串长度,大于或等于指定最小长度,则返回原字符串。 +```js +'xyzabc'.padStart(5, 'ab'); // 'xyzabc' +``` +* 用来补全的字符串长度和原字符串长度之和,超过指定最小长度,则截去超出部分的补全字符串。 +```js +'ab'.padStart(5,'012345'); // "012ab" +``` +* 省略第二个参数,则用`空格`补全。 +```js +'x'.padStart(4); // ' x' +'x'.padEnd(4); // 'x ' +``` +## 2.4 模版字符串 +用于拼接字符串,ES6之前: +```js +let a = 'abc' + + 'def' + + 'ghi'; +``` +ES6之后: +```js +let a = ` + abc + def + ghi +` +``` +**拼接变量**: +在**反引号(\`)**中使用`${}`包裹变量或方法。 +```js +// ES6之前 +let a = 'abc' + v1 + 'def'; + +// ES6之后 +let a = `abc${v1}def` +``` + +# 3.字符串拓展(ES7) +用来为字符串填充特定字符串,并且都有两个参数:**字符串目标长度**和**填充字段**,第二个参数可选,默认空格。 +```js +'es8'.padStart(2); // 'es8' +'es8'.padStart(5); // ' es8' +'es8'.padStart(6, 'woof'); // 'wooes8' +'es8'.padStart(14, 'wow'); // 'wowwowwowwoes8' +'es8'.padStart(7, '0'); // '0000es8' + +'es8'.padEnd(2); // 'es8' +'es8'.padEnd(5); // 'es8 ' +'es8'.padEnd(6, 'woof'); // 'es8woo' +'es8'.padEnd(14, 'wow'); // 'es8wowwowwowwo' +'es8'.padEnd(7, '6'); // 'es86666' +``` +从上面结果来看,填充函数只有在字符长度小于目标长度时才有效,若字符长度已经等于或小于目标长度时,填充字符不会起作用,而且目标长度如果小于字符串本身长度时,字符串也不会做截断处理,只会原样输出。 + + +# 参考资料 +1. [MDN Text formatting](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/Text_formatting) +2. [W3school JavaScript String 对象](www.w3school.com.cn/jsref/jsref_obj_string.asp) + +--- +**本部分内容到这结束** + +|Author|王平安| +|---|---| +|E-mail|pingan8787@qq.com| +|博 客|www.pingan8787.com| +|微 信|pingan8787| +|每日文章推荐|https://github.com/pingan8787/Leo_Reading/issues| +|ES小册|js.pingan8787.com| \ No newline at end of file diff --git "a/Cute-JavaScript/Cute-JS/level1/9.\346\255\243\345\210\231\350\241\250\350\276\276\345\274\217.md" "b/Cute-JavaScript/Cute-JS/level1/9.\346\255\243\345\210\231\350\241\250\350\276\276\345\274\217.md" new file mode 100644 index 00000000..46bc69d5 --- /dev/null +++ "b/Cute-JavaScript/Cute-JS/level1/9.\346\255\243\345\210\231\350\241\250\350\276\276\345\274\217.md" @@ -0,0 +1,463 @@ +本文是 **重温基础** 系列文章的第九篇。 +今日感受:时间管理-角色管理法。 + +**本章节复习的是JS中的正则表达式,JS中用来匹配字符串的强大工具。** + +前置知识: +JS中的正则表达式是用来匹配字符串中指定字符组合的模式。 +另外需要记住:正则表达式也是对象。 + +# 1.创建正则表达式 +* 使用一个正则表达式字面量: +```js +let reg = /ab+c/; +let reg = /^[a-zA-z]/gi; +``` +* 使用`RegExp`对象: +`new RegExp(str[, attr])`接收2个参数,`str`是一个字符串,指定正则表达式匹配规则,`attr`可选,表示匹配模式,值有`g`(全局匹配),`i`(区分大小写的匹配)和`m`(多行匹配)。 +```js +let reg = new RegExp('ab+c'); +let reg = new RegExp(/^[a-zA-z]/, "gi"); +let reg = new RegExp("^[a-zA-z]", "gi"); +``` + +正则表达式的返回值,是一个新的`RegExp`对象,具有指定的模式和标志。 +**返回信息介绍**: +|对象|属性|描述|案例中对应的值| +|---|---|---|---| +|`reg`|`lastIndex`|下一个匹配的索引(仅在使用`g`参数时可用)|`0`| +|`reg`|`source`|模式文本。在正则表达式创建时更新,不执行。|`"ab+c"`| +|`reg`|`ignoreCase`|是否使用了 "i" 标记使正则匹配忽略大小写。|`true`| +|`reg`|`global`|是否使用了 "g" 标记来进行全局的匹配。|`true`| +|`reg`|`multiline`|是否使用了 "m" 标记使正则工作在多行模式。|`false`| + + + +**关于正则表达式的一些方法属性,文章后面介绍,这里先复习定义和使用**。 + +# 2.使用正则表达式 +JS的正则表达式可以被用于: +* `RegExp`对象的`exec`和`test`方法; +* `String`对象的`match`、`replace`、`search`和`split`方法。 + +## 2.1 RegExp对象方法 +|方法|介绍| +|---|---| +|`exec`|检索字符串中指定的值。返回找到的值,并确定其位置。| +|`test`|检索字符串中指定的值。返回 `true` 或 `false`。| + +### 2.1.1 `exec(str)` +`str`: 需要检索的字符串。 +若检索成功,返回匹配的数组,否则返回null。 +```js +let str = "hello leo!"; +let reg = new RegExp("leo", "g"); +let result = reg.exec(str); +// 也可以写成 +let result = /leo/g.exec("hello leo!"); + +/* +[ + 0: "leo", + groups: undefined, + index: 6, + input: "hello leo!", + length: 1 +] +*/ + +let result2 = /(leo \S)/g.exec("hello leo hi leo!"); +/* + 0: "leo hi" + 1: "leo hi" + 2: "hi" + groups: undefined + index: 6 + input: "hello leo hi leo!" + length: 3 +*/ +``` +**返回信息介绍**: +|对象|属性|描述|案例中对应的值| +|---|---|---|---| +|`result`|`[0]`|匹配到的所有字符串|`"leo"`| +|`result`|`input`|初始字符串。|`"hello leo!"`| +|`result`|`index`|在输入的字符串中匹配到的以0开始的索引值。|`6`| +|`result2`|`[1],...,[n]`|括号中的分组捕获|`[1]=> "leo hi";[2] => "hi"`| + +### 2.1.2 `test(str)` +`str`:需要检索的字符串。 +若匹配成功返回`true`否则`false`。 +等价于 `reg.exec(str) != null`。 +```js +let str = "hello leo!"; +let res = /^leo/.test(str); // fasle +``` +`^str`表示匹配以`str`开头的字符串,这些符号文章后面会介绍。 + +**注意:** + +若正则使用全局标志( `g` ),则` test() `会改变正则表达式的 `lastIndex` 属性,连续调用` test() `方法,后续的执行将会从 `lastIndex` 处开始匹配字符串,(`exec()` 同样改变正则本身的 `lastIndex`属性值): + +```js +var regex = /leo/g; + +// regex.lastIndex is at 0 +regex.test('leo'); // true + +// regex.lastIndex is now at 3 +regex.test('leo'); // false +``` + +## 2.2 String对象方法 +|方法|介绍| +|---|---| +|`search`|检索与正则表达式相匹配的值。| +|`match`|找到一个或多个正则表达式的匹配。| +|`replace`|替换与正则表达式匹配的子串。| +|`split`|把字符串分割为字符串数组。| + +### 2.2.1 `search` +`str.search(reg)`: +`str`:被检索的源字符串。 +`reg`:可以是需要检索的**字符串**,也可以是需要检索的`RegExp`对象,可以添加标志,如`i`。 + +若检索成功,返回**第一个**与`RegExp`对象匹配的字符串的起始位置,否则返回`-1`。 +```js +let str = "hello leo!"; +let res = str.search(/leo/g); // 6 + +let str1 = "hello leoleoleoleo!"; +let res1 = str.search(/leo/g); // 6 +let res2 = str.search(/pingan/g); // -1 +``` + +### 2.2.2 `match` +`str.match(reg)`: +`str`:被检索的源字符串。 +`reg`:可以是需要检索的**字符串**,也可以是需要检索的`RegExp`对象,可以添加标志,如`i`。 + +若检索成功,返回与`reg`匹配的所有结果的一个**数组**,数组的第一项是进行匹配完整的字符串,之后的项是用圆括号捕获的结果,否则返回`null`。 +```js +let str = 'For more information, see Chapter 3.4.5.1'; +let reg = /see (chapter \d+(\.\d)*)/i; +let result = str.match(reg); +/* +logs [ 'see Chapter 3.4.5.1', + 'Chapter 3.4.5.1', + '.1', + index: 22, + input: 'For more information, see Chapter 3.4.5.1' ] +*/ +``` +`'see Chapter 3.4.5.1'` 是整个匹配。 +`'Chapter 3.4.5.1'` 被`'(chapter \d+(\.\d)*)'`捕获。 +`'.1'` 是被`'(\.\d)'`捕获的最后一个值。 +`'index'` 属性`(22) `是整个匹配从零开始的索引。 +`'input'` 属性是被解析的原始字符串。 + +### 2.2.3 `replace` +将字符串中**指定字符**替换成**其他字符**,或替换成一个与正则表达式匹配的字符串。 +`str.replace(sub/reg,val)`: +* `str`: 源字符串 +* `sub`: 使用字符串来检索被替换的文本 +* `reg`: 使用RegExp对象来检索来检索被替换的文本 +* `val`: 指定替换文本 +返回替换成功之后的字符串,不改变源字符串内容。 +```js +let str = "hello leo!"; +let res = str.replace('leo','pingan');//"hello pingan!" +``` + +**val可以使用特殊变量名**: +|变量名|代表的值| +|---|---| +|`$$`|插入一个 "$"。| +|`$&`|插入匹配的子串。| +|`$`|插入当前匹配的子串左边的内容。| +|`$'`|插入当前匹配的子串右边的内容。| +|`$n`|假如第一个参数是 RegExp对象,并且 n 是个小于100的非负整数,那么插入第 n 个括号匹配的字符串。提示:索引是从1开始| +```js +let str = "hello leo!"; +let res = str.replace(/(\w+)\s* \s*(\w+)/, "$2:$1"); +// "leo:hello!" +``` + +### 2.2.4 `split` +将一个字符串,按照指定符号分割成一个字符串数组。 +`str.split(sub[, maxlength])`: +* `str`: 源字符串 +* `sub`: 指定的分割符号或正则 +* `maxlength`: 源字符串 +```js +let str = "hello leo!"; +let res = str.split(); //["hello leo!"] +let res = str.split(""); //["h", "e", "l", "l", "o", " ", "l", "e", "o", "!"] +let res = str.split(" ");//["hello", "leo!"] +let res = str.split(/\s+/);//["hello", "leo!"] + +let res = str.split("",3);//["h", "e", "l"] +``` + +## 2.3 使用情况 +* 当我们想要查找一个字符串中的一个匹配**是否找到**,可以用`test`或`search`方法。 +* 当我们想要得到匹配的**更多信息**,我们就需要用到`exec`或`match`方法。 + + +# 3.正则表达式符号介绍 +详细的每个符号的用法,可以查阅 [W3school JavaScript RegExp 对象](www.w3school.com.cn/jsref/jsref_obj_regexp.asp) +## 3.1 修饰符 +|修饰符|描述| +|---|---| +|`i`|执行对大小写不敏感的匹配。| +|`g`|执行全局匹配(查找所有匹配而非在找到第一个匹配后停止)。| +|`m`|执行多行匹配。| + +```js +let str = "hello leo!" +let res = /Leo/i.test(str); // i 不区分大小写 所以返回true +let res = /Leo/.test(str); // fasle +``` + +## 3.2 方括号 +用于查找指定返回之内的字符: +|表达式|描述| +|---|---| +|`[abc]`|查找方括号之间的任何字符。| +|`[^abc]`|查找任何不在方括号之间的字符。| +|`[0-9]`|查找任何从 0 至 9 的数字。| +|`[a-z]`|查找任何从小写 a 到小写 z 的字符。| +|`[A-Z]`|查找任何从大写 A 到大写 Z 的字符。| +|`[A-z]`|查找任何从大写 A 到小写 z 的字符。| +|`[adgk]`|查找给定集合内的任何字符。| +|`[^adgk]`|查找给定集合外的任何字符。| +|`(red)`|查找任何指定的选项。| + +```js +let str = "hello leo!"; +let res = str.match(/[a-m]/g); +//["h", "e", "l", "l", "l", "e"] +let res = str.match(/[^a-m]/g); +//["o", " ", "o", "!"] +``` + +## 3.3 元字符 +元字符是拥有特殊含义的字符: +|元字符|描述| +|---|---| +|`.`|查找单个字符,除了换行和行结束符。| +|`\w`|查找单词字符。| +|`\W`|查找非单词字符。| +|`\d`|查找数字。| +|`\D`|查找非数字字符。| +|`\s`|查找空白字符。| +|`\S`|查找非空白字符。| +|`\b`|匹配单词边界。| +|`\B`|匹配非单词边界。| +|`\0`|查找 NUL 字符。| +|`\n`|查找换行符。| +|`\f`|查找换页符。| +|`\r`|查找回车符。| +|`\t`|查找制表符。| +|`\v`|查找垂直制表符。| +|`\xxx`|查找以八进制数 xxx 规定的字符。| +|`\xdd`|查找以十六进制数 dd 规定的字符。| +|`\uxxxx`|查找以十六进制数 xxxx 规定的 Unicode 字符。| + +```js +let str = "hello leo hi pingan!"; +let res = str.match(/o\s/g); +//["o ", "o "] +let res = str.match(/\s/g); +//[" ", " ", " "] +``` + +## 3.4 量词 +|量词|描述| +|---|---| +|`n+`|匹配任何包含至少一个 n 的字符串。| +|`n*`|匹配任何包含零个或多个 n 的字符串。| +|`n?`|匹配任何包含零个或一个 n 的字符串。| +|`n{X}`|匹配包含 X 个 n 的序列的字符串。| +|`n{X,Y}`|匹配包含 X 至 Y 个 n 的序列的字符串。| +|`n{X,}`|匹配包含至少 X 个 n 的序列的字符串。| +|`n$`|匹配任何结尾为 n 的字符串。| +|`^n`|匹配任何开头为 n 的字符串。| +|`?=n`|匹配任何其后紧接指定字符串 n 的字符串。| +|`?!n`|匹配任何其后没有紧接指定字符串 n 的字符串。| +```js +let str = "hello leo!"; +let res = str.match(/^hello/g); +// ["hello"] +let res = str.match(/^pingan/g); +//null + +``` + +# 4. 正则表达式拓展(ES6) +## 4.1 介绍 +在ES5中有两种情况。 +* 参数是**字符串**,则第二个参数为正则表达式的修饰符。 +```js +let a = new RegExp('abc', 'i'); +// 等价于 +let a = /abx/i; +``` +* 参数是**正则表达式**,返回一个原表达式的拷贝,且不能有第二个参数,否则报错。 +```js +let a = new RegExp(/abc/i); +//等价于 +let a = /abx/i; + +let a = new RegExp(/abc/, 'i'); +// Uncaught TypeError +``` +ES6中使用: +第一个参数是正则对象,第二个是指定修饰符,如果第一个参数已经有修饰符,则会被第二个参数覆盖。 +```js +new RegExp(/abc/ig, 'i'); +``` + +## 4.2 字符串的正则方法 +常用的四种方法:`match()`、`replace()`、`search()`和`split()`。 + +## 4.3 u修饰符 +添加`u`修饰符,是为了处理大于`uFFFF`的Unicode字符,即正确处理四个字节的UTF-16编码。 +```js +/^\uD83D/u.test('\uD83D\uDC2A'); // false +/^\uD83D/.test('\uD83D\uDC2A'); // true +``` +由于ES5之前不支持四个字节UTF-16编码,会识别为两个字符,导致第二行输出`true`,加入`u`修饰符后ES6就会识别为一个字符,所以输出`false`。 + +**注意:** +加上`u`修饰符后,会改变下面正则表达式的行为: +* (1)点字符 +点字符(`.`)在正则中表示除了**换行符**以外的任意单个字符。对于码点大于`0xFFFF`的Unicode字符,点字符不能识别,必须加上`u`修饰符。 +```js +var a = "𠮷"; +/^.$/.test(a); // false +/^.$/u.test(a); // true +``` +* (2)Unicode字符表示法 +使用ES6新增的大括号表示Unicode字符时,必须在表达式添加`u`修饰符,才能识别大括号。 +```js +/\u{61}/.test('a'); // false +/\u{61}/u.test('a'); // true +/\u{20BB7}/u.test('𠮷'); // true +``` +* (3)量词 +使用`u`修饰符后,所有量词都会正确识别码点大于`0xFFFF`的 Unicode 字符。 +```js +/a{2}/.test('aa'); // true +/a{2}/u.test('aa'); // true +/𠮷{2}/.test('𠮷𠮷'); // false +/𠮷{2}/u.test('𠮷𠮷'); // true +``` +* (4)i修饰符 +不加`u`修饰符,就无法识别非规范的`K`字符。 +```js +/[a-z]/i.test('\u212A') // false +/[a-z]/iu.test('\u212A') // true +``` + +**检查是否设置`u`修饰符:** +使用`unicode`属性。 +```js +const a = /hello/; +const b = /hello/u; + +a.unicode // false +b.unicode // true +``` + +## 4.4 y修饰符 +`y`修饰符与`g`修饰符类似,也是全局匹配,后一次匹配都是从上一次匹配成功的下一个位置开始。区别在于,`g`修饰符**只要**剩余位置中存在匹配即可,而`y`修饰符是必须从**剩余第一个**开始。 +```js +var s = 'aaa_aa_a'; +var r1 = /a+/g; +var r2 = /a+/y; + +r1.exec(s) // ["aaa"] +r2.exec(s) // ["aaa"] + +r1.exec(s) // ["aa"] 剩余 '_aa_a' +r2.exec(s) // null +``` +**`lastIndex`属性**: +指定匹配的开始位置: +```js +const a = /a/y; +a.lastIndex = 2; // 从2号位置开始匹配 +a.exec('wahaha'); // null +a.lastIndex = 3; // 从3号位置开始匹配 +let c = a.exec('wahaha'); +c.index; // 3 +a.lastIndex; // 4 +``` +**返回多个匹配**: +一个`y`修饰符对`match`方法只能返回第一个匹配,与`g`修饰符搭配能返回所有匹配。 +```js +'a1a2a3'.match(/a\d/y); // ["a1"] +'a1a2a3'.match(/a\d/gy); // ["a1", "a2", "a3"] +``` +**检查是否使用`y`修饰符**: +使用`sticky`属性检查。 +```js +const a = /hello\d/y; +a.sticky; // true +``` + +## 4.5 flags属性 +`flags`属性返回所有正则表达式的修饰符。 +```js +/abc/ig.flags; // 'gi' +``` + +# 5. 正则表达式拓展(ES9) +在正则表达式中,点(`.`)可以表示任意单个字符,除了两个:用`u`修饰符解决**四个字节的UTF-16字符**,另一个是行终止符。 +**终止符**即表示一行的结束,如下四个字符属于“行终止符”: +* U+000A 换行符(\n) +* U+000D 回车符(\r) +* U+2028 行分隔符(line separator) +* U+2029 段分隔符(paragraph separator) +```js +/foo.bar/.test('foo\nbar') +// false +``` +上面代码中,因为`.`不匹配`\n`,所以正则表达式返回`false`。 +换个醒,可以匹配任意单个字符: +```js +/foo[^]bar/.test('foo\nbar') +// true +``` +ES9引入`s`修饰符,使得`.`可以匹配任意单个字符: +```js +/foo.bar/s.test('foo\nbar') // true +``` +这被称为`dotAll`模式,即点(`dot`)代表一切字符。所以,正则表达式还引入了一个`dotAll`属性,返回一个布尔值,表示该正则表达式是否处在`dotAll`模式。 +```js +const re = /foo.bar/s; +// 另一种写法 +// const re = new RegExp('foo.bar', 's'); + +re.test('foo\nbar') // true +re.dotAll // true +re.flags // 's' +``` +`/s`修饰符和多行修饰符`/m`不冲突,两者一起使用的情况下,`.`匹配所有字符,而`^`和`$`匹配每一行的行首和行尾。 + + +# 参考资料 +1.[MDN 正则表达式](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/Regular_Expressions) +2.[W3school JavaScript RegExp 对象](www.w3school.com.cn/jsref/jsref_obj_regexp.asp) + +--- +**本部分内容到这结束** + +|Author|王平安| +|---|---| +|E-mail|pingan8787@qq.com| +|博 客|www.pingan8787.com| +|微 信|pingan8787| +|每日文章推荐|https://github.com/pingan8787/Leo_Reading/issues| +|ES小册|js.pingan8787.com| \ No newline at end of file diff --git "a/Cute-JavaScript/Cute-JS/level2/1.JS\345\257\271\350\261\241\344\273\213\347\273\215.md" "b/Cute-JavaScript/Cute-JS/level2/1.JS\345\257\271\350\261\241\344\273\213\347\273\215.md" new file mode 100644 index 00000000..55d91f04 --- /dev/null +++ "b/Cute-JavaScript/Cute-JS/level2/1.JS\345\257\271\350\261\241\344\273\213\347\273\215.md" @@ -0,0 +1,231 @@ +从这篇文章开始,复习 [MDN 中级教程](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/Working_with_Objects) 的内容了,在**初级教程**中,我和大家分享了一些比较简单基础的知识点,并放在我的 【[Cute-JavaScript](http://js.pingan8787.com)】系列文章中。 + +**关于【[Cute-JavaScript](http://js.pingan8787.com)】**: +一本可爱的JavaScript小书,内容分成三大部分:**ES规范系列**、**JS基础系列**和**面试题系列**,目前我还在抓紧更新,喜欢的朋友可以 [github](https://github.com/pingan8787/Leo-JavaScript/tree/master/Cute-JavaScript) 上Star一下呀,下面放一张首页图: +![Cute-JavaScript](http://images.pingan8787.com/Cute-JavaScript.png) + + +--- +正文开始 + +本文是 **重温基础** 系列文章的第十五篇。 +今日感受:耐心为人处世。 + +**本章节复习的是JS中的关于对象还有原型等相关知识。** + +**前置知识**: +关于使用对象,可以先阅读一下我的[《12.使用对象》](https://github.com/pingan8787/Leo-JavaScript/blob/master/Cute-JavaScript/Cute-JS/level1/12.%E4%BD%BF%E7%94%A8%E5%AF%B9%E8%B1%A1.md)这篇文章。 + +下面也先重温一点基础。 + +# 1.概念 +对象是一个包含相关数据和方法的集合,由**变量**和**方法**组成,通常称为对象的**属性**和**方法**,比如: +```js +let me = { + name : 'pingan', + eat: function(){ + console.log('eat eat eat!!!'); + } +} +``` +其中,`name`就是`me`这个对象的一个属性,`eat`就是`me`这个对象的一个方法。 +访问对象的属性是这样的: +```js +me.name; // "pingan" +me.eat(); // "eat eat eat!!!" +``` +另外在访问对象属性时,有以下两种方式: +```js +let me = { + name : 'pingan', +} +// 点表示法 +me.name; // me.name => "pingan" + +// 括号表示法 +me["name"];// me.name => "pingan" +``` +括号表示法中,必须是字符串。 + +我们常常这么设置对象的属性: +```js +let me = { + name : 'pingan', +} +// 点表示法 +me.name = "leo"; // me => {name: "leo"} + +// 括号表示法 +me["name"] = "leo";// me => {name: "leo"} +``` + +# 2.简单的面向对象介绍 +这里简单介绍下JavaScrip的面向对象编程OOP。 +> 面向对象编程(Object Oriented Programming,OOP,面向对象程序设计)是一种计算机编程架构。OOP 的一条基本原则是计算机程序是由单个能够起到子程序作用的单元或对象组合而成。 —— 百度百科 + +![OOP](http://images.pingan8787.com/OOP1.png) + +我们这里定义一个简单的对象模型,比如我,我的身上可能有很多信息(姓名,年龄,身高等等),这时候我们可以将这些信息抽取出来,像这样: +```js +let leo = { + name : 'leo', + age : 26, + height : 180, +} +``` +这样我们就将我的信息抽取成一个JS的对象了,但是这样有个局限性,这样定义的话,一次只能定义一个人,如果这时候,有一百个人,那么我们就需要定义一百个这样的对象,显然这是不可取的。 +所以,这里就引入一个重要的函数——**构造函数**,**将相同的特性封装成通用的对象**,实现定义一次,其他地方都可以使用,这也是OOP的核心思想: +```js +// 传入 name 参数使得可以定义任何人作为对象 +function Person (name){ + let me = {}; + me.name = name; + me.doSomething = function(){ + console.log(me.name); + } + return me; +} +``` +创建一个函数“`Person`”,只要传入不同的`name`即可得到不同的对象: +```js +let leo = Person("leo"); +leo.name; // "leo" + +let pingan = Person("pingan"); +pingan.name; // "pingan" +``` +但是似乎`Person`对象的定义,显得不够精简,因为还要定义一个**空对象**来接收各个属性和方法,幸好JavaScrip在构造函数中提供一个便捷的方法,我们将代码改造下: +```js +function Person (name){ + this.name = name; + this.doSomething = function(){ + console.log(this.name); + } +} +``` +对于`this`关键词,即无论是该对象的哪个实例被构造函数创建,它的`name`属性都是参数`name`的值,`doSomething`方法中使用的也是参数`name`。简单理解就是用`this`指代了`Person`。 + +**构造函数通常首字母大写,用于区分普通函数。** + +接下来,通过`new`关键词,使用前面创建的构造函数(使用构造函数也叫实例化): +```js +let leo = new Person("leo"); +leo.name; // "leo" + +let pingan = new Person("pingan"); +pingan.name; // "pingan" +``` +然后一个简单的构造函数就写好了,通常在开发的时候,可能会有很多的参数: +```js +function Man(name, age, height, weight){ + this.name = name; + this.age = age + '岁'; + this.HeightAndWeight = { + height, + weight + }; + this.doSomething = function (){ + console.log(` + ${this.name}: height:${this.HeightAndWeight.height}m, + weight:${this.HeightAndWeight.weight}Kg!!` + ); + }; +} + +let leo = new Man("leo",25,1.8,68); +leo.doSomething(); // leo: height:1.8m, weight:68Kg!! +``` + +# 3.JS中的原型 +## 3.1理解原型 +这里需要先了解一下`Object`和`Function`,这两个函数都是JS的自带函数,`Object`继承自己,`Function`继承自己,并且`Object`和`Function`相互继承对方,即`Object`和`Function`既是函数也是对象。 +```js +console.log(Function instanceof Object); // true +console.log(Object instanceof Function); // true +``` +`Object` 是 `Function`的实例,而`Function`是它自己的实例。 +```js +console.log(Function.prototype); // ƒ () { [native code] } +console.log(Object.prototype); // Object +``` +另外,只有通过`Function`创建的函数都是函数对象,其他都是普通对象(通常由`Object`创建): +```js +function f1(){}; +typeof f1 //"function" + + +var o1 = new f1(); +typeof o1 //"object" + +var o2 = {}; +typeof o2 //"object" +``` + +**理论知识:** + +JavaScript 常被描述为一种基于原型的语言 (prototype-based language)——每个对象拥有一个**原型对象**,对象以其原型为模板、从原型继承方法和属性。 +原型对象也可能拥有原型,并从中继承方法和属性,一层一层、以此类推。这种关系常被称为**原型链** (prototype chain),它解释了为何一个对象会拥有定义在其他对象中的属性和方法。 +准确地说,这些属性和方法定义在Object的构造器函数(constructor functions)之上的`prototype`属性上,而非对象实例本身。 + +**个人理解:** + +* JS中所有的函数对象,都有一个`prototype`属性,对应当前对象的原型,但普通对象没有,而`prototype`属性下还有一个`constructor`,指向这个函数。 +```js +var p = {}; +p.prototype; // undefined +p instanceof Object; // true + +function f (){}; +f.prototype; // object {constructor: ƒ} +f === f.prototype.constructor; // true +Object === Object.prototype.constructor; // true +``` +* JS中所有的对象,都有一个`_proto_`属性,指向**实例对象的构造函数原型**(由于`_proto_`是个非标准属性,因此只有**ff**和**chrome**两个浏览器支持,标准方法是`Object.getPrototypeOf()`)。 +```js +var p = new Person(); +p._proto === Person.prototype; //true +``` + +**修改原型:** +经常我们也需要对原型进行修改: +```js +function Person (name){ + this.name = name; +} +// 添加一个getName方法 +Person.prototype.getName = function(){ + return "名字:" + this.name; +} +var p = new Person("leo"); +p.getName(); // "名字:leo" +``` + +这里也说明了原型进行继承,`p`继承`Person`原型中新增的函数属性`getName`。 + +## 3.2原型链 +**概念:** +javascript中,每个对象都会在内部生成一个 `proto` 属性,当我们访问一个对象属性时,如果这个对象不存在就回去 `proto` 指向的对象里面找,一层一层找下去,,知道找到为止,如果到了原型链顶端,还没找到,则返回`undefined`,这就是javascript原型链的概念。 + +![原型链:](http://images.pingan8787.com/%E5%8E%9F%E5%9E%8B%E9%93%BE.jpg) + +**总结:** +* 除了`Object`的`prototype`的原型是`null`,所有对象和原型都有自己的原型,对象的原型指向原型对象。 +* JS中所有的东西都是对象,所有的东西都由`Object`衍生而来, 即所有东西原型链的终点指向null。 + +**更加详细的介绍,可以查看下面参考文章。** + +--- +# 参考文章: +[1.MDN JavaScript 对象入门](https://developer.mozilla.org/zh-CN/docs/Learn/JavaScript/Objects) + +[2.基于js中的原型](https://www.jb51.net/article/123976.htm) + +**本部分内容到这结束** + +|Author|王平安| +|---|---| +|E-mail|pingan8787@qq.com| +|博 客|www.pingan8787.com| +|微 信|pingan8787| +|每日文章推荐|https://github.com/pingan8787/Leo_Reading/issues| +|JS小册|js.pingan8787.com| diff --git "a/Cute-JavaScript/Cute-JS/level2/2.JSON\345\257\271\350\261\241\344\273\213\347\273\215.md" "b/Cute-JavaScript/Cute-JS/level2/2.JSON\345\257\271\350\261\241\344\273\213\347\273\215.md" new file mode 100644 index 00000000..55259542 --- /dev/null +++ "b/Cute-JavaScript/Cute-JS/level2/2.JSON\345\257\271\350\261\241\344\273\213\347\273\215.md" @@ -0,0 +1,235 @@ +本文是 **重温基础** 系列文章的第十六篇。 +今日感受:静。 + +**本章节复习的是JS中的关于JSON对象相关知识。** + +**前置知识**: +`JSON`是一种按照`JavaScript`对象语法的数据格式。 + +# 1.概念 +概念有三点: +> `JSON`全称`JavaScript` 对象表示法(JavaScript Object Notation)。 +> `JSON` 是**存储**和**交换文本信息**的语法。类似 `XML`。 +> `JSON` 比 `XML` 更小、更快,更易解析。 +> ———— 摘自 [W3school JSON教程](http://www.w3school.com.cn/json/index.asp) + +`JSON` 使用 `JavaScript` 语法来描述数据对象,但是 `JSON` 仍然独立于语言和平台。`JSON` 解析器和 `JSON` 库支持许多不同的编程语言。 + +# 2.语法 +`JSON`在使用过程中可作为一个**对象**或者**字符串**存在,当作为**对象**时,用于获取`JSON`中的数据,而作为**字符串**时常用于网络数据传输。 + +`JSON`语法规则: +1. 数据在名称/值对中 +2. 数据由逗号分隔 +3. 花括号保存对象 +4. 方括号保存数组 + +通常,`JSON`数据书写格式是`名称/键值`: +```js +"name" : "pingan" +``` + +而`JSON`的值可以是 : +1. 数字(整数或浮点数) +2. 字符串(在双引号中) +3. 逻辑值(`true` 或 `false`) +4. 数组(在方括号中) +5. 对象(在花括号中) +6. `null` + +**JSON常常有三种类型:** +三种类型:简单之,对象和数组。 +必须注意:JSON字符串必须是**双引号**,单引号会语法错误。 + +## 2.1 简单值 +简单值可以是字符串: +```js +"hello leo!" +``` +也可以是数字,逻辑值: +```js +1 +``` + +## 2.2 对象类型 +内容放在`花括号`内,是多个键值对。 +JSON对象 与 js 对象的三个区别: +1. JSON对象 必须加双引号,而 js 对象属性名可以不加双引号。 +2. JSON对象 没有变量声明,而 js 对象有。 +2. JSON对象 没有分号,而 js 对象有。 +```js +// js 对象 +var obj = { + name : "pingan", + age : "25", +}; +// json 对象 +{ + "name" : "pingan", + "age" : "25", + "box" : [ + "a","b","c" + ] +} +``` + +## 2.3 数组类型 +内容放在`方括号`内。 +JSON数组也没有分号和变量,常常可以把JSON数组和对象结合使用,构成更复杂的数据集合。 +```js +[ + { + "name" : "leo", + "age" : 25, + "box" : ["a","b","c"] + }, + { + "name" : "pingan", + "age" : 25, + "box" : ["a","b","c"] + } +] +``` + +# 3. 使用 +JSON最常见的用法就是,从服务端获取JSON数据,再将JSON数据转成JavaScrip对象使用。 +JSON对象有两个方法: +* `JSON.stringify()`: 序列化操作,将`JavaScript对象`转换成`JSON字符串`。 +* `JSON.prase()`:反序列化操作,将`JSON字符串`解析成`JavaScript值`。 + +## 3.1 序列化操作 +序列化操作常常使用`JSON.stringify()`。 +简单例子: +```js +let leo = { + name : "leo", + age : 25, + box : ["a","b","c"] +} +let pingan = JSON.stringify(leo); +console.log(pingan); // "{"name":"leo","age":25,"box":["a","b","c"]}" +``` + +**注意**: +1. 默认情况下,`JSON.stringify()`输出的JSON字符串不包含任何空格字符或缩进,因此结果就像上面那样。 +2. 序列化`JavaScript对象`时,所有函数及原型成员都会被忽略,不体现在结果上。 +3. 值为`undefined`的任何属性都会被跳过。 + +因此,最终的值都是有效的JSON数据类型的实例属性。 + +## 3.2 反序列化操作 +序列化操作常常使用`JSON.parse()`。 +简单例子: +```js +let copyPingan = JSON.parse(pingan); +copyPingan; // {name: "leo", age: 25, box: Array(3)} +``` +如果传入`JSON.parse()`的字符串不是有效的JSON,则会抛出错误。 +**注意**: +虽然`pingan`和`copyPingan`属性相同,但两者独立,没有任何关系。 + +# 4.序列化选项 +`JSON.stringify()`除了要传入序列化对象作为参数,还可以接收其他两个参数,用来指定序列化JavaScript对象的方式: +1. 过滤器:可以是个**数组**,也可以是个**函数**。 +2. 选项:表示是否在`JSON字符串`中**保留缩进**。 + +单独或组合使用两者,可以更加全面深入的控制JSON的序列化。 + +## 4.1 过滤器 +若过滤器的参数是**数组**,则`JSON.stringify()`返回的结果将只包含数组中的属性: +```js +var leo = { + name : "leo", + age : 25, + box : ["a","b","c"] +} +var pingan = JSON.stringify(leo,["name","age"]); +console.log(pingan); // "{"name":"leo","age":25}" +``` + +若过滤器的参数是**函数**,则情况就不一样了,传入的函数需有两个参数(属性名和属性值): +```js +var leo = { + "name" : "leo", + "age" : 25, + "box" : ["a","b","c"] +} +var pingan = JSON.stringify(leo,function(key, value){ + switch(key){ + case "name": + return "我叫" + value + case "age": + return value + "岁" + default: + return value + } +}); +console.log(pingan); // "{"name":"我叫leo","age":"25岁","box":["a","b","c"]}" +``` +**注意**:使用`switch`的时候,必须指定`default`否则会返回`undefined`。 + +## 4.2 选项 +`JSON.stringify()`第三个参数是个选项,控制结果中的缩进和空白符。 +1. 若选项只有一个值,则表示**每个级别缩进的空格数**,最大值为`10`,超过`10`则只会是`10`。 +```js +var leo = { + "name" : "leo", + "age" : 25, + "box" : ["a","b","c"] +} +var pingan = JSON.stringify(leo, null, 4); +console.log(pingan); +/* +"{ + "name": "leo", + "age": 25, + "box": [ + "a", + "b", + "c" + ] +}" +*/ +``` + +# 5.解析选项 +`JSON.parse()`可以接收一个函数作为参数,对每个键值对调用,为了跟`JSON.stringify()`的`过滤函数`区别,这个函数成为`还原函数`。 +* 若还原函数返回`undefined`,则表示要从结果中删除对应的键。 +* 若还原函数返回其他值,则将该值插入结果中。 + +还原函数接收两个参数:属性名和属性值。 + +举例,在日期字符串转换为Date对象中,经常要用到还原函数: +```js +var leo = { + "name" : "leo", + "age" : 25, + "date" : new Date(1993, 9, 9) +} +var pingan = JSON.stringify(leo); +var copy = JSON.parse(pingan,function (key, value){ + // return key == "date" ? new Date(value) : value; + if(key == "date"){ + return new Date(value); + }else{ + return value; + } +}) +console.log(copy); +// "{"name":"leo","age":25,"date":"1993-10-08T16:00:00.000Z"}" +``` + + +# 参考文章: +1. [W3school JSON教程](http://www.w3school.com.cn/json/index.asp) +2. 《JavaScrip高级程序设计》 + +**本部分内容到这结束** + +|Author|王平安| +|---|---| +|E-mail|pingan8787@qq.com| +|博 客|www.pingan8787.com| +|微 信|pingan8787| +|每日文章推荐|https://github.com/pingan8787/Leo_Reading/issues| +|JS小册|js.pingan8787.com| \ No newline at end of file diff --git "a/Cute-JavaScript/Cute-JS/level2/3.WebAPI\344\273\213\347\273\215.md" "b/Cute-JavaScript/Cute-JS/level2/3.WebAPI\344\273\213\347\273\215.md" new file mode 100644 index 00000000..2f9263ee --- /dev/null +++ "b/Cute-JavaScript/Cute-JS/level2/3.WebAPI\344\273\213\347\273\215.md" @@ -0,0 +1,636 @@ +本文是 **重温基础** 系列文章的第十七篇。 +今日感受:挑战。 + +**本章节复习的是JS中的关于WebAPI相关知识,介绍的API比较多。** + +**前置知识:** +需要了解 [JS 对象](http://pingan8787.com/2019/01/14/131-%E3%80%90JavaScript%E3%80%91%E3%80%90%E9%87%8D%E6%B8%A9%E5%9F%BA%E7%A1%80%E3%80%9115-JS%E5%AF%B9%E8%B1%A1%E4%BB%8B%E7%BB%8D/) 的相关知识。 + + +# 1.WebAPI介绍 +> API:应用程序接口,是基于编程语言构建的结构,使开发人员更容易地创建复杂的功能。它们抽象了复杂的代码,并提供一些简单的接口规则直接使用。 + +客户端JavaScript提供很多可用的API,他们本身不是JavaScript语言的一部分,却建立在JavaScript语言核心的顶部,为使用JavaScript代码提供额外的超强能力。他们通常分为两类: +* **浏览器API**: +内置于Web浏览器,可以从浏览器和电脑周围环境获取数据,并用于复杂的操作。例如`Geolocation API`提供了一些简单的JavaScript结构来获取位置数据,这种API通常抽象很多复杂逻辑,我们只要调用API即可。 + +* **第三方API**: +缺省情况下不会内置于浏览器,而必须在Wen中某个地方获取代码和信息,例如`Twitter API`可以推送最新推文给我们,它提供一系列第三方API让我们获取Twitter的服务和信息。 + +## 1.1 API如何工作 +**那么,这些API是如何工作的?** +不同的JavaScript的API工作内容不同,但具有以下几个相同点: +1. **都是基于对象** +API通过使用一个或多个JavaScript对象与我们的代码交互,这些对象作为API使用的数据(包含在对象属性中)的容器,和API提供的功能(包含在对象的方法中)。 +如`Geolocation API`则是由以下几个对象组成: +* `Geolocation`, 包含三种控制地理数据检索的方法。 +* `Position`, 表示在给定的时间的相关设备的位置。它包含一个当前位置的`Coordinates` 对象。还包含了一个时间戳,这个时间戳表示获取到位置的时间。 +* `Coordinates`,包含有关设备位置的大量有用数据,包括经纬度,高度,运动速度和运动方向等。 + +通常这么使用: +```js +navigator.geolocation.getCurrentPosition(function(options){ + // ... do something +}); +``` + +2. **都有可识别的入口点** +即API的入口位置,如`Geolocation API`的入口在`Navigator.geolocation` 属性, 它返回浏览器的 `Geolocation` 对象,所有有用的地理定位方法都可用。 +在**文档对象模型(DOM)**的API入口位置更简单,常常都挂载在`Document`对象上,或者`HTML`元素上: +```js +let a = document.createElement('div');// 创建一个新的div元素 +let b = document.querySelector('div');// 获取一个已经存在的div元素 +a.textContent = "hi leo!"; // 给a元素设置文本 +b.appendChild(a); // 将a元素添加到b元素下 +//
    hi leo!
    +``` +详细的后面介绍。 + +3. **都使用事件来处理状态的变化** +有些特定的API会包含一些事件,当事件触发,我们便可以运行函数的处理属性,做一些不同的操作。例如`XMLHttpRequest`对象的实例,有很多的事件可以调用。 +```js +// 简单写 +const url = "https://www.baidu.com"; +const req = new XMLHttpRequest(); +req.open('GET', url); +req.responseType = "json"; +req.send(); +req.onload = function(){ + // ... do something +} +``` + +4. **都会在适当位置提供额外的安全机制** +主要还是处于安全考虑,提供额外的安全机制,比如常见同源策略,一些特定页面只能在https协议的网页工作等。 + +接下来看看这些API都能实现什么作用。 + +# 2.文档API +我们在开发web页面和应用时,就会经常操作页面文档的结构,这里就会大量用到`document`对象,控制HTML和样式信息的**文档对象模型(DOM)**。 +下面贴一张web页面视图中的浏览器的主要部分(来源MDN): +![浏览器](https://mdn.mozillademos.org/files/14557/document-window-navigator.png) + +**介绍**: +* `window`:是载入浏览器的标签,JavaScript中用`Window`对象表示,可以实现很多操作:获取窗口大小,操作载入窗口的文档,存储文档数据等。 +* `navigator`:浏览器存在web上的状态和表示(即用户代理),JavaScript中用`Navigator`对象表示,用于获取对象的一些信息,如:摄像头的地理信息,用户偏爱的语言等。 +* `document`:是实际载入窗口的页面,JavaScript中用`Document`对象表示,可以实现很多文档操作:获取DOM元素,修改文本内容,设置新样式等。 + +## 2.1 文档对象模型(DOM) +在浏览器标签中当前载入的文档用文档对象模型(Document Object Model,简称DOM)表示。 +这是一个由浏览器生成的“树结构”,使得编程语言可以很容易访问HTML结构,操作等。 + +下面是基本的DOM操作: +```js +let a = document.createElement('a');// 1. 创建一个新的a元素 +let b = document.querySelector('a');// 2. 获取一个已经存在的a元素 +a.textContent = "进入pingan8787博客"; // 3. 给a元素设置文本 +a.href = "http://www.pingan8787.com"; // 4. 给a元素添加跳转地址 +a.style.color = "red"; // 5. 给a元素添加样式 +a.style.backgroundColor = "black"; +a.setAttribute("my_class"); // 6. 给a元素添加class类名 +b.appendChild(a); // 7. 将a元素添加到b元素下 +``` + +这么一个简单的例子,实际可以看出DOM提供的方法很多,下面分类介绍下这些方法: +1. **DOM HTML操作** +* **改变HTML输出流**: +通过`document.write()`直接修改HTML输出流写内容。 +```js +document.write("hello leo"); +``` +**注意:**千万不要在文档加载之后使用,否则覆盖文档。 + +* **改变HTML内容**: +通过`innerHTML`属性来改变HTML元素的内容: +```js +document.getElementById(id).innerHTML = new HTML; +// 通过ID获取 (getElementById) +// 通过name属性(getElementsByName) +// 通过标签名 (getElementsByTagName) +// 通过类名 (getElementsByClassName) +// 获取html的方法 (document.documentElement) +// 获取body的方法 (document.body) +// 通过选择器获取一个元素(querySelector) +// 通过选择器获取一组元素(querySelectorAll) +``` +例子: +```js +document.getElementById("id1").innerHTML = "hello leo!" +``` + +* **改变HTML属性**: +通过`attribute`属性来改变元素的属性: +```js +document.getElementById(id).attribute = new value +``` +例子: +```html + +``` +```js +document.getElementById("id1").src = "leo.png"; +``` +详细的文档可以查看[HTML DOM Document 对象](http://www.w3school.com.cn/jsref/dom_obj_document.asp) + +2. **DOM CSS操作** +* **改变HTML样式** +通过`style.styleName`属性修改元素的样式: +```js +document.getElementById(id).style.property = new style; +``` +例子: +```js +document.getElementById("id1").style.color = "red"; +document.getElementById("id1").style.backgroundColor = "green"; +``` +注意:`style`后面的样式的属性名只能使用**命名小驼峰**。 + +3. **DOM 事件操作** +如要对元素做点击事件的绑定,可以在HTML的`onclick`事件属性上绑定方法: +```html +
    +
    +``` +另外可以绑定的事件类型还有很多: +```js +
    +
    +
    +
    +``` +详细的文档可以查看[HTML DOM Event 对象](http://www.w3school.com.cn/jsref/dom_obj_event.asp) + +4. **DOM 节点操作** +* **创建新的HTML元素** +需要先创建一个新元素节点,然后在使用`appendChild`方法,向一个已存在的元素上追加这个新元素: +```html +
    +``` +```js +// 创建新元素节点 +let a = document.createElement('p'); +let b = document.createTextNode('新的文本'); +a.appendChild(b); + +// 获取已存在元素 +let div = document.getElementById('id1'); + +// 追加新元素 +div.appendChild(a); +``` + +* **删除已有的HTML元素** +如果需要删除HTML元素,就必须先获得该元素的父元素,再使用`removeChild`方法删除: +```html +
    +

    hello leo

    +
    +``` +```js +let a = document.getElementById("id1"); +let b = document.getElementById("id2"); +a.removeChild(b); +``` + +## 2.2 Window对象 +Window 对象表示浏览器中打开的窗口。 +Window 对象涉及到的属性和方法非常多,具体可以查看 [Window 对象](http://www.w3school.com.cn/jsref/dom_obj_window.asp) 比较详细。 +这里一个简单的例子,实现获取视窗(显示文档的内部窗口)的宽高,并设置给指定的Div元素,并且当我们调整窗口时候,也会及时改变Div的宽高。 +```js +let div = document.querySelector('div'); +// 获取宽高 +let w = window.innerWidth; +let h = window.innerHeight; +// 设置宽高 +div.style.width = w + 'px'; +div.style.heitgh = h + 'px'; +// 设置及时调整div宽高 +window.onresize = function(){ + w = window.innerWidth; + h = window.innerHeight; + div.style.width = w + 'px'; + div.style.heitgh = h + 'px'; +} +``` + +# 3.获取服务器数据API +这一部分主要复习`Ajax`(全称:Asynchronous JavaScript and XML)。 +`Ajax`主要要解决的问题是:避免重复加载刷新整个页面,因为有时候我们只是小部分数据更新,这时候我们这要刷新这部分的数据,而不是整个页面。 +`Ajax`不是新的编程语言,而是一种使用现有标准的新方法。 + +下面看一下**Ajax**模型图: +![Ajax模型](https://mdn.mozillademos.org/files/6477/moderne-web-site-architechture@2x.png) + +Ajax模型包括使用Web API作为代理来更智能地请求数据,而不仅仅是让浏览器重新加载整个页面。让我们来思考这个意义: +比如:我们逛淘宝,搜索或打开商品页面时,顶部导航来和页眉页脚等,都保持不变。 +这样做的好处: +* 页面更新更快,减少等待刷新,体验更好,响应更快。 +* 下载的数据更少,减少带宽的浪费。 + +另外为了提高加载速度,网站有时候也会将首次请求和数据存储在用户计算机中,下次再请求就直接先使用本地版本,如果内容有更新才会去服务器获取新数据,原理图: +![存储和获取新数据](https://mdn.mozillademos.org/files/6479/web-app-architecture@2x.png) + + +## 3.1 Ajax基础使用 +`XMLHttpRequest` 是 `AJAX` 的基础。 +所有现代浏览器均支持 `XMLHttpRequest` 对象(IE5 和 IE6 使用 `ActiveXObject`)。 + +创建 `XMLHttpRequest` 对象的语法: +```js +variable=new XMLHttpRequest(); +// IE5 和 IE使用 ActiveX 对象: +variable=new ActiveXObject("Microsoft.XMLHTTP"); +``` +为了兼容所有浏览器(包括IE5 IE6),我们就必须检查浏览器是否支持`XMLHttpRequest`对象,若支持则创建`XMLHttpRequest`对象,否则创建`ActiveXObject`对象。 + +```js +let my_http; +if (window.XMLHttpRequest){ + // 非 ES5 ES6 + my_http=new XMLHttpRequest(); + if (xmlhttp.readyState==4 && xmlhttp.status==200){ + // 网络请求成功后,执行的操作 + } + } +}else{ + // ES5 ES6 + my_http=new ActiveXObject("Microsoft.XMLHTTP"); +} +``` + +## 3.2 Ajax发送请求 +向服务器发送请求时,我们可以使用 `XMLHttpRequest` 对象的 `open()` 和 `send()` 方法: +```js +my_http.open(method,url,async); +my_http.send(string); +``` +参数解释: +`open(method,url,async)`方法接收三个参数: +* `method`:请求的类型;`GET` 或 `POST`。 +* `url`:文件在服务器上的位置。 +* `async`:`true`(异步)或 `false`(同步)。 + +`send(string)`方法接收一个参数: +* `string`:仅用于 `POST` 请求。 + +例子: +```js +// GET +my_http.open("GET","http://www.baidu.com",true); +my_http.send(); + +// POST +my_http.open("POST","http://www.baidu.com",true); +my_http.setRequestHeader( + "Content-type", + "application/x-www-form-urlencoded" +); // 添加 HTTP 头 +my_http.send("name=leo"); +``` +如果POST请求的话需要设置HTTP头: +```js +setRequestHeader(header,value) +``` +参数解释: +`setRequestHeader(header,value)`方法接收两个参数: +* `header`: 规定头的名称。 +* `value`: 规定头的值。 + +## 3.3 Ajax服务器响应 +接收服务器响应,我们可以使用 `XMLHttpRequest` 对象的 `responseText` 或 `responseXML` 属性。 +* `responseText`:获得`字符串形式`的响应数据。 +* `responseXML`:获得 `XML` 形式的响应数据。 + +* **responseText属性** +当服务端的响应不是XML,使用`responseText`属性。 +若是字符串的响应就可以直接使用。 +```js +document.getElementById("id1").inndeHTML = my_http.responseText; +``` + +## 3.4 Ajax readyState +`readyState`保存着`XMLHttpRequest`对象的状态。 +我们发送Ajax请求的整个过程中,`XMLHttpRequest`对象的`readyState`会改变,当`readyState`改变的时候会触发`onreadystatechange`事件。 + +`XMLHttpRequest`对象的三个重要属性: + +|属性|描述| +|---|---| +|onreadystatechange|存储函数(或函数名),每当 `readyState` 属性改变时,就会调用该函数。| +|readyState|存有 XMLHttpRequest 的状态。从 0 到 4 发生变化。0(请求未初始化),1(服务器连接已建立),2(请求已接收),3(请求处理中),4(请求已完成,且响应已就绪)| +|status|200("OK"),404(未找到页面)| + +通常我们会在`readyState`等于4且`status`等于200时候表示响应已就绪,可以执行业务操作: +```js +if (xmlhttp.readyState==4 && xmlhttp.status==200){ + // 网络请求成功后,执行的操作 +} +``` + +# 4.图形绘制API +这里主要介绍的是HTML5新增的`canvas`元素,JavaScript为`canvas`添加了很多API,进一步增强`canvas`。 +`canvas` 元素可以通过JavaScript脚本,在网页上绘制图形。 +比如,它可以被用来绘制图形,制作图片集合,甚至用来实现动画效果。你可以(也应该)在元素标签内写入可提供替代的的代码内容,这些内容将会在在旧的、不支持`canvas`元素的浏览器或是禁用了JavaScript的浏览器内渲染并展现。 + +详细完整的课程推荐: +1. [MDN Canvas教程](https://developer.mozilla.org/zh-CN/docs/Web/API/Canvas_API/Tutorial) +2. [W3C HTML5 Canvas](http://www.w3school.com.cn/html5/html_5_canvas.asp) + +下面复习一下`canvas`的一个使用流程: + +## 4.1 简单绘制 + +1. **创建画布,规定元素id、宽度和高度** + +要在网页中创建 2D 或者 3D 场景,必须在 HTML 文件中插入一个`canvas`元素,以界定网页中的绘图区域。 +```html + + 卧槽你的浏览器竟然不支持 canvas! + +``` +在`canvas`标签内,写一些浏览器不支持画布功能时候的提示内容。 +画布默认尺寸为 300 × 150 像素。 + + +2. **获取画布上下文,并完成设置** + +我们需要获得一个对绘画区域的特殊的引用(称为“**上下文**”)用来在画布上绘图。可通过 `HTMLCanvasElement.getContext()` 方法获得基础的绘画功能,需要提供一个字符串参数来表示所需上下文的类型。 +下面创建一个2d画布: +```js +let ctx = canvas.getContext("2d"); +``` +`ctx` 变量包含一个 `CanvasRenderingContext2D 对象`,画布上所有绘画操作都会涉及到这个对象。 +下面将画布背景涂黑: +```js +ctx.fillStyle = 'rgb(0, 0, 0)'; +ctx.fillRect(0, 0, 200, 100); +``` + +## 4.2 绘制基础 +首先需要知道一些2D画布的知识,画布左上角的坐标是(0, 0),横坐标(x)轴向右延伸,纵坐标(y)轴向下延伸。 +![2D画布](https://mdn.mozillademos.org/files/224/Canvas_default_grid.png) + +1. 绘制实线 +```js +ctx.lineTo(100,50); // 绘制目标坐标 +ctx.lineWidth = 5; // 线条宽度 +ctx.stroke(); // 绘制实线 +``` + +2. 绘制矩形 +`fillRect(x, y, width, height)` 绘制一个填充的矩形 +`strokeRect(x, y, width, height)` 绘制一个矩形的边框 +`clearRect(x, y, width, height)` 清除指定矩形区域,让清除部分完全透明。 +x与y指定了在canvas画布上所绘制的矩形的左上角(相对于原点)的坐标。 +width和height设置矩形的尺寸。 +```js +// 实心矩形 +ctx.fillStyle = 'rgb(255, 0, 0)'; // 矩形颜色 +ctx.fillRect(50, 50, 100, 150); // 矩形起止坐标 + +// 空心矩形 +ctx.strokeStyle = 'rgb(255, 255, 255)';// 矩形边框颜色 +ctx.strokeRect(25, 25, 175, 200);// 矩形起止坐标 +``` + +3. 绘制路径 +可以通过路径来实现复杂的图形,下面整理了一些常用到的方法: +* `beginPath()`:在画笔当前所在位置开始绘制一条路径。在新的画布中,画笔起始位置为 (0, 0)。 +* `moveTo()`:将画笔移动至另一个坐标点,不记录、不留痕迹,只将画笔“跳”至新位置。 +* `fill()`:通过为当前所绘制路径的区域填充颜色来绘制一个新的填充形状。 +* `stroke()`:通过为当前绘制路径的区域描边,来绘制一个只有边框的形状。 +* 路径也可和矩形一样使用 `lineWidth` 和 `fillStyle` / `strokeStyle` 等功能。 + +```js +ctx.fillStyle = 'rgb(255, 0, 0)'; // 设置填充颜色 +ctx.beginPath(); // 开始 +ctx.moveTo(50, 50); // 移动到结束点的坐标 +// .. 可以在到处移动 +ctx.fill(); // 绘制 +``` + +4. 绘制圆形 +`arc(x, y, radius, startAngle, endAngle, anticlockwise)` +画一个以(x,y)为圆心的以radius为半径的圆弧(圆),从startAngle开始到endAngle结束,按照anticlockwise给定的方向(默认为顺时针)来生成。 +`arcTo(x1, y1, x2, y2, radius)` +根据给定的控制点和半径画一段圆弧,再以直线连接两个控制点。 + +`arc`方法,该方法有六个参数: +* `x,y`为绘制圆弧所在圆上的圆心坐标。 +* `radius`为半径。 +* `startAngle`以及`endAngle`参数用弧度定义了开始以及结束的弧度。 +* `anticlockwise`是布尔值,为true时,是逆时针方向,否则顺时针方向。 +这些都是以x轴为基准。 + +**注意**:arc()函数中表示角的单位是弧度,不是角度。角度与弧度的js表达式: +弧度=(Math.PI/180)*角度。 +```js +cxt.fillStyle="#FF0000"; +cxt.beginPath(); +cxt.arc(70,18,15,0,Math.PI*2,true); +cxt.closePath(); +cxt.fill(); +``` + +5. 绘制文本 +`fillText(text, x, y [, maxWidth])` +在指定的(x,y)位置填充指定的文本,绘制的最大宽度是可选的. +`strokeText(text, x, y [, maxWidth])` +在指定的(x,y)位置绘制文本边框,绘制的最大宽度是可选的. + +```js +ctx.strokeStyle = 'white'; +ctx.lineWidth = 1; +ctx.font = '36px arial'; +ctx.strokeText('hello leo !', 50, 50); + +ctx.fillStyle = 'red'; +ctx.font = '48px georgia'; +ctx.fillText('hello leo !', 50, 150); +``` +另外还有一些有样式的文本,可以使用这些属性: +* `font = value` +当前绘制的文本字体.默认是 10px sans-serif。 +* `textAlign = value` +文本对齐选项. 可选的值包括:`start`, `end`, `left`, `right` or `center`. 默认值是 start。 +* `textBaseline = value` +基线对齐选项. 可选的值包括:`top`, `hanging`, `middle`, `alphabetic`, `ideographic`, `bottom`。默认值是 `alphabetic`。 +* `direction = value` +文本方向。可能的值包括:`ltr`, `rtl`, `inherit`。默认值是 `inherit`。 +```js +ctx.font = "48px serif"; +ctx.textBaseline = "hanging"; +ctx.strokeText("Hello leo ", 0, 100); +``` + +6. 绘制图片 +* `drawImage(image, x, y)` +其中 `image` 是 `image` 或者 `canvas` 对象,`x` 和 `y` 是其在目标 `canvas` 里的起始坐标。 +```js +let img = new Image(); +img.src = "leo.png"; +img.onload = function(){ + ctx.drawImage(img, 50, 50); +}; +``` +`drawImage`方法还有更多参数: +```js +ctx.drawImage(image, 20, 20, 185, 175, 50, 50, 185, 175); +``` +* 第一个参数不变,为图片引用。 +* 参数 2、3 表示裁切部分左上顶点的坐标,参考原点为原图片本身左上角的坐标。原图片在该坐标左、上的部分均不会绘制出来。 +* 参数 4、5 表示裁切部分的长、宽。 +* 参数 6、7 表示裁切部分左上顶点在画布中的位置坐标,参考原点为画布左上顶点。 +* 参数 8、9 表示裁切部分在画布中绘制的长、宽。本例中绘制时与裁切时面积相同,你也可以定制绘制的尺寸。 + + +详细完整的课程推荐: +1. [MDN Canvas教程](https://developer.mozilla.org/zh-CN/docs/Web/API/Canvas_API/Tutorial) +2. [W3C HTML5 Canvas](http://www.w3school.com.cn/html5/html_5_canvas.asp) + + +# 5.视频音频API +HTML5提供了用于文档中嵌入富媒体的元素`