ES6中的对象
对象是一组数据和功能的集合,在我们日常编程中必不可缺,ES6 在对象方面做了很多改进,一起来看下。
一、对象字面量扩展
1.1、对象属性简写
要定义一个对象,为了简单方便起见,通常会使用对象字面量的形式,如下:
var name = 'yix', |
都是相同名称,或许,你会觉得在 person
对象里要写两遍 name
和 age
显得比较繁琐。针对这个问题,ES6提供了一个简写方式:
var name = 'yix', |
如上面代码所看到的,所谓的简写方式主要是通过去除对象属性后面的 冒号,以及相同名称的属性值。
所以,在 ES6 中,以下两种写法也是等价的:
function person(name, age) { |
1.2 函数简写定义
类似地,除了对象属性简写外,ES6 还引入了函数定义的简写。如,下面两种写法是等价的:
var person = { |
通过对比,我们可以发现,在对象中函数简写定义就是去除 冒号,以及关键词 function
。这样一来,整体代码变小,也提高了编码效率。
1.3 获取属性名
对象字面量是一组键值对的组合。一般来说,要设置或获取对象中的属性值,主要有两种方式,通过 方括号 []
或者使用 点号 .
。如下面:
var person = {}; |
这两种获取属性值的方式各有优缺点,点号 .
方式书写更简洁,但对于一些特殊的属性名,比如,当属性名中包含空格时,它无法正常获取属性值,甚至会抛出一个错误:
var person = { |
由上面代码,方括号 []
这种形式则不会受含有空格属性名的限制,但它的写法相对于 点号 .
来说又繁琐了些。
在 ES6 中,我们可以使用变量来定义对象中的属性,同时,也可以使用该变量(或等价的值)来获取对象下对应的属性值:
var myName = 'my name'; |
这样的好处是,我们可以同时在对象内部或外部(其他函数或对象)使用这个变量。
其实,不仅仅如此。ES6 还允许我们通过 表达式 的方式来定义属性:
var name = ' name'; |
二、对象方法扩展
2.1 Object.is()
要比较两个数据是否相等,我们通常会使用 ==
和 ===
。因为使用 ==
作比较时,右边的数据会先作类型转换再进行比较,这样就会经常出现一些不同类型的数据却相等的情况。所以,在平常的开发中,我们更倾向于使用 ===
,因为它是直接比较,只有完全相等的两个数据才会返回 true
。
ES6 中在对象上新增了 Object.is()
方法,我们用它来判断两个数据是否完全相等,它的作用和 ===
基本相同,但也有略微差异:
console.log(0 === 0); // true |
其实,在我们看来,+0 和 0 的确是不等,但 ===
却返回了 true
,而 Object.is(+0, -0)
则是返回 false
。
ES5中规定,NaN
与任何值(甚至自身)都不相等,所以 ===
返回了 false
。但实际上,我们会觉得这种设计是存在缺陷的,为什么 NaN
不等于 NaN
?所以, Object.is(NaN, NaN)
返回的结果是 true
。
而对于一些正常数据的比较,基本和 ===
返回的值是相同的。
2.2 Object.assign()
在我们平时编码中,会经常执行合并对象的操作。如果你是用 jquery库,你可以使用它现成的 API,即 $.extend(target, sources1, sources2)
。但如果你是用原生js,那你只能先循环读取一个或多个源对象,再循环读取每个源对象下的属性,并把这些属性赋值到目标对象上,以下是简单实现:
if (typeof Object.assign != 'function') { |
在 ES6 中,Object.assign()
方法可以把任意多个的源对象自身的可枚举属性拷贝给目标对象,然后返回目标对象。 它的语法如下,其中 target 为目标对象,而 sources 为任意多个源对象:
Object.assign(target, ...sources) |
来看它的一些实际例子,首先是合并多个对象:
var target = {a: 1}, |
当源对象中和目标对象之间、或者源对象和源对象之间,有属性名是相同的,则后面对象的属性值会覆盖前面的:
var target = {a: 1}, |
属性值是基本类型还好,但倘若属性值为复杂类型,则通常会出现一些值得丢失,因为会被覆盖:
var obj1 = {people: {name: 'yix', age: 28}}, |
如果该方法的参数是原始类型,则它会把该参数转换为对象,并且需要注意的是,只有字符串类型才能被枚举出来,而数字类型、布尔类型只会转换为对应的包装对象:
var v1 = 'abc'; |
如果该方法的参数是数组,它同样会把数组当成对象来处理。比如 [‘a’, ‘b’] 会处理成 {0: ‘a’, 1: ‘b’},所以如果合并的两个数组的话,则后面的数组项会替换前面的数组项,因为它们具有相同的属性名:
var obj = Object.assign(['a', 'b', 'c'], ['aa', 'bb'], ['e']); |
这显然是不可取的。上面的做法只是一个演示,如果要合并多个数组,我们可以用数组自身的方法 concat
。
如果该方法的参数是一些特殊值,比如 undefined
、 null
,那它会直接报错,因为这些特殊值无法转换为对象:
console.log(Object.assign(undefined)); // 报错 Cannot convert undefined or null to object |
前面的定义中说了,Object.assign
只是浅拷贝,它只能拷贝 源对象自身的并且可枚举的属性,而 继承属性和不可枚举属性是不能拷贝的。
var person = function(name) { |
上面中的 age 属性是对象原型上的,这是属于原型继承属性,因此不能被拷贝到源对象上。
三、原型方法扩展
一直以来,原型(prototype)都是JS中面向对象编程最重要的一部分。通过它,我们可以在对象之间继承各种属性和方法。
3.1 Object.getPrototypeOf()
该方法最早出现在 ES5 中,它的作用是获取对象的prototype:
var person = { |
3.2 Object.setPrototypeOf()
虽然通过上面的方法很轻松的就获取到对象的prototype,但却没有一种符合规范的方法可以改变对象的prototype(实际上可以用 __proto__
进行 Polyfill),也就是说只能读取,不能设置。而 ES6 提供了 Object.setPrototypeOf
方法解决了这个问题,该方法的语法如下,其中 obj
为被设置原型的对象,而 prototype
为设置的原型:
Object.setPrototypeOf(obj, prototype) |
来看个实际例子:
var person1 = { |
上面的例子中,newPerson
一开始继承的是 person1
对象,所以对应的属性和原型都是 person1
对象。而后面通过 setPrototypeOf
改变了 newPerson
的原型,于是它所有的内容都来自 person2
对象。其实读取和设置原型,都是基于js内部的 [[Prototype]]
对象。
3.3 super
由于对象中存在多层继承,有时候可能无法找到对象的原型对象。而 ES6 中引入 super
,它是一个指向当前对象原型的指针,这样,我们可以很轻松的访问原型对象。
还是用例子来说明下:
var person1 = { |
上面的代码中,对象 person3
的 super
为 person2
,而 person4
的 super
为 person3
。