JS 反射機制及 Reflect 詳解


一、什么是反射機制

反射機制是在編譯階段不知道是哪個類被加載,而是在運行的時候才加載、執行。
也就是說,反射機制指的是程序在運行時能夠獲取自身的信息。
js 中的 apply 就是反射機制。

二、Reflect

1、Reflect 定義

Reflect 是一個內建的對象,用來提供方法去攔截 JavaScript 的操作。
Reflect 不是一個函數對象,所以它是不可構造的,也就是說它不是一個構造器,不能通過 new 操作符去新建或者將其作為一個函數去調用 Reflect 對象。
Reflect 的所有屬性和方法都是靜態的。

Reflect 內部封裝了一系列對對象的底層操作
Reflect 成員方法就是 Proxy 處理對象的默認實現

const proxy = new Proxy(obj, {
  get(target, property) {
    // 如果沒有定義 get 方法,那么默認返回的就是 Reflect 的 get 方法
    return Reflect.get(target, property)
  }
})

2、Reflect API 匯總

Reflect 提供了一套用於操作對象的 API,我們之前操作對象可以用 Object 上面的一些方法,也可以用 in、delete 這種操作符,使用 Reflect 就統一了操作方式

handler ⽅法 默認調⽤ 功能
get Reflect.get() 獲取對象身上某個屬性的值
set Reflect.set() 在對象上設置屬性
has Reflect.has() 判斷一個對象是否存在某個屬性
deleteProperty Reflect.deleteProperty() 刪除對象上的屬性
getProperty Reflect.getPrototypeOf() 獲取指定對象原型的函數
setProperty Reflect.setPrototypeOf() 設置或改變對象原型的函數
isExtensible Reflect.isExtensible() 判斷一個對象是否可擴展 (即是否能夠添加新的屬性)
preventExtensions Reflect.preventExtensions() 阻止新屬性添加到對象
getOwnPropertyDescriptor Reflect.getOwnPropertyDescriptor() 獲取給定屬性的屬性描述符
defineProperty Reflect.defineProperty() 定義或修改一個對象的屬性
ownKeys Reflect.ownKeys() 返回由目標對象自身的屬性鍵組成的數組
apply Reflect.apply() 對一個函數進行調用操作,同時可以傳入一個數組作為調用參數
construct Reflect.construct() 對構造函數進行 new 操作,實現創建類的實例
.preventExtensions Reflect.preventExtensions() 阻止新屬性添加到對象

3、.apply()

Reflect.apply(target, thisArgument, argumentsList)

  • target:目標函數(必選)
  • thisArgument:target 函數調用時綁定的 this 對象(可選)
  • argumentsList:target 函數調用時傳入的實參列表,該參數應該是一個類數組的對象(可選)

① ES5 用法

先指定方法,再去調用 apply

Math.floor.apply(null, [1.72])  // 1

② ES6 用法

先傳遞 apply,再指定是哪個方法

Reflect.apply(Math.floor, null, [1.72])  // 1

靜態掃描時 Math.floor 是沒有被執行,等到運行時再動態的將 Math.floor 作為參數傳進來的

③ 實際應用

// ES5 用法
let price = 101.5
if (price > 100) {
  price = Math.floor.apply(null, [price])
} else {
  price = Math.ceil.apply(null, [price])
}

price  // 101
// ES6 用法
let price = 101.5

Reflect.apply(price > 100 ? Math.floor : Math.ceil, null, [price])  // 101

4、.construct()

使用反射的方式去實現創建類的實例,類似於 new target(…args)
Reflect.construct(target, argumentsList[, newTarget])

  • target:被運行的目標函數(必選)
  • argumentsList:調用構造函數的數組或者偽數組(可選)
  • newTarget:該參數為構造函數, 參考 new.target 操作符,如果沒有 newTarget 參數, 默認和 target 一樣(可選)

① ES5 用法

let a = new Date()

a.getTime()  // 1632632744483

② ES6 用法

let b = Reflect.construct(Date, [])

b.getTime()  // 1632632744484

5、.defineProperty()

靜態方法 Reflect.defineProperty() 基本等同於 Object.defineProperty() 方法
Reflect.defineProperty(target, propertyKey, attributes)

  • target:目標對象(必選)
  • propertyKey:要定義或修改的屬性的名稱(可選)
  • attributes:要定義或修改的屬性的描述(可選)

① ES5 用法

const student = {}
const r = Object.defineProperty(student, 'name', { value: 'Mike' })

student  // {name: "Mike"}
r  // {name: "Mike"}

② ES6 用法

const student = {}
const r = Reflect.defineProperty(student, 'name', { value: 'Mike' })

student  // {name: "Mike"}
r  // true

這兩個方法效果上來看是一摸一樣的,都可以改變一個對象的值
區別在於返回值不同:Object是返回這個值,Reflect是返回true

PS: 在 W3C 中,以后所有的 Object 上面的方法,都會慢慢遷移到 Reflect 對象,可能以后會在 Object 上面移除這些方法

6、.deleteProperty()

Reflect.deleteProperty 允許你刪除一個對象上的屬性,返回一個 Boolean 值表示該屬性是否被成功刪除,它幾乎與非嚴格的 delete operator 相同
Reflect.deleteProperty(target, propertyKey)

  • target:刪除屬性的目標對象
  • propertyKey:將被刪除的屬性的名稱

① ES5 用法

const obj = { x: 1, y: 2 }
const a = delete obj.x

obj  // {y: 2}
a  // true

② ES6 用法

const obj = { x: 1, y: 2 }
const a = Reflect.deleteProperty(obj, 'x')

obj  // {y: 2}
a  // true

7、.get()

Reflect.get() 方法的工作方式,就像從 object (target[propertyKey]) 中獲取屬性,但它是作為一個函數執行的
Reflect.get(target, propertyKey[, receiver])

① ES5 用法

const obj = { x: 1, y: 2 }

obj.x  // 1
obj['x']  // 1

② ES6 用法

const obj = { x: 1, y: 2 }

Reflect.get(obj, 'x')  // 1

Reflect.get(['a', 'b', 'c'], 1)  // b

8、.getOwnPropertyDescriptor()

靜態方法 Reflect.getOwnPropertyDescriptor()Object.getOwnPropertyDescriptor() 方法相似
如果在對象中存在,則返回給定的屬性的屬性描述符,否則返回 undefined
Reflect.getOwnPropertyDescriptor(target, propertyKey)

① ES5 用法

const obj = { x: 1, y: 2 }

Object.getOwnPropertyDescriptor(obj, 'x')
// {value: 1, writable: true, enumerable: true, configurable: true}

② ES6 用法

const obj = { x: 1, y: 2 }

Reflect.getOwnPropertyDescriptor(obj, 'x')
// {value: 1, writable: true, enumerable: true, configurable: true}

Reflect.getOwnPropertyDescriptor({ x: 'hello' }, 'y')
// undefined

Reflect.getOwnPropertyDescriptor([], 'length')
// {value: 0, writable: true, enumerable: false, configurable: false}

③ 對比

如果 Reflect.getOwnPropertyDescriptor 的第一個參數不是一個對象(一個原始值),那么將造成 TypeError 錯誤
而對於 Object.getOwnPropertyDescriptor,非對象的第一個參數將被強制轉換為一個對象處理

Reflect.getOwnPropertyDescriptor("foo", 0);
// TypeError: "foo" is not non-null object

Object.getOwnPropertyDescriptor("foo", 0);
// { value: "f", writable: false, enumerable: true, configurable: false }

9、.getPrototypeOf()

靜態方法 Reflect.getPrototypeOf()Object.getPrototypeOf() 方法是一樣的,都是返回指定對象的原型(即,內部的 [[Prototype]] 屬性的值)
Reflect.getPrototypeOf(target)

① ES5 用法

const d = New Date()

Object.getPrototypeOf(d)
// {constructor: ƒ, toString: ƒ, toDateString: ƒ, toTimeString: ƒ, toISOString: ƒ, …}

② ES6 用法

const d = New Date()

Reflect.getPrototypeOf(d)
// {constructor: ƒ, toString: ƒ, toDateString: ƒ, toTimeString: ƒ, toISOString: ƒ, …}

10、.has()

判斷一個對象是否存在某個屬性,和 in 運算符 的功能完全相同
Reflect.has(target, propertyKey)

const obj = { x: 1, y: 2 }

Reflect.has(obj, 'x')  // true
Reflect.has(obj, 'z')  // false

11、.isExtensible()

判斷一個對象是否可擴展
Reflect.isExtensibleObject.isExtensible 方法一樣,都是判斷一個對象是否可擴展 (即是否能夠添加新的屬性)
Reflect.isExtensible(target)

const obj = { x: 1, y: 2 }

Reflect.isExtensible(obj)  // true

Object.freeze(obj)  // 阻止新屬性添加到對象
obj.z = 3

Reflect.isExtensible(obj)  // false
obj  // {x: 1, y: 2}

12、.ownKeys()

判斷對象自身屬性
Reflect.ownKeys 方法返回一個由目標對象自身的屬性鍵組成的數組,它的返回值等同於 `Object.getOwnPropertyNames(target).concat(Object.getOwnPropertySymbols(target))
Reflect.ownKeys(target)

const obj = { x: 1, y: 2 }

Reflect.ownKeys(obj)  // ["x", "y"]
Reflect.ownKeys([])  // ["length"]
Reflect.ownKeys([1, 2])  // ["0", "1", "length"]

13、.preventExtensions()

阻止新屬性添加到對象,等同於Object.freeze()
Reflect.preventExtensions 方法阻止新屬性添加到對象,例如:防止將來對對象的擴展被添加到對象中,與 Object.preventExtensions() 方法一致

Reflect.preventExtensions(target)

const obj = { x: 1, y: 2 }

Reflect.isExtensible(obj)  // true

Reflect.preventExtensions(obj)  // 阻止新屬性添加到對象
obj.z = 3

Reflect.isExtensible(obj)  // false
obj  // {x: 1, y: 2}

14、.set()

寫數據
Reflect.set 方法允許你在對象上設置屬性,用來給屬性賦值,類似 property accessor 的語法,但它是以函數的方式
Reflect.set(target, propertyKey, value[, receiver])

const obj = { x: 1, y: 2 }
Reflect.set(obj, 'z', 4)

obj  // {x: 1, y: 2, z: 4}

const arr = ['apple', 'pear']
Reflect.set(arr, 1, 'banana')

arr  // ["apple", "banana"]

15、.setPrototypeOf()

Reflect.setPrototypeOf 方法改變指定對象的原型 (即內部的 [[Prototype]] 屬性值)
Reflect.setPrototypeOf(target, prototype)

const arr = ['apple', 'pear']
Reflect.getPrototypeOf(arr)
// [constructor: ƒ, concat: ƒ, copyWithin: ƒ, fill: ƒ, find: ƒ,…]

Reflect.setPrototypeOf(arr, String.prototype)
Reflect.getPrototypeOf(arr)
// String {"", constructor: ƒ, anchor: ƒ, big: ƒ, blink: ƒ, …}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM