1.spread/rest
运算符...通常被称为spread或者rest(展开或收集运算符)。可以将数组(实际上是任何iterable)展开成独立的值
它还有另一种可以看成反向的行为,把一系列值收集到一起成为数组
scss
function foo(x,...z){
console.log(x,z)
}
foo(1,2,3,4,5) //1 2 [3,4,5]
...z基本上就是在说:将剩下的参数(除了命名参数x)收集到一起组成一个名为z的数组。如果没有命名参数,自然就会收集所有的参数。
他最好的一点是弃用了arguments类数组,因为下面的args就是一个真正的数组
scss
function foo(...args){
args.shift()
}
function foo(...vals=[1,2,3]){}这是很诱人但是是不合法的
2.默认值表达式
函数默认值可以不只是31这样的简单值,他们可以是任意合法表达式,甚至是函数调用
scss
function bar(val){
console.log('bar called');
return y + val
}
function foo(x = y + 3,z = bar(x)){
console.log(x,z);
}
var y = 5
foo() //'bar calles'
//8 13
foo(10) //'bar calles'
//10,15
y = 6
foo(undefined,10) //9,10
可以看到,默认值表达式是惰性求值,这意味着他们只在需要的时候运行--也就是是在参数省略或者为undefined的时候运行。
这里有一个微妙的细节,函数声明中参数是在他们自己的作用域中的,即foo()括号里面作用域,而不是在函数体作用域。这意味着默认值表达式中的标识符首先匹配到形式参数作用域,再到外层作用域(不会到函数体作用域中寻找)
ini
var w = 1,z = 2
function foo(x = w + 1,y = x + 1,z = z + 1){
console.log(x,y,z);
}
foo()
这段代码中,首先对参数进行rhs查询,w+1表达式会先在形式参数作用域寻找w,没有找到,因为在外层作用域中找到w。x+1会在形式参数作用域中找到x,而这里x已经被初始化了,因此对y的赋值工作可以正常运行。但是,z+1中的z会发现这是一个没有被初始化的参数变量,所以他永远不会从外层作用域中寻找z。在ES6中引入了TDZ,变量在未初始化的状态下访问会抛出TDZReferenceError报错。
3.解构与对象属性赋值模式
对于对象解构赋值有一个很巧妙但是极其重要的细节需要理解
ini
var x = 10,y = 20
var o = {a:x,b:y}
console.log(o.a,o.b); //10 20
在{a:x,b:y}中,我们知道a是对象属性,而x是要赋给他的值。换句话说,这是target:souce。
但在解构中这是相反的,解构的语法模式是souce:target
javascript
var aa = 10,bb = 20
var o = {x:aa,y:bb}
var {x:AA,y:BB} = o
console.log(AA,BB);//10 20
console.log(x,y); //ReferenceError
同时,解构可以捕获子对象/类的值本身
css
({a:x,a:y,a:[z]}) = {a:[1]} //解构的时候不用var或者let、const声明的话要用括号包起来
x.push(2)
y[0] = 10
x //[10,2]
y //[10,2]
z //1
很明显,解构实际上是浅拷贝,x和y指向同一个对象的地址
解构赋值表达式 对象或者数组解构的赋值表达式的完成值是所有右侧对象/数组的值
less
var o = {a:1,b:2,c:3}
var a,b,c,p
p = {a,b,c} = o //实际上相当于p = o
a //1
b //2
c //3
p === o //ture
通过持有对象/数组的值作为完成值,可以将解构赋值表达式组成链
css
let o = {a:1,b:2,c:3}
({a} = {b,c} = o)
4.对象字面量扩展
在ES6中,,关联到对象字面量属性上的函数也有简洁的方法
老方法
css
var o = {
x:function(){},
y:function(){}
}
在ES6中可以
javascript
var o = {
x(){},
y(){}
}
//实际上代码是
var o = {
x:function(){},
y:function(){}
}
可以看出来,简洁的代码实际上是一个匿名函数,这里的一个小小区别是:x:function(){}中的x只是对象的一个属性名称,是函数的标识符,可以通过o.x来调用函数,但是实际上这个函数是匿名函数,因此无法在这个函数内部使用递归或者事件绑定
5.标签模板字面量
typescript
function foo(string,...value) {
console.log(string);
console.log(value);
}
let a = 'hello'
foo`${a} world!` //['', ' world!']
//[ 'hello' ]
从本质上来说,这是一类不需要()的特殊函数调用。标签部分,即foo这部分,是一个要调用的函数值,他可以是任意结果为函数的表达式,甚至可以是一个结果为另一个函数的函数调用。
第一个参数名为string,是一个由所有普通字符串(插入表达式之间的部分)组成的数组,空格也算在内。
为了方便起见,第二个参数我们也收集成数组,value数组的参数是已经求值的在字符串字面值插入表达式的结果
6.原始字符串
在前面的代码中,标签函数的第一个参数还包括一些额外数据:所有字符串的原始未处理版本
typescript
function foo(string,...value) {
console.log(string);
console.log(string.raw);
}
foo`hello\nworld`
//[ 'hello
//world' ]
//[ 'hello\nworld' ]
即原始版本可以理解为保留了原始的转义码\n,而处理过的版本将它当做是一个单独的换行符