JavaScript 對(duì)象所有API解析

首發(fā)于segmentfault:JavaScript 對(duì)象所有API解析

之前看到【深度長(zhǎng)文】JavaScript數(shù)組所有API全解密JavaScript字符串所有API全解密這兩篇高質(zhì)量的文章。發(fā)現(xiàn)沒寫對(duì)象API解析(估計(jì)是博主覺得簡(jiǎn)單,就沒寫)。剛好我看到《JavaScript面向?qū)ο缶幊讨改希ǖ?版)》,覺得有必要寫(或者說chao)一下,也好熟悉下對(duì)象的所有API用法。

創(chuàng)建對(duì)象的兩種方式:

var o = new Object();
var o = {}; // 推薦

該構(gòu)造器可以接受任何類型的參數(shù),并且會(huì)自動(dòng)識(shí)別參數(shù)的類型,并選擇更合適的構(gòu)造器來完成相關(guān)操作。比如:

var o = new Object('something');
o.constructor; // ? String() { [native code] }
var n = new Object(123);
n.constructor; // ? Number() { [native code] }

一、Object構(gòu)造器的成員

Object.prototype

該屬性是所有對(duì)象的原型(包括 Object對(duì)象本身),語言中的其他對(duì)象正是通過對(duì)該屬性上添加?xùn)|西來實(shí)現(xiàn)它們之間的繼承關(guān)系的。所以要小心使用。
比如:

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

二、Object.prototype 的成員

Object.prototype.constructor

該屬性指向用來構(gòu)造該函數(shù)對(duì)象的構(gòu)造器,在這里為Object()

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

Object.prototype.toString(radix)

該方法返回的是一個(gè)用于描述目標(biāo)對(duì)象的字符串。特別地,當(dāng)目標(biāo)是一個(gè)Number對(duì)象時(shí),可以傳遞一個(gè)用于進(jìn)制數(shù)的參數(shù)radix,該參數(shù)radix,該參數(shù)的默認(rèn)值為10。

var o = {prop:1};
o.toString(); // '[object object]'
var n = new Number(255);
n.toString(); // '255'
n.toString(16); // 'ff'

Object.prototype.toLocaleString()

該方法的作用與toString()基本相同,只不過它做一些本地化處理。該方法會(huì)根據(jù)當(dāng)前對(duì)象的不同而被重寫,例如Date(),Number(),Array(),它們的值都會(huì)以本地化的形式輸出。當(dāng)然,對(duì)于包括Object()在內(nèi)的其他大多數(shù)對(duì)象來說,該方法與toString()是基本相同的。
在瀏覽器環(huán)境下,可以通過BOM對(duì)象Navigatorlanguage屬性(在IE中則是userLanguage)來了解當(dāng)前所使用的語言:

navigator.language; //'en-US'

Object.prototype.valueOf()

該方法返回的是用基本類型所表示的this值,如果它可以用基本類型表示的話。如果Number對(duì)象返回的是它的基本數(shù)值,而Date對(duì)象返回的是一個(gè)時(shí)間戳(timestamp)。如果無法用基本數(shù)據(jù)類型表示,該方法會(huì)返回this本身。

// 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)

該方法僅在目標(biāo)屬性為對(duì)象自身屬性時(shí)返回true,而當(dāng)該屬性是從原型鏈中繼承而來或根本不存在時(shí),返回false。

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

Object.prototype.isPrototypeOf(obj)

如果目標(biāo)對(duì)象是當(dāng)前對(duì)象的原型,該方法就會(huì)返回true,而且,當(dāng)前對(duì)象所在原型上的所有對(duì)象都能通過該測(cè)試,并不局限與它的直系關(guān)系。

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

Object.prototype.propertyIsEnumerable(prop)

如果目標(biāo)屬性能在for in循環(huán)中被顯示出來,該方法就返回true

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

三、在ES5中附加的Object屬性

ES3中,除了一些內(nèi)置屬性(如:Math.PI),對(duì)象的所有的屬性在任何時(shí)候都可以被修改、插入、刪除。在ES5中,我們可以設(shè)置屬性是否可以被改變或是被刪除——在這之前,它是內(nèi)置屬性的特權(quán)。ES5中引入了屬性描述符的概念,我們可以通過它對(duì)所定義的屬性有更大的控制權(quán)。這些屬性描述符(特性)包括:

value——當(dāng)試圖獲取屬性時(shí)所返回的值。
writable——該屬性是否可寫。
enumerable——該屬性在for in循環(huán)中是否會(huì)被枚舉
configurable——該屬性是否可被刪除。
set()——該屬性的更新操作所調(diào)用的函數(shù)。
get()——獲取屬性值時(shí)所調(diào)用的函數(shù)。
另外,數(shù)據(jù)描述符(其中屬性為:enumerableconfigurable,value,writable)與存取描述符(其中屬性為enumerable,configurable,set(),get())之間是有互斥關(guān)系的。在定義了set()get()之后,描述符會(huì)認(rèn)為存取操作已被 定義了,其中再定義valuewritable會(huì)引起錯(cuò)誤
以下是ES3風(fēng)格的屬性定義方式:

var person = {};
person.legs = 2;

以下是等價(jià)的ES5通過數(shù)據(jù)描述符定義屬性的方式:

var person = {};
Object.defineProperty(person, 'legs', {
    value: 2,
    writable: true,
    configurable: true,
    enumerable: true
});

其中, 除了value的默認(rèn)值為undefined以外,其他的默認(rèn)值都為false。這就意味著,如果想要通過這一方式定義一個(gè)可寫的屬性,必須顯示將它們?cè)O(shè)為true。
或者,我們也可以通過ES5的存儲(chǔ)描述符來定義:

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屬性),在具體行為中運(yùn)用這些描述符:

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文檔:如何追蹤變化 把一個(gè)普通 JavaScript 對(duì)象傳給 Vue 實(shí)例的 data 選項(xiàng),Vue 將遍歷此對(duì)象所有的屬性,并使用 Object.defineProperty 把這些屬性全部轉(zhuǎn)為 getter/setter。Object.defineProperty 是僅 ES5 支持,且無法 shim 的特性,這也就是為什么 Vue 不支持 IE8 以及更低版本瀏覽器的原因。

Object.defineProperties(obj, props) (ES5)

該方法的作用與defineProperty()基本相同,只不過它可以用來一次定義多個(gè)屬性。
比如:

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()去猜測(cè)某個(gè)給定的對(duì)象的原型是什么,如今在ES5中,我們可以直接詢問改對(duì)象“你的原型是什么?”

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

Object.create(obj, descr) (ES5)

該方法主要用于創(chuàng)建一個(gè)新對(duì)象,并為其設(shè)置原型,用(上述)屬性描述符來定義對(duì)象的原型屬性。

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是原型上的自身上的屬性。

現(xiàn)在,我們甚至可以用它來創(chuàng)建一個(gè)完全空白的對(duì)象,這樣的事情在ES3中可是做不到的。

var o = Object.create(null);
typeof o.toString(); // 'undefined'

Object.getOwnPropertyDesciptor(obj, property) (ES5)

該方法可以讓我們?cè)敿?xì)查看一個(gè)屬性的定義。甚至可以通過它一窺那些內(nèi)置的,之前不可見的隱藏屬性。

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

Object.getOwnPropertyNames(obj) (ES5)

該方法返回一個(gè)數(shù)組,其中包含了當(dāng)前對(duì)象所有屬性的名稱(字符串),不論它們是否可枚舉。當(dāng)然,也可以用Object.keys()來單獨(dú)返回可枚舉的屬性。

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()方法用于禁止向某一對(duì)象添加更多屬性,而isExtensible()方法則用于檢查某對(duì)象是否還可以被添加屬性。

var deadline = {};
Object.isExtensible(deadline); // true
deadline.date = 'yesterday'; // 'yesterday'
Object.preventExtensions(deadline);
Object.isExtensible(deadline); // false
deadline.date = 'today';
deadline.date; // 'today'
// 盡管向某個(gè)不可擴(kuò)展的對(duì)象中添加屬性不算是一個(gè)錯(cuò)誤操作,但它沒有任何作用。
deadline.report = true;
deadline.report; // undefined

Object.seal(obj) (ES5)

Object.isSeal(obj) (ES5)

seal()方法可以讓一個(gè)對(duì)象密封,并返回被密封后的對(duì)象。
seal()方法的作用與preventExtensions()基本相同,但除此之外,它還會(huì)將現(xiàn)有屬性
設(shè)置成不可配置。也就是說,在這種情況下,我們只能變更現(xiàn)有屬性的值,但不能刪除或(用defineProperty())重新配置這些屬性,例如不能將一個(gè)可枚舉的屬性改成不可枚舉。

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()方法用于執(zhí)行一切不受seal()方法限制的屬性值變更。Object.freeze() 方法可以凍結(jié)一個(gè)對(duì)象,凍結(jié)指的是不能向這個(gè)對(duì)象添加新的屬性,不能修改其已有屬性的值,不能刪除已有屬性,以及不能修改該對(duì)象已有屬性的可枚舉性、可配置性、可寫性。也就是說,這個(gè)對(duì)象永遠(yuǎn)是不可變的。該方法返回被凍結(jié)的對(duì)象。

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循環(huán)。它只返回當(dāng)前對(duì)象的屬性(不像for-in),而且這些屬性也必須是可枚舉的(這點(diǎn)和Object.getOwnPropertyNames()不同,不論是否可以枚舉)。返回值是一個(gè)字符串?dāng)?shù)組。

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屬性

Object.is(value1, value2) (ES6)

該方法用來比較兩個(gè)值是否嚴(yán)格相等。它與嚴(yán)格比較運(yùn)算符(===)的行為基本一致。
不同之處只有兩個(gè):一是+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) {
           // 針對(duì)+0不等于-0的情況
           return x !== 0 || 1 / x === 1 / y;
        }
        // 針對(duì) NaN的情況
        return x !== x && y !== y;
    },
    configurable: true,
    enumerable: false,
    writable: true
});

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

該方法用來源對(duì)象(source)的所有可枚舉的屬性復(fù)制到目標(biāo)對(duì)象(target)。它至少需要兩個(gè)對(duì)象作為參數(shù),第一個(gè)參數(shù)是目標(biāo)對(duì)象target,后面的參數(shù)都是源對(duì)象(source)。只有一個(gè)參數(shù)不是對(duì)象,就會(huì)拋出TypeError錯(cuò)誤。

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
// 如果目標(biāo)對(duì)象與源對(duì)象有同名屬性,或多個(gè)源對(duì)象有同名屬性,則后面的屬性會(huì)覆蓋前面的屬性。
var source3 = {a:2,b:3,c:4};
Object.assign(target, source3);
target; // {a:2,b:3,c:4}

Object.assign只復(fù)制自身屬性,不可枚舉的屬性(enumerablefalse)和繼承的屬性不會(huì)被復(fù)制。

Object.assign({b: 'c'}, 
    Object.defineProperty({}, 'invisible', {
        enumerable: false,
        value: 'hello'
    })
);
// {b: 'c'}

屬性名為Symbol值的屬性,也會(huì)被Object.assign()復(fù)制。

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

對(duì)于嵌套的對(duì)象,Object.assign()的處理方法是替換,而不是添加。

Object.assign({a: {b:'c',d:'e'}}, {a:{b:'hello'}});
// {a: {b:'hello'}}

對(duì)于數(shù)組,Object.assign()把數(shù)組視為屬性名為0、1、2的對(duì)象。

Object.assign([1,2,3], [4,5]);
// [4,5,3]

Object.getOwnPropertySymbols(obj) (ES6)

該方法會(huì)返回一個(gè)數(shù)組,該數(shù)組包含了指定對(duì)象自身的(非繼承的)所有 symbol 屬性鍵。
該方法和 Object.getOwnPropertyNames() 類似,但后者返回的結(jié)果只會(huì)包含字符串類型的屬性鍵,也就是傳統(tǒng)的屬性名。

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

Object.setPrototypeOf(obj, prototype) (ES6)

該方法設(shè)置一個(gè)指定的對(duì)象的原型 ( 即, 內(nèi)部[[Prototype]]屬性)到另一個(gè)對(duì)象或 null。
__proto__屬性用來讀取或設(shè)置當(dāng)前對(duì)象的prototype對(duì)象。目前,所有瀏覽器(包括IE11)都部署了這個(gè)屬性。

// ES6寫法
var obj = {
    method: function(){
        // code ...
    }
};
// obj.__proto__ = someOtherObj;
// ES5寫法
var obj = Object.create(someOtherObj);
obj.method = function(){
    // code ...
};

該屬性沒有寫入ES6的正文,而是寫入了附錄。__proto__前后的雙下劃線說明它本質(zhì)上是一個(gè)內(nèi)部屬性,而不是正式對(duì)外的一個(gè)API。無論從語義的角度,還是從兼容性的角度,都不要使用這個(gè)屬性。而是使用Object.setPrototypeOf()(寫操作),Object.getPrototypeOf()(讀操作),或Object.create()(生成操作)代替。
在實(shí)現(xiàn)上,__proto__調(diào)用的Object.prototype.__proto__。
Object.setPrototypeOf()方法的作用與__proto__作用相同,用于設(shè)置一個(gè)對(duì)象的prototype對(duì)象。它是ES6正式推薦的設(shè)置原型對(duì)象的方法。

五、在ES8中附加的Object屬性

Object.getOwnPropertyDescriptors(obj) (ES8)

該方法基本與Object.getOwnPropertyDescriptor(obj, property)用法一致,只不過它可以用來獲取一個(gè)對(duì)象的所有自身屬性的描述符。

Object.getOwnPropertyDescriptor(Object.prototype, 'toString');
// {writable: true, enumerable: false, configurable: true, value: ? toString()}
Object.getOwnPropertyDescriptors(Object.prototype); // 可以自行在瀏覽器控制臺(tái)查看效果。

Object.values(obj) (ES8)

Object.values() 方法與Object.keys類似。返回一個(gè)給定對(duì)象自己的所有可枚舉屬性值的數(shù)組,值的順序與使用for...in循環(huán)的順序相同 ( 區(qū)別在于for-in循環(huán)枚舉原型鏈中的屬性 )。

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() 方法返回一個(gè)給定對(duì)象自己的可枚舉屬性[key,value]對(duì)的數(shù)組,數(shù)組中鍵值對(duì)的排列順序和使用 for...in 循環(huán)遍歷該對(duì)象時(shí)返回的順序一致(區(qū)別在于一個(gè)for-in循環(huán)也枚舉原型鏈中的屬性)。

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]]

關(guān)于

作者:常以軒轅Rowboat為名混跡于江湖。前端路上 | PPT愛好者 | 所知甚少,唯善學(xué)。
個(gè)人博客
segmentfault個(gè)人主頁
掘金個(gè)人主頁
知乎
github

小結(jié):

您可能會(huì)發(fā)現(xiàn)MDN上還有一些API,本文沒有列舉到。因?yàn)槟切┦欠菢?biāo)準(zhǔn)的API。熟悉對(duì)象的API對(duì)理解原型和原型鏈相關(guān)知識(shí)會(huì)有一定幫助。常用的API主要有Object.prototype.toString(),Object.prototype.hasOwnProperty()Object.getPrototypeOf(obj),Object.create(),Object.defineProperty,Object.keys(obj)Object.assign()。

參考資料

MDN Object API
JavaScript面向?qū)ο缶幊讨改希ǖ?版)(豆瓣讀書鏈接)
阮一峰 ES6標(biāo)準(zhǔn)入門2

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容