JavaScript 對象全部API解析

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

1、Object構造器的成員

Object.prototype

該屬性是全部對象的原型(包括 Object對象自己),語言中的其餘對象正是經過對該屬性上添加東西來實現它們之間的繼承關係的。因此要當心使用。
好比:vue

var s = new String('xuanyuan');
Object.prototype.custom = 1;
console.log(s.custom); // 1

2、Object.prototype 的成員

Object.prototype.constructor

該屬性指向用來構造該函數對象的構造器,在這裏爲Object()react

Object.prototype.constructor === Object; // true
var o = new Object();
o.constructor === Object; // true

Object.prototype.toString(radix)

該方法返回的是一個用於描述目標對象的字符串。特別地,當目標是一個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'

Object.prototype.toLocaleString()

該方法的做用與toString()基本相同,只不過它作一些本地化處理。該方法會根據當前對象的不一樣而被重寫,例如Date(),Number(),Array(),它們的值都會以本地化的形式輸出。固然,對於包括Object()在內的其餘大多數對象來講,該方法與toString()是基本相同的。
在瀏覽器環境下,能夠經過BOM對象Navigatorlanguage屬性(在IE中則是userLanguage)來了解當前所使用的語言:es6

navigator.language; //'en-US'

Object.prototype.valueOf()

該方法返回的是用基本類型所表示的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

Object.prototype.hasOwnProperty(prop)

該方法僅在目標屬性爲對象自身屬性時返回true,而當該屬性是從原型鏈中繼承而來或根本不存在時,返回false編程

var o = {prop:1};
o.hasOwnProperty('prop'); // true
o.hasOwnProperty('toString'); // false
o.hasOwnProperty('formString'); // false

Object.prototype.isPrototypeOf(obj)

若是目標對象是當前對象的原型,該方法就會返回true,並且,當前對象所在原型上的全部對象都能經過該測試,並不侷限與它的直系關係。segmentfault

var s = new String('');
Object.prototype.isPrototypeOf(s); // true
String.prototype.isPrototypeOf(s); // true
Array.prototype.isPrototypeOf(s); // false

Object.prototype.propertyIsEnumerable(prop)

若是目標屬性能在for in循環中被顯示出來,該方法就返回true數組

var a = [1,2,3];
a.propertyIsEnumerable('length'); // false
a.propertyIsEnumerable(0); // true

3、在ES5中附加的Object屬性

ES3中,除了一些內置屬性(如:Math.PI),對象的全部的屬性在任什麼時候候均可以被修改、插入、刪除。在ES5中,咱們能夠設置屬性是否能夠被改變或是被刪除——在這以前,它是內置屬性的特權。ES5中引入了屬性描述符的概念,咱們能夠經過它對所定義的屬性有更大的控制權。這些屬性描述符(特性)包括:

value——當試圖獲取屬性時所返回的值。
writable——該屬性是否可寫。
enumerable——該屬性在 for in循環中是否會被枚舉
configurable——該屬性是否可被刪除。
set()——該屬性的更新操做所調用的函數。
get()——獲取屬性值時所調用的函數。
另外, 數據描述符(其中屬性爲: enumerableconfigurablevaluewritable)與 存取描述符(其中屬性爲 enumerableconfigurableset()get())之間是有互斥關係的。在定義了 set()get()以後,描述符會認爲存取操做已被 定義了,其中再定義 valuewritable引發錯誤
如下是 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 (刪不掉)

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()基本相同,只不過它能夠用來一次定義多個屬性。
好比:

var glass = Object.defineProperties({}, {
    'color': {
        value: 'transparent',
        writable: true
    },
    'fullness': {
        value: 'half',
        writable: false
    }
});
glass.fullness; // 'half'

Object.getPrototypeOf(obj) (ES5)

以前在ES3中,咱們每每須要經過Object.prototype.isPrototypeOf()去猜想某個給定的對象的原型是什麼,現在在ES5中,咱們能夠直接詢問改對象「你的原型是什麼?」

Object.getPrototypeOf([]) === Array.prototype; // true
Object.getPrototypeOf(Array.prototype) === Object.prototype; // true
Object.getPrototypeOf(Object.prototype) === null; // true

Object.create(obj, descr) (ES5)

該方法主要用於建立一個新對象,併爲其設置原型,用(上述)屬性描述符來定義對象的原型屬性。

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.getOwnPropertyDesciptor(obj, property) (ES5)

該方法可讓咱們詳細查看一個屬性的定義。甚至能夠經過它一窺那些內置的,以前不可見的隱藏屬性。

Object.getOwnPropertyDescriptor(Object.prototype, 'toString');
// {writable: true, enumerable: false, configurable: true, value: ƒ toString()}

Object.getOwnPropertyNames(obj) (ES5)

該方法返回一個數組,其中包含了當前對象全部屬性的名稱(字符串),不論它們是否可枚舉。固然,也能夠用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);
// []

Object.preventExtensions(obj) (ES5)

Object.isExtensible(obj) (ES5)

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

Object.seal(obj) (ES5)

Object.isSeal(obj) (ES5)

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異常

Object.freeze(obj) (ES5)

Object.isFrozen(obj) (ES5)

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'] (可枚舉)

Object.keys(obj) (ES5)

該方法是一種特殊的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']

4、在ES6中附加的Object屬性

Object.is(value1, value2) (ES6)

該方法用來比較兩個值是否嚴格相等。它與嚴格比較運算符(===)的行爲基本一致。
不一樣之處只有兩個:一是+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
});

Object.assign(target, ...sources) (ES6)

該方法用來源對象(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只複製自身屬性,不可枚舉的屬性(enumerablefalse)和繼承的屬性不會被複制。

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]

Object.getOwnPropertySymbols(obj) (ES6)

該方法會返回一個數組,該數組包含了指定對象自身的(非繼承的)全部 symbol 屬性鍵。
該方法和 Object.getOwnPropertyNames() 相似,但後者返回的結果只會包含字符串類型的屬性鍵,也就是傳統的屬性名。

Object.getOwnPropertySymbols({a: 'b', [Symbol('c')]: 'd'});
// [Symbol(c)]

Object.setPrototypeOf(obj, prototype) (ES6)

該方法設置一個指定的對象的原型 ( 即, 內部[[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正式推薦的設置原型對象的方法。

5、在ES8中附加的Object屬性

Object.getOwnPropertyDescriptors(obj) (ES8)

該方法基本與Object.getOwnPropertyDescriptor(obj, property)用法一致,只不過它能夠用來獲取一個對象的全部自身屬性的描述符。

Object.getOwnPropertyDescriptor(Object.prototype, 'toString');
// {writable: true, enumerable: false, configurable: true, value: ƒ toString()}
Object.getOwnPropertyDescriptors(Object.prototype); // 能夠自行在瀏覽器控制檯查看效果。

Object.values(obj) (ES8)

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(obj) (ES8)

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.definePropertyObject.keys(obj)Object.assign()

參考資料

MDN Object API
JavaScript面向對象編程指南(第2版)(豆瓣讀書連接)
阮一峯 ES6標準入門2

關於

做者:常以軒轅Rowboat若川爲名混跡於江湖。前端路上 | PPT愛好者 | 所知甚少,惟善學。
我的博客
segmentfault前端視野專欄,開通了前端視野專欄,歡迎關注
掘金專欄,歡迎關注
知乎前端視野專欄,開通了前端視野專欄,歡迎關注
github,歡迎follow~

相關文章
相關標籤/搜索