日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網(wǎng)為廣大站長提供免費收錄網(wǎng)站服務,提交前請做好本站友鏈:【 網(wǎng)站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(50元/站),

點擊這里在線咨詢客服
新站提交
  • 網(wǎng)站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會員:747

函數(shù)重載

在其他語言中,我們一般都聽說過重載的概念,對于一個方法,如果我們期望傳入不同的參數(shù)類型,或者傳入不同的參數(shù)個數(shù),甚至傳入?yún)?shù)的順序不一樣,而去執(zhí)行不同的處理代碼,以返回相應的結果,那么他們的方法名是可以相同的,而且它們不會產(chǎn)生沖突,這就是所謂的重載。

這是因為像JAVA等這樣的強類型語言,他們具有類型約束和方法簽名,這樣就使得他們能夠根據(jù)調(diào)用時傳入?yún)?shù)的情況來決定使用哪一個同名方法來處理需要的操作。

是在說我嗎?

但是js屬于弱類型語言,也就是說它不會規(guī)定參數(shù)必須傳入哪種類型,定義的方法也不具有簽名的功能,而且在定義多個同名方法時,類似于css里面的層疊樣式表的效果,后定義的同名方法會覆蓋前面的方法,還有一個就是函數(shù)具有提升的特性,這也就使得它無法實現(xiàn)重載的功能。

怎么辦呢?

其實js中也確實不需要重載的功能,因為沒有類型的約束,在一個方法里面就可以做很多自由的發(fā)揮,不但能滿足需要重載的需求,而且還能玩一些花樣。

不過話又說回來,沒有了約束,就容易犯錯,都用一個方法體來處理所有情況,就會容易出亂子,使得我們需要使用一堆的類似if-else的語句來做到這一點。

那么我們能不能在現(xiàn)有js運行方式的基礎上,借鑒其他語言對于重載的運用,來繞個彎子變量來實現(xiàn)一下屬于js自己的重載方式呢?

試一試

我們今天只討論在js中變相實現(xiàn)重載的方式,而不討論它的意義與實際應用場景,我們通過一個簡單的例子,來支撐這個討論,其他的交由你們來自由發(fā)揮。

讓我們開始吧

js重載的方式

在js中主要有以下幾種實現(xiàn)重載的方式:

  1. 使用剩余參數(shù)的形式來接收可變的參數(shù),并通過一些判斷手段來處理不同情況的邏輯。
  2. 使用arguments的形式,來動態(tài)判斷需要執(zhí)行的操作
  3. 使用proxy的形式來攔截函數(shù)的行為,以達到控制不同的邏輯分支。

前兩種方式比較相似,思路一樣,只是實現(xiàn)手段有所不同,用戶需要自己寫判斷邏輯。第三種方式結合proxy來隱藏實現(xiàn)細節(jié),讓用戶只關注各自的分工。但它們都是在參數(shù)動態(tài)判斷方面做文章。

前兩種方式的優(yōu)缺點:

優(yōu)點:可以直接定義函數(shù)或使用表達式

缺點:函數(shù)名不能相同,需要寫較多的判斷分支

第三種方式的優(yōu)缺點:

優(yōu)點:可以不用寫各種參數(shù)形式的分支

缺點:只能使用表達式定義函數(shù)

由于前兩種網(wǎng)上已經(jīng)有很多的實現(xiàn)思路與方案,因此這里不再進行探討,其中有很多奇妙的實現(xiàn),可以做到在js中使用重載的思想。

所以在此我們只討論第三種方案,我們下面來看一下它的思路是什么,是否滿足重載的需求,它是如何實現(xiàn)的,以及它能滿足我們什么樣的需求?

這是什么呢?

需求假設

我們現(xiàn)在有這樣一個場景和需求:

自己開了一家服裝店,由于生意火爆,我們想要答謝新老顧客,現(xiàn)在推出了一個活動,全場無論任何服裝,只要買一件就直接打95折,只要買兩件就全部打9折,只要買三件就全部打85折,只要買四件及以上,就全部打8折。

如果用代碼來實現(xiàn),其實就是給方法中傳入一個兩個三個四個參數(shù)的問題,因此我們自然而然的就想到了使用重載來實現(xiàn)這個需求。

接下來我們就試著自己實現(xiàn)一個這樣的功能,看看可不可以創(chuàng)建一個賦能方法來使某個業(yè)務處理函數(shù)具有重載的能力。

思路分析

要生成這樣一個賦能方法,我們需要有對函數(shù)改造的能力,在創(chuàng)建業(yè)務處理函數(shù)的時候,最好能夠改變它的默認行為,在執(zhí)行的時候也能夠對它進行攔截以做一些額外的操作。

那么我們很自然而然的就想到了使用Proxy,先生成一個Proxy函數(shù),然后在給它設置屬性的時候,我們進行攔截,把賦值操作中的value緩存起來,以備將來調(diào)用的時候做分支處理,根據(jù)參數(shù)的個數(shù)與類型來控制需要執(zhí)行的業(yè)務邏輯代碼。它真的能做到嗎?我們來看一下下面的一步步代碼實現(xiàn)。

實現(xiàn)需求

function Overload(defaultCall) {  let func = defaultCall || new Function()  func.overloadCached = []  return new Proxy(func, {    set(target, prop, value) {      if(prop === 'load') {        target.overloadCached.push(value)      }    },    Apply(target, thisArg, argumentsList) {      for(let i = target.overloadCached.length - 1; i > -1; i--) {        if(argumentsList.length === target.overloadCached[i].length || (argumentsList.length > target.overloadCached[i].length)) {          return target.overloadCached[i].apply(thisArg, argumentsList)        }      }      return target.apply(thisArg, argumentsList)    }  })}let sum = Overload()sum.load = function (a) {  return a * 0.95;}sum.load = function (a, b) {  return (a + b) * 0.9;}sum.load = function (a, b, c) {  return (a + b + c) * 0.85;}sum.load = function (a, b, c, d, ...arg) {  return (arg.concat(a,b,c,d).reduce((total, cur) => {return total + cur},0)) * 0.8;}console.log(sum(200));console.log(sum(200, 300));console.log(sum(180, 280, 190));console.log(sum(270, 260, 310, 240));console.log(sum(180, 220, 240, 210, 190));//輸出:190,450,552.5,864,832

可以看到,我們實現(xiàn)了一個Overload函數(shù),用來返回一個Proxy,通過它去load不同的方法來實現(xiàn)對同名方法的重載,調(diào)用的時候只需要一個方法名即可,Proxy中我們對set(即設置該Proxy的值的操作)和apply(即執(zhí)行該Proxy的操作)兩種操作進行了攔截,用到了一個叫做overloadCached的屬性來緩存我們的處理函數(shù),在調(diào)用函數(shù)的時候,我們使用從后往前遍歷的方式,來達到后定義優(yōu)先生效的原則。

通過打印結果我們知道,它已經(jīng)滿足了我們的需求假設。

默認處理

從上面的代碼中我們發(fā)現(xiàn),Overload函數(shù)可以傳入一個叫做defaultCall的參數(shù),它是用來處理默認操作的,也就是說如果后面定義的所有方法都不能夠處理的時候,將使用該默認函數(shù)進行處理,如果沒有定義該函數(shù),那么調(diào)用sum時如果沒有滿足的執(zhí)行函數(shù),將會返回undefined。

現(xiàn)在我們給它傳入一個默認的處理函數(shù),那么上面的需求將可以寫成這樣:

function Overload(defaultCall) {  let func = defaultCall || new Function()  func.overloadCached = []  return new Proxy(func, {    set(target, prop, value) {      if(prop === 'load') {        target.overloadCached.push(value)      }    },    apply(target, thisArg, argumentsList) {      for(let i = target.overloadCached.length - 1; i > -1; i--) {        //注意這里的變化        if(argumentsList.length === target.overloadCached[i].length) {          return target.overloadCached[i].apply(thisArg, argumentsList)        }      }      return target.apply(thisArg, argumentsList)    }  })}let sum = Overload(function () {  return ([].__proto__.reduce.call(arguments, (total, cur) => {return total + cur},0)) * 0.8;})sum.load = function (a) {  return a * 0.95;}sum.load = function (a, b) {  return (a + b) * 0.9;}sum.load = function (a, b, c) {  return (a + b + c) * 0.85;}console.log(sum(200));console.log(sum(200, 300));console.log(sum(180, 280, 190));console.log(sum(270, 260, 310, 240));console.log(sum(180, 220, 240, 210, 190));//輸出:190,450,552.5,864,832

我們注意Overload函數(shù)的變化,現(xiàn)在依然滿足上面的需求。

處理兼容

由于我們把四個參數(shù)即以上的處理函數(shù)改為通過傳入默認函數(shù)的方式來實現(xiàn),因此我們修改了Overload方法,這顯然是不合理的,因為這樣不設置默認函數(shù)的時候會出問題,因此我們做一個兼容處理,修改之后就變成了這樣:

function Overload(defaultCall) {  let func = defaultCall || new Function()  func.overloadCached = []  return new Proxy(func, {    set(target, prop, value) {      if(prop === 'load') {        let str = value.toString()        let m1 = str.match(/(.+?)/)        if(m1 && m1[0].indexOf("...") != -1) {          value.rest = true        }        target.overloadCached.push(value)      }    },    apply(target, thisArg, argumentsList) {      for(let i = target.overloadCached.length - 1; i > -1; i--) {        if((argumentsList.length === target.overloadCached[i].length) || (target.overloadCached[i].rest && argumentsList.length > target.overloadCached[i].length)) {          return target.overloadCached[i].apply(thisArg, argumentsList)        }      }      return target.apply(thisArg, argumentsList)    }  })}//輸出:190,450,552.5,864,832

現(xiàn)在使用這個Overload函數(shù)就已經(jīng)能夠處理上面的這兩種情況了。我們設定了一個rest屬性來給方法打上了一個標識。

需求延伸

如果我們現(xiàn)在在上面的需求基礎上,又想要對金額做一些處理,比如希望能夠加上$、¥、€等前綴,來區(qū)分不同的幣種。

這個時候我們需要增加新的重載函數(shù),而加了幣種之后的函數(shù)可能與現(xiàn)有的函數(shù)參數(shù)個數(shù)相同(比如sum('$', 220, 240)和sum(270, 260, 310)),這就造成了誤判,那么我們能不能再做一個類型區(qū)分呢?

應該是可以的,但是我們必須約定一種格式,比如下面這種形式,我們需要在獲取Proxy屬性的時候(這里就用到了攔截獲取Proxy屬性的操作),將類型進行緩存,以便將來時候的時候來做類型的判斷:

//我們約定了10種類型//n→number//s→string//b→boolean//o→object//a→array//d→date//S→Symbol//r→regexp//B→bigint//f→functionfunction Overload(defaultCall) {  let func = defaultCall || new Function()  func.overloadCached = []  func.modifier = []  return new Proxy(func, {    get(target, property, receiver) {      if(property !== 'load') {        target.modifier.push(property)      }      return receiver    },    set(target, prop, value) {      if(['n','s','b','o','a','d','S','r','B','f'].includes(prop)) {        target.modifier.push(prop)      }      if(prop === 'load' || target.modifier.length !== 0) {        let str = value.toString()        let m1 = str.match(/(.+?)/)        if(m1 && m1[0].indexOf("...") != -1) {          value.rest = true        }        value.modifier = target.modifier        target.overloadCached.push(value)        target.modifier = []      }    },    apply(target, thisArg, argumentsList) {      for(let i = target.overloadCached.length - 1; i > -1; i--) {        if((argumentsList.length === target.overloadCached[i].length) || (target.overloadCached[i].rest && argumentsList.length > target.overloadCached[i].length)) {          if(target.overloadCached[i].modifier.length !== 0){            let ty = {              '[object Number]': ['n'],              '[object String]': ['s'],              '[object Boolean]': ['b'],              '[object Object]': ['o'],              '[object Array]': ['a'],              '[object Date]': ['d'],              '[object Symbol]': ['S'],              '[object Regexp]': ['r'],              '[object BigInt]': ['B'],              '[object Function]': ['f'],            }            if(target.overloadCached[i].modifier.some((m, j) => {              return !ty[({}).__proto__.toString.call(argumentsList[j])].includes(m)            })) {              continue            }          }          return target.overloadCached[i].apply(thisArg, argumentsList)        }      }      return target.apply(thisArg, argumentsList)    }  })}let sum = Overload()sum.load.n = function (a) {  return a * 0.95;}sum.load.n.n = function (a, b) {  return (a + b) * 0.9;}sum.load.n.n.n = function (a, b, c) {  return (a + b + c) * 0.85;}sum.load.s.n.n = function (a, b, c) {  return a + (b + c) * 0.85;}sum.load.n.n.n.n = function (a, b, c, d, ...arg) {  return (arg.concat(a,b,c,d).reduce((total, cur) => {return total + cur},0)) * 0.8;}sum.load.s.n.n.n = function (a, b, c, d, ...arg) {  return a + (arg.concat(b,c,d).reduce((total, cur) => {return total + cur},0)) * 0.8;}console.log(sum(200));console.log(sum(200, 300));console.log(sum(260, 310, 240));console.log(sum('€', 280, 190));console.log(sum(180, 220, 240, 210, 190));console.log(sum('$', 220, 240, 210, 190));//輸出:190,450,688.5,€399.5,832,$688

我們現(xiàn)在已經(jīng)加上了類型判斷,通過傳入的參數(shù)類型與個數(shù)的不同,能夠相應的去執(zhí)行對應的函數(shù),其實參數(shù)的順序一個道理,也是支持的。

類型擴展

上面的類型約定我們可能看起來怪怪的,而且比較難以理解,因此我們可以擴展一下類型約定的表示方式,改造后的Overload函數(shù)如下:

function Overload(defaultCall) {  let func = defaultCall || new Function()  func.overloadCached = []  func.modifier = []  return new Proxy(func, {    get(target, property, receiver) {      if(property !== 'load') {        if(property.indexOf(',') !== -1) {          property.split(',').map(item => {            target.modifier.push(item)          })        }else{          property.split('').map(item => {            target.modifier.push(item)          })        }      }      return receiver    },    set(target, prop, value) {      let modi = null      if(prop.indexOf(',') !== -1) {        modi = prop.split(',')      }else{        modi = prop.split('')      }      if(modi.every(p => {        return ['n','s','b','o','a','d','S','r','B','f','number','string','boolean','object','array','date','Symbol','regexp','bigint','function'].includes(p)      })) {        modi.map(item => {          target.modifier.push(item)        })      }      if(prop === 'load' || target.modifier.length !== 0) {        let str = value.toString()        let m1 = str.match(/(.+?)/)        if(m1 && m1[0].indexOf("...") != -1) {          value.rest = true        }        value.modifier = target.modifier        target.overloadCached.push(value)        target.modifier = []      }    },    apply(target, thisArg, argumentsList) {      for(let i = target.overloadCached.length - 1; i > -1; i--) {        if((argumentsList.length === target.overloadCached[i].length) || (target.overloadCached[i].rest && argumentsList.length > target.overloadCached[i].length)) {          if(target.overloadCached[i].modifier.length !== 0){            let ty = {              '[object Number]': ['n','number'],              '[object String]': ['s','string'],              '[object Boolean]': ['b','boolean'],              '[object Object]': ['o','object'],              '[object Array]': ['a','array'],              '[object Date]': ['d','date'],              '[object Symbol]': ['S','Symbol'],              '[object Regexp]': ['r','regexp'],              '[object BigInt]': ['B','bigint'],              '[object Function]': ['f','function'],            }            if(target.overloadCached[i].modifier.some((m, j) => {              return !ty[({}).__proto__.toString.call(argumentsList[j])].includes(m)            })) {              continue            }          }          return target.overloadCached[i].apply(thisArg, argumentsList)        }      }      return target.apply(thisArg, argumentsList)    }  })}

這樣我們就可以支持一下幾種類型約定的書寫形式:

sum.load.s.n.n = function (a, b, c) {  return a + (b + c) * 0.85;}sum.load['snn'] = function (a, b, c) {  return a + (b + c) * 0.85;}sum.load.snn = function (a, b, c) {  return a + (b + c) * 0.85;}//對于全稱不能夠寫成.(點)的形式sum.load['string,number,number'] = function (a, b, c) {  return a + (b + c) * 0.85;}//這四種形式的任意一種對于console.log(sum('$', 280, 190));//都會輸出:$399.5

到此為止,我們已經(jīng)能夠支持參數(shù)的個數(shù)、類型、順序的不同,會執(zhí)行不同的處理函數(shù),滿足了重載的基本需求,完成了我們在最開始的需求假設的實現(xiàn)。

結語

目前這種方式只能支持函數(shù)表達式的方式來進行重載,這里只是給大家提供一個自定義實現(xiàn)重載的方式,結合自己的業(yè)務場景,小伙伴們可以自由發(fā)揮,其實目前js的既有方式能滿足我們需要重載的場景,而不需要額外設計重載的代碼。

具體這種方式的優(yōu)劣,大家可以自行判斷,并且可以根據(jù)這種思路重新設計一下實現(xiàn)的手段。

分享到:
標簽:JS
用戶無頭像

網(wǎng)友整理

注冊時間:

網(wǎng)站:5 個   小程序:0 個  文章:12 篇

  • 51998

    網(wǎng)站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會員

趕快注冊賬號,推廣您的網(wǎng)站吧!
最新入駐小程序

數(shù)獨大挑戰(zhàn)2018-06-03

數(shù)獨一種數(shù)學游戲,玩家需要根據(jù)9

答題星2018-06-03

您可以通過答題星輕松地創(chuàng)建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學四六

運動步數(shù)有氧達人2018-06-03

記錄運動步數(shù),積累氧氣值。還可偷

每日養(yǎng)生app2018-06-03

每日養(yǎng)生,天天健康

體育訓練成績評定2018-06-03

通用課目體育訓練成績評定