1. Let和const命令
let命令
let命令的特性
- 不存在变量提升
- 块级作用域(只在该代码块内生效)
- 暂时性死区/先声明后调用(在代码块内,使用let声明变量之前,该变量都是不可用的。)
- 相同作用域下,不允许重复声明
let命令与var命令
,都是用来声明变量的。与var相比,let不存在变量提升问题,仅在所在的代码块内有效。
for (var i = 0; i < 5; i++) {
}
console.log('===: ', i) // 5个5
原因
:由var声明的变量,在全局作用域内都有效(只要不在其他的两个作用域内),所以在全局只有一个i,每次循环都会i进行修改。并且由var声明的变量,只会循环一次。解决
:使用let来声明变量,let生命的变量仅在当前的块级作用域内有效,每次循环的变量都是一个新的变量。- V8引擎会记住上一轮循环的值
let a = 1;
console.log(a) // 1
console.log(this.a) // undefined
ES6 的
块级作用域必须有大括号
,如果没有大括号,JavaScript 引擎就认为不存在块级作用域。
if (true) let x = 1; // 第一种写法,报错
if (true) { // 第二种写法,不报错
let x = 1;
}
暂时性死区
- 在代码块内,使用let命令声明变量之前,该变量都是不可用的。这在语法上,称为“暂时性死区”
- 在代码块内,使用let、const命令,就绑定了该代码块
const命令
const命令特性
- 常用来声明一个常量,一旦声明,常量的值便不能修改。
- 基本数据类型的值不能修改。引用数据类型的地址不能修改,但内部的数据可以修改。
- 只声明不赋值,会报错
- 其他特性与let特性一致
const本质
const实际上保证的,并不是变量的值不得改动,而是变量指向的那个内存地址所保存的数据不得改动。对于简单类型的数据(数值、字符串、布尔值),值就保存在变量指向的那个内存地址,因此等同于常量。但对于复合类型的数据(主要是对象和数组),变量指向的内存地址,保存的只是一个指向实际数据的指针,const只能保证这个指针是固定的(即总是指向另一个固定的地址),至于它指向的数据结构是不是可变的,就完全不能控制了。
声明变量的六种方法
- var
- let
- const
- function
- import
- class
var命令和function命令声明的全局变量,依旧是顶层对象(window)的属性;另一方面规定,let命令、const命令、class命令声明的全局变量,不属于顶层对象的属性。也就是说,从 ES6 开始,全局变量将逐步与顶层对象的属性脱钩。
顶层对象
- 在浏览器环境中指的是window对象。node指的是global对象。
- ES5 之中,顶层对象的属性与全局变量是等价的。var.a = this.a = window.a
- ES6中全局变量与顶层对象开始脱钩。
var a = 1;
console.log(window.a) // 1
let a = 1;
console.log(window.a) // undefined
2. 解构赋值
- 可解构:数组、对象、函数参数、String、Set、Map
- 只要某种数据结构具有 Iterator 接口,都可以采用数组形式的解构赋值
- 除对象之外,解构其他数据类型。都使用 []
- 对象解构时,左右两侧数据类型要一致,解构对象键名要相同
- 可配合扩展运算符、reset参数,一起使用
- 可以设置默认值,前提是值===undefined时才会生效
- 如果解构不成功,变量的值就等于undefined
作用
- 交换变量的值
- 提取数据
- 函数参数的默认值
// array
let arr = [1,2,3,4];
let [r1, r2 ,...r3] = arr;
// object
let obj = {
name: '啦啦啦',
age: 25,
type: true
};
let {name, ...age} = obj;
// String
let str = 'string';
let [s1, ...s2] = str;
// Set
let [a1, a2, a3, a4] = new Set(arr);
// Map
let [b1 ,b2] = new Map().set({name: 'map1'}, true).set({name: 'map2'}, false)
// 解构函数参数
function f ([a, b]) {
console.log(a, b)
}
f([1,2])
// 默认值
let [x, y, ...z] = ['a']
console.log(z) // []
let [a, b = '默认值'] = [1, 2]
console.log(a, b) // 1,默认值
3. 扩展运算符 & reset参数
- 可扩展数组、字符串、Set
- 主要用于函数调用,扩展参数
- rest 参数之后不能再有其他参数(即只能是最后一个参数),否则会报错
function arrs (a, b, c) {
console.log(a, b, c)
}
let a = [1,2,3];
arrs(...a)
扩展运算符的应用
- 复制数组: let a3 = […a1];
- 合并数组: […a1,…a2]
- 与解构赋值结合
- 将字符串转为真正的数组: […str]
- 实现了 Iterator 接口的对象
- Map 和 Set 结构,Generator 函数
4. 箭头函数
let a = () => '10'
- ES6允许使用 => 定义函数
- 没有或有多个参数时使用()来表示
- 不加{},默认为return
- 作用:1.优化回调函数 2.更加简洁,减少代码量
function f () {
setTimeout(() => {
console.log('id: ', this.id)
}, 100)
}
let id = 21;
f.call({id: 42})
-
箭头函数本身没有this。它的this是定义时所在的对象,而不是调用时的对象 || 本身没有this,内部this就是外部(普通函数)的this
-
因为没有this,所以不能使用call,apply,bind方法,但外层的普通函数可以使用
-
因为没有this,所以不能当做构函函数,不能与new一起使用
-
没有arguments参数,可使用reset参数代替
-
将动态this变为静态this
补充
- 参考文章
- 箭头函数没有prototype(原型),箭头函数本身没有this
- 箭头函数的this在定义的时候继承自外层第一个普通函数的this。
- 如果箭头函数外层没有普通函数,严格模式和非严格模式下它的this都会指向window(全局对象)
箭头函数转换为ES5
// ES6
function foo() {
setTimeout(() => {
console.log('id:', this.id);
}, 100);
}
// ES5
function foo() {
var _this = this;
setTimeout(function () {
console.log('id:', _this.id);
}, 100);
}
5. Promise()
含义
: Promise是解决异步编程的一种解决方案作用
:解决异步回调问题,回调地狱三种状态
:进行中,成功,失败特点
:1.Promise的状态不受外部影响 2.状态一旦改变,便不会再变
缺点
- 无法取消Promise,一旦新建它就会立即执行,无法中途取消
- 如果不设置回调函数,Promise内部抛出的错误,不会反应到外部
- 当处于pending状态时,无法得知目前进展到哪一个阶段
基本用法
let pro = new Promise((resolve, reject) => {
resolve(100)
})
- Promise对象是一个构造函数,用来生成Promise实例
- 接受一个回调函数,回调函数有两个参数:resolve,reject
- Promise一旦新建便会立即执行
then()
let pro = new Promise((resolve, reject) => {
resolve(100)
})
pro.then(res => {
console.log('成功:', res)
return Number(res) * 2
}).then(res => {
console.log('链式调用:', res)
}).catch(err => {
console.log('失败:', err)
})
- then()
- then方法,是Promise实例状态改变时的回调函数
返回一个新的Promise对象,此时才是异步,catch同理 || then方法是异步执行的,之前都是同步执行
- 可以链式调用
- catch(): 发生错误时的回调函数
补充
promise.then(function(value) {
// success
}, function(error) {
// failure
})
- then方法可以接受两个回调函数作为参数
- 第一个回调函数是Promise对象的状态变为resolved时调用
- 第二个回调函数是Promise对象的状态变为rejected时调用,第二个函数是可选的,不一定要提供
Promise().all()方法
- 参考文章
- Promise.all方法用于将多个 Promise 对象实例包装,生成并返回一个新的Promise实例
- Promise.all方法接受一个数组作为参数(Promise.all方法的参数可以不是数组,但必须具有 Iterator 接口,且返回的每个成员都是 Promise 实例)
Promise.all([ // 数组中所有的promise实例都变为resolve的时候,该方法才会返回
getVirtualCoinAnalyze(),
getEarningsCount()
]).then(res => { // res中包含所有数据
let [ dianying, youying ] = res; // 配合解构赋值进行使用
this.data = dianying.data;
}).catch(err => { // 当数组中有一个promise对象,状态为reject时,整个Promise.all调用会立即终止,并返回一个reject的新的promise对象
console.log(err)
})
Promise().resolve()
// 返回一个新的Promise实例,状态为resolve
let resolve = Promise.resolve('成功');
resolve.then(res => {
console.log(res)
})
Promise().reject()
// 返回一个新的Promise实例,状态为reject
let reject = Promise.reject('失败');
reject.catch(err => {
console.log(err)
})
6. Iterator && for…of
Iterator(迭代器)
含义
:为不同的数据结构,提供统一的访问机制。任何数据只要部署了Iterator接口,就可以进行遍历操作
作用
- 为不同的数据提供统一的访问机制
- 使数据结构成员按某种次序排列
- 创造新的遍历命令 for…of
ES6规定
默认的Iterator接口
部署在数据结构的Symbol.iterator属性,或者说,一个数据结构只要具有Symbol.iterator属性,就可以认为是“可遍历的”
原生具备Iterator接口的数据结构
- Array
- String
- Set
- Map
- Arguments
调用Iterator的场合
- 解构赋值
- …
- 以数组为参数的方法
- for…of
- set
- map
- Array.from
- Promise.all()
for…of
- ES6引入for…of,可以遍历所有的数据结构
- for…of循环内部调用的是数据结构内部的symbol.iterator方法
- 可遍历
- 数组
- 字符串
- set
- map
- arguments对象
- 带有symbol.iterator的对象
for...in && for..of
-
for…in
- 主要是用来遍历对象,不适合遍历数组
- 循环读取的是键名
-
for…of
- 可遍历所有数据结构
- 循环读取的是键值
let obj = {
0: '小明',
1: 22,
length: 2
}
let newObj = Array.from(obj)
for (let i of newObj) {
console.log('===: ', i)
}
7. Set 和 Map 数据结构
数据结构
- Array
- Object
- Set
- Map
Set()
- ES6新增的数据结构,类似于数组,内部的值都是唯一的,不存在重复的数据,常用于数组去重
- 是一个构造函数
// 一维数组去重
let arr = [1,2,1,2,3,4];
console.log('转换数组的方式1', [...new Set(arr)])
console.log(Array.from('转换数组的方式2', new Set(arr)))
// 字符串去重
console.log([...new Set('aassdd')].join('-'))
set()的操作方法
- size: 返回Set实例的成员总数
- add
- has: 返回一个布尔值,表示该值是否为Set的成员
- delete
- clear
let newSet = new Set();
newSet.add(1)
console.log(newSet.size)
newSet.add(2)
newSet.delete(2)
console.log(newSet.has(2))
newSet.clear()
WeakSet()
含义
:与Set类似,也是不重复值得集合
区别
:- WeakSet的值只能是对象,而不能是其他类型的值
- WeakSet中的对象都是弱引用,垃圾回收机制会自动回收该对象所占用的内存(不会考虑是否还在占用)
- WeakSet 的成员是不适合引用的,因为它会随时消失
场景
:WeakSet 适合临时存放一组对象,以及存放跟对象绑定的信息。只要这些对象在外部消失,它在 WeakSet 里面的引用就会自动消失
let newSet = new WeakSet();
// let arr = [[1,2],[3,4]]
let arr = [
{name: '小明'},
{name: '小红'}
]
newSet.add(arr)
console.log(newSet)
- 是一个构造函数
- 接受一个数组或类似数组的对象作为参数(实际上,任何具有 Iterable 接口的对象,都可以作为 WeakSet 的参数)
- 不能遍历
操作方法
- add()
- delete()
- has()
Map()
-
Object
- 键值对,键名只能是字符串
- 字符串 - 值
-
Map()
- ES6新增数据结构,类似于对象
- 键名可以是任意类型,即值 —— 值
- 是一个构造函数,接受数组作为参数
- Map的键名实际上是跟内存地址绑定的,只要内存地址不一样,就视为两个键
let arr = [
['name', '小明'],
['age', '22']
]
let newMap = new Map(arr);
console.log(newMap.get('name'))
- 操作方法
- size()
- set()
- get()
- has()
- delete()
- clear()
let newMap = new Map();
let a = {name: '小明'};
newMap.set(a, '这是一个值')
console.log(newMap.get(a))
WeakMap()
- WeakMap只接受对象作为键名,null除外
- WeakMao的键名所指向的对象,不计入垃圾回收机制
- 是一个构造函数
- 不可遍历
场景
:以DOM节点作为键名
操作
- set()
- get()
- has()
- delete()
8. 模块化
- 称为“编译时加载”或者静态加载
- ES6 的模块自动采用严格模式,不管你有没有在模块头部加上"use strict"
- 作用:按需加载
export命令
- export命令用于规定模块的
对外接口
- exprot可以输出
变量,方法,class
, 可以使用 as 进行重命名 - export命令规定的是对外的接口,
必须与模块内部的变量建立一一对应关系
- export命令可以
出现在模块的任何位置
,只要处于模块顶层就可以
。如果处于块级作用域内,就会报错,因为处于条件代码块之中,就没法做静态优化了
export的三种写法
// 第一种写法
export var num = 100;
// 推荐使用第二种写法,这样写更加直观
var num = 100;
export function totail (x, y){
return x * y
}
export {
num,
totail
}
// 第三种写法
var num = 100;
export {
num as isNum // 使用 as 关键字重命名。之后便可以使用不同的名字输出两次
}
import命令
import { foo } from './pro.js'
import { num as isNum } from './profile'
- 接受一对
{ }
,里面指定要从其他模块导入的变量名。大括号里面的变量名,必须与被导入模块对外接口的名称相同
- from指定
模块文件的位置
可以是相对路径,也可以是绝对路径,.js后缀可以省略
- import命令
具有提升效果,会提升到整个模块的头部,首先执行
, 静态执行,不允许有表达式 - import命令要使用
as关键字
,将输入的变量重命名
import {a} from './xxx.js'
a.foo = 'hello'; // 合法操作
- import命令
输入的变量都是只读的
,因为它的本质是输入接口
。只能调用,不能对加载
的功能做其他修改、如果是对象的话,可以修改
exprot default 命令
import fo from './pro'
- export default命令用于
指定模块的默认输出
一个模块只能有一个默认输出
,因此export default命令只能使用一次
- import命令后面才
不用加大括号
本质上
,export default就是输出一个叫做default的变量或方法,然后系统允许你为它取任意名字
exprot defalut function foo() {
console.log('第一种写法')
}
function foo () {
console.log('第二种写法')
}
function fun () {
console.log('默认导出')
}
exprot default { // 只能有一个 export default,但可以导出多个方法
foo,
fun
}
exprot defalut可以和 exprot 一起配合使用
import 默认接口,{foo} from './foo'
exprot default function sum () {
console.log('默认接口')
}
function foo () {
console.log('普通对外接口')
}
exprot {
foo
}
模块的整体加载
import * as aclice from './aclice'
console.log('圆面积:' + circle.area(4));
*
代表加载所有aclice模块中的输出的内容, 配合as使用
公用方法实现异步回调
- 目的:解决异步回调问题
- 创建一个公用方法并导出
// 公用方法
function checkMsg (text, success) {
workworldAPI.checkMsg(text).then(res => {
success(true) // sussess是回调函数
}).catch(err => {
console.log(err.message)
})
}
export default {
checkMsg
}
// 调用该方法
import checkMsg from '@/utils/processAmount'
checkMsg('参数', (res) => { // 回调函数
console.log(res) // true
})
function aaa (num, success, err) {
if (num > 5) {
success('成功的回调')
} else {
err('失败的回调')
}
}
aaa(8, res => {
console.log('res: ', res)
}, err => {
console.log('err: ', err)
})