成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

借助 Proxy 實(shí)現(xiàn)回調(diào)函數(shù)執(zhí)行計(jì)數(shù)

source / 2116人閱讀

摘要:本文不深入分析惰性計(jì)算的內(nèi)部原理后面打算多帶帶做一次分享,而是介紹下我是如何實(shí)現(xiàn)上面的回調(diào)函數(shù)執(zhí)行計(jì)數(shù)。問(wèn)題明確下需求或者說(shuō)要解決的問(wèn)題,針對(duì)如下的代碼能夠統(tǒng)計(jì)代碼執(zhí)行過(guò)程中傳入的回調(diào)函數(shù)和的實(shí)際執(zhí)行次數(shù)。

背景

最近在做一個(gè)簡(jiǎn)化版的 Lazy.js:simply-lazy,目的是深入分析 Lazy.js 中惰性求值的實(shí)現(xiàn),同時(shí)由于簡(jiǎn)化了實(shí)現(xiàn)過(guò)程,便于在分享(計(jì)劃近期分享)時(shí)作為 demo 展示。

惰性求值的一個(gè)重要特性是延遲了計(jì)算過(guò)程,從而能夠提升性能,例如:

Lazy([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
  .map(i => i * 2)
  .filter(i => i <= 10)
  .take(3)
  .each(i => print(i))

注:為了書寫方便,回調(diào)函數(shù)使用了 ES 的“=>”來(lái)定義。

這里對(duì)原始數(shù)據(jù)執(zhí)行 map、filter 后只取前 3 個(gè)結(jié)果值,Lazy.js 的實(shí)現(xiàn)策略是 map、filter 中的回調(diào)函數(shù)也盡可能少地被調(diào)用,可以看下統(tǒng)計(jì)出的回調(diào)函數(shù)的調(diào)用次數(shù):

demo 地址:http://www.luobotang.cn/simpl...
注意:需要瀏覽器環(huán)境支持 ES6 特性,建議使用較新版本的 Chrome 打開。

從上面的 demo 中可以看到,第三種情況下,雖然仍舊要執(zhí)行與前面相同的 map、filter 的過(guò)程,但是由于最終只需要返回前 3 個(gè)結(jié)果值,此時(shí) map、filter 的回調(diào)函數(shù)執(zhí)行次數(shù)是減少了的。

本文不深入分析 Lazy.js 惰性計(jì)算的內(nèi)部原理(后面打算多帶帶做一次分享),而是介紹下我是如何實(shí)現(xiàn)上面的回調(diào)函數(shù)執(zhí)行計(jì)數(shù)。

問(wèn)題

明確下需求或者說(shuō)要解決的問(wèn)題,針對(duì)如下的代碼:

Lazy([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
  .map(i => i * 2)
  .filter(i => i <= 10)
  .take(3)
  .each(i => print(i))

能夠統(tǒng)計(jì)代碼執(zhí)行過(guò)程中 map、filter 傳入的回調(diào)函數(shù)(i => i * 2i => i <= 10)的實(shí)際執(zhí)行次數(shù)。

實(shí)現(xiàn)這個(gè)需求,可以采用粗暴的模式,例如:

var mapCount = 0
var filterCount = 0

Lazy([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
  .map(i => { mapCount++; return i * 2 })
  .filter(i => { filterCount++; return i <= 10 })
  .take(3)
  .each(i => print(i))

console.log("map: " + mapCount)
console.log("filter: " + filterCount)

不過(guò)這樣寫的話我的 demo 頁(yè)面展示的代碼不久太丑了嗎,計(jì)數(shù)的過(guò)程其實(shí)是額外的工作,寫到 demo 代碼里面也影響其他人閱讀不是嗎。

所以,我想實(shí)現(xiàn)不修改 demo 代碼的計(jì)數(shù)。

設(shè)計(jì)

其實(shí)在考慮需求的時(shí)候,就已經(jīng)琢磨過(guò)要實(shí)現(xiàn)的話得采用哪些技術(shù)了。比較自然的想法,就是用“假”的 Lazy 函數(shù)來(lái)替換原有的 Lazy 函數(shù),這樣后續(xù)的調(diào)用過(guò)程就可以進(jìn)行任意的 hack 的了。例如:

function FakeLazy(list) {
  var seq = Lazy(list)
  return {
    map() { /* ... */ },
    filter() { /* ... */ },
    take() { /* ... */ },
    each() { /* ... */ }
  }
}

貌似是可以的,也應(yīng)該是可以的,因?yàn)楹罄m(xù)的調(diào)用實(shí)際上是被“劫持”了,我可以把計(jì)數(shù)的代碼添加到回調(diào)函數(shù)被調(diào)用的時(shí)候執(zhí)行,例如:

map(fn) {
  var subSeq = seq.map(function(e, i){
    mapCount++
    return fn(e, i)
  })
  // ...
}

對(duì)于 filter 也要執(zhí)行類似的處理,而 take、each 則直接調(diào)用原有的 seq 對(duì)象上的方法就好了。

另外,由于每次調(diào)用后都會(huì)產(chǎn)生一個(gè)新的序列對(duì)象(sequence),為了能夠正常鏈接后續(xù)的調(diào)用,還要繼續(xù)返回一個(gè)新的劫持的序列對(duì)象。有點(diǎn)麻煩,不過(guò)也能實(shí)現(xiàn)。

可以看到,這種“劫持”對(duì)象的過(guò)程,比較繁瑣,不僅要劫持到關(guān)心的方法,還得保證對(duì)象其他的方法也能正常調(diào)用。而在 ES6/ES2015 中,有更好的技術(shù)可以采用:Proxy - MDN。

Proxy 這樣使用:

var proxy = new Proxy(target, handler);

這樣可以得到一個(gè)代理對(duì)象 proxy,與前面的“劫持”對(duì)象類似,在程序中直接使用 proxy 來(lái)替代原始的 target 對(duì)象。不過(guò) Proxy 對(duì)象的強(qiáng)大之處在于,對(duì)于該代理對(duì)象的各種“請(qǐng)求”,會(huì)調(diào)用相應(yīng)的 handler 中傳入的回調(diào)函數(shù)。這樣就不需要代理對(duì)象實(shí)現(xiàn)原始對(duì)象的所有功能,只需要處理那些關(guān)心的情況。

對(duì)于前面的情況,使用 Proxy 可以大致這樣處理:

function FakeLazy(list) {
  var seq = Lazy(list)
  return Proxy(seq, {
    get(target, name) {
      if (name === "map" || name === "filter") {
        // 執(zhí)行處理...
      } else {
        return target[name] // 不需要處理的情況直接返回原始對(duì)象的屬性或方法
      }
    }
  })
}

返回的代理對(duì)象在被訪問(wèn)任何屬性或方法時(shí),都會(huì)被攔截,首先調(diào)用 handler 中的 get() 方法,這樣除了要特殊處理的 map 和 filter,其他的直接返回原有屬性或方法。

實(shí)現(xiàn)

思路有了,然后就是具體的實(shí)現(xiàn)工作了。

首先看下頁(yè)面處理邏輯,每個(gè) demo 代碼塊我都包裝在一個(gè)函數(shù)中的,然后將執(zhí)行代碼、執(zhí)行結(jié)果、回調(diào)計(jì)數(shù)結(jié)果分別輸出到頁(yè)面上,也就是前面圖中的那樣。

基本過(guò)程為:

var demos = [(Lazy, print) => {
Lazy([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
  .map(i => i * 2)
  .filter(i => i <= 10)
  .take(3)
  .each(i => print(i))
}, (Lazy, print) => {
  // ...
}/*, ...*/]

demos.forEach(demoFn => {
  var el = document.createElement("div")

  var soure // 獲取執(zhí)行代碼...
  var result // 獲取執(zhí)行結(jié)果...
  var count // 獲取回調(diào)計(jì)數(shù)結(jié)果...

  el.innerHTML = (
    renderSource(soure) +
    renderResult(result) +
    renderCount(count)
  )

  document.body.appendChild(el)
})

看到這里,已經(jīng)不耐煩的同學(xué)可以直接去 demo 頁(yè)面上扒代碼來(lái)看了,相較于我枯燥的描述,代碼可能看起來(lái)更簡(jiǎn)單些。

(1)獲取執(zhí)行代碼

通過(guò) demoFn.toString() 就可以了,不過(guò)需要額外去除函數(shù)定義的頭尾部分,只在頁(yè)面展示執(zhí)行代碼。

(2)獲取執(zhí)行結(jié)果

通過(guò)傳入的 print() 來(lái)收集執(zhí)行結(jié)果,也不復(fù)雜:

var output = []
var print = msg => output.push(msg)

然后將 print 函數(shù)傳入 demoFn 函數(shù),這樣代碼執(zhí)行后輸出的結(jié)果會(huì)被收集到 output 中,然后渲染到頁(yè)面就可以了。

(3)獲取回調(diào)計(jì)數(shù)結(jié)果

這個(gè)是比較復(fù)雜的部分,對(duì)應(yīng)的實(shí)現(xiàn)思路就是前面講的了。不過(guò)由于 Lazy.js 中每次方法調(diào)用返回的是新的序列對(duì)象,要多次生成代理,所以我將生成代理序列對(duì)象的代碼多帶帶抽出:

// 計(jì)數(shù)對(duì)象
var count = {map: 0, filter: 0}

function proxySeq(seq) {
  var handler = {
    get(target, name) {
      // 特別處理 `map` 和 `filter`
      if (name === "map" || name === "filter") {
        // 返回一個(gè)可以實(shí)現(xiàn)計(jì)數(shù)的函數(shù)
        return fn => {
          // 這個(gè) fn 是返回的函數(shù)被調(diào)用時(shí)傳入的回調(diào)函數(shù),把這個(gè)回調(diào)
          // 函數(shù)包裝一下再傳給原始序列對(duì)象的 map 或 filter 方法,
          // 從而實(shí)現(xiàn)調(diào)用計(jì)數(shù)
          var _fn = (v, i) => {
            count[name]++ // 計(jì)數(shù)
            return fn(v, i) // 調(diào)用回調(diào)函數(shù)
          }
          // 仍舊返回一個(gè)新的代理對(duì)象
          return proxySeq(target[name](_fn))
        }
      } else {
        return target[name]
      }
    }
  }
  return new Proxy(seq, handler)
}

通過(guò) proxySeq() 來(lái)實(shí)現(xiàn)一個(gè) FakeLazy:

var _lazy = list => proxySeq(Lazy(list))

和前面的 print 函數(shù)一起作為參數(shù)來(lái)調(diào)用 demoFn 函數(shù)從而執(zhí)行代碼:

demoFn(_lazy, print)

執(zhí)行過(guò)程中可以收集執(zhí)行結(jié)果和回調(diào)函數(shù)執(zhí)行次數(shù),這是借助一個(gè)個(gè)代理對(duì)象來(lái)“劫持” map、filter 實(shí)現(xiàn)的。

渲染的過(guò)程的就是字符串拼接了,不再贅述。

小結(jié)

代碼勝過(guò)萬(wàn)語(yǔ)千言,感興趣的同學(xué)可以去讀一下 demo 頁(yè)面的源碼。

最后感嘆一下,Lazy.js 的實(shí)現(xiàn)還是蠻有意思的,之后我會(huì)結(jié)合 simply-lazy 分享下惰性求值的實(shí)現(xiàn)原理。對(duì)了,demo 頁(yè)面使用的其實(shí)是 simply-lazy,而非 Lazy.js。

其實(shí)無(wú)論是 simply-lazy 還是這里 demo 頁(yè)面的實(shí)現(xiàn),都有一些不足,例如 demo 頁(yè)面中其實(shí)沒(méi)有處理 take(),這樣后續(xù)如果再調(diào)用 map 或 filter,就無(wú)法計(jì)數(shù)。不過(guò)這些相對(duì)于我要介紹的東西而言,不是那么重要,咱們且得魚忘筌吧。^_^

最后的最后,感謝閱讀!

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/91247.html

相關(guān)文章

  • Webpack 熱更新機(jī)制

    摘要:聯(lián)想到我在微信小程序上的開發(fā)體驗(yàn),真心覺(jué)得如果有熱更新機(jī)制的話,開發(fā)效率要高很多。熱更新示例下面通過(guò)例子來(lái)進(jìn)一步解釋熱更新機(jī)制。 想必作為前端大佬的你,工作中應(yīng)該用過(guò) webpack,并且對(duì)熱更新的特性也有了解。如果沒(méi)有,當(dāng)然也沒(méi)關(guān)系。 下面我要講的,是我對(duì) Webpack 熱更新機(jī)制的一些認(rèn)識(shí)和理解,不足之處,歡迎指正。 首先: 熱更新是啥? 熱更新,是指 Hot Module Re...

    mikasa 評(píng)論0 收藏0
  • 異步

    摘要:在異步機(jī)制中,任務(wù)隊(duì)列就是用來(lái)維護(hù)異步任務(wù)回調(diào)函數(shù)的隊(duì)列。四對(duì)象對(duì)象是工作組提出的一種規(guī)范,目的是為異步編程提供統(tǒng)一接口。 異步 1.JavaScript單線程的理解 Javascript語(yǔ)言的執(zhí)行環(huán)境是單線程(single thread)。所謂單線程,就是指一次只能完成一件任務(wù)。如果有多個(gè)任務(wù),就必須排隊(duì),前面一個(gè)任務(wù)完成,再執(zhí)行后面一個(gè)任務(wù),以此類推。 2.JavaScript單線...

    goji 評(píng)論0 收藏0
  • React Hooks入門: 基礎(chǔ)

    摘要:當(dāng)組件安裝和更新時(shí),回調(diào)函數(shù)都會(huì)被調(diào)用。好在為我們提供了第二個(gè)參數(shù),如果第二個(gè)參數(shù)傳入一個(gè)數(shù)組,僅當(dāng)重新渲染時(shí)數(shù)組中的值發(fā)生改變時(shí),中的回調(diào)函數(shù)才會(huì)執(zhí)行。 前言   首先歡迎大家關(guān)注我的Github博客,也算是對(duì)我的一點(diǎn)鼓勵(lì),畢竟寫東西沒(méi)法獲得變現(xiàn),能堅(jiān)持下去也是靠的是自己的熱情和大家的鼓勵(lì),希望大家多多關(guān)注呀!React 16.8中新增了Hooks特性,并且在React官方文檔中新增...

    mrli2016 評(píng)論0 收藏0
  • Python 弱引用 學(xué)習(xí)

    摘要:引用計(jì)數(shù)會(huì)記錄給定對(duì)象的引用個(gè)數(shù),并在引用個(gè)數(shù)為零時(shí)收集該對(duì)象。在對(duì)象群組內(nèi)部使用弱引用即不會(huì)在引用計(jì)數(shù)中被計(jì)數(shù)的引用有時(shí)能避免出現(xiàn)引用環(huán),因此弱引用可用于解決循環(huán)引用的問(wèn)題。 參考 1.weakref – Garbage-collectable references to objects2.Python弱引用介紹 和許多其它的高級(jí)語(yǔ)言一樣,Python使用了垃圾回收器來(lái)自動(dòng)銷毀那些不...

    philadelphia 評(píng)論0 收藏0
  • [面試專題]一線互聯(lián)網(wǎng)大廠面試總結(jié)

    摘要:道阻且長(zhǎng)啊前端面試總結(jié)前端面試筆試面試騰訊一面瀏覽器工作原理瀏覽器的主要組件包括用戶界面包括地址欄后退前進(jìn)按鈕書簽?zāi)夸洖g覽器引擎用來(lái)查詢及操作渲染引擎的接口渲染引擎渲染界面和是基于兩種渲染引擎構(gòu)建的,使用自主研發(fā)的渲染引擎,和都使用網(wǎng)絡(luò)用來(lái) 道阻且長(zhǎng)啊TAT(前端面試總結(jié)) 前端 面試 筆試 面試 騰訊一面 1.瀏覽器工作原理 瀏覽器的主要組件包括: 用戶界面- 包括地址欄、后退/前...

    lemanli 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

閱讀需要支付1元查看
<