Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 99久久国产综合精品成人影院,久久免费视频一区,亚洲免费二区

          整合營銷服務(wù)商

          電腦端+手機(jī)端+微信端=數(shù)據(jù)同步管理

          免費(fèi)咨詢熱線:

          如何理解JavaScript代理對(duì)象(JavaScr

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

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

          代理對(duì)象的基礎(chǔ)

          一個(gè)Proxy是由兩個(gè)主要組件創(chuàng)建的:目標(biāo)對(duì)象和處理器。目標(biāo)對(duì)象是你想攔截操作的原始對(duì)象,處理器是一個(gè)包含名為陷阱的方法的對(duì)象,這些方法定義了這些操作的自定義行為。

          創(chuàng)建一個(gè)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

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

          理解目標(biāo)、屬性和值

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

          常見的處理器方法

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

          代理對(duì)象的應(yīng)用場景

          1. 數(shù)據(jù)驗(yàn)證

          使用代理對(duì)象可以通過驗(yàn)證或修改屬性值來強(qiáng)制執(zhí)行數(shù)據(jù)約束。

          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; // 拋出錯(cuò)誤: 無效的年齡

          2. 日志記錄

          代理對(duì)象可以輕松記錄屬性訪問情況,為調(diào)試或性能監(jiān)控提供見解。

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

          3. 安全性

          代理對(duì)象可以通過防止未授權(quán)的屬性訪問或操作來增強(qiáng)對(duì)象安全性。

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

          4. 記憶化

          代理對(duì)象可用于記憶化,緩存耗時(shí)的函數(shù)調(diào)用結(jié)果以提高性能。

          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]); // 計(jì)算并緩存
          console.log(memoizedFibonacci[5]);  // 從緩存中獲取

          實(shí)戰(zhàn)示例:電商場景

          考慮一個(gè)電商場景,你想使用代理對(duì)象來強(qiáng)制執(zhí)行某些業(yè)務(wù)規(guī)則。

          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('無效的數(shù)量');
              }
              target[prop]=value;
              return true;
            },
          });
          securedProduct.quantity=15; // 有效賦值
          securedProduct.quantity=-5; // 拋出錯(cuò)誤: 無效的數(shù)量

          在這個(gè)例子中,Proxy確保產(chǎn)品的數(shù)量不能被設(shè)置為負(fù)值,從而在電商上下文中執(zhí)行了一個(gè)業(yè)務(wù)規(guī)則。

          結(jié)束

          JavaScript Proxy對(duì)象為創(chuàng)建動(dòng)態(tài)和可定制的對(duì)象行為提供了一個(gè)多功能工具。無論是用于數(shù)據(jù)驗(yàn)證、日志記錄、安全性還是性能優(yōu)化,代理對(duì)象都為開發(fā)者提供了對(duì)對(duì)象交互的細(xì)粒度控制。理解并利用Proxy對(duì)象可以在各種實(shí)際場景中編寫出更干凈、可維護(hù)和安全的代碼。

          這篇文章中,谷歌 Robotics 研究科學(xué)家 Eric Jang 對(duì)生物學(xué)可信深度學(xué)習(xí)(BPDL)研究提出了質(zhì)疑。他認(rèn)為,設(shè)計(jì)反向傳播的生物學(xué)可信替代方法壓根就是一個(gè)錯(cuò)誤的問題。機(jī)器學(xué)習(xí)領(lǐng)域的一個(gè)嚴(yán)重錯(cuò)誤就是,對(duì)統(tǒng)計(jì)學(xué)工具和最優(yōu)控制算法賦予了太多生物學(xué)意義。

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

          生物學(xué)可信深度學(xué)習(xí) (BPDL) 是神經(jīng)科學(xué)與機(jī)器學(xué)習(xí)交叉領(lǐng)域中的一個(gè)活躍研究課題,主要研究如何利用在大腦中可實(shí)現(xiàn)的「學(xué)習(xí)規(guī)則」來訓(xùn)練深度神經(jīng)網(wǎng)絡(luò)。

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

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

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

          在學(xué)習(xí)過程中,大腦會(huì)調(diào)整突觸以優(yōu)化行為。在皮層中,突觸嵌入在多層網(wǎng)絡(luò)中,這導(dǎo)致我們難以確定單個(gè)突觸的調(diào)整對(duì)整個(gè)系統(tǒng)行為的影響。而反向傳播算法在深度神經(jīng)網(wǎng)絡(luò)中解決了上述問題,不過長期以來人們一直認(rèn)為反向傳播在生物層面上存在問題。

          去年 4 月,來自 DeepMind、牛津大學(xué)和谷歌大腦的 Timothy P. Lillicrap、Adam Santoro、Geoffrey Hinton 等人在 Nature 子刊《Nature Reviews Neuroscience》發(fā)表文章,認(rèn)為反向連接可能會(huì)引發(fā)神經(jīng)活動(dòng),而其中的差異可用于局部逼近誤差信號(hào),從而促進(jìn)大腦深層網(wǎng)絡(luò)中的有效學(xué)習(xí)。即盡管大腦可能未實(shí)現(xiàn)字面形式的反向傳播,但是反向傳播的部分特征與理解大腦中的學(xué)習(xí)具備很強(qiáng)的關(guān)聯(lián)性

          大腦對(duì)反向傳播算法的近似。

          然而,討論并未終止。最近,谷歌 Robotics 研究科學(xué)家 Eric Jang 發(fā)表博客,對(duì) BPDL 中的反向傳播觀點(diǎn)提出質(zhì)疑。

          反向傳播為什么一定要有生物學(xué)對(duì)應(yīng)?

          Eric Jang 首先列舉了推動(dòng) BPDL 發(fā)展的主要原因:

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

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

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

          而 Eric Jang 的反對(duì)意見主要在于,設(shè)計(jì)反向傳播的生物學(xué)可信替代方法壓根就是一個(gè)錯(cuò)誤的問題。BPDL 的重要前提中包含了一個(gè)錯(cuò)誤的假設(shè):層激活是神經(jīng)元,權(quán)重是突觸,因此借助反向傳播的學(xué)習(xí)必須在生物學(xué)習(xí)中有對(duì)應(yīng)的部分

          盡管 DNN 叫做深度「神經(jīng)網(wǎng)絡(luò)」,并在多項(xiàng)任務(wù)中展現(xiàn)出了卓越能力,但它們本質(zhì)上與生物神經(jīng)網(wǎng)絡(luò)毫無關(guān)聯(lián)。機(jī)器學(xué)習(xí)領(lǐng)域的一個(gè)嚴(yán)重錯(cuò)誤就是,對(duì)統(tǒng)計(jì)學(xué)工具和最優(yōu)控制算法賦予了太多生物學(xué)意義。這往往使初學(xué)者感到困惑。

          DNN 是一系列線性操作和非線性操作的交織,序列應(yīng)用于實(shí)值輸入,僅此而已。它們通過梯度下降進(jìn)行優(yōu)化,利用一種叫做「反向傳播」的動(dòng)態(tài)規(guī)劃機(jī)制對(duì)梯度進(jìn)行高效計(jì)算。

          動(dòng)態(tài)規(guī)劃是世界第九大奇跡,Eric Jang 認(rèn)為這是計(jì)算機(jī)科學(xué)領(lǐng)域 Top 3 成就之一。反向傳播在網(wǎng)絡(luò)深度方面具備線性時(shí)間復(fù)雜度,因而從計(jì)算成本的角度來看,它很難被打敗。許多 BPDL 算法往往不如反向傳播,因?yàn)樗鼈儑L試在更新機(jī)制中利用高效的優(yōu)化機(jī)制,且具備額外的約束。

          如果目標(biāo)是構(gòu)建生物學(xué)可信的學(xué)習(xí)機(jī)制,那么 DNN 中的單元不應(yīng)與生物神經(jīng)元一一對(duì)應(yīng)。嘗試使用生物神經(jīng)元模型模仿 DNN 是落后的,就像用人腦模擬 Windows 操作系統(tǒng)一樣。這很難,而且人腦無法很好地模擬 Windows 系統(tǒng)。

          我們反過來試一下呢:優(yōu)化函數(shù)逼近器,以實(shí)現(xiàn)生物學(xué)可信的學(xué)習(xí)規(guī)則。這種方式較為直接:

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

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

          「元學(xué)習(xí)」是另一種選擇?

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

          Eric Jang 認(rèn)為,很多研究者之所以還沒理解這個(gè)觀點(diǎn)(即我們應(yīng)該用元學(xué)習(xí)方法來模擬生物學(xué)可信的回路),是因?yàn)槟壳八懔€不夠強(qiáng),無法同時(shí)訓(xùn)練元學(xué)習(xí)器和學(xué)習(xí)器。要想制定元優(yōu)化方案,我們還需要強(qiáng)大的算力和研究基礎(chǔ)設(shè)施,但 JAX 等工具的出現(xiàn)已經(jīng)讓這一任務(wù)變得簡單得多。

          真正的生物學(xué)純粹主義者可能會(huì)說,利用梯度下降和反向傳播尋找學(xué)習(xí)規(guī)則不是一種「進(jìn)化上可信的學(xué)習(xí)規(guī)則」,因?yàn)檫M(jìn)化明顯缺乏執(zhí)行動(dòng)態(tài)規(guī)劃甚至是梯度計(jì)算的能力。但如果使元學(xué)習(xí)器在進(jìn)化上可信,這一點(diǎn)就能得到修正。例如,用來選擇優(yōu)秀函數(shù)逼近器的機(jī)制其實(shí)根本不需要依賴反向傳播。相反,我們可以制定一個(gè)元 - 元問題,讓選擇過程本身遵守進(jìn)化選擇的規(guī)則,但是選擇過程還是使用反向傳播。

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

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

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

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

          什么是 Proxy

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

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

          雖然這是一個(gè)不錯(cuò)的總結(jié),但是我卻并沒有從中搞清楚 Proxy 能做什么,以及它能幫我們實(shí)現(xiàn)什么。

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

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

          Proxy 的 API

          var
           p=new
           
          Proxy
          (
          target
          ,
           handler
          );
          

          Proxy 構(gòu)造函數(shù)獲取一個(gè) target 對(duì)象,和一個(gè)用來攔截 target 對(duì)象不同行為的 handler對(duì)象。你可以設(shè)置下面這些攔截項(xiàng):

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

          這只是一部分?jǐn)r截項(xiàng),你可以在 MDN 上找到完整的列表。

          下面是將 Proxy 用在驗(yàn)證上的一個(gè)簡單的例子:

          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 來驗(yàn)證賦給被代理對(duì)象的值。

          使用 Proxy 來調(diào)試

          為了在實(shí)踐中展示 Proxy 的能力,我創(chuàng)建了一個(gè)簡單的監(jiān)測庫,用來監(jiān)測給定的對(duì)象或類,監(jiān)測項(xiàng)如下:

          • 函數(shù)執(zhí)行時(shí)間
          • 函數(shù)的調(diào)用者或?qū)傩缘脑L問者
          • 統(tǒng)計(jì)每個(gè)函數(shù)或?qū)傩缘谋辉L問次數(shù)。

          這是通過在訪問任意對(duì)象、類、甚至是函數(shù)時(shí),調(diào)用一個(gè)名為 proxyTrack 的函數(shù)來完成的。

          如果你希望監(jiān)測是誰給一個(gè)對(duì)象的屬性賦的值,或者一個(gè)函數(shù)執(zhí)行了多久、執(zhí)行了多少次、誰執(zhí)行的,這個(gè)庫將非常有用。我知道可能還有其他更好的工具來實(shí)現(xiàn)上面的功能,但是在這里我創(chuàng)建這個(gè)庫就是為了用一用這個(gè) 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
          ();
          

          如果我們運(yùn)行這段代碼,控制臺(tái)將會(huì)輸出:

          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 個(gè)參數(shù):第一個(gè)是要監(jiān)測的對(duì)象/類,第二個(gè)是一個(gè)配置項(xiàng)對(duì)象,如果沒傳遞的話將被置為默認(rèn)值。我們看看這個(gè)配置項(xiàng)默認(rèn)值長啥樣:

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

          可以看到,你可以通過配置你關(guān)心的監(jiān)測項(xiàng)來監(jiān)測你的目標(biāo)。比如你希望將結(jié)果輸出出來,那么你可以將 console.log 賦給 stdout。

          還可以通過賦給 filter 的回調(diào)函數(shù)來自定義地控制輸出哪些信息。你將會(huì)得到一個(gè)包括有監(jiān)測信息的對(duì)象,并且如果你希望保留這個(gè)信息就返回 true,反之返回 false。

          在 React 中使用 proxyTrack

          因?yàn)?React 的組件實(shí)際上也是類,所以你可以通過 proxyTrack 來實(shí)時(shí)監(jiān)控它。比如:

          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
          ;
           
          }
          }));
          

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

          實(shí)現(xiàn) proxyTrack

          我們來看看 proxyTrack 的實(shí)現(xiàn)。

          首先是這個(gè)函數(shù)本身:

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

          沒什么特別的嘛,這里只是調(diào)用相關(guān)函數(shù)。

          再看看 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
          ),
           
          });
           
          }
           
          });
          }
          

          可以看到,假如我們希望監(jiān)測對(duì)象的屬性,我們創(chuàng)建了一個(gè)帶有 get 和 set 攔截器的被監(jiān)測對(duì)象。下面是 set 攔截器的實(shí)現(xiàn):

          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 函數(shù)(至少對(duì)我來說是這樣):

          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
          ),
           
          });
          }
          

          在這個(gè)案例中,因?yàn)槲覀兿M麛r截這個(gè)類上不屬于原型上的屬性,所以我們給這個(gè)類的原型創(chuàng)建了個(gè)代理,并且創(chuàng)建了個(gè)構(gòu)造函數(shù)攔截器。

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

          作者:前端下午茶 公號(hào) / SHERlocked93


          主站蜘蛛池模板: 精品国产一区二区三区久久久狼| 亚洲香蕉久久一区二区三区四区| 99久久精品午夜一区二区| 亚洲综合一区无码精品| 久久久不卡国产精品一区二区| 亚洲AV无码一区二区大桥未久| 性无码免费一区二区三区在线| 国产aⅴ一区二区| 精品乱码一区二区三区四区| 亚洲bt加勒比一区二区| 国产一区三区三区| 国产精品视频一区二区三区不卡| 国产一区二区三区播放| 日韩aⅴ人妻无码一区二区| 一区二区传媒有限公司| 3d动漫精品啪啪一区二区中| 人妻无码一区二区三区免费| 久久综合精品不卡一区二区| 国产成人久久精品一区二区三区| 久久久国产精品一区二区18禁| 久久久久人妻精品一区三寸蜜桃| 久久国产精品一区| 免费一区二区无码东京热| 中文字幕av无码一区二区三区电影| 91一区二区三区| 国产精品免费一区二区三区四区| 亚洲毛片αv无线播放一区| 日本一区二区免费看| 亚洲美女一区二区三区| 91国偷自产一区二区三区| 国模无码一区二区三区| 国产精品资源一区二区| 一区二区在线视频免费观看| 中文字幕一区二区三区永久 | 人妻少妇精品视频一区二区三区 | 日韩精品无码一区二区三区| 中文字幕一区一区三区| 亚洲变态另类一区二区三区| 国产在线一区二区视频| 99久久精品费精品国产一区二区 | 亚洲欧美国产国产一区二区三区|