一、补充
var x = 123;
{
let x = x;
//将右侧x赋值给左侧x时,会先读取x值,而当前块级作用域中变量x会通过let声明
//在声明前引用变量,所以报错
console.log(x)
}
//报错
二、函数默认值
es5中,函数默认值的写法,需要在函数体内创建两个变量,根据实参的值来赋值,麻烦。
function test (a, b){
//不能直接判断实参的值,如果实参的值为0、''、false等虚值falsy,判断就会出错
let a = arguments[0] === undefined ? 6 : arguments[0],
b = arguments[1] === undefined ? 8 : arguments[1];
return a + b
}
es6中提供了更方便的语法,函数参数默认值
function test (a = 6, b = 8){
return a + b
}
(1)函数参数作用域
es6中,设置函数参数后,调用函数时,参数会产生一个单独的作用域。函数形参值会在传入实参后进行计算。
var x = 1;
function f(x, y = x) {
//函数调用时,参数形成一个作用域,对变量y进行求值时,x的值为2。
//参数默认值变量x指向形参x,所以y被赋值为2
console.log(y);
}
f(2) // 2
如果参数默认值变量在参数作用域中未找到,则会到往外层作用域中查找,直至找到相应变量或报引用错误。
let a = 123;
function test( x = a , y =6){
return x + y
}
test( undefined, 8 ) //如果想让前面形参取默认值,实参需要传undefined,传null不能达到目的。
难题:
1、参数作用域是一种块级作用域
let x = 123;
function test (x = x){
// 相当于 let x = x 声明前引用,会报错 Cannot access 'y' before initialization
console.log(x)
}
test() // Uncaught ReferenceError: Cannot access 'x' before initialization
2、
let x = 123;
function test ( x , y = function(){ x = 2 }){
//let x = 567 // 执行函数体代码时,形参作用域已经消失,不能冗余声明变量?
//如果像上述所说,形参作用域消失后,形参x放哪儿,函数体内部通过var声明的x又放在哪儿?
//let声明的变量在词法环境中,var声明的变量在变量环境中?
var x = 567
y()
console.log(x)
}
test(1) // => 567
(2)形参默认值表达式 都是 **惰性求值**(每次都重新计算)
二、解构赋值
数组结构
(1)为了简化声明变量的同时进行赋值的操作,es6增加了解构赋值 Destructuring
解构赋值的策略为 模式匹配,只要两边结构相等,左边的变量就会被赋予对应的值。
let [a, b, c] = [1,2,3] // a,b,c => 1,2,3
let [a,[b],c] = [1,[2],3] // a,b,c => 1,2,3
let [, , c] = [1, 2, 3] // c => 3
(2)解构数组时,还可利用 拓展符 获得包含对应元素的数组,如果没有对应的元素**解构失败**时,返回**空数组**
let [a, b, ...c] = [1,2,3,4,5,6];
console.log(c) // => [3,4,5,6]
(3)单个变量没有对应值时,赋值为undefined
let [a,b,...c] = [8]
console.log(a,b,c) // => 8, undefined, []
(4)解构赋值的时候也可以设置默认值,用默认值的话传undefined,传null也会赋值的。
let [a = 6] = [];
console.log(a) // => 6
(5)**默认值**也可以设为**变量**
let [x = y, y = 1] = []
//解构失败的时候会报错,会用到变量y给x赋值
let [x = y, y = 1] = [null]
//结构成功时,不会提前引用变量y,也就不会报错
对象结构
声明变量时,给对应key赋值。对象解构时,只写一个属性名时,属性值为同名变量。
let {a, b, c} = {a: 1, b:2, c:3};
// let {a:a, b:b, c:c} = {a: 1, b:2, c:3}
默认值也可以是个变量,**结构失败才会去引用默认值变量**
let { a = b, b} = {a:123, b:567}
console.log(a,b) // => 123 567
let { a = b, b } = {b:567}
//报错
解构赋值时,取对应值并重新命名结构为 { 'key' : 'newName' },新key值不能为字符串,不然会报错。<br />对象的解构赋值的**内部机制**,是**先找到同名属性,然后再赋给对应的变量,被赋值的是后者,前者是匹配模式,不是变量。**
let {a: 'newA', b: 'newB'} = {a:'a', b: 'b'}
console.log(newA,newB)
结构对应:<br />**左边解构也要写清晰,增加可读性**
let obj = {
p:{
foo(){},
arr: [1,2,3]
}
};
//结构比较复杂的话,最好把结构也写清楚,增加代码可读性
let {
p :
{
foo:fn,
arr:newArr
}
} = obj
console.log(fn,newArr)
// p 是匹配模式
//如果要结构变量p,可以写成
let {p, p:{foo:fn, arr:newArr}} = obj
console.log(p, fn, newArr)
注意点
(1)结构一个已经声明的变量时,大括号不能放在行首,解决方式为用圆括号抱起来。
let x;
{x} = { x: 1 }
大括号放在行首,引擎会将 {x} 解析为一个代码块。