第一章 let和const命令
let的用法和var类似,但是所声明的变量只在let命令所在的代码块内有效。
{ let a = 10; var b = 1; } a //ReferenceError:a is not defined. b //1
在
for
循环中使用let定义计数变量i时,每一次循环的i
其实都是一个新的变量,但是因为JavaScript引擎内部会记住上一轮循环的值,初始化本轮的变量i时,就在上一轮循环的基础上进行计算。此外,for
循环设置循环变量的那部分是一个父作用域,而循环体内部是一个单独的子作用域。for(let i = 0;i < 3;i++){ let i = 'abc'; console.log(i); } //abc //abc //abc
- 用
var
命令定义变量是会存在“变量提升”现象,即变量可以在声明之前使用,值为undefined
。let
命令声明的变量一定要在声明后使用,否则报错。 - 在代码块内,使用
let
声明变量之前,该变量都是不可用的。这在语法上,称为“暂时性死区”(temporal dead zone,简称TDZ)。 let
出现之后,typeof
运算符不是百分之百安全了。在变量没有用let
定义之前,都属于该变量的“死区”,使用该变量就会报错。但是,一个根本没有被声明的变量,使用typeof反而不会报错。typeof x; //ReferenceError let x; typeof y; //"undefined"
let
不允许在相同作用域内,重复声明同一个变量。不能再函数内部重新声明参数。- ES5只有全局作用域和函数作用域,没有块级作用域。产生问题:内层变量可能会覆盖外层变量;用来计数的循环变量泄露为全局变量。
- ES6中,
let
为JavaScript增加了的块级作用域。ES6允许块级作用域的任意嵌套。外部作用域无法读取内层作用域的变量。内层作用域可以定义外层作用域的同名变量。 - ES6的块级作用域允许声明函数的规则,只在使用大括号的情况下成立,如果没有使用大括号,就会报错。
const
声明一个只读的变量,一旦声明,常量的值就不能改变。const
一旦声明,就必须立即初始化,不能留到以后赋值。const
的作用域与let
命令相同:只在声明所在的块级作用域内有效。cosnt
声明的变量也是不提升,同样存在暂时性死区,只能在声明的位置后面使用。同时不可重复声明。const
保证的不是变量的值不得改动,而是变量指向的那个内存地址不得改动。对于简单类型的数据(数值、字符串、布尔值),值就保存在指向的那个内存地址,因此等同于常量。但是对于复合类型的数据(主要是对象和数组),变量指向的内存地址,保存的是一个指针,const
只能保证这个指针是固定的,至于它指向的数据结构是不是可变的,就完全不能控制了。- 顶层对象,在浏览器环境指的是
window
对象,在Node指的是global
对象。ES5中,顶层对象的属性与全局变量是等价的。 从ES6开始,全局变量将逐步与顶层对象的属性脱钩。用var
命令和function
命令声明的全局变量,依旧是顶层对象的属性,let
命令、const
命令、class
命令声明的全局变量,不属于顶层对象的属性。
第二章、变量的解构赋值
- 解构(Destructuring):ES6允许按照一定模式,从数组和对象中提取值,对变量进行赋值。
- 本质上,这种写法属于“模式匹配”,只要等号两边的模式相同,左边的变量就会被赋予对应的值。
- 解构不成功,变量的值就等于
undefined
。 不完全解构,即等号左边的模式,只匹配一部分的等号右边的数组。这种情况下,解构依然可以成功。
let [x,y] = [1,2,3] x //1 y //2
- 在数组结构中,如果等号右边不是数组(严格地说,不是可遍历的结构),那么将会报错。事实上,只要某种数据结构具有
Iterator
接口,都可以采用数组形式的解构赋值。 - 解构赋值允许指定默认值。只有当一个数组成员严格等于(
===
)undefined
,默认值才会生效。另外,null
不严格等于undefined
。 - 如果默认值是一个表达式,那么这个表达式是惰性求值,即只有在用到的时候,才会求值。同时,默认值可以引用解构赋值的其他变量,但该变量必须已经声明。
- 解构不仅可以用于数组,还可以用于对象。两者最大的不同在于:数组的元素是按照次序排列的,变量的取值由它的位置决定;而对象的属性没有次序,变量必须与属性同名,才能取到正常的值。
如果变量名和属性名不一致,则必须写成这样:
//foo为匹配模式,baz是变量,被赋值的是baz let {foo:baz} = {foo:'aaa',bar:'bbb'} baz //"aaa"
对象的解构赋值内部机制,是先找到同名属性,然后再赋给对应的变量。真正被赋值的是后者,而不是前者。
解构可以用于嵌套结构的对象。下面的代码有三次解构赋值。只有
line
是变量,loc
和start
都是模式,不是变量。const node = { loc:{ start:{ line:1, column:5 } } }; let {loc , loc:{start},loc:{start:{line}}} = node; line //1 loc //Object{start:Object} start//Object{line:1 , column:5}
- 对象的解析也可以指定默认值,默认值生效的条件是,对象的属性值严格等于
undefined
。 - 字符串可以解构,字符串被转换成了一个类似数组的对象。类似数组的对象都有一个
length
属性,因此还可以对这个属性解构赋值。 - 解构赋值时,如果等号右边是数值和布尔值,则先会转为对象。
- 解构赋值的规则:只要等号右边的值不是对象或数组,就先将其转为对象。由于
undefined
和null
无法转为对象,所以对他们进行解构赋值,都会报错。 - 函数的参数也可以使用解构赋值。函数参数的解构也可以使用默认值。
- 不能使用圆括号的三种情况:
- 变量声明语句
- 函数参数
- 赋值语句 - 可以使用圆括号的情况只有一种:赋值语句的非模式部分,可以使用圆括号。
- 变量解构赋值的用途:
- 交换变量的值。
- 从函数返回多个值
- 函数参数的定义
- 提取JSON数据
- 函数参数的默认值
- 遍历Map结构
- 输入模块的指定方法
第三章、字符串的扩展
- JavaScript运行
\uxxxx
形式表示一个字符。xxxx
表示字符的Unicode码点。但是,这种表示法只限于在\u0000
~\uFFFF
之间的字符。超过这个范围的字符必须使用双字节的形式表示。ES6中,将码点放入大括号中就能正确解读该字符。 JavaScript的6中表示字符的方式:
'\z' === 'z' //true '\172' === 'z' //true '\x7A' === 'z' //true '\u007A' === 'z' //true '\u{7A}' === 'z' //true
codePointAt
方法能够正确处理4个字节存储的字符,返回第一个字符的码点。for ... of
循环可以正确的识别32位的UTF-16字符。- ES5提供
String.fromCharCode
方法,用于从码点返回对应字符,但是这个方法不能识别32位的UTF-16字符。ES6提供了String.fromCodePoint
方法,可以识别大于0xFFFF
的字符,弥补了String.fromCharCode
不足,作用上正好与codePointAt
方法相反。 codePointAt
方法定义在字符串的实例对象上,而fromCodePoint
方法定义在String
对象上。- ES6为字符串添加了遍历器接口,使得字符串可以被
for...of
循环遍历。这个遍历器最大的优点是可以识别大于0xFFFF
的码点。 normalize()
方法用来将不同的表示方法统一为相同的形式,这称为Unicode正规化。该方法可以接受一个参数来指定normalize
的方式,参数的四个可选值如下。NFC
,默认参数,标准等价合成NFD
,标准等价分解NFKC
,兼容等价合成NFKD
,兼容等价分解
传统上,JavaScript只有
indexOf
方法,可以用来确定一个字符串是否包含在另一个字符串中。ES6又提供了三种新方法。includes()
:返回布尔值,表示是否找到了参数字符串。startsWith()
:返回布尔值,表示参数字符串是否在原字符串的头部endsWith()
:返回布尔值,表示参数字符串是否在原字符串的尾部。
这三个方法都支持第二个参数,表示开始搜索的位置。使用第二个参数n时,
endsWith
的行为与其他两个方法有所不同,它针对前n个字符,而其他两个方法针对从第n个位置直到字符串结束。repeat
方法返回一个新的字符串,表示将原字符串重复n次。参数如果是小数,会被取整。如果参数是负数或者Infinity
,会报错。但是,参数如果是-1~0之间的小数,则等同于0,这是因为会先进行取整运算。-1~0之间的小数取整之后等于-0,参数NaN
等同于0,参数是字符串则会先转换成数字。- 字符串补全功能。
padStart()
用于头部补全,padEnd()
用于尾部补全。两个参数,第一个指定字符串的最小长度,第二个参数是用来补全的字符串。 - 模板字符串(template string)是增强版的字符串,用反引号(`)标识。模板字符串中嵌入变量,需要将变量名写在
${}
之中。