数组提供了很多方法,为了让开发变得简单高效,这里整理汇总了一些有用的JavaScript对象方法。
开发中常见的需求,经常会有类似的封装http
到原型Vue.prototype
,一般人是这样封装的,但容易被篡改。
1 | function Vue(){ |
熟悉Object.defineProperty
或者说熟悉对象API的人,一般是如下代码写的,则不会出现被修改的问题。
1 | function Vue(){ |
vue-router 源码里就是类似这样写的,this.$router
,this.$route
无法修改。
1 | // vue-router 源码 |
创建对象的两种方式:
1 | var o = new Object(); |
该构造器可以接受任何类型的参数,并且会自动识别参数的类型,并选择更合适的构造器来完成相关操作。比如:
1 | var o = new Object('something'); |
Object 构造器的成员
Object.prototype
该属性是所有对象的原型(包括 Object
对象本身),语言中的其他对象正是通过对该属性上添加东西来实现它们之间的继承关系的。所以要小心使用。比如:
1 | var s = new String('若川'); |
Object.prototype 的成员
Object.prototype.constructor
该属性指向用来构造该函数对象的构造器,在这里为Object()
1 | Object.prototype.constructor === Object; // true |
Object.prototype.toString(radix)
该方法返回的是一个用于描述目标对象的字符串。特别地,当目标是一个 Number 对象时,可以传递一个用于进制数的参数radix,该参数radix,该参数的默认值为 10。
1 | var o = {prop:1}; |
Object.prototype.toLocaleString()
该方法的作用与toString()
基本相同,只不过它做一些本地化处理。该方法会根据当前对象的不同而被重写,例如Date()
,Number()
,Array()
,它们的值都会以本地化的形式输出。当然,对于包括Object()
在内的其他大多数对象来说,该方法与toString()
是基本相同的。在浏览器环境下,可以通过BOM
对象Navigator
的language
属性(在IE中则是userLanguage
)来了解当前所使用的语言:
1 | navigator.language; //'en-US' |
Object.prototype.valueOf()
该方法返回的是用基本类型所表示的this
值,如果它可以用基本类型表示的话。如果Number
对象返回的是它的基本数值,而Date
对象返回的是一个时间戳(timestamp
)。如果无法用基本数据类型表示,该方法会返回this
本身。
1 | // Object |
Object.prototype.hasOwnProperty(prop)
该方法仅在目标属性为对象自身属性时返回true
,而当该属性是从原型链中继承而来或根本不存在时,返回false
。
1 | var o = {prop:1}; |
Object.prototype.isPrototypeOf(obj)
如果目标对象是当前对象的原型,该方法就会返回true
,而且,当前对象所在原型上的所有对象都能通过该测试,并不局限与它的直系关系。
1 | var s = new String(''); |
Object.prototype.propertyIsEnumerable(prop)
如果目标属性能在for in
循环中被显示出来,该方法就返回true
1 | var a = [1,2,3]; |
在ES5中附加的Object属性
在ES3中,除了一些内置属性(如:Math.PI
),对象的所有的属性在任何时候都可以被修改、插入、删除。在ES5中,可以设置属性是否可以被改变或是被删除——在这之前,它是内置属性的特权。ES5中引入了属性描述符的概念,可以通过它对所定义的属性有更大的控制权。这些属性描述符(特性)包括:
value
——当试图获取属性时所返回的值。writable
——该属性是否可写。enumerable
——该属性在for in
循环中是否会被枚举configurable
——该属性是否可被删除。set()
——该属性的更新操作所调用的函数。get()
——获取属性值时所调用的函数。另外,数据描述符(其中属性为:enumerable
,configurable
,value
,writable
)与存取描述符(其中属性为enumerable
,configurable
,set()
,get()
)之间是有互斥关系的。在定义了set()
和get()
之后,描述符会认为存取操作已被 定义了,其中再定义value
和writable
会引起错误。以下是ES3风格的属性定义方式:
1 | var person = {}; |
以下是等价的 ES5 通过数据描述符定义属性的方式:
1 | var person = {}; |
其中, 除了 value 的默认值为undefined
以外,其他的默认值都为false
。这就意味着,如果想要通过这一方式定义一个可写的属性,必须显示将它们设为true
。或者,也可以通过ES5
的存储描述符来定义:
1 | var person = {}; |
这样一来,多了许多可以用来描述属性的代码,如果想要防止别人篡改属性,就必须要用到它们。此外,也不要忘了浏览器向后兼容ES3方面所做的考虑。例如,跟添加Array.prototype
属性不一样,不能在旧版的浏览器中使用shim
这一特性。另外,还可以(通过定义nonmalleable
属性),在具体行为中运用这些描述符:
1 | var person = {}; |
Object.defineProperty(obj, prop, descriptor) (ES5)
具体用法可参见上文,或者查看 MDN。MDN Object.defineProperty(obj, descriptor)
Vue.js 文档:把一个普通 JavaScript 对象传给 Vue 实例的 data
选项,Vue 将遍历此对象所有的属性,并使用 Object.defineProperty
把这些属性全部转为 getter
/setter
。Object.defineProperty
是仅 ES5 支持,且无法 shim
的特性,这也就是为什么 Vue 不支持 IE8 以及更低版本浏览器的原因。
Object.defineProperties(obj, props)(ES5)
该方法的作用与defineProperty()
基本相同,只不过它可以用来一次定义多个属性。比如:
1 | var glass = Object.defineProperties({}, { |
Object.getPrototypeOf(obj) (ES5)
之前在ES3中,往往需要通过Object.prototype.isPrototypeOf()
去猜测某个给定的对象的原型是什么,如今在ES5中,可以直接询问改对象“你的原型是什么?”
1 | Object.getPrototypeOf([]) === Array.prototype; // true |
Object.create(obj, descr) (ES5)
该方法主要用于创建一个新对象,并为其设置原型,用(上述)属性描述符来定义对象的原型属性。
1 | var parent = {hi: 'Hello'}; |
现在,甚至可以用它来创建一个完全空白的对象,这样的事情在ES3中可是做不到的。
1 | var o = Object.create(null); |
Object.getOwnPropertyDesciptor(obj, property) (ES5)
该方法可以详细查看一个属性的定义。甚至可以通过它一窥那些内置的,之前不可见的隐藏属性。
1 | Object.getOwnPropertyDescriptor(Object.prototype, 'toString'); |
Object.getOwnPropertyNames(obj) (ES5)
该方法返回一个数组,其中包含了当前对象所有属性的名称(字符串),不论它们是否可枚举。当然,也可以用Object.keys()
来单独返回可枚举的属性。
1 | Object.getOwnPropertyNames(Object.prototype); |
Object.preventExtensions(obj) (ES5)
Object.isExtensible(obj) (ES5)
preventExtensions()
方法用于禁止向某一对象添加更多属性,而isExtensible()
方法则用于检查某对象是否还可以被添加属性。
1 | var deadline = {}; |
Object.seal(obj) (ES5)
Object.isSeal(obj) (ES5)
seal()
方法可以让一个对象密封,并返回被密封后的对象。seal()
方法的作用与preventExtensions()
基本相同,但除此之外,它还会将现有属性 设置成不可配置。也就是说,在这种情况下,只能变更现有属性的值,但不能删除或(用defineProperty()
)重新配置这些属性,例如不能将一个可枚举的属性改成不可枚举。
1 | var person = {legs:2}; |
Object.freeze(obj) (ES5)
Object.isFrozen(obj) (ES5)
freeze()
方法用于执行一切不受seal()
方法限制的属性值变更。Object.freeze()
方法可以冻结一个对象,冻结指的是不能向这个对象添加新的属性,不能修改其已有属性的值,不能删除已有属性,以及不能修改该对象已有属性的可枚举性、可配置性、可写性。也就是说,这个对象永远是不可变的。该方法返回被冻结的对象。
1 | var deadline = Object.freeze({date: 'yesterday'}); |
Object.keys(obj) (ES5)
该方法是一种特殊的for-in
循环。它只返回当前对象的属性(不像for-in
),而且这些属性也必须是可枚举的(这点和Object.getOwnPropertyNames()
不同,不论是否可以枚举)。返回值是一个字符串数组。
1 | Object.prototype.customProto = 101; |
在ES6中附加的Object属性
Object.is(value1, value2)(ES6)
该方法用来比较两个值是否严格相等。它与严格比较运算符(===)的行为基本一致。不同之处只有两个:一是+0
不等于-0
,而是NaN
等于自身。
1 | Object.is('若川', '若川'); // true |
ES5可以通过以下代码部署Object.is
1 | Object.defineProperty(Object, 'is', { |
Object.assign(target, …sources)(ES6)
该方法用来源对象(source
)的所有可枚举的属性复制到目标对象(target
)。它至少需要两个对象作为参数,第一个参数是目标对象target
,后面的参数都是源对象(source
)。只有一个参数不是对象,就会抛出TypeError
错误。
1 | var target = {a: 1}; |
属性名为Symbol
值的属性,也会被Object.assign()
复制。
1 | Object.assign({a: 'b'}, {[Symbol('c')]: 'd'}); |
对于嵌套的对象,Object.assign()
的处理方法是替换,而不是添加。
1 | Object.assign({a: {b:'c',d:'e'}}, {a:{b:'hello'}}); |
对于数组,Object.assign()
把数组视为属性名为 0、1、2 的对象。
1 | Object.assign([1,2,3], [4,5]); |
Object.getOwnPropertySymbols(obj) (ES6)
该方法会返回一个数组,该数组包含了指定对象自身的(非继承的)所有 symbol
属性键。该方法和 Object.getOwnPropertyNames()
类似,但后者返回的结果只会包含字符串类型的属性键,也就是传统的属性名。
1 | Object.getOwnPropertySymbols({a: 'b', [Symbol('c')]: 'd'}); |
Object.setPrototypeOf(obj, prototype) (ES6)
该方法设置一个指定的对象的原型 ( 即, 内部[[Prototype]]
属性)到另一个对象或 null
。__proto__
属性用来读取或设置当前对象的prototype
对象。目前,所有浏览器(包括IE11)都部署了这个属性。
1 | // ES6写法 |
该属性没有写入ES6
的正文,而是写入了附录。__proto__
前后的双下划线说明它本质上是一个内部属性,而不是正式对外的一个 API。无论从语义的角度,还是从兼容性的角度,都不要使用这个属性。而是使用Object.setPrototypeOf()
(写操作),Object.getPrototypeOf()
(读操作),或Object.create()
(生成操作)代替。在实现上,__proto__
调用的Object.prototype.__proto__
。Object.setPrototypeOf()
方法的作用与__proto__
作用相同,用于设置一个对象的prototype
对象。它是ES6正式推荐的设置原型对象的方法。
在ES8中附加的Object属性
Object.getOwnPropertyDescriptors(obj) (ES8)
该方法基本与Object.getOwnPropertyDescriptor(obj, property)
用法一致,只不过它可以用来获取一个对象的所有自身属性的描述符。
1 | Object.getOwnPropertyDescriptor(Object.prototype, 'toString'); |
Object.values(obj) (ES8)
Object.values()
方法与Object.keys
类似。返回一个给定对象自己的所有可枚举属性值的数组,值的顺序与使用for...in
循环的顺序相同 ( 区别在于for-in
循环枚举原型链中的属性 )。
1 | var obj = {a:1,b:2,c:3}; |
Object.entries(obj) (ES8)
Object.entries()
方法返回一个给定对象自己的可枚举属性[key,value]
对的数组,数组中键值对的排列顺序和使用 for...in
循环遍历该对象时返回的顺序一致(区别在于一个for-in
循环也枚举原型链中的属性)。
1 | var obj = {a:1,b:2,c:3}; |
在ES10中附加的Object属性
Object.fromEntries(iterable) (ES10)
Object.fromEntries()
方法返回一个给定可迭代对象(类似Array
、Map
或其他可迭代对象)对应属性的新对象。
Object.fromEntries()
是 Object.entries()
的逆操作。
1 | var arr = [['a',1],['b',2],['c',3]]; |
小结
MDN上还有一些API,这里没有列举到。因为那些是非标准的API。熟悉对象的 API 对理解原型和原型链相关知识会有一定帮助。常用的 API 主要有Object.prototype.toString()
,Object.prototype.hasOwnProperty()
, Object.getPrototypeOf(obj)
,Object.create()
,Object.defineProperty
,Object.keys(obj)
,Object.assign()
。