1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

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

        共 24816字,需瀏覽 50分鐘

         ·

        2021-04-04 05:29


        作者 @chengyuming 

        原文地址:https://chengyuming.cn/views/basis/issue.html

              獲取一個(gè)月有多少天

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

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

        new Date(2019120).getDate(); // 31
        new Date(201820).getDate(); // 28
        // 根據(jù)這個(gè)我們可以得到一個(gè)方法
        function getMonthLength(month{
          const date = new Date(month);
          const year = date.getFullYear();
          // 月份是從 0 開始計(jì)算的
          const _month = date.getMonth() + 1;
          return new Date(year, _month, 0).getDate();
        }

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

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

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

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

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

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

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

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

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

        void 運(yùn)算符

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

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

        void true// undefined
        void 0// undefined

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

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

        關(guān)于 JSON.stringify

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

        JSON.stringify(42); // "42"
        JSON.stringify('42'); // ""42""(含有雙引號(hào)的字符串)
        JSON.stringify(null); // "null"
        JSON.stringify(true); // "true"

        不安全的 JSON 值

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

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

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

        toJSON 方法

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

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

        const o = {
          a'cym',
          toJSON() {
            return { c'b' };
          },
        };

        JSON.stringify(o); // {"c":"b"}

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

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

        1. 當(dāng) replacer 是一個(gè)數(shù)組時(shí),那么他必須是一個(gè)字符串?dāng)?shù)組,其中包含序列化要處理的對(duì)象的屬性名稱,除此之外的屬性就會(huì)被忽略
        const obj = {
          a42,
          b30,
          c100,
        };
        JSON.stringify(obj, ['a''c']); // {"a":42,"c":100}
        1. 當(dāng) replacer 是一個(gè)函數(shù)時(shí),他會(huì)對(duì)對(duì)象本身調(diào)用一次,然后在對(duì)對(duì)象中的每個(gè)屬性各調(diào)用一次。每次傳遞兩個(gè)參數(shù)(對(duì)象的鍵和值)。如果要忽略某個(gè)鍵就返回 undecided,否則就返回指定的值
        const obj = {
          a42,
          b30,
          c100,
        };
        JSON.stringify(obj, (k, v) => {
          // 注意:第一次 k 是 undefined,v 是原對(duì)象
          if (k !== 'c'return v;
        }); // "{"a":42,"b":30}"

        一元運(yùn)算符

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

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

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

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

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

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

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

        -1 是一個(gè) 哨位值,哨位值是那些在各個(gè)類型中被賦予了特殊含義的值。在 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)換為 真/假 值

        const str = 'hello world';
        ~str.indexOf('lo'); // -4,真值
        if (~str.indexOf('lo')) {
          // true
          // 找到匹配
        }
        ~str.indexOf('ol'); // 0,假值
        !~str.indexOf('ol'); // true
        if (!~str.indexOf('ol')) {
          // true
          // 沒有找到匹配
        }

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

        字位截除

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

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

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

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

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

        ~~1.9// 1
        1.9 | 0// 1

        ~~-1.9// -1
        -1.9 | 0// -1

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

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

        Promise.all

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

        const urls = ['./1.json''./2.json''./3.json'];
        function getData(url{
          // 返回一個(gè) Promise 利用 Promise.all 接受
          return new Promise((resolve, reject) => {
            const xhr = new XMLHttpRequest();
            xhr.responseType = 'json';
            xhr.onreadystatechange = () => {
              if (xhr.readyState === 4) {
                if (xhr.status === 200) {
                  resolve(xhr.response);
                }
              }
            };
            xhr.open('GET', url, true);
            xhr.send(null);
          });
        }
        function getMultiData(urls{
          // Promise.all 接受一個(gè)包含 promise 的數(shù)組,如果不是 promise 數(shù)組會(huì)被轉(zhuǎn)成 promise
          Promise.all(urls.map(url => getData(url))).then(results => {
            console.log(results);
          });
        }

        不用 Promise

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

        使用對(duì)象做映射

        const urls = ['./1.json''./2.json''./3.json'];
        function getAllDate(urls, cd{
          const result = {};
          function getData(url, idx{
            const xhr = new XMLHttpRequest();
            xhr.responseType = 'json';
            xhr.onreadystatechange = () => {
              if (xhr.readyState === 4) {
                if (xhr.status === 200) {
                  result[idx] = xhr.response;
                  // 如果兩者 length 相等說明都請(qǐng)求完成了
                  if (Object.keys(result).length === urls.length) {
                    // 給對(duì)象添加length屬性,方便轉(zhuǎn)換數(shù)組
                    result.length = urls.length;
                    cd && cd(Array.from(result));
                  }
                }
              }
            };
          }
          // 觸發(fā)函數(shù)執(zhí)行
          urls.forEach((url, idx) => getData(url, idx));
        }
        // 使用
        getAllDate(urls, data => {
          console.log(data);
        });

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

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

        function getGroupData(urls, cb{
          const results = [];
          let count = 0;
          const getData = url => {
            const xhr = new XMLHttpRequest();
            xhr.responseType = 'json';
            xhr.onreadystatechange = _ => {
              if (xhr.readyState === 4) {
                if (xhr.status === 200) {
                  results.push(xhr.response);
                  if (++count === urls.length) {
                    cb && cb(results);
                  }
                }
              }
            };
            xhr.open('GET', url, true);
            xhr.send(null);
          };
          urls.forEach(url => getData(url));
        }

        getGroupData(urls, data => {
          console.log(data);
        });

        類型轉(zhuǎn)換問題

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

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

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

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

        const a = {
          val1,
          toString() {
            return this.val++;
          },
        };
        if (a == 1 && a == 2 && a == 3) {
          console.log('ok');
        }

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

        var i = 1;
        Object.defineProperty(window'a', {
          get() {
            return i++;
          },
        });

        if (a == 1 && a == 2 && a == 3) {
          console.log('OK');
        }

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

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

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

        1..toString 的問題

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

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

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

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

        Generator

        對(duì)象增加迭代器

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

        const obj = {
          01,
          12,
          23,
          34,
          length4,
          [Symbol.iterator]() {
            let idx = 0
            return {
              next() {
                return {
                  value: obj[idx],
                  done: idx++ >= obj.length,
                }
              }
            }
          }
        }
        // 此時(shí)對(duì)象就被添加了迭代器
        [...obj]  // 1 2 3 4
        for (const val of obj) {
          console.log(val)  // 1 2 3 4
        }

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

        const obj = {
          01,
          12,
          23,
          34,
          length4,
          [Symbol.iterator]: function* ({
            let idx = 0
            while (idx !== this.length) {
              yield this[idx++]
            }
          }

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

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

        function generator(str{
          let idx = 0;
          return {
            next() {
              return {
                value: str[idx],
                done: idx++ >= str.length,
              };
            },
          };
        }
        // 測(cè)試
        const str = 'as';
        let gen = generator(str);
        console.log(gen.next());
        console.log(gen.next());
        console.log(gen.next());
        console.log(gen.next());
        gen = generator('str');
        console.log(gen.next());
        console.log(gen.next());
        console.log(gen.next());
        console.log(gen.next());
        // { value: 'a', done: false }
        // { value: 's', done: false }
        // { value: undefined, done: true }
        // { value: undefined, done: true }
        // { value: 's', done: false }
        // { value: 't', done: false }
        // { value: 'r', done: false }
        // { value: undefined, done: true }

        簡單模擬 co

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

        首先來看一則例子

        const fs = require('fs');
        const path = require('path');
        const { promisify } = require('util');
        const readFile = promisify(fs.readFile);

        functionread({
          const name = yield readFile(path.resolve(__dirname, 'name.txt'), 'utf8');
          const age = yield readFile(path.resolve(__dirname, name), 'utf8');
          return age;
        }

        const it = read();

        let { value, done } = it.next();
        value.then(data => {
          let { value, done } = it.next(data);
          // console.log(data, '???')
          value.then(data => {
            let { value, done } = it.next(data);
            console.log(value);
          });
        });

        使用 co 庫可以很容易解決這個(gè)問題

        const co = require('co');
        // co 接受一個(gè)生成器
        co(read()).then(data => {
          console.log(data);
        });
        // 那模擬一下
        function _co(it{
          // 首先返回一個(gè) promise
          return new Promise((resolve, reject) => {
            // 因?yàn)榭梢詡髦档脑颍豢梢灾苯邮褂醚h(huán)實(shí)現(xiàn),需要使用 遞歸
            function next(data{
              const { value, done } = it.next(data);
              if (done) return resolve(value);
              // 保證值是一個(gè) promise
              Promise.resolve(value).then(data => {
                next(data);
              }, reject);
            }
            next();
          });
        }

        菲波那切數(shù)列

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

        簡單寫法

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

        優(yōu)化版本

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

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

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

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

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

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

        觀察者模式

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

        // 被觀察者
        class Subject {
          constructor() {
            this.subs = [];
          }
          add(observer) {
            this.subs.push(observer);
          }
          notify(...args) {
            this.subs.forEach(ob => ob.update(...args));
          }
        }
        // 觀察者
        class Observer {
          update(...args) {
            console.log('Observer -> update -> args', args);
          }
        }

        // 使用
        const o1 = new Observer();
        const o2 = new Observer();
        const o3 = new Observer();
        const o5 = new Observer();
        const sub = new Subject();
        // 添加觀察者
        sub.add(o1);
        sub.add(o2);
        sub.add(o3);
        // 通知觀察者
        sub.notify('嘿嘿嘿');

        發(fā)布訂閱模式

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

        class PubSub {
          constructor() {
            this.handlers = {};
          }
          subscribe(type, fn) {
            if (!this.handlers[type]) {
              this.handlers[type] = [];
            }
            this.handlers[type].push(fn);
          }
          publish(type, ...args) {
            if (!this.handlers[type]) return;
            this.handlers[type].forEach(fn => fn(...args));
          }
        }

        const ps = new PubSub();

        ps.subscribe('a'console.log);
        ps.subscribe('a'console.log);
        ps.subscribe('a'console.log);
        ps.subscribe('a'console.log);
        ps.publish('a''hello world');

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

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

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

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

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

        奇偶數(shù)判斷

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

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

        格式化金錢

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

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

        通常我們會(huì)使用正則來處理

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

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

        function formatPrice(price{
          return String(price)
            .split('')
            .reverse()
            .reduce((prev, next, index) => {
              return (index % 3 ? next : next + ',') + prev;
            });
        }

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

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

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

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

        深度凍結(jié)對(duì)象

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

        const deepFreeze = o => {
          const propNames = Object.getOwnPropertyNames(o);
          propNames.forEach(name => {
            const prop = o[name];
            if (typeof prop === 'object' && prop !== null) {
              deepFreeze(prop);
            }
          });
          return Object.freeze(o);
        };

        脫敏處理

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

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

        樹遍歷

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

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

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

        先序遍歷

        const treeForEach = (tree, func) => {
          tree.forEach(data => {
            func(data);
            data.children && treeForEach(data.children, func);
          });
        };

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

        const treeForEach = (tree, func) => {
          tree.forEach(data => {
            data.children && treeForEach(data.children, func);
            func(data);
          });
        };
        1. 廣度優(yōu)先

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

        const treeForEach = (tree, func) => {
          let node,
            list = [...tree];
          while ((node = list.shift())) {
            func(node);
            node.children && list.push(...node.children);
          }
        };

        數(shù)組分組

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

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

        下劃線與駝峰

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

        const toHump = name => {
          return name.replace(/\_(\w)/gfunction (all, letter{
            return letter.toUpperCase();
          });
        };

        const toLine = name => {
          return name.replace(/([A-Z])/g'_$1').toLowerCase();
        };

        校驗(yàn)時(shí)間格式

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

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

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

        最后



        如果你覺得這篇內(nèi)容對(duì)你挺有啟發(fā),我想邀請(qǐng)你幫我三個(gè)小忙:

        1. 點(diǎn)個(gè)「在看」,讓更多的人也能看到這篇內(nèi)容(喜歡不點(diǎn)在看,都是耍流氓 -_-)

        2. 歡迎加我微信「qianyu443033099」拉你進(jìn)技術(shù)群,長期交流學(xué)習(xí)...

        3. 關(guān)注公眾號(hào)「前端下午茶」,持續(xù)為你推送精選好文,也可以加我為好友,隨時(shí)聊騷。

        點(diǎn)個(gè)在看支持我吧,轉(zhuǎn)發(fā)就更好了


        瀏覽 52
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        評(píng)論
        圖片
        表情
        推薦
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            欧美偷拍精品 | 色777色 | 涩涩涩综合网 | 男女啪啪电影 | 孩性freesexhd中国 | 一本久久久综合狠狠躁 | 人人爱,人人操 | 日韩一级片免费观看 | 草逼女同视频 | 美女尻逼软件 |