聊聊ES6的解构赋值(进阶篇)

1,227

嵌套解构

如果你正在解构的值拥有嵌套的对象或数组,你也可以解构这些嵌套的值:

var a1= [1,[2,3,4],5];
var o1 = { x:{y:{z:6}} };
var  [ a,[b,c,d],e ] = a1;
var { x:{ y :{ z:w}} } = o1;
console.log(a,b,c,d,e)
// 1,2,3,4,
5console.log( w )
// 6

嵌套的解构可以是一种将对象名称空间扁平化的简单方法,例如:

var app = {   
    model:{      
        User:function(){...}   
    }
}

//取代:

// var User = App.model.User;

var { model :{ User } } = App;  

参数解构

你能在下面的代码中发现赋值吗?

function foo(x) { 
    console.log( x );
}
foo( 42 );

其中的赋值有点儿呗隐藏的感觉:当foo( 42 )被执行时,42倍赋值给x。如果参数/参数值是一种赋值,那么按常理说它是一个可以被解构的赋值。

考虑参数的数组解构:

function foo( [x,y] ){
    console.log(x,y)
}
foo( [ 1, 2]  )/
/ 1 2
foo( [ 1] );
// 1 undefined
foo( [ ] )
// undefined undefined

参数也可以进行对象解构:

function foo ( { x , y } ){
    console.log( x,y )
}
foo( { y:1,x:2 } );
// 2 1
foo( { y: 42 } )
//undefined 42
foo( {} )
// undefined undefined

这种技术是明明参数值(一个长期以来被渴求的JS特性)的一种近似解法:对象上的属性映射到被解构的同名参数上。这也意味着我们免费地(在任何位置)得到了可选参数,如你所见,省去参数x 可以如我们期望的那样工作。

当然,先前讨论过得所有解构的种类对于参数解构来说都是可用的,包括嵌套解构,默认值和其他。解构也可以和其他ES6函数参数功能很好地混合在一起,比如默认参数值和剩余/收集参数。示例:

function f1([ x= 2,y=3,z ]){...}
function f2([ x,y,...z ],w){...}
function f3([ x,y,...z ],...w){...}
function f4( { x:X ,y} ){...}
function f5( { x:X=10,y=20} ){...}
function f6( { x = 10 } = {},{ y } ){...}

我们从这个代码段中取出一个例子:

function f3([ x,y,...z ],...w){
  console.log(x,y,z,w)
}
f3( [ ] )
// undefined undefined [] []
f3([1,2,3,4],56)
// 1 2 [ 3,4 ] [ 5, 6 ]

这里使用了两个...操作符,他们都是将值收集到数组中(z和w),虽然...z是从第一个数组参数值的剩余值中收集,而 ...w是从第一个之后的剩余主参数值中收集的。

解构默认值+参数默认值

有一个微妙的地方你应当注意要特别小心——解构默认值与函数参数默认值的行为之间的不同。例如:

function fn6( { x = 10 }  = {} , { y } = {} ){   
     console.log(x,y)
 }
fo( {},{} )
// 10 undefined

为什么这样?如果在第一个参数值的对象中没有一个同名属性被传递,那么命名参数x将默认为10。但y是undefined是怎么回事?值 { y:10 }是一个作为函数参数默认值的对象,不是结构默认值。因此,它仅在第二个参数根本没有被传递或者undefined被传递时生效

在前面代码中,我们传递了第二个参数({}),所以默认值 { y:10 }不被使用,而解构 { y }会针对被传入的空对象{}发生。

现在,将 { y } = { y:10 } 与 { x =10 } = {} 比较一下。

对于x的使用形式来说,如果第一个函数参数值被省略或者undefined,会默认地使用空对象{}。然后,不管在第一个参数值的位置上是什么值——要么默认的{},要么是你传入的——都会被 { x=10 }解构,它会检查属性x是否被找到,如果没有找到或者是undefined,默认值10都会被设置到命名参数x上。

 function fn6( { x = 10 }  = {} , { y } = { y:10 } ){ 
    console.log(x,y)
}
fn6()// 10 10
fn6(undefined,undefined)
// 10 10f
n6({},{}) 
// 10 undefined
fn6({x:2},{y:3})
//  2 3

一般来说,与参数y的默认行为比起来,参数x的默认行为可能看起来更可取也更合理。因此,理解{ x = 10 } = {  }形式与 { y } = { y = 10 }形式为何与如何不同是很重要的。如果这仍然有点儿模糊,回头再把它读一遍,并亲自玩弄一番。

嵌套默认值:解构与重构

虽然一开始可能很难掌握,但是伟一个嵌套的对象的属性设置默认值产生了一种有趣的惯用法:将对象解构与一种我称为重构的东西一起使用。考虑在一个嵌套的对象结构中的一组默认值,就像下面这样:

var defaults = { 
    options:{   
        remove :true,
        enable:false,
        instance:{}
    },
    log:{
        warn:true,
        error:true
    }
};

现在,我们假设你有一个称为config的对象,它有一些这其中的值,但也许不全有,而且你想要将所有的默认值设置到这个对象的缺失点上,但不覆盖已经存在的特定设置:

var config = {   
    options:{     
        remove:false,    
        instancs:null
}
}

有些人可能喜欢用覆盖赋值的方式来完成这个任务,你可能会被ES6的Object.assign工具所吸引,来首先克隆defaults中的属性然后使用从config中克隆的属性覆盖它。但是这里有一个重大的问题,Object.assign是浅拷贝,这意味着当它拷贝defaults.options时,它仅拷贝这个对象的引用,而不是深度克隆这个对象的属性到一个config.options对象。Object.assign需要在你的对象树的每一层中实施才能得到你期望的深度克隆。那让我们检视一下ES6的带有默认值的对象解构能否帮到我们:

config.options = config.options || {}
config.log = config.log || {}
({  
   options :{    
    remove:config.options.remove = defaults.options.remove,    
    enable:config.options.enable = defaults.options.enable,    
    instance = config.options.instance = defaults.options.instance   
 } = {},   
log:{     
    warn:config.log.warn = defaults.log.warn     
    error:config.log.error = defaults.log.error    
} = {}   
} = config )

不像Object.assign(...)的虚假诺言(因为它只是浅拷贝)那么好,但是我想它要比手动的方式强多了。虽然它仍然很不幸地带有冗余和重复。

前面的代码段的方式可以工作,因为我黑进了结构和默认机制来为我做属性的===undefined检查和赋值的决定。这里的技巧是。我解构了config(看看代码段末尾 = config ),但是我将所有解构出来的值又立即赋值回config,带着config.options.enable赋值引用,但还是太多了,下面的技巧在你知道你正在解构的所有属性的名字都是唯一的情况下做的最好。但即使不是这样的情况你也仍然可以使用它,只是没有那么好——你将不得不分阶段解构,或者创建独一无二的本地变量作为临时的别名。

如果我们将所有的属性完全解构为顶层变量,那么我们就可以立即重构来重组原本的嵌套对象解构,但是所有那些游荡在外的临时变量将会污染作用域。所以我们通过一个普通的{}包围块来使用块作用域。

 {    
    let {      
        options:{        
            remove = defaults.options.remove,       
            enable = defaults.options.enable,
            instance = defaults.options.instance      
         } = {},      
         log:{        
             warn=defaults.log.warn,        
             error=defaults.log.error      
         } = {}    
    } = config;    
//重构
  config = {      
    options:{remove,enable,instance },      
    log:{warn,error}    
    }  
}

这样看起来就好多了!

希望此篇文章给大家带来帮助!本文章部分摘自**《你不懂的JS》**!