变量提升与var、let及const
- 函数提升优先于变量提升(var),函数提升会把整个函数挪到作用域顶部,变量提升只会把声明挪到作用域顶部
- var 存在变量提升,我们能在声明之前使用。let、const 因为暂时性死区的原因,不能在声明前使用
- var 在全局作用域下声明变量会导致变量挂载在 window 上(变成window的属性),其他两者不会
- let 和 const 作用基本一致,但是后者声明的变量不能再次赋值
var、let及const的区别
- let(建议用let取代var)
- 作用域是块级作用域(之前只有函数作用域和全局作用域)(并且for循环的括号和{}是在两个不同的作用域,可以独立声明同名变量并使用)
- 不存在变量声明提前 (在let之前使用,会报错(因为暂时性死区) is not defined)
- 不可以重复定义 (var可以,不会报错,但是let 会说has already been declared)
- 存在存在暂时性死区
在块级作用域内存在let/const,它所声明的变量就“绑定”了这个区域。在这个代码块(块级作用域)内,使用let/const命令声明变量之前,该变量都是不可用。意义:标准化代码,将所有的变量声明放在最前面
- const(所有的函数都应该用const)
- 一般用来声明常量,不允许修改
- 一旦声明变量,就必须立即赋值
- 和let一样,都是块级作用域,存在暂时性死区,没有变量声明提前,不允许重复定义
- var
- var的作用域是函数作用域,在一个函数内部利用var声明一个变量,则这个变量只在函数内有效
- 存在变量声明提前(但是赋值并没有提前,提前访问会返回undefined)
变量的结构赋值
如下所述
- 数组的解构赋值
1
2
3
4
5let [a, b, c] = [1, 2, 3]; // 可以一次性为三个变量赋值
let [ , , third] = ["foo", "bar", "baz"]; // third为'baz
let [head, ...tail] = [1, 2, 3, 4];
head // 1
tail // [2, 3, 4] - 对象的解构赋值。eg. let { foo, bar } = { foo: ‘aaa’, bar: ‘bbb’ }; 变量必须与属性同名,才能取到正确的值
- 字符串的解构赋值。eg. const [a, b, c, d, e] = ‘hello’;
- 函数参数也可以用解构赋值。可以实现:函数的参数表面上是一个数组/对象,但在传入参数的那一刻,数组参数就被解构成变量x和y。对于函数内部的代码来说,它们能感受到的参数就是x和y
- … 就是让数组把item全部铺开的意思
此外还可以设置默认值:
1 | var {x, y = 5} = {x: 1}; |
应用:使用数组成员对变量赋值;函数的参数如果是对象的成员,优先使用解构赋值;如果函数返回多个值,优先使用对象的解构赋值;使用扩展运算符(…)拷贝数组,eg.拷贝item数组: const itemsCopy = [...items];
1 | // bad |
常用的扩展方法
- Array.from() 将类似数组的对象转为数组(arguement,Nodelist),也能将普通对象的key值取出来生成一个数组。
Symbol,Set和Map
- 新的原始数据类型Symbol,表示独一无二的值。可以作为对象的属性名使用,但此时Symbol为名属性,不会出现在for…in和for…if循环中也不会被Object.keys()、Object.getOwnPropertyNames()、JSON.stringify()返回。Object.getOwnPropertySymbols方法返回一个数组,成员是当前对象的所有用作属性名的 Symbol 值。Reflect.ownKeys方法可以返回所有类型的键名,包括常规键名和 Symbol 键名,生成一个数组。
- 新的数据结构 Set。它类似于数组,但是成员的值都是唯一的,没有重复的值
- 新的数据结构 Map。类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键
Promise, Generator, async函数
见我写的另一篇文章:《异步编程》