目录
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():拦截对象属性检查kinobj,返回布尔
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属性上面
类名.prototypename:返回紧跟在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