暂时性死区 TDZ(Temproal Dead Zone)
暂时性死区 TDZ(Temporal Dead Zone),在块级作用域中 let 声明变量,将变量锁住在当前行,并且将变量捆绑到当前的块级作用域内部。
函数参数默认值的问题
falsy(虚值)
falsy 虚值,在通过 Boolean 进行转化的时候,是假的值就是虚值,undefined null 0 NaN false ’ '(空字符串)
函数参数默认值的写法
- ES5 函数参数默认值写法
...(a, b){
var a = typeof arguments[0] !== "undefined" ? arguments[0] : 1;
var b = typeof arguments[0] !== "undefined" ? arguments[0] : 2;
...
- ES6 函数参数默认值的基本语法
function foo(x = 1, y = 2) {
console.log(x + y); // 1 + 2
}
foo();
函数参数默认值生效的条件
传入的参数严格等于(===)undefined,函数默认值生效
function foo(x = 1, y = 2) {
console.log(x + y); // 2 + 2
}
foo(2);
function foo(x = 1, y = 2) {
console.log(x + y); // 2 + 2
}
foo(2, undefined);
function foo(x = 1, y = 2) {
console.log(x + y); // undefined + 2
}
foo("undefined", undefined);
函数默认值赋值的作用域相关
先看以下代码
function fn(x = 2) {
let x = 3;
console.log(x); // SyntaxError: Identifier 'x' has already been declared
}
fn();
- 这说明函数括号内和函数大括号内并不是一般形式的父子作用域关系
- 不能等同于以下
{
let x = 2;
{
let x = 3;
console.log(x); // 3
}
}
- 更接近于以下
{
let x = 2;
let x = 3;
console.log(x); // SyntaxError: Identifier 'x' has already been declared
}
此时再看以下代码
let x = 1;
function foo(y = x) {
let x = 2;
console.log(x, y); // 2 1
}
foo();
如果通过刚刚的方式进行划分作用域
以下代码是会报错的
{
let x = 1;
{
let y = x;
let x = 2;
console.log(x, y); // ReferenceError: Cannot access 'x' before initialization
}
}
综上所述,我认为可以这样看:由于 let 不会变量提升,所以使用 let 声明的变量可以看作是代码一开始知道有这个变量,但是没有初始化,代码运行到这一行才初始化的变量,对于没有初始化的变量进行使用是会报出错误 ReferenceError: Cannot access ‘x’ before initialization ,而这仅仅只能解释上面这一段划分作用域的代码,所以对于函数中的划分应该还有别的解释
对于函数中的默认参数,如果出现默认参数变量和函数体中新声明的变量名相同,应当看作默认参数变量在函数执行之前就已经存在,这样就只能在函数外部作用域去寻找默认参数变量
let x = 1;
function foo(y = x) {
let x = 2;
console.log(y); // 1 此时 foo(y = x) 中的 x 应该去函数体外部作用域寻找,同时,函数体内不能重复声明变量 y
}
foo();
如果函数中的默认参数变量于形参变量相同,则会出现类似于 let x = x 这样的代码,在运行时由于使用 let 声明,所以在此作用域内 x 是一个新的变量,同时赋值给自己就会出现报错 ReferenceError: Cannot access ‘x’ before initialization
let x = 1;
function fn(x = x) {
// --> let x = x 在预编译中为 x:undefined 与函数第一行变量声明冲突
let x = 2; // --> SyntaxError: Identifier 'x' has already been declared
console.log(x);
}
fn();
惰性求值,每次需要重新计算表达式,如果函数参数的默认值没有生效,则不会计算 a + 1 的值
let a = 99;
function foo(b = a + 1) {
console.log(b);
}
foo(); // 100
a = 100;
foo(); // 101
解构赋值
模式匹配或者结构化赋值,数组与数组匹配解构,对象与对象解构
let [a, b, c] = [1, 2, 3];
let [d, [e], [f]] = [1, [2], [3]];
let [a, [b, c], [d, [e, f, [g]]]] = [1, [2, 3], [4, [5, 6, [7]]]];
解构失败
- 解构失败,变量多了,对应解构的值少了,导致 undefined 进行填充
let [a, [b, c], [d, [e, f, [g]]]] = [1, [2, 3], [, [, 6, [7]]]];
console.log(a, b, c, d, e, f, g); // 1 2 3 undefined undefined 6 7
- 不完全解构,值多了,变量少了
let [a, [b, c], [, [, , [g]]]] = [1, [2, 3], [4, [5, 6, [7]]]];
console.log(a, b, c, g); // 1 2 3 7
- 解构的默认值:传入的参数严格等于 undefined 时,默认值才会生效,所以存在惰性求值的问题
let [a = 6] = [1]; //a = 1;
let [a, b = 2] = [1]; //(a = 1), (b = 2);
let [a, b = 2] = [1, undefined]; //(a = 1), (b = 2);
let [a, b = 2] = [1, null]; //(a = 1), (b = null);
- 默认值给函数
function test() {
console.log(1);
}
let [x = test()] = [1]; // x = 1;
let [x = test()] = []; // x = undefined;
// 默认值是变量,存在暂时性死区
let [x = y, y = 1] = [];
console.log(x, y); // ReferenceError: Cannot access 'y' before initialization
ES6 的对象赋值加强
ES5:
var name = "zhangsan",
age = 14;
var person = {
name: name,
age: age,
sex: 'male',
eat: function() {
console.log(1);
}
}
ES6:
let na = 'na';
let me = 'me';
var person = {
[na + me]:name,
age,
sex: 'male',
eat() {
}
}
对象解构
结构是完全一样的两个对象,才可以通过解构进行变量赋值
let { a:a, b:b, c:c } = { a:1, b:2, c:3 };
console.log( a, b, c); // 1 2 3
// 简写:
let { a, b, c } = { a:1, b:2, c:3 };
console.log(a, b, c);
- 不完全解构,嵌套默认值
let { a = 2, b, c} = { a:1, b:2, c:3, e:4, f:5};
console.log(a, b, c); // 1 2 3
- 解构失败
let { a = 2, b, c, d, e, f, g, h} = { b:2, c:3, e:4, f:5};
console.log(a, b, c, d, e, f, g); // 2 2 3 undefined 4 5 undefined undefined
- 数组的解构是存在一个顺序的问题,对象的解构是不存在顺序的
let { a, b, c } = { c:3, b:2, a:1};
console.log(a, b, c); // 1 2 3
let [ a, b, c ] = [ 3, 1, 2 ];
console.log(a, b, c); // 3 1 2
- 对象解构赋值重命名,a(属性名):a(变量)
let { a: e, b: f, c: g } = { a: 1, b: 2, c: 3 };
console.log(e, f, g); // 1 2 3
var person = {
name : "zhangsan",
age:50,
son:{
name:"lisi",
age: 30,
son: {
name:"wangwu",
age:12
}
}
}
var { son:{ son:son1 } } = person;
console.log(son1); // { name:"wangwu", age:12 }