前端学习篇 -- ES6 学习

1、let 与 const

1.1 let
1.1.1 作用

与var类似,声明一个变量

1.1.2 特点
  1. 声明的变量只在代码块内有效

    {
      let a = 10
      var b = 1
    }
    console.log(a) // ReferenceError: a is not defined.
    console.log(b) // 1
    
  2. 不允许重复声明

    {
      let a = 10
      var a = 1
    }
    console.log(a)  // SyntaxError: Identifier 'a' has already been declared
    
  3. 不存在变量提升

    // var 的情况
    console.log(foo) // 输出undefined
    var foo = 2
    
    // let 的情况
    console.log(bar) // 报错ReferenceError
    let bar = 2
    
  4. 暂时性死区

    • 使用let命令声明变量之前,该变量都是不可用的。这在语法上,称为“暂时性死区”(temporal dead zone,简称 TDZ)。
    if (true) {
      // TDZ开始
      tmp = 'abc' // ReferenceError
      console.log(tmp) // ReferenceError
    
      let tmp // TDZ结束
      console.log(tmp) // undefined
    
      tmp = 123
      console.log(tmp) // 123
    }
    
1.1.3 应用

循环遍历加监听

闭包方法输出索引

<button>按钮1</button>
<button>按钮2</button>
<button>按钮3</button>
<script>
  var btns = document.querySelectorAll("button")
  for (var i = 0; i < btns.length; i++) {
    (function(i) {
      btns[i].onclick = function() {
      alert(i)
    }
    })(i)
  }
</script>

let 方法

<button>按钮1</button>
<button>按钮2</button>
<button>按钮3</button>
<script>
  var btns = document.querySelectorAll("button")
  for (let i = 0; i < btns.length; i++) {
    btns[i].onclick = function() {
      alert(i)
    }
  }
</script>
1.2 const
1.2.1 作用

声明一个常量,一旦声明就不能修改

1.2.2 特点
  1. 声明后值不能修改
  2. 只声明不赋值也会报错
  3. 只能先声明后使用,不会被提前解析
  4. 不能重复声明一个常量

注意:const声明的对象中属性是可以修改的

1.3 声明变量的六种方法

ES5:var function

ES6:let const import class

2、解构赋值

2.1 作用

从对象或数组中提取数组,并赋值给变量(多个)

2.2 对象的解构赋值
let { foo, bar } = { foo: 'aaa', bar: 'bbb' }
console.log(foo) //aaa
console.log(bar) //bbb
2.3 数组的解构赋值
let [, b, c] = [1, 2, 3]
conssole.log(b) //2
conssole.log(c) //3
2.4 函数参数的解构赋值
//数组
function add([x, y]){
  return x + y
}

add([1, 2]) // 3

//对象
function move({x = 0, y = 0}) {
  return [x, y]
}

move({x: 3, y: 8}) // [3, 8]
move({x: 3}) // [3, 0]
move({}) // [0, 0]
move() // [0, 0]
2.5 剩余运算符
//数组
let [a, ...b] = [1, 2, 3]
console.log(a) // 1
console.log(b) // [2,3]

//对象
let {a, b, ...rest} = {a: 10, b: 20, c: 30, d: 40}
console.log(a)    // 10
console.log(b)    // 20
console.log(rest) // {c: 30, d: 40}

3、模板字符串

3.1 作用

简化字符串的拼接

3.2 使用

必须用 `` 包含,变化的部分用 ${XXX} 定义

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

// 字符串中嵌入变量
let name = "Bob", time = "today";
`Hello ${name}, how are you ${time}?`

// 调用函数
function fn() {
  return "Hello World"
}

`foo ${fn()} bar`
// foo Hello World bar

4、对象的扩展

4.1 简写
4.1.1 属性名简写
function f(x, y) {
  return {x, y}
}
// 等同于
function f(x, y) {
  return {x: x, y: y}
}
f(1, 2) // Object {x: 1, y: 2}
4.1.2 方法名简写
const person = {
  sayHi() {
    console.log("Hi")
  }
}
person.sayHi() // "Hi"
//等同于
const person = {
  sayHi: function(){
    console.log("Hi")
  }
}
person.sayHi() // "Hi"
4.2 对象的拓展运算符
// 复制对象,浅拷贝,改变更深层次有影响
let person = {name: "Amy", age: 15}
let someone = { ...person }
console.log(someone)  // {name: "Amy", age: 15}

// 可用于合并两个对象,浅拷贝,改变更深层次有影响
let age = {age: 15}
let name = {name: "Amy"}
let person = {...age, ...name}
console.log(person)  // {age: 15, name: "Amy"}
4.3 新增方法
4.3.1 Object.is()

比较两个值是否严格相等,与严格比较运算符(===)不同之处只有两个

Object.is(+0, -0)   // false
Object.is(NaN, NaN) // true
4.3.2 Object.assign()

合并对象,浅拷贝,改变更深层次有影响

const target = { a: 1 }
const source1 = { b: 2 }
const source2 = { c: 3 }

Object.assign(target, source1, source2)
console.log(target) // {a:1, b:2, c:3}

// 如果目标对象与源对象有同名属性,或多个源对象有同名属性,则后面的属性会覆盖前面的属性
4.3.3 Object.getOwnPropertyDescriptor()

对象的每个属性都有一个描述对象(Descriptor),用来控制该属性的行为

获取该属性的描述对象,描述对象的enumerable属性,称为“可枚举性”,如果该属性为false,就表示某些操作会忽略当前属性

const obj = { foo: 123 }
console.log(Object.getOwnPropertyDescriptor(obj, 'foo')) 
/* {
  value: 123,        // 属性值
  writable: true,    // 属性值为true,说明属性是可以重写的
  enumerable: true,  // 属性值为true,说明属性是可以被枚举的
  configurable: true // 属性值为true,说明属性是可以被删除或可以被再次修改的
} */

有四个操作会忽略enumerable为false的属性:

  1. for…in循环:只遍历对象自身的和继承的可枚举的属性
  2. Object.keys():返回对象自身的所有可枚举的属性的键名
  3. JSON.stringify():只串行化对象自身的可枚举的属性
  4. Object.assign(): 忽略enumerable为false的属性,只拷贝对象自身的可枚举的属性
4.3.4 Object.defineProperty()

定义或修改一个属性

Object.defineProperty(object, propertyName, attributesObject)
第一个参数是属性所在的对象
第二个参数是属性名
第三个参数是属性的描述对象

const o = Object.defineProperty({}, 'p', {
  value: 123,
  writable: false,
  enumerable: true,
  configurable: false
})
console.log(o.p) // 123
o.p = 246
console.log(o.p) // 123

// 因为writable为false,所以无法改变该属性的值
4.3.5 __proto__属性

读取或设置当前对象的prototype对象

const obj = {
  money: 5000
}
const son = {}
son.__proto__ = obj    // 是浅拷贝,改变更深层次有影响
console.log(son.money) // 5000
4.3.6 Object.keys() Object.values() Object.entries()
  1. Object.keys()

    • 返回一个数组,,成员是参数对象自身的(不含继承的)所有可遍历(enumerable)属性的键名
    const arr = { foo: 'bar', baz: 42 }
    console.log(Object.keys(arr)) // ["foo", "baz"]
    
  2. Object.values()

    • 返回一个数组,成员是参数对象自身的(不含继承的)所有可遍历(enumerable)属性的键值
    const arr = { foo: 'bar', baz: 42 }
    console.log(Object.values(arr)) // ["bar", 42]
    
  3. Object.entries()

    • 返回一个数组,成员是参数对象自身的(不含继承的)所有可遍历(enumerable)属性的键值对数组
    const arr = { foo: 'bar', baz: 42 }
    console.log(Object.entries(arr)) // [ ["foo", "bar"], ["baz", 42] ]
    

5、箭头函数

5.1 使用方法

代码块只有一句时可以省略 {}

5.1.1 一个参数
const f = v => v

// 等同于
const f = function (v) {
  return v
}
5.1.2 没有参数
const f = () => console.log('我是箭头函数')

// 等同于
const f = function () {
  console.log('我是箭头函数')
}
5.1.3 两个或以上参数
const sum = (num1, num2) => num1 + num2

// 等同于
const sum = function(num1, num2) {
  return num1 + num2
}
5.2 注意点
5.2.1 返回对象处理

由于大括号被解释为代码块,必须在对象外面加上括号,否则会报错

// 报错
const getTempItem = id => { id, name: "Temp" }

// 不报错
const getTempItem = id => ({ id, name: "Temp" })
5.2.2 this 指向

箭头函数的 this 就是定义时所在的对象,而不是使用时所在的对象

扩展:看外层是否有函数(作用域),如果有,最近的外层函数(作用域)的this指向就是箭头函数的this指向

function Timer() {
  this.s1 = 0
  this.s2 = 0

  // 箭头函数,this 指向 Timer() 函数
  setInterval(() => this.s1++, 1000)

  // 普通函数
  setInterval(function () {
    this.s2++
  }, 1000)
}
let timer = new Timer()

setTimeout(() => console.log('s1: ', timer.s1), 3000)

setTimeout(() => console.log('s2: ', timer.s2), 3000)
// s1: 3
// s2: 0

//上面代码中,前者的this绑定定义时所在的作用域(即Timer函数),后者的this指向运行时所在的作用域(即全局对象)
5.2.3 其他注意点
  1. 不可以当作构造函数
  2. 不可以使用arguments对象
    • 该对象在函数体内不存在。如果要用,可以用 rest 参数代替
5.3 应用场景

当把一个函数当做参数传递给另一个函数的时候,应用场景最广

setTimeout(() => {})

6、三点运算符

6.1 rest 参数

用来取代 arguments ,比 arguments 灵活,只能是最后部分形参参数

特点:arguments 返回的是伪数组,rest 参数返回的是真数组

function fn(...values) { // 定义时前面加三个点
  console.log(values) // 使用时直接用
  values.forEach(function(value, index) {
    console.log(value, index)
  })
}
fn(1, 2, 3)
6.2 扩展运算

填充数组,把 arr1 的值填充到 arr

let arr = [1, 6]
let arr1 = [2, 3, 4, 5]
arr = [1, ...arr1, 6]
console.log(arr) // [1, 2, 3, 4, 5, 6]
console.log(...arr) // 1,2,3,4,5,6

7、Promise对象

Promise: ES6新增的API,是一个构造函数,容器中存放了一个异步任务

Promise 本身不是异步,往往内部都是封装一个异步任务

7.1 作用

将异步操作以同步的流程表达出来,避免了层层嵌套的回调函数(俗称“回调地狱”)

7.2 状态
  1. pending(进行中):等待状态,比如正在进行网络请求或者定时器没有到时间
  2. fulfilled (已成功):满足状态,当主动回调了 resolve 时,就处于该状态,并且回调.then()
  3. rejected(已失败):拒绝状态,当主动回调了 reject 时,就处于该状态,并且回调.catch()

Promise对象的状态改变,只有两种可能:从pending变为 fulfilled 和从pending变为rejected

7.3 语法

Promise构造函数接受一个函数作为参数,该函数的两个参数分别是resolve和reject。它们是两个函数,由 JavaScript 引擎提供,不用自己部署

7.3.1 创建 Promise
const promise = new Promise((resolve, reject) => {
  if (/* 异步操作成功 */) {
    //如果 true ,把容器的状态改为 resolve
    resolve(value)
  } else {
    //如果 false ,把容器的状态改为 reject
    reject(error)
  }
})

resolve函数的作用是:将Promise对象的状态从“未完成”变为“成功”,在异步操作成功时调用,并将异步操作的结果,作为参数传递出去;

reject函数反之;

7.3.2 then 方法

Promise实例生成以后,可以用then方法分别指定resolve状态和reject状态的回调函数

promise.then((value) => {
  // 成功的回调,接收的就是容器中的 resolve 函数
  // 容器中 resolve 函数传的是什么,这里接收就是什么
  console.log(value)
}, (error) => {
  // 失败的回调,接收的就是容器中的 reject 函数
  console.log(error)
})
7.3.3 链式写法

假设让下面三个异步操作按顺序执行(异步操作不会因代码写的顺序执行),就用 Promise 嵌套方式

//封装 Promise API
function fzPromise(isFlag,data,err) {
  return new Promise((resolve, reject) => {
    if (isFlag) {
      resolve(data)
    } else {
      reject(err)
    }
  })
}
const promiseOne = new fzPromise(true, 1, '操作失败')
const promiseTwo = new fzPromise(true, 2, '操作失败')
const promiseThree = new fzPromise(false, 3, '操作失败')

promiseOne
  .then((data) => {
    console.log(data)
    // 当 promiseOne 读取成功,当前函数 return 的结果就能在后面的 then 中的回调函数接收到
    return promiseTwo
  },(err) => {
    console.log(err)
  })
  // 这里是 promiseTwo 的 resolve
  .then((data) => {
    console.log(data)
    return promiseThree
  },(err) => {
    console.log(err)
  })
  // 这里是 promiseThree 的 resolve
  .then((data) => {
    console.log(data)
  },(err) => {
    // 因为 promiseThree 是 false ,所以结果是 reject 传的 '操作失败'
    console.log(err)
  })
    
// 1
// 2
// 操作失败
7.4 其他方法
7.4.1 Promise.resolve()
Promise.resolve('foo')
// 等价于
new Promise(resolve => resolve('foo'))
let thenable = {
  then(resolve, reject) {
    resolve(42)
  }
}

let p1 = Promise.resolve(thenable)
p1.then(function(value) {
  console.log(value)  // 42
})
7.4.2 Promise.reject()
const p = Promise.reject('出错了')
// 等同于
const p = new Promise((resolve, reject) => reject('出错了'))

p.then(null, function (s) {
  console.log(s)
})
// 出错了
7.4.3 Promise.all()

用于将多个 Promise 实例,包装成一个新的 Promise 实例, 接受一个数组作为参数, 可以不是数组,但必须具有 Iterator 接口

const p = Promise.all([p1, p2, p3])

p的状态由p1p2p3决定,分成两种情况

  1. 只有p1p2p3的状态都变成fulfilledp的状态才会变成fulfilled,此时p1p2p3的返回值组成一个数组,传递给p的回调函数
  2. 只要p1p2p3之中有一个被rejectedp的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数
7.4.4 Promise.race()
const p = Promise.race([p1, p2, p3])

同样是将多个 Promise 实例,包装成一个新的 Promise 实例, 只要p1p2p3之中有一个实例率先改变状态,p的状态就跟着改变。那个率先改变的 Promise 实例的返回值,就传递给p的回调函数

7.4.5 Promise.allSettled()

Promise.allSettled()方法接受一组 Promise 实例作为参数,包装成一个新的 Promise 实例。只有等到所有这些参数实例都返回结果,不管是fulfilled还是rejected,包装实例才会结束

8、Symbol 数据类型

ES5 中对象的属性名都是字符串,容易造成重名,污染环境

8.1 特点
  1. Symbol 属性值对应的值是唯一的,解决命名冲突问题
  2. Symbol 值不能与其它数据类型进行计算,包括同字符串拼接
  3. for in ,for of 遍历时不会遍历 Symbol,也不会被 Object.key()、Json.stringify() 转换
  4. 可以使用 Object.getOwnPropertySymbols() 拿到对象中的 Symbol 键值,返回一个数组
const obj = {}
const foo = Symbol('foo')
const bar = Symbol('bar')
obj[foo] = 'foo'
obj[bar] = 'bar'
for (let i in obj) {
  console.log(i) // 无输出
}

const symbols = Object.getOwnPropertySymbols(obj)
console.log(symbols) // [Symbol(foo), Symbol(bar)]
8.2 使用方法

Symbol函数前不能使用new命令,否则会报错。这是因为生成的 Symbol 是一个原始类型的值,不是对象

8.2.1 带参数
// 如果不加参数,它们在控制台的输出都是Symbol(),不利于区分
const s1 = Symbol('foo')  //标识
const s2 = Symbol('bar')  //标识
console.log(s1) // Symbol(foo)
console.log(s2) // Symbol(bar)
8.2.2 不带参数
const s1 = Symbol()
const s2 = Symbol()
console.log(s1) // Symbol()
console.log(s2) // Symbol()
console.log(s1 === s2) // false
8.3 方法
8.3.1 Symbol.for()

不会每次调用就返回一个新的 Symbol 类型的值,而是会先检查给定的key是否已经存在,如果不存在才会新建一个值。Symbol.for() 创建的值会被登记

const s1 = Symbol.for('foo')
const s2 = Symbol.for('foo')
// 等同于 const s2 = s1

console.log(s1 === s2)
// true
8.3.2 Symbol.keyFor()

返回一个已登记的 Symbol 类型值的key

const s1 = Symbol.for("foo")
Symbol.keyFor(s1) // "foo"

const s2 = Symbol("foo")
Symbol.keyFor(s2) // undefined
8.4 内置的 Symbol 值

除了定义自己使用的 Symbol 值以外,ES6 还提供了 11 个内置的 Symbol 值,指向语言内部使用的方法

8.4.1 Symbol.iterator

指向该对象的默认遍历器方法, 对象进行for...of循环时,会调用Symbol.iterator方法,返回该对象的默认遍历器

const myIterable = {}
myIterable[Symbol.iterator] = function* () {
  yield 1
  yield 2
  yield 3
}

for (let i of myIterable) {
  console.log(i)
}
// 1
// 2
// 3

console.log([...myIterable]) // [1, 2, 3]
8.5 消除魔术字符串

魔术字符串指的是:在代码之中多次出现,与代码形成强耦合的某一个具体的字符串或者数值

function getArea(shape, options) {
  let area = 0
  switch (shape) {
  case 'Triangle': // 魔术字符串
    area = .5 * options.width * options.height;
    break;
  }
  return area
}
getArea('Triangle', { width: 100, height: 100 }) // 魔术字符串

上面代码中,字符串Triangle就是一个魔术字符串。它多次出现,与代码形成“强耦合”,不利于将来的修改和维护

常用的消除魔术字符串的方法,就是把它写成一个变量

const shapeType = {
  triangle: Symbol('triangle')
}

function getArea(shape, options) {
  let area = 0
  switch (shape) {
    case shapeType.triangle:
    area = .5 * options.width * options.height
    break;
  }
  return area;
}

getArea(shapeType.triangle, { width: 100, height: 100 })

9、Iterator 和 for…of 循环

9.1 概念

遍历器(Iterator)是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署 Iterator 接口,就可以完成遍历操作(即依次处理该数据结构的所有成员)。

9.2 作用
  1. 为各种数据结构,提供一个统一的、简便的访问接口
  2. 使得数据结构的成员能够按某种次序排列
  3. ES6 创造了一种新的遍历命令for...of循环,Iterator 接口主要供for...of消费
9.3 遍历过程
  1. 创建一个指针对象,指向当前数据结构的起始位置。也就是说,遍历器对象本质上,就是一个指针对象。
  2. 第一次调用指针对象的next方法,可以将指针指向数据结构的第一个成员。
  3. 第二次调用指针对象的next方法,指针就指向数据结构的第二个成员。
  4. 不断调用指针对象的next方法,直到它指向数据结构的结束位置。

具体来说,就是返回一个包含value和done两个属性的对象。其中,value属性是当前成员的值,done属性是一个布尔值,表示遍历是否结束。

例子:

function makeIterator(array) {
  let nextIndex = 0
  return {
    next: function() {
      return nextIndex < array.length ?
        {value: array[nextIndex++], done: false} :
        {value: undefined, done: true}
    }
  };
}
var it = makeIterator(['a', 'b'])

console.log(it.next()) // { value: "a", done: false }
console.log(it.next()) // { value: "b", done: false }
console.log(it.next()) // { value: undefined, done: true }
9.4 默认有 Iterator 接口

原生具备iterator接口(即默认部署了Symbol.iterator属性),for...of循环本质上就是调用这个接口产生的遍历器

  • Array
  • Map
  • Set
  • String
  • TypedArray
  • 函数的 arguments 对象
  • NodeList 对象

对象(Object)之所以没有默认部署 Iterator 接口,是因为对象的哪个属性先遍历,哪个属性后遍历是不确定的,需要开发者手动指定

const arr = ['red', 'green', 'blue']

for(let v of arr) {
  console.log(v) // red green blue
}

const obj = {}
obj[Symbol.iterator] = arr[Symbol.iterator].bind(arr)

for(let v of obj) {
  console.log(v) // red green blue
}

10、Generator函数

10.1 概念

Generator 函数是一个状态机,封装了多个内部状态 , 执行 Generator 函数会返回一个遍历器对象 , 可以依次遍历 Generator 函数内部的每一个状态

特征:

  1. function关键字与函数名之间有一个星号
  2. 函数体内部使用yield表达式,定义不同的内部状态(yield在英语里的意思就是“产出”)
function* helloWorldGenerator() {
  yield 'hello'
  yield 'world'
  return 'ending'
}

var hw = helloWorldGenerator()

hw.next()
// { value: 'hello', done: false }

hw.next()
// { value: 'world', done: false }

hw.next()
// { value: 'ending', done: true }

hw.next()
// { value: undefined, done: true }

调用 Generator 函数后,该函数并不执行,返回的也不是函数运行结果,而是一个指向内部状态的指针对象。必须调用遍历器对象的next方法,使得指针移向下一个状态, 直到遇到下一个yield表达式(或return语句)为止

写法:

function * foo(x, y) { ··· }
function *foo(x, y) { ··· }
function* foo(x, y) { ··· } // 通用
function*foo(x, y) { ··· }
10.2 yield 和 yield* 表达式

只能用在 Generator 函数里面,用在其他地方都会报错

与 return 的区别:

  • 相似之处在于,都能返回紧跟在语句后面的那个表达式的值
  • 区别在于每次遇到yield,函数暂停执行,下一次再从该位置继续向后执行,而return语句不具备位置记忆的功能
  • 一个函数里面,只能执行一次 return语句,但是可以执行多次 yield表达式

位置

yield表达式如果用在另一个表达式之中,必须放在圆括号里面

console.log('Hello' + (yield))

yield 和 yield*的区别

如果在 Generator 函数内部,调用另一个 Generator 函数。需要在前者的函数体内部,自己手动完成遍历

function* foo() {
  yield 'a'
  yield 'b'
}

function* bar() {
  yield 'x'
  // 手动遍历 foo()
  for (let i of foo()) {
    console.log(i)
  }
  yield 'y'
}

for (let v of bar()){
  console.log(v)
}
// x
// a
// b
// y

使用 yeild* 表达式

function* foo() {
  yield 'a'
  yield 'b'
}

function* bar() {
  yield 'x'
  yield* foo()
  yield 'y'
}

// 等同于
function* bar() {
  yield 'x'
  yield 'a'
  yield 'b'
  yield 'y'
}

// 等同于
function* bar() {
  yield 'x'
  for (let v of foo()) {
    yield v
  }
  yield 'y'
}

for (let v of bar()){
  console.log(v)
}
// "x"
// "a"
// "b"
// "y"
10.3 next 方法的参数

yield表达式本身没有返回值,或者说总是返回undefinednext方法可以带一个参数,该参数就会被当作上一个yield表达式的返回值

function* foo(x) {
  var y = 2 * (yield (x + 1))
  var z = yield (y / 3)    
  return (x + y + z)
}

var a = foo(5)
a.next() // Object{value:6, done:false}   返回 5 + 1,即 6
a.next() // Object{value:NaN, done:false} 返回 2 * undefined / 3,即 NaN
a.next() // Object{value:NaN, done:true}  返回 5 + NaN + undefined,即 NaN

var b = foo(5)
b.next() // { value:6, done:false }   返回 5 + 1,即 6
b.next(12) // { value:8, done:false } 返回 2 * 12 / 3,即 8
b.next(13) // { value:42, done:true } 返回 5 + 24 + 13,即 42

10.4 应用
  1. 异步操作的同步化表达

    //封装异步操作
    function getNews(url) {
      $.get(url, function(res) {
        let url = 'http://localhost:8080' + res.commentsUrl; //第二次请求地址
        SX.next(url); //调用第二个 yield ,把拿到的第二次请求地址当参数传入
      })
    }
    
    function* sendXml() {
      let url = yield getNews('http://localhost:8080?id=3'); //第一次请求
      yield getNews(url); //发送第二次请求
    }
    
    let SX = sendXml()
    SX.next()
    
  2. 部署 Iterator 接口

    • 利用 Generator 函数,可以在任意对象上部署 Iterator 接口
    function* iterEntries(obj) {
      let keys = Object.keys(obj);
      for (let i=0; i < keys.length; i++) {
        let key = keys[i];
        yield [key, obj[key]];
      }
    }
    
    let myObj = { foo: 3, bar: 7 };
    
    for (let [key, value] of iterEntries(myObj)) {
      console.log(key, value);
    }
    
    // foo 3
    // bar 7
    

11、async 函数

11.1 改进

async函数对 Generator 函数的改进,体现在以下四点

  1. 内置执行器
    • 调用就执行, 不像 Generator 函数,需要调用next方法
  2. 更好的语义
    • async表示函数里有异步操作,await表示紧跟在后面的表达式需要等待结果
  3. 更广的适用性
  4. 返回值是 Promise
    • Generator 函数的返回值是 Iterator 对象, async函数可以用then方法指定下一步的操作
11.2 基本用法

async函数返回一个 Promise 对象,可以使用then方法添加回调函数。当函数执行的时候,一旦遇到await就会先返回,等到异步操作完成,再接着执行函数体内后面的语句。

async function getNews(url) {
  return new Promise((resolve, reject) => {
    $.ajax({
      type: "get",
      url,
      success: data => {
        resolve(data)
      }
    })
  })
}

async function sendXml() {
  let result = await getNews('http://localhost:8080?id=3') //第一次请求
  result = await getNews('http://localhost:8080?id=3' + result.commentsUrl) //第二次请求
}
sendXml()

12、Module (模块)

12.1 与 CommonJS 模块的差异

CommonJS 模块输出的是一个值的拷贝,ES6 模块输出的是值的引用。
CommonJS 模块是运行时加载,ES6 模块是编译时输出接口。

12.2 优点
  1. 避免变量污染,命名冲突
  2. 提供代码的复用率,维护性
  3. 依赖关系管理
12.3 export 与 import
  1. 变量名导出
export.js =>   //公用的 js 
// export 用于导出
export let uname = 'zhangsan';  
export let age = 18;  

import.js =>
// import 用于导入
import {uname, age} form './export.js'
console.log(`我叫${uname},今年${age}`) // 我叫zhangsan,今年18
  1. 花括号导出
export.js =>   // 公用的 js 
//export 用于导出
let uname = 'zhangsan' 
let age= 18; 
export {uname,age}

import.js =>
//import 用于导入
import {uname as myname,age} form './export.js'
console.log(`我叫${myname},今年${age}`) //我叫zhangsan,今年18

  1. 对象型导出
export.js =>   // 公用的 js 
// export 用于导出
let obj = {
  uname : 'zhangsan', 
  age: 18; 
} 
export {obj}
  
import.js =>
// import 用于导入
import {obj} form './export.js'
obj.age = 50;
console.log(obj) 
//
{
  uname : 'zhangsan',
  age: 50 
} 
  1. script 标签使用

    //module.js
    export default function test(){
      return 'test...'
    }
    
    // index.js
    import test from './module.js'
    console.log(test())
    
    // 方法 1 : 引入module.js,然后在script标签里面调用
    <script type="module">
      import test from './module.js'
      console.log(test())
    </script>
    
    // 方法 2 : 直接引入index.js,使用src引入
    <script type="module" src="./index.js"></script>
    
  2. 默认导出

    export default // 由导入的地方起名字
    

13、Set 和 Map

13.1 Set

无序的不可重复的多个 value 的集合体,似于数组

13.1.1 基本用法
  1. 创建 new Set(Array)

    let set = new Set([1,3,2,5,6,3])
    console.log(set) //Set(5) {1,3,2,5,6}
    
  2. 添加 add(value)

    let set = new Set([1,3,2,5,6,3]);
    set.add(7)
    console.log(set) // Set(6) {1,3,2,5,6,7}
    
  3. 删除 delete(value)

    let set = new Set([1,3,2,5,6,3]);
    set.delete(3)
    console.log(set) // Set(4) {1,2,5,6}
    
  4. 检索 has(value)

    let set = new Set([1,3,2,5,6,3])
    console.log(set.has(3)) // true
    console.log(set.has(8)) // false
    
  5. 清除 clear()

    let set = new Set([1,3,2,5,6,3])
    set.clear()
    console.log(set) // Set(0) {}
    
  6. 数据长度 size

    let set = new Set([1, 3, 2, 5, 6, 3])
    console.log(set.size) // 5
    
13.1.2 应用

去除数组的重复成员

let arr = [1, 2, 4, 4, 1, 3]
let newArr = [...new Set(arr)]
console.log(newArr)  // [1,2,4,3]
13.3 遍历操作

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

返回的都是遍历器对象,由于 Set 结构没有键名,只有键值(或者说键名和键值是同一个值),所以keys方法和values方法的行为完全一致

(1)keys(),values(),entries()

let set = new Set(['red', 'green', 'blue'])

for (let item of set.keys()) {
  console.log(item)
}
// red
// green
// blue

for (let item of set.values()) {
  console.log(item)
}
// red
// green
// blue

for (let item of set.entries()) {
  console.log(item)
}
// ["red", "red"]
// ["green", "green"]
// ["blue", "blue"]

(2)forEach()

let set = new Set([1, 4, 9])
set.forEach((value, key) => console.log(key + ' : ' + value))
// 1 : 1
// 4 : 4
// 9 : 9
13.2 Map

无序的 key 不重复的多个 key-value 的集合体

13.2.1 基本用法
  1. 创建 new Map(Array)

    //数组里套数组,1,2是 key,名字是 value
    let map = new Map([[1,'zhangsan'],[2,'lisi']])
    console.log(map) // Map(2) {1 => "zhangsan", 2 => "lisi"}
    
  2. 添加 set(key,value)

    let map = new Map([
      [1, 'zhangsan'],
      [2, 'lisi']
    ])
    map.set(3, 'wangwu')
    console.log(map) // Map(3) {1 => "zhangsan", 2 => "lisi", 3 => "wangwu"}
    
  3. 检索 get(key)

    返回检索到的元素,没有返回 undefined

    let map = new Map([
      [1, 'zhangsan'],
      [2, 'lisi']
    ])
    console.log(map.get(2)) // lisi
    
  4. 检索 has(key)

    let map = new Map([
      [1, 'zhangsan'],
      [2, 'lisi']
    ])
    console.log(map.has(2)) // true
    
  5. 删除 delete(key)

    let map = new Map([
      [1, 'zhangsan'],
      [2, 'lisi']
    ])
    map.delete(2)
    console.log(map) //Map(1) {1 => "zhangsan"}
    
  6. 清除 clear()

    let map = new Map([
      [1, 'zhangsan'],
      [2, 'lisi']
    ])
    map.clear()
    console.log(map) // Map(0) {}
    
  7. 数据长度 size

    let map = new Map([
      [1, 'zhangsan'],
      [2, 'lisi']
    ])
    console.log(map.size) // 2
    

14、BigInt 数据类型

JS 第八种基本数据类型,大整数值。只能是整数。

BigInt(10.2)    // → RangeError
BigInt(null)    // → TypeError
BigInt("abc")   // → SyntaxError

JS 中的Number类型只能安全地表示 -9007199254740991 (-(2^53-1)) 和9007199254740991(2^53-1)之间的整数,任何超出此范围的整数值都可能失去精度。

console.log(9999999999999999)    // → 10000000000000000

使用 BIgInt 可以在标准JS中执行对大整数的算术运算,而不会有精度损失的风险。

14.1 创建 BigInt
console.log(1n) // 1n
console.log(BigInt(1)) // 1n
14.2 与 Number 数据比较
console.log(10n === 10)    // false
console.log(10n == 10)     // true
console.log(typeof 10n)    // bigint
console.log(typeof 10)     // number
14.3 运算符使用

除一元加号(+)运算符外,所有算术运算符都可用于BigInt

console.log(10n + 20n)    // 30n
console.log(10n - 20n)    // -10n
console.log(+10n)         // TypeError: Cannot convert a BigInt value to a number
console.log(-10n)         // -10n
console.log(10n * 20n)    // 200n
console.log(20n / 10n)    // 2n
console.log(23n % 10n)    // 3n
console.log(10n ** 3n)    // 1000n
let x = 10n
console.log(++x)          // 11n
console.log(--x)          // 9n

不支持一元加号(+)运算符的原因是某些程序可能依赖于+始终生成Number的不变量,或者抛出异常。 更改+的行为也会破坏asm.js代码

注意:除法(/)运算符的结果会自动向下舍入到最接近的整数

console.log(25n / 10n)    // 2n
14.4 隐式类型转换

因为隐式类型转换可能丢失信息,所以不允许在BigInt和 Number 之间进行混合操作

console.log(10 + 10n)    // TypeError

// 调用Number()或BigInt()来转换操作数
BigInt(10) + 10n    // 20n
10 + Number(10n)    // 20

关系运算符

console.log(10n > 5)    // true
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值