
今天這篇文章,我主要想給大家總結一下我們日常開發中一些常用的Object的操作方法,希望可以對各位有所幫助。
01、JavaScript對象有兩種類型
- Native:在ECMAScript标準中定義和描述,包括JavaScript内置對象(數組,日期對象等)和使用者自定義對象;
- Host:在主機環境(如浏覽器)中實作并提供給開發者使用,比如Windows對象和所有的DOM對象;
02、建立對象并添加成員
最簡單的方法(即Object Literal,對象字面變量),之後便可以向它添加屬性。
字面量:字面量表示如何表達這個值,一般除去表達式,給變量指派時,等号右邊都可以認為是字面量。
// 1. 建立空對象後,在添加屬性
const obj = { }
obj.uname = 'dengke'
obj.fn = () => {
console.log('ggg')
}
console.log(obj) // { uname: 'dengke', fn: ƒ }
// 2. 建立對象并且直接添加屬性 (常用)
const obj1 = {
uname:'dengke',
fn: () => {
console.log('ggg')
}
}
console.log(obj1) // { uname: "dengke", fn: ƒ }
補充
- 擴充運算符(spread)是三個點(...)也可以建立對象(傳回一個新對象),注意這是一個淺拷貝
const obj = { name: 'dengke' }
const obj1 = {
age: 18,
temp: {
a: 10
}
}
const obj2 = { ...obj, ...obj1 }
console.log(obj2) // { name: 'dengke', age: 18, temp: { a: 10 } }
obj2.temp.a = 20
console.log(obj2) // { name: 'dengke', age: 18, temp: { a: 20 } }
console.log(obj1) // { name: 'dengke', age: 18, temp: { a: 20 } }
03、通路對象屬性
const obj = {
info: 'wakaka',
inner: {
a: 10,
b: 20
},
arr: [1, 2],
sayHi: (name) => {
console.log(`hi,${name}`)
}
}
// 用 dot(點 .) 的方式通路
console.log(obj.info) // wakaka
console.log(obj.inner) // {"a":10,"b":20}
console.log(obj.arr) // [1,2]
obj.sayHi('dengke') // hi,dengke
// 用 [] 的方式通路
console.log(obj['info']) // wakaka
console.log(obj['inner']) // {"a":10,"b":20}
console.log(obj['arr']) // [1,2]
obj['sayHi']('dengke') // hi,dengke
補充
- 如果要通路的對象不存在,可以使用 邏輯運算符 || 指定預設值
隻要“||”前面為false,不管“||”後面是true還是false,都傳回“||”後面的值。
隻要“||”前面為true,不管“||”後面是true還是false,都傳回“||”前面的值。
console.log(obj.age || 18) // 18
- 很多時候,我們想根據這個值是否為空來做接下來的操作,可以使用空值運算符 (??) (es11)
有一個冷門運算符??可以判斷undefined和null,這樣是比較符合普遍需求的。
const age = 0
const a = age ?? 123
console.log(a) // 0
- 可選鍊式操作符(?.) (es11)
這是當對象上沒有這個鍵的時候,不會報錯,而是指派undefined
const foo = { name: "zengbo" }
let a = foo.name?.toUpperCase() // "ZENGBO"
let b = foo.name?.firstName?.toUpperCase() // "undefined"
04、删除對象屬性
利用關鍵字 `delete`
const o = {
p: 10,
m: 20
}
delete o.p
console.log(o) // { m: 20 }
// 删除對象的屬性後,在通路傳回 undefined
console.log(o.p) // undefined
05、作為函數參數
const displayPerson = (person) => {
console.log(`name: ${person.name || '無名氏'}`)
console.log(`age: ${person['age'] || 0}`)
}
displayPerson({ name: 'dengke', age: 18 })
// name: dengke
// age: 18
displayPerson({ })
// name: 無名氏
// age: 0
06、枚舉對象的屬性
在js裡面枚舉對象屬性一共有三種方法:
- for in: 會周遊對象中所有的可枚舉屬性(包括自有屬性和繼承屬性)
- Object.keys(): 會傳回一個包括所有的可枚舉的自有屬性的名稱組成的數組
- Object.getOwnPropertyNames(): 會傳回自有屬性的名稱 (不管是不是可枚舉的)
1)、 for...in 會周遊對象中所有的可枚舉屬性(包括自有屬性和繼承屬性)
const obj = {
itemA: 'itemA',
itemB: 'itemB'
}
// 使用Object.create建立一個原型為obj的對象 (模拟繼承來的屬性)
var newObj = Object.create(obj)
newObj.newItemA = 'newItemA'
newObj.newItemB = 'newItemB'
for(i in newObj){
console.log(i)
}
// newItemA
// newItemB
// itemA
// itemB
// 現在我們将其中的一個屬性變為不可枚舉屬性
Object.defineProperty(newObj, 'newItemA', {
enumerable: false
})
for(i in newObj){
console.log(i)
}
// newItemB
// itemA
// itemB
補充
如果不想讓for...in枚舉繼承來的屬性可以借助Object.prototype.hasOwnProperty()
// 接上例
for(i in newObj){
if( newObj.hasOwnProperty(i) ) console.log(i)
}
// newItemB
Object.prototype.hasOwnProperty()該方法在下文有更具體的介紹
2)、 Object.keys(): 會傳回一個包括所有的可枚舉的自有屬性的名稱組成的數組
// 接上例
const result = Object.keys(newObj)
console.log(result) // ["newItemB"]
Object.keys()該方法在下文有更具體的介紹
3)、Object.getOwnPropertyNames() 會傳回自有屬性的名稱 (不管是不是可枚舉的)
// 接上例
const result = Object.keys(newObj)
console.log(result) // ['newItemA','newItemB']
07、資料類型檢測
- typeof 常用 多用于原始資料類型的判斷
const fn = function(n){
console.log(n)
}
const str = 'string'
const arr = [1,2,3]
const obj = {
a:123,
b:456
}
const num = 1
const b = true
const n = null
const u = undefined
console.log(typeof str) // string
console.log(typeof arr) // object
console.log(typeof obj) // object
console.log(typeof num) // number
console.log(typeof b) // boolean
console.log(typeof n) // object null是一個空的對象
console.log(typeof u) // undefined
console.log(typeof fn) // function
通過上面的檢測我們發現typeof檢測的Array和Object的傳回類型都是Object,是以用typeof是無法檢測出來數組和對象的。
- tostring 常用 最實用的檢測各種類型
我們經常會把這個封裝成一個函數,使用起來更加友善
/**
* @description: 資料類型的檢測
* @param {any} data 要檢測資料類型的變量
* @return {string} type 傳回具體的類型名稱【小寫】
*/
const isTypeOf = (data) => {
return Object.prototype.toString.call(data).replace(/\[object (\w+)\]/, '$1').toLowerCase()
}
console.log(isTypeOf({})) // object
console.log(isTypeOf([])) // array
console.log(isTypeOf("ss")) // string
console.log(isTypeOf(1)) // number
console.log(isTypeOf(false)) // boolean
console.log(isTypeOf(/w+/)) // regexp
console.log(isTypeOf(null)) // null
console.log(isTypeOf(undefined)) // undefined
console.log(isTypeOf(Symbol("id"))) // symbol
console.log(isTypeOf(() => { })) // function
08、Object常用的API
1)、 Object.assign()
Object.assign() 方法用于将所有可枚舉屬性的值從一個或多個源對象配置設定到目标對象。它将傳回目标對象。常用來合并對象。
const obj1 = { a: 1, b: 2 }
const obj2 = { b: 4, c: 5 }
const obj3 = Object.assign(obj1, obj2)
const obj4 = Object.assign({}, obj1) // 克隆了obj1對象
console.log(obj1) // { a: 1, b: 4, c: 5 } 對同名屬性b進行了替換 obj1發生改變是因為obj2賦給了obj1
console.log(obj2) // { b: 4, c: 5 }
console.log(obj3) // { a: 1, b: 4, c: 5 }
console.log(obj4) // { a: 1, b: 4, c: 5 }
文法
Object.assign(target, ...sources)
- 參數:target 目标參數,sources源對象
- 傳回值:目标對象
注意
- 如果目标對象中的屬性具有相同的鍵,則屬性将被源對象中的屬性覆寫。
- Object.assign 方法隻會拷貝源對象自身的并且可枚舉的屬性到目标對象。
- assign其實是淺拷貝而不是深拷貝
也就是說,如果源對象某個屬性的值是對象,那麼目标對象拷貝得到的是這個對象的引用。同名屬性會替換。
const obj5 = {
name: 'dengke',
a: 10,
fn: {
sum: 10
}
}
const obj6 = Object.assign(obj1, obj5)
console.log(obj6) // { a: 10, b: 2, name: 'dengke', fn: {…}}
console.log(obj1) // {a: 10, b: 2, name: 'dengke', fn: {…}} 對同名屬性a進行了替換
- Object.assign 不會在那些source對象值為null或undefined的時候抛出錯誤。
2)、 Object.keys()
上邊枚舉對象屬性時有用到了Object.keys(),在這裡就具體為大家介紹一下它。
Object.keys() 方法會傳回一個由一個給定對象的自身可枚舉屬性組成的數組,數組中屬性名的排列順序和正常循環周遊該對象時傳回的順序一緻。與Object.values()相似,差別在于這個傳回的是資料的屬性就是key。接下來就會介紹Object.values(),不要着急。
const arr = ['a', 'b', 'c']
console.log(Object.keys(arr)) // ['0', '1', '2']
const obj = { 0: 'a', 1: 'b', 2: 'c' }
console.log(Object.keys(obj)) // ['0', '1', '2']
const obj2 = { 100: 'a', 2: 'b', 7: 'c' }
console.log(Object.keys(obj2)) // ['2', '7', '100']
文法
Object.keys(obj)
- 參數:obj要傳回其枚舉自身屬性的對象。
- 傳回值:一個表示給定對象的所有可枚舉屬性的字元串數組。
注意
- 在ES5裡,如果此方法的參數不是對象(而是一個原始值),那麼它會抛出 TypeError。在ES2015中,非對象的參數将被強制轉換為一個對象。
Object.keys("foo") // TypeError: "foo" is not an object (ES5 code)
Object.keys("foo") // ["0", "1", "2"] (ES2015 code)
3)、Object.values()
Object.values() 方法傳回一個給定對象自身的所有可枚舉屬性值的數組,值的順序與使用for...in循環的順序相同 ( 差別在于 for-in 循環枚舉原型鍊中的屬性 )。與Object.keys()相似,差別在于這個傳回的是資料的值也就是value
const obj1 = { foo: 'bar', baz: 42 }
console.log(Object.values(obj1)) // ['bar', 42]
const obj2 = { 0: 'a', 1: 'b', 2: 'c' }
console.log(Object.values(obj2)) // ['a', 'b', 'c']
文法
Object.values(obj)
- 參數:obj被傳回可枚舉屬性值的對象。
- 傳回值:一個包含對象自身的所有可枚舉屬性值的數組。
注意
- 對象key為number的話,會從升序枚舉傳回。
const obj3 = { 100: 'a', 2: 'b', 7: 'c' }
console.log(Object.values(obj3)) // ['b', 'c', 'a']
4)、Object.entries(obj)
Object.entries() 方法傳回一個給定對象自身可枚舉屬性的鍵值對數組。可使用Object.fromEntries()方法,相當于反轉了Object.entries()方法傳回的資料結構。接下來也會介紹Object.fromEntries()
const obj1 = {
name: 'dengke',
age: 18
};
for (const [key, value] of Object.entries(obj1)) {
console.log(`${key}: ${value}`);
}
// "name: dengke"
// "age: 18"
const obj2 = { foo: 'bar', baz: 42 }
console.log(Object.entries(obj2)) // [ ['foo', 'bar'], ['baz', 42] ]
const obj3 = { 0: 'a', 1: 'b', 2: 'c' }
console.log(Object.entries(obj3)) // [ ['0', 'a'], ['1', 'b'], ['2', 'c'] ]
文法
Object.entries(obj)
- 參數:obj可以傳回其可枚舉屬性的鍵值對的對象。
- 傳回值:給定對象自身可枚舉屬性的鍵值對數組。
補充
- 将Object轉換為Map,new Map()構造函數接受一個可疊代的entries。借助Object.entries方法你可以很容易的将Object轉換為Map:
const obj = { foo: "bar", baz: 42 }
const map = new Map(Object.entries(obj))
console.log(map) // Map { foo: "bar", baz: 42 }
5)、Object.fromEntries()
Object.fromEntries() 方法把鍵值對清單轉換為一個對象。與Object.entries()相反。相當于反轉了Object.entries()方法傳回的資料結構。(下面補充裡有具體的示範)
const entries = new Map([
['foo', 'bar'],
['baz', 42]
]);
const obj = Object.fromEntries(entries);
console.log(obj);
// Object { foo: "bar", baz: 42 }
文法
Object.fromEntries(iterable)
- 參數:iterable類似Array、Map或者其它實作了可疊代協定的可疊代對象。
- 傳回值:一個由該疊代對象條目提供對應屬性的新對象。
補充
- Map 轉化為 Object
通過 Object.fromEntries, 可以将Map轉換為Object:
const map = new Map([ ['foo', 'bar'], ['baz', 42] ])
const obj = Object.fromEntries(map)
console.log(obj)
// { foo: "bar", baz: 42 }
- Array 轉化為 Object
通過 Object.fromEntries, 可以将Array轉換為Object:
const arr = [ ['0', 'a'], ['1', 'b'], ['2', 'c'] ]
const obj = Object.fromEntries(arr)
console.log(obj)
// { 0: "a", 1: "b", 2: "c" }
- 對象轉換
Object.fromEntries 是與 Object.entries()相反的方法,用 數組處理函數 可以像下面這樣轉換對象:
const object1 = { a: 1, b: 2, c: 3 }
const object2 = Object.fromEntries(
Object.entries(object1)
.map(([ key, val ]) => [ key, val * 2 ])
)
// Object.entries(object1) >>> [["a",1],["b",2],["c",3]]
console.log(object2) // { a: 2, b: 4, c: 6 }
6)、 Object.prototype.hasOwnProperty()
上邊枚舉對象屬性時為了避免for..in周遊繼承來的屬性,給大家補充了可以借助Object.prototype.hasOwnProperty()方法進行判斷,在這裡也具體為大家介紹一下它。
hasOwnProperty() 方法會傳回一個布爾值,訓示對象自身屬性中是否具有指定的屬性(也就是,是否有指定的鍵)。
const obj1 = {};
obj1.property1 = 42
console.log(obj1.hasOwnProperty('property1')) // true
console.log(obj1.hasOwnProperty('toString')) // false
console.log(obj1.hasOwnProperty('hasOwnProperty')) // false
文法
obj.hasOwnProperty(prop)
- 參數:prop 要檢測的屬性的String字元串形式表示的名稱,或者Symbol。
- 傳回值:用來判斷某個對象是否含有指定的屬性的布爾值Boolean。
注意
- 隻會對自身屬性進行判斷,繼承來的一律傳回false。配合for...in使用,可以避免其周遊繼承來的屬性。
const o = new Object()
o.prop = 'exists'
console.log(o.hasOwnProperty('prop')) // true
console.log(o.hasOwnProperty('toString')) // false
console.log(o.hasOwnProperty('hasOwnProperty')) // false
- 即使屬性的值是 null 或 undefined,隻要屬性存在,hasOwnProperty 依舊會傳回 true。
const o = new Object();
o.propOne = null
o.propTwo = undefined
console.log(o.hasOwnProperty('propOne')) // true
console.log(o.hasOwnProperty('propTwo')) // true
7)、 Object.getOwnPropertyNames()
上邊枚舉對象屬性時也有用到該方法,在這裡也具體為大家介紹一下它。
Object.getOwnPropertyNames() 傳回一個數組,該數組對元素是 obj自身擁有的枚舉或不可枚舉屬性名稱字元串。數組中枚舉屬性的順序與通過for...in循環Object.keys疊代該對象屬性時一緻。數組中不可枚舉屬性的順序未定義。
const arr = ["a", "b", "c"];
console.log(Object.getOwnPropertyNames(arr).sort()) // ["0", "1", "2", "length"]
// 類數組對象
const obj = { 0: "a", 1: "b", 2: "c"};
console.log(Object.getOwnPropertyNames(obj).sort()) // ["0", "1", "2"]
// 使用Array.forEach輸出屬性名和屬性值
Object.getOwnPropertyNames(obj).forEach(function(val, idx, array) {
console.log(val + " -> " + obj[val]);
})
// 0 -> a
// 1 -> b
// 2 -> c
// 不可枚舉屬性
const my_obj = Object.create({}, {
getFoo: {
value: function() { return this.foo; },
enumerable: false
}
});
my_obj.foo = 1;
console.log(Object.getOwnPropertyNames(my_obj).sort())
// ["foo", "getFoo"]
文法
obj.getOwnPropertyNames(obj)
-參數:obj一個對象,其自身的可枚舉和不可枚舉屬性的名稱被傳回。
- 傳回值:在給定對象上找到的自身屬性對應的字元串數組。
補充
- Object.getOwnPropertyNames和Object.keys的差別:Object.keys隻适用于可枚舉的屬性,而Object.getOwnPropertyNames傳回對象的全部屬性名稱(包括不可枚舉的)。
'use strict'
(function () {
// 人類的構造函數
const person = function (name, age, sex) {
this.name = name
this.age = age
this.sex = sex
this.sing = () => {
console.log('sing');
}
}
// new 一個ladygaga
const gaga = new person('ladygaga', 26, 'girl')
// 給嘎嘎發放一個不可枚舉的身份證
Object.defineProperty(gaga, 'id', {
value: '1234567890',
enumerable: false
})
//檢視gaga的個人資訊
const arr = Object.getOwnPropertyNames(gaga)
console.log(arr) // name, age, sex, sing, id
// 注意和getOwnPropertyNames的差別,不可枚舉的id沒有輸出
const arr1 = Object.keys(gaga)
console.log(arr1) // name, age, sex, sing
})()
- 如果你隻要擷取到可枚舉屬性,可以用Object.keys或用for...in循環(for...in會擷取到原型鍊上的可枚舉屬性,可以使用hasOwnProperty()方法過濾掉)。
- 擷取不可枚舉的屬性,可以使用Array.prototype.filter()方法,從所有的屬性名數組(使用Object.getOwnPropertyNames()方法獲得)中去除可枚舉的屬性(使用Object.keys()方法獲得),剩餘的屬性便是不可枚舉的屬性了:
const target = myObject;
const enum_and_nonenum = Object.getOwnPropertyNames(target);
const enum_only = Object.keys(target);
const nonenum_only = enum_and_nonenum.filter(function(key) {
const indexInEnum = enum_only.indexOf(key);
if (indexInEnum == -1) {
// 沒有發現在enum_only健集中意味着這個健是不可枚舉的,
// 是以傳回true 以便讓它保持在過濾結果中
return true;
} else {
return false;
}
});
console.log(nonenum_only);
注意
- 在 ES5 中,如果參數不是一個原始對象類型,将抛出一個 TypeError異常。在 ES2015 中,非對象參數被強制轉換為對象。
Object.getOwnPropertyNames('foo') // TypeError: "foo" is not an object (ES5 code)
Object.getOwnPropertyNames('foo') // ['length', '0', '1', '2'] (ES2015 code)
8)、 Object.freeze()
Object.freeze() 方法可以當機一個對象。一個被當機的對象再也不能被修改;當機了一個對象則不能向這個對象添加新的屬性,不能删除已有屬性,不能修改該對象已有屬性的可枚舉性、可配置性、可寫性,以及不能修改已有屬性的值。此外,當機一個對象後該對象的原型也不能被修改。freeze() 傳回和傳入的參數相同的對象。
const obj = {
prop: 42
}
Object.freeze(obj)
obj.prop = 33
console.log(obj.prop)
// 42
文法
obj.freeze(obj)
- 參數:obj要被當機的對象。
- 傳回值:被當機的對象。
補充
- 被當機的對象是不可變的。但也不總是這樣。下例展示了當機對象不是常量對象(淺當機)。
const obj1 = {
internal: {}
}
Object.freeze(obj1)
obj1.internal.a = 'aValue'
console.log(obj1.internal.a) // 'aValue'
- 要使對象不可變,需要遞歸當機每個類型為對象的屬性(深當機)。
// 深當機函數.
function deepFreeze(obj) {
// 取回定義在obj上的屬性名
const propNames = Object.getOwnPropertyNames(obj)
// 在當機自身之前當機屬性
propNames.forEach(function(name) {
const prop = obj[name]
// 如果prop是個對象,當機它
if (typeof prop == 'object' && prop !== null)
deepFreeze(prop)
})
// 當機自身
return Object.freeze(obj);
}
const obj2 = {
internal: {}
}
deepFreeze(obj2)
obj2.internal.a = 'anotherValue'
obj2.internal.a // undefined
9)、 Object.isFrozen()
Object.isFrozen() 方法判斷一個對象是否被當機。
// 一個對象預設是可擴充的, 是以它也是非當機的。
Object.isFrozen({}) // false
// 一個不可擴充的空對象同時也是一個當機對象。
var vacuouslyFrozen = Object.preventExtensions({})
Object.isFrozen(vacuouslyFrozen) // true
var frozen = { 1: 81 }
Object.isFrozen(frozen) // false
// 使用Object.freeze是當機一個對象最友善的方法.
Object.freeze(frozen)
Object.isFrozen(frozen) // true
文法
obj.isFrozen(obj)
- 參數:obj被檢測的對象。
- 傳回值:表示給定對象是否被當機的Boolean。
注意
- 在 ES5 中,如果參數不是一個對象類型,将抛出一個TypeError異常。在 ES2015 中,非對象參數将被視為一個當機的普通對象,是以會傳回true。
Object.isFrozen(1) // (ES5 code)
// TypeError: 1 is not an object
Object.isFrozen(1) // (ES2015 code)
// true