在平常开发过程当中,虽然 Object
上的一些原生方法经常会看到,比如在一些扩展的第三方插件中可能会遇到,但是遇到了类似于 Object.fromEntries()
和 Object.entries()
这种长的比较像的但是使用频率较低的 API
,往往又是傻傻分不清楚,更别说它们具体是做什么用的,所以今天就打算将其汇总一下,将 Object
上涉及到的平常可能会遇到的 API
整体的学习记录一下,免得下次再次遇到又是一头雾水
其实简单来说,Object
上的原生 API
主要分为两部分,一部分是 Object
上面的方法,而另一部分则是 Object.prototype
上面的方法,下面我们就一个一个来看
Object 上的方法
Object
上面涉及到的方法其实不算很多,大致都可以按类别划分,所以我们将会分类来进行介绍,下表是一些本文当中没有涉及到的方法,其中有一些在之前的文章当中我们也都已经介绍过了,感兴趣的话可以自行参考,所以这里就不详细展开了
方法 | 描述 |
---|---|
Object.assign() |
用于将所有可枚举属性的值从一个或多个源对象复制到目标对象,一般克隆场景使用较多,详细可见 对象的浅拷贝 |
Object.create() |
创建一个新对象,使用现有的对象来提供新创建的对象的 __proto__ ,详细可见 Object.create() |
Object.is(value1, value2) |
判断两个值是否是 相同的值(同 === ),但是这个可以比对 NaN |
Object.getOwnPropertyNames() |
返回一个由指定对象的所有自身属性的属性名(包括不可枚举属性但不包括 Symbol 值作为名称的属性)组成的数组,详细可见 Object.getOwnPropertyNames() |
Object.getOwnPropertySymbols() |
返回一个给定对象自身的所有 Symbol 属性的数组 |
Object.values() |
返回一个给定对象自身的所有可枚举属性值的数组 |
Object.keys() |
返回一个由一个给定对象的自身可枚举属性组成的数组,这两个详细可见 Object.keys() |
Object.defineProperty() 和 Object.defineProperties()
涉及到的两个方法如下
方法 | 描述 |
---|---|
Object.defineProperty() |
直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回这个对象,详细可见 Object.defineProperty() |
Object.defineProperties() |
这个同上面那个类似,但是可以同时定义和修改多个属性 |
Object.defineProperty()
这个属性在之前我们已经介绍过了,该方法允许精确添加或修改对象的属性,比如 enumerable
,configurable
和 writable
等,也可以用其来实现数据双向绑定,可谓是用处多多,不过我们今天主要来看这个跟它长的十分相像的 Object.defineProperties()
其实它俩是一个东西,不过当定义或修改对象的多个属性时,使用 Object.defineProperty()
就会比较麻烦了,在这种情况下我们可以考虑使用 Object.defineProperties()
1 | var man = {} |
Object.entries() 和 Object.fromEntries()
涉及到的两个方法如下
方法 | 描述 |
---|---|
Object.entries() |
返回一个给定对象自身可枚举属性的键值对数组,详细可见 Object.entries(obj) |
Object.fromEntries() |
可以把把键值对列表转换为一个对象 |
Object.entries()
在迭代器相关章节我们曾经使用过这个方法,它接收一个可以返回其可枚举属性的键值对的对象,返回给定对象自身可枚举属性的键值对数组,Object
之所以不能被 for-of
遍历,主要是因为它没有部署 Iterator
接口,在这种情况下我们可以使用 Object.entries()
将其包裹一下,利用其返回的键值对数组再来进行遍历(会存在一定问题,以实际使用场景来决定是否这样使用),看下面这两个例子
1 | const obj = { |
这里需要注意,上面例子的返回结果的排序是改变过的,至于为什么可以参考 为什么 Object.keys 的返回值会自动排序,另外一个例子就是将 Object
转换为 Map
,new Map()
函数接受一个可迭代的 entries
,借助 Object.entries
方法可以很容易的将 Object
转换为 Map
1 | var obj = { foo: 'bar', baz: 42 } |
下面我们再来看看 Object.fromEntries()
这个方法,其实简单来说,就是 Object.entries
的反转
但是需要注意的是,这个
API
现在的兼容性还不是很好,可以考虑使用 polyfill
该方法接收一个键值对的列表参数(可迭代对象,类似 Array
,Map
或者其它实现了可迭代协议的对象)并返回一个由该迭代对象条目提供对应属性的新对象,生成的是一个具有两个元素的类数组的对象,第一个元素是将用作属性键的值,第二个元素是与该属性键关联的值,来看几个例子,比如将 Map
转化为 Object
1 | const map = new Map([['foo', 'bar'], ['baz', 42]]) |
也可以将 Array
转化为 Object
1 | const arr = [['0', 'a'], ['1', 'b'], ['2', 'c']] |
getOwnPropertyDescriptor() 和 getOwnPropertyDescriptors()
涉及到的几个方法如下
方法 | 描述 |
---|---|
Object.getOwnPropertyDescriptor() |
返回指定对象上一个自有属性对应的属性描述符(直接赋予的,不需要从原型链上进行查找的属性) |
Object.getOwnPropertyDescriptors() |
获取一个对象的所有自身属性的描述符 |
Object.setPrototypeOf() |
这个方法不建议使用,它的作用是设置一个指定的对象的原型到另一个对象,更推荐使用 Object.create() |
Object.getPrototypeOf() |
返回指定对象的原型(内部 [[Prototype]] 属性的值) |
这两个方法的作用也是一样的,都是返回指定对象『自有属性』对应的属性描述符,不过一个是返回指定的,一个是返回全部的
需要注意,这里指的是自有属性,意思是直接赋予该对象的属性,而不需要从原型链上进行查找的属性
两者的语法如下
1 | Object.getOwnPropertyDescriptor(obj, prop) |
下面我们通过一个例子来了解它们如何使用
1 | var obj = {} |
关于 Object.getOwnPropertyDescriptor(obj, prop)
方法有一个需要注意的地方
- 在
ES5
中,如果该方法的第一个参数不是对象(而是基本类型),那么就会产生出现TypeError
- 而在
ES6
中,第一个的参数不是对象的话就会被强制转换为对象
但是 Object.getOwnPropertyDescriptors(obj)
这个方法的作用不仅仅只是用于查看对象的属性描述符,比如还可以用来『浅拷贝』对象
1 | var obj = { |
另外还可以用来创建子类,创建子类的典型方法是定义子类,将其原型设置为超类的实例,然后在该实例上定义属性,其实就是我们常说的继承,也就是原来经常使用的
1 | father.call(this) |
但是现在我们可以通过 Object.getOwnPropertyDescriptors()
更为优雅的来实现
1 | function Foo() { } |
在上面浅拷贝的例子当中涉及到一个方法 Object.getPrototypeOf(obj)
,它的作用是返回指定对象的原型(内部 [[Prototype]]
属性的值),比如下面这个例子
1 | var reg = /^\s/ |
但是这里特别需要注意了,Object.getPrototypeOf(Object)
返回的并不是 Object.prototype
,看下面这个特殊的例子
1 | Object.prototype === Function.prototype.__proto__ // true |
在 JavaScript
中的 Object
其实是构造函数,即是创建对象的包装器,所以我们一般用法是
1 | var obj = new Object() |
而 Object.getPrototypeOf(Object)
的意思是把 Object
这一构造函数看作对象,返回的当然是函数对象的原型,也就是 Function.prototype
,所以结果是 true
,所以正确的方法应该是,Object.prototype
是构造出来的对象的原型
1 | var obj = new Object() |
同样的,Object.getPrototypeOf(obj)
方法在 ES5
中的参数如果不是对象,也会出现 TypeError
,而 ES6
中会被强制转换为对象
Object.preventExtensions(),Object.seal() 和 Object.freeze()
涉及到的几个方法如下
方法 | 描述 |
---|---|
Object.preventExtensions() |
让一个对象变的不可扩展,也就是永远不能再添加新的属性 |
Object.isExtensible() |
判断一个对象是否是可扩展的(是否可以在它上面添加新的属性) |
Object.seal() |
封闭一个对象,阻止添加新属性并将所有现有属性标记为不可配置 |
Object.isSealed() |
判断一个对象是否被密封 |
Object.freeze() |
冻结一个对象,一个被冻结的对象再也不能被修改 |
Object.isFrozen() |
判断一个对象是否被冻结 |
最后这六个我们放到一起来进行介绍,因为它们主要涉及到的都是对象的扩展、密封和冻结,通过字面意思也可以发现,它们针对于对象限制的严格程度是一层更胜一层
Object.preventExtensions()
先来看看 Object.preventExtensions()
,它的作用是阻止对象扩展,让一个对象变的不可扩展,也就是永远不能再添加新的属性,但是也有几个需要注意的地方
- 一般来说,不可扩展对象的属性可能仍然可被删除,只是不可扩展
Object.preventExtensions()
仅阻止添加属性,但属性仍然可以添加到对象原型
1 | // 示例一 |
如果是严格模式,则会报错
1 | 'use strict' |
在 JavaScript
当中新增了一个 Object.isExtensible()
方法用来判断一个对象是否是可扩展的
1 | // 新对象默认是可扩展的 |
Object.seal()
下面我们再来看看密封,密封则会在不可扩展的基础之上更进一步,Object.seal()
方法会封闭一个对象,阻止添加新属性并将所有现有属性标记为不可配置,简单来说就是不能添加新的属性,不能删除已有属性,以及不能修改已有属性的可枚举性、可配置性、可写性,但可以修改已有属性的值的对象
1 | var obj = { name: 'zhangsan' } |
如果修改已有属性的可枚举性、可配置性、可写性,会提示报错
1 | var obj = { |
同样的,也提供了 Object.isSealed()
方法用来判断一个对象是否被密封
1 | var obj = {} |
如果一个对象不可扩展,并且它的属性也变的不可配置,则这个对象也就成了密封对象
1 | var obj = { |
Object.freeze()
最后我们再来看看 Object.freeze()
这个方法,这个方法比 Object.seal
更绝,冻结对象是指那些不能添加新的属性,不能修改已有属性的值,不能删除已有属性,以及不能修改已有属性的可枚举性、可配置性、可写性的对象,也就是说,这个对象永远是不可变的,比如在一般情况下任何修改尝试都会静默失败
1 | var obj = { |
但是在严格模式下则会报错
1 | 'use strict' |
比如之前的示例当中,密封一个对象还是可以修改的,但是 Object.freeze()
这个方法
1 | var obj = { name: 'zhangsan' } |
不仅仅适用于对象,数组也是一样的情况,不过被冻结的对象也不是不可变的,比如冻结对象不是常量对象
1 | obj = { |
同拷贝一样,要使对象不可变,需要递归冻结每个属性
1 | // 深冻结函数 |
Object.prototype 上的方法
相较于 Object
而言,Object.prototype
上的方法就少了许多,排除掉一些实验性的,主要有下面这些
方法 | 描述 |
---|---|
Object.prototype.hasOwnProperty() |
返回一个布尔值,指示对象自身属性中是否具有指定的属性(也就是,是否有指定的键),见 JavaScript 中的类型判断 |
Object.prototype.toString() |
返回一个表示该对象的字符串,见 JavaScript 中的类型判断 |
Object.prototype.isPrototypeOf() |
用于测试一个对象是否存在于另一个对象的原型链上,见 JavaScript 中的类型判断 |
Object.prototype.propertyIsEnumerable() |
返回一个布尔值,表示指定的属性是否可枚举 |
Object.prototype.valueOf() |
返回指定对象的原始值 |
一眼看去,是不是发现很多熟悉的面孔,一些之前我们已经详细介绍过的方法就不再展开了,这里主要看几个比较少见的
Object.prototype.propertyIsEnumerable()
该方法返回一个布尔值,表示指定的属性是否可枚举,看下面这个例子
1 | const obj = {} |
每个对象都有一个 propertyIsEnumerable
方法,此方法可以确定对象中指定的属性是否可以被 for-in
循环枚举,但是通过原型链继承的属性除外,如果对象没有指定的属性,则此方法返回 false
Object.prototype.valueOf()
最后的最后,我们来看一个特殊的方法 Object.prototype.valueOf()
,调用该方法会返回指定对象的原始值,但是很少需要我们自己手动的调用 valueOf
方法,因为当遇到要预期的原始值的对象时,JavaScript
会自动调用它
默认情况下,valueOf
方法由 Object
后面的每个对象继承,每个内置的核心对象都会覆盖此方法以返回适当的值,如果对象没有原始值,则 valueOf
将返回对象本身,JavaScript
的许多内置对象都重写了该函数,以实现更适合自身的功能需要,因此不同类型对象的 valueOf()
方法的返回值和返回值类型均可能不同,具体可见下表
对象 | 返回值 |
---|---|
Array |
返回数组对象本身 |
boolean |
布尔值 |
Date |
存储的时间是从 1970 年 1 月 1 日午夜开始计的毫秒数 UTC |
Function |
函数本身 |
number |
数字值 |
Object |
对象本身,这是默认情况 |
string |
字符串值 |
Math 和 Error |
没有 valueOf 方法 |
下面我们主要来看几个特殊的例子,先来看看布尔类型
1 | // 布尔类型 |
函数
1 | function foo() { } |
字符串
1 | var str = 'string' |