es6学习(2019/7/17-7/24)

数值的扩展 date:(2019.07.17)

二进制和八进制表示法

Number.isFinite(), Number.isNaN()

  Number.isFinite(15); // true
  Number.isFinite(0.8); // true
  Number.isFinite(NaN); // false
  Number.isFinite(Infinity); // false
  Number.isFinite(-Infinity); // false
  Number.isFinite('foo'); // false
  Number.isFinite('15'); // false
  Number.isFinite(true); // false
  Number.isNaN(NaN) // true
  Number.isNaN(15) // false
  Number.isNaN('15') // false
  Number.isNaN(true) // false
  Number.isNaN(9/NaN) // true
  Number.isNaN('true' / 0) // true
  Number.isNaN('true' / 'true') // true
  • 它们与传统的全局方法isFinite()和isNaN()的区别在于,传统方法先调用Number()将非数值的值转为数值,再进行判断,而这两个新方法只对数值有效,Number.isFinite()对于非数值一律返回false, Number.isNaN()只有对于NaN才返回true,非NaN一律返回false。

Number.parseInt(), Number.parseFloat()

  • ES6 将全局方法parseInt()和parseFloat(),移植到Number对象上面,行为完全保持不变,这样做的目的,是逐步减少全局性方法,使得语言逐步模块化
Number.parseInt === parseInt // true
Number.parseFloat === parseFloat // true

Number.isInteger()

  • 用来判断一个数值是否为整数
  • JavaScript 内部,整数和浮点数采用的是同样的储存方法,所以 25 和 25.0 被视为同一个值
  • 如果参数不是数值,Number.isInteger返回false

Number.EPSILON

  • 它表示 1 与大于 1 的最小浮点数之间的差
  function withinErrorMargin (left, right) {
    return Math.abs(left - right) < Number.EPSILON * Math.pow(2, 2);
  }
  0.1 + 0.2 === 0.3 // false
  withinErrorMargin(0.1 + 0.2, 0.3) // true
  上面的代码为浮点数运算,部署了一个误差检查函数

Math 对象的扩展

ES6 在 Math 对象上新增了 17 个与数学相关的方法。所有这些方法都是静态方法,只能在 Math 对象上调用

  • Math.trunc方法用于去除一个数的小数部分,返回整数部分,对于非数值,Math.trunc内部使用Number方法将其先转为数值,对于空值和无法截取整数的值,返回NaN
Math.trunc = Math.trunc || function(x) {
  return x < 0 ? Math.ceil(x) : Math.floor(x);
};
  • Math.sign方法用来判断一个数到底是正数、负数、还是零。对于非数值,会先将其转换为数值
它会返回五种值。
参数为正数,返回+1;
参数为负数,返回-1;
参数为 0,返回0;
参数为-0,返回-0;
其他值,返回NaN
Math.sign('')  // 0
Math.sign(true)  // +1
Math.sign(false)  // 0
Math.sign(null)  // 0
Math.sign('9')  // +1
Math.sign('foo')  // NaN
Math.sign()  // NaN
Math.sign(undefined) 
  • Math.cbrt方法用于计算一个数的立方根
  • Math.hypot方法返回所有参数的平方和的平方根

指数运算符

  • ES2016 新增了一个指数运算符(**)
2 ** 2 // 4
2 ** 3 // 8
  • 指数运算符可以与等号结合,形成一个新的赋值运算符(**=)

数组的扩展

1.扩展运算符

含义

  • 扩展运算符(spread)是三个点(...)。它好比 rest 参数的逆运算,将一个数组转为用逗号分隔的参数序列。
    console.log(...[1, 2, 3])
    // 1 2 3
  • 该运算符主要用于函数调用
    function add(x, y) {
      return x + y;
    }
    const numbers = [4, 38];
    add(...numbers) // 42
  • 扩展运算符后面还可以放置表达式
  • 如果扩展运算符后面是一个空数组,则不产生任何效果
  • 只有函数调用时,扩展运算符才可以放在圆括号中,否则会报错
  • 替代函数的 apply 方法

扩展运算符的应用

  • 复制数组,只是复制了指向底层数据结构的指针
  • 合并数组,但是浅拷贝
  • 与解构赋值结合
  // ES5
  a = list[0], rest = list.slice(1)
  // ES6
  [a, ...rest] = list
    • 如果将扩展运算符用于数组赋值,只能放在参数的最后一位,否则会报错
  • 扩展运算符还可以将字符串转为真正的数组
  • 实现了 Iterator 接口的对象,都可以用扩展运算符转为真正的数组
  • Map 和 Set 结构,Generator 函数,都可以使用扩展运算符

2.Array.from()

  • Array.from方法用于将两类对象转为真正的数组:类似数组的对象(array-like object)和可遍历(iterable)的对象(包括 ES6 新增的数据结构 Set 和 Map)
  • 只要是部署了 Iterator 接口的数据结构,Array.from都能将其转为数组
  • 如果参数是一个真正的数组,Array.from会返回一个一模一样的新数组
  • Array.from还可以接受第二个参数,作用类似于数组的map方法,用来对每个元素进行处理,将处理后的值放入返回的数组

3.Array.of()

  • Array.of方法用于将一组值,转换为数组
  • 这个方法的主要目的,是弥补数组构造函数Array()的不足。因为参数个数的不同,会导致Array()的行为有差异。
Array() // []
Array(3) // [, , ,]
Array(3, 11, 8) // [3, 11, 8]
  • Array.of基本上可以用来替代Array()或new Array(),并且不存在由于参数不同而导致的重载

4.数组实例的copyWithin()

  • 在当前数组内部,将指定位置的成员复制到其他位置(会覆盖原有成员),然后返回当前数组,会修改当前数组
  • Array.prototype.copyWithin(target, start = 0, end = this.length)
  • 它接受三个参数:
    • target(必需):从该位置开始替换数据。如果为负值,表示倒数。
    • start(可选):从该位置开始读取数据,默认为 0。如果为负值,表示从末尾开始计算。
    • end(可选):到该位置前停止读取数据,默认等于数组长度。如果为负值,表示从末尾开始计算。
    • 这三个参数都应该是数值,如果不是,会自动转为数值。
    [1, 2, 3, 4, 5].copyWithin(0, 3)
    // [4, 5, 3, 4, 5]

5.数组实例的find()和findIndex()

  • 数组实例的find方法,用于找出第一个符合条件的数组成员。它的参数是一个回调函数,所有数组成员依次执行该回调函数,直到找出第一个返回值为true的成员,然后返回该成员。如果没有符合条件的成员,则返回undefined。
[1, 4, -5, 10].find((n) => n < 0)
// -5
  • find方法的回调函数可以接受三个参数,依次为当前的值、当前的位置和原数组
  • 数组实例的findIndex方法的用法与find方法非常类似,返回第一个符合条件的数组成员的位置,如果所有成员都不符合条件,则返回-1
  • find函数接收了第二个参数person对象,回调函数中的this对象指向person对象
  • 这两个方法都可以发现NaN,弥补了数组的indexOf方法的不足

6.数组实例的fill()

  • fill方法使用给定值,填充一个数组
  • fill方法用于空数组的初始化非常方便。数组中已有的元素,会被全部抹去。
  • fill方法还可以接受第二个和第三个参数,用于指定填充的起始位置和结束位置

7.数组实例的entries(),keys()和values()

  • 用于遍历数组
  • 可以用for...of循环进行遍历,唯一的区别是keys()是对键名的遍历、values()是对键值的遍历,entries()是对键值对的遍历

8.数组实例的includes()

  • 方法返回一个布尔值,表示某个数组是否包含给定的值,该方法的第二个参数表示搜索的起始位置

9.数组实例的flat(),flatMap()

  • 数组的成员有时还是数组,Array.prototype.flat()用于将嵌套的数组“拉平”,变成一维的数组,该方法返回一个新数组,对原数据没有影响
  • 默认只会“拉平”一层,如果想要“拉平”多层的嵌套数组,可以将flat()方法的参数写成一个整数,表示想要拉平的层数,默认为1
    [1, 2, [3, [4, 5]]].flat()
    // [1, 2, 3, [4, 5]]
    [1, 2, [3, [4, 5]]].flat(2)
    // [1, 2, 3, 4, 5]
    
  • 如果不管有多少层嵌套,都要转成一维数组,可以用Infinity关键字作为参数
  • 如果原数组有空位,flat()方法会跳过空位。
    [1, 2, , 4, 5].flat()
    // [1, 2, 4, 5]
    
  • 遍历函数返回的是一个双层的数组,但是默认只能展开一层,因此flatMap()返回的还是一个嵌套数组

10.数组的空位

  • 空位不是undefined,一个位置的值等于undefined,依然是有值的,空位是没有任何值,in运算符可以说明这一点
  • Array.from方法会将数组的空位,转为undefined
  • 扩展运算符(...)也会将空位转为undefined
  • copyWithin()会连空位一起拷贝
  • fill()会将空位视为正常的数组位置
  • for...of循环也会遍历空位
  • entries()、keys()、values()、find()和findIndex()会将空位处理成undefined

TDD作业

Number API

<p align="center" style="display:inline-block">
<p>Number->Number.isInteger()</p>
<img src="./img/TDD-Number.isInteger.png" height="150">
</p>
<p align="center" style="display:inline-block">
<p> Number->Number.isNaN()</p>
<img src="./img/TDD-Number.isNaN.png" height="150">
</p>

Array API

<p align="center" style="display:inline-block">
<p> Array->Array.from()</p>
<img src="./img/TDD-Array.from.png" height="150">
</p>
<p align="center" style="display:inline-block">
<p> Array->Array.from()</p>
<img src="./img/TDD-Array.from.png" height="150">
</p>
<p align="center" style="display:inline-block">
<p> Array->Array.of()</p>
<img src="./img/TDD-Array.of.png" height="150">
</p>
<p align="center" style="display:inline-block">
<p> Array->Array.fill()</p>
<img src="./img/TDD-Array.fill.png" height="150">
</p>
<p align="center" style="display:inline-block">
<p> Array->Array.find()</p>
<img src="./img/TDD-Array.find.png" height="150">
</p>
<p align="center" style="display:inline-block">
<p> Array->Array.findIndex()</p>
<img src="./img/TDD-Array.findIndex.png" height="150">
</p>
<p align="center" style="display:inline-block">
<p> Array->Array.entries()</p>
<img src="./img/TDD-Array.entries.png" height="150">
</p>
<p align="center" style="display:inline-block">
<p> Array->Array.keys()</p>
<img src="./img/TDD-Array.keys.png" height="150">
</p>
<p align="center" style="display:inline-block">
<p> Array->Array.values()</p>
<img src="./img/TDD-Array.values.png" height="150">
</p>
图区:


TDD-Number.isInteger.png

TDD-Number.isNaN.png

TDD-Array.values.png
TDD-Array.entries.png
TDD-Array.fill.png
TDD-Array.find.png
TDD-Array.findIndex.png
TDD-Array.from.png
TDD-Array.keys.png
TDD-Array.of.png

date:(2019.07.18)

let const

  1. for循环的计数器很适合let命令
  2. let声明不存在变量提升
  3. let const声明变量,暂存性死区(TDZ)
  • 暂时性死区的本质就是,只要一进入当前作用域,所要使用的变量就已经存在了,但是不可获取,只有等到声明变量的那一行代码出现,才可以获取和使用该变量。
  1. 不允许出现重复声明

块级作用域

  1. 为什么需要块级作用域?
  • 内层变量可能会覆盖外层变量
  • 用来计数的循环变量泄露为全局变量
  1. es6的块级作用域
  • ES6 允许块级作用域的任意嵌套
  • 使得立即执行函数不那么必要了
  1. 块级作用域与函数声明
  • ES5 规定,函数只能在顶层作用域和函数作用域之中声明,不能在块级作用域声明,浏览器没有遵守这个规定
  • ES6 的块级作用域必须有大括号

const命令

  1. 基本用法
  • const声明一个只读的常量。一旦声明,常量的值就不能改变
  • const一旦声明变量,就必须立即初始化
  • 只在声明所在的块级作用域内有效
  • 声明的常量也是不提升,同样存在暂时性死区,只能在声明的位置后面使用
  • 不可重复声明
  1. 本质
  • const实际上保证的,并不是变量的值不得改动,而是变量指向的那个内存地址所保存的数据不得改动
  • 可以用const什么{}和[]且赋值不会报错
  • 想将对象冻结,应该使用Object.freeze方法
  1. ES6 声明变量的六种方法
  • var、function、let、const、import、class,ES6 一共有 6 种声明变量的方法。

顶层对象的属性

  • 顶层对象,在浏览器环境指的是window对象,在 Node 指的是global对象。ES5 之中,顶层对象的属性与全局变量是等价的
  • var命令和function命令声明的全局变量,依旧是顶层对象的属性
  • let命令、const命令、class命令声明的全局变量,不属于顶层对象的属性。也就是说,从 ES6 开始,全局变量将逐步与顶层对象的属性脱钩

globalThis对象

  • JavaScript 语言存在一个顶层对象,它提供全局环境,但是,顶层对象在各种实现里面是不统一的
  • 浏览器里面,顶层对象是window,但 Node 和 Web Worker 没有window
  • 浏览器和 Web Worker 里面,self也指向顶层对象,但是 Node 没有self
  • Node 里面,顶层对象是global,但其他环境都不支持

字符串的扩展

1.字符Unicode表示法

  • ES6 加强了对 Unicode 的支持,允许采用\uxxxx形式表示一个字符,其中xxxx表示字符的 Unicode 码点。
  "\u0061"
  // "a"
  • JavaScript 共有 6 种方法可以表示一个字符。
  '\z' === 'z'  // true
  '\172' === 'z' // true
  '\x7A' === 'z' // true
  '\u007A' === 'z' // true
  '\u{7A}' === 'z' // true

2.字符串的遍历器接口

  • ES6 为字符串添加了遍历器接口,使得字符串可以被for...of循环遍历
  for (let codePoint of 'foo') {
    console.log(codePoint)
  }
  // "f"
  // "o"
  // "o"

3.直接输入U+2028和U+2029

  • javaScript 字符串允许直接输入字符,以及输入字符的转义形式

    '中' === '\u4e2d' // true

  • javaScript 规定有5个字符,不能在字符串里面直接使用,只能使用转义形式。

U+005C:反斜杠(reverse solidus)
U+000D:回车(carriage return)
U+2028:行分隔符(line separator)
U+2029:段分隔符(paragraph separator)
U+000A:换行符(line feed)

4.JSON.stringify()的改造

5.模板字符串

  • 模板字符串(template string)是增强版的字符串,用反引号(`)标识,它可以当作普通字符串使用,也可以用来定义多行字符串,或者在字符串中嵌入变量
// 普通字符串
`In JavaScript '\n' is a line-feed.`

// 多行字符串
`In JavaScript this is
 not legal.`

console.log(`string text line 1
string text line 2`);

// 字符串中嵌入变量
let name = "Bob", time = "today";
`Hello ${name}, how are you ${time}?`
  • 模板字符串表示多行字符串,所有的空格和缩进都会被保留在输出之中,使用trim方法消除它
  • 模板字符串中嵌入变量,需要将变量名写在${}之中,大括号内部可以放入任意的 JavaScript 表达式,可以进行运算,以及引用对象属性
let x = 1;
let y = 2;
`${x} + ${y} = ${x + y}`
// "1 + 2 = 3"
`${x} + ${y * 2} = ${x + y * 2}`
// "1 + 4 = 5"
let obj = {x: 1, y: 2};
`${obj.x + obj.y}`
// "3"
  • 模板字符串之中还能调用函数
function fn() {
  return "Hello World";
}

`foo ${fn()} bar`
  • 模板字符串甚至还能嵌套

6.实例:模板编译

  • 使用<%...%>放置 JavaScript 代码,使用<%= ... %>输出 JavaScript 表达式

7.标签模板

  • 模板字符串的功能,不仅仅是上面这些。它可以紧跟在一个函数名后面,该函数将被调用来处理这个模板字符串。这被称为“标签模板”功能
  • 标签模板其实不是模板,而是函数调用的一种特殊形式。“标签”指的就是函数,紧跟在后面的模板字符串就是它的参数
  • 如果模板字符里面有变量,就不是简单的调用了,而是会将模板字符串先处理成多个参数,再调用函数
    let a = 5;
    let b = 10;

    tag`Hello ${ a + b } world ${ a * b }`;
    // 等同于
    tag(['Hello ', ' world ', ''], 15, 50);

8.模板字符串的限制

函数的扩展

1.函数参数的默认值

基本用法

  • ES6 允许为函数的参数设置默认值,即直接写在参数定义的后面
  • 参数变量是默认声明的,所以不能用let或const再次声明
  function foo(x = 5) {
    let x = 1; // error
    const x = 2; // error
  }
  • 使用参数默认值时,函数不能有同名参数
  • 一个容易忽略的地方是,参数默认值不是传值的,而是每次都重新计算默认值表达式的值。也就是说,参数默认值是惰性求值的

与解构赋值默认值结合使用

  • 参数默认值可以与解构赋值的默认值,结合起来使用
  function foo({x, y = 5} = {}) {
    console.log(x, y);
  }

  foo() // undefined 5
  • 上面代码指定,如果没有提供参数,函数foo的参数默认为一个空对象

参数默认值的位置

  • 通常情况下,定义了默认值的参数,应该是函数的尾参数。因为这样比较容易看出来,到底省略了哪些参数,如果非尾部的参数设置默认值,实际上这个参数是没法省略的
  • 如果传入undefined,将触发该参数等于默认值,null则没有这个效果

函数的 length 属性

  • 指定了默认值以后,函数的length属性,将返回没有指定默认值的参数个数。也就是说,指定了默认值后,length属性将失真

    (function(...args) {}).length // 0

  • 如果设置了默认值的参数不是尾参数,那么length属性也不再计入后面的参数了

    (function(a, b=1, c){}).length
    1
    (function(a, b, c=1){}).length
    2
    (function(a, b, c){}).length
    3

作用域

  • 一旦设置了参数的默认值,函数进行声明初始化时,参数会形成一个单独的作用域
  • 如果参数的默认值是一个函数,该函数的作用域也遵守这个规则

应用

  • 利用参数默认值,可以指定某一个参数不得省略,如果省略就抛出一个错误
function throwIfMissing() {
  throw new Error('Missing parameter');
}
function foo(mustBeProvided = throwIfMissing()) {
  return mustBeProvided;
}
foo()
// Error: Missing parameter
  • 参数的默认值不是在定义时执行,而是在运行时执行

rest参数

  • ES6 引入 rest 参数(形式为...变量名),用于获取函数的多余参数,这样就不需要使用arguments对象了。rest 参数搭配的变量是一个数组,该变量将多余的参数放入数组中
  • rest 参数之后不能再有其他参数
  • 函数的length属性,不包括 rest 参数

严格模式

  • ES2016 规定只要函数参数使用了默认值、解构赋值、或者扩展运算符,那么函数内部就不能显式设定为严格模式
  • 两种方法可以规避这种限制。第一种是设定全局性的严格模式
  • 第二种是把函数包在一个无参数的立即执行函数里面

name 属性

  • 函数的name属性,返回该函数的函数名
  • 变量f等于一个匿名函数,ES5 和 ES6 的name属性返回的值不一样
  • 如果将一个具名函数赋值给一个变量,则 ES5 和 ES6 的name属性都返回这个具名函数原本的名字。
  • Function构造函数返回的函数实例,name属性的值为anonymous
  • bind返回的函数,name属性值会加上bound前缀

箭头函数

  • =>

  • 如果箭头函数不需要参数或需要多个参数,就使用一个圆括号代表参数部分

  • 如果箭头函数的代码块部分多于一条语句,就要使用大括号将它们括起来,并且使用return语句返回

  • 如果箭头函数直接返回一个对象,必须在对象外面加上括号,否则会报错

  • 如果箭头函数只有一行语句,且不需要返回值,可以采用下面的写法,就不用写大括号了

    let fn = () => void doesNotReturn();

  • 箭头函数可以与变量解构结合使用

  • 箭头函数的一个用处是简化回调函数

使用注意点

  • 箭头函数有几个使用注意点。

    • 函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象。
    • 不可以当作构造函数,也就是说,不可以使用new命令,否则会抛出一个错误。
    • 不可以使用arguments对象,该对象在函数体内不存在。如果要用,可以用 rest 参数代替。
    • 不可以使用yield命令,因此箭头函数不能用作 Generator 函数。
  • 上面四点中,第一点尤其值得注意。this对象的指向是可变的,但是在箭头函数中,它是固定的

  • 除了this,以下三个变量在箭头函数之中也是不存在的,指向外层函数的对应变量:arguments、super、new.target

  • 由于箭头函数没有自己的this,所以当然也就不能用call()、apply()、bind()这些方法去改变this的指向

不适用场合

  • 第一个场合是定义对象的方法,且该方法内部包括this
  • 第二个场合是需要动态this的时候,也不应使用箭头函数

嵌套的箭头函数

尾调用优化

  • 尾调用(Tail Call)是函数式编程的一个重要概念,指某个函数的最后一步是调用另一个函数
  • 以下三种情况,都不属于尾调用
    // 情况一
    function f(x){
      let y = g(x);
      return y;
    }
    // 情况二
    function f(x){
      return g(x) + 1;
    }
    // 情况三
    function f(x){
      g(x);
    }
  • 尾调用不一定出现在函数尾部,只要是最后一步操作即可

尾递归

函数参数的尾逗号

  • ES2017 允许函数的最后一个参数有尾逗号

Unicode

<p align="center" style="display:inline-block">
<p> Unicode->In strings</p>
<img src="./img/TDD-unicode.png" height="150">
</p>

Template strings

<p align="center" style="display:inline-block">
<p> Template strings->basics</p>
<img src="./img/TDD-templateString-basics.png" height="150">
</p>
<p align="center" style="display:inline-block">
<p> Template strings->Multiline</p>
<img src="./img/TDD-template-multiline.png" height="150">
</p>
<p align="center" style="display:inline-block">
<p> Template strings->Tagged template strings</p>
<img src="./img/TDD-templateTagged.png" height="150">
</p>
<p align="center" style="display:inline-block">
<p> Template strings->raw</p>
<img src="./img/TDD-template-String.raw.png" height="150">
</p>

Symbol

<p align="center" style="display:inline-block">
<p> Symbol->Symbol.for</p>
<img src="./img/TDD-symbol.for.png" height="150">
</p>
<p align="center" style="display:inline-block">
<p> Symbol->Symbol.keyfor</p>
<img src="./img/TDD-symbol.keyfor.png" height="150">
</p>
<p align="center" style="display:inline-block">
<p> Symbol->Basics</p>
<img src="./img/TDD-symbol-basics.png" height="150">
</p>

Object API

<p align="center" style="display:inline-block">
<p> Symbol->Object.is()</p>
<img src="./img/TDD-Object.is.png" height="150">
</p>

Modules

<p align="center" style="display:inline-block">
<p> Modules->import</p>
<img src="./img/TDD-module-import.png" height="150">
</p>

Default Parameters

<p align="center" style="display:inline-block">
<p> Default Parameters->Basics</p>
<img src="./img/TDD-DefaultParameters.png" height="150">
</p>

Spread operator

<p align="center" style="display:inline-block">
<p> Spread operator->With Array</p>
<img src="./img/TDD-spreaddestructuring.png" height="150">
</p>
<p align="center" style="display:inline-block">
<p> Spread operator->With String</p>
<img src="./img/TDD-spread-string.png" height="150">
</p>

Rest operator

<p align="center" style="display:inline-block">
<p> Rest operator->As parameter
</p>
<img src="./img/TDD-rest-parameter.png" height="150">
</p>
<p align="center" style="display:inline-block">
<p> Rest operator->With destructuring</p>
<img src="./img/TDD-spread-string.png" height="150">
</p>
图片区域:


TDD-unicode.png
TDD-arrowbasis.png
TDD-arrowFunction-binding.png
TDD-blockscope-const.png
TDD-blockscope-let.png
TDD-DefaultParameters.png
TDD-module-import.png
TDD-Object.is.png
TDD-rest-parameter.png
TDD-spreaddestructuring.png
TDD-spread-string.png
TDD-symbol.for.png
TDD-symbol.keyfor.png
TDD-symbol-basics.png
TDD-template-multiline.png
TDD-template-String.raw.png
TDD-templateString-basics.png
TDD-templateTagged.png

date(07.22)

变量的解构赋值

数组的解构赋值

  1. 基本用法
  • 按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构
  • 本质上,这种写法属于“模式匹配”,只要等号两边的模式相同,左边的变量就会被赋予对应的值
  • 解构不成功,变量的值就等于undefined
  • 不完全解构,即等号左边的模式,只匹配一部分的等号右边的数组。这种情况下,解构依然可以成功
  • 如果等号的右边不是数组(或者严格地说,不是可遍历的结构),那么将会报错
let [foo] = 1;
let [foo] = false;
let [foo] = NaN;
let [foo] = undefined;
let [foo] = null;
let [foo] = {};
  • 事实上,只要某种数据结构具有 Iterator 接口,都可以采用数组形式的解构赋值
  1. 默认值
  • ES6 内部使用严格相等运算符(===),判断一个位置是否有值。所以,只有当一个数组成员严格等于undefined,默认值才会生效
  • 默认值是一个表达式,那么这个表达式是惰性求值
  • 默认值可以引用解构赋值的其他变量,但该变量必须已经声明

对象的解构赋值

  1. 简介
  • 对象的解构与数组有一个重要的不同。数组的元素是按次序排列的,变量的取值由它的位置决定;而对象的属性没有次序,变量必须与属性同名,才能取到正确的值
  • 如果解构失败,变量的值等于undefined
  • 将Math对象的对数、正弦、余弦三个方法,赋值到对应的变量上,使用起来就会方便很多
let { log, sin, cos } = Math;
  • 如果变量名与属性名不一致,必须写成下面这样
let obj = { first: 'hello', last: 'world' };
let { first: f, last: l } = obj;
f // 'hello'
l // 'world'
  • 对象的解构赋值可以取到继承的属性
  • 解构也可以用于嵌套结构的对象
let obj = {
  p: [
    'Hello',
    { y: 'World' }
  ]
};
let { p: [x, { y }] } = obj;
x // "Hello"
y // "World"
  • 注意,这时p是模式,不是变量,因此不会被赋值。如果p也要作为变量赋值,可以写成下面这样。
let obj = {
  p: [
    'Hello',
    { y: 'World' }
  ]
};
let { p, p: [x, { y }] } = obj;
x // "Hello"
y // "World"
p // ["Hello", {y: "World"}]
  1. 默认值
  • 对象的解构也可以指定默认值
  • 默认值生效的条件是,对象的属性值严格等于undefined
  1. 注意点
  • 如果要将一个已经声明的变量用于解构赋值,必须非常小心
  • 解构赋值允许等号左边的模式之中,不放置任何变量名。因此,可以写出非常古怪的赋值表达式
  • 由于数组本质是特殊的对象,因此可以对数组进行对象属性的解构
let arr = [1, 2, 3];
let {0 : first, [arr.length - 1] : last} = arr;//方括号这种写法,属于“属性名表达式”
first // 1
last // 3
  1. 字符串的解构赋值
  • 字符串也可以解构赋值。这是因为此时,字符串被转换成了一个类似数组的对象
const [a, b, c, d, e] = 'hello';
a // "h"
b // "e"
c // "l"
d // "l"
e // "o"
  • 类似数组的对象都有一个length属性,因此还可以对这个属性解构赋值
let {length : len} = 'hello';
len // 5
  1. 数值和布尔值的解构赋值
  • 解构赋值的规则是,只要等号右边的值不是对象或数组,就先将其转为对象。由于undefined和null无法转为对象,所以对它们进行解构赋值,都会报错
  1. 函数参数的解构赋值
  • undefined就会触发函数参数的默认值
  1. 圆括号问题
  • es6规则:只要有可能导致解构的歧义,就不得使用圆括号;阮一峰建议只要有可能,就不要在模式中放置圆括号
  • 不能使用圆括号的情况
    1. 变量声明语句
// 全部报错
let [(a)] = [1];
let {x: (c)} = {};
let ({x: c}) = {};
let {(x: c)} = {};
let {(x): c} = {};

let { o: ({ p: p }) } = { o: { p: 2 } };
    1. 函数参数
// 报错
function f([(z)]) { return z; }
// 报错
function f([z,(x)]) { return x; }
    1. 赋值语句的模式
  • 可以使用圆括号的情况
    • 赋值语句的非模式部分,可以使用圆括号
  1. 用途
    1. 交换变量的值
    1. 从函数返回多个值
    1. 函数参数的定义
    1. JSON数据的提取
    1. 函数参数的默认值
    1. 遍历map数组
      • 任何部署了 Iterator 接口的对象,都可以用for...of循环遍历。Map 结构原生支持 Iterator 接口,配合变量的解构赋值,获取键名和键值就非常方便。
              const map = new Map();
              map.set('first', 'hello');
              map.set('second', 'world');

              for (let [key, value] of map) {
                console.log(key + " is " + value);
              }
              // first is hello
              // second is world
      • 如果只想获取键名,或者只想获取键值,可以写成下面这样。
              // 获取键名
              for (let [key] of map) {
                // ...
              }
              // 获取键值
              for (let [,value] of map) {
                // ...
              }
    1. 输入模块的指定方法

class表达式
与函数一样,类也可以使用表达式的形式定义

const myClass = new Me{
    getClassName(){
        return Me.name;
    }
}
set
var s = new set();
s.add()

不会添加重复值
Set函数可以接受一个数组(或者具有 iterable 接口的其他数据结构)作为参数,用来初始化
set可以可以去重(包括NaN)
两个对象总是不相等的
Set结构实例有一下属性:
Set.prototype.constructor:构造函数,默认就是Set函数。
Set.prototype.size:返回Set实例的成员总数。
Set实例分为两大类,操作方法和遍历方法

Set.prototype.add(value):添加某个值,返回 Set 结构本身。
Set.prototype.delete(value):删除某个值,返回一个布尔值,表示删除是否成功。
Set.prototype.has(value):返回一个布尔值,表示该值是否为Set的成员。
Set.prototype.clear():清除所有成员,没有返回值。

Array.from方法可以将 Set 结构转为数组
数组去重:Array.from(new Set(array))

Set.prototype.keys():返回键名的遍历器
Set.prototype.values():返回键值的遍历器
Set.prototype.entries():返回键值对的遍历器
Set.prototype.forEach():使用回调函数遍历每个成员

for of;map;filter;
因此使用 Set 可以很容易地实现并集(Union)、交集(Intersect)和差集(Difference)。

let a = new Set([1, 2, 3]);
let b = new Set([4, 3, 2]);

// 并集

let union = new Set([...a, ...b]);
// Set {1, 2, 3, 4}

// 交集

let intersect = new Set([...a].filter(x => b.has(x)));
// set {2, 3}

// 差集

let difference = new Set([...a].filter(x => !b.has(x)));
// Set {1}

map

ES6 提供了 Map 数据结构。它类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键

Set和Map都可以用来生成新的 Map

size 属性

Map.prototype.set(key, value)
Map.prototype.get(key)
Map.prototype.has(key)
Map.prototype.delete(key)
Map.prototype.clear()
Map.prototype.keys():返回键名的遍历器。
Map.prototype.values():返回键值的遍历器。
Map.prototype.entries():返回所有成员的遍历器。
Map.prototype.forEach():遍历 Map 的所有成员

iterator

1. Iterator(遍历器)的概念

  1. 遍历器(Iterator)就是这样一种机制。它是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署 Iterator 接口,就可以完成遍历操作(即依次处理该数据结构的所有成员)
  2. Iterator 的作用有三个:一是为各种数据结构,提供一个统一的、简便的访问接口;二是使得数据结构的成员能够按某种次序排列;三是 ES6 创造了一种新的遍历命令for...of循环,Iterator 接口主要供for...of消费
  3. Iterator 的遍历过程是这样的
    (1)创建一个指针对象,指向当前数据结构的起始位置。也就是说,遍历器对象本质上,就是一个指针对象。
    (2)第一次调用指针对象的next方法,可以将指针指向数据结构的第一个成员。
    (3)第二次调用指针对象的next方法,指针就指向数据结构的第二个成员。
    (4)不断调用指针对象的next方法,直到它指向数据结构的结束位置。
    每一次调用next方法,都会返回数据结构的当前成员的信息。具体来说,就是返回一个包含value和done两个属性的对象。其中,value属性是当前成员的值,done属性是一个布尔值,表示遍历是否结束。

2. 默认 Iterator 接口

3. 调用 Iterator 接口的场合

4. 字符串的 Iterator 接口

5. Iterator 接口与 Generator 函数

6. 遍历器对象的 return(),throw()

7. for...of 循环

promise

1.Promise的含义

  1. Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大
  2. 所谓Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。Promise 提供统一的 API,各种异步操作都可以用同样的方法进行处理
  3. Promise对象有以下两个特点。
    • 对象的状态不受外界影响。Promise对象代表一个异步操作,有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。这也是Promise这个名字的由来,它的英语意思就是“承诺”,表示其他手段无法改变。
    • 一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise对象的状态改变,只有两种可能:从pending变为fulfilled和从pending变为rejected。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果,这时就称为 resolved(已定型)。如果改变已经发生了,你再对Promise对象添加回调函数,也会立即得到这个结果。这与事件(Event)完全不同,事件的特点是,如果你错过了它,再去监听,是得不到结果的。
    • Promise也有一些缺点。首先,无法取消Promise,一旦新建它就会立即执行,无法中途取消。其次,如果不设置回调函数,Promise内部抛出的错误,不会反应到外部。第三,当处于pending状态时,无法得知目前进展到哪一个阶段

2.基本用法

const promise = new Promise(function(resolve, reject) {
  // ... some code

  if (/* 异步操作成功 */){
    resolve(value);
  } else {
    reject(error);
  }
});
  1. Promise构造函数接受一个函数作为参数,该函数的两个参数分别是resolve和reject。它们是两个函数,由 JavaScript 引擎提供,不用自己部署
  2. resolve函数的作用是,将Promise对象的状态从“未完成”变为“成功”(即从 pending 变为 resolved),在异步操作成功时调用,并将异步操作的结果,作为参数传递出去
  3. reject函数的作用是,将Promise对象的状态从“未完成”变为“失败”(即从 pending 变为 rejected),在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去
  4. Promise实例生成以后,可以用then方法分别指定resolved状态和rejected状态的回调函数
promise.then(function(value) {
  // success
}, function(error) {
  // failure
});
  • then方法可以接受两个回调函数作为参数。第一个回调函数是Promise对象的状态变为resolved时调用,第二个回调函数是Promise对象的状态变为rejected时调用。其中,第二个函数是可选的,不一定要提供。这两个函数都接受Promise对象传出的值作为参数
  1. Promise 新建后就会立即执行。
let promise = new Promise(function(resolve, reject) {
  console.log('Promise');
  resolve();
});
promise.then(function() {
  console.log('resolved.');
});
console.log('Hi!');
// Promise
// Hi!
// resolved
  • Promise 新建后立即执行,所以首先输出的是Promise。然后,then方法指定的回调函数,将在当前脚本所有同步任务执行完才会执行,所以resolved最后输出
  1. resolve函数的参数除了正常的值以外,还可能是另一个 Promise
const p1 = new Promise(function (resolve, reject) {
  // ...
});
const p2 = new Promise(function (resolve, reject) {
  // ...
  resolve(p1);
})
  • p1和p2都是 Promise 的实例,但是p2的resolve方法将p1作为参数,即一个异步操作的结果是返回另一个异步操作
  • 注意,这时p1的状态就会传递给p2,也就是说,p1的状态决定了p2的状态。如果p1的状态是pending,那么p2的回调函数就会等待p1的状态改变;如果p1的状态已经是resolved或者rejected,那么p2的回调函数将会立刻执行

3.Promise.prototype.then()

  1. Promise 实例具有then方法,是定义在原型对象Promise.prototype上的,它的作用是为 Promise 实例添加状态改变时的回调函数。then方法的第一个参数是resolved状态的回调函数,第二个参数(可选)是rejected状态的回调函数,then方法返回的是一个新的Promise实例(注意,不是原来那个Promise实例),因此可以采用链式写法,即then方法后面再调用另一个then方法

4.Promise.prototype.catch()

  1. Promise.prototype.catch方法是.then(null, rejection)或.then(undefined, rejection)的别名,用于指定发生错误时的回调函数
p.then((val) => console.log('fulfilled:', val))
  .catch((err) => console.log('rejected', err));
// 等同于
p.then((val) => console.log('fulfilled:', val))
  .then(null, (err) => console.log("rejected:", err));
const promise = new Promise(function(resolve, reject) {
  throw new Error('test');
});
//等同于
promise.catch(function(error) {
  console.log(error);
});
// Error: test
// 写法一
const promise = new Promise(function(resolve, reject) {
  try {
    throw new Error('test');
  } catch(e) {
    reject(e);
  }
});
promise.catch(function(error) {
  console.log(error);
});

// 写法二
const promise = new Promise(function(resolve, reject) {
  reject(new Error('test'));
});
promise.catch(function(error) {
  console.log(error);
});
  • 比较上面两种写法,可以发现reject方法的作用,等同于抛出错误。
  1. 如果 Promise 状态已经变成resolved,再抛出错误是无效的
  2. Promise 对象的错误具有“冒泡”性质,会一直向后传递,直到被捕获为止。也就是说,错误总是会被下一个catch语句捕获
  3. 一般来说,不要在then方法里面定义 Reject 状态的回调函数(即then的第二个参数),总是使用catch方法
// bad
promise
  .then(function(data) {
    // success
  }, function(err) {
    // error
  });

// good
promise
  .then(function(data) { //cb
    // success
  })
  .catch(function(err) {
    // error
  });
  • 第二种写法要好于第一种写法,理由是第二种写法可以捕获前面then方法执行中的错误,也更接近同步的写法(try/catch)。因此,建议总是使用catch方法,而不使用then方法的第二个参数

5.Promise.prototype.finally()

  1. inally方法用于指定不管 Promise 对象最后状态如何,都会执行的操作
promise
.then(result => {···})
.catch(error => {···})
.finally(() => {···});
  • 不管promise最后的状态,在执行完then或catch指定的回调函数以后,都会执行finally方法指定的回调函数
  1. finally方法的回调函数不接受任何参数,这意味着没有办法知道,前面的 Promise 状态到底是fulfilled还是rejected。这表明,finally方法里面的操作,应该是与状态无关的,不依赖于 Promise 的执行结果

6.Promise.all()

  1. Promise.all方法用于将多个 Promise 实例,包装成一个新的 Promise 实例
  • const p = Promise.all([p1, p2, p3]);
  • Promise.all方法接受一个数组作为参数,p1、p2、p3都是 Promise 实例,如果不是,就会先调用下面讲到的Promise.resolve方法,将参数转为 Promise 实例,再进一步处理。(Promise.all方法的参数可以不是数组,但必须具有 Iterator 接口,且返回的每个成员都是 Promise 实例。)
  • p的状态由p1、p2、p3决定,分成两种情况。
  • (1)只有p1、p2、p3的状态都变成fulfilled,p的状态才会变成fulfilled,此时p1、p2、p3的返回值组成一个数组,传递给p的回调函数;(2)只要p1、p2、p3之中有一个被rejected,p的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数

7.Promise.race()

  • Promise.race方法同样是将多个 Promise 实例,包装成一个新的 Promise 实例
  • 上面代码中,只要p1、p2、p3之中有一个实例率先改变状态,p的状态就跟着改变。那个率先改变的 Promise 实例的返回值,就传递给p的回调函数

8.Promise.resolve()

  1. 有时需要将现有对象转为 Promise 对象,Promise.resolve方法就起到这个作用
  2. Promise.resolve方法的参数分成四种情况。
  • 参数是一个 Promise 实例

    • 如果参数是 Promise 实例,那么Promise.resolve将不做任何修改、原封不动地返回这个实例。
  • 参数是一个thenable对象

    • thenable对象指的是具有then方法的对象,比如下面这个对象。
let thenable = {
  then: function(resolve, reject) {
    resolve(42);
  }
};
    • Promise.resolve方法会将这个对象转为 Promise 对象,然后就立即执行thenable对象的then方法。
  • 参数不是具有then方法的对象,或根本就不是对象
    • 如果参数是一个原始值,或者是一个不具有then方法的对象,则Promise.resolve方法返回一个新的 Promise 对象,状态为resolved。
  • 不带有任何参数
    • Promise.resolve()方法允许调用时不带参数,直接返回一个resolved状态的 Promise 对象,所以,如果希望得到一个 Promise 对象,比较方便的方法就是直接调用Promise.resolve()方法。

9.Promise.reject()

  1. Promise.reject(reason)方法也会返回一个新的 Promise 实例,该实例的状态为rejected
  • 注意,Promise.reject()方法的参数,会原封不动地作为reject的理由,变成后续方法的参数

10.应用

  1. 异步加载图片
function loadImageAsync(url) {
  return new Promise(function(resolve, reject) {
    const image = new Image();

    image.onload = function() {
      resolve(image);
    };

    image.onerror = function() {
      reject(new Error('Could not load image at ' + url));
    };

    image.src = url;
  });
}
  1. Ajax 操作
const getJSON = function(url) {
  const promise = new Promise(function(resolve, reject){
    const handler = function() {
      if (this.readyState !== 4) {
        return;
      }
      if (this.status === 200) {
        resolve(this.response);
      } else {
        reject(new Error(this.statusText));
      }
    };
    const client = new XMLHttpRequest();
    client.open("GET", url);
    client.onreadystatechange = handler;
    client.responseType = "json";
    client.setRequestHeader("Accept", "application/json");
    client.send();
  });
  return promise;
};
getJSON("/posts.json").then(function(json) {
  console.log('Contents: ' + json);
}, function(error) {
  console.error('出错了', error);
});
  • getJSON是对 XMLHttpRequest 对象的封装,用于发出一个针对 JSON 数据的 HTTP 请求,并且返回一个Promise对象。需要注意的是,在getJSON内部,resolve函数和reject函数调用时,都带有参数。如果调用resolve函数和reject函数时带有参数,那么它们的参数会被传递给回调函数。reject函数的参数通常是Error对象的实例,表示抛出的错误

11.Promise.try()

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
禁止转载,如需转载请通过简信或评论联系作者。
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,921评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,635评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,393评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,836评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,833评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,685评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,043评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,694评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,671评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,670评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,779评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,424评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,027评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,984评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,214评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,108评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,517评论 2 343

推荐阅读更多精彩内容

  • [TOC] 参考阮一峰的ECMAScript 6 入门参考深入浅出ES6 let和const let和const都...
    郭子web阅读 1,768评论 0 1
  • 第一章:块级作用域绑定 块级声明 1.var声明及变量提升机制:在函数作用域或者全局作用域中通过关键字var声明的...
    BeADre_wang阅读 815评论 0 0
  • 本文为阮一峰大神的《ECMAScript 6 入门》的个人版提纯! babel babel负责将JS高级语法转义,...
    Devildi已被占用阅读 1,970评论 0 4
  • let ES6新增的用于变量声明的关键字 通过let声明的变量,不允许重复声明 不支持变量声明预解析,let变量,...
    年过古稀的Coder阅读 285评论 0 1
  • 第5章 引用类型(返回首页) 本章内容 使用对象 创建并操作数组 理解基本的JavaScript类型 使用基本类型...
    大学一百阅读 3,211评论 0 4