欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

JavaScript中的Proxy-Reflect操作方法

 更新時(shí)間:2023年11月06日 10:14:11   作者:coder!mq  
這篇文章主要介紹了JavaScript中的Proxy-Reflect操作方法,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧

一、監(jiān)聽對象的操作

我們先來看一個(gè)需求:有一個(gè)對象,我們希望監(jiān)聽這個(gè)對象中的屬性被設(shè)置或獲取的過程

  • 通過我們前面所學(xué)的知識,能不能做到這一點(diǎn)呢?
  • 其實(shí)是可以的,我們可以通過之前的屬性描述符中的存儲屬性描述符來做到; 監(jiān)聽對象的操作

左邊這段代碼就利用了前面講過的 Object.defineProperty 的存儲屬性描述符來 對屬性的操作進(jìn)行監(jiān)聽。

const obj = {
    name: "why",
    age: 18,
    height: 1.88
}
// 需求: 監(jiān)聽對象屬性的所有操作
// 監(jiān)聽屬性的操作
// 1.針對一個(gè)屬性
// let _name = obj.name
// Object.defineProperty(obj, "name", {
//   set: function(newValue) {
//     console.log("監(jiān)聽: 給name設(shè)置了新的值:", newValue)
//     _name = newValue
//   },
//   get: function() {
//     console.log("監(jiān)聽: 獲取name的值")
//     return _name
//   }
// })
// 2.監(jiān)聽所有的屬性: 遍歷所有的屬性, 對每一個(gè)屬性使用defineProperty
const keys = Object.keys(obj)
for (const key of keys) {
    let value = obj[key]
    Object.defineProperty(obj, key, {
        set: function(newValue) {
            console.log(`監(jiān)聽: 給${key}設(shè)置了新的值:`, newValue)
            value = newValue
        },
        get: function() {
            console.log(`監(jiān)聽: 獲取${key}的值`)
            return value
        }
    })
}
// console.log(obj.name)
// obj.name = "kobe"
console.log(obj.age)
obj.age = 17
console.log(obj.age)

但是這樣做有什么缺點(diǎn)呢?

  • 首先,Object.defineProperty設(shè)計(jì)的初衷,不是為了去監(jiān)聽截止一個(gè)對象中所有的屬性的。
    • 我們在定義某些屬性的時(shí)候,初衷其實(shí)是定義普通的屬性,但是后面我們強(qiáng)行將它變成了數(shù)據(jù)屬性描述符。
  • 其次,如果我們想監(jiān)聽更加豐富的操作,比如新增屬性、刪除屬性,那么 Object.defineProperty是無能為力的。

所以我們要知道,存儲數(shù)據(jù)描述符設(shè)計(jì)的初衷并不是為了去監(jiān)聽一個(gè)完整的對象。

二、Proxy類基本使用

在ES6中,新增了一個(gè)Proxy類,這個(gè)類從名字就可以看出來,是用于幫助我們創(chuàng)建一個(gè)代理的:

  • 也就是說,如果我們希望監(jiān)聽一個(gè)對象的相關(guān)操作,那么我們可以先創(chuàng)建一個(gè)代理對象(Proxy對象);
  • 之后對該對象的所有操作,都通過代理對象來完成,代理對象可以監(jiān)聽我們想要對原對象進(jìn)行哪些操作;

我們可以將上面的案例用Proxy來實(shí)現(xiàn)一次:

  • 首先,我們需要new Proxy對象,并且傳入需要偵聽的對象以及一個(gè)處理對象,可以稱之為handler;
    • const p = new Proxy(target, handler)
  • 其次,我們之后的操作都是直接對Proxy的操作,而不是原有的對象,因?yàn)槲覀冃枰趆andler里面進(jìn)行偵聽;
const obj = {
    name: "why",
    age: 18,
    height: 1.88
}
// 1.創(chuàng)建一個(gè)Proxy對象
const objProxy = new Proxy(obj, {
    set: function(target, key, newValue) {
        console.log(`監(jiān)聽: 監(jiān)聽${key}的設(shè)置值: `, newValue)
        target[key] = newValue
    },
    get: function(target, key) {
        console.log(`監(jiān)聽: 監(jiān)聽${key}的獲取`)
        return target[key]
    }
})
// 2.對obj的所有操作, 應(yīng)該去操作objProxy
// console.log(objProxy.name)
// objProxy.name = "kobe"
// console.log(objProxy.name)
// objProxy.name = "james"
objProxy.address = "廣州市"
console.log(objProxy.address)

三、Proxy常見捕獲器

1.Proxy的set和get捕獲器

如果我們想要偵聽某些具體的操作,那么就可以在handler中添加對應(yīng)的捕捉器(Trap):

set和get分別對應(yīng)的是函數(shù)類型;

  • set函數(shù)有四個(gè)參數(shù):
    • target:目標(biāo)對象(偵聽的對象);
    • property:將被設(shè)置的屬性key;
    • value:新屬性值;
    • receiver:調(diào)用的代理對象;
  • get函數(shù)有三個(gè)參數(shù):
    • target:目標(biāo)對象(偵聽的對象);
    • property:被獲取的屬性key;
    • receiver:調(diào)用的代理對象;

2.Proxy其它捕獲器

13個(gè)捕獲器分別是做什么的呢?

handler.getPrototypeOf()

  • Object.getPrototypeOf 方法的捕捉器。

handler.setPrototypeOf()

  • Object.setPrototypeOf 方法的捕捉器。

handler.isExtensible()

  • Object.isExtensible 方法的捕捉器(判斷是否可以新增屬性)。

handler.preventExtensions()

  • Object.preventExtensions 方法的捕捉器。

handler.getOwnPropertyDescriptor()

  • Object.getOwnPropertyDescriptor 方法的捕捉器。

handler.defineProperty()

  • Object.defineProperty 方法的捕捉器。 Proxy所有捕獲器

handler.ownKeys()

  • Object.getOwnPropertyNames 方法和 Object.getOwnPropertySymbols 方法的捕捉器。

handler.has()

  • in 操作符的捕捉器。

handler.get()

  • 屬性讀取操作的捕捉器。

handler.set()

  • 屬性設(shè)置操作的捕捉器。

handler.deleteProperty()

  • delete 操作符的捕捉器。

handler.apply()

  • 函數(shù)調(diào)用操作的捕捉器。
  • handler.construct()
  • new 操作符的捕捉器。
const obj = {
    name: "why",
    age: 18,
    height: 1.88
}
// 1.創(chuàng)建一個(gè)Proxy對象
const objProxy = new Proxy(obj, {
    set: function(target, key, newValue) {
        console.log(`監(jiān)聽: 監(jiān)聽${key}的設(shè)置值: `, newValue)
        target[key] = newValue
    },
    get: function(target, key) {
        console.log(`監(jiān)聽: 監(jiān)聽${key}的獲取`)
        return target[key]
    },
    deleteProperty: function(target, key) {
        console.log(`監(jiān)聽: 監(jiān)聽刪除${key}屬性`)
        delete obj.name
    },
    has: function(target, key) {
        console.log(`監(jiān)聽: 監(jiān)聽in判斷 ${key}屬性`)
        return key in target
    }
})
delete objProxy.name
console.log("age" in objProxy)

3.Proxy的construct和apply

當(dāng)然,我們還會(huì)看到捕捉器中還有construct和apply,它們是應(yīng)用于函數(shù)對象的:

function foo(num1, num2) {
    console.log(this, num1, num2)
}
const fooProxy = new Proxy(foo, {
    apply: function(target, thisArg, otherArgs) {
        console.log("監(jiān)聽執(zhí)行了apply操作")
        target.apply(thisArg, otherArgs)
    },
    construct: function(target, otherArray) {
        console.log("監(jiān)聽執(zhí)行了new操作")
        console.log(target, otherArray)
        return new target(...otherArray)
    }
})
// fooProxy.apply("abc", [111, 222])
new fooProxy("aaa", "bbb")

四、Reflect介紹和作用

Reflect也是ES6新增的一個(gè)API,它是一個(gè)對象,字面的意思是反射

那么這個(gè)Reflect有什么用呢?

  • 它主要提供了很多操作JavaScript對象的方法,有點(diǎn)像Object中操作對象的方法;
  • 比如Reflect.getPrototypeOf(target)類似于 Object.getPrototypeOf();
  • 比如Reflect.defineProperty(target, propertyKey, attributes)類似于Object.defineProperty() ;

如果我們有Object可以做這些操作,那么為什么還需要有Reflect這樣的新增對象呢?

  • 這是因?yàn)樵谠缙诘腅CMA規(guī)范中沒有考慮到這種對 對象本身 的操作如何設(shè)計(jì)會(huì)更加規(guī)范,所以將這些API放到了Object上面;
  • 但是Object作為一個(gè)構(gòu)造函數(shù),這些操作實(shí)際上放到它身上并不合適;
  • 另外還包含一些類似于 in、delete操作符,讓JS看起來是會(huì)有一些奇怪的;
  • 所以在ES6中新增了Reflect,讓我們這些操作都集中到了Reflect對象上;
  • 另外在使用Proxy時(shí),可以做到不操作原對象

那么Object和Reflect對象之間的API關(guān)系,可以參考MDN文檔:

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Reflect/Comparing_Reflect_and_Object_methods

Object和Reflect之間的區(qū)別:Reflect有返回值

"use strict"
const obj = {
    name: "why",
    age: 18
}
Object.defineProperty(obj, "name", {
    configurable: false
})
// Reflect.defineProperty()
// 1.用以前的方式進(jìn)行操作
// delete obj.name
// if (obj.name) {
//   console.log("name沒有刪除成功")
// } else {
//   console.log("name刪除成功")
// }
// 2.Reflect
if (Reflect.deleteProperty(obj, "name")) {
    console.log("name刪除成功")
} else {
    console.log("name沒有刪除成功")
}

五、Reflect的基本使用

Reflect中有哪些常見的方法呢?它和Proxy是一一對應(yīng)的,也是13個(gè):

Reflect.getPrototypeOf(target)

  • 類似于 Object.getPrototypeOf()。

Reflect.setPrototypeOf(target, prototype)

  • 設(shè)置對象原型的函數(shù). 返回一個(gè) Boolean, 如果更新成功,則返回 true。

Reflect.isExtensible(target)

  • 類似于 Object.isExtensible()

Reflect.preventExtensions(target)

  • 類似于 Object.preventExtensions()。返回一個(gè)Boolean。

Reflect.getOwnPropertyDescriptor(target, propertyKey)

  • 類似于 Object.getOwnPropertyDescriptor()。如果對象中存在 該屬性,則返回對應(yīng)的屬性描述符, 否則返回 undefined.

Reflect.defineProperty(target, propertyKey, attributes)

  • 和 Object.defineProperty() 類似。如果設(shè)置成功就會(huì)返回 true Reflect的常見方法

Reflect.ownKeys(target)

  • 返回一個(gè)包含所有自身屬性(不包含繼承屬性)的數(shù)組。(類似于 Object.keys(), 但不會(huì)受enumerable影響).

Reflect.has(target, propertyKey)

  • 判斷一個(gè)對象是否存在某個(gè)屬性,和 in 運(yùn)算符 的功能完全相同。

Reflect.get(target, propertyKey[, receiver])

  • 獲取對象身上某個(gè)屬性的值,類似于 target[name]。

Reflect.set(target, propertyKey, value[, receiver])

  • 將值分配給屬性的函數(shù)。返回一個(gè)Boolean,如果更新成功,則返回true。

Reflect.deleteProperty(target, propertyKey)

  • 作為函數(shù)的delete操作符,相當(dāng)于執(zhí)行 delete target[name]。

Reflect.apply(target, thisArgument, argumentsList)

  • 對一個(gè)函數(shù)進(jìn)行調(diào)用操作,同時(shí)可以傳入一個(gè)數(shù)組作為調(diào)用參數(shù)。和 Function.prototype.apply() 功能類似。

Reflect.construct(target, argumentsList[, newTarget])

  • 對構(gòu)造函數(shù)進(jìn)行 new 操作,相當(dāng)于執(zhí)行 new target(…args)。

那么我們可以將之前Proxy案例中對原對象的操作,都修改為Reflect來操作:

const obj = {
    name: "why",
    age: 18
}
const objProxy = new Proxy(obj, {
    set: function(target, key, newValue, receiver) {
        // target[key] = newValue
        // 1.好處一: 代理對象的目的: 不再直接操作原對象
        // 2.好處二: Reflect.set方法有返回Boolean值, 可以判斷本次操作是否成功
        const isSuccess = Reflect.set(target, key, newValue)
        if (!isSuccess) {
            throw new Error(`set ${key} failure`)
        }
    },
    get: function(target, key, receiver) {
    }
})
// 操作代理對象
objProxy.name = "kobe"
console.log(obj)

1.Reflect的receiver

我們發(fā)現(xiàn)在使用getter、setter的時(shí)候有一個(gè)receiver的參數(shù),它的作用是什么呢?

如果我們的源對象(obj)有setter、getter的訪問器屬性,那么可以通過receiver來改變里面的this

const obj = {
    _name: "why",
    set name(newValue) {
        console.log("this:", this) // 默認(rèn)是obj
        this._name = newValue
    },
    get name() {
        return this._name
    }
}
// obj.name = "aaaa"
// console.log(obj.name)
// obj.name = "kobe"
const objProxy = new Proxy(obj, {
    set: function(target, key, newValue, receiver) {
        // target[key] = newValue
        // 1.好處一: 代理對象的目的: 不再直接操作原對象
        // 2.好處二: Reflect.set方法有返回Boolean值, 可以判斷本次操作是否成功
        /*
           3.好處三:
             > receiver就是外層Proxy對象
             > Reflect.set/get最后一個(gè)參數(shù), 可以決定對象訪問器setter/getter的this指向
        */
        console.log("proxy中設(shè)置方法被調(diào)用" + key)
        const isSuccess = Reflect.set(target, key, newValue, receiver)
        if (!isSuccess) {
            throw new Error(`set ${key} failure`)
        }
    },
    get: function(target, key, receiver) {
        console.log("proxy中獲取方法被調(diào)用")
        return Reflect.get(target, key, receiver)
    }
})
// 操作代理對象
objProxy.name = "kobe"
console.log(objProxy.name)

2.Reflect的construct

function Person(name, age) {
    this.name = name
    this.age = age
}
function Student(name, age) {
    // Person.call(this, name, age)
    const _this = Reflect.construct(Person, [name, age], Student)
    return _this
}
// const stu = new Student("why", 18)
const stu = new Student("why", 18)
console.log(stu)
console.log(stu.__proto__ === Student.prototype)

到此這篇關(guān)于JavaScript中的Proxy-Reflect詳解的文章就介紹到這了,更多相關(guān)JavaScript Proxy-Reflect內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評論