ES6学习——新的语法:对象解构(Object Destructuring)

来源:互联网 发布:网络每4分钟断一次 编辑:程序博客网 时间:2024/05/02 02:43

解构在ES6中应该是一种新的语法,在其他语言中我没怎么见到这种语法,也可以说是赋值操作的另一种形式,因为解构的整个定义都在规范的赋值操作符章节下面,有兴趣的可以看规范的12.14.5。目前浏览器对这个新语法的支持还不是很好,但是Firefox 43已经支持了大部分解构特性,这里我们仍然继续使用Kinoma Studio来测试代码。

这篇文章主要讲对象的解构赋值操作,先看个简单的例子有点印象:

let obj = { first: 'Jane', last: 'Doe' };let { first: f, last: l } = obj;//<strong>仔细看这里,我们习惯赋值是右侧表达式赋值给左侧表达式,但解构却是从左侧赋值给右侧</strong>trace(f + " " +l);//Jane Doelet f = obj.first,l = obj.last//旧的写法

这个简单的例子可能看不出来这种语法的必要性,先不要着急,我们先弄清一个问题,在解构赋值的右侧,也就是右侧表达式,可以是什么?在规范的12.14.5.2章节中有如下的描述:

ObjectAssignmentPattern : { } 

1. Let valid be RequireObjectCoercible(value). 

2. ReturnIfAbrupt(valid). 

3. Return NormalCompletion(empty). 


从上面的描述中可以清晰的看到,需要右侧的表达式可以转换成对象,在有兴趣的可以继续看RequireObjectCoercible方法是怎么定义的,这里就不在继续解释了,我们可以记住除了null和undefined都可以转成对象形式,即使是字面量,像:2,"123",true等。下面看个例子:

let {length : len} = 'abc'; // len = 3let {toString: s} = 123; // s = Number.prototype.toStringtrace(len+"\n");//3trace(s);//[object Function]let { prop: x } = undefined; // # Exception: run: cannot coerce undefined to object!let { prop: y } = null; // # Exception: run: cannot coerce undefined to object!

接着看两个例子,说明其他特点:

let obj = { a: [{ foo: 123, bar: 'abc' }, {}], b: true };let { a: [{foo: f}] } = obj; //深层嵌套赋值let { x: x } = { x: 7, y: 3 ,z:6};//只取x的值trace(f);//123trace(x);//7

解构操作还有一种更简单的写法,如果需要赋值的变量名和对象的属性名称一样,可以写成下面这样:

let { x, y } = { x: 11, y: 8,z:6 };trace(x + " " + y);//11 8

其实我们在左侧可以使用计算属性的特性,计算属性是Object Literal Extensions章节要讲的内容,这里先大概了解一下:

const FOO = 'foo';let { [FOO]: f } = { foo: 123 }; // f = 123//FOO可以是变量

上面的例子中左侧都是声明变量并初始化的形式,那么左侧可不可以直接给变量赋值呢?答案肯定是可以的,但要注意形式:

let x,y,obj = { x: 7, y: 3 };//{x,y} = obj;//语法错误,大括号里面会被当成复合语句。这里需要记住({x,y} = obj);


我们在用eval函数处理JSON字符串的时候也会有这个问题,所以需要加上小括号才行:eval('('+json+')'),道理一样。还有一个问题是重复声明的语法错误,需要注意:

let x;let { x, y } = { x: 11, y: 8 };//注意这里会有语法错误,报重复声明x变量

下面说一个我在使用Kinoma中遇到的一个解构的问题:
var o = {};({x:o.x,y:o.x,z:o.z} = {x:2,y:4,z:5});//这种写法在规范中应该是可以的,但是在Kinoma中总是报no reference错误,无法执行。我换了Firefox 43进行测试,上面的写法是OK的。还有一点要注意,上面是先声明了变量o,然后在进行解构赋值。如果我们直接声明并初始化的形似:var {x:o.x} = {x:2};//这里会报错,并不会把o先声明成对象,然后在进行解构

下面我们看一个连续赋值的问题:

var o = { a:1, b:2, c:3 },a, b, c, p;p = {c} = {a,b} = o;trace( a + " " + b + " " + c); // 1 2 3trace(p === o);//true

上面的例子说明了解构操作不会影响变量的连续赋值,最终p是严格等于o的。


解构操作还有一个特性,就是可以有默认值:

let {foo: x=3, bar: y} = {}; // x = 3; y = undefinedlet {prop: y=2} = {prop: undefined}; // y = 2function log(x) { trace(x+"\n"); return'YES'}let {x:a = log(4)} = {x:0};trace(a);//'YES',注意这里默认值的处理和规范不一样,具体参考函数参数默认值那篇文章//基本上函数参数默认值的那些特性都可以应用在这里。应用默认值的时候不要写的太复杂,否则很难看懂,像下面例子这样:var x = 200, y = 300, z = 100;var o1 = {x: { y: 42 }, z: { y: z } };({ y: x = { y: y } } = o1);//o1没有y属性,所以x被赋与默认的对象,默认对象中有个y属性,值为全局变量y的值({ z: y = { y: z } } = o1);//把o1摸z属性赋值给y.y,值是全局变量z({ x: z = { y: x } } = o1);//把o1的x属性赋给z.y,值为o1.x.ytrace( x.y + " " + y.y + " " + z.y ); // 300 100 42

最后看个简单的例子,查找数组元素第一个为偶数的数值和位置:

function findArray(arr,cb){for(let i=0;i<arr.length;i++){if(cb(arr[i])){return {ele:arr[i],index:i};}}return {ele:undefined,index:-1};}let arr = [21,3,5,6,7],cb = function(ele){return ele % 2 == 0};<strong>let {ele,index} = findArray(arr,cb);</strong>trace(ele + " " + index);                let {index:idx} = findArray(arr,cb);

解构这种用法在一些情况下确实会简单化代码,大家可以在ES6得到应用的时候大胆使用解构赋值。


*以上代码全部在Kinoma Studio中通过测试

0 0