整合營銷服務商

          電腦端+手機端+微信端=數據同步管理

          免費咨詢熱線:

          如何理解JavaScript代理對象(JavaScr

          如何理解JavaScript代理對象(JavaScript Proxy)

          avaScript的Proxy對象是一種強大且靈活的特性,它允許你攔截并自定義對對象執行的操作。自ECMAScript 6(ES6)引入以來,Proxy對象為控制對象的基本操作行為提供了一種機制,使高級用例和改進的安全性成為可能。

          代理對象的基礎

          一個Proxy是由兩個主要組件創建的:目標對象和處理器。目標對象是你想攔截操作的原始對象,處理器是一個包含名為陷阱的方法的對象,這些方法定義了這些操作的自定義行為。

          創建一個Proxy

          const targetObject={
            name: 'John',
            age: 25,
          };
          const handler={
            get(target, prop) {
              console.log(`獲取屬性 ${prop}`);
              return target[prop];
            },
          };
          const proxy=new Proxy(targetObject, handler);
          console.log(proxy.name); // 輸出: 獲取屬性 name, John

          在這個例子中,get陷阱攔截屬性訪問并在返回實際屬性值之前記錄一條消息。

          理解目標、屬性和值

          • 目標(Target):目標是Proxy包裹的原始對象。在上面的例子中,targetObject就是目標。
          • 屬性(Prop):屬性表示對象上被訪問的屬性。在get陷阱中,prop是被訪問的屬性的名稱。
          • 值(Value):值指的是賦給屬性的值。在set陷阱中,value是被賦給屬性的新值。

          常見的處理器方法

          1. get(target, prop, receiver):get陷阱攔截屬性訪問,并允許你自定義讀取屬性時的行為。
          2. set(target, prop, value, receiver):set陷阱攔截屬性賦值,并使你能夠驗證或修改被賦的值。
          3. has(target, prop):has陷阱在使用in操作符檢查屬性是否存在時觸發。
          4. deleteProperty(target, prop):deleteProperty陷阱在使用delete操作符刪除屬性時被調用。
          5. apply(target, thisArg, argumentsList):apply陷阱在Proxy作為函數調用時被觸發。

          代理對象的應用場景

          1. 數據驗證

          使用代理對象可以通過驗證或修改屬性值來強制執行數據約束。

          const validatedUser=new Proxy({}, {
            set(target, prop, value) {
              if (prop==='age' && (typeof value !=='number' || value < 0 || value > 120)) {
                throw new Error('無效的年齡');
              }
              target[prop]=value;
              return true;
            },
          });
          validatedUser.age=30; // 有效賦值
          validatedUser.age=-5; // 拋出錯誤: 無效的年齡

          2. 日志記錄

          代理對象可以輕松記錄屬性訪問情況,為調試或性能監控提供見解。

          const loggedObject=new Proxy({}, {
            get(target, prop) {
              console.log(`訪問屬性: ${prop}`);
              return target[prop];
            },
          });
          loggedObject.name='Alice'; // 訪問屬性: name
          console.log(loggedObject.name); // 訪問屬性: name

          3. 安全性

          代理對象可以通過防止未授權的屬性訪問或操作來增強對象安全性。

          const securedObject=new Proxy({ secret: 'classified' }, {
            get(target, prop) {
              if (prop==='secret') {
                throw new Error('未授權的訪問');
              }
              return target[prop];
            },
          });
          console.log(securedObject.publicInfo); // 訪問允許
          console.log(securedObject.secret); // 拋出錯誤: 未授權的訪問

          4. 記憶化

          代理對象可用于記憶化,緩存耗時的函數調用結果以提高性能。

          function fibonacci(n) {
            if (n <=1) {
              return n;
            }
            return fibonacci(n - 1) + fibonacci(n - 2);
          }
          const memoizedFibonacci=new Proxy({}, {
            get(target, prop) {
              if (!(prop in target)) {
                target[prop]=fibonacci(Number(prop));
              }
              return target[prop];
            },
          });
          console.log(memoizedFibonacci[10]); // 計算并緩存
          console.log(memoizedFibonacci[5]);  // 從緩存中獲取

          實戰示例:電商場景

          考慮一個電商場景,你想使用代理對象來強制執行某些業務規則。

          const product={
            name: 'Smartphone',
            price: 500,
            quantity: 10,
          };
          const securedProduct=new Proxy(product, {
            set(target, prop, value) {
              if (prop==='quantity' && value < 0) {
                throw new Error('無效的數量');
              }
              target[prop]=value;
              return true;
            },
          });
          securedProduct.quantity=15; // 有效賦值
          securedProduct.quantity=-5; // 拋出錯誤: 無效的數量

          在這個例子中,Proxy確保產品的數量不能被設置為負值,從而在電商上下文中執行了一個業務規則。

          結束

          JavaScript Proxy對象為創建動態和可定制的對象行為提供了一個多功能工具。無論是用于數據驗證、日志記錄、安全性還是性能優化,代理對象都為開發者提供了對對象交互的細粒度控制。理解并利用Proxy對象可以在各種實際場景中編寫出更干凈、可維護和安全的代碼。

          這篇文章中,谷歌 Robotics 研究科學家 Eric Jang 對生物學可信深度學習(BPDL)研究提出了質疑。他認為,設計反向傳播的生物學可信替代方法壓根就是一個錯誤的問題。機器學習領域的一個嚴重錯誤就是,對統計學工具和最優控制算法賦予了太多生物學意義。

          選自Eric Jang博客,作者: Eric Jang,機器之心編譯,編輯:魔王、張倩

          生物學可信深度學習 (BPDL) 是神經科學與機器學習交叉領域中的一個活躍研究課題,主要研究如何利用在大腦中可實現的「學習規則」來訓練深度神經網絡。

          2015 年,深度學習巨頭 Yoshua Bengio 發表論文《Towards Biologically Plausible Deep Learning》,探索了更加符合生物學邏輯的深度表征學習版本。該論文的主要觀點如下:

          • 負責突觸權重更新的基礎學習規則 (Spike-Timing-Dependent Plasticity, STDP) 源于一個簡單的更新規則,該規則從機器學習的角度來看是有意義的,可以理解為在某個目標函數上的梯度下降,只要神經動態活動能將放電速率推向更好的目標函數值(可能是監督式、無監督式或獎賞驅動的);
          • 這與變分 EM 法的形式相對應,也就是使用神經動力學實現的近似而非精確的后驗;
          • 我們可以利用近似來估計上述變分解釋(variational interpretation)中更新隱藏狀態所需的梯度,只需將激活向前和向后傳播,并且用成對的層來學習形成去噪自編碼器。

          次年,在 NIPS 2016 Workshop 上,Yoshua Bengio 做了同名演講,其中就探討了「反向傳播」機制的生物學可信性。

          在學習過程中,大腦會調整突觸以優化行為。在皮層中,突觸嵌入在多層網絡中,這導致我們難以確定單個突觸的調整對整個系統行為的影響。而反向傳播算法在深度神經網絡中解決了上述問題,不過長期以來人們一直認為反向傳播在生物層面上存在問題。

          去年 4 月,來自 DeepMind、牛津大學和谷歌大腦的 Timothy P. Lillicrap、Adam Santoro、Geoffrey Hinton 等人在 Nature 子刊《Nature Reviews Neuroscience》發表文章,認為反向連接可能會引發神經活動,而其中的差異可用于局部逼近誤差信號,從而促進大腦深層網絡中的有效學習。即盡管大腦可能未實現字面形式的反向傳播,但是反向傳播的部分特征與理解大腦中的學習具備很強的關聯性

          大腦對反向傳播算法的近似。

          然而,討論并未終止。最近,谷歌 Robotics 研究科學家 Eric Jang 發表博客,對 BPDL 中的反向傳播觀點提出質疑。

          反向傳播為什么一定要有生物學對應?

          Eric Jang 首先列舉了推動 BPDL 發展的主要原因:

          1. 深度神經網絡 (DNN) 可以學習執行生物大腦能夠完成的感知任務(如目標檢測與識別);
          2. 如果激活單元及其權重與 DNN 的關系相當于神經元和突觸之于生物大腦,那么反向傳播(訓練深度神經網絡的主要方法)與什么類似呢?
          3. 如果使用反向傳播無法實現大腦中的學習規則,那么這些規則要如何實現呢?基于反向傳播的更新規則如何在遵循生物學約束的同時實現類似的性能?

          有人曾列舉了反向傳播并非生物學可信的諸多理由,以及提出修復辦法的多種算法。

          來源:https://psychology.stackexchange.com/questions/16269/is-back-prop-biologically-plausible

          而 Eric Jang 的反對意見主要在于,設計反向傳播的生物學可信替代方法壓根就是一個錯誤的問題。BPDL 的重要前提中包含了一個錯誤的假設:層激活是神經元,權重是突觸,因此借助反向傳播的學習必須在生物學習中有對應的部分

          盡管 DNN 叫做深度「神經網絡」,并在多項任務中展現出了卓越能力,但它們本質上與生物神經網絡毫無關聯。機器學習領域的一個嚴重錯誤就是,對統計學工具和最優控制算法賦予了太多生物學意義。這往往使初學者感到困惑。

          DNN 是一系列線性操作和非線性操作的交織,序列應用于實值輸入,僅此而已。它們通過梯度下降進行優化,利用一種叫做「反向傳播」的動態規劃機制對梯度進行高效計算。

          動態規劃是世界第九大奇跡,Eric Jang 認為這是計算機科學領域 Top 3 成就之一。反向傳播在網絡深度方面具備線性時間復雜度,因而從計算成本的角度來看,它很難被打敗。許多 BPDL 算法往往不如反向傳播,因為它們嘗試在更新機制中利用高效的優化機制,且具備額外的約束。

          如果目標是構建生物學可信的學習機制,那么 DNN 中的單元不應與生物神經元一一對應。嘗試使用生物神經元模型模仿 DNN 是落后的,就像用人腦模擬 Windows 操作系統一樣。這很難,而且人腦無法很好地模擬 Windows 系統。

          我們反過來試一下呢:優化函數逼近器,以實現生物學可信的學習規則。這種方式較為直接:

          1. 使用模型神經元和突觸連接構建神經網絡的生物學可信模型。神經元利用脈沖序列、頻率編碼或梯度實現互相通信,并遵循任何「生物學可信」的約束。其參數需要訓練。
          2. 使用計算機輔助搜索,為這些模型神經元設計生物學可信的學習規則。例如,將每個神經元的前向行為和局部更新規則建模為基于人工神經網絡的決策。
          3. 更新函數逼近器,使生物學模型生成期望的學習行為。我們可以通過反向傳播訓練神經網絡。

          用來尋找學習規則的函數逼近器的選擇是無關緊要的——我們真正在乎的是生物大腦如何學習像感知這樣的困難任務,同時遵循已知的限制條件,如生物神經元不把所有的激活都存儲在記憶中,或者只使用局部的學習規則。我們應該利用深度學習的能力找出優秀的函數逼近器,并以此來尋找優秀的生物學習規則。

          「元學習」是另一種選擇?

          「我們應該(人工地)學習如何以生物的方式學習」并非一個全新的觀點,但對于神經科學 + AI 社區來說,這一點還不夠明顯。元學習(學習如何學習)是近年來興起的一個領域,它給出了獲取能夠執行學習行為的系統的方法,該系統有超越梯度下降的潛力。如果元學習可以幫我們找到更加樣本高效或者更優秀、更魯棒的學習器,那它為什么不能幫我們找到遵循生物學習約束的規則呢?其實,最近的幾項研究 [1, 2, 3, 4, 5] 已經探索了這一問題。你確實可以使用反向傳播來訓練一個優于普通反向傳播的獨立學習規則。

          Eric Jang 認為,很多研究者之所以還沒理解這個觀點(即我們應該用元學習方法來模擬生物學可信的回路),是因為目前算力還不夠強,無法同時訓練元學習器和學習器。要想制定元優化方案,我們還需要強大的算力和研究基礎設施,但 JAX 等工具的出現已經讓這一任務變得簡單得多。

          真正的生物學純粹主義者可能會說,利用梯度下降和反向傳播尋找學習規則不是一種「進化上可信的學習規則」,因為進化明顯缺乏執行動態規劃甚至是梯度計算的能力。但如果使元學習器在進化上可信,這一點就能得到修正。例如,用來選擇優秀函數逼近器的機制其實根本不需要依賴反向傳播。相反,我們可以制定一個元 - 元問題,讓選擇過程本身遵守進化選擇的規則,但是選擇過程還是使用反向傳播。

          所以,不要再給反向傳播賦予生物學意義了!

          原文鏈接:https://blog.evjang.com/2021/02/backprop.html

          自己是一名從事了多年開發的web前端老程序員,目前辭職在做自己的web前端私人定制課程,今年年初我花了一個月整理了一份最適合2019年學習的web前端學習干貨,各種框架都有整理,送給每一位前端小伙伴,想要獲取的可以關注我的頭條號并在后臺私信我:前端,即可免費獲取。

          Proxy 對象(Proxy)是 ES6 的一個非常酷卻鮮為人知的特性。雖然這個特性存在已久,但是我還是想在本文中對其稍作解釋,并用一個例子說明一下它的用法。

          什么是 Proxy

          正如 MDN 上簡單而枯燥的定義:

          Proxy 對象用于定義基本操作的自定義行為(如屬性查找,賦值,枚舉,函數調用等)。

          雖然這是一個不錯的總結,但是我卻并沒有從中搞清楚 Proxy 能做什么,以及它能幫我們實現什么。

          首先,Proxy 的概念來源于元編程。簡單的說,元編程是允許我們運行我們編寫的應用程序(或核心)代碼的代碼。例如,臭名昭著的 eval 函數允許我們將字符串代碼當做可執行代碼來執行,它是就屬于元編程領域。

          Proxy API 允許我們在對象和其消費實體中創建中間層,這種特性為我們提供了控制該對象的能力,比如可以決定怎樣去進行它的 get 和 set,甚至可以自定義當訪問這個對象上不存在的屬性的時候我們可以做些什么。

          Proxy 的 API

          var
           p=new
           
          Proxy
          (
          target
          ,
           handler
          );
          

          Proxy 構造函數獲取一個 target 對象,和一個用來攔截 target 對象不同行為的 handler對象。你可以設置下面這些攔截項:

          • has —?攔截 in 操作。比如,你可以用它來隱藏對象上某些屬性。
          • get —?用來攔截讀取操作。比如當試圖讀取不存在的屬性時,你可以用它來返回默認值。
          • set — 用來攔截賦值操作。比如給屬性賦值的時候你可以增加驗證的邏輯,如果驗證不通過可以拋出錯誤。
          • apply — 用來攔截函數調用操作。比如,你可以把所有的函數調用都包裹在 try/catch 語句塊中。

          這只是一部分攔截項,你可以在 MDN 上找到完整的列表。

          下面是將 Proxy 用在驗證上的一個簡單的例子:

          const
           
          Car={
           maker
          :
           
          'BMW'
          ,
           year
          :
           
          2018
          ,
          };
          const
           proxyCar=new
           
          Proxy
          (
          Car
          ,
           
          {
           
          set
          (
          obj
          ,
           prop
          ,
           value
          )
           
          {
           
          if
           
          (
          prop==='maker'
           
          &&
           value
          .
          length 
          <
           
          1
          )
           
          {
           
          throw
           
          new
           
          Error
          (
          'Invalid maker'
          );
           
          }
           
          if
           
          (
          prop==='year'
           
          &&
           
          typeof
           value 
          !=='number'
          )
           
          {
           
          throw
           
          new
           
          Error
          (
          'Invalid year'
          );
           
          }
           obj
          [
          prop
          ]=value
          ;
           
          return
           
          true
          ;
           
          }
          });
          proxyCar
          .
          maker=''
          ;
           
          // throw exception
          proxyCar
          .
          year='1999'
          ;
           
          // throw exception
          

          可以看到,我們可以用 Proxy 來驗證賦給被代理對象的值。

          使用 Proxy 來調試

          為了在實踐中展示 Proxy 的能力,我創建了一個簡單的監測庫,用來監測給定的對象或類,監測項如下:

          • 函數執行時間
          • 函數的調用者或屬性的訪問者
          • 統計每個函數或屬性的被訪問次數。

          這是通過在訪問任意對象、類、甚至是函數時,調用一個名為 proxyTrack 的函數來完成的。

          如果你希望監測是誰給一個對象的屬性賦的值,或者一個函數執行了多久、執行了多少次、誰執行的,這個庫將非常有用。我知道可能還有其他更好的工具來實現上面的功能,但是在這里我創建這個庫就是為了用一用這個 API。

          使用 proxyTrack

          首先,我們看看怎么用:

          function
           
          MyClass
          ()
           
          {}
          MyClass
          .
          prototype={
           isPrime
          :
           
          function
          ()
           
          {
           
          const
           num=this
          .
          num
          ;
           
          for
          (
          var
           i=2
          ;
           i 
          <
           num
          ;
           i
          ++)
           
          if
          (
          num 
          %
           i===0
          )
           
          return
           
          false
          ;
           
          return
           num 
          !==1
           
          &&
           num 
          !==0
          ;
           
          },
           num
          :
           
          null
          ,
          };
          MyClass
          .
          prototype
          .
          constructor=MyClass
          ;
          const
           trackedClass=proxyTrack
          (
          MyClass
          );
          function
           start
          ()
           
          {
           
          const
           my=new
           trackedClass
          ();
           my
          .
          num=573723653
          ;
           
          if
           
          (!
          my
          .
          isPrime
          ())
           
          {
           
          return
           
          `
          $
          {
          my
          .
          num
          }
           is not prime
          `;
           
          }
          }
          function
           main
          ()
           
          {
           start
          ();
          }
          main
          ();
          

          如果我們運行這段代碼,控制臺將會輸出:

          MyClass
          .
          num is being 
          set
           by start 
          for
           the 
          1
           time
          MyClass
          .
          num is being get by isPrime 
          for
           the 
          1
           time
          MyClass
          .
          isPrime was called by start 
          for
           the 
          1
           time and took 
          0
           mils
          .
          MyClass
          .
          num is being get by start 
          for
           the 
          2
           time
          

          proxyTrack 接受 2 個參數:第一個是要監測的對象/類,第二個是一個配置項對象,如果沒傳遞的話將被置為默認值。我們看看這個配置項默認值長啥樣:

          const
           defaultOptions={
           trackFunctions
          :
           
          true
          ,
           trackProps
          :
           
          true
          ,
           trackTime
          :
           
          true
          ,
           trackCaller
          :
           
          true
          ,
           trackCount
          :
           
          true
          ,
           stdout
          :
           
          null
          ,
           filter
          :
           
          null
          ,
          };
          

          可以看到,你可以通過配置你關心的監測項來監測你的目標。比如你希望將結果輸出出來,那么你可以將 console.log 賦給 stdout。

          還可以通過賦給 filter 的回調函數來自定義地控制輸出哪些信息。你將會得到一個包括有監測信息的對象,并且如果你希望保留這個信息就返回 true,反之返回 false。

          在 React 中使用 proxyTrack

          因為 React 的組件實際上也是類,所以你可以通過 proxyTrack 來實時監控它。比如:

          class
           
          MyComponent
           extends 
          Component
          {...}
          export
           
          default
           connect
          (
          mapStateToProps
          )(
          proxyTrack
          (
          MyComponent
          ,
           
          {
           trackFunctions
          :
           
          true
          ,
           trackProps
          :
           
          true
          ,
           trackTime
          :
           
          true
          ,
           trackCaller
          :
           
          true
          ,
           trackCount
          :
           
          true
          ,
           filter
          :
           
          (
          data
          )=>
           
          {
           
          if
          (
           data
          .
          type==='get'
           
          &&
           data
          .
          prop==='componentDidUpdate'
          )
           
          return
           
          false
          ;
           
          return
           
          true
          ;
           
          }
          }));
          

          可以看到,你可以將你不關心的信息過濾掉,否則輸出將會變得雜亂無章。

          實現 proxyTrack

          我們來看看 proxyTrack 的實現。

          首先是這個函數本身:

          export
           
          function
           proxyTrack
          (
          entity
          ,
           options=defaultOptions
          )
           
          {
           
          if
           
          (
          typeof
           entity==='function'
          )
           
          return
           trackClass
          (
          entity
          ,
           options
          );
           
          return
           trackObject
          (
          entity
          ,
           options
          );
          }
          

          沒什么特別的嘛,這里只是調用相關函數。

          再看看 trackObject:

          function
           trackObject
          (
          obj
          ,
           options={})
           
          {
           
          const
           
          {
           trackFunctions
          ,
           trackProps 
          }=options
          ;
           let resultObj=obj
          ;
           
          if
           
          (
          trackFunctions
          )
           
          {
           proxyFunctions
          (
          resultObj
          ,
           options
          );
           
          }
           
          if
           
          (
          trackProps
          )
           
          {
           resultObj=new
           
          Proxy
          (
          resultObj
          ,
           
          {
           
          get
          :
           trackPropertyGet
          (
          options
          ),
           
          set
          :
           trackPropertySet
          (
          options
          ),
           
          });
           
          }
           
          return
           resultObj
          ;
          }
          function
           proxyFunctions
          (
          trackedEntity
          ,
           options
          )
           
          {
           
          if
           
          (
          typeof
           trackedEntity==='function'
          )
           
          return
          ;
           
          Object
          .
          getOwnPropertyNames
          (
          trackedEntity
          ).
          forEach
          ((
          name
          )=>
           
          {
           
          if
           
          (
          typeof
           trackedEntity
          [
          name
          ]==='function'
          )
           
          {
           trackedEntity
          [
          name
          ]=new
           
          Proxy
          (
          trackedEntity
          [
          name
          ],
           
          {
           apply
          :
           trackFunctionCall
          (
          options
          ),
           
          });
           
          }
           
          });
          }
          

          可以看到,假如我們希望監測對象的屬性,我們創建了一個帶有 get 和 set 攔截器的被監測對象。下面是 set 攔截器的實現:

          function
           trackPropertySet
          (
          options={})
           
          {
           
          return
           
          function
           
          set
          (
          target
          ,
           prop
          ,
           value
          ,
           receiver
          )
           
          {
           
          const
           
          {
           trackCaller
          ,
           trackCount
          ,
           stdout
          ,
           filter 
          }=options
          ;
           
          const
           error=trackCaller 
          &&
           
          new
           
          Error
          ();
           
          const
           caller=getCaller
          (
          error
          );
           
          const
           contextName=target
          .
          constructor
          .
          name==='Object'
           
          ?
           
          ''
           
          :
           
          `
          $
          {
          target
          .
          constructor
          .
          name
          }.`;
           
          const
           name=`
          $
          {
          contextName
          }
          $
          {
          prop
          }`;
           
          const
           hashKey=`
          set_$
          {
          name
          }`;
           
          if
           
          (
          trackCount
          )
           
          {
           
          if
           
          (!
          callerMap
          [
          hashKey
          ])
           
          {
           callerMap
          [
          hashKey
          ]=1
          ;
           
          }
           
          else
           
          {
           callerMap
          [
          hashKey
          ]++;
           
          }
           
          }
           let output=`
          $
          {
          name
          }
           is being 
          set
          `;
           
          if
           
          (
          trackCaller
          )
           
          {
           output 
          +=`
           by $
          {
          caller
          .
          name
          }`;
           
          }
           
          if
           
          (
          trackCount
          )
           
          {
           output 
          +=`
           
          for
           the $
          {
          callerMap
          [
          hashKey
          ]}
           time
          `;
           
          }
           let canReport=true
          ;
           
          if
           
          (
          filter
          )
           
          {
           canReport=filter
          ({
           type
          :
           
          'get'
          ,
           prop
          ,
           name
          ,
           caller
          ,
           count
          :
           callerMap
          [
          hashKey
          ],
           value
          ,
           
          });
           
          }
           
          if
           
          (
          canReport
          )
           
          {
           
          if
           
          (
          stdout
          )
           
          {
           stdout
          (
          output
          );
           
          }
           
          else
           
          {
           console
          .
          log
          (
          output
          );
           
          }
           
          }
           
          return
           
          Reflect
          .
          set
          (
          target
          ,
           prop
          ,
           value
          ,
           receiver
          );
           
          };
          }
          

          更有趣的是 trackClass 函數(至少對我來說是這樣):

          function
           trackClass
          (
          cls
          ,
           options={})
           
          {
           cls
          .
          prototype=trackObject
          (
          cls
          .
          prototype
          ,
           options
          );
           cls
          .
          prototype
          .
          constructor=cls
          ;
           
          return
           
          new
           
          Proxy
          (
          cls
          ,
           
          {
           construct
          (
          target
          ,
           args
          )
           
          {
           
          const
           obj=new
           target
          (...
          args
          );
           
          return
           
          new
           
          Proxy
          (
          obj
          ,
           
          {
           
          get
          :
           trackPropertyGet
          (
          options
          ),
           
          set
          :
           trackPropertySet
          (
          options
          ),
           
          });
           
          },
           apply
          :
           trackFunctionCall
          (
          options
          ),
           
          });
          }
          

          在這個案例中,因為我們希望攔截這個類上不屬于原型上的屬性,所以我們給這個類的原型創建了個代理,并且創建了個構造函數攔截器。

          別忘了,即使你在原型上定義了一個屬性,但如果你再給這個對象賦值一個同名屬性,JavaScript 將會創建一個這個屬性的本地副本,所以賦值的改動并不會改變這個類其他實例的行為。這就是為何只對原型做代理并不能滿足要求的原因。

          作者:前端下午茶 公號 / SHERlocked93


          主站蜘蛛池模板: 亚洲色偷偷偷网站色偷一区| 日韩国产精品无码一区二区三区| 亚洲视频一区在线播放| 在线电影一区二区三区| 久久青草国产精品一区| 午夜视频久久久久一区| 亚洲欧洲专线一区| 国产品无码一区二区三区在线蜜桃| 国产SUV精品一区二区88| 91在线视频一区| 久久青草精品一区二区三区| 日韩一区二区在线观看| 日韩在线视频一区二区三区 | av无码一区二区三区| 国模精品一区二区三区视频 | 波多野结衣的AV一区二区三区| 成人在线观看一区| 无码视频一区二区三区| 成人丝袜激情一区二区| 国产乱码精品一区二区三区四川人 | 成人乱码一区二区三区av| av无码一区二区三区| 中文字幕一区二区区免| jizz免费一区二区三区| 国产亚洲自拍一区| 日韩A无码AV一区二区三区| 国产一区二区在线视频播放| 中字幕一区二区三区乱码| 亚洲一区二区三区丝袜| 亚洲AV无码片一区二区三区| 日韩一区二区三区射精| 日韩aⅴ人妻无码一区二区| 无码少妇丰满熟妇一区二区| 午夜爽爽性刺激一区二区视频| 午夜爽爽性刺激一区二区视频| 日本精品一区二区三区视频| 一区二区三区久久精品| 国产亚洲一区二区手机在线观看 | 日韩AV片无码一区二区不卡| 国产色欲AV一区二区三区| 国产一区二区三区不卡在线观看|