以前看到 【深度長文】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
~