1、let 与 const
1.1 let
1.1.1 作用
与var类似,声明一个变量
1.1.2 特点
-
声明的变量只在代码块内有效
{ let a = 10 var b = 1 } console.log(a) // ReferenceError: a is not defined. console.log(b) // 1
-
不允许重复声明
{ let a = 10 var a = 1 } console.log(a) // SyntaxError: Identifier 'a' has already been declared
-
不存在变量提升
// var 的情况 console.log(foo) // 输出undefined var foo = 2 // let 的情况 console.log(bar) // 报错ReferenceError let bar = 2
-
暂时性死区
- 使用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 特点
- 声明后值不能修改
- 只声明不赋值也会报错
- 只能先声明后使用,不会被提前解析
- 不能重复声明一个常量
注意: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的属性:
- for…in循环:只遍历对象自身的和继承的可枚举的属性
- Object.keys():返回对象自身的所有可枚举的属性的键名
- JSON.stringify():只串行化对象自身的可枚举的属性
- 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()
-
Object.keys()
- 返回一个数组,,成员是参数对象自身的(不含继承的)所有可遍历(enumerable)属性的键名
const arr = { foo: 'bar', baz: 42 } console.log(Object.keys(arr)) // ["foo", "baz"]
-
Object.values()
- 返回一个数组,成员是参数对象自身的(不含继承的)所有可遍历(enumerable)属性的键值
const arr = { foo: 'bar', baz: 42 } console.log(Object.values(arr)) // ["bar", 42]
-
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 其他注意点
- 不可以当作构造函数
- 不可以使用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 状态
- pending(进行中):等待状态,比如正在进行网络请求或者定时器没有到时间
- fulfilled (已成功):满足状态,当主动回调了 resolve 时,就处于该状态,并且回调.then()
- 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
的状态由p1
、p2
、p3
决定,分成两种情况
- 只有
p1
、p2
、p3
的状态都变成fulfilled
,p
的状态才会变成fulfilled
,此时p1
、p2
、p3
的返回值组成一个数组,传递给p
的回调函数 - 只要
p1
、p2
、p3
之中有一个被rejected
,p
的状态就变成rejected
,此时第一个被reject
的实例的返回值,会传递给p
的回调函数
7.4.4 Promise.race()
const p = Promise.race([p1, p2, p3])
同样是将多个 Promise 实例,包装成一个新的 Promise 实例, 只要p1
、p2
、p3
之中有一个实例率先改变状态,p
的状态就跟着改变。那个率先改变的 Promise 实例的返回值,就传递给p
的回调函数
7.4.5 Promise.allSettled()
Promise.allSettled()
方法接受一组 Promise 实例作为参数,包装成一个新的 Promise 实例。只有等到所有这些参数实例都返回结果,不管是fulfilled
还是rejected
,包装实例才会结束
8、Symbol 数据类型
ES5 中对象的属性名都是字符串,容易造成重名,污染环境
8.1 特点
- Symbol 属性值对应的值是唯一的,解决命名冲突问题
- Symbol 值不能与其它数据类型进行计算,包括同字符串拼接
- for in ,for of 遍历时不会遍历 Symbol,也不会被 Object.key()、Json.stringify() 转换
- 可以使用 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 作用
- 为各种数据结构,提供一个统一的、简便的访问接口
- 使得数据结构的成员能够按某种次序排列
- ES6 创造了一种新的遍历命令
for...of
循环,Iterator 接口主要供for...of
消费
9.3 遍历过程
- 创建一个指针对象,指向当前数据结构的起始位置。也就是说,遍历器对象本质上,就是一个指针对象。
- 第一次调用指针对象的
next
方法,可以将指针指向数据结构的第一个成员。 - 第二次调用指针对象的
next
方法,指针就指向数据结构的第二个成员。 - 不断调用指针对象的
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 函数内部的每一个状态
特征:
function
关键字与函数名之间有一个星号- 函数体内部使用
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
表达式本身没有返回值,或者说总是返回undefined
。next
方法可以带一个参数,该参数就会被当作上一个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 应用
-
异步操作的同步化表达
//封装异步操作 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()
-
部署 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 函数的改进,体现在以下四点
- 内置执行器
- 调用就执行, 不像 Generator 函数,需要调用
next
方法
- 调用就执行, 不像 Generator 函数,需要调用
- 更好的语义
async
表示函数里有异步操作,await
表示紧跟在后面的表达式需要等待结果
- 更广的适用性
- 返回值是 Promise
- Generator 函数的返回值是 Iterator 对象,
async
函数可以用then
方法指定下一步的操作
- Generator 函数的返回值是 Iterator 对象,
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 优点
- 避免变量污染,命名冲突
- 提供代码的复用率,维护性
- 依赖关系管理
12.3 export 与 import
- 变量名导出
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
- 花括号导出
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
- 对象型导出
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
}
-
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>
-
默认导出
export default // 由导入的地方起名字
13、Set 和 Map
13.1 Set
无序的不可重复的多个 value 的集合体,似于数组
13.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}
-
添加 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}
-
删除 delete(value)
let set = new Set([1,3,2,5,6,3]); set.delete(3) console.log(set) // Set(4) {1,2,5,6}
-
检索 has(value)
let set = new Set([1,3,2,5,6,3]) console.log(set.has(3)) // true console.log(set.has(8)) // false
-
清除 clear()
let set = new Set([1,3,2,5,6,3]) set.clear() console.log(set) // Set(0) {}
-
数据长度 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 基本用法
-
创建 new Map(Array)
//数组里套数组,1,2是 key,名字是 value let map = new Map([[1,'zhangsan'],[2,'lisi']]) console.log(map) // Map(2) {1 => "zhangsan", 2 => "lisi"}
-
添加 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"}
-
检索 get(key)
返回检索到的元素,没有返回 undefined
let map = new Map([ [1, 'zhangsan'], [2, 'lisi'] ]) console.log(map.get(2)) // lisi
-
检索 has(key)
let map = new Map([ [1, 'zhangsan'], [2, 'lisi'] ]) console.log(map.has(2)) // true
-
删除 delete(key)
let map = new Map([ [1, 'zhangsan'], [2, 'lisi'] ]) map.delete(2) console.log(map) //Map(1) {1 => "zhangsan"}
-
清除 clear()
let map = new Map([ [1, 'zhangsan'], [2, 'lisi'] ]) map.clear() console.log(map) // Map(0) {}
-
数据长度 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