目录
ES6, 全称 ECMAScript 6.0 ,是 JavaScript 的下一个版本标准
JavaScript 语言名称是商标( Oracle 公司注册的商标),正式名称是 ECMAScript 。
简洁教程:ES6 教程
详细教程:ES6 教程 阮一峰
简洁归纳:1.5万字概括ES6全部特性(已更新ES2020)
表达式
声明
声明 | 值 | 作用域 | 声明次数 | 变量提升 |
---|---|---|---|---|
var 声明变量 | 值可改变 | 全局范围 | 可以声明多次 | 有,变量可以在声明之前使用undefined |
let 声明变量 | 值可改变,for循环作计数器 | 所在代码块 | 只能声明一次 | 无,一定要在声明后使用 |
const 声明常量 | 值不可改变,一旦声明必须初始化 | 所在代码块 | 只能声明一次 | 无,一定要在声明后使用 |
function | ||||
class | ||||
import |
暂时性死区(temporal dead zone,简称 TDZ):
如果区块中存在let和const命令,这个区块对这些命令声明的变量,从一开始就形成了封闭作用域。凡是在声明之前就使用这些变量,就会报错。
//父作用域有声明,可在子作用域里用
//父子作用域都有声明,子作用域就用子作用域的声明
for (let i = 0; i < 3; i++) //父作用域
{ //子作用域
let i = 'abc';
console.log(i);
}
// abc
// abc
// abc
//例子
var a = [];
for (var i = 0; i < 3; i++) {
a[i] = function () {
console.log(i);
};
a[i]();
}
console.log('a[1]输出:')
a[1]();
//结果
0
1
2
a[1]输出:
3//var,全局变量只有一个i,数组a的每个函数的i都指向一个i,每循环一次i就+1
1//let,局部变量,每次循环i都是一个新变量,数组a的每个函数的i都指向不同i,
//JavaScript 引擎内部会记住上一轮循环的值,初始化本轮的变量i时,就在上一轮循环的基础上进行计算
for (var i = 0; i < 10; i++) {
setTimeout(function(){
console.log(i);
})
}
// 输出十个 10
for (let j = 0; j < 10; j++) {
setTimeout(function(){
console.log(j);
})
}
// 输出 0123456789
//暂时性死区
//console.log(a); //ReferenceError: a is not defined,声明变量 a 之前,a 不存在
let a = "apple";
console.log(b); //undefined,当脚本开始运行的时候,b 已经存在了,
var b = "banana";
解构赋值
- 解构遵循匹配模式:只要等号两边的模式相同,左边的变量就会被赋予对应的值
- 解构赋值规则:只要等号右边的值不是对象或数组,就先将其转为对象
- 解构不成功时 变量的值等于undefined
- 不完全解构:左边只匹配一部分右边
- 默认值:
- 解构默认值生效条件:属性值严格等于undefined
- undefined和null无法转为对象,因此无法进行解构
- 如果默认值是一个表达式,只有在需要该表达式的时候,才会求值
let [x = f()] = [1];
因为x能取到值,所以函数f根本不会执行 let [x = y, y = 1] = [];
// ReferenceError: y is not defined
x用y做默认值时,y还没有声明。
表示
- 数组解构:
const [x, y] = [1, 2]
- 规则:等号的右边是数组(或严格地说,具有Iterator接口)
- 默认:
const [x, y = 2] = [1]
- 对象解构:
const { x, y } = { x: 1, y: 2 }
- 变量必须与属性同名,才能取到正确的值,顺序不对没事
- 真正被赋值的是属性后面的变量,而不是属性。
- 默认:
let { foo: foo, bar: bar } = { foo: 'aaa', bar: 'bbb' };
- 改名:const { x, y: z } = { x: 1, y: 2 }
- 字符串解构:
const [a, b, c, d, e] = "hello"
- 数值解构:
const { toString: s } = 123
- 布尔解构:
const { toString: b } = true
- 函数参数解构:
- 数组解构:
function Func([x = 0, y = 1]) {}
- 对象解构:
function Func({ x = 0, y = 1 } = {}) {}
- 数组解构:
应用场景
- 交换变量值:[x, y] = [y, x]
- 返回函数多个值:const [x, y, z] = Func()
- 定义函数参数:Func([1, 2])
- 提取JSON数据:const { name, version } = packageJson
- 定义函数参数默认值:function Func({ x = 1, y = 2 } = {}) {}
- 遍历Map结构:for (let [k, v] of Map) {}
- 输入模块指定属性和方法:const { readFile, writeFile } = require(“fs”)
内置对象
扩展
字符串扩展
//JavaScript 共有 6 种方法可以表示一个字符
'\z' === 'z' // true
'\172' === 'z' // true
'\x7A' === 'z' // true
'\u007A' === 'z' // true
'\u{7A}' === 'z' // true
//JavaScript 字符串允许直接输入字符,以及输入字符的转义形式
'中' === '\u4e2d' // true
- Unicode表示法:大括号包含表示Unicode字符(\u{0xXX}或\u{0XXX})
- 下面不能在字符串里面直接使用,只能使用转义形式。
- U+005C:反斜杠(reverse solidus)
- U+000D:回车(carriage return)
- U+2028:行分隔符(line separator) 正则表达式依然不允许直接输入该字符
- U+2029:段分隔符(paragraph separator) 正则表达式依然不允许直接输入该字符
- U+000A:换行符(line feed)
- 下面不能在字符串里面直接使用,只能使用转义形式。
- 字符串遍历:可通过for-of遍历字符串
- 字符串模板:可单行可多行可插入变量的增强版字符串 反引号 `
- 模板字符串中嵌入变量,需要将变量名写在
${ }
之中 - 模板字符串还能嵌套
- 模板字符串中嵌入变量,需要将变量名写在
- 标签模板:模板字符串紧跟在一个函数名后面,该函数将被调用来处理这个模板字符串。
String.raw()
:返回把字符串所有变量替换且对斜杠进行转义(即斜杠前面再加一个斜杠)的结果String.fromCodePoint()
:返回码点对应字符,但不能识别码点大于0xFFFF的字符。codePointAt()
:返回字符对应码点(String.fromCodePoint()的逆操作),能够正确处理4个字节储存的字符(Unicode 码点大于0xFFFF的字符)normalize()
:把字符的不同表示方法统一为同样形式,返回新字符串(Unicode正规化),比如两个字符合成一个字符,像拼音有音调repeat()
:把字符串重复n次,返回新字符串'x'.repeat(3); // "xxx"
matchAll()
:返回正则表达式在字符串的所有匹配at()
:接受一个整数作为参数,返回参数指定位置的字符,支持负索引(即倒数的位置)
replace()
:只能替换第一个匹配replaceAll()
:可以一次性替换所有匹配。
padStart()
:如果某个字符串不够指定长度,在头部补全'x'.padStart(4, 'ab') // 'abax'
padEnd()
:如果某个字符串不够指定长度,在尾部补全'x'.padEnd(5, 'ab') // 'xabab'
trim()
:消除字符串空格trimStart()
:消除字符串头部的空格,trimEnd()
:消除尾部的空格
indexOf()
:确定一个字符串是否包含在另一个字符串中includes()
:是否存在指定字符串startsWith()
:是否存在字符串头部指定字符串endsWith()
:是否存在字符串尾部指定字符串
//如果使用模板字符串表示多行字符串,所有的空格和缩进都会被保留在输出之中。
$('#list').html(`
<ul>
<li>first</li>
<li>second</li>
</ul>
`);
//上面代码中,所有模板字符串的空格和换行,都是被保留的,
//比如<ul>标签前面会有一个换行。如果你不想要这个换行,可以使用trim方法消除它。
$('#list').html(`
<ul>
<li>first</li>
<li>second</li>
</ul>
`.trim());
数值扩展
- 二进制表示法:0b或0B开头表示二进制(0bXX或0BXX)
- 八进制表示法:0o或0O开头表示二进制(0oXX或0OXX)
- 数值分隔符:下划线(_)作为分隔符
- 不能放在数值的最前面(leading)或最后面(trailing)。
- 不能两个或两个以上的分隔符连在一起。
- 小数点的前后不能有分隔符。
- 科学计数法里面,表示指数的e或E前后不能有分隔符。
- 分隔符不能紧跟着进制的前缀0b、0B、0o、0O、0x、0X
- Number.EPSILON:数值最小精度
- Number.MIN_SAFE_INTEGER:最小安全数值(-2^53)
- Number.MAX_SAFE_INTEGER:最大安全数值(2^53)
- Number.parseInt():返回转换值的整数部分
- Number.parseFloat():返回转换值的浮点数部分
- Number.isFinite():是否为有限数值
- Number.isNaN():是否为NaN
- Number.isInteger():是否为整数
- Number.isSafeInteger():是否在数值安全范围内
- Math.trunc():返回数值整数部分
- Math.sign():返回数值类型(正数1、负数-1、零0)
- Math.cbrt():返回数值立方根
- Math.clz32():返回数值的32位无符号整数形式
- Math.imul():返回两个数值相乘
- Math.fround():返回数值的32位单精度浮点数形式
- Math.hypot():返回所有数值平方和的平方根
- Math.expm1():返回e^n - 1
- Math.log1p():返回1 + n的自然对数(Math.log(1 + n))
- Math.log10():返回以10为底的n的对数
- Math.log2():返回以2为底的n的对数
- Math.sinh():返回n的双曲正弦
- Math.cosh():返回n的双曲余弦
- Math.tanh():返回n的双曲正切
- Math.asinh():返回n的反双曲正弦
- Math.acosh():返回n的反双曲余弦
- Math.atanh():返回n的反双曲正切
对象扩展
- 简洁表示法:直接写入变量和函数作为对象的属性和方法({ prop, method() {} })
- 属性名表达式:字面量定义对象时使用[]定义键([prop],不能与上同时使用)
- 方法的name属性:返回方法函数名
- 取值函数(getter)和存值函数(setter):get/set 函数名(属性的描述对象在get和set上)
- bind返回的函数:bound 函数名
- Function构造函数返回的函数实例:anonymous
- 属性的可枚举性和遍历:描述对象的enumerable
- super关键字:指向当前对象的原型对象(只能用在对象的简写方法中method() {})
- Object.is():对比两值是否相等
- Object.assign():合并对象(浅拷贝),返回原对象
- Object.getPrototypeOf():返回对象的原型对象
- Object.setPrototypeOf():设置对象的原型对象
__proto__
:返回或设置对象的原型对象
属性遍历
- 描述:自身、可继承、可枚举、非枚举、Symbol
- 遍历
- for-in:遍历对象自身可继承可枚举属性
- Object.keys():返回对象自身可枚举属性键组成的数组
- Object.getOwnPropertyNames():返回对象自身非Symbol属性键组成的数组
- Object.getOwnPropertySymbols():返回对象自身Symbol属性键组成的数组
- Reflect.ownKeys():返回对象自身全部属性键组成的数组
- 规则
- 首先遍历所有数值键,按照数值升序排列
- 其次遍历所有字符串键,按照加入时间升序排列
- 最后遍历所有Symbol键,按照加入时间升序排列
数组扩展
- 扩展运算符
...
:转换数组为用逗号分隔的参数序列([…arr],相当于rest/spread参数的逆运算)
内部使用for…of循环,所以也可以用于 Set 结构 - Array.from():转换具有Iterator接口的数据结构为真正数组,返回新数组
- 类数组对象:包含length的对象、Arguments对象、NodeList对象
- 可遍历对象:String、Set结构、Map结构、Generator函数
- Array.of():转换一组值为真正数组,返回新数组
- copyWithin():把指定位置的成员复制到其他位置,返回原数组
- find():返回第一个符合条件的成员
- findIndex():返回第一个符合条件的成员索引值
- fill():根据指定值填充整个数组,返回原数组
- keys():返回以索引值为遍历器的对象
- values():返回以属性值为遍历器的对象
- entries():返回以索引值和属性值为遍历器的对象
- 数组空位:ES6明确将数组空位转为undefined(空位处理规不一,建议避免出现)
扩展应用
- 克隆数组:const arr = […arr1]
- 合并数组:const arr = […arr1, …arr2]
- 拼接数组:arr.push(…arr1)
- 代替apply:Math.max.apply(null, [x, y]) => Math.max(…[x, y])
- 转换字符串为数组:[…“hello”]
- 转换类数组对象为数组:[…Arguments, …NodeList]
- 转换可遍历对象为数组:[…String, …Set, …Map, …Generator]
- 与数组解构赋值结合:const [x, …rest/spread] = [1, 2, 3]
- 计算Unicode字符长度:Array.from(“hello”).length => […“hello”].length
重点难点
使用keys()、values()、entries()返回的遍历器对象,可用for-of自动遍历或next()手动遍历
函数扩展
允许函数的最后一个参数有尾逗号
有时函数也可以写成:methods() {fun1(){}, fun2(){},}
或 methods : fun1(){}, fun2(){},
- 参数默认值:为函数参数指定默认值
- 形式:function Func(x = 1, y = 2) {}
- 参数赋值:惰性求值(函数调用后才求值)
- 参数位置:尾参数
- 参数作用域:函数作用域
- 声明方式:默认声明,不能用const或let再次声明
- length:返回没有指定默认值的参数个数
- 与解构赋值默认值结合:function Func({ x = 1, y = 2 } = {}) {}
- 应用
- 指定某个参数不得省略,省略即抛出错误:function Func(x = throwMissing()) {}
- 将参数默认值设为undefined,表明此参数可省略:Func(undefined, 1)
- rest/spread参数(…):返回函数多余参数
- 形式:以数组的形式存在,之后不能再有其他参数
- 作用:代替Arguments对象
- length:返回没有指定默认值的参数个数但不包括rest/spread参数
- 严格模式:在严格条件下运行JS
- 应用:只要函数参数使用默认值、解构赋值、扩展运算符,那么函数内部就不能显式设定为严格模式
- name属性:返回函数的函数名
- 将匿名函数赋值给变量:空字符串(ES5)、变量名(ES6)
- 将具名函数赋值给变量:函数名(ES5和ES6)
- bind返回的函数:bound 函数名(ES5和ES6)
- Function构造函数返回的函数实例:anonymous(ES5和ES6)
- 箭头函数(=>):函数简写
- 无参数:() => {}
- 单个参数:x => {}
- 多个参数:(x, y) => {}
- 解构参数:({x, y}) => {}
- 嵌套使用:部署管道机制
- this指向固定化
- 并非因为内部有绑定this的机制,而是根本没有自己的this,导致内部的this就是外层代码块的this
- 因为没有this,因此不能用作构造函数
- 尾调用优化:只保留内层函数的调用帧
- 尾调用
- 定义:某个函数的最后一步是调用另一个函数
- 形式:function f(x) { return g(x); }
- 尾递归
- 定义:函数尾调用自身
- 作用:只要使用尾递归就不会发生栈溢出,相对节省内存
- 实现:把所有用到的内部变量改写成函数的参数并使用参数默认值
- 尾调用
箭头函数误区
- 函数体内的this是定义时所在的对象而不是使用时所在的对象
- 可让this指向固定化,这种特性很有利于封装回调函数
- 不可当作构造函数,因此箭头函数不可使用new命令
- 不可使用yield命令,因此箭头函数不能用作Generator函数
- 不可使用Arguments对象,此对象在函数体内不存在(可用rest/spread参数代替)
- 返回对象时必须在对象外面加上括号
正则扩展
- 变更RegExp构造函数入参:允许首参数为正则对象,尾参数为正则修饰符(返回的正则表达式会忽略原正则表达式的修饰符)
- 正则方法调用变更:字符串对象的match()、replace()、search()、split()内部调用转为调用RegExp实例对应的RegExp.prototype[Symbol.方法]
- u修饰符:Unicode模式修饰符,正确处理大于\uFFFF的Unicode字符
- 点字符(.)
- Unicode表示法
- 量词
- 预定义模式
- i修饰符
- 转义
- y修饰符:粘连修饰符,确保匹配必须从剩余的第一个位置开始全局匹配(与g修饰符作用类似)
- unicode:是否设置u修饰符
- sticky:是否设置y修饰符
- flags:返回正则表达式的修饰符
y修饰符隐含头部匹配标志^
单单一个y修饰符对match()只能返回第一个匹配,必须与g修饰符联用才能返回所有匹配
运算符扩展
Symbol 数据类型
ES6 Symbol
ES6 数据类型除了 Number 、 String 、 Boolean 、 Object、 null 和 undefined ,还新增了 Symbol 。
- Symbol,表示独一无二的值,最大的用法是用来定义对象的唯一属性名。
- Symbol 函数栈不能用 new 命令,因为 Symbol 是原始数据类型,不是对象。
- 使用场景:由于每一个 Symbol 的值都是不相等的,所以 Symbol 作为对象的属性名,可以保证属性不重名。
- Symbol 作为对象属性名时不能用.运算符,要用方括号。因为.运算符后面是字符串,所以取到的是字符串 sy 属性,而不是 Symbol 值 sy 属性。
- Symbol 值作为属性名时,该属性是公有属性不是私有属性,可以在类的外部访问。但是不会出现在 for…in 、 for…of 的循环中,也不会被 Object.keys() 、 Object.getOwnPropertyNames() 返回。如果要读取到一个对象的 Symbol 属性,可以通过 Object.getOwnPropertySymbols() 和 Reflect.ownKeys() 取到。
let sy1 = Symbol("kk");
let sy;
sy === sy1;
console.log("sy1 = ",sy1)
console.log("sy = ",sy)
let sy = Symbol("key1");
// 写法1
let syObject = {};
syObject[sy] = "kk";
console.log(syObject); // {Symbol(key1): "kk"}
// 写法2
let syObject = {
[sy]: "kk"
};
console.log(syObject); // {Symbol(key1): "kk"}
// 写法3
let syObject = {};
Object.defineProperty(syObject, sy, {value: "kk"});
console.log(syObject); // {Symbol(key1): "kk"}
//
let syObject = {};
syObject[sy] = "kk";
syObject[sy]; // "kk"
syObject.sy; // undefined
Symbol.for()
,首先会在全局搜索被登记的 Symbol 中是否有该字符串参数作为名称的 Symbol 值,如果有即返回该 Symbol 值,若没有则新建并返回一个以该字符串参数为名称的 Symbol 值,并登记在全局环境中供搜索。
Symbol.keyFor()
返回一个已登记的 Symbol 类型值的 key ,用来检测该字符串参数作为名称的 Symbol 值是否已被登记。
Set 数据结构
- ES6 提供了新的数据结构 Set。它类似于数组,但是成员的值都是唯一的,没有重复的值。(就像数学中的集合)
- Set本身是一个构造函数,用来生成 Set 数据结构。
- 可以去除数组重复成员
- 可以去除字符串里面的重复字符
- 向 Set 加入值的时候,不会发生类型转换,所以5和"5"是两个不同的值。
- 两个空对象不相等
Set 结构的实例的属性:
- constructor:构造函数,返回Set
- size:返回实例成员总数
方法:
- add():添加值,返回实例
- delete():删除值,返回布尔
- has():检查值,返回布尔
- clear():清除所有成员
遍历方法:Set的遍历顺序就是插入顺序
- keys():返回以属性值为遍历器的对象
- values():返回以属性值为遍历器的对象
- entries():返回以属性值和属性值为遍历器的对象
- forEach():使用回调函数遍历每个成员
- 扩展运算符也能遍历
let arr = [... set]
应用场景
- 去重字符串:
[...new Set(str)].join("")
- 去重数组:
[...new Set(arr)]
或Array.from(new Set(arr))
- 集合数组:
- 声明:const a = new Set(arr1)、const b = new Set(arr2)
- 并集:new Set([…a, …b])
- 交集:new Set([…a].filter(v => b.has(v)))
- 差集:new Set([…a].filter(v => !b.has(v)))
- 映射集合:
- 声明:let set = new Set(arr)
- 映射:set = new Set([…set].map(v => v * 2))或set = new Set(Array.from(set, v => v * 2))
Map 数据结构
ES6 Map 与 Set
Map 对象保存键值对。任何值(对象或者原始值) 都可以作为一个键或一个值。
- Map 和 Object 的区别
- 一个 Object 的键只能是字符串或者 Symbols,但一个 Map 的键可以是任意值。
- Map 中的键值是有序的(FIFO 原则),而添加到对象中的键则不是。
- Map 的键值对个数可以从 size 属性获取,而 Object 的键值对个数只能手动计算。
- Object 都有自己的原型,原型链上的键名有可能和你自己在对象上的设置的键名产生冲突。
属性:
- constructor:构造函数,返回Map
- size:返回实例成员总数
方法: - get():返回键值对
- set():添加键值对,返回实例
- delete():删除键值对,返回布尔
- has():检查键值对,返回布尔
- clear():清除所有成员
- keys():返回以键为遍历器的对象
- values():返回以值为遍历器的对象
- entries():返回以键和值为遍历器的对象
- forEach():使用回调函数遍历每个成员
注意:
- 对同一个键多次赋值,后面的值将覆盖前面的值
- 对同一个对象的引用,被视为一个键
- 对同样值的两个实例,被视为两个键
- 键跟内存地址绑定,只要内存地址不一样就视为两个键
Proxy 类
Proxy
ES6的代理模式 | Proxy
var proxy = new Proxy(target, handler);
在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。
target
:所要拦截的目标对象
handler
:也是一个对象,用来定制拦截行为方法
Proxy.revocable()
:返回可取消的Proxy实例(返回{ proxy, revoke },通过revoke()取消代理)
拦截方式
get()
:拦截对象属性读取
set()
:拦截对象属性设置,返回布尔
has()
:拦截对象属性检查k
in
obj
,返回布尔
deleteProperty()
:拦截对象属性删除delete obj[k]
,返回布尔
defineProperty()
:拦截对象属性定义Object.defineProperty()
、Object.defineProperties()
,返回布尔
ownKeys()
:拦截对象属性遍历for-in
、Object.keys()
、Object.getOwnPropertyNames()
、Object.getOwnPropertySymbols()
,返回数组
getOwnPropertyDescriptor()
:拦截对象属性描述读取Object.getOwnPropertyDescriptor()
,返回对象
getPrototypeOf()
:拦截对象原型读取instanceof
、Object.getPrototypeOf()
、Object.prototype.__proto__
、Object.prototype.isPrototypeOf()
、Reflect.getPrototypeOf()
,返回对象
setPrototypeOf()
:拦截对象原型设置Object.setPrototypeOf()
,返回布尔
isExtensible()
:拦截对象是否可扩展读取Object.isExtensible()
,返回布尔
preventExtensions()
:拦截对象不可扩展设置Object.preventExtensions()
,返回布尔
apply()
:拦截Proxy实例作为函数调用proxy()
、proxy.apply()
、proxy.call()
construct()
:拦截Proxy实例作为构造函数调用new proxy()
应用场景
Proxy.revocable()
:不允许直接访问对象,必须通过代理访问,一旦访问结束就收回代理权不允许再次访问
get()
:读取未知属性报错、读取数组负数索引的值、封装链式操作、生成DOM嵌套节点
set()
:数据绑定(Vue数据绑定实现原理)、确保属性值设置符合要求、防止内部属性被外部读写
has()
:隐藏内部属性不被发现、排除不符合属性条件的对象
deleteProperty()
:保护内部属性不被删除
defineProperty()
:阻止属性被外部定义
ownKeys()
:保护内部属性不被遍历
Reflect 类
Reflect 可以用于获取目标对象的行为,它与 Object 类似,但是更易读,为操作对象提供了一种更优雅的方式。它的方法与 Proxy 是对应的。
Reflect 对象使用函数的方式实现了 Object 的命令式操作。
定义:保持Object方法的默认行为
方法:
get():返回对象属性
set():设置对象属性,返回布尔
has():检查对象属性,返回布尔
deleteProperty():删除对象属性,返回布尔
defineProperty():定义对象属性,返回布尔
ownKeys():遍历对象属性,返回数组(Object.getOwnPropertyNames()+Object.getOwnPropertySymbols())
getOwnPropertyDescriptor():返回对象属性描述,返回对象
getPrototypeOf():返回对象原型,返回对象
setPrototypeOf():设置对象原型,返回布尔
isExtensible():返回对象是否可扩展,返回布尔
preventExtensions():设置对象不可扩展,返回布尔
apply():绑定this后执行指定函数
construct():调用构造函数创建实例
设计目的:
将Object属于语言内部的方法放到Reflect上
将某些Object方法报错情况改成返回false
让Object操作变成函数行为
Proxy与Reflect相辅相成
废弃方法:
Object.defineProperty() => Reflect.defineProperty()
Object.getOwnPropertyDescriptor() => Reflect.getOwnPropertyDescriptor()
重点难点:
Proxy方法和Reflect方法一一对应
Proxy和Reflect联合使用,前者负责拦截赋值操作,后者负责完成赋值操作
语句与运算
Class 类
类:
- 可以为匿名或命名
- 不可重复声明
- 类定义不会被提升,必须在访问前对类进行定义
- 类也可以使用表达式的形式定义
const MyClass = class Me { }
- 类必须使用new调用,函数那样调用Class,将会报错
- new.target属性,一般用在构造函数之中,返回new命令作用于的那个构造函数。
- Class 内部调用new.target,返回当前 Class
- 子类继承父类时,new.target会返回子类
属性:
prototype:类的所有方法都定义在类的prototype属性上面
类名.prototype
name:返回紧跟在class关键字后面的类名(存在时)。
类名.name
原型对象的属性:定义在类上
实例对象的属性:定义在this对象上,实例属性除了定义在constructor()方法里面的this上面,也可以定义在类的最顶层
类的属性名,可以采用表达式。
方法:
- 类中方法不需要 function 关键字。
- 类中方法与方法之间不需要逗号分隔,加了会报错
- ES6中,类的内部所有定义的方法,都是不可枚举的(non-enumerable),ES5的写法可以
- 向类添加方法:
Object.assign(Point.prototype, { toString(){}, toValue(){} });
- 默认方法/构造函数:
constructor()
- 在“类”的内部可以使用get和set关键字,对某个属性设置存值函数和取值函数,拦截该属性的存取行为。存值函数和取值函数是设置在属性的 Descriptor 对象上的
- Generator函数:如果某个方法之前加上星号(*),就表示该方法是一个 Generator 函数
静态属性:加上
static
关键字静态方法:类相当于实例的原型,所有在类中定义的方法,都会被实例继承。如果在一个方法前,加上static关键字,就表示该方法不会被实例继承,而是直接通过类来调用,这就称为“静态方法”
静态块:允许在类的内部设置一个代码块,在类生成时运行一次,主要作用是对静态属性进行初始化。
私有方法:
私有属性:
- 在属性前加#
- V8 引擎改进了in运算符,使它也可以用来判断私有属性。
类的继承:class Child extends Father { … }
- 子类 constructor 方法中必须有 super ,且必须出现在 this 之前。
- 调用父类构造函数,只能出现在子类的构造函数。
class Child extends Father {
constructor(){
super();
}
}
//声明类
class Point {
//构造函数
constructor(a) {
this.a = a;
}
toString() {...}
toValue() {...}
static classMethod() {}
}
Point.classMethod()
// 等同于
Point.prototype = {
constructor(a) {...},
toString() {},
toValue() {},
...
};
//使用
let p = new Point(6)
//覆盖方法 / 初始化时添加方法
Point.prototype={
//methods
}
//添加方法
Object.assign(Point.prototype,{
//methods
})
Iterator 遍历器
定义:为各种不同的数据结构提供统一的访问机制
原理:创建一个指针指向首个成员,按照次序使用next()指向下一个成员,直接到结束位置(数据结构只要部署Iterator接口就可完成遍历操作)
作用:
- 为各种数据结构提供一个统一的简便的访问接口
- 使得数据结构成员能够按某种次序排列
- ES6创造了新的遍历命令for-of,Iterator接口主要供for-of消费
形式:for-of(自动去寻找Iterator接口)
数据结构:
- 集合:Array、Object、Set、Map
- 原生具备接口的数据结构:String、Array、Set、Map、TypedArray、Arguments、NodeList
部署:默认部署在Symbol.iterator(具备此属性被认为可遍历的iterable)
遍历器对象:
- next():下一步操作,返回{ done, value }(必须部署)
- return():for-of提前退出调用,返回{ done: true }
- throw():不使用,配合Generator函数使用
ForOf循环:
定义:调用Iterator接口产生遍历器对象(for-of内部调用数据结构的Symbol.iterator())
遍历字符串:for-in获取索引,for-of获取值(可识别32位UTF-16字符)
遍历数组:for-in获取索引,for-of获取值
遍历对象:for-in获取键,for-of需自行部署
遍历Set:for-of获取值 => for (const v of set)
遍历Map:for-of获取键值对 => for (const [k, v] of map)
遍历类数组:包含length的对象、Arguments对象、NodeList对象(无Iterator接口的类数组可用Array.from()转换)
计算生成数据结构:Array、Set、Map
keys():返回遍历器对象,遍历所有的键
values():返回遍历器对象,遍历所有的值
entries():返回遍历器对象,遍历所有的键值对
与for-in区别:
- 有着同for-in一样的简洁语法,但没有for-in那些缺点、
- 不同于forEach(),它可与break、continue和return配合使用
- 提供遍历所有数据结构的统一操作接口
应用场景:
- 写具有Iterator接口的数据结构的Symbol.iterator
- 解构赋值:对Set进行结构
- 扩展运算符:将部署Iterator接口的数据结构转为数组
- yield*:yield*后跟一个可遍历的数据结构,会调用其遍历器接口
- 接受数组作为参数的函数:for-of、Array.from()、new Set()、new WeakSet()、new Map()、new WeakMap()、Promise.all()、Promise.race()
Module 模块
- ES6 的模块化分为导出(export) 与导入(import)两个模块。
- 模块导入导出各种类型的变量,如字符串,数值,函数,类。
- 导出的函数声明与类声明必须要有名称(export default 命令另外考虑)。
- 不仅能导出声明还能导出引用(例如函数)。
- export 命令可以出现在模块的任何位置,但必需处于模块顶层。
- import 命令会提升到整个模块的头部,首先执行。
- as 取别名
- import 命令的特点
- 只读属性:不允许在加载模块的脚本里面,改写接口的引用指向,即可以改写 import 变量类型为对象的属性值,不能改写 import 变量类型为基本类型的值。
- 单例模式:多次重复执行同一句 import 语句,那么只会执行一次,而不会执行多次。import 同一模块,声明不同接口引用,会声明对应变量,但只执行一次 import 。
- 静态执行特性:import 是静态执行,所以不能使用表达式和变量
- export default 命令
- 在一个文件或模块中,export、import 可以有多个,export default 仅有一个。
- export default 中的 default 是对应的导出接口变量。
- 通过 export 方式导出,在导入时要加{ },export default 则不需要。
- export default 向外暴露的成员,可以使用任意变量来接收。
- export 与 import 可以在同一模块使用,使用特点:
- 可以将导出接口改名,包括 default。
- 复合使用 export 与 import ,也可以导出全部,当前模块导出的接口会覆盖继承导出的。
/*-----export [test.js]-----*/
let myName = "Tom";
let myAge = 20;
let myfn = function(){
return "My name is" + myName + "! I'm '" + myAge + "years old."
}
let myClass = class myClass {
static a = "yeah!";
}
export { myName, myAge, myfn, myClass }
/*-----import [xxx.js]-----*/
import { myName, myAge, myfn, myClass } from "./test.js";
console.log(myfn());// My name is Tom! I'm 20 years old.
console.log(myAge);// 20
console.log(myName);// Tom
console.log(myClass.a );// yeah!
//导入的变量名,须和导出的接口名称相同,即顺序可以不一致。
/*-----export [test.js]-----*/
let myName = "Tom";
export { myName as exportName }
/*-----import [xxx.js]-----*/
import { exportName } from "./test.js";
console.log(exportName);// Tom
//使用 as 重新定义导出的接口名称,隐藏模块内部的变量
/*-----export [test1.js]-----*/
let myName = "Tom";
export { myName }
/*-----export [test2.js]-----*/
let myName = "Jerry";
export { myName }
/*-----import [xxx.js]-----*/
import { myName as name1 } from "./test1.js";
import { myName as name2 } from "./test2.js";
console.log(name1);// Tom
console.log(name2);// Jerry
//可以改写 import 变量类型为对象的属性值,不能改写 import 变量类型为基本类型的值。
import {a} from "./xxx.js"
a = {}; // error
import {a} from "./xxx.js"
a.foo = "hello"; // a = { foo : 'hello' }
异步编程
Promise 类
参考资料:
JavaScript Promise
异步函数Async(es2017)
① 概念:
Promise 是一个 ECMAScript 6 提供的类,目的是更加优雅地书写复杂的异步任务。
② 状态:
Promise 对象代表一个异步操作,有三种状态:
- pending(进行中)
- resolved(已完成,又称 Fulfilled)
- rejected(已失败)
执行回调里的 resolve();
这个 promise 就被标记为 resolverd
③ 构造函数/起始函数:
new Promise(function(resolve, reject){...})
Promise 构造函数只有一个参数,是一个函数,这个函数在构造之后会直接被异步运行,所以我们称之为起始函数。起始函数包含两个参数 resolve 和 reject 都是函数:
resolve()
代表一切正常。reject()
是出现异常时所调用的。
throw "Some error";
或者reject("Some error")
resolve 和 reject 的作用域只有起始函数,不包括 then 以及其他序列
resolve 和 reject 并不能够使起始函数停止运行,别忘了 return。
//例子
new Promise(function (resolve, reject) {
setTimeout(function () {
console.log("First");
resolve();
}, 1000);
})
④ 方法:
Promise 类有三个方法,这三个方法的参数都是函数,
.then()
可以将参数中的函数添加到当前 Promise 的正常执行序列,传入的函数会按顺序依次执行,有任何异常都会直接跳到 catch 序列.catch()
则是设定 Promise 的异常处理序列,.finally()
是在 Promise 执行的最后一定会执行的序列。
resolve() 中可以放置一个参数向下一个 then 传递一个值
then 中的函数返回一个值,该值传递给下一个 then。
then 中的函数返回一个 Promise 对象,那么下一个 then 将相当于对这个返回的 Promise 进行操作。
1.异步函数执行的结果总是一个promise,这个promise在异步函数开始执行的时候被创建。
2.然后函数体被执行,执行可能会通过return或throw永久完成,或者通过await临时完成(这种情况,之后会继续执行)。
3.promise被返回。
当执行异步函数体的时候,return x将会resolve(x),throw err将会reject(err),promise的完成是异步的。换句话说,then(),catch()中的回调函数总是在当前代码完成之后执行。
异步的方法函数最后一个参数为回调函数
回调函数的第一个参数包含了错误信息(error)
//例子
new Promise(function (resolve, reject) {
console.log(1111);
resolve(2222);
}).then(function (value) {
console.log(value);
return 3333;
}).then(function (value) {
console.log(value);
throw "An error";
}).catch(function (err) {
console.log(err);
});
//结果
1111
2222
3333
An error
//例子
//分三次输出字符串,第一次间隔 1 秒,第二次间隔 4 秒,第三次间隔 3 秒
//1
setTimeout(function () {
console.log("First");
setTimeout(function () {
console.log("Second");
setTimeout(function () {
console.log("Third");
}, 3000);
}, 4000);
}, 1000);
//2
new Promise(function (resolve, reject) {
setTimeout(function () {
console.log("First");
resolve();
}, 1000);
}).then(function () {
return new Promise(function (resolve, reject) {
setTimeout(function () {
console.log("Second");
resolve();
}, 4000);
});
}).then(function () {
setTimeout(function () {
console.log("Third");
}, 3000);
});
//3
function print(delay, message) {
return new Promise(function (resolve, reject) {
setTimeout(function () {
console.log(message);
resolve();
}, delay);
});
}
print(1000, "First").then(function () {
return print(4000, "Second");
}).then(function () {
print(3000, "Third");
});
//4
function print(delay, message) {
return new Promise(function (resolve, reject) {
setTimeout(function () {
console.log(message);
resolve();
}, delay);
});
}
async function asyncFunc() {
await print(1000, "First");
await print(4000, "Second");
await print(3000, "Third");
}
asyncFunc();
Generator 函数
- Generator 有两个区分于普通函数的部分:
- 在 function 后面,函数名之前有个 * ;
- 函数内部有 yield 表达式。
- Generator 函数不会像普通函数一样立即执行,而是返回一个指向内部状态对象的指针,所以要调用遍历器对象Iterator 的 next 方法,指针就会从函数头部或者上一次停下来的地方开始执行。
- 通过 yield 关键字,把函数的执行流挂起,为改变执行流程提供了可能,从而为异步编程提供解决方案。
- next 方法:
- 当 next 方法不传入参数的时候,yield 表达式的返回值是 undefined 。
- 当 next 传入参数的时候,该参数会作为上一步yield的返回值。
- return 方法
- return 方法返回给定值,并结束遍历 Generator 函数。
- return 方法提供参数时,返回该参数;不提供参数时,返回 undefined 。
- throw 方法
- throw 方法可以再 Generator 函数体外面抛出异常,再函数体内部捕获。
- yield* 表达式
- yield* 表达式表示 yield 返回一个遍历器对象,用于在 Generator 函数内部,调用另一个 Generator 函数
//Generator 函数
function* sendParameter(){
console.log("start");
var x = yield '2';
console.log("one:" + x);
var y = yield '3';
console.log("two:" + y);
console.log("total:" + (x + y));
}
//next不传参
var sendp1 = sendParameter();
sendp1.next();
// start
// {value: "2", done: false}
sendp1.next();
// one:undefined
// {value: "3", done: false}
sendp1.next();
// two:undefined
// total:NaN
// {value: undefined, done: true}
//next传参
var sendp2 = sendParameter();
sendp2.next(10);
// start
// {value: "2", done: false}
sendp2.next(20);
// one:20
// {value: "3", done: false}
sendp2.next(30);
// two:30
// total:50
// {value: undefined, done: true}
function* foo(){
yield 1;
yield 2;
yield 3;
}
var f = foo();
f.next();
// {value: 1, done: false}
f.return("foo");
// {value: "foo", done: true}
f.next();
// {value: undefined, done: true}
var g = function* () {
try {
yield;
} catch (e) {
console.log('catch inner', e);
}
};
var i = g();
i.next();
try {
i.throw('a');
i.throw('b');
} catch (e) {
console.log('catch outside', e);
}
// catch inner a
// catch outside b
function* callee() {
console.log('callee: ' + (yield));
}
function* caller() {
while (true) {
yield* callee();
}
}
const callerObj = caller();
callerObj.next();
// {value: undefined, done: false}
callerObj.next("a");
// callee: a
// {value: undefined, done: false}
callerObj.next("b");
// callee: b
// {value: undefined, done: false}
// 等同于
function* caller() {
while (true) {
for (var value of callee) {
yield value;
}
}
}
Async 函数
ES6 async 函数
async/await 异步函数
异步函数Async(es2017)
async function name([param[, param[, ... param]]]) { statements }
- 函数声明:async function
- 参数:
- name: 函数名称。
- param: 要传递给函数的参数的名称。
- statements: 函数体语句。
- 返回值:async 函数返回一个 Promise 对象,可以使用 then 方法添加回调函数。
[return_value] = await expression;
- await 操作符只能在异步函数 async function 内部使用
- 参数:
expression
:一个 Promise 对象或者任何要等待的值。 - 返回值:返回 Promise 对象的处理结果。
- 如果等待的不是 Promise 对象,则返回该值本身。
- 如果等待的是 Promise 对象,await 将等待 Promise 正常处理完成并返回其处理结果
//定义函数,该函数返回一个Promise对象
function testAwait (x) {
return new Promise(resolve => {
setTimeout(() => {
resolve(x);
}, 2000);
});
}
//定义async异步函数 //
async function helloAsync() {
var x = await testAwait ("hello world");
console.log(x);
}
//调用函数
helloAsync ();
//
//结果
//hello world
//定义普通函数
function testAwait(){
console.log("testAwait");
}
//定义async异步函数
async function helloAsync(){
await testAwait();
console.log("helloAsync");
}
//调用函数
helloAsync();
//
//结果
// testAwait
// helloAsync