91超碰碰碰碰久久久久久综合_超碰av人澡人澡人澡人澡人掠_国产黄大片在线观看画质优化_txt小说免费全本

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Proxy使用實例分析

發布時間:2022-02-24 16:00:42 來源:億速云 閱讀:180 作者:iii 欄目:開發技術

這篇文章主要講解了“Proxy使用實例分析”,文中的講解內容簡單清晰,易于學習與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學習“Proxy使用實例分析”吧!

了解代理模式

一個例子

作為一個單身鋼鐵直男程序員,小王最近逐漸喜歡上了前端小妹,不過呢,他又和前臺小妹不熟,所以決定委托與前端小妹比較熟的UI小姐姐幫忙給自己搭橋引線。小王于是請UI小姐姐吃了一頓大餐,然后拿出一封情書委托它轉交給前臺小妹,情書上寫的 我喜歡你,我想和你睡覺,不愧鋼鐵直男。不過這樣寫肯定是沒戲的,UI小姐姐吃人嘴短,于是幫忙改了情書,改成了我喜歡你,我想和你一起在晨輝的沐浴下起床,然后交給了前臺小妹。雖然有沒有撮合成功不清楚啊,不過這個故事告訴我們,小王活該單身狗。

其實上面就是一個比較典型的代理模式的例子,小王想給前臺小妹送情書,因為不熟所以委托UI小姐姐UI小姐姐相當于代理人,代替小王完成了送情書的事情。

引申

通過上面的例子,我們想想Vue的數據響應原理,比如下面這段代碼

const xiaowang = {
  love: '我喜歡你,我想和你睡覺'
}
// 送給小姐姐情書
function sendToMyLove(obj) {
    console.log(obj.love)
    return '流氓,滾'
}
console.log(sendToMyLove(xiaowang))

如果沒有UI小姐姐代替送情書,顯示結局是悲慘的,想想Vue2.0的雙向綁定,通過Object.defineProperty來監聽的屬性 get,set方法來實現雙向綁定,這個Object.defineProperty就相當于UI小姐姐

const xiaowang = {
  loveLetter: '我喜歡你,我想和你睡覺'
}
// UI小姐姐代理
Object.defineProperty(xiaowang,'love', {
  get() {
    return xiaowang.loveLetter.replace('睡覺','一起在晨輝的沐浴下起床')
  }
})


// 送給小姐姐情書
function sendToMyLove(obj) {
    console.log(obj.love)
    return '小伙子還挺有詩情畫意的么,不過老娘不喜歡,滾'
}
console.log(sendToMyLove(xiaowang))

雖然依然是一個悲慘的故事,因為送奔馳的成功率可能會更高一些。但是我們可以看到,通過Object.defineproperty可以對對象的已有屬性進行攔截,然后做一些額外的操作。

存在的問題

Vue2.0中,數據雙向綁定就是通過Object.defineProperty去監聽對象的每一個屬性,然后在get,set方法中通過發布訂閱者模式來實現的數據響應,但是存在一定的缺陷,比如只能監聽已存在的屬性,對于新增刪除屬性就無能為力了,同時無法監聽數組的變化,所以在Vue3.0中將其換成了功能更強大的Proxy

(推薦教程:Vue 2教程)

了解Proxy

ProxyES6新推出的一個特性,可以用它去攔截js操作的方法,從而對這些方法進行代理操作。

用Proxy重寫上面的例子

比如我們可以通過Proxy對上面的送情書情節進行重寫:

const xiaowang = {
  loveLetter: '我喜歡你,我想和你睡覺'
}
const proxy = new Proxy(xiaowang, {
  get(target,key) {
    if(key === 'loveLetter') {
      return target[key].replace('睡覺','一起在晨輝的沐浴下起床')
    }
  }
})
// 送給小姐姐情書
function sendToMyLove(obj) {
    console.log(obj.loveLetter)
    return '小伙子還挺有詩情畫意的么,不過老娘不喜歡,滾'
}
console.log(sendToMyLove(proxy))

再看這樣一個場景

請分別使用Object.definePropertyProxy完善下面的代碼邏輯.

function observe(obj, callback) {}


const obj = observe(
  {
    name: '子君',
    sex: '男'
  },
  (key, value) => {
    console.log(`屬性[${key}]的值被修改為[${value}]`)
  }
)


// 這段代碼執行后,輸出 屬性[name]的值被修改為[妹紙]
obj.name = '妹紙'


// 這段代碼執行后,輸出 屬性[sex]的值被修改為[女]
obj.name = '女'

看了上面的代碼,希望大家可以先自行實現以下,下面我們分別用Object.definePropertyProxy去實現上面的邏輯.

  1. 使用Object.defineProperty

/**
 * 請實現這個函數,使下面的代碼邏輯正常運行
 * @param {*} obj 對象
 * @param {*} callback 回調函數
 */
function observe(obj, callback) {
  const newObj = {}
  Object.keys(obj).forEach(key => {
    Object.defineProperty(newObj, key, {
      configurable: true,
      enumerable: true,
      get() {
        return obj[key]
      },
      // 當屬性的值被修改時,會調用set,這時候就可以在set里面調用回調函數
      set(newVal) {
        obj[key] = newVal
        callback(key, newVal)
      }
    })
  })
  return newObj
}


const obj = observe(
  {
    name: '子君',
    sex: '男'
  },
  (key, value) => {
    console.log(`屬性[${key}]的值被修改為[${value}]`)
  }
)


// 這段代碼執行后,輸出 屬性[name]的值被修改為[妹紙]
obj.name = '妹紙'


// 這段代碼執行后,輸出 屬性[sex]的值被修改為[女]
obj.name = '女'
  1. 使用Proxy

function observe(obj, callback) {
  return new Proxy(obj, {
    get(target, key) {
      return target[key]
    },
    set(target, key, value) {
      target[key] = value
      callback(key, value)
    }
  })
}


const obj = observe(
  {
    name: '子君',
    sex: '男'
  },
  (key, value) => {
    console.log(`屬性[${key}]的值被修改為[${value}]`)
  }
)


// 這段代碼執行后,輸出 屬性[name]的值被修改為[妹紙]
obj.name = '妹紙'


// 這段代碼執行后,輸出 屬性[sex]的值被修改為[女]
obj.name = '女'

通過上面兩種不同實現方式,我們可以大概的了解到Object.definePropertyProxy的用法,但是當給對象添加新的屬性的時候,區別就出來了,比如

// 添加編程網站
obj.gzh = 'W3Cschool億速云'

使用Object.defineProperty無法監聽到新增屬性,但是使用Proxy是可以監聽到的。對比上面兩段代碼可以發現有以下幾點不同

  • Object.defineProperty監聽的是對象的每一個屬性,而Proxy監聽的是對象自身

  • 使用Object.defineProperty需要遍歷對象的每一個屬性,對于性能會有一定的影響

  • Proxy對新增的屬性也能監聽到,但Object.defineProperty無法監聽到。

初識Proxy

概念與語法

MDN中,關于Proxy是這樣介紹的: Proxy 對象用于定義基本操作的自定義行為(如屬性查找、賦值、枚舉、函數調用等)。什么意思呢?Proxy就像一個攔截器一樣,它可以在讀取對象的屬性,修改對象的屬性,獲取對象屬性列表,通過for in循環等等操作的時候,去攔截對象上面的默認行為,然后自己去自定義這些行為,比如上面例子中的set,我們通過攔截默認的set,然后在自定義的set里面添加了回調函數的調用

Proxy的語法格式如下

/**
* target: 要兼容的對象,可以是一個對象,數組,函數等等
* handler: 是一個對象,里面包含了可以監聽這個對象的行為函數,比如上面例子里面的`get`與`set`
* 同時會返回一個新的對象proxy, 為了能夠觸發handler里面的函數,必須要使用返回值去進行其他操作,比如修改值
*/
const proxy = new Proxy(target, handler)

在上面的例子里面,我們已經使用到了handler里面提供的getset方法了,接下來我們一一看一下handler里面的方法。

handler 里面的方法列表

handler里面的方法可以有以下這十三個,每一個都對應的一種或多種針對proxy代理對象的操作行為

  1. handler.get

當通過proxy去讀取對象里面的屬性的時候,會進入到get鉤子函數里面

  1. handler.set

當通過proxy去為對象設置修改屬性的時候,會進入到set鉤子函數里面

  1. handler.has

當使用in判斷屬性是否在proxy代理對象里面時,會觸發has,比如

   const obj = {
     name: '子君'
   }
   console.log('name' in obj)
  1. handler.deleteProperty

當使用delete去刪除對象里面的屬性的時候,會進入deleteProperty`鉤子函數

  1. handler.apply

proxy監聽的是一個函數的時候,當調用這個函數時,會進入apply鉤子函數

  1. handle.ownKeys

當通過Object.getOwnPropertyNames,Object.getownPropertySymbols,Object.keys,Reflect.ownKeys去獲取對象的信息的時候,就會進入ownKeys這個鉤子函數

  1. handler.construct

當使用new操作符的時候,會進入construct這個鉤子函數

  1. handler.defineProperty

當使用Object.defineProperty去修改屬性修飾符的時候,會進入這個鉤子函數

  1. handler.getPrototypeOf

當讀取對象的原型的時候,會進入這個鉤子函數

  1. handler.setPrototypeOf

當設置對象的原型的時候,會進入這個鉤子函數

  1. handler.isExtensible

當通過Object.isExtensible去判斷對象是否可以添加新的屬性的時候,進入這個鉤子函數

  1. handler.preventExtensions

當通過Object.preventExtensions去設置對象不可以修改新屬性時候,進入這個鉤子函數

  1. handler.getOwnPropertyDescriptor

在獲取代理對象某個屬性的屬性描述時觸發該操作,比如在執行 Object.getOwnPropertyDescriptor(proxy, "foo") 時會進入這個鉤子函數

Proxy提供了十三種攔截對象操作的方法,本文主要挑選其中一部分在Vue3中比較重要的進行說明,其余的建議可以直接閱讀MDN關于Proxy的介紹。

詳細介紹

get

當通過proxy去讀取對象里面的屬性的時候,會進入到get鉤子函數里面

當我們從一個proxy代理上面讀取屬性的時候,就會觸發get鉤子函數,get函數的結構如下

/**
 * target: 目標對象,即通過proxy代理的對象
 * key: 要訪問的屬性名稱
 * receiver: receiver相當于是我們要讀取的屬性的this,一般情況
 *           下他就是proxy對象本身,關于receiver的作用,后文將具體講解
 */
handle.get(target,key, receiver)

示例

我們在工作中經常會有封裝axios的需求,在封裝過程中,也需要對請求異常進行封裝,比如不同的狀態碼返回的異常信息是不同的,如下是一部分狀態碼及其提示信息:

// 狀態碼提示信息
const errorMessage = {
  400: '錯誤請求',
  401: '系統未授權,請重新登錄',
  403: '拒絕訪問',
  404: '請求失敗,未找到該資源'
}


// 使用方式
const code = 404
const message = errorMessage[code]
console.log(message)

但這存在一個問題,狀態碼很多,我們不可能每一個狀態碼都去枚舉出來,所以對于一些異常狀態碼,我們希望可以進行統一提示,如提示為系統異常,請聯系管理員,這時候就可以使用Proxy對錯誤信息進行代理處理

// 狀態碼提示信息
const errorMessage = {
  400: '錯誤請求',
  401: '系統未授權,請重新登錄',
  403: '拒絕訪問',
  404: '請求失敗,未找到該資源'
}


const proxy = new Proxy(errorMessage, {
  get(target,key) {
    const value = target[key]
    return value || '系統異常,請聯系管理員'
  }
})


// 輸出 錯誤請求
console.log(proxy[400])
// 輸出 系統異常,請聯系管理員
console.log(proxy[500])

set

當為對象里面的屬性賦值的時候,會觸發set

當給對象里面的屬性賦值的時候,會觸發set,set函數的結構如下

/**
 * target: 目標對象,即通過proxy代理的對象
 * key: 要賦值的屬性名稱
 * value: 目標屬性要賦的新值
 * receiver: 與 get的receiver 基本一致
 */
handle.set(target,key,value, receiver)

示例

某系統需要錄入一系列數值用于數據統計,但是在錄入數值的時候,可能錄入的存在一部分異常值,對于這些異常值需要在錄入的時候進行處理, 比如大于100的值,轉換為100, 小于0的值,轉換為0, 這時候就可以使用proxyset,在賦值的時候,對數據進行處理

const numbers = []
const proxy = new Proxy(numbers, {
  set(target,key,value) {
    if(value < 0) {
      value = 0
    }else if(value > 100) {
      value = 100
    }
    target[key] = value
    // 對于set 來說,如果操作成功必須返回true, 否則會被視為失敗
    return true
  }
})


proxy.push(1)
proxy.push(101)
proxy.push(-10)
// 輸出 [1, 100, 0]
console.log(numbers)

對比Vue2.0

在使用Vue2.0的時候,如果給對象添加新屬性的時候,往往需要調用$set, 這是因為Object.defineProperty只能監聽已存在的屬性,而新增的屬性無法監聽,而通過$set相當于手動給對象新增了屬性,然后再觸發數據響應。但是對于Vue3.0來說,因為使用了Proxy, 在他的set鉤子函數中是可以監聽到新增屬性的,所以就不再需要使用$set

const obj = {
  name: '子君'
}
const proxy = new Proxy(obj, {
  set(target,key,value) {
    if(!target.hasOwnProperty(key)) {
      console.log(`新增了屬性${key},值為${value}`)
    }
    target[key] = value
    return true
  }
})
// 新增 公眾號 屬性
// 輸出 新增了屬性gzh,值為前端有的玩
proxy.gzh = '前端有的玩'

has

當使用in判斷屬性是否在proxy代理對象里面時,會觸發has

/**
 * target: 目標對象,即通過proxy代理的對象
 * key: 要判斷的key是否在target中
 */
 handle.has(target,key)

示例

一般情況下我們在js中聲明私有屬性的時候,會將屬性的名字以_開頭,對于這些私有屬性,是不需要外部調用,所以如果可以隱藏掉是最好的,這時候就可以通過has在判斷某個屬性是否在對象時,如果以_開頭,則返回false

const obj =  {
  publicMethod() {},
  _privateMethod(){}
}
const proxy = new Proxy(obj, {
  has(target, key) {
    if(key.startsWith('_')) {
      return false
    }
    return Reflect.get(target,key)
  }
})


// 輸出 false
console.log('_privateMethod' in proxy)


// 輸出 true
console.log('publicMethod' in proxy)

deleteProperty

當使用delete去刪除對象里面的屬性的時候,會進入deleteProperty`攔截器

/**
 * target: 目標對象,即通過proxy代理的對象
 * key: 要刪除的屬性
 */
 handle.deleteProperty(target,key)

示例

現在有一個用戶信息的對象,對于某些用戶信息,只允許查看,但不能刪除或者修改,對此使用Proxy可以對不能刪除或者修改的屬性進行攔截并拋出異常,如下

const userInfo = {
  name: '子君',
  gzh: '前端有的玩',
  sex: '男',
  age: 22
}
// 只能刪除用戶名和公眾號
const readonlyKeys = ['name', 'gzh']
const proxy = new Proxy(userInfo, {
  set(target,key,value) {
    if(readonlyKeys.includes(key)) {
      throw new Error(`屬性${key}不能被修改`)
    }
    target[key] = value
    return true
  },
   deleteProperty(target,key) {
    if(readonlyKeys.includes(key)) {
      throw new Error(`屬性${key}不能被刪除`)
      return
    }
    delete target[key]
    return true
  }
})
// 報錯 
delete proxy.name

對比Vue2.0

其實與$set解決的問題類似,Vue2.0是無法監聽到屬性被刪除的,所以提供了$delete用于刪除屬性,但是對于Proxy,是可以監聽刪除操作的,所以就不需要再使用$delete

其他操作

在上文中,我們提到了Proxyhandler提供了十三個函數,在上面我們列舉了最常用的三個,其實每一個的用法都是基本一致的,比如ownKeys,當通過Object.getOwnPropertyNames,Object.getownPropertySymbols,Object.keys,Reflect.ownKeys去獲取對象的信息的時候,就會進入ownKeys這個鉤子函數,使用這個我們就可以對一些我們不像暴露的屬性進行保護,比如一般會約定_開頭的為私有屬性,所以在使用Object.keys去獲取對象的所有key的時候,就可以把所有_開頭的屬性屏蔽掉。關于剩余的那些屬性,建議大家多去看看MDN中的介紹。

Reflect

在上面,我們獲取屬性的值或者修改屬性的值都是通過直接操作target來實現的,但實際上ES6已經為我們提供了在Proxy內部調用對象的默認行為的API,即Reflect。比如下面的代碼

const obj = {}
const proxy = new Proxy(obj, {
  get(target,key,receiver) {
    return Reflect.get(target,key,receiver)
  }
})

大家可能看到上面的代碼與直接使用target[key]的方式沒什么區別,但實際上Reflect的出現是為了讓Object上面的操作更加規范,比如我們要判斷某一個prop是否在一個對象中,通常會使用到in,即

const obj = {name: '子君'}
console.log('name' in obj)

但上面的操作是一種命令式的語法,通過Reflect可以將其轉變為函數式的語法,顯得更加規范

Reflect.has(obj,'name')

除了has,get之外,其實Reflect上面總共提供了十三個靜態方法,這十三個靜態方法與Proxyhandler上面的十三個方法是一一對應的,通過將ProxyReflect相結合,就可以對對象上面的默認操作進行攔截處理,當然這也就屬于函數元編程的范疇了。

感謝各位的閱讀,以上就是“Proxy使用實例分析”的內容了,經過本文的學習后,相信大家對Proxy使用實例分析這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是億速云,小編將為大家推送更多相關知識點的文章,歡迎關注!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

正定县| 青阳县| 蒙城县| 和政县| 辽源市| 绥中县| 娄烦县| 洛浦县| 东源县| 万州区| 吴江市| 沙田区| 崇信县| 利川市| 峨山| 临沂市| 翼城县| 潮安县| 葵青区| 德兴市| 宁南县| 区。| 铜陵市| 平乐县| 大足县| 玛多县| 淳化县| 米脂县| 独山县| 开远市| 鄂州市| 永城市| 社旗县| 蛟河市| 霍城县| 封开县| 张家港市| 报价| 邻水| 含山县| 巴青县|