Object
自己是一個函數,用來將任意值轉爲對象。javascript
若是參數爲空(或者爲 undefined
和 null
),Object()
返回一個空對象。html
var obj = Object(); // 等同於 var obj = Object(undefined); var obj = Object(null); obj instanceof Object // true
若是參數是原始類型的值,Object
方法將其轉爲對應的包裝對象的實例。java
var obj = Object(1); obj instanceof Object // true obj instanceof Number // true var obj = Object('foo'); obj instanceof Object // true obj instanceof String // true var obj = Object(true); obj instanceof Object // true obj instanceof Boolean // true
若是 Object
方法的參數是一個對象,它老是返回該對象,即不用轉換。segmentfault
var arr = []; var obj = Object(arr); // 返回原數組 obj === arr // true var value = {}; var obj = Object(value) // 返回原對象 obj === value // true var fn = function () {}; var obj = Object(fn); // 返回原函數 obj === fn // true
所以,能夠寫一個判斷變量是否爲對象的函數。這個方法經常使用於保證某個值必定是對象。數組
function isObject(value) { return value === Object(value); } isObject([]) // true isObject(true) // false
Object
構造函數的首要用途,是直接經過它來生成新對象。瀏覽器
var obj = new Object(); // 等同於 var obj = {}
new Object()
構造函數與 Object()
的用法很類似,幾乎如出一轍。使用時,能夠接受一個參數,若是該參數是一個對象,則直接返回這個對象;若是是一個原始類型的值,則返回該值對應的包裝對象。函數
var o1 = {a: 1}; var o2 = new Object(o1); o1 === o2 // true var obj = new Object(123); obj instanceof Number // true
二者區別是語義不一樣。Object(value)
表示將 value
轉成一個對象,new Object(value)
則表示新生成一個對象,它的值是 value
。this
Object
對象的原生方法分紅兩類:Object
自己的方法與Object
的實例方法。prototype
自己的方法就是直接定義在 Object
對象的方法。code
Object.keys()
:遍歷對象自身的(非繼承的)可枚舉屬性,返回屬性名。Object.getOwnPropertyNames()
:遍歷對象自身的(非繼承的)所有(可枚舉+不可枚舉歷)屬性。Object.values()
:遍歷對象自身的(非繼承的)可枚舉屬性,返回屬性值。Object.entries()
:遍歷對象自身的(非繼承的)可枚舉屬性,返回鍵值對。Object.getOwnPropertyDescriptor()
:獲取某個屬性的描述對象。Object.getOwnPropertyDescriptors()
:獲取對象的全部屬性的描述對象。Object.defineProperty()
:定義某個屬性的描述對象。Object.defineProperties()
:定義多個屬性的描述對象。Object.preventExtensions()
:防止對象擴展,沒法添加新屬性。Object.isExtensible()
:判斷對象是否可擴展。Object.seal()
:禁止對象配置,沒法添加新屬性,沒法刪除屬性。Object.isSealed()
:判斷一個對象是否可配置。Object.freeze()
:凍結一個對象,沒法添加新屬性,沒法刪除屬性,沒法改變屬性值。Object.isFrozen()
:判斷一個對象是否被凍結。Object.create()
:以參數爲原型返回一個新的實例對象。Object.getPrototypeOf()
:獲取對象的原型對象。Object.setPrototypeOf()
:設置對象的原型對象。Object.assign()
Object.is()
實例方法就是定義在 Object
原型對象 Object.prototype
上的方法。它能夠被 Object
實例直接使用。
Object
實例對象的方法,主要有如下六個:
Object.prototype.valueOf()
:返回當前對象對應的值。Object.prototype.toString()
:返回當前對象對應的字符串形式。Object.prototype.toLocaleString()
:返回當前對象對應的本地字符串形式。Object.prototype.hasOwnProperty()
:判斷某個屬性是否爲當前對象自身的屬性,仍是繼承自原型對象的屬性。Object.prototype.isPrototypeOf()
:判斷當前對象是否爲另外一個對象的原型。Object.prototype.propertyIsEnumerable()
:判斷對象自身的(非繼承的)屬性是否可枚舉。Object.keys
方法和 Object.getOwnPropertyNames
方法都用來遍歷對象的屬性。
Object.keys
方法的參數是一個對象,返回一個數組。該數組的成員都是該對象自身的(非繼承的)全部屬性名,且只返回可枚舉的屬性。
var obj = Object.defineProperties({}, { p1: { value: 1, enumerable: true }, p2: { value: 2, enumerable: false } }); Object.keys(obj) // ["p1"]
Object.getOwnPropertyNames
方法與 Object.keys
相似,也是接受一個對象做爲參數,返回一個數組,該數組的成員是參數對象自身的(非繼承的)所有屬性的屬性名,無論該屬性是否可枚舉。
var a = ['Hello', 'World']; Object.keys(a) // ["0", "1"] Object.getOwnPropertyNames(a) // ["0", "1", "length"]
上面代碼中,數組的 length 屬性是不可枚舉的屬性,因此只出如今 Object.getOwnPropertyNames
方法的返回結果中。
因爲 JavaScript 沒有提供計算對象屬性個數的方法,因此能夠用這兩個方法代替。
var obj = { p1: 123, p2: 456 }; Object.keys(obj).length // 2 Object.getOwnPropertyNames(obj).length // 2
通常狀況下,幾乎老是使用 Object.keys
方法,遍歷對象的屬性。
Object.values()
方法返回一個數組,成員是參數對象自身的(非繼承的)全部可枚舉屬性的屬性值。
var obj = { p1: 123, p2: 456 }; Object.values(obj) // [123, 456]
Object.entries()
方法返回一個數組,成員是參數對象自身的(非繼承的)全部可枚舉屬性的鍵值對數組。
var obj = { p1: 123, p2: 456 }; Object.entries(obj) // [["p1", "123"], ["p2", 456]]
實例對象的 hasOwnProperty()
方法接受一個字符串做爲參數,返回一個布爾值,表示該實例對象自身是否具備該屬性。有返回 true
,沒有或是繼承的屬性都返回 false
。
var obj = { p: 123 }; obj.hasOwnProperty('p') // true obj.hasOwnProperty('toString') // false
Object.getPrototypeOf()
方法返回參數對象的原型。這是獲取原型對象的標準方法。
var F = function () {}; var f = new F(); Object.getPrototypeOf(f) === F.prototype // true
Object.prototype
的原型是 null
。
Object.getPrototypeOf(Object.prototype) === null // true
Object.setPrototypeOf()
方法爲參數對象設置原型,返回該參數對象。它接受兩個參數,第一個是現有對象,第二個是原型對象。
var a = {}; var b = {x: 1}; Object.setPrototypeOf(a, b); Object.getPrototypeOf(a) === b // true a.x // 1
new
命令可使用 Object.setPrototypeOf()
方法模擬。
var F = function () { this.foo = 'bar'; }; var f = new F(); // 等同於 var f = Object.setPrototypeOf({}, F.prototype); F.call(f);
實例對象的 __proto__
屬性,返回該對象的原型。該屬性可讀寫。
var obj = {}; var p = {}; obj.__proto__ = p; Object.getPrototypeOf(obj) === p // true
根據語言標準,__proto__
屬性只有瀏覽器才須要部署,其餘環境能夠沒有這個屬性。它先後的兩根下劃線,代表它本質是一個內部屬性,不該該對使用者暴露。所以,應該儘可能少用這個屬性,而是用 Object.getPrototypeof()
和 Object.setPrototypeOf()
,進行原型對象的讀寫操做。
實例對象的 isPrototypeOf()
方法,用來判斷該對象是否爲參數對象的原型。
var o1 = {}; var o2 = Object.create(o1); var o3 = Object.create(o2); o2.isPrototypeOf(o3) // true o1.isPrototypeOf(o3) // true
只要實例對象處在參數對象的原型鏈上,isPrototypeOf()
方法都返回true。
Object.prototype.isPrototypeOf({}) // true Object.prototype.isPrototypeOf([]) // true Object.prototype.isPrototypeOf(/xyz/) // true Object.prototype.isPrototypeOf(Object.create(null)) // false
因爲 Object.prototype
處於原型鏈的最頂端,因此對各類實例都返回 true
,只有直接繼承自 null
的對象除外。
Object.create()
方法接受一個對象做爲參數,目的是以參數對象爲原型,返回一個實例對象。該實例徹底繼承原型對象的屬性。
不少時候,須要從一個實例對象 A 生成另外一個實例對象 B,若是 A 是由構造函數建立的,那麼能夠很輕鬆的獲得 A 的構造函數從新生成實例 B,然而不少時候,A 只是一個普通的對象,並非由構造函數生成的,這時候就須要使用Object.create()
方法由 A 生成 B。
var A = { print: function () { console.log('hello'); } }; var B = Object.create(A); Object.getPrototypeOf(B) === A // true B.print() // hello B.print === A.print // true
Object.create()
方法兼容性處理,即生成實例的本質:
if (typeof Object.create !== 'function') { Object.create = function (obj) { function F() {} // 新建一個空的構造函數 F F.prototype = obj; // 讓 F.prototype 屬性指向參數對象 obj return new F(); // 最後返回一個 F 的實例 }; }
下面三種方式生成的新對象是等價的:
var obj1 = Object.create({}); var obj2 = Object.create(Object.prototype); var obj3 = new Object();
若是想要生成一個不繼承任何屬性(好比沒有 toString
和 valueOf
方法)的對象,能夠將 Object.create
的參數設爲 null
。由於生成的實例對象原型是 null
,因此它就不具有定義在 Object.prototype
原型上面的方法。
var obj = Object.create(null);
Object.create()
方法還能夠接受第二個參數。該參數是一個屬性描述對象,它所描述的對象屬性,會添加到實例對象,做爲該對象自身的屬性。
var obj = Object.create({}, { p1: { value: 123, enumerable: true, configurable: true, writable: true, }, p2: { value: 'abc', enumerable: true, configurable: true, writable: true, } }); // 等同於 var obj = Object.create({}); obj.p1 = 123; obj.p2 = 'abc';
Object.create()
方法生成的對象,繼承了它的原型對象的構造函數。
function A() {} var a = new A(); var b = Object.create(a); b.constructor === A // true b instanceof A // true
Object.getOwnPropertyDescriptor()
能夠獲取某個屬性的屬性描述對象。它的第一個參數是對象,第二個參數是對象的某個屬性名。返回的是該屬性的屬性描述對象。
var obj = { p1: 'a', p2: 'b'}; Object.getOwnPropertyDescriptor(obj, 'p1') // { value: "a", // writable: true, // enumerable: true, // configurable: true // }
只能用於對象自身的(非繼承的)屬性。繼承的或不存在的屬性返回 undefined
。
Object.getOwnPropertyDescriptor(obj, 'toString') // undefined
Object.getOwnPropertyDescriptors()
能夠獲取參數對象的全部屬性的屬性描述對象。ES2017 引入標準。
Object.getOwnPropertyDescriptors(obj) // { p1: {value: "a", writable: true, enumerable: true, configurable: true} // p2: {value: "b", writable: true, enumerable: true, configurable: true} // }
Object.defineProperty()
方法容許經過屬性描述對象,定義或修改一個屬性,而後返回修改後的描述對象。
Object.defineProperty(object, propertyName, attributesObject)
Object.defineProperty()
方法接受三個參數,依次以下。
object
:屬性所在的對象propertyName
:字符串,表示屬性名attributesObject
:屬性描述對象var obj = Object.defineProperty({}, 'p', { value: 123, writable: false, enumerable: true, configurable: false }); obj.p // 123 obj.p = 246; obj.p // 123
注意,上例中第一個參數是{ }(一個新建的空對象),p屬性直接定義在這個空對象上面,而後返回這個對象,這是 Object.defineProperty()
的常見用法。
若是屬性已經存在,Object.defineProperty()
方法至關於更新該屬性的屬性描述對象。
Object.defineProperties()
方法能夠定義或修改多個屬性。接受兩個參數。
var obj = Object.defineProperties({}, { p1: { value: 123, enumerable: true }, p2: { value: 'abc', enumerable: true }, p3: { get: function () { return this.p1 + this.p2 }, enumerable:true, configurable:true } }); obj.p1 // 123 obj.p2 // "abc" obj.p3 // "123abc"
注意,一旦定義了取值函數 get
或存值函數 set
,就不能同時定義 writable
屬性或 value
屬性,不然會報錯。
元屬性默認值
Object.defineProperty()
和 Object.defineProperties()
參數裏面的屬性描述對象,writable
、configurable
、enumerable
這三個屬性的默認值都爲 false
。
var obj = {}; Object.defineProperty(obj, 'foo', {}); Object.getOwnPropertyDescriptor(obj, 'foo') // { // value: undefined, // writable: false, // enumerable: false, // configurable: false // }
實例對象的 propertyIsEnumerable()
方法返回一個布爾值,用來判斷某個屬性是否可枚舉。
var obj = {}; obj.p = 123; obj.propertyIsEnumerable('p') // true obj.propertyIsEnumerable('toString') // false
注意,這個方法只能用於判斷對象自身的屬性,對於繼承的屬性一概返回 false
。
有時須要凍結對象的讀寫狀態,防止對象被改變。JavaScript 提供了三種凍結方法,最弱的一種是 Object.preventExtensions()
,其次是 Object.seal()
,最強的是 Object.freeze()
。
Object.preventExtensions()
方法可使得一個對象沒法再添加新的屬性。
var obj = new Object(); Object.preventExtensions(obj); Object.defineProperty(obj, 'p', { value: 'hello' }); // TypeError: Cannot define property p, object is not extensible. obj.p = 1; obj.p // undefined
Object.isExtensible()
方法用於檢查是否能夠爲一個對象添加屬性。能夠添加返回 true
,不能夠添加返回 false
。
var obj = new Object(); Object.isExtensible(obj) // true Object.preventExtensions(obj); Object.isExtensible(obj) // false
Object.seal()
方法使得一個對象既沒法添加新屬性,也沒法刪除舊屬性。
var obj = { p: 'hello' }; Object.seal(obj); delete obj.p; obj.p // "hello" obj.x = 'world'; obj.x // undefined
Object.seal
實質是把屬性描述對象的 configurable
屬性設爲 false
,所以屬性描述對象就不能再改變了。
var obj = { p: 'a' }; // seal方法以前 Object.getOwnPropertyDescriptor(obj, 'p') // {... configurable: true } Object.seal(obj); // seal方法以後 Object.getOwnPropertyDescriptor(obj, 'p') // {... configurable: false } Object.defineProperty(obj, 'p', { enumerable: false }) // TypeError: Cannot redefine property: p
Object.seal
只是禁止新增或刪除屬性,並不影響修改某個屬性的值。
var obj = { p: 'a' }; Object.seal(obj); obj.p = 'b'; obj.p // 'b'
Object.seal
方法對 p 屬性的 value
無效,是由於此時 p 屬性的可寫性由writable
決定。
Object.isSealed()
方法用於檢查一個對象是否使用了 Object.seal
方法。未使用返回false
,使用了返回 true
。
var obj = { p: 'a' }; Object.seal(obj); Object.isSealed(obj) // true
此時,Object.isExtensible()
方法也返回 false
。
Object.isExtensible(obj) // false
Object.freeze()
方法可使得一個對象沒法添加新屬性、沒法刪除舊屬性、也沒法改變屬性的值,使得這個對象實際上變成了常量。
var obj = { p: 'hello' }; Object.freeze(obj); obj.p = 'world'; obj.p // "hello" obj.t = 'hello'; obj.t // undefined delete obj.p // false obj.p // "hello"
Object.isFrozen()
方法用於檢查一個對象是否使用了Object.freeze方法。未使用返回false
,使用了返回 true
。此時 Object.isExtensible()
也返回 false
。
var obj = { p: 'hello' }; Object.freeze(obj); Object.isFrozen(obj) // true Object.isExtensible(obj) // false
侷限性
以上三個方法鎖定對象有侷限性,並非徹底凍結。
能夠經過改變原型對象,來爲對象增長新屬性。
var obj = new Object(); Object.preventExtensions(obj); var proto = Object.getPrototypeOf(obj); proto.t = 'hello'; obj.t // hello
解決方案是,把 obj
的原型也凍結住。
Object.preventExtensions(proto); proto.t = 'hello'; obj.t // undefined
若是屬性值是對象,以上三個方法只能凍結屬性指向的對象地址,而不能凍結對象自己。
var obj = { foo: 1, bar: ['a', 'b'] }; Object.freeze(obj); obj.bar.push('c'); obj.bar // ["a", "b", "c"]
obj.bar
屬性指向一個數組,obj
對象被凍結之後,這個指向沒法改變,即沒法指向其餘值,可是所指向的數組是能夠改變的。
徹底凍結
var constantize = (obj) => { Object.freeze(obj); Object.keys(obj).forEach((key, i) => { if ( typeof obj[key] === 'object' ) { constantize(obj[key]); } }); }; var obj = { foo: 1, bar: ['a', 'b'] }; constantize(obj); obj.bar.push('c'); // TypeError: Cannot add property 2, object is not extensible
Object.assign()
方法用於對象的合併,將全部自身的(非繼承的)可枚舉屬性的值從一個或多個源對象複製到目標對象。返回目標對象。目標對象自身也會改變。
Object.assign(target, ...sources)
target
: 目標對象。sources
: 源對象。若是目標對象中的屬性具備相同的鍵,則屬性將被源中的屬性覆蓋。後來的源的屬性將相似地覆蓋早先的屬性。
var o1 = { a: 1, b: 1, c: 1 }; var o2 = { b: 2, c: 2 }; var o3 = { c: 3 }; var obj = Object.assign({}, o1, o2, o3); obj // { a: 1, b: 2, c: 3 }
Object.assign()
不會跳過那些值爲 null
或 undefined
的源對象。
var o1 = { a: null, b: 1}; var o2 = { c: undefined }; var obj = Object.assign({}, o1, o2); obj // {a: null, b: 1, c: undefined}
Object.assign()
拷貝的是屬性值。假如源對象的屬性值是一個指向對象的引用,它也只拷貝那個引用值。
var obj1 = { a: 0 , b: { c: 0 } }; var obj2 = Object.assign({}, obj1); obj2 // { a: 0, b: { c: 0 } }; obj2.b.c = 3; obj1 // { a: 0, b: { c: 3 } }; obj2 // { a: 0, b: { c: 3 } };
所以針對深拷貝,須要使用其餘方法。
var obj1 = { a: 0 , b: { c: 0}}; var obj2 = JSON.parse(JSON.stringify(obj1)); obj1.b.c = 4; obj2 // { a: 0, b: { c: 0}}
Object.assign()
若是遇到存取器定義的屬性,會只拷貝值。
var obj = { foo: 1, get bar() { return 2; } }; var copy = Object.assign({}, obj); copy // { foo: 1, bar: 2 }
所以必須使用 Object.getOwnPropertyDescriptors()
方法配合 Object.defineProperties()
方法,就能夠實現正確拷貝。但僅限於可拷貝 getter
和 setter
,對於屬性的引用類型仍是屬於淺拷貝。
var obj = { foo: { a : 0 }, get bar() { return 2; } }; var target = Object.defineProperties({}, Object.getOwnPropertyDescriptors(obj) ); Object.getOwnPropertyDescriptor(target, 'bar') // { get : ƒ bar(), set : undefined, enumerable : true, configurable : true } obj.foo.a = 6 target.foo.a // 6
若是屬性不可寫,會引起報錯,若是在引起錯誤以前添加了任何屬性,則能夠更改target對象。
Object.is() 用來比較兩個值是否嚴格相等,與嚴格比較運算符(===)的行爲基本一致。返回布爾值,相等返回 true,不相等返回 false。
不一樣之處只有兩個:一是+0不等於-0,二是NaN等於自身。
+0 === -0 //true NaN === NaN // false Object.is(+0, -0) // false Object.is(NaN, NaN) // true
詳情見 JavaScript 的相等比較。
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 });
valueOf
方法的做用是返回一個對象的「值」,默認狀況下返回對象自己。
var obj = new Object(); obj.valueOf() === obj // true
主要用途是,JavaScript 自動類型轉換時會默認調用這個方法。所以,若是給實例對象自定義 valueOf()
方法,覆蓋 Object.prototype.valueOf()
,就能夠獲得想要的結果。
var obj = new Object(); obj.valueOf = function () { return 2; }; 1 + obj // 3
toString
方法的做用是返回一個對象的字符串形式,默認狀況下返回類型字符串。
var obj = {}; obj.toString() // "[object Object]"
JavaScript 自動類型轉換時也會調用這個方法。所以能夠經過自定義實例對象的 toString
方法,覆蓋掉 Object.prototype.toString()
,獲得想要的字符串形式。
var obj = new Object(); obj.toString = function () { return 'hello'; }; obj + ' ' + 'world' // "hello world"
數組、字符串、函數、Date 對象都分別部署了自定義的 toString
方法,覆蓋了 Object.prototype.toString()
方法。
[1, 2, 3].toString() // "1,2,3" '123'.toString() // "123" (function () { return 123; }).toString() // "function () { // return 123; // }" (new Date()).toString() // "Tue May 10 2016 09:11:31 GMT+0800 (CST)"
Object.prototype.toString.call(value)
可用於判斷數據類型,詳情見 判斷數據類型的各類方法。
Object.prototype.toLocaleString
方法與 toString
的返回結果相同,也是返回一個值的字符串形式。
var obj = {}; obj.toString(obj) // "[object Object]" obj.toLocaleString(obj) // "[object Object]"
這個方法的主要做用是留出一個接口,讓各類不一樣的對象實現本身版本的 toLocaleString
,用來返回針對某些地域的特定的值。
目前,主要有三個對象自定義了 toLocaleString
方法。
Array.prototype.toLocaleString()
Number.prototype.toLocaleString()
Date.prototype.toLocaleString()
日期的實例對象的 toString
和 toLocaleString
返回值就不同,並且 toLocaleString
的返回值跟用戶設定的所在地域相關。
var date = new Date(); date.toString() // "Thu Nov 29 2018 16:50:00 GMT+0800 (中國標準時間)" date.toLocaleString() // "2018/11/29 下午4:50:00"