1.let和const命令
暂时性死区的本质就是,只要一进入当前作用域,所要使用的变量就已经存在了,但是不可获取,只有等到声明变量的那一行代码出现,才可以获取和使用该变量。
- let命令在代码块内有效(for循环中使用效果好)。
- let命令不存在变量提升。
- let命令在代码块内有效(for循环中使用效果好)。
- 暂时性死区(temporal dead zone,简称 TDZ):在块级作用域中,如果在let和const声明了变量之前使用变量,就会报错。
- let命令不允许在相同作用域重复声明一个变量。
- {}:块级作用域可以任意嵌套。
- 块级作用域的出现,实际上使得获得广泛应用的立即执行函数表达式(IIFE)不再必要了。
- 块级作用域是一个语句,将多个操作封装在一起,没有返回值。
- const声明一个只读的常量。一旦声明,常量的值就不能改变,声明之后,立即初始化。
- const实际上保证的,并不是变量的值不得改动,而是变量指向的那个内存地址不得改动。对于简单类型的数据(数值、字符串、布尔值),值就保存在变量指向的那个内存地址,因此等同于常量。但对于复合类型的数据(主要是对象和数组),变量指向的内存地址,保存的只是一个指针,const只能保证这个指针是固定的,至于它指向的数据结构是不是可变的,就完全不能控制了。因此,将一个对象声明为常量必须非常小心。
- 变量声明的6中方法。var,function,let,const,import,class
变量的解构赋值
- 数组的解构赋值
let [a, b, c] = [1, 2, 3];
- 对象的解析赋值
let { foo, bar } = { foo: "aaa", bar: "bbb" };
foo // "aaa"
bar // "bbb"
- 字符串的解析赋值
const [a, b, c, d, e] = 'hello';
a // "h"
b // "e"
c // "l"
d // "l"
e // "o"
- 数值和布尔值的解构赋值
let {toString: s} = 123;
s === Number.prototype.toString // true
let {toString: s} = true;
s === Boolean.prototype.toString // true
- 函数参数的解构赋值
function add([x, y]){
return x + y;
}
add([1, 2]); // 3
字符串的扩展
- 字符的 Unicode 表示法
- codePointAt()
- String.fromCodePoint()
- 字符串的遍历器接口
for (let codePoint of 'foo') {
console.log(codePoint)
}
// "f"
// "o"
// "o"
- at()
'abc'.at(0) // "a"
'𠮷'.at(0) // "𠮷"
- normalize():用来将字符的不同表示方法统一为同样的形式,这称为Unicode正规化。
'abc'.at(0) // "a"
'𠮷'.at(0) // "𠮷"
- includes(), startsWith(), endsWith()
includes():返回布尔值,表示是否找到了参数字符串。
startsWith():返回布尔值,表示参数字符串是否在源字符串的头部。
endsWith():返回布尔值,表示参数字符串是否在源字符串的尾部。- repeat()
'x'.repeat(3) // "xxx"
- padStart(),padEnd() :padStart()用于头部补全,padEnd()用于尾部补全。
'x'.padStart(5, 'ab') // 'ababx'
'x'.padEnd(4, 'ab') // 'xaba'
- 模板字符串:模板字符串(template string)是增强版的字符串,用反引号(`)标识。它可以当作普通字符串使用,也可以用来定义多行字符串,或者在字符串中嵌入变量。
$('#result').append(`
There are <b>${basket.count}</b> items
in your basket, <em>${basket.onSale}</em>
are on sale!
`);
正则的扩展
- u修饰符:用来正确处理大于\uFFFF的Unicode字符。
- y修饰符:y修饰符的作用与g修饰符类似,也是全局匹配,后一次匹配都从上一次匹配成功的下一个位置开始。不同之处在于,g修饰符只要剩余位置中存在匹配就可,而y修饰符确保匹配必须从剩余的第一个位置开始,这也就是“粘连”的涵义。
- sticky属性:表示是否设置了y修饰符。
var r = /hello\d/y;
r.sticky // true
- flags属性:会返回正则表达式的修饰符。
/abc/ig.source
// "abc"
/abc/ig.flags
// 'gi'
数值的扩展
- 二进制和八进制表示法:前缀0b(或0B)和0o(或0O)表示
- Number.isFinite():用来检查一个数值是否为有限的。 Number.isNaN():用来检查一个值是否为NaN。
- ES6将全局方法parseInt()和parseFloat(),移植到Number对象上面,行为完全保持不变。
Number.parseInt('12.34') // 12
Number.parseFloat('123.45#') // 123.45
- Number.isInteger()用来判断一个值是否为整数。
数组的扩展
- Array.from方法用于将两类对象转为真正的数组:类似数组的对象(array-like object)和可遍历(iterable)的对象。
- Array.of方法用于将一组值,转换为数组。
- 数组实例的copyWithin方法,在当前数组内部,将指定位置的成员复制到其他位置(会覆盖原有成员),然后返回当前数组。也就是说,使用这个方法,会修改当前数组。
- find方法,用于找出第一个符合条件的数组成员。它的参数是一个回调函数,所有数组成员依次执行该回调函数,直到找出第一个返回值为true的成员,然后返回该成员。findIndex方法的用法与find方法非常类似,返回第一个符合条件的数组成员的位置,如果所有成员都不符合条件,则返回-1。
- fill方法使用给定值,填充一个数组。
- eys()是对键名的遍历、values()是对键值的遍历,entries()是对键值对的遍历。
- Array.prototype.includes方法返回一个布尔值,表示某个数组是否包含给定的值,与字符串的includes方法类似。
函数
- ES6 允许为函数的参数设置默认值。
- ES6 引入 rest 参数(形式为“...变量名”),用于获取函数的多余参数,这样就不需要使用arguments对象了。
- 扩展运算符:扩展运算符(spread)是三个点(...)。
console.log(1, ...[2, 3, 4], 5)
// 1 2 3 4 5
- name 属性
function foo() {}
foo.name // "foo"
- ES6允许使用“箭头”(=>)定义函数。
- 绑定this:函数绑定运算符是并排的两个双冒号(::)。
foo::bar;
// 等同于
bar.bind(foo);
- 尾调用(Tail Call)是函数式编程的一个重要概念,本身非常简单,一句话就能说清楚,就是指某个函数的最后一步是调用另一个函数。
对象的扩展
- 属性的简洁表示法:
var foo = 'bar';
var baz = {foo};
baz // {foo: "bar"}
- 属性名表达式
- 方法的 name 属性
- Object.is():ES6提出“Same-value equality”(同值相等)算法,用来解决这个问题。
- Object.assign():Object.assign方法用于对象的合并,将源对象(source)的所有可枚举属性,复制到目标对象(target)。
- 属性的遍历:for...in,Object.keys(obj),Object.getOwnPropertyNames(obj),Object.getOwnPropertySymbols(obj),Reflect.ownKeys(obj)。
- proto属性(前后各两个下划线),用来读取或设置当前对象的prototype对象。
- Object.keys(),Object.values(),Object.entries()
ES5 引入了Object.keys方法,返回一个数组,成员是参数对象自身的(不含继承的)所有可遍历(enumerable)属性的键名。
Object.values方法返回一个数组,成员是参数对象自身的(不含继承的)所有可遍历(enumerable)属性的键值。
Object.entries方法返回一个数组,成员是参数对象自身的(不含继承的)所有可遍历(enumerable)属性的键值对数组。
symbol
- Symbol:保证每个属性的名字都是独一无二的,这样就从根本上防止属性名的冲突,Symbol值通过Symbol函数生成。
- Symbol.for():有时,我们希望重新使用同一个Symbol值,Symbol.for方法可以做到这一点。它接受一个字符串作为参数,然后搜索有没有以该参数作为名称的Symbol值。如果有,就返回这个Symbol值,否则就新建并返回一个以该字符串为名称的Symbol值。
- Symbol.keyFor:返回一个已登记的 Symbol 类型值的key。
Set和Map数据结构
- Set:ES6 提供了新的数据结构 Set。它类似于数组,但是成员的值都是唯一的,没有重复的值。Set 本身是一个构造函数,用来生成 Set 数据结构。add(),delete(), has(), clear()
- WeakSet:WeakSet结构与Set类似,也是不重复的值的集合。无法引用WeakSet的成员,因此WeakSet是不可遍历的。
- Map:ES6提供了Map数据结构。它类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。size(), set(), get(), has(), delete(), clear()
- WeakMap:WeakMap结构与Map结构基本类似,唯一的区别是它只接受对象作为键名(null除外),不接受其他类型的值作为键名,而且键名所指向的对象,不计入垃圾回收机制。
Proxy
- Proxy 可以理解成,在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。Proxy 这个词的原意是代理,用在这里表示由它来“代理”某些操作,可以译为“代理器”。
- Proxy 实例的方法
Proxy 对象的所有用法,都是上面这种形式,不同的只是handler参数的写法。其中,new Proxy()表示生成一个Proxy实例,target参数表示所要拦截的目标对象,handler参数也是一个对象,用来定制拦截行为。
var proxy = new Proxy(target, handler);
Promise
Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。then方法返回的是一个新的Promise实例(注意,不是原来那个Promise实例)。因此可以采用链式写法,即then方法后面再调用另一个then方法。
Iterator(遍历器)的概念
JavaScript原有的表示“集合”的数据结构,主要是数组(Array)和对象(Object),ES6又添加了Map和Set。这样就有了四种数据集合,用户还可以组合使用它们,定义自己的数据结构,比如数组的成员是Map,Map的成员是对象。这样就需要一种统一的接口机制,来处理所有不同的数据结构。
Iterator的遍历过程是这样的。
- 创建一个指针对象,指向当前数据结构的起始位置。也就是说,遍历器对象本质上,就是一个指针对象。
- 第一次调用指针对象的next方法,可以将指针指向数据结构的第一个成员。
- 第二次调用指针对象的next方法,指针就指向数据结构的第二个成员。
- 不断调用指针对象的next方法,直到它指向数据结构的结束位置。
每一次调用next方法,都会返回数据结构的当前成员的信息。具体来说,就是返回一个包含value和done两个属性的对象。其中,value属性是当前成员的值,done属性是一个布尔值,表示遍历是否结束。
Generator
执行 Generator 函数会返回一个遍历器对象,也就是说,Generator 函数除了状态机,还是一个遍历器对象生成函数。返回的遍历器对象,可以依次遍历 Generator 函数内部的每一个状态。
形式上,Generator 函数是一个普通函数,但是有两个特征。一是,function关键字与函数名之间有一个星号;二是,函数体内部使用yield语句,定义不同的内部状态(yield在英语里的意思就是“产出”)。
需要注意的是,yield语句后面的表达式,只有当调用next方法、内部指针指向该语句时才会执行,因此等于为JavaScript提供了手动的“惰性求值”(Lazy Evaluation)的语法功能。
- 协程的 Generator 函数实现:Generator 函数是协程在 ES6 的实现,最大特点就是可以交出函数的执行权(即暂停执行)。