ES6对象的扩展
来源:互联网 发布:来个污女多的聊天软件 编辑:程序博客网 时间:2024/04/30 14:06
1、属性的简洁表示法
ES6允许直接写入变量和函数,作为对象的属性和方法。这样的书写更加简洁。
上面代码表明,ES6允许在对象之中,只写属性名,不写属性值。这时,属性值等于属性名所代表的变量。下面是另一个例子。
除了属性简写,方法也可以简写。
下面是一个实际的例子。
这种写法用于函数的返回值,将会非常方便。
CommonJS模块输出变量,就非常合适使用简洁写法。
属性的赋值器(setter)和取值器(getter),事实上也是采用这种写法。
注意,简洁写法的属性名总是字符串,这会导致一些看上去比较奇怪的结果。
上面代码中,class
是字符串,所以不会因为它属于关键字,而导致语法解析报错。
如果某个方法的值是一个Generator函数,前面需要加上星号。
2、属性名表达式
JavaScript语言定义对象的属性,有两种方法。
上面代码的方法一是直接用标识符作为属性名,方法二是用表达式作为属性名,这时要将表达式放在方括号之内。
但是,如果使用字面量方式定义对象(使用大括号),在ES5中只能使用方法一(标识符)定义属性。
ES6允许字面量定义对象时,用方法二(表达式)作为对象的属性名,即把表达式放在方括号内。下面是另一个例子。
表达式还可以用于定义方法名。
注意,属性名表达式与简洁表示法,不能同时使用,会报错。
3、方法的name属性
函数的name
属性,返回函数名。对象方法也是函数,因此也有name
属性。
上面代码中,方法的name
属性返回函数名(即方法名)。如果使用了取值函数,则会在方法名前加上get
。如果是存值函数,方法名的前面会加上set
。
有两种特殊情况:bind
方法创造的函数,name
属性返回“bound”加上原函数的名字;Function
构造函数创造的函数,name
属性返回“anonymous”。
4、Object.js
ES6提出“Same-value equality”(同值相等)算法,用来解决这个问题。Object.is就是部署这个算法的新方法。它用来比较两个值是否严格相等,与严格比较运算符(===)的行为基本一致。
不同之处只有两个:一是+0
不等于-0
,二是NaN
等于自身。
ES5可以通过下面的代码,部署Object.is
。
4、Object.assgin()
基本用法Object.assign
方法用于对象的合并,将源对象(source)的所有可枚举属性,复制到目标对象(target)。Object.assign
方法的第一个参数是目标对象,后面的参数都是源对象。
注意,如果目标对象与源对象有同名属性,或多个源对象有同名属性,则后面的属性会覆盖前面的属性。
如果只有一个参数,Object.assign
会直接返回该参数。
如果该参数不是对象,则会先转成对象,然后返回。
由于undefined
和null
无法转成对象,所以如果它们作为参数,就会报错。
undefined
和null
不在首参数,就不会报错。v1
、v2
、v3
分别是字符串、布尔值和数值,结果只有字符串合入目标对象(以字符数组的形式),数值和布尔值都会被忽略。这是因为只有字符串的包装对象,会产生可枚举属性。上面代码中,布尔值、数值、字符串分别转成对应的包装对象,可以看到它们的原始值都在包装对象的内部属性[[PrimitiveValue]]
上面,这个属性是不会被Object.assign
拷贝的。只有字符串的包装对象,会产生可枚举的实义属性,那些属性则会被拷贝。
Object.assign
拷贝的属性是有限制的,只拷贝源对象的自身属性(不拷贝继承属性),也不拷贝不可枚举的属性(enumerable: false
)。
上面代码中,Object.assign
要拷贝的对象只有一个不可枚举属性invisible
,这个属性并没有被拷贝进去。
属性名为Symbol值的属性,也会被Object.assign
拷贝。
注意点
Object.assign
方法实行的是浅拷贝,而不是深拷贝。也就是说,如果源对象某个属性的值是对象,那么目标对象拷贝得到的是这个对象的引用。上面代码中,源对象obj1
的a
属性的值是一个对象,Object.assign
拷贝得到的是这个对象的引用。这个对象的任何变化,都会反映到目标对象上面。
对于这种嵌套的对象,一旦遇到同名属性,Object.assign
的处理方法是替换,而不是添加。
上面代码中,target
对象的a
属性被source
对象的a
属性整个替换掉了,而不会得到{ a: { b: 'hello', d: 'e' } }
的结果。这通常不是开发者想要的,需要特别小心。
有一些函数库提供Object.assign
的定制版本(比如Lodash的_.defaultsDeep
方法),可以解决浅拷贝的问题,得到深拷贝的合并。
注意,Object.assign
可以用来处理数组,但是会把数组视为对象。
上面代码中,Object.assign
把数组视为属性名为0、1、2的对象,因此目标数组的0号属性4
覆盖了原数组的0号属性1
。
常见用途
Object.assign
方法有很多用处。
(1)为对象添加属性
上面方法通过Object.assign
方法,将x
属性和y
属性添加到Point
类的对象实例。
上面代码使用了对象属性的简洁表示法,直接将两个函数放在大括号中,再使用assign方法添加到SomeClass.prototype之中。
(3)克隆对象
上面代码将原始对象拷贝到一个空对象,就得到了原始对象的克隆。
不过,采用这种方法克隆,只能克隆原始对象自身的值,不能克隆它继承的值。如果想要保持继承链,可以采用下面的代码。
(4)合并多个对象
将多个对象合并到某个对象。
如果希望合并后返回一个新对象,可以改写上面函数,对一个空对象合并。
上面代码中,DEFAULTS
对象是默认值,options
对象是用户提供的参数。Object.assign
方法将DEFAULTS
和options
合并成一个新对象,如果两者有同名属性,则option
的属性值会覆盖DEFAULTS
的属性值。
注意,由于存在深拷贝的问题,DEFAULTS
对象和options
对象的所有属性的值,都只能是简单类型,而不能指向另一个对象。否则,将导致DEFAULTS
对象的该属性不起作用。
属性的可枚举性
对象的每个属性都有一个描述对象(Descriptor),用来控制该属性的行为。Object.getOwnPropertyDescriptor
方法可以获取该属性的描述对象。
描述对象的enumerable
属性,称为”可枚举性“,如果该属性为false
,就表示某些操作会忽略当前属性。
ES5有三个操作会忽略enumerable
为false
的属性。
for...in
循环:只遍历对象自身的和继承的可枚举的属性Object.keys()
:返回对象自身的所有可枚举的属性的键名JSON.stringify()
:只串行化对象自身的可枚举的属性
ES6新增了一个操作Object.assign()
,会忽略enumerable
为false
的属性,只拷贝对象自身的可枚举的属性。
这四个操作之中,只有for...in
会返回继承的属性。实际上,引入enumerable
的最初目的,就是让某些属性可以规避掉for...in
操作。比如,对象原型的toString
方法,以及数组的length
属性,就通过这种手段,不会被for...in
遍历到。
上面代码中,toString
和length
属性的enumerable
都是false
,因此for...in
不会遍历到这两个继承自原型的属性。
另外,ES6规定,所有Class的原型的方法都是不可枚举的。
总的来说,操作中引入继承的属性会让问题复杂化,大多数时候,我们只关心对象自身的属性。所以,尽量不要用for...in
循环,而用Object.keys()
代替。
7、属性的遍历
ES6一共有5种方法可以遍历对象的属性。
(1)for...in
for...in
循环遍历对象自身的和继承的可枚举属性(不含Symbol属性)。
(2)Object.keys(obj)
Object.keys
返回一个数组,包括对象自身的(不含继承的)所有可枚举属性(不含Symbol属性)。
(3)Object.getOwnPropertyNames(obj)
Object.getOwnPropertyNames
返回一个数组,包含对象自身的所有属性(不含Symbol属性,但是包括不可枚举属性)。
(4)Object.getOwnPropertySymbols(obj)
Object.getOwnPropertySymbols
返回一个数组,包含对象自身的所有Symbol属性。
(5)Reflect.ownKeys(obj)
Reflect.ownKeys
返回一个数组,包含对象自身的所有属性,不管是属性名是Symbol或字符串,也不管是否可枚举。
以上的5种方法遍历对象的属性,都遵守同样的属性遍历的次序规则。
- 首先遍历所有属性名为数值的属性,按照数字排序。
- 其次遍历所有属性名为字符串的属性,按照生成时间排序。
- 最后遍历所有属性名为Symbol值的属性,按照生成时间排序。
Reflect.ownKeys
方法返回一个数组,包含了参数对象的所有属性。这个数组的属性次序是这样的,首先是数值属性2
和10
,其次是字符串属性b
和a
,最后是Symbol属性。8、_proto_属性,Object.setPrototypeOf(),Object.getPrototypeOf()
(1)_proto_属性__proto__
属性(前后各两个下划线),用来读取或设置当前对象的prototype
对象。目前,所有浏览器(包括IE11)都部署了这个属性。该属性没有写入ES6的正文,而是写入了附录,原因是__proto__
前后的双下划线,说明它本质上是一个内部属性,而不是一个正式的对外的API,只是由于浏览器广泛支持,才被加入了ES6。标准明确规定,只有浏览器必须部署这个属性,其他运行环境不一定需要部署,而且新的代码最好认为这个属性是不存在的。因此,无论从语义的角度,还是从兼容性的角度,都不要使用这个属性,而是使用下面的Object.setPrototypeOf()
(写操作)、Object.getPrototypeOf()
(读操作)、Object.create()
(生成操作)代替。
在实现上,__proto__
调用的是Object.prototype.__proto__
,具体实现如下。
如果一个对象本身部署了__proto__
属性,则该属性的值就是对象的原型。
(2)Object.setPrototypeOf()
Object.setPrototypeOf
方法的作用与__proto__
相同,用来设置一个对象的prototype
对象。它是ES6正式推荐的设置原型对象的方法。
该方法等同于下面的函数。
下面是一个例子。
(3)Object.getPrototypeOf()
该方法与setPrototypeOf方法配套,用于读取一个对象的prototype对象。
下面是一个例子。9、Object.values(),Object.entries()
Object.keys()
ES5引入了Object.keys
方法,返回一个数组,成员是参数对象自身的(不含继承的)所有可遍历(enumerable)属性的键名。
Object.values
方法返回一个数组,成员是参数对象自身的(不含继承的)所有可遍历(enumerable)属性的键值。
返回数组的成员顺序,与《属性的遍历》部分介绍的排列规则一致。
上面代码中,属性名为数值的属性,是按照数值大小,从小到大遍历的,因此返回的顺序是b
、c
、a
。
Object.values
只返回对象自身的可遍历属性。
上面代码中,Object.create
方法的第二个参数添加的对象属性(属性p
),如果不显式声明,默认是不可遍历的。Object.values
不会返回这个属性。
Object.values
会过滤属性名为Symbol值的属性。
如果Object.values
方法的参数是一个字符串,会返回各个字符组成的一个数组。
上面代码中,字符串会先转成一个类似数组的对象。字符串的每个字符,就是该对象的一个属性。因此,Object.values
返回每个属性的键值,就是各个字符组成的一个数组。
如果参数不是对象,Object.values
会先将其转为对象。由于数值和布尔值的包装对象,都不会为实例添加非继承的属性。所以,Object.values
会返回空数组。
Object.entries
方法返回一个数组,成员是参数对象自身的(不含继承的)所有可遍历(enumerable)属性的键值对数组。除了返回值不一样,该方法的行为与Object.values
基本一致。
如果原对象的属性名是一个Symbol值,该属性会被省略。
上面代码中,原对象有两个属性,Object.entries
只输出属性名非Symbol值的属性。将来可能会有Reflect.ownEntries()
方法,返回对象自身的所有属性。
Object.entries
的基本用途是遍历对象的属性。
Object.entries
方法的一个用处是,将对象转为真正的Map
结构。
自己实现Object.entries
方法,非常简单。
10、Object.entries()
目前,ES7有一个提案,将Rest运算符(解构赋值)/扩展运算符(...
)引入对象。Babel转码器已经支持这项功能。
(1)解构赋值
对象的解构赋值用于从一个对象取值,相当于将所有可遍历的、但尚未被读取的属性,分配到指定的对象上面。所有的键和它们的值,都会拷贝到新对象上面。
上面代码中,变量z
是解构赋值所在的对象。它获取等号右边的所有尚未读取的键(a
和b
),将它们连同值一起拷贝过来。
由于解构赋值要求等号右边是一个对象,所以如果等号右边是undefined
或null
,就会报错,因为它们无法转为对象。
解构赋值必须是最后一个参数,否则会报错。
上面代码中,解构赋值不是最后一个参数,所以会报错。
注意,解构赋值的拷贝是浅拷贝,即如果一个键的值是复合类型的值(数组、对象、函数)、那么解构赋值拷贝的是这个值的引用,而不是这个值的副本。
上面代码中,x
是解构赋值所在的对象,拷贝了对象obj
的a
属性。a
属性引用了一个对象,修改这个对象的值,会影响到解构赋值对它的引用。
另外,解构赋值不会拷贝继承自原型对象的属性。
上面代码中,对象o3
是o2
的拷贝,但是只复制了o2
自身的属性,没有复制它的原型对象o1
的属性。
下面是另一个例子。
上面代码中,变量x
是单纯的解构赋值,所以可以读取继承的属性;解构赋值产生的变量y
和z
,只能读取对象自身的属性,所以只有变量z
可以赋值成功。
解构赋值的一个用处,是扩展某个函数的参数,引入其他操作。
上面代码中,原始函数baseFunction
接受a
和b
作为参数,函数wrapperFunction
在baseFunction
的基础上进行了扩展,能够接受多余的参数,并且保留原始函数的行为。
(2)扩展运算符
扩展运算符(...
)用于取出参数对象的所有可遍历属性,拷贝到当前对象之中。
这等同于使用Object.assign
方法。
扩展运算符可以用于合并两个对象。
如果用户自定义的属性,放在扩展运算符后面,则扩展运算符内部的同名属性会被覆盖掉。
上面代码中,a
对象的x
属性和y
属性,拷贝到新对象后会被覆盖掉。
这用来修改现有对象部分的部分属性就很方便了。
上面代码中,newVersion
对象自定义了name
属性,其他属性全部复制自previousVersion
对象。
如果把自定义属性放在扩展运算符前面,就变成了设置新对象的默认属性值。
扩展运算符的参数对象之中,如果有取值函数get
,这个函数是会执行的。
如果扩展运算符的参数是null
或undefined
,这个两个值会被忽略,不会报错。
11、Object.getOwnPropertyDescriptors()
Object.getOwnPropertyDescriptor
方法,返回某个对象属性的描述对象(descriptor)。Object.getOwnPropertyDescriptors
方法,返回指定对象所有自身属性(非继承属性)的描述对象。Object.getOwnPropertyDescriptors
方法返回一个对象,所有原对象的属性名都是该对象的属性名,对应的属性值就是该属性的描述对象。
该方法的实现非常容易。
该方法的提出目的,主要是为了解决Object.assign()
无法正确拷贝get
属性和set
属性的问题。上面代码中,source
对象的foo
属性的值是一个赋值函数,Object.assign
方法将这个属性拷贝给target1
对象,结果该属性的值变成了undefined
。这是因为Object.assign
方法总是拷贝一个属性的值,而不会拷贝它背后的赋值方法或取值方法。
这时,Object.getOwnPropertyDescriptors
方法配合Object.defineProperties
方法,就可以实现正确拷贝。
上面代码中,将两个对象合并的逻辑提炼出来,就是下面这样。
Object.getOwnPropertyDescriptors
方法的另一个用处,是配合Object.create
方法,将对象属性克隆到一个新对象。这属于浅拷贝。上面代码会克隆对象obj
。
另外,Object.getOwnPropertyDescriptors
方法可以实现,一个对象继承另一个对象。以前,继承另一个对象,常常写成下面这样。
__proto__
只有浏览器要部署,其他环境不用部署。如果去除__proto__
,上面代码就要改成下面这样。有了Object.getOwnPropertyDescriptors
,我们就有了另一种写法。
Object.getOwnPropertyDescriptors
也可以用来实现Mixin(混入)模式。
上面代码中,对象a
和b
被混入了对象c
。
出于完整性的考虑,Object.getOwnPropertyDescriptors
进入标准以后,还会有Reflect.getOwnPropertyDescriptors
方法
- ES6对象的扩展
- 【es6】对象的扩展
- ES6--对象的扩展
- ES6对象的扩展
- ES6-Array对象的扩展
- 【ES6】对象的扩展2
- es6入门-对象的扩展
- ES6--对象、函数的扩展
- 07、ES6 对象的扩展
- ES6(八) 对象的扩展
- ES6--内置对象的扩展
- ES6-对象的扩展-对象的扩展运算符
- ES6 对象的扩展 简单测试
- 自学-ES6篇-对象的扩展
- Math 对象es6上面的扩展
- es6 javascript对象的扩展运算符
- ES6之路--对象的扩展
- ES6 —(对象的扩展)
- 循环日程赛
- uva 1009 - Balloons in a Box
- 针对安卓系统相机之旋转相片
- 开源技术推荐:安全测试框架AndroBugs Framework
- java周切分,按照指定weekday为一周的开始
- ES6对象的扩展
- freeswitch结构之switch_core_session_t
- 秩为1的矩阵的幂规律
- Android,ijkplayer增加https协议支持,编译运行ijkplayer
- Android创建文件夹及文件并写入数据
- aaaaaaa
- ios 如何判断null类型
- POJ 2104&2761 主席树模版
- linux下ps与grep命令的相关用法