变量新增
变量的定义
Let
- 在全局定义的变量不在属于全局对象的属性
var a = {};
console.log(this.a === a); //true
let b = {};
console.log(this.b === b); //false
- 具有块级作用域
let i = 0;
{
console.log(i); //0
i++;
}
console.log(i); //1
//相当于
let i = 0;
{
let iCopy = i;
console.log(iCopy); //0,使用内层的
i++; //修改外层的
}
console.log(i); //1
/*由上述就可得知循环中的块级作用域*/
var a = [];
for (let i = 0;i < 2; i++) {
a[i] = function () {
console.log(i);
};
}
a[1](); //1
a[0](); //0
//相当于
for (let i = 0;i < 2;) {
let iCopy = i;
a[iCopy] = function () {
console.log(iCopy); //使用内层的
};
i++; //修改外层的
}
a[1](); //1
a[0](); //0
- 块级作用域必须有大括号
/*像一些条件循环语句中若只有一条语句可以省略大括号,这种情况下会若使用let则必须声明大括号,否则会报错*/
for(let i = 0; i < 1; i++)
let a = 10; //SyntaxError
if(true)
let a = 10; //SyntaxError
- For 循环中的父子级作用域
for (let i = 0; i < 3; i++) { //小括号中的父作用域
let i = 1; //大括号是子作用域
console.log(i); //1,三次循环都输出1
}
- 不存在变量提升
console.log(a); //undefined
var a = 2;
console.log(b); //ReferenceError
let b = 2;
- 不能在同一作用域下重复声明同一个变量
let a;
let a = 0; //SyntaxError
let a;
var a = 0; //SyntaxError
function func(arg) { //只有运行时才会发现是否重复定义了同名变量,所以函数不调用是不会报错的
let arg; //SyntaxError
}
func();
- 拥有暂时性死区(temporal dead zone):指的是使用 let 关键字定义变量时,会在该语句到该作用域的开始这部分无法使用该变量
//TDZ开始
tmp = 'abc'; //ReferenceError
console.log(tmp); //ReferenceError
let tmp;
//TDZ结束
console.log(tmp); //undefined
tmp = 1;
console.log(tmp); //1
/*
隐藏的暂时性死区
对于一些像赋值语句表达式是右结合性的,所以会先执行右部
*/
let a = a; //ReferenceError
//相当于
//TDZ开始
a; //ReferenceError
let a = a;
//TDZ结束
let a = b, b = 1;
//相当于
//TDZ开始
b; //ReferenceError
let a = b;
let b = 1;
//TDZ结束
let a = 1 ,b = a;
//相当于,所以不会报错
//TDZ开始
let a = 1;
//TDZ结束
let b = a;
Const
同样具有 let 命令所有的性质:
- 在全局定义的常量不在属于全局对象的属性
- 具有块级作用域
- 块级作用域必须有大括号
- 不存在变量提升
- 不能在同一作用域下重复声明同一个变量
- 拥有存在暂时性死区
只不过比 let 多了一条
- 使用该关键字声明的变量值无法改变,所以声明时必须赋值(同其他语言若常量是引用数据类型则至少引用地址不可改变)
变量的解构赋值
数组解构赋值
基本用法:[...变量] = 数组
- 从数组对应位置为变量数组中的相应变量赋值
- 若为匹配到的模式则会返回
undefined或空数组 - 可以为每个解构的变量赋予默认值,若未匹配到或这个位置的值是
undefined则采用默认值
/*有了解构赋值就又多了一种方式交换两个变量的值*/
let a = 1;
let b = 2;
[a, b] = [b, a];
console.log(a, b); //2 1
/*定义同时解构*/
let [a,b] = [1,2];
console.log(a,b); //1 2
/*先定义后解构*/
let a, b;
[a, b] = [1, 2];
console.log(a, b); //1 2
/*嵌套解构*/
let [a, [b]] = [1, [2]];
console.log(a, b); //1 2
/*部分解构*/
let [a] = [1, 2];
console.log(a); //1
let [,b] = [1, 2];
console.log(b); //2
/*可变参数解构*/
let [a, ...b] = [1, 2, 3];
console.log(a, b); //1 [ 2, 3 ]
/*未匹配到的*/
let [a, b, ...c] = [1];
console.log(a, b, c); //1 undefined []
/*默认值*/
let [a ,b = 1] = [1]; //对为匹配到的采取默认值
console.log(a ,b); //1 1
let [a ,b = 1] = [1, undefined]; //对undefined采取默认值
console.log(a ,b); //1 1
let [a ,b = 1] = [1, null]; //只对undefined采取默认值
console.log(a ,b); //1 null
let [a = 1 ,b = a] = []; //以a的值作为b的默认值
console.log(a ,b); //1 1
let [a = 1 ,b = a] = [2]; //此时b的默认值是拿到的a已经解构的值,而不是a定义的默认值
console.log(a ,b); //2 2
let [a = 1 ,b = a] = [2 ,3]; //赋值后此时不会采用默认值
console.log(a ,b); //2 3
对象的解构赋值
基本用法:{...属性: 变量} = 对象,当属性名和变量名同名时 {...变量} = 对象
- 由于对象没有次序,所以依靠对象的属性名来确定为那个变量赋值相应的属性,可以解构继承的属性
- 若为匹配到的模式则会返回
undefined或空对象 - 可以为每个解构的变量赋予默认值,若未匹配到或这个位置的值是
undefined则采用默认值
/*定义同时解构*/
let { a: a, b: b } = { a: 1, b: 2 };
console.log(a, b); //1 2
//可以简写为
let { a, b } = { a: 1, b: 2 };
console.log(a, b); //1 2
//若属性名与变量名不同时不能简写,否则会匹配不到
let { a: a1, b: b1 } = { a: 1, b: 2 };
console.log(a1, b1); //1 2
//不是按照位置,而是按照属性,所以匹配不到返回undefined
let { a1, b1 } = { a: 1, b: 2 };
console.log(a1, b1); //undefined undefined
/*先定义后解构*/
let a, b;
{ a, b } = { a: 1, b: 2 }; //报错,SyntaxError,会当作代码块来解析
({ a, b } = { a: 1, b: 2 }); //使用小括号包裹,不要将大括号开头即可
/*嵌套解构*/
let { a, n: { b } } = { a: 2, n: { b: 3 } }; //此时n是匹配模式,而不是变量
console.log(a, b); //2 3
let { a, p: { b } } = { a: 2, n: { b: 3 } }; //此时p匹配模式不存在,则会报错 TypeError
console.log(a, b); //2 3
/*解构继承属性*/
let { toString } = { }; //可以解构继承的属性
console.log(toString); //[Function: toString]
/*部分解构*/
let { b } = { a: 1, b: 2 };
console.log(b);
/*可变参数解构*/
let { a, ...b } = { a: 1, b: 2 };
console.log(a, b); //1 { b: 2 }
/*未匹配到的*/
let { a, b, ...c } = { a: 1 };
console.log(a, b, c); //1 undefined {}
/*默认值*/
let { a, b = 1 } = { a: 1 }; //对为匹配到的采取默认值
console.log(a, b); //1 1
let { a, b = 1 } = { a: 1 ,b: undefined}; //对undefined采取默认值
console.log(a, b); //1 1
let { a, b = 1 } = { a: 1 ,b: null}; //只对undefined采取默认值
console.log(a, b); //1 null
let { a = 1, b = a } = {}; //以a的值作为b的默认值
console.log(a, b); //1 1
let { a = 1, b = a } = { a: 2 }; //此时b的默认值是拿到的a已经解构的值,而不是a定义的默认值
console.log(a, b); //2 2
let { a = 1, b = a } = { a: 2 ,b: 3 }; //赋值后此时不会采用默认值
console.log(a, b); //2 3
原始数据类型的解构赋值
原始数据类型会先转化为对应的包装对象后进行解构操作
- 字符串既可以使用数组方式解构单个字符,也可以使用对象方式解构字符串上的方法和属性
let [a, b] = "ab";
console.log(a, b); //a b
let { length } = "ab";
console.log(length); //2
- 数值和布尔类型的只能使用对象解构他们转化成包装对象上的方法
let { toString } = 123;
console.log(toString === Number.prototype.toString); //true
let { toString } = true;
console.log(toString === Boolean.prototype.toString); //true
函数参数解构赋值
/*为函数参数指定默认值*/
function add(a = 1, b = 1) {
console.log(a + b);
}
add(); //2
add(1, 2); //3
- 数组解构
let arr = [1, 2];
/*对传入的数组解构*/
function add([a, b]) {
console.log(a + b);
}
add(); //TypeError,为传入参数则是对undefined解构,则无法解构就会报错
add(1, 2); //TypeError,传入非数组无法解构就会报错
add(arr); //3
/*对传入的数组解构,并指定解构默认值*/
function add([a, b=100]) {
console.log(a + b);
}
add([1]); //101
add(arr); //3
/*对传入的数组解构,若未传入参数则解构默认数组*/
function add([a, b] = [1, 1]) {
console.log(a + b);
}
add(); //2
add(arr); //3
- 对象解构
let obj = { a: 1, b: 2 };
/*传入对象进行解构*/
function show({ a, b }) {
console.log(a, b);
}
show(); //TypeError,为传入参数则是对undefined解构,则无法解构就会报错
show(1); //undefined undefined 不会像解构数组那样无法解构就报错,而是将原始数据类型转化成包装对象解构,匹配不到就是undefined
show(obj); //1 2
/*对传入的对象解构,并指定解构默认值*/
function show({ a = 100, b = 100 }) {
console.log(a, b);
}
show({}); //100 100 解构对象中没有该属性就会使用默认值
show(obj); //1 2
/*对传入对象解构,若未传入参数则解构默认对象*/
function show({ a, b } = { a: 100, b: 100 }) {
console.log(a, b);
}
show(); //100 100 未传入参数解构默认对象
show({}); //undefined undefined 匹配不到就是undefined
show(obj); //1 2

Comments NOTHING