以前看到 【深度长文】JavaScript数组全部API全解密和 JavaScript字符串全部API全解密这两篇高质量的文章。发现没写对象API解析(估计是博主以为简单,就没写)。恰好我看到《JavaScript面向对象编程指南(第2版)》,以为有必要写(或者说chao)一下,也好熟悉下对象的全部API用法。
建立对象的两种方式:html
var o = new Object(); var o = {}; // 推荐
该构造器能够接受任何类型的参数,而且会自动识别参数的类型,并选择更合适的构造器来完成相关操做。好比:前端
var o = new Object('something'); o.constructor; // ƒ String() { [native code] } var n = new Object(123); n.constructor; // ƒ Number() { [native code] }
该属性是全部对象的原型(包括 Object
对象自己),语言中的其余对象正是经过对该属性上添加东西来实现它们之间的继承关系的。因此要当心使用。
好比:vue
var s = new String('xuanyuan'); Object.prototype.custom = 1; console.log(s.custom); // 1
该属性指向用来构造该函数对象的构造器,在这里为Object()
react
Object.prototype.constructor === Object; // true var o = new Object(); o.constructor === Object; // true
该方法返回的是一个用于描述目标对象的字符串。特别地,当目标是一个Number对象时,能够传递一个用于进制数的参数radix
,该参数radix
,该参数的默认值为10。git
var o = {prop:1}; o.toString(); // '[object Object]' var n = new Number(255); n.toString(); // '255' n.toString(16); // 'ff'
该方法的做用与toString()
基本相同,只不过它作一些本地化处理。该方法会根据当前对象的不一样而被重写,例如Date()
,Number()
,Array()
,它们的值都会以本地化的形式输出。固然,对于包括Object()
在内的其余大多数对象来讲,该方法与toString()
是基本相同的。
在浏览器环境下,能够经过BOM
对象Navigator
的language
属性(在IE
中则是userLanguage
)来了解当前所使用的语言:es6
navigator.language; //'en-US'
该方法返回的是用基本类型所表示的this
值,若是它能够用基本类型表示的话。若是Number
对象返回的是它的基本数值,而Date
对象返回的是一个时间戳(timestamp
)。若是没法用基本数据类型表示,该方法会返回this
自己。github
// Object var o = {}; typeof o.valueOf(); // 'object' o.valueOf() === o; // true // Number var n = new Number(101); typeof n; // 'object' typeof n.vauleOf; // 'function' typeof n.valueOf(); // 'number' n.valueOf() === n; // false // Date var d = new Date(); typeof d.valueOf(); // 'number' d.valueOf(); // 1503146772355
该方法仅在目标属性为对象自身属性时返回true
,而当该属性是从原型链中继承而来或根本不存在时,返回false
。编程
var o = {prop:1}; o.hasOwnProperty('prop'); // true o.hasOwnProperty('toString'); // false o.hasOwnProperty('formString'); // false
若是目标对象是当前对象的原型,该方法就会返回true
,并且,当前对象所在原型上的全部对象都能经过该测试,并不局限与它的直系关系。segmentfault
var s = new String(''); Object.prototype.isPrototypeOf(s); // true String.prototype.isPrototypeOf(s); // true Array.prototype.isPrototypeOf(s); // false
若是目标属性能在for in
循环中被显示出来,该方法就返回true
数组
var a = [1,2,3]; a.propertyIsEnumerable('length'); // false a.propertyIsEnumerable(0); // true
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风格的属性定义方式:
var person = {}; person.legs = 2;
如下是等价的ES5经过数据描述符定义属性的方式:
var person = {}; Object.defineProperty(person, 'legs', { value: 2, writable: true, configurable: true, enumerable: true });
其中, 除了value的默认值为undefined
之外,其余的默认值都为false
。这就意味着,若是想要经过这一方式定义一个可写的属性,必须显示将它们设为true
。
或者,咱们也能够经过ES5
的存储描述符来定义:
var person = {}; Object.defineProperty(person, 'legs', { set:function(v) { return this.value = v; }, get: function(v) { return this.value; }, configurable: true, enumerable: true }); person.legs = 2;
这样一来,多了许多能够用来描述属性的代码,若是想要防止别人篡改咱们的属性,就必需要用到它们。此外,也不要忘了浏览器向后兼容ES3
方面所作的考虑。例如,跟添加Array.prototype
属性不同,咱们不能再旧版的浏览器中使用shim
这一特性。
另外,咱们还能够(经过定义nonmalleable
属性),在具体行为中运用这些描述符:
var person = {}; Object.defineProperty(person, 'heads', {value: 1}); person.heads = 0; // 0 person.heads; // 1 (改不了) delete person.heads; // false person.heads // 1 (删不掉)
具体用法可参见上文,或者查看MDN。
MDN Object.defineProperty(obj, descriptor)
Vue.js文档: 如何追踪变化 把一个普通 JavaScript 对象传给 Vue 实例的 data 选项,Vue 将遍历此对象全部的属性,并使用 Object.defineProperty 把这些属性所有转为 getter/setter。Object.defineProperty 是仅 ES5 支持,且没法 shim 的特性,这也就是为何 Vue 不支持 IE8 以及更低版本浏览器的缘由。
该方法的做用与defineProperty()
基本相同,只不过它能够用来一次定义多个属性。
好比:
var glass = Object.defineProperties({}, { 'color': { value: 'transparent', writable: true }, 'fullness': { value: 'half', writable: false } }); glass.fullness; // 'half'
以前在ES3
中,咱们每每须要经过Object.prototype.isPrototypeOf()
去猜想某个给定的对象的原型是什么,现在在ES5
中,咱们能够直接询问改对象“你的原型是什么?”
Object.getPrototypeOf([]) === Array.prototype; // true Object.getPrototypeOf(Array.prototype) === Object.prototype; // true Object.getPrototypeOf(Object.prototype) === null; // true
该方法主要用于建立一个新对象,并为其设置原型,用(上述)属性描述符来定义对象的原型属性。
var parent = {hi: 'Hello'}; var o = Object.create(parent, { prop: { value: 1 } }); o.hi; // 'Hello' // 得到它的原型 Object.getPrototypeOf(parent) === Object.prototype; // true 说明parent的原型是Object.prototype Object.getPrototypeOf(o); // {hi: "Hello"} // 说明o的原型是{hi: "Hello"} o.hasOwnProperty('hi'); // false 说明hi是原型上的 o.hasOwnProperty('prop'); // true 说明prop是原型上的自身上的属性。
如今,咱们甚至能够用它来建立一个彻底空白的对象,这样的事情在ES3
中但是作不到的。
var o = Object.create(null); typeof o.toString(); // 'undefined'
该方法可让咱们详细查看一个属性的定义。甚至能够经过它一窥那些内置的,以前不可见的隐藏属性。
Object.getOwnPropertyDescriptor(Object.prototype, 'toString'); // {writable: true, enumerable: false, configurable: true, value: ƒ toString()}
该方法返回一个数组,其中包含了当前对象全部属性的名称(字符串),不论它们是否可枚举。固然,也能够用Object.keys()
来单独返回可枚举的属性。
Object.getOwnPropertyNames(Object.prototype); // ["__defineGetter__", "__defineSetter__", "hasOwnProperty", "__lookupGetter__", "__lookupSetter__", "propertyIsEnumerable", "toString", "valueOf", "__proto__", "constructor", "toLocaleString", "isPrototypeOf"] Object.keys(Object.prototype); // [] Object.getOwnPropertyNames(Object); // ["length", "name", "arguments", "caller", "prototype", "assign", "getOwnPropertyDescriptor", "getOwnPropertyDescriptors", "getOwnPropertyNames", "getOwnPropertySymbols", "is", "preventExtensions", "seal", "create", "defineProperties", "defineProperty", "freeze", "getPrototypeOf", "setPrototypeOf", "isExtensible", "isFrozen", "isSealed", "keys", "entries", "values"] Object.keys(Object); // []
preventExtensions()
方法用于禁止向某一对象添加更多属性,而isExtensible()
方法则用于检查某对象是否还能够被添加属性。
var deadline = {}; Object.isExtensible(deadline); // true deadline.date = 'yesterday'; // 'yesterday' Object.preventExtensions(deadline); Object.isExtensible(deadline); // false deadline.date = 'today'; deadline.date; // 'today' // 尽管向某个不可扩展的对象中添加属性不算是一个错误操做,但它没有任何做用。 deadline.report = true; deadline.report; // undefined
seal()
方法可让一个对象密封,并返回被密封后的对象。seal()
方法的做用与preventExtensions()
基本相同,但除此以外,它还会将现有属性
设置成不可配置。也就是说,在这种状况下,咱们只能变动现有属性的值,但不能删除或(用defineProperty()
)从新配置这些属性,例如不能将一个可枚举的属性改为不可枚举。
var person = {legs:2}; // person === Object.seal(person); // true Object.isSealed(person); // true Object.getOwnPropertyDescriptor(person, 'legs'); // {value: 2, writable: true, enumerable: true, configurable: false} delete person.legs; // false (不可删除,不可配置) Object.defineProperty(person, 'legs',{value:2}); person.legs; // 2 person.legs = 1; person.legs; // 1 (可写) Object.defineProperty(person, "legs", { get: function() { return "legs"; } }); // 抛出TypeError异常
freeze()
方法用于执行一切不受seal()
方法限制的属性值变动。Object.freeze()
方法能够冻结一个对象,冻结指的是不能向这个对象添加新的属性,不能修改其已有属性的值,不能删除已有属性,以及不能修改该对象已有属性的可枚举性、可配置性、可写性。也就是说,这个对象永远是不可变的。该方法返回被冻结的对象。
var deadline = Object.freeze({date: 'yesterday'}); deadline.date = 'tomorrow'; deadline.excuse = 'lame'; deadline.date; // 'yesterday' deadline.excuse; // undefined Object.isSealed(deadline); // true; Object.isFrozen(deadline); // true Object.getOwnPropertyDescriptor(deadline, 'date'); // {value: "yesterday", writable: false, enumerable: true, configurable: false} (不可配置,不可写) Object.keys(deadline); // ['date'] (可枚举)
该方法是一种特殊的for-in
循环。它只返回当前对象的属性(不像for-in
),并且这些属性也必须是可枚举的(这点和Object.getOwnPropertyNames()
不一样,不管是否能够枚举)。返回值是一个字符串数组。
Object.prototype.customProto = 101; Object.getOwnPropertyNames(Object.prototype); // [..., "constructor", "toLocaleString", "isPrototypeOf", "customProto"] Object.keys(Object.prototype); // ['customProto'] var o = {own: 202}; o.customProto; // 101 Object.keys(o); // ['own']
ES6
中附加的Object
属性该方法用来比较两个值是否严格相等。它与严格比较运算符(===)的行为基本一致。
不一样之处只有两个:一是+0
不等于-0
,而是NaN
等于自身。
Object.is('xuanyuan', 'xuanyuan'); // true Object.is({},{}); // false Object.is(+0, -0); // false +0 === -0; // true Object.is(NaN, NaN); // true NaN === NaN; // false
ES5
能够经过如下代码部署Object.is
Object.defineProperty(Object, 'is', { value: function() {x, y} { if (x === y) { // 针对+0不等于-0的状况 return x !== 0 || 1 / x === 1 / y; } // 针对 NaN的状况 return x !== x && y !== y; }, configurable: true, enumerable: false, writable: true });
该方法用来源对象(source
)的全部可枚举的属性复制到目标对象(target
)。它至少须要两个对象做为参数,第一个参数是目标对象target
,后面的参数都是源对象(source
)。只有一个参数不是对象,就会抛出TypeError
错误。
var target = {a: 1}; var source1 = {b: 2}; var source2 = {c: 3}; obj = Object.assign(target, source1, source2); target; // {a:1,b:2,c:3} obj; // {a:1,b:2,c:3} target === obj; // true // 若是目标对象与源对象有同名属性,或多个源对象有同名属性,则后面的属性会覆盖前面的属性。 var source3 = {a:2,b:3,c:4}; Object.assign(target, source3); target; // {a:2,b:3,c:4}
Object.assign
只复制自身属性,不可枚举的属性(enumerable
为false
)和继承的属性不会被复制。
Object.assign({b: 'c'}, Object.defineProperty({}, 'invisible', { enumerable: false, value: 'hello' }) ); // {b: 'c'}
属性名为Symbol
值的属性,也会被Object.assign()
复制。
Object.assign({a: 'b'}, {[Symbol('c')]: 'd'}); // {a: 'b', Symbol(c): 'd'}
对于嵌套的对象,Object.assign()
的处理方法是替换,而不是添加。
Object.assign({a: {b:'c',d:'e'}}, {a:{b:'hello'}}); // {a: {b:'hello'}}
对于数组,Object.assign()
把数组视为属性名为0、一、2的对象。
Object.assign([1,2,3], [4,5]); // [4,5,3]
该方法会返回一个数组,该数组包含了指定对象自身的(非继承的)全部 symbol
属性键。
该方法和 Object.getOwnPropertyNames()
相似,但后者返回的结果只会包含字符串类型的属性键,也就是传统的属性名。
Object.getOwnPropertySymbols({a: 'b', [Symbol('c')]: 'd'}); // [Symbol(c)]
该方法设置一个指定的对象的原型 ( 即, 内部[[Prototype]]
属性)到另外一个对象或 null
。__proto__
属性用来读取或设置当前对象的prototype
对象。目前,全部浏览器(包括IE11
)都部署了这个属性。
// ES6写法 var obj = { method: function(){ // code ... } }; // obj.__proto__ = someOtherObj; // ES5写法 var obj = Object.create(someOtherObj); obj.method = function(){ // code ... };
该属性没有写入ES6
的正文,而是写入了附录。__proto__
先后的双下划线说明它本质上是一个内部属性,而不是正式对外的一个API。不管从语义的角度,仍是从兼容性的角度,都不要使用这个属性。而是使用Object.setPrototypeOf()
(写操做),Object.getPrototypeOf()
(读操做),或Object.create()
(生成操做)代替。
在实现上,__proto__
调用的Object.prototype.__proto__
。Object.setPrototypeOf()
方法的做用与__proto__
做用相同,用于设置一个对象的prototype
对象。它是ES6
正式推荐的设置原型对象的方法。
ES8
中附加的Object
属性该方法基本与Object.getOwnPropertyDescriptor(obj, property)
用法一致,只不过它能够用来获取一个对象的全部自身属性的描述符。
Object.getOwnPropertyDescriptor(Object.prototype, 'toString'); // {writable: true, enumerable: false, configurable: true, value: ƒ toString()} Object.getOwnPropertyDescriptors(Object.prototype); // 能够自行在浏览器控制台查看效果。
Object.values()
方法与Object.keys
相似。返回一个给定对象本身的全部可枚举属性值的数组,值的顺序与使用for...in
循环的顺序相同 ( 区别在于for-in
循环枚举原型链中的属性 )。
var obj = {a:1,b:2,c:3}; Object.keys(obj); // ['a','b','c'] Object.values(obj); // [1,2,3]
Object.entries()
方法返回一个给定对象本身的可枚举属性[key,value]
对的数组,数组中键值对的排列顺序和使用 for...in
循环遍历该对象时返回的顺序一致(区别在于一个for-in
循环也枚举原型链中的属性)。
var obj = {a:1,b:2,c:3}; Object.keys(obj); // ['a','b','c'] Object.values(obj); // [1,2,3] Object.entries(obj); // [['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()
。
MDN Object API
JavaScript面向对象编程指南(第2版)(豆瓣读书连接)
阮一峰 ES6标准入门2
做者:常以轩辕Rowboat若川为名混迹于江湖。前端路上 | PPT爱好者 | 所知甚少,惟善学。
我的博客segmentfault
前端视野专栏,开通了前端视野专栏,欢迎关注
掘金专栏,欢迎关注
知乎前端视野专栏,开通了前端视野专栏,欢迎关注
github,欢迎follow
~