第七色在线视频,2021少妇久久久久久久久久,亚洲欧洲精品成人久久av18,亚洲国产精品特色大片观看完整版,孙宇晨将参加特朗普的晚宴

ES6+ Proxy

1. 前言

本節(jié)我們將學習 ES6 的新增知識點 ——Proxy,Proxy 是代理的意思。Proxy 是一個對象,用于定義基本操作的自定義行為(如屬性查找、賦值、枚舉、函數(shù)調(diào)用等)。這是 MDN 上的定義,但是不容易理解,想要理解 Proxy 我們首先需要知道什么是代理?

在日常開發(fā)中比較常見的代理常見有,使用 Charles 代理抓包、nginx 服務器的反向代理,以及 VPN 等,都用到了代理。什么是代理呢?我們先看一張圖:

What is a proxy server? - Seobility Wiki

上圖是客戶端訪問網(wǎng)絡的示意圖,客戶端不能直接訪問網(wǎng)絡,它只能先訪問代理服務器,只有代理服務器才能有權限訪問,然后代理服務器把客戶端請求的信息轉發(fā)給目標服務器,最后代理服務器在接收到目標服務器返回的結果再轉發(fā)給客戶端,這樣就完成了整個請求的響應過程。這是現(xiàn)在大多數(shù)服務器的架構,我們可以把上圖的 Proxy Server 理解為 Nginx。代理有正向代理和反向代理,有興趣的小伙伴可以去深入了解一下。

本節(jié)說的 Proxy 就是作用在 JavaScript 中的一種代理服務,代理的過程其實就是一種對數(shù)據(jù)的劫持過程,Proxy 可以對我們定義的對象的屬性進行劫持,當我們訪問或設置屬性時,會去調(diào)用對應的鉤子執(zhí)行。在 ES5 中我們曾學習過 Object.defineProperty() 它的作用和 Proxy 是相同的,但是 Object.defineProperty() 存在一些性能問題,Proxy 對其進行了升級和擴展更加方便和易用。本節(jié)我們將學習 Proxy 的使用。

2. Object.defineProperty()

在學習 Proxy 之前,我們先來回歸一下 ES5 中的 Object.defineProperty() ,接觸過前端框架的同學應該都知道 Vue 和 React,其中 Vue 中的響應式數(shù)據(jù)底層就是使用 Object.defineProperty() 這個 API 來實現(xiàn)的。下面是 Object.defineProperty() 的語法。

Object.defineProperty(obj, prop, descriptor)

Object.defineProperty() 會接收三個參數(shù):

  • obj 需要觀察的對象;
  • prop 是 obj 上的屬性名;
  • descriptor 對 prop 屬性的描述。

當我們?nèi)ビ^察一個對象時需要在 descriptor 中去定義屬性的描述參數(shù)。在 descriptor 對象中提供了 get 和 set 方法,當我們訪問或設置屬性值時會觸發(fā)對應的函數(shù)。

var obj = {};
var value = undefined;

Object.defineProperty(obj, "a", {
  get: function() {
    console.log('value:', value)
    return value;
  },
  set: function(newValue) {
    console.log('newValue:', newValue)
    value = newValue;
  },
  enumerable: true,
  configurable: true
});
obj.a;	// value: undefined
obj.a = 20;	//  newValue: 20

上面的代碼中,我們使用一個變量 value 來保存值,這里需要注意的是,不能直接使用 obj 上的值,否則就會出現(xiàn)死循環(huán)。

Object.defineProperty() 是 Vue2 的核心, Vue2 在初始化時會對數(shù)據(jù)進行劫持,如果劫持的屬性還是對象的話需要遞歸劫持。下面我們把 Vue2 中數(shù)據(jù)劫持的核心代碼寫出來。

var data = {
  name: 'imooc',
  lession: 'ES6 Wiki',
  obj: {
    a: 1
  }
}

observer(data);


function observer(data) {
  if (typeof data !== 'object' || data == null) {
    return;
  }

  const keys = Object.keys(data);

  for (let i = 0; i < keys.length; i++) {
    let key = keys[i];
    let value = obj[key];
    defineReactive(obj, key, value);
  }
}

function defineReactive(obj, key, value) {
  observer(value);

  Object.defineProperty(obj, key, {
    get() {
      return value;
    },
    set(newValue) {
      if (newValue === value) return;
      observer(newValue);
      value = newValue;
    }
  })
}

上面代碼的核心是 defineReactive 方法,它是遞歸的核心函數(shù),用于重新定義對象的讀寫。從上面的代碼中我們發(fā)現(xiàn) Object.defineProperty() 是有缺陷的,當觀察的數(shù)據(jù)嵌套非常深時,這樣是非常耗費性能的,這也是為什么現(xiàn)在 Vue 的作者極力推廣 Vue3 的原因之一,Vue3 的底層使用了 Proxy 來代替 Object.defineProperty() 那 Proxy 具體有什么好處呢?

3. Proxy

首先我們來看下 Proxy 是如何使用的,語法:

const p = new Proxy(target, handler)

Proxy 對象是一個類,需要通過 new 去實例化一個 Proxy 對象,它接收的參數(shù)比較簡單,只有兩個:

  • target:需要使用 Proxy 進行觀察的目標對象;
  • handler:對目標對象屬性進行處理的對象,包含了處理屬性的回調(diào)函數(shù)等。
const handler = {
	get: function(obj, prop) {
    return obj[prop];
  },
  set: function(obj, prop, value) {
    return obj[prop] = value;
  }
};

const p = new Proxy({}, handler);
p.a = 1;

console.log(p.a, p.b);      // 1, undefined

對比上面的 Object.defineProperty() API 直觀的看 Proxy 做了一些精簡,把對象、屬性和值作為 get 和 set 的參數(shù)傳入進去,不必考慮死循環(huán)的問題了。這是直觀的感受。

上面我們使用了 Object.defineProperty() API 簡單地實現(xiàn)了 Vue2 的響應式原理,那么 Vue 使用 Proxy 是怎么實現(xiàn)的呢?它帶來了哪些好處呢?下面我們看實現(xiàn)源碼:

var target = {
  name: 'imooc',
  lession: 'ES6 Wiki',
  obj: {
    a: 1
  }
}
var p = reactive(target);
console.log(p.name);		// 獲取值: imooc
p.obj.a = 10;						// 獲取值: {a : 1}
console.log(p.obj.a);		// 獲取值: {a : 10}


function reactive(target) {
  return createReactiveObject(target)
}

function createReactiveObject(target) {
  // 判斷如果不是一個對象的話返回
  if (!isObject(target)) return target

  // target觀察前的原對象; proxy觀察后的對象:observed
  observed = new Proxy(target, {
    get(target, key, receiver) {
      const res = target[key];
      console.log('獲取值:', res)
      // todo: 收集依賴...
      return isObject(res) ? reactive(res) : res
    },
    set(target, key, value, receiver) {
      target[key] = value;
    }
  })

  return observed
}

上面的代碼是從 Vue3 中摘出來的 reactive 函數(shù)的實現(xiàn),我們可以直觀地看到?jīng)]有對 target 進行遞歸循環(huán)去創(chuàng)建觀察對象。而且,當我們對 obj 下的 a 屬性設置值時,執(zhí)行 get 函數(shù),這是為什么呢?這就是 Proxy 的優(yōu)點,在對 obj 下屬性設置值時,首先需要調(diào)用 set 方法獲取 target 下 obj 的值,然后判斷 obj 又是一個對象再去調(diào)用 reactive 函數(shù)進行觀察。這樣就不需要遞歸地去對嵌套數(shù)據(jù)進行觀察了,而是在獲取值的時候,判斷獲取的值是不是一個對象,這樣極大地節(jié)約了資源。

4. 小結

本節(jié)主要通過代理和 Object.defineProperty() API 的學習來理解 ES6 的新增知識點 ——Proxy,并且通過 Vue2 和 Vue3 實現(xiàn)響應式原理來對比 Object.defineProperty() 和 Proxy 的優(yōu)缺點,從而更深入地理解 Proxy。