自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

開發(fā)中經(jīng)常遇到的JavaScript問題整理(超實(shí)用)

開發(fā) 前端
今天遇到一個需求,已知月份,得到這個月的第一天和最后一天作為查詢條件查范圍內(nèi)的數(shù)據(jù)

 [[385332]]

獲取一個月有多少天

今天遇到一個需求,已知月份,得到這個月的第一天和最后一天作為查詢條件查范圍內(nèi)的數(shù)據(jù)

new Date(year, month, date, hrs, min, sec),new Date 可以接受這些參數(shù)創(chuàng)建一個時間對象 其中當(dāng)我們把 date 設(shè)置為 0 的時候,可以直接通過 getDate() 獲取到最后一天的日期然后得到我們要的最后一天

 

  1. new Date(2019, 12, 0).getDate(); // 31 
  2. new Date(2018, 2, 0).getDate(); // 28 
  3. // 根據(jù)這個我們可以得到一個方法 
  4. function getMonthLength(month) { 
  5.   const date = new Date(month); 
  6.   const year = date.getFullYear(); 
  7.   // 月份是從 0 開始計(jì)算的 
  8.   const _month = date.getMonth() + 1; 
  9.   return new Date(year, _month, 0).getDate(); 

關(guān)于函數(shù)的 length 屬性

360 面試過程遇到一個很有趣的問題,是關(guān)于函數(shù)的 length 屬性的,題簡寫如下

 

  1. (() => 1).length === 0; // 輸出什么 

我所理解的擁有 length 的對象一般都是數(shù)組或者類數(shù)組對象,或者定義了 length 屬性的對象,所以我回答說這個應(yīng)該是 false 吧,后來面試告訴我函數(shù)是有 length 屬性的,函數(shù)的 length 屬性就是函數(shù)參數(shù)的個數(shù),瞬間我恍然大悟,函數(shù)的參數(shù)就是 arguments,而 arguments 也是一個類數(shù)組對象所以他是有 length 屬性的

 

  1. // so 
  2. (() => 1).length === 0; // 輸出 true 
  3. (a => a).length; // 輸出 1 

數(shù)組中字符串鍵值的處理

在 JavaScript 中數(shù)組是通過數(shù)字進(jìn)行索引,但是有趣的是他們也是對象,所以也可以包含 字符串 鍵值和屬性,但是這些不會被計(jì)算在數(shù)組的長度(length)內(nèi)

如果字符串鍵值能夠被強(qiáng)制類型轉(zhuǎn)換為十進(jìn)制數(shù)字的話,它就會被當(dāng)做數(shù)字索引來處理

 

  1. const arr = []; 
  2. arr[0] = 1; 
  3. arr['1'] = '嘿嘿'
  4. arr['cym'] = 'cym'
  5. console.log(arr); // [1, '嘿嘿', cym: 'cym'
  6. console.log(arr.length); // 2 

void 運(yùn)算符

undefined 是一個內(nèi)置標(biāo)志符,它的值為 undefined(除非被重新定義過),通過 void 運(yùn)算符即可得到該值

在 void 之后的語句或表達(dá)式都將返回 undefined。void 并不會改變表達(dá)式的結(jié)果,只是讓表達(dá)式不返回值

 

  1. void true; // undefined 
  2. void 0; // undefined 

void 運(yùn)算符在其他地方也可以派上用場,比如不讓表達(dá)式返回任何結(jié)果。

 

  1. // 該函數(shù)不需要有任何返回結(jié)果 
  2. function doSomething(sign) { 
  3.   if (!sign) { 
  4.     return void setTimeout(doSomething, 100); 
  5.   } 
  6. // 或許你經(jīng)常向下面一樣這么寫 
  7. function doSomething(sign) { 
  8.   if (!sign) { 
  9.     setTimeout(doSomething, 100); 
  10.     return
  11.   } 

關(guān)于 JSON.stringify

JSON.stringify 和 toString() 效果基本相同,只不過序列化的結(jié)果總是字符串

  1. JSON.stringify(42); // "42" 
  2. JSON.stringify('42'); // ""42""(含有雙引號的字符串) 
  3. JSON.stringify(null); // "null" 
  4. JSON.stringify(true); // "true" 

不安全的 JSON 值

所有安全的 JSON 值都可以使用 JSON.stringify 序列化,不安全的 JSON 值有:undefined、function、symbol 和 循環(huán)引用。JSON.stringify

在對象中遇到這些不安全的 JSON 值的時候會自動將其忽略,在數(shù)組中遇到則會返回 null,以保證數(shù)組成員位置不變

 

  1. JSON.stringify(undefined); // null 
  2. JSON.stringify(function () {}); // null 
  3. JSON.stringify([1, undefined, 2, function () {}, 3]); // "1, null, 2, null, 3" 
  4. JSON.stringify({ a: 2, b: function () {} }); // "{"a":2}" 

toJSON 方法

如果對象中定義了 toJSON 方法,那么在 JSON 序列化的時候優(yōu)先調(diào)用該方法,主要是為了處理循環(huán)引用的時候,我們讓其返回一個合理的值

也就是說 toJSON 方法應(yīng)該返回一個能夠被字符串安全化的 JSON 值

 

  1. const o = { 
  2.   a: 'cym'
  3.   toJSON() { 
  4.     return { c: 'b' }; 
  5.   }, 
  6. }; 
  7.  
  8. JSON.stringify(o); // {"c":"b"

JSON.stringify 的第二個參數(shù)

我們可以向 JSON.stringify 中傳遞一個可選參數(shù) replacer,他可以書數(shù)組也可以書函數(shù),用來指定對象序列化的時候哪些屬性應(yīng)該被處理,哪些應(yīng)該被排除,和 toJSON 很像

1.當(dāng) replacer 是一個數(shù)組時,那么他必須是一個字符串?dāng)?shù)組,其中包含序列化要處理的對象的屬性名稱,除此之外的屬性就會被忽略

 

  1. const obj = { 
  2.   a: 42, 
  3.   b: 30, 
  4.   c: 100, 
  5. }; 
  6. JSON.stringify(obj, ['a''c']); // {"a":42,"c":100} 

2.當(dāng) replacer 是一個函數(shù)時,他會對對象本身調(diào)用一次,然后在對對象中的每個屬性各調(diào)用一次。每次傳遞兩個參數(shù)(對象的鍵和值)。如果要忽略某個鍵就返回 2.undecided,否則就返回指定的值

 

  1. const obj = { 
  2.   a: 42, 
  3.   b: 30, 
  4.   c: 100, 
  5. }; 
  6. JSON.stringify(obj, (k, v) => { 
  7.   // 注意:第一次 k 是 undefined,v 是原對象 
  8.   if (k !== 'c'return v; 
  9. }); // "{"a":42,"b":30}" 

一元運(yùn)算符

我們都知道一個字符串轉(zhuǎn)換為數(shù)字,可以使用 + "12" 轉(zhuǎn)換為數(shù)字 12,也可以使用 -,這樣的 +、- 是一元運(yùn)算符,這樣將數(shù)字轉(zhuǎn)換為字符串的方法屬于顯示轉(zhuǎn)換

- 運(yùn)算符還有反轉(zhuǎn)符號位的功能,當(dāng)然不能把一元操作符連在一起寫,不然會變成 --,當(dāng)做遞減運(yùn)算符號來計(jì)算了,我們可以理解為 - 運(yùn)算符出在單數(shù)次數(shù)會轉(zhuǎn)符號位,出現(xiàn)雙次數(shù)會抵消反轉(zhuǎn),比如說 1 - - 1 === 2

 

  1. # 這是 js 代碼哦,不是 python 
  2. 1 + - + - + - 1   # 0 
  3. 1 - - 1           # 2 
  4. 1 - - - 1         # 0 

字位反轉(zhuǎn)操作符

~~ 返回 2 的補(bǔ)碼,~x 大致等同于 -(x+1)

 

  1. ~42; // -(42+1) ===> -43 

在 -(x+1) 中唯一能夠得到 0(或者嚴(yán)格來說時候 -0)的 x 值是 -1,也就是說 ~ 和一些數(shù)字在一起會返回一個假值 0,其他情況下則返回真值

-1 是一個 哨位值,哨位值是那些在各個類型中被賦予了特殊含義的值。在 C 語言中 -1 代表函數(shù)執(zhí)行失敗,大于等于 0 的值代表函數(shù)執(zhí)行成功

比如在 JavaScript 中字符串的 indexOf 方法也遵循這一慣例,該方法在字符串中搜索指定的字符串,如果找到就返回該子字符串所在的位置,否則返回 -1

~ 的用途

我們知道在 JavaScript 中假值有:undefined、null、false、+0、-0、NaN、'',其他都為真值,所以負(fù)數(shù)也是真值,那么我們就可以拿著 ~ 和 indexOf 一起檢結(jié)果強(qiáng)制類型轉(zhuǎn)換為 真/假 值

 

  1. const str = 'hello world'
  2. ~str.indexOf('lo'); // -4,真值 
  3. if (~str.indexOf('lo')) { 
  4.   // true 
  5.   // 找到匹配 
  6. ~str.indexOf('ol'); // 0,假值 
  7. !~str.indexOf('ol'); // true 
  8. if (!~str.indexOf('ol')) { 
  9.   // true 
  10.   // 沒有找到匹配 

~ 要比 >=0 和 == -1 更簡潔

字位截除

我們經(jīng)常使用 ~~ 來截取數(shù)字值的小數(shù)部分,以為這是和 Math.floor 效果是一樣的,實(shí)際上并非如此

~~ 中第一個 ~ 執(zhí)行 ToInt32 并反轉(zhuǎn)字位,然后第二個在進(jìn)行一次字位反轉(zhuǎn),就是將所有的字位反轉(zhuǎn)回原值,最后得到的結(jié)果仍是 ToInt32 的結(jié)果

~~ 只適用于 32 位的數(shù)字,更重要的是他對負(fù)數(shù)的處理與 Math.floor 不同,所以使用時要多加注意

 

  1. Math.floor(1.9); // 1 
  2. ~~1.9; // 1 
  3. // 操作負(fù)數(shù) 
  4. Math.floor(-1.9); // -2 
  5. ~~-1.9; // -1 

~~x 能將值截除為一個 32 位的整數(shù),x | 0 也可以,而且看起來更簡潔哦,不過出于對運(yùn)算符優(yōu)先級的考慮,我們更傾向于使用 ~~x

 

  1. ~~1.9; // 1 
  2. 1.9 | 0; // 1 
  3.  
  4. ~~-1.9; // -1 
  5. -1.9 | 0; // -1 

給定一組 url 實(shí)現(xiàn)并發(fā)請求

原題是這樣的:給定一組 url,利用 js 的異步實(shí)現(xiàn)并發(fā)請求,并按順序輸出結(jié)果

Promise.all

首先我們可以想到的是利用 Promise.all 來實(shí)現(xiàn),代碼實(shí)現(xiàn)如下

 

  1. const urls = ['./1.json''./2.json''./3.json']; 
  2. function getData(url) { 
  3.   // 返回一個 Promise 利用 Promise.all 接受 
  4.   return new Promise((resolve, reject) => { 
  5.     const xhr = new XMLHttpRequest(); 
  6.     xhr.responseType = 'json'
  7.     xhr.onreadystatechange = () => { 
  8.       if (xhr.readyState === 4) { 
  9.         if (xhr.status === 200) { 
  10.           resolve(xhr.response); 
  11.         } 
  12.       } 
  13.     }; 
  14.     xhr.open('GET', url, true); 
  15.     xhr.send(null); 
  16.   }); 
  17. function getMultiData(urls) { 
  18.   // Promise.all 接受一個包含 promise 的數(shù)組,如果不是 promise 數(shù)組會被轉(zhuǎn)成 promise 
  19.   Promise.all(urls.map(url => getData(url))).then(results => { 
  20.     console.log(results); 
  21.   }); 

不用 Promise

原題是不用 Promise 來實(shí)現(xiàn),我們可以寫一個方法,加個回調(diào)函數(shù),等數(shù)據(jù)全部回來之后,觸發(fā)回調(diào)函數(shù)傳入得到的數(shù)據(jù),那么數(shù)據(jù)全部回來的就是我們要考慮的核心問題,我們可以用個數(shù)組或者對象,然后判斷一下數(shù)組的 length 和傳入的 url 的長度是否一樣來做判斷

使用對象做映射

 

  1. const urls = ['./1.json''./2.json''./3.json']; 
  2. function getAllDate(urls, cd) { 
  3.   const result = {}; 
  4.   function getData(url, idx) { 
  5.     const xhr = new XMLHttpRequest(); 
  6.     xhr.responseType = 'json'
  7.     xhr.onreadystatechange = () => { 
  8.       if (xhr.readyState === 4) { 
  9.         if (xhr.status === 200) { 
  10.           result[idx] = xhr.response; 
  11.           // 如果兩者 length 相等說明都請求完成了 
  12.           if (Object.keys(result).length === urls.length) { 
  13.             // 給對象添加length屬性,方便轉(zhuǎn)換數(shù)組 
  14.             result.length = urls.length; 
  15.             cd && cd(Array.from(result)); 
  16.           } 
  17.         } 
  18.       } 
  19.     }; 
  20.   } 
  21.   // 觸發(fā)函數(shù)執(zhí)行 
  22.   urls.forEach((url, idx) => getData(url, idx)); 
  23. // 使用 
  24. getAllDate(urls, data => { 
  25.   console.log(data); 
  26. }); 

使用數(shù)組實(shí)現(xiàn)

和上面的基本思路差不多,不過這次換成了數(shù)組,也可以給個信號量來做判斷

 

  1. function getGroupData(urls, cb) { 
  2.   const results = []; 
  3.   let count = 0; 
  4.   const getData = url => { 
  5.     const xhr = new XMLHttpRequest(); 
  6.     xhr.responseType = 'json'
  7.     xhr.onreadystatechange = _ => { 
  8.       if (xhr.readyState === 4) { 
  9.         if (xhr.status === 200) { 
  10.           results.push(xhr.response); 
  11.           if (++count === urls.length) { 
  12.             cb && cb(results); 
  13.           } 
  14.         } 
  15.       } 
  16.     }; 
  17.     xhr.open('GET', url, true); 
  18.     xhr.send(null); 
  19.   }; 
  20.   urls.forEach(url => getData(url)); 
  21.  
  22. getGroupData(urls, data => { 
  23.   console.log(data); 
  24. }); 

類型轉(zhuǎn)換問題

原題:如何讓 (a == 1 && a == 2 && a == 3) 的值為 true?

這個問題考查的數(shù)據(jù)類型轉(zhuǎn)換,== 類型轉(zhuǎn)換有個基本規(guī)則

  • NaN 與任何值都不相等,包括自己本身
  • undefined 與 null 相等(==),其他都不等
  • 對象與字符串類型做比較,會把對象轉(zhuǎn)換成字符串然后做比較
  • 其他類型比較都要轉(zhuǎn)換成 數(shù)字 做比較

那么這個問題我們重寫 toString 或者 valueOf 方法就可以了

 

  1. const a = { 
  2.   val: 1, 
  3.   toString() { 
  4.     return this.val++; 
  5.   }, 
  6. }; 
  7. if (a == 1 && a == 2 && a == 3) { 
  8.   console.log('ok'); 

還有一種方法實(shí)現(xiàn)

 

  1. var i = 1; 
  2. Object.defineProperty(window, 'a', { 
  3.   get() { 
  4.     return i++; 
  5.   }, 
  6. }); 
  7.  
  8. if (a == 1 && a == 2 && a == 3) { 
  9.   console.log('OK'); 

拓展一下 [] == ![]為什么是 true

上面隱式類型轉(zhuǎn)換規(guī)則中提到,其他類型比較都要轉(zhuǎn)換成數(shù)字做比較,這個就是對應(yīng)那條規(guī)則的

  • 首先 [].toString() 會得到一個 '' 字符串
  • ![] 得到一個布爾值 false
  • '' 與 false 比較肯定要轉(zhuǎn)換成數(shù)字比較
  • 那么 '' 轉(zhuǎn)換則為 0, false 轉(zhuǎn)換也是 0
  • 所以這道題就是 true

1..toString 的問題

有時候我們看到別人的代碼中會寫到數(shù)字調(diào)其他類型的方法的時候會寫成 1..toString() 這樣的寫法

因?yàn)橹苯佑谜麛?shù)型數(shù)字調(diào)方法就會報(bào)錯,但是如果是一個浮點(diǎn)數(shù)的話就不會報(bào)錯了

因?yàn)榭赡茉?. 上面存在爭議,一個數(shù)字后面加點(diǎn),解釋器他不知道你這是小數(shù)還是要調(diào)取方法,所以就跑異常了

 

  1. 1.toString()     // Uncaught SyntaxError: Invalid or unexpected token 
  2. 1..toString()    // '1' 
  3. 1.2.toString()   // '1.2' 

Generator

對象增加迭代器

類數(shù)組對象的特征:必須有長度、索引、能夠被迭代,否則這個對象不可以使用 ... 語法轉(zhuǎn)數(shù)組,我們可以使用 Array.from 轉(zhuǎn),當(dāng)然我們也可以給對象添加一個迭代器

 

  1. const obj = { 
  2.   0: 1, 
  3.   1: 2, 
  4.   2: 3, 
  5.   3: 4, 
  6.   length: 4, 
  7.   [Symbol.iterator]() { 
  8.     let idx = 0 
  9.     return { 
  10.       next() { 
  11.         return { 
  12.           value: obj[idx], 
  13.           done: idx++ >= obj.length, 
  14.         } 
  15.       } 
  16.     } 
  17.   } 
  18. // 此時對象就被添加了迭代器 
  19. [...obj]  // 1 2 3 4 
  20. for (const val of obj) { 
  21.   console.log(val)  // 1 2 3 4 

上面的問題可以字節(jié)使用生成器來實(shí)現(xiàn),生成器返回一個迭代器,迭代器有 next 方法,調(diào)用 next 方法可以返回 value 和 done

 

  1. const obj = { 
  2.   0: 1, 
  3.   1: 2, 
  4.   2: 3, 
  5.   3: 4, 
  6.   length: 4, 
  7.   [Symbol.iterator]: function* () { 
  8.     let idx = 0 
  9.     while (idx !== this.length) { 
  10.       yield this[idx++] 
  11.     } 
  12.   } 

實(shí)現(xiàn)一個字符串的迭代器

實(shí)現(xiàn)一個字符串的迭代器:傳入一組字符串并返回單個字符的范例。一旦更新的字符串,輸出也跟著替換掉舊的

 

  1. function generator(str) { 
  2.   let idx = 0; 
  3.   return { 
  4.     next() { 
  5.       return { 
  6.         value: str[idx], 
  7.         done: idx++ >= str.length, 
  8.       }; 
  9.     }, 
  10.   }; 
  11. // 測試 
  12. const str = 'as'
  13. let gen = generator(str); 
  14. console.log(gen.next()); 
  15. console.log(gen.next()); 
  16. console.log(gen.next()); 
  17. console.log(gen.next()); 
  18. gen = generator('str'); 
  19. console.log(gen.next()); 
  20. console.log(gen.next()); 
  21. console.log(gen.next()); 
  22. console.log(gen.next()); 
  23. // { value: 'a', done: false } 
  24. // { value: 's', done: false } 
  25. // { value: undefined, done: true } 
  26. // { value: undefined, done: true } 
  27. // { value: 's', done: false } 
  28. // { value: 't', done: false } 
  29. // { value: 'r', done: false } 
  30. // { value: undefined, done: true } 

簡單模擬 co

模擬一下 co 的實(shí)現(xiàn)

首先來看一則例子

 

  1. const fs = require('fs'); 
  2. const path = require('path'); 
  3. const { promisify } = require('util'); 
  4. const readFile = promisify(fs.readFile); 
  5.  
  6. functionread() { 
  7.   const name = yield readFile(path.resolve(__dirname, 'name.txt'), 'utf8'); 
  8.   const age = yield readFile(path.resolve(__dirname, name), 'utf8'); 
  9.   return age; 
  10.  
  11. const it = read(); 
  12.  
  13. let { value, done } = it.next(); 
  14. value.then(data => { 
  15.   let { value, done } = it.next(data); 
  16.   // console.log(data, '???'
  17.   value.then(data => { 
  18.     let { value, done } = it.next(data); 
  19.     console.log(value); 
  20.   }); 
  21. }); 

使用 co 庫可以很容易解決這個問題

 

  1. const co = require('co'); 
  2. // co 接受一個生成器 
  3. co(read()).then(data => { 
  4.   console.log(data); 
  5. }); 
  6. // 那模擬一下 
  7. function _co(it) { 
  8.   // 首先返回一個 promise 
  9.   return new Promise((resolve, reject) => { 
  10.     // 因?yàn)榭梢詡髦档脑?,不可以直接使用循環(huán)實(shí)現(xiàn),需要使用 遞歸 
  11.     function next(data) { 
  12.       const { value, done } = it.next(data); 
  13.       if (done) return resolve(value); 
  14.       // 保證值是一個 promise 
  15.       Promise.resolve(value).then(data => { 
  16.         next(data); 
  17.       }, reject); 
  18.     } 
  19.     next(); 
  20.   }); 

菲波那切數(shù)列

  • 今天新東方的面試還提到了菲波那切數(shù)列,其實(shí)這個東西蠻很有趣,簡單介紹一下
  • 1、1、2、3、5、8、13、21、34 ....
  • 這道題有個規(guī)律,第一項(xiàng)加上第二項(xiàng)永遠(yuǎn)等于第三項(xiàng):1 + 1 = 2;1 + 2 = 3;2 + 3 = 5;3 + 5 = 8 ....
  • 要求是傳入第幾項(xiàng),得到該值,根據(jù)這個規(guī)律來實(shí)現(xiàn)一下

簡單寫法

 

  1. function fibonacci(n) { 
  2.   // 第一項(xiàng)和第二項(xiàng)都返回1 
  3.   if (n === 1 || n === 2) return 1; 
  4.   // 我們只要返回 n - 1(n的前一項(xiàng))與 n - 2(n的前兩項(xiàng))的和便是我們要的值 
  5.   return fibonacci(n - 1) + fibonacci(n - 2); 

優(yōu)化版本

上面的寫法,求 20 次以內(nèi)的總和運(yùn)行會很快,50 次以上特別慢,100 次 以上可能就爆棧了,所以我們需要優(yōu)化寫法,緩存每次計(jì)算后的值

 

  1. function feibo(n, sum1 = 1, sum2 = 1) { 
  2.   if (n === 1 || n === 2) return sum2; 
  3.   return feibo(n - 1, sum2, sum1 + sum2); 

這種寫法緩存了,每次計(jì)算后的值,執(zhí)行效率會很高,100 次以上也會秒返回結(jié)果,這個也叫作尾遞歸優(yōu)化

觀察者與發(fā)布訂閱

一直以來,我以為發(fā)布訂閱和觀察者是一個思路,一次偶然的機(jī)會我發(fā)現(xiàn)他們是兩種不同的設(shè)計(jì)思路

雖然他們都是實(shí)現(xiàn)了對象的一種一對多的依賴關(guān)系,當(dāng)一個對象的狀態(tài)發(fā)生改變時,所有依賴它的對象都將得倒通知,然后自動更新。但是他們之間是有一定區(qū)別的。

觀察者模式

觀察者模式會有 觀察者 與 被觀察者(觀察目標(biāo)) 兩個對象存在,觀察者可以有多個,觀察目標(biāo)可以添加多個觀察者,可以通知觀察者。觀察者模式是面向與目標(biāo)和觀察者編程的,耦合目標(biāo)和觀察者

 

  1. // 被觀察者 
  2. class Subject { 
  3.   constructor() { 
  4.     this.subs = []; 
  5.   } 
  6.   add(observer) { 
  7.     this.subs.push(observer); 
  8.   } 
  9.   notify(...args) { 
  10.     this.subs.forEach(ob => ob.update(...args)); 
  11.   } 
  12. // 觀察者 
  13. class Observer { 
  14.   update(...args) { 
  15.     console.log('Observer -> update -> args', args); 
  16.   } 
  17.  
  18. // 使用 
  19. const o1 = new Observer(); 
  20. const o2 = new Observer(); 
  21. const o3 = new Observer(); 
  22. const o5 = new Observer(); 
  23. const sub = new Subject(); 
  24. // 添加觀察者 
  25. sub.add(o1); 
  26. sub.add(o2); 
  27. sub.add(o3); 
  28. // 通知觀察者 
  29. sub.notify('嘿嘿嘿'); 

發(fā)布訂閱模式

發(fā)布訂閱模式會有一個調(diào)度中心的概念。是面向調(diào)度中心編程的,對發(fā)布者與訂閱者解耦

 

  1. class PubSub { 
  2.   constructor() { 
  3.     this.handlers = {}; 
  4.   } 
  5.   subscribe(type, fn) { 
  6.     if (!this.handlers[type]) { 
  7.       this.handlers[type] = []; 
  8.     } 
  9.     this.handlers[type].push(fn); 
  10.   } 
  11.   publish(type, ...args) { 
  12.     if (!this.handlers[type]) return
  13.     this.handlers[type].forEach(fn => fn(...args)); 
  14.   } 
  15.  
  16. const ps = new PubSub(); 
  17.  
  18. ps.subscribe('a', console.log); 
  19. ps.subscribe('a', console.log); 
  20. ps.subscribe('a', console.log); 
  21. ps.subscribe('a', console.log); 
  22. ps.publish('a''hello world'); 

字符串轉(zhuǎn) txt 文件(blob)

有個要求:純前端實(shí)現(xiàn),不可以使用 nodejs

實(shí)現(xiàn)原理也很簡單,就像我們平時下載一個本地文件一樣,可以動態(tài)的創(chuàng)建一個可以下載的 a 標(biāo)簽,給它設(shè)置 download 屬性,然后把下載的內(nèi)容轉(zhuǎn) blob 創(chuàng)建下載鏈接下載即可

具體實(shí)現(xiàn)如下:

 

  1. function exportTxt(text, filename) { 
  2.   const eleLink = document.createElement('a'); 
  3.   eleLink.download = filename; 
  4.   eleLink.style.display = 'none'
  5.   // 將內(nèi)容轉(zhuǎn)為 blob 
  6.   const blob = new Blob([text]); 
  7.   eleLink.href = URL.createObjectURL(blob); 
  8.   document.body.appendChild(eleLink); 
  9.   eleLink.click(); 
  10.   document.body.removeChild(eleLink); 

奇偶數(shù)判斷

可能會遇到一個做奇偶數(shù)判斷的方法吧,反正我遇到了,一句話搞定

 

  1. const isEven = num => num % 2 === 0; 

格式化金錢

項(xiàng)目中我們經(jīng)常會遇到金錢格式化需求,或者說數(shù)字格式化一下,方便閱讀(數(shù)字比較大的情況下)

比如說 999999999,直接閱讀很不直觀,格式化后 999,999,999

通常我們會使用正則來處理

 

  1. function formatPrice(price) { 
  2.   return String(price).replace(/\B(?=(\d{3})+(?!\d))/g, ','); 

也可以不使用正則然后優(yōu)雅的處理

 

  1. function formatPrice(price) { 
  2.   return String(price) 
  3.     .split(''
  4.     .reverse() 
  5.     .reduce((prev, nextindex) => { 
  6.       return (index % 3 ? next : next + ',') + prev; 
  7.     }); 

上面是兩種提到的比較常用的方案,但是 js 還有個比較牛逼的 API 可以直接實(shí)現(xiàn)這個需求哦,它就是 toLocaleString,我們可以直接數(shù)字調(diào)用這個方法就可以實(shí)現(xiàn),金額的格式化

 

  1. (999999999).toLocaleString(); // 999,999,999 
  2. // 當(dāng)然還可以更秀一點(diǎn) 
  3. const options = { 
  4.   style: 'currency'
  5.   currency: 'CNY'
  6. }; 
  7. (123456).toLocaleString('zh-CN', options); // ¥123,456.00 

toLocaleString 可以接收兩個可選參數(shù):locales 和 options,而且這個 api 在各大瀏覽器通用不存在兼容問題并且這個 api 不止存在 Number 的原型上,Array、Object、Date 原型上都有這個 api,并且格式化出來的值可以根據(jù)我們傳入的參數(shù)出現(xiàn)各種結(jié)果

參數(shù)及用法可以參考 MDN

深度凍結(jié)對象

在 vue 項(xiàng)目開發(fā)中,有些不變的常量,我們不想 vue 為他做雙向綁定,以減少一些性能上消耗,我們可以把使用 Object.freeze 將對象凍結(jié),此時 vue 將不會對這個對象進(jìn)行凍結(jié),但是這個凍結(jié)只是凍結(jié)對象第一層,所以遇到對象層級比較深的話,我們可以寫個深度凍結(jié)的 api,來對常量對象做一些凍結(jié)優(yōu)化

 

  1. const deepFreeze = o => { 
  2.   const propNames = Object.getOwnPropertyNames(o); 
  3.   propNames.forEach(name => { 
  4.     const prop = o[name]; 
  5.     if (typeof prop === 'object' && prop !== null) { 
  6.       deepFreeze(prop); 
  7.     } 
  8.   }); 
  9.   return Object.freeze(o); 
  10. }; 

脫敏處理

在一些涉及到用戶隱私情況下,可能會遇到對用戶的手機(jī)號身份證號之類的信息脫敏,但是這個脫敏數(shù)據(jù)的規(guī)則是根據(jù)用戶信息要脫敏字段動態(tài)的生成的,此時我們動態(tài)拼接正則來實(shí)現(xiàn)一個動態(tài)脫敏規(guī)則

 

  1. const encryptReg = (before = 3, after = 4) => { 
  2.   return new RegExp('(\\d{' + before + '})\\d*(\\d{' + after + '})'); 
  3. }; 
  4. // 使用:'13456789876'.replace(encryptReg(), '$1****$2') -> "134****9876" 

樹遍歷

對于樹結(jié)構(gòu)的遍歷一般有深度優(yōu)先和廣度優(yōu)先

廣度優(yōu)先和深度優(yōu)先的概念很簡單,區(qū)別如下:

  1. 深度優(yōu)先,訪問完一顆子樹再去訪問后面的子樹,而訪問子樹的時候,先訪問根再訪問根的子樹,稱為先序遍歷;先訪問子樹再訪問根,稱為后序遍歷。
  2. 廣度優(yōu)先,即訪問樹結(jié)構(gòu)的第 n+1 層前必須先訪問完第 n 層

1.深度優(yōu)先

先序遍歷

 

  1. const treeForEach = (tree, func) => { 
  2.   tree.forEach(data => { 
  3.     func(data); 
  4.     data.children && treeForEach(data.children, func); 
  5.   }); 
  6. }; 

后序遍歷,只需要調(diào)換一下節(jié)點(diǎn)遍歷和子樹遍歷的順序即可

 

  1. const treeForEach = (tree, func) => { 
  2.   tree.forEach(data => { 
  3.     data.children && treeForEach(data.children, func); 
  4.     func(data); 
  5.   }); 
  6. }; 

2.廣度優(yōu)先

廣度優(yōu)先的思路是,維護(hù)一個隊(duì)列,隊(duì)列的初始值為樹結(jié)構(gòu)根節(jié)點(diǎn)組成的列表,重復(fù)執(zhí)行以下步驟直到隊(duì)列為空。取出隊(duì)列中的第一個元素,進(jìn)行訪問相關(guān)操作,然后將其后代元素(如果有)全部追加到隊(duì)列最后。

 

  1. const treeForEach = (tree, func) => { 
  2.   let node, 
  3.     list = [...tree]; 
  4.   while ((node = list.shift())) { 
  5.     func(node); 
  6.     node.children && list.push(...node.children); 
  7.   } 
  8. }; 

數(shù)組分組

開發(fā)移動端的時候,遇到一個首頁菜單改版的需求,首頁菜單根據(jù)權(quán)限控制顯隱,而菜單每頁展示八個小菜單,超過八個做 swipe 滑動切換,當(dāng)時項(xiàng)目用了 vant 做的 UI 框架,菜單那模塊就選擇了他的輪播插件,菜單做成了一個扁平化的 list 配置,首先根據(jù)權(quán)限過濾出所有有權(quán)限的菜單項(xiàng),然后每八個一分組,處理成一個二維數(shù)據(jù)來遍歷菜單

 

  1. const arrayGroupBySize = (arr, size = 2) => { 
  2.   const result = []; 
  3.   for (let i = 0, len = arr.length; i < len; i += size) { 
  4.     result.push(arr.slice(i, i + size)); 
  5.   } 
  6.   return result; 
  7. }; 

下劃線與駝峰

做一些數(shù)據(jù)持久化的工作的時候經(jīng)常會出現(xiàn)下劃線命名和駝峰命名的轉(zhuǎn)化,因?yàn)樵谇岸颂幚碇幸?guī)范是駝峰命名,而像 mysql 之類的規(guī)范是下劃線命名,所以在處理后返回給前端的數(shù)據(jù)需要轉(zhuǎn)換為駝峰命名,而對數(shù)據(jù)庫的讀寫需要下劃線命名

 

  1. const toHump = name => { 
  2.   return name.replace(/\_(\w)/g, function (all, letter) { 
  3.     return letter.toUpperCase(); 
  4.   }); 
  5. }; 
  6.  
  7. const toLine = name => { 
  8.   return name.replace(/([A-Z])/g, '_$1').toLowerCase(); 
  9. }; 

校驗(yàn)時間格式

業(yè)務(wù)中遇到一個校驗(yàn)一下傳入時間格式是否為一個時間格式,下面的方法可以完美校驗(yàn)

 

  1. const isDate = str => { 
  2.   return typeof str !== 'number' && str !== null && new Date(str) !== 'Invalid Date'
  3. }; 

持續(xù)記錄中...

責(zé)任編輯:華軒 來源: 程序員成長指北
相關(guān)推薦

2011-04-11 13:25:59

Sybase安裝

2011-04-11 13:28:31

Oracle安裝

2019-03-06 14:26:31

Javascript面試前端

2022-01-09 23:16:49

編程語言程序員

2020-11-08 16:04:03

開發(fā)工具技術(shù)

2010-06-04 19:31:25

遠(yuǎn)程MySQL權(quán)限

2011-03-04 17:38:52

2010-04-21 08:50:08

2010-04-06 13:07:45

Oracle數(shù)據(jù)庫

2019-12-16 15:37:57

JavaScript人生第一份工作瀏覽器

2022-09-25 22:56:52

JavaScrip編程技巧

2022-09-30 09:26:35

JavaScript技巧

2023-01-29 09:18:14

ScrollendJavaScript事件

2010-03-31 17:17:32

2009-11-30 15:21:52

2010-04-23 10:30:17

Oracle移植

2010-04-07 16:41:50

Oracle SQL優(yōu)

2010-03-22 15:38:46

Python常用模塊

2020-07-22 13:50:39

shell命令前端

2010-05-21 12:23:32

MySQL數(shù)據(jù)庫
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號