购买
下载掌阅APP,畅读海量书库
立即打开
畅读海量书库
扫码下载掌阅APP

2.7 对象的扩展

对象(object)是JavaScript中最重要的数据结构。ES6对它进行了重大升级。

2.7.1 对象字面量

ES6允许在大括号里面直接写入变量和函数,作为对象的属性和方法。这样的书写方式可使代码更加简洁,如代码示例2-64所示。

代码示例2-64

在上面的代码中,变量foo直接写在大括号里面。这时,属性名就是变量名,属性值就是变量值。下面是另一个例子,如代码示例2-65所示。

代码示例2-65

除了属性可以简写,方法也可以简写,如代码示例2-66所示。

代码示例2-66

一个实际的例子,如代码示例2-67所示。

代码示例2-67

属性的赋值器(setter)和取值器(getter)事实上也采用了这种写法,如代码示例2-68所示。

代码示例2-68

2.7.2 属性名表达式

JavaScript定义对象的属性有两种方法,如代码示例2-69所示。

代码示例2-69

上面代码的方法一直接用标识符作为属性名,方法二则用表达式作为属性名,这时要将表达式放在方括号之内。

但是,如果使用字面量的方式定义对象(使用大括号),则在ES5中只能使用方法一(标识符)定义属性,如代码示例2-70所示。

代码示例2-70

当ES6允许使用字面量定义对象时,用方法二(表达式)作为对象的属性名,即把表达式放在方括号内,如代码示例2-71所示。

代码示例2-71

下面是另一个例子,如代码示例2-72所示。

代码示例2-72

表达式还可以用于定义方法名,如代码示例2-73所示。

代码示例2-73

注意,属性名表达式与简洁表示法不能同时使用,否则会报错,如代码示例2-74所示。

代码示例2-74

注意,属性名表达式如果是一个对象,默认情况下会自动将对象转换为字符串[object Object],这一点要特别注意,如代码示例2-75所示。

代码示例2-75

在上面的代码中,[keyA]和[keyB]得到的都是[object Object],所以[keyB]会把[keyA]覆盖掉,而myObject最后只有一个[object Object]属性。

2.7.3 super关键字

我们知道,this关键字总是指向函数所在的当前对象,ES6又新增了另一个类似的关键字super,用于指向当前对象的原型对象。

在代码示例2-76中,对象obj.find()方法通过super.foo引用了原型对象proto的foo属性。

代码示例2-76

注意,当super关键字表示原型对象时,只能用在对象的方法之中,而用在其他地方都会报错,如代码示例2-77所示。

代码示例2-77

上面3种super的用法都会报错,因为对于JavaScript引擎来讲,这里的super都没有用在对象的方法之中。第1种写法将super用在属性里面,第2种和第3种写法将super用在一个函数里面,然后赋值给foo属性。目前,只有对象方法的简写法可以让JavaScript引擎确认所定义的是对象的方法。

JavaScript引擎内部,super.foo等同于Object.getPrototypeOf(this).foo(属性),如代码示例2-78所示。

代码示例2-78

在上面的代码中,super.foo指向了原型对象proto的foo方法,但是绑定的this还是当前对象obj,因此输出的是world。

2.7.4 对象的扩展运算符

前面介绍过扩展运算符,ES2018将这个运算符引入了对象。

1.解构赋值

对象的解构赋值用于从一个对象取值,相当于将目标对象自身的所有可遍历的、但尚未被读取的属性分配到指定的对象上面。所有的键和它们的值,都会复制到新对象上面,如代码示例2-79所示。

代码示例2-79

在上面的代码中,变量 z 是解构赋值所在的对象。它获取等号右边的所有尚未读取的键(a和b),将它们连同值一起复制过来。

由于解构赋值要求等号右边是一个对象,所以如果等号右边是undefined或null,就会报错,因为它们无法转换为对象,如代码示例2-80所示。

代码示例2-80

解构赋值必须是最后一个参数,否则会报错,如代码示例2-81所示。

代码示例2-81

在上面的代码中,解构赋值不是最后一个参数,所以会报错。

注意,解构赋值的复制是浅复制,即如果一个键的值是复合类型的值(数组、对象、函数),则解构赋值复制的是这个值的引用,而不是这个值的副本,如代码示例2-82所示。

代码示例2-82

在上面的代码中, x 是解构赋值所在的对象,复制了对象obj的a属性。a属性引用了一个对象,修改这个对象的值会影响解构赋值对它的引用。

另外,扩展运算符的解构赋值不能复制继承自原型对象的属性,如代码示例2-83所示。

代码示例2-83

在上面的代码中,对象o3复制了o2,但是只复制了o2自身的属性,而没有复制它的原型对象o1的属性。

下面是另一个例子,如代码示例2-84所示。

代码示例2-84

在上面的代码中,变量 x 是单纯的解构赋值,所以可以读取对象o继承的属性;变量 y z 是扩展运算符的解构赋值,只能读取对象o自身的属性,所以变量 z 可以赋值成功,变量 y 却取不到值。ES6规定,在变量声明语句之中,如果使用解构赋值,则扩展运算符后面必须是一个变量名,而不能是一个解构赋值表达式,所以上面代码引入了中间变量newObj,如果写成下面这种形式就会报错。

代码示例2-85

解构赋值的一个用处是扩展某个函数的参数,引入其他操作,如代码示例2-86所示。

代码示例2-86

在上面的代码中,原始函数baseFunction()接收a和b作为参数,函数HigerFunction()在baseFunction()的基础上进行了扩展,能够接收多余的参数,并且保留原始函数的行为。

2.扩展运算符

对象的扩展运算符用于取出参数对象的所有可遍历属性,然后复制到当前对象之中,如代码示例2-87所示。

代码示例2-87

由于数组是特殊的对象,所以对象的扩展运算符也可以用于数组,如代码示例2-88所示。

代码示例2-88

如果扩展运算符的后面是一个空对象,则没有任何效果,如代码示例2-89所示。

代码示例2-89

如果扩展运算符后面不是对象,则会自动将其转换为对象,如代码示例2-90所示。

代码示例2-90

在上面的代码中,扩展运算符后面是整数1,会自动转换为数值的包装对象Number{1}。由于该对象没有自身属性,所以返回一个空对象。

但是,如果扩展运算符后面是字符串,则它会自动转换成一个类似数组的对象,因此返回的不是空对象,如代码示例2-91所示。

代码示例2-91

对象的扩展运算符等同于使用Object.assign()方法,如代码示例2-92所示。

代码示例2-92

上面的例子只是复制了对象实例的属性,如果想完整克隆一个对象,并且复制对象原型的属性,则可以采用下面的写法,如代码示例2-93所示。

代码示例2-93

在上面的代码中,写法一的__proto__属性在非浏览器的环境中不一定可部署,因此推荐使用写法二和写法三。

扩展运算符可以用于合并两个对象,如代码示例2-94所示。

代码示例2-94 nxgmlXio21ktoOurEH3BFyhjoSFaG1cwJDsgpeTAblo8SrWDvy5LJyFTFb9M3iyT

点击中间区域
呼出菜单
上一章
目录
下一章
×