文章目录
一、定义变量 let、const
ES6声明变量的方法6种:var、function、let、const、import、class
1、 let、const
特点:
① 块级作用域;
② 不存在变量提升;
③ 暂时性死区;(代码块内,使用let命令声明变量之前,该变量都是不可用的)
④ 相同作用域重复声明同一个变量,会报错;
const 声明只读的常量,必须赋值并且不能改变;(否则会报错)
对象声明为常量要小心
const变量指向内存地址保存数据不得改动:
- 对于简单数据类型,等同于常量;
- 对于复合数据类型,只能保证指针固定,不能控制指针指向的数据结构。
//********块级作用域
for (let i = 0; i < 10; i++) {
// ...
}
console.log(i);// ReferenceError: i is not defined
var a = [];
for (var i = 0; i < 10; i++) {
a[i] = function () {
console.log(i);//i都是同一个变量i
};
}
a[6](); // 10
//i只在本轮循环有效,每一次循环都是一个新变量
var a = [];
for (let i = 0; i < 10; i++) {
a[i] = function () {
console.log(i);
};
}
a[6](); // 6
//********变量提升
// var 的情况
console.log(foo); // 输出undefined
var foo = 2;
// let 的情况:不存在变量提升,会报错
console.log(bar); // 报错ReferenceError
let bar = 2;
//**********暂时性死区(temporal dead zone,TDZ)
if (true) {
// TDZ开始
tmp = 'abc'; // ReferenceError
console.log(tmp); // ReferenceError
let tmp; // TDZ结束
console.log(tmp); // undefined
tmp = 123;
console.log(tmp); // 123
}
let x = x; // ReferenceError: x is not defined
//在变量x的声明语句还没有执行完成前,就去取x的值,导致报错”x 未定义“。
//*********不允许重复声明
function func(arg) {
let arg;
}
func() // 报错
function func(arg) {
{
let arg;
}
}
func() // 不报错
//const声明的复合类型数据,指针固定,数据可以改变
const foo = {};
foo.prop = 123; // 为 foo 添加一个属性,可以成功
foo.prop // 123
foo = {}; // TypeError: "foo" is read-only 将 foo 指向另一个对象,就会报错
2、块级作用域
ES5只有全局和函数作用域,带来不合理场景:
第一种场景,内层变量可能会覆盖外层变量;
第二种场景,用来计数的循环变量泄露为全局变量。
作用:块级作用域可以任意嵌套;内层可以定义外层作用域的同名变量
var tmp = new Date();
function f() {
console.log(tmp);
if (false) {
var tmp = 'hello world';//变量提升
}
}
f(); // undefined
var s = 'hello';
for (var i = 0; i < s.length; i++) {
console.log(s[i]);
}
console.log(i); // 5
3、顶层对象的属性
顶层对象,在浏览器环境指的是window对象,在 Node 指的是global对象。
ES5 中,顶层对象的属性与全局变量是等价的;ES6 开始,全局变量将逐步与顶层对象的属性脱钩。
ES6中:
- var、function声明的全局变量,依旧是顶层对象的属性;
- let、const、class声明的全局变量,不属于顶层对象的属性。
var a = 1;
// 如果在 Node 的 REPL 环境,可以写成 global.a,或者采用通用方法,写成 this.a
window.a // 1
let b = 1;
window.b // undefined
二、变量的解构赋值
用途:(多思考)
① 交换变量的值;
② 从函数返回多个值;
③ 函数参数与变量名对应;
④ 快速提取JSON数据;
⑤ 设置函数参数的默认值;
⑥ 遍历Map结构;
⑦ 输入模块的指定方法。
1、数组:变量取值由位置决定
- 模式匹配(两边相等);
- 不完全解构;
- 等号右边不是数组,报错;
- 只要某种数据结构具有 Iterator 接口,都可以采用数组形式的解构赋值;
- 等号左边可指定默认值;(ES6 内部使用严格相等运算符(===),判断一个位置是否有值);如果默认值是一个表达式,那么这个表达式是惰性求值的,即只有在用到的时候,才会求值。
注意:rest 参数搭配的变量是一个数组,该变量将多余的参数放入数组中。
//模式匹配
let [foo, [[bar], baz]] = [1, [[2], 3]];
foo // 1
bar // 2
baz // 3
let [x, , y] = [1, 2, 3];
x // 1
y // 3
// ...变量名:是一个数组,将其展开
let [head, ...tail] = [1, 2, 3, 4];
head // 1
tail // [2, 3, 4]
let [x, y, ...z] = ['a'];
x // "a"
y // undefined 如果解构不成功,变量的值就等于undefined
z // []
//不完全解构
let [a, [b], d] = [1, [2, 3], 4];
a // 1
b // 2
d // 4
let [foo] = undefined;
let [foo] = null;
foo //报错
//Set结构
let [x, y, z] = new Set(['a', 'b', 'c']);
x // "a"
//指定默认值:只有当一个数组成员严格等于undefined,默认值才会生效。
let [x, y = 'b'] = ['a']; // x='a', y='b'
let [x, y = 'b'] = ['a', undefined]; // x='a', y='b'
let [x = 1] = [undefined];
x // 1只有严格等于undefined,默认值才生效
let [x = 1] = [null];
x // null
2、对象:变量要与属性同名
- 没有对应的同名属性,导致取不到值,最后等于undefined;
- 对象的解构赋值的内部机制,是先找到同名属性,然后再赋给对应的变量。真正被赋值的是后者,而不是前者。
- 嵌套赋值
- 默认值生效的条件是,对象的属性值严格等于undefined;解构失败,变量的值等于undefined
- 已声明的变量用于解构赋值,会报错;
let { foo, bar, baz: fun } = { foo: "aaa", bar: "bbb", baz: "ccc" };
//实际可写作let { foo: foo, bar: bar },真正被赋值的是后面的变量(属性值位置)
foo // "aaa"
bar // "bbb"
fun // "ccc" 变量名与属性名不一致。真正被赋值的是baz(变量), foo是匹配的模式
baz // error: foo is not defined
let obj = {
p: [
'Hello',
{ y: 'World' }
]
};
let { p, p: [x, { y }] } = obj;
x // "Hello"
y // "World"
p // ["Hello", {y: "World"}] 注意:p: [x, { y }] 这里的p是模式,不是变量,不会被赋值,要取值,必须如本例中对象里面给出p
// 嵌套赋值
let obj = {};
let arr = [];
({ foo: obj.prop, bar: arr[0] } = { foo: 123, bar: true });
obj // {prop:123}
arr // [true]
//指定默认值
var {x = 3} = {};
x // 3
var {x: y = 3} = {};
y // 3
//解构模式是嵌套的对象,子对象所在的父属性不存在,报错
let {foo: {bar}} = {baz: 'baz'};// 报错
let x;
{x} = {x: 1};//js引擎会将{}理解为代码块
// SyntaxError: syntax error
// 正确的写法:({x} = {x: 1});
//数组本质是特殊对象
let arr = [1, 2, 3];
let {0 : first, [arr.length - 1] : last} = arr;
first // 1
last // 3
3、字符串:此时编程类似数组的对象
const [a, b, c, d, e] = 'hello';
a // "h"
b // "e"
c // "l"
d // "l"
e // "o"
let {length : len} = 'hello';
len // 5 类似数组的对象都有一个length属性
4、数值和布尔值
解构赋值时,如果等号右边是数值和布尔值,则会先转为对象;
undefined、null无法转为对象,会报错。
let {toString: s} = 123;
s === Number.prototype.toString // true
let {toString: s} = true;
s === Boolean.prototype.toString // true
let { prop: x } = undefined; // TypeError
let { prop: y } = null; // TypeError
5、函数参数
- 为函数参数指定默认值,而不是为变量指定默认值
[[1, 2], [3, 4]].map(([a, b]) => a + b);
// [ 3, 7 ]
//使用默认值:为函数参数指定默认值,而不是为变量指定默认值
function move({x = 0, y = 0, z} = { z = 10 }) {
return [x, y, z];
}
move({x: 3, y: 8, z}); // [3, 8, undefined]
move({x: 3}); // [3, 0, undefined]
[1, undefined, 3].map((x = 'yes') => x);
// [ 1, 'yes', 3 ]
6、圆括号问题
不能使用的情况:
- 变量声明语句
- 函数参数
- 赋值语句的模式
可以使用的情况:只有一种,赋值语句的非模式部分
//会报错的情况:
let [(a)] = [1];//变量声明
let {x: (c)} = {};//变量声明
function f([z,(x)]) { return x; }//函数参数
({ p: a }) = { p: 42 };//赋值语句的模式
//可以使用:赋值语句的非模式部分
[(b)] = [3]; // 正确
({ p: (d) } = {}); // 正确