ES6
let 和 const
-
let 块级作用域,不存在变量提升,暂时性死区,不允许重复声明
- for 循环有一个特别之处,就是设置循环变量的那部分是一个父作用域,而循环体内部是一个单独的子作用域。
for (let i = 0; i < 3; i++) { let i = "abc"; console.log(i); } // abc // abc // abc上面代码正确运行,输出了 3 次 abc。这表明函数内部的变量 i 与循环变量 i 不在同一个作用域,有各自单独的作用域。
- 在代码块内,使用 let 命令声明变量之前,该变量都是不可用的。这在语法上,称为“暂时性死区”
- 暂时性死区的本质就是,只要一进入当前作用域,所要使用的变量就已经存在了,但是不可获取,只有等到声明变量的那一行代码出现,才可以获取和使用该变量
-
当 const 指向一个引用类型的数据时,其实指向的是他的地址,如果该数据发生改变,那么 const 的数据会随着改变
- const 实际上保证的,并不是变量的值不得改动,而是变量指向的那个内存地址所保存的数据不得改动。对于简单类型的数据(数值、字符串、布尔值),值就保存在变量指向的那个内存地址,因此等同于常量。但对于复合类型的数据(主要是对象和数组),变量指向的内存地址,保存的只是一个指向实际数据的指针,const 只能保证这个指针是固定的(即总是指向另一个固定的地址),至于它指向的数据结构是不是可变的,就完全不能控制了。
-
globalThis
变量的解构赋值
- 解构赋值允许指定默认值
-
数组的解构赋值
- 完全解构
let [a, b, c] = [1, 2, 3]; let [head, ...tail] = [1, 2, 3, 4]; let [x, y, ...z] = ["a"];- 不完全解构
let [x, y] = [1, 2, 3];- 对于 Set 结构,也可以使用数组的解构赋值;只要某种数据结构具有 Iterator 接口,都可以采用数组形式的解构赋值。
-
对象的解构赋值
- 数组的元素是按次序排列的,变量的取值由它的位置决定;而对象的属性没有次序,变量必须与属性同名,才能取到正确的值
let { bar, foo } = { foo: "aaa", bar: "bbb" }; let { log, sin, cos } = Math; const { log } = console; log("hello"); // hello,将console.log赋值到log变量。 let { foo: baz } = { foo: "aaa", bar: "bbb" }; // 变量名与属性名不一致,foo是匹配的模式,baz才是变量。真正被赋值的是变量baz,而不是模式foo- 圆括号与大括号
// 错误的写法 let x; {x} = {x: 1}; // SyntaxError: syntax error // JavaScript 引擎会将{x}理解成一个代码块,从而发生语法错误。只有不将大括号写在行首,避免 JavaScript 将其解释为代码块,才能解决这个问题。 // 正确的写法 let x; ({x} = {x: 1}); -
字符串、数值和布尔值也可以解构赋值
-
函数参数也可以解构赋值
-
解构赋值的用途
- 交换变量的值
- 从函数返回多个值
function example() { return [1, 2, 3]; } let [a, b, c] = example();- 输入模块的指定方法
const { SourceMapConsumer, SourceNode } = require("source-map");
字符串的扩展
- 模板字符串
- 标签模板
- 它可以紧跟在一个函数名后面,该函数将被调用来处理这个模板字符串。这被称为“标签模板”功能(tagged template)。
alert`hello`; // 等同于 alert(["hello"]);- 标签模板其实不是模板,而是函数调用的一种特殊形式。“标签”指的就是函数,紧跟在后面的模板字符串就是它的参数。
字符串的新增方法
- includes(), startsWith(), endsWith()
- includes():返回布尔值,表示是否找到了参数字符串。
- startsWith():返回布尔值,表示参数字符串是否在原字符串的头部。
- endsWith():返回布尔值,表示参数字符串是否在原字符串的尾部。
- repeat() 重复
- padStart(),padEnd() 补全
- trimStart(),trimEnd() 消除空格
- matchAll() 正则
正则的扩展
-
RegExp 函数
- 如果 RegExp 构造函数第一个参数是一个正则对象,那么可以使用第二个参数指定修饰符。而且,返回的正则表达式会忽略原有的正则表达式的修饰符,只使用新指定的修饰符。
-
具名组匹配
- ES2018 引入了具名组匹配(Named Capture Groups),允许为每一个组匹配指定一个名字,既便于阅读代码,又便于引用。
- exec() 方法在一个指定字符串中执行一个搜索匹配。返回一个结果数组或 null。
// 原来的 const RE_DATE = /(\d{4})-(\d{2})-(\d{2})/; // 使用exec方法,就可以将这三组匹配结果提取出来 const matchObj = RE_DATE.exec("1999-12-31"); const year = matchObj[1]; // 1999 const month = matchObj[2]; // 12 const day = matchObj[3]; // 31 // 现在可以 const RE_DATE = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/; const matchObj = RE_DATE.exec("1999-12-31"); const year = matchObj.groups.year; // 1999 const month = matchObj.groups.month; // 12 const day = matchObj.groups.day; // 31 // 对象解构赋值 let { groups: { one, two }, } = /^(?<one>.*):(?<two>.*)$/u.exec("foo:bar"); one; // foo two; // bar // 字符串替换 let re = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/u; "2015-01-02".replace(re, "$<day>/$<month>/$<year>"); // '02/01/2015'- “具名组匹配”在圆括号内部,模式的头部添加“问号 + 尖括号 + 组名”(?),然后就可以在 exec 方法返回结果的 groups 属性上引用该组名。同时,数字序号(matchObj[1])依然有效。
- 解构赋值和替换
- 有了具名组匹配以后,可以使用解构赋值直接从匹配结果上为变量赋值。
- 字符串替换时,使用$<组名>引用具名组。
- 引用 没看懂 引用
-
y 修饰符
- y 修饰符,叫做“粘连”(sticky)修饰符。
- y 修饰符的作用与 g 修饰符类似,也是全局匹配,后一次匹配都从上一次匹配成功的下一个位置开始。不同之处在于,g 修饰符只要剩余位置中存在匹配就可,而 y 修饰符确保匹配必须从剩余的第一个位置开始,这也就是“粘连”的涵义。
-
s 修饰符
- s 修饰符,使得.可以匹配任意单个字符。
- /s 修饰符和多行修饰符/m 不冲突,两者一起使用的情况下,.匹配所有字符,而^和$匹配每一行的行首和行尾。
-
后行断言
-
“先行断言”指的是,x 只有在 y 前面才匹配,必须写成/x(?=y)/。比如,只匹配百分号之前的数字,要写成/\d+(?=%)/。“先行否定断言”指的是,x 只有不在 y 前面才匹配,必须写成/x(?!y)/。比如,只匹配不在百分号之前的数字,要写成/\d+(?!%)/。
-
“后行断言”正好与“先行断言”相反,x 只有在 y 后面才匹配,必须写成/(?<=y)x/。比如,只匹配美元符号之后的数字,要写成/(?<=$)\d+/。“后行否定断言”则与“先行否定断言”相反,x 只有不在 y 后面才匹配,必须写成/(?<!y)x/。比如,只匹配不在美元符号后面的数字,要写成/(?<!$)\d+/。
-
数值的扩展
- Number.isFinite(), Number.isNaN()
- Number.parseInt(), Number.parseFloat()
- Number.isInteger()
- Math 对象
- Math.trunc 方法用于去除一个数的小数部分,返回整数部分。
- Math.sign 方法用来判断一个数到底是正数、负数、还是零。对于非数值,会先将其转换为数值。
- Math.cbrt()方法用于计算一个数的立方根。
- Math.clz32()、Math.imul()、Math.fround()、Math.hypot()
- 对数方法和双曲线方法
- **
- ES2016 新增了一个指数运算符(**)。
- 特点是右结合,而不是常见的左结合。多个指数运算符连用时,是从最右边开始计算的。
- BigInt 数据类型
函数的扩展
- 函数参数可以设置默认值
- rest 参数
- 引入 rest 参数(形式为...变量名),用于获取函数的多余参数,这样就不需要使用 arguments 对象了。rest 参数搭配的变量是一个数组,该变量将多余的参数放入数组中。
- 箭头函数
- 如果箭头函数直接返回一个对象,必须在对象外面加上括号,否则会报错。
- 注意:
- 函数体内的 this 对象,就是定义时所在的对象,而不是使用时所在的对象。
- 不可以当作构造函数,也就是说,不可以使用 new 命令,否则会抛出一个错误。
- 不可以使用 arguments 对象,该对象在函数体内不存在。如果要用,可以用 rest 参数代替。
- 不可以使用 yield 命令,因此箭头函数不能用作 Generator 函数。
- 箭头函数里面根本没有自己的 this,而是引用外层的 this
- 函数参数最后一个可以加逗号
尾调用优化
-
递归本质上是一种循环操作。纯粹的函数式编程语言没有循环操作命令,所有的循环都用递归实现,这就是为什么尾递归对这些语言极其重要。对于其他支持“尾调用优化”的语言(比如 Lua,ES6),只需要知道循环可以用递归代替,而一旦使用递归,就最好使用尾递归。
-
ES6 的尾调用优化只在严格模式下开启,正常模式是无效的。
-
这是因为在正常模式下,函数内部有两个变量,可以跟踪函数的调用栈。
- func.arguments:返回调用时函数的参数。
- func.caller:返回调用当前函数的那个函数。
-
尾调用优化发生时,函数的调用栈会改写,因此上面两个变量就会失真。严格模式禁用这两个变量,所以尾调用模式仅在严格模式下生效。
-
想在正常模式下可以自己实现尾调用优化,用“循环”代替“递归”
数组的扩展
- 扩展运算符
- 应用
- 字符串:可以将字符串转换为真正的数组
[..."hello"]; // [ "h", "e", "l", "l", "o" ]- 能够正确识别四个字节的 Unicode 字符
console.log("x\uD83D\uDE80y".length); // 4 console.log([..."x\uD83D\uDE80y"].length); // 3
- 应用
- 空位
- ES6 明确将空位转为 undefined
- 但有的数组方法会将空位跳过,所以尽量在数组中不使用空格
- Array.from()
- Array.of()
- copyWithin()
- find() 和 findIndex()
- fill() 使用给定值,填充一个数组。
- entries(),keys() 和 values() 遍历数组
- includes()
- flat(),flatMap()
对象的扩展
-
ES6 允许在大括号里面,直接写入变量和函数,作为对象的属性和方法。
const x = 1; const y = 10; return { x, y }; function clear() { let ms = {}; } module.exports = { getItem, setItem, clear }; -
属性名表达式
- ES6 允许把表达式放在方括号内作为对象的属性名;
- 表达式还可以用于定义方法名。
- 属性名表达式如果是一个对象,默认情况下会自动将对象转为字符串[object Object]。
-
super,指向当前对象的原型对象。
-
解构赋值
- 变量声明语句之中,如果使用解构赋值,扩展运算符后面必须是一个变量名,而不能是一个解构赋值表达式
-
链判断运算符?.
iterator.return?.(); // 判断函数方法是否存在,存在立即执行,否则返回undefined // 左侧的对象是否为null或undefined,如果是返回undefined // 等同于 iterator.return == null || undefined ? undefined : iterator.return();- 如果属性链有圆括号,链判断运算符对圆括号外部没有影响,只对圆括号内部有影响。
- 为了保证兼容以前的代码,允许 foo?.3:0 被解析成 foo ? .3 : 0,因此规定如果?.后面紧跟一个十进制数字,那么?.不再被看成是一个完整的运算符,而会按照三元运算符进行处理,也就是说,那个小数点会归属于后面的十进制数字,形成一个小数
-
Null 判断运算符??
- 它的行为类似||,但是只有运算符左侧的值为 null 或 undefined 时,才会返回右侧的值。
- || 当左侧为 false 或''或 0 时,右侧的默认值也会生效
const headerText = response.settings.headerText || "Hello, world!";- 如果多个逻辑运算符一起使用,必须用括号表明优先级,否则会报错。
对象的新增方法
- Object.is() 在所有环境中,只要两个值是一样的,它们就应该相等。
- Object.assign() 用于对象的合并
- Object.assign 拷贝的属性是有限制的,只拷贝源对象的自身属性(不拷贝继承属性),也不拷贝不可枚举的属性(enumerable: false)。
- 如果希望合并后返回一个新对象,可以改写上面函数,对一个空对象合并。
- 为属性指定默认值
- Object.getOwnPropertyDescriptors()方法,返回指定对象所有自身属性(非继承属性)的描述对象。
- 该方法的引入目的,主要是为了解决 Object.assign()无法正确拷贝 get 属性和 set 属性的问题。
- 与其他方法的搭配
- 原型
- Object.keys(),Object.values(),Object.entries(),Object.fromEntries()
- **Object.entries()**方法返回一个给定对象自身可枚举属性的键值对数组,其排列与使用 for...in 循环遍历该对象时返回的顺序一致(区别在于 for-in 循环还会枚举原型链中的属性)。