前言
知识的学习需要从两方面入手,一方面是天马行空的想象力,联想拓展,思维活跃,才能把握知识主干与发展趋势。我们往往过于重视这一点,从而忽略了另一方面,那就是系统化学习知识的基础点,打牢地基,训练一套成熟的学科思维方法。要做到这一点,需要花费很多时间和精力,进度较为缓慢,但这是内功,影响了我们后续发展的潜力与动力。慢就是快。 之前对于ES6的语法都是零零落落,毫无章法。故此,系统化的学习,每天进步一点点,持之以恒,收货一定是巨大的。
正文
let和const命令
-
let命令
- 基本用法
ES6 新增了
let命令,用来声明变量。它的用法类似于var,但是所声明的变量,只在let命令所在的代码块内有效。 - for循环作用域
var a = []; for (let i = 0; i < 10; i++) { a[i] = function () { console.log(i); }; } a[6](); // 6上面代码中,变量
i是let声明的,当前的i只在本轮循环有效,所以每一次循环的i其实都是一个新的 变量,所以最后输出的是6。你可能会问,如果每一轮循环的变量i都是重新声明的,那它怎么知道上一轮 循环的 值,从而计算出本轮循环的值?这是因为 JavaScript 引擎内部会记住上一轮循环的值,初始化本轮 的变量i时,就在上一轮循环的基础上进行计算。 另外,for循环还有一个特别之处,就是设置循环变量的那部分是一个父作用域,而循环体内部是一个单独的子作用域。- 不存在变量提升
var命令会发生“变量提升”现象,即变量可以在声明之前使用,值为undefined。 - 暂时性锁区
只要块级作用域内存在
let命令,它所声明的变量就“绑定”(binding)这个区域,不再受外部的影响。 - 不允许重复声明
let不允许在相同作用域内,重复声明同一个变量。
- 基本用法
ES6 新增了
-
块级作用域
ES5 只有全局作用域和函数作用域,没有块级作用域,这带来很多不合理的场景。let实际上为 JavaScript 新增了块级作用域。 -
const命令
- 基本用法
const声明一个只读的常量。一旦声明,常量的值就不能改变。const声明的变量不得改变值,这意味 着,const一旦声明变量,就必须立即初始化,不能留到以后赋值。对于const来说,只声明不赋值,就 会报错。const的作用域与let命令相同:只在声明所在的块级作用域内有效。const命令声明的常量也是不提 升,同样存在暂时性死区,只能在声明的位置后面使用。const声明的常量,也与let一样不可重复声明。 - 本质
const实际上保证的,并不是变量的值不得改动,而是变量指向的那个内存地址所保存的数据不得改动。对于简单类型的数据(数值、字符串、布尔值),值就保存在变量指向的那个内存地址,因此等同于常量。但对于复合类型的数据(主要是对象和数组),变量指向的内存地址,保存的只是一个指向实际数据的指针,const只能保证这个指针是固定的(即总是指向另一个固定的地址),至于它指向的数据结构是不是可变的,就完全不能控制了。因此,将一个对象声明为常量必须非常小心。 - ES6声明变量的6种方法
ES5只有两种声明变量的方法:
var和unction。ES6又新增了四种,除了上面所讲的let和const,还有import命令和class命令。
- 基本用法
-
顶层对象的属性(待完善)
顶层对象,在浏览器环境指的是window对象,在 Node 指的是global对象。ES5 之中,顶层对象的属性与全局变量是等价的。
window.a = 1
a // 1
b = 2
window.b // 2
上面代码中,顶层对象的属性赋值与全局变量的赋值,是一回事。
5. global对象
变量的解构与赋值
-
数组的解构赋值
- 基本用法 ES6 允许按照一定模式,从数组或对象中提取值,对变量进行赋值,这被称作解构(Destructuring)。 以前变量只能直接赋值:
let a = 1; let b = 2; let c = 3;现在ES6允许这样赋值:
let [a, b, c] = [1, 2, 3]上面代码表示,从数组中提取值,按照对应位置,对变量赋值。 本质上,这种写法属于“模式匹配”,只要等号两边的模式相同,左边的变量就会被赋予对应的值。 事情的结果有成功,也就会有失败,解构成功自然符合我们的意愿,那如果失败了的话,那需要被考虑。
let [foo] = [] let [bar, foo] = [1]以上两种解构都不成功,foo的值都会等于undefined。 另一种情况是不完全解构,即等号左边的模式,只匹配一部分的等号右边的数组。这种情况下,解构
依然可以成功。let [a, b] = [1, 2, 3] a // 1 b // 2 let [a, [b], d] = [1, [2, 3], 4]; a // 1 b // 2 d // 4上面两个例子,都属于不完全解构,但是可以成功。 如果等号的右边不是数组(或者严格地说,不是可遍历的结构),那么将会报错。
// 报错 let [foo] = 1; let [foo] = false; let [foo] = NaN; let [foo] = undefined; let [foo] = null; let [foo] = {};- 默认赋值 解构赋值允许指定默认值。
let [foo = true] = []; foo // true let [x, y = 'b'] = ['a']; // x='a', y='b' let [x, y = 'b'] = ['a', undefined]; // x='a', y='b'注意,ES6 内部使用严格相等运算符(===),判断一个位置是否有值。所以,只有当一个数组成员严格 等于undefined,默认值才会生效。
-
对象的解构赋值 解构不仅仅可以用在数组上,还可以用在对象上面。
- 基本用法
let {foo, bar} = {foo: 'last', bar: 'next'} foo // 'last' bar // 'next'对象的解构与数组有个很大的不同,数组的元素是按次序排列的,变量的取值由它的位置决定。而对象的 属性没有次序,变量必须与属性同名,才能取到正确的值。
let { bar, foo } = { foo: 'aaa', bar: 'bbb' }; foo // "aaa" bar // "bbb" let { baz } = { foo: 'aaa', bar: 'bbb' }; baz // undefined上面代码的第一个例子,等号左边的两个变量的次序,与等号右边两个同名属性的次序不一致,但是对取 值完全没有影响。第二个例子的变量没有对应的同名属性,导致取不到值,最后等于undefined。 如果解构失败,变量的值等于undefined。 对象的结构赋值,可以很方便的将现有对象的方法,赋值给某个变量。
// 例一 let { log, sin, cos } = Math; // 例二 const { log } = console; log('hello') // hello上面例一将
Math对象的对数,正弦,余弦方法赋值给了log,sin,cos变量。这样一来,可以直接通过变量调用Math的方法,更加便利。 -
字符串的解构赋值
字符串也可以解构赋值。这是因为此时,字符串被转换成了一个类似数组的对象。const [a, b, c, d, e] = 'hello'; a // "h" b // "e" c // "l" d // "l" e // "o"类似数组的对象都有一个
length属性,因此还可以对这个属性进行解构赋值。let {length : len} = 'hello'; len // 5 -
数值和布尔值的结构赋值
解构赋值时,如果等号右边是数值和布尔值,则会先转为对象。数值和布尔值的包装对象都有toString属性,其属性值用于解构赋值。 解构赋值的规则是,只要等号右边的值不是对象或数组,就先将其转为对象。由于undefined和null无法转为对象,所以对它们进行解构赋值,都会报错。 -
函数参数的解构赋值
函数的参数也可以使用解构赋值。function add([x, y]){ return x + y; } add([1, 2]); // 3上面代码中,函数add的参数表面上是一个数组,但在传入参数的那一刻,数组参数就被解构成变量x和y。 对于函数内部的代码来说,它们能感受到的参数就是x和y。