本篇旨在梳理物件靜態方法,與此同時採用對比的方式介紹Reflect介面,在梳理過程中當做學習筆記,如果對於讀者有幫助就更好了。
一、Object靜態方法與對應Reflect
1、遍歷物件屬性方法
- for...in:非靜態方法,此處為展示繼承效果,返回非Symbol類的繼承和自身屬性。
- Object.keys(obj):返回一個陣列,陣列成員為obj自身(非繼承)、可列舉、非Symbol型別屬性名。
- Object.values(obj):返回自身(非繼承)、可列舉、非Symbol型別屬性的值。
- Object.entries(obj):返回自身(非繼承)、可列舉、非Symbol型別屬性的鍵值對陣列。
- Object.fromEntries(obj):Object.entries方法的逆操作,將一個鍵值對陣列轉換為物件。
- Object.getOwnPropertyNames(obj):返回一個陣列,陣列成員為obj自身屬性(非繼承)、非Symbol型別屬性名,與Object.keys()的區別在於返回陣列成員包括不可列舉屬性。
- Object.getOwnPropertySymbols(obj):返回一個陣列,陣列成員為obj自身所有的Symbol型別屬性名。
- Reflect.ownKeys(obj):返回一個陣列,陣列成員為obj自身所有的屬性,是Object.getOwnPropertyNames(obj)與Object.getOwnPropertySymbols(obj)結果集合。
// 定義三個Symbol型別屬性名
let [s1, s2, s3] = [Symbol(), Symbol(), Symbol()]
let parent = {
k1: 'a',
k2: 'b',
[s1]: 's1',
f1: function() {}
}
let child = {}
//child繼承parent
Object.setPrototypeOf(child, parent)
//為child物件新增屬性及描述
Object.defineProperties(child, {
k3: {
value: 'c',
enumerable: true
},
k4: {
value: 'd',
enumerable: false
},
[s2]: {
value: 's2',
enumerable: true
},
[s3]: {
value: 's3',
enumerable: false
},
f2: {
value: function() {},
enumerable: true
}
})
//arr承載for...in 的屬性名
let arr = []
for (let key in child) {
arr.push(key)
}
arr // ["k3", "f2", "k1", "k2", "f1"]
Object.keys(child) //["k3", "f2"]
Object.values(child)//["c", ƒ]
Object.entries(child)//[["k3", "c"],["f2", ƒ]]
Object.getOwnPropertyNames(child) //["k3", "k4", "f2"]
Object.getOwnPropertySymbols(child) // [Symbol(), Symbol()]
Reflect.ownKeys(child) // ["k3", "k4", "f2", Symbol(), Symbol()]
複製程式碼
2、物件模型方法
物件模型方法包括獲取屬性描述及定義屬性
- Object.getOwnPropertyDescriptor(obj , propertyKey):返回一個物件,物件為obj屬性propertyKey描述。
- Reflect.getOwnPropertyDescriptor(obj , propertyKey):返回一個物件,物件為obj屬性propertyKey描述。將來會替代Object.getOwnPropertyDescriptor(obj,propertykey)。
- Object.getOwnPropertyDescriptors(obj):返回一個物件,物件obj的全部屬性的描述。
- Object.defineProperty(obj , propertyKey , attributes):在obj物件中新增屬性和屬性描述。
- Reflect.defineProperty(obj , propertyKey , attributes):用法同Object.defineProperty(obj,propertykey,attributes),將來會替代Object.defineProperty(obj,propertykey,attributes)。
- Object.defineProperties(obj , propertyKeys , attributes)通過描述物件,定義多個屬性。
注意:Object.defineProperty || Reflect.defineProperty || Object.defineProperties 定義屬性時,屬性描述沒有設定為true的話,全部預設為false。
let obj = {}
//新增屬性描述
Object.defineProperty(obj, 'a', {
value: "a",
enumnerabl: true
})
//新增屬性描述
Reflect.defineProperty(obj, 'b', {
value: "b",
writable: true
})
//新增屬性描述
Object.defineProperties(obj, {
'c': {
value: 'c',
configurable: true
}
})
Object.getOwnPropertyDescriptor(obj, 'a')
//{value: "a", writable: false, enumerable: false, configurable: false}
Reflect.getOwnPropertyDescriptor(obj, 'a')
//{value: "a", writable: false, enumerable: false, configurable: false}
Object.getOwnPropertyDescriptors(obj)
/*a: {value: "a", writable: false, enumerable: false, configurable: false}
b: {value: "b", writable: true, enumerable: false, configurable: false}
c: {value: "c", writable: false, enumerable: false, configurable: true}*/複製程式碼
3、控制物件方法
擴充套件--密封--凍結:物件控制程度依次增強。
- Object.isExtensible(obj):判斷物件是否可擴充套件,返回布林值。
- Object.preventExtensions(obj):阻止物件obj擴充套件,其實就是阻止新增屬性,不影響修改已有屬性及刪除屬性。
let obj = {
a: 'a',
b: 'b'
}
Object.preventExtensions(obj)
Object.isExtensible(obj) //false複製程式碼
- Reflect.preventExtensions(obj):作用同Object.preventExtensions(obj)。
- Reflect.isExtensible (obj):作用同Object.isExtensible(obj),區別在於如果引數不是物件,Object.isExtensible會返回false,因為非物件本是不可擴充套件的,而Reflect.isExtensible會報錯。
let obj = {
a: 'a',
b: 'b'
}
Reflect.preventExtensions(obj)
Reflect.isExtensible(obj) //false
複製程式碼
- Object.seal(obj):密封物件obj。除了修改已有屬性的值,其他操作(修改已有屬性描述、新增屬性、刪除屬性)均不允許。
- Object.isSealed(obj):判斷物件是否密封,返回布林值。
let obj = {
a: 'a',
b: 'b'
}
Object.seal(obj)
Object.isSealed(obj) //true複製程式碼
- Object.freeze(obj):凍結物件,物件一旦凍結,屬性的操作全部不允許,換句話說,物件凍結,物件永不可變。
- Object.isFrozen(obj):判斷一個物件是否被凍結,返回一個布林值。
let obj = {
a: 'a',
b: 'b'
}
Object.freeze(obj)
Object.isFrozen(obj) //true複製程式碼
4、原型鏈方法
該節主要介紹例項物件之間的繼承以及檢視例項物件繼承源。
- Object.create(proto [,propertiesObject]):使用指定的物件及其屬性建立一個新物件。Object.create生成的物件,動態繼承原型物件。
proto:新建立物件的原型物件,可以為null。propertiesObject:一個或多個屬性的描述。
經常見到有文章用new命令與Object.create方法比較,個人理解new命令是建立夠建構函式的例項(Object本身就是個函式,可以當成建構函式使用),而如何從一個例項物件生成另外一個例項物件呢,這個時候Object.create就應運而生了。
const parent = {
print: function() {
console.log("I am from parent")
}
}
const child = Object.create(parent)
child.print() //I am from parent
複製程式碼
class parent {
constructor(x = 1, y = 2) {
this.x = x;
this.y = y
}
multi() {
console.log(this.x * this.y)
}
}
let child = new parent(3, 4)
let grandson = Object.create(child)
grandson.multi() //12複製程式碼
- Object.setPrototypeOf(obj , proto):設定該物件的原型。使用obj.__proto__設定為proto,返回引數物件obj本身。
個人理解:Object.create()與Object.setPrototypeOf()兩種方法的差別在於:Object.create()利用原型物件生成一個新物件,而Object.setPrototype()是為已有物件設定原型物件,在實際使用時,在不新增屬性集描述情況下,兩者沒有差別。
- Reflect.setPrototypeOf(obj , proto):返回表示是否成功的布林值,與Object.setPrototypeOf(obj , proto)對應,個人認為將來Reflect.setPrototypeOf會替代Object.setPrototypeOf。
- Object.getPropertypeOf(obj):讀取物件的__proto__屬性,返回物件obj的原型物件。
- Reflect.getPrototypeOf(obj):與Object.getPrototypeOf(obj)對應,個人認為將來Reflect.getPrototypeOf會替代Object.getPrototypeOf。
區別:如果引數不是物件,Object.getPrototypeOf會將這個引數轉為物件,然後再執行,而Reflect.getPrototypeOf會報錯。
let a = { a: 'a' }
let b = Object.setPrototypeOf({}, a)
let c = Object.create(a)
//注意Reflect.setPrototypeOf(obj,proto)的使用
let d = {}
Reflect.setPrototypeOf(d, a)
Object.getPrototypeOf(b) //{a: "a"}
Reflect.getPrototypeOf(c) //{a: "a"}
Reflect.getPrototypeOf(d) //{a: "a"}複製程式碼
- Object.assign(target , source1 , ...):用於將所有可列舉屬性的值從一個或多個源物件複製到目標物件,返回目標物件。
Object.assign方法複製的屬性有幾個點:非繼承、屬性可列舉、同名覆蓋(前提是可列舉)、Symbol類屬性唯一,最重要的是隻能淺拷貝。
let [s1, s2, s3] = [Symbol(), Symbol(), Symbol()]
let parent = {
parent: 'parent',
s1: 's1'
}
let child = Object.create(parent, {
c1: {
value: 'c1',
enumbrable: true
},
c2: {
value: 'c2',
enumerable: false
},
c3: {
value: 'c3',
enumerable: true
},
[s2]: {
value: 's2',
enumerable: true
},
[s3]: {
value: 's3',
enumerable: true
}
})
let c_assign = { c1: 'ca1', c2: 'ca2', [s2]: 'sa2', }
Object.assign(c_assign, child, { d: 'd' })
c_assign //{c1: "ca1", c2: "ca2", c3: "c3", d: "d", Symbol(): "s2", Symbol(): "s3"}複製程式碼
二、Reflect例項物件方法
1、Reflect操作例項物件屬性方法
- Reflect.has(obj , properKey):判斷例項物件obj是否存在屬性propertyKey,返回布林值。
- Reflect.deleteProperty(obj , propertyKey):等同於delete obj[propertyKey],刪除例項物件的屬性,返回布林值。
- Reflect.get(target , name [,receiver]):獲取屬性值的方法。
不設定receiver時,獲取target物件的屬性。如果target不是物件,報錯。
receiver的功能可以理解為資料來源,將資料來源中的屬性值作用在target物件部署讀取函式的屬性上。target物件中存在兩個關鍵詞get和this。
let o1 = {
a: 1,
b: 2,
get sum() {
return this.a + this.b
}
}
let o2 = {
a: 3,
b: 4
}
Reflect.get(o1, 'a') //1
Reflect.get(o1, 'sum', o2) //7複製程式碼
- Reflect.set(target , propertyKey , value [,receiver]):設定例項物件target屬性propertyKey值為value,如果設定receiver,則對於receiver中的屬性進行設值。
如果屬性propertyKey存在,則更改屬性propertyKey值為value;如果屬性不存在,則新增一個屬性並賦值。
當receiver存在時,可以理解為使用目標物件target中設定賦值函式的屬性propertyKey設定receiver中屬性值為value。target物件中存在兩個關鍵詞set和this。
另外,receiver中屬性在賦值函式中不存在時,為receiver物件新增一個屬性,並賦值。
//展示效果
let o3 = {
a: 1,
set c(v) {
this.a = v
}
}
let o4 = {
a: 0
}
Reflect.set(o3, 'a', 2) //o3.a=2
Reflect.set(o3, 'b', 3) //o3.b=3
Reflect.set(o3, 'c', 5, o4) //o4.a=5、o3.a=2複製程式碼
let o3 = {
a: 1,
set c(v) {
this.d = v
}
}
let o4 = {
a: 0
}
Reflect.set(o3, 'c', 5, o4) //o4.d=5複製程式碼
//Reflect.set在Array上的使用
let arr = ['a', 'b', 'c']
Reflect.set(arr, 2, 'd') //arr=["a", "b", "d"]
Reflect.set(arr, 4, 'd') //arr= ["a", "b", "d", empty, "d"]
Reflect.set(arr, 'length', 2) //arr= ["a", "b"]複製程式碼
2、Reflect函式繫結方法
- Reflect.apply(fn , thisArg,args):指定的引數列表args向目標函式fn發起呼叫。
thisArg是函式fn呼叫時繫結的this物件。
args是呼叫時傳遞的類陣列的物件引數列表
//為了區分教程上案例
const fn = function(v) {
let arr = Reflect.ownKeys(this)
return arr[v]
}
let obj = {
a: 'a',
b: 'b',
c: 'c'
}
Reflect.apply(fn, obj, [1]) //'b'複製程式碼
3、Reflect呼叫建構函式方法
- Reflect.construct(target , args):與new操作符一樣,來呼叫建構函式。
這個方法還有第三個引數,可選,暫時不涉及
function A(a) {
this.b = a
}
let a = Reflect.construct(A, ['aa'])
//a={b:'aa'}複製程式碼
三、後續
希望大家針對文中的內容多多指點,另外對於文章沒有提到的方法大家提出來,我也會後續進行更改和補充,謝謝大家。