<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          實現(xiàn)雙向綁定Object.defineProperty與proxy的VS

          共 5558字,需瀏覽 12分鐘

           ·

          2022-01-11 00:43

          關注 入坑互聯(lián)網(wǎng) ,回復“加群

          加入我們一起學習,天天進步


          實現(xiàn)雙向綁定的方法有很多,KnockoutJS基于觀察者模式的雙向綁定,Ember基于數(shù)據(jù)模型的雙向綁定,Angular基于臟檢查的雙向綁定,本篇文章我們重點講面試中常見的基于數(shù)據(jù)劫持的雙向綁定。

          常見的基于數(shù)據(jù)劫持的雙向綁定有兩種實現(xiàn),一個是目前Vue在用的Object.defineProperty,另一個是ES2015中新增的Proxy,而Vue的作者宣稱將在Vue3.0版本后加入Proxy從而代替Object.defineProperty,通過本文你也可以知道為什么Vue未來會選擇Proxy。

          (嚴格來講Proxy應該被稱為『代理』而非『劫持』,不過由于作用有很多相似之處,我們在下文中就不再做區(qū)分,統(tǒng)一叫『劫持』。)

          什么是數(shù)據(jù)劫持

          指的是在訪問或者修改對象的某個屬性時,通過一段代碼攔截這個行為,進行額外的操作或者修改返回結果。

          比較典型的是 Object.defineProperty() 和 ES2015 中新增的 Proxy 對象。數(shù)據(jù)劫持最著名的應用當屬雙向綁定,這也是一個已經(jīng)被討論爛了的面試必考題。例如 Vue 2.x 使用的是 Object.defineProperty()(Vue 在 3.x 版本之后改用 Proxy 進行實現(xiàn))。

          Object.defineProperty

          ES5 提供了 Object.defineProperty 方法,該方法可以在一個對象上定義一個新屬性,或者修改一個對象的現(xiàn)有屬性,并返回這個對象。

          語法:

          Object.defineProperty(obj, prop, descriptor)

          參數(shù):

          obj: 要在其上定義屬性的對象。

          prop:  要定義或修改的屬性的名稱。

          descriptor: 將被定義或修改的屬性的描述符。

          來個例子:

          // 這是將要被劫持的對象const data = {  name: '',};
          function say(name) { if (name === '孫紅雷') { console.log('給大家推薦一款超好玩的游戲'); } else if (name === '綠茶婊') { console.log('綠茶婊,撩妹屬性爆表'); } else { console.log('來做我的兄弟'); }}
          // 遍歷對象,對其屬性值進行劫持Object.keys(data).forEach(function(key) { Object.defineProperty(data, key, { //當且僅當該屬性的 enumerable 為 true 時,該屬性才能夠出現(xiàn)在對象的枚舉屬性中。默認為 false。 enumerable: true, //當且僅當該屬性的 configurable 為 true 時,該屬性描述符才能夠被改變,也能夠被刪除。默認為 false。 configurable: true, get: function() { console.log('get'); }, set: function(newVal) { // 當屬性值發(fā)生變化時我們可以進行額外操作 console.log(`大家好,我系${newVal}`); say(newVal); }, });});
          data.name = '綠茶婊';//大家好,我系綠茶婊//綠茶婊,撩妹屬性爆表

          Proxy

          Proxy 用于修改某些操作的默認行為,等同于在語言層面做出修改,所以屬于一種“元編程”(meta programming),即對編程語言進行編程。

          Proxy 可以理解成,在目標對象之前架設一層“攔截”,外界對該對象的訪問,都必須先通過這層攔截,因此提供了一種機制,可以對外界的訪問進行過濾和改寫。Proxy 這個詞的原意是代理,用在這里表示由它來“代理”某些操作,可以譯為“代理器”。

          我們來看看它的語法:

          var proxy = new Proxy(target, handler);

          proxy 對象的所有用法,都是上面這種形式,不同的只是handler參數(shù)的寫法。其中,new Proxy()表示生成一個Proxy實例,target參數(shù)表示所要攔截的目標對象,handler參數(shù)也是一個對象,用來定制攔截行為。

          var proxy = new Proxy({}, {    get: function(obj, prop) {        console.log('設置 get 操作')        return obj[prop];    },    set: function(obj, prop, value) {        console.log('設置 set 操作')        obj[prop] = value;    }});
          proxy.time = 35; // 設置 set 操作
          console.log(proxy.time); // 設置 get 操作 // 35

          除了 get 和 set 之外,proxy 可以攔截多達 13 種操作,比如 has(target, propKey),可以攔截 propKey in proxy 的操作,返回一個布爾值。

          // 使用 has 方法隱藏某些屬性,不被 in 運算符發(fā)現(xiàn)var handler = {  has (target, key) {    if (key[0] === '_') {      return false;    }    return key in target;  }};var target = { _prop: 'foo', prop: 'foo' };var proxy = new Proxy(target, handler);console.log('_prop' in proxy); // false

          又比如說 apply 方法攔截函數(shù)的調(diào)用、call 和 apply 操作。

          apply 方法可以接受三個參數(shù),分別是目標對象、目標對象的上下文對象(this)和目標對象的參數(shù)數(shù)組,不過這里我們簡單演示一下:

          var target = function () { return 'I am the target'; };var handler = {  apply: function () {    return 'I am the proxy';  }};
          var p = new Proxy(target, handler);
          p();// "I am the proxy"

          又比如說 ownKeys 方法可以攔截對象自身屬性的讀取操作。具體來說,攔截以下操作:

          Object.getOwnPropertyNames()Object.getOwnPropertySymbols()Object.keys()

          下面的例子是攔截第一個字符為下劃線的屬性名,不讓它被 for of 遍歷到。

          let target = {  _bar: 'foo',  _prop: 'bar',  prop: 'baz'};
          let handler = { ownKeys (target) { return Reflect.ownKeys(target).filter(key => key[0] !== '_'); }};
          let proxy = new Proxy(target, handler);for (let key of Object.keys(proxy)) { console.log(target[key]);}// "baz"

          我們使用 proxy 再來寫一下 watch 函數(shù)。使用效果如下:

          (function() {    var root = this;
          function watch(target, func) {
          var proxy = new Proxy(target, { get: function(target, prop) { return target[prop]; }, set: function(target, prop, value) { target[prop] = value; func(prop, value); } });
          if(target[name]) proxy[name] = value; return proxy; }
          this.watch = watch;})()
          var obj = { value: 1}
          var newObj = watch(obj, function(key, newvalue) { if (key == 'value') document.getElementById('container').innerHTML = newvalue;})
          document.getElementById('button').addEventListener("click", function() { newObj.value += 1});

          我們也可以發(fā)現(xiàn),使用 defineProperty 和 proxy 的區(qū)別,當使用 defineProperty,我們修改原來的 obj 對象就可以觸發(fā)攔截,而使用 proxy,就必須修改代理對象,即 Proxy 的實例才可以觸發(fā)攔截。

          vue3為什么用proxy?舍棄Object.defineProperty

          Object.defineProperty缺點:

          ①不能監(jiān)聽數(shù)組的變化

          數(shù)組的以下幾個方法不會觸發(fā) set:

          push、pop、shift、unshift、splice、sort、reverse;

          Vue 把這些方法定義為變異方法 (mutation method),指的是會修改原來數(shù)組的方法。與之對應則是非變異方法 (non-mutating method),例如 filter, concat, slice 等,它們都不會修改原始數(shù)組,而會返回一個新的數(shù)組。

          let arr = [1,2,3]let obj = {}Object.defineProperty(obj, 'arr', {  get () {    console.log('get arr')    return arr  },  set (newVal) {    console.log('set', newVal)    arr = newVal  }})obj.arr.push(4) // 只會打印 get arr, 不會打印 setobj.arr = [1,2,3,4] // 這個能正常 set

          ②必須遍歷對象的每個屬性

          使用 Object.defineProperty() 多數(shù)要配合 Object.keys() 和遍歷,于是多了一層嵌套。

          Object.keys(obj).forEach(key => {  Object.defineProperty(obj, key, {    // ...  })})

          ③必須深層遍歷嵌套的對象

          如果是這一類嵌套對象,那就必須逐層遍歷,直到把每個對象的每個屬性都調(diào)用 Object.defineProperty() 為止。Vue 的源碼中就能找到這樣的邏輯 (叫做 walk 方法)。

          let obj = {  info: {    name: 'eason'  }}

          針對Object.defineProperty有這些缺點,為什么用proxy?

          1.proxy可以直接監(jiān)聽數(shù)組的變化;

          2.proxy可以監(jiān)聽對象而非屬性.它在目標對象之前架設一層“攔截”,外界對該對象的訪問,都必須先通過這層攔截,因此提供了一種機制,可以對外界的訪問進行過濾和改寫。

          3.Proxy返回的是一個新對象,我們可以只操作新的對象達到目的,而Object.defineProperty只能遍歷對象屬性直接修改。

          4.Proxy有多達13種攔截方法,不限于apply、ownKeys、deleteProperty、has等等是Object.defineProperty不具備的。

          當然,Proxy的劣勢就是兼容性問題,而且無法用polyfill磨平,因此Vue的作者才聲明需要等到下個大版本(3.0)才能用Proxy重寫。

          ?? 看完三件事

          如果你覺得這篇內(nèi)容對你挺有啟發(fā),我想邀請你幫我三個小忙:

          1. 點贊,讓更多的人也能看到這篇內(nèi)容(收藏不點贊,都是耍流氓)。
          2. 關注公眾號「入坑互聯(lián)網(wǎng)」,不定期分享原創(chuàng)知識。
          3. 也看看其它文章

          如何做到優(yōu)秀,好難!

          面試題聯(lián)盟之CSS篇

          年底前端面試及答案-html/css

          年底面試之es6總結

          年底面試之JavaScript總結(用心收集)

          - END -


          結伴同行前端路



          瀏覽 43
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  男人的天堂天天在线视频 | 色情电影A片 | 亚洲无码免费观看高清 | 微信群拉人| 久操视频在线观看免费视频 |