整合營銷服務商

          電腦端+手機端+微信端=數據同步管理

          免費咨詢熱線:

          js 常用簡寫技巧(干貨滿滿)

          js 常用簡寫技巧(干貨滿滿)

          享一些自己常用的js簡寫技巧,長期更新,會著重挑選一些實用的簡寫技巧,使自己的代碼更簡潔優雅~

          這里只會收集一些大多數人不知道的用法,但是確實能提高自己的編碼技巧,像ES6那些基礎的簡寫語法或者是三目運算符代替if else那些我覺得是基礎,沒必要寫在這里浪費精力。

          注意本篇內容涉及到的語法從ES6到ES11不等,具體使用需要考慮項目兼容性問題!

          另外推薦一個只用一行代碼實現一個方法的實用網站1loc.dev,github

          If-Else 用 || 或 ?? 運算符進行簡化

          邏輯或操作符||,這里要注意的是0和''也會認為是false

          如果||前面的值是0 '' false null undefined NaN其中的任意一種,則直接返回||后面的值

          function(obj){
            var a=obj || {}
          }
          // 等價于=>>
          function(obj){
            var a;
            if(
              obj===0 || 
              obj==="" || 
              obj===false || 
              obj===null || 
              obj===undefined ||
              isNaN(obj)
            ){
               a={}
             } else {
               a=obj;
            }
          }
          


          空值合并操作符??如果沒有定義左側返回右側。如果是,則返回左側。

          這種方法非常實用,有時候僅僅只是想判斷一個字段有沒有值,而不是把空字符串或者0也當做false處理

          function(obj){
            var a=obj ?? {}
          }
          // 等價于=>>
          function(obj){
            var a;
            if(
              obj===null || 
              obj===undefined
            ){
               a={}
             } else {
               a=obj;
            }
          }
          


          輸入框非空的判斷(有時候不想把0當成false可以用此方法。比如分數0也是個值,這種情況就不能認為是false)

          if(value !==null && value !==undefined && value !==''){}
          // 等價于==>
          if((value ?? '') !==''){}
          


          includes的正確使用姿勢

          在上面邏輯或操作符||代碼段里有一個if判斷比較長,這時候就可以用includes去簡化代碼

          if(
            obj===0 || 
            obj==="" || 
            obj===false || 
            obj===null || 
            obj===undefined
          ){
            // ...
          }
          
          // 使用includes簡化
          if([0, '', false, null, undefined].includes(obj)){
            // ...
          }
          


          防止崩潰的可選鏈(?.)

          可選鏈操作符?. 如果訪問未定義的屬性,則會產生錯誤。這就是可選鏈的用武之地。 在未定義屬性時使用可選鏈運算符,undefined將返回而不是錯誤。這可以防止你的代碼崩潰。

          const student={
            name: "lwl",
            address: {
              state: "New York"
            },
          }
          
          // 一層一層判斷
          console.log(student && student.address && student.address.ZIPCode) // 輸出:undefined
          // 使用可選鏈操作符
          console.log(student?.address?.ZIPCode) // 輸出:undefined
          


          可選鏈運算符也可以用于方法調用。如果方法存在,它將被調用,否則將返回 undefined。例如:

          const obj={
            foo() {
              console.log('Hello from foo!')
            }
          }
          
          obj.foo?.() // 輸出:'Hello from foo!'
          obj.bar?.() // 輸出:undefined,因為 bar 方法不存在
          


          同樣,數組也可以使用。例如:

          const arr=[1, 2, 3, 4]
          
          console.log(arr[0]) // 輸出:1
          console.log(arr[4]) // 輸出:undefined
          
          // 使用可選鏈運算符
          console.log(arr?.[0]) // 輸出:1
          console.log(arr?.[4]) // 輸出:undefined
          console.log(arr?.[0]?.toString()) // 輸出:'1'
          
          // 多維數組也可以
          const arr2=[[[1]]]
          console.log(arr2?.[0]?.[0]?.[0]) // 輸出:1
          console.log(arr2?.[0]?.[1]?.[0]) // 輸出:undefined
          


          邏輯空賦值(??=)

          邏輯空賦值??=邏輯空賦值運算符(x ??=y)僅在 x 是 nullish (null 或 undefined) 時對其賦值。

          const a={ duration: 50 };
          
          a.duration ??=10;
          console.log(a.duration);
          // expected output: 50
          
          a.speed ??=25;
          console.log(a.speed);
          // expected output: 25
          


          快速生成1-10的數組

          生成0-9,利用了數組的下標值。推薦使用方法二或方法三,比較靈活,改成(v, k)=> k + 1就是1-10

          // 方法一
          const arr1=[...new Array(10).keys()]
          // 方法二
          const arr2=Array.from(Array(10), (v, k)=> k)
          // 方法三
          const arr3=Array.from({length: 10}, (v, k)=> k)
          


          生成1-10,通過map的特性

          const arr1=[...Array(10)].map((v, i)=> i + 1)
          // 下面這兩個和上面生成0-9的方式一樣
          const arr2=Array.from(Array(10), (v, k)=> k + 1)
          const arr3=Array.from({length: 10}, (v, k)=> k + 1)
          


          快速生成10個0的數組

          const arr=new Array(10).fill(0)
          


          快速生成10個[]的數組(二維數組)

          注意: 二維數組不能直接寫成new Array(10).fill([])(也就是fill方法不能傳引用類型的值,[]換成new Array()也不行),因為fill里傳入引用類型值會導致每一個數組都指向同一個地址,改變一個數據的時候其他數據也會隨之改變,詳見mdn官方說明

          // 錯誤寫法
          const arr=new Array(10).fill([]) // 注意這是錯誤寫法,不要這么寫
          // 正確寫法
          const arr=new Array(10).fill().map(()=> new Array())
          


          數組降維

          你是否還在用遞歸給一個多維數組降維?如果是,那你應該知道一下es6的 flat() 方法。

          如果不確定需要降維的數組有多深,可以傳入最大值作為參數Infinity,默認值深度為1

          const arr=[1, [2, [3, 4], 5], 6]
          const flatArr=arr.flat(Infinity) // 輸出 [1, 2, 3, 4, 5, 6]
          


          你是否在使用map的時候想要對數組降維?大概像這樣:

          const arr=[1, 2, 3, 4]
          const result=arr.map(v=> [v, v * 2]).flat()
          console.log(result); // 輸出 [1, 2, 2, 4, 3, 6, 4, 8]
          


          其實js也提供了更簡便的方法,那就是flatMap(),可以改成這樣:

          const result=arr.flatMap(v=> [v, v * 2])
          


          Set集合實踐:數組去重、交集、并集、差集

          js// 1.數組去重 
          let arr1=[1, 3, 2, 1, 4, 2, 1] 
          let result1=[...new Set(arr1)] 
          console.log(result1) // [1, 3, 2, 4] 
          
          // 2.交集 
          let arr2=[4, 3, 4, 7] 
          let result2=[...new Set(arr1)].filter(v=> new Set(arr2).has(v)) 
          console.log(result2) // [3, 4] 
          
          // 3.并集 
          let result3=[...new Set([...arr1, ...arr2])] 
          console.log(result3) // [1, 3, 2, 4, 7] 
          
          // 4.差集 
          let result4=[...new Set(arr1)].filter(v=> !(new Set(arr2).has(v))) 
          console.log(result4) // [1, 2]
          


          在沒有第三個變量的情況下交換兩個變量

          在 JavaScript 中,你可以使用解構從數組中拆分值。這可以應用于交換兩個變量而無需第三個

          比較簡單,es6語法

          let x=1;
          let y=2;
          
          // 交換變量
          [x, y]=[y, x];
          


          將對象的值收集到數組中

          用于Object.values()將對象的所有值收集到一個新數組中

          const info={ name: "Matt", country: "Finland", age: 35 };
          
          // LONGER FORM
          let data=[];
          for (let key in info) {
            data.push(info[key]);
          }
          
          // SHORTHAND
          const data=Object.values(info);
          


          指數運算符(用的不多)

          你Math.pow()習慣把一個數字提高到一個冪嗎?你知道你也可以使用**運算符嗎?

          雖然可以簡寫,不過我還是建議寫成Math.pow()方法,代碼更有語義化。

          注意:**運算符要求操作數為數值類型,不過在js里也能正常運行。

          Math.pow(2, 3); // 輸出: 8 
          2 ** 3; // 輸出: 8 
          
          Math.pow(4, 0.5); // 輸出: 2 
          4 ** 0.5; // 輸出: 2 
          
          Math.pow(3, -2); // 輸出: 0.1111111111111111 
          3 ** -2; // 輸出: 0.1111111111111111 
          
          Math.pow('2', '3'); // 輸出: 8 (參數被自動轉換為數字) 
          '2' ** '3'; // js中輸出: 8,其他語言可能報錯
          


          Math.floor() 簡寫(用的不多)

          向下取整Math.floor()并不是什么新鮮事。但是你知道你也可以使用~~運算符嗎?

          同上雖然可以簡寫,不過我還是建議寫成Math.floor()方法,代碼更有語義化。

          注意:對于正數而言兩者都是直接去掉小數位,但對于負數來說Math.floor()是向下取整,~~依然是只去掉小數位,整數位不變。 請看下面輸出結果:

          Math.floor(3.14); // 輸出: 3 
          Math.floor(5.7); // 輸出: 5 
          Math.floor(-2.5); // 輸出: -3 
          Math.floor(10); // 輸出: 10
          
          ~~3.14; // 輸出: 3 
          ~~5.7; // 輸出: 5 
          ~~(-2.5); // 輸出: -2 
          ~~10; // 輸出: 10
          


          逗號運算符(,)

          逗號, )運算符對它的每個操作數從左到右求值,并返回最后一個操作數的值。這讓你可以創建一個復合表達式,其中多個表達式被評估,復合表達式的最終值是其成員表達式中最右邊的值。這通常用于為 for 循環提供多個參數。

          這里只說一下函數return的時候用逗號運算符簡化代碼的技巧,其他用法請直接點擊查看官方文檔。

          舉一個簡單的例子:

          // 簡化前
          const result=arr=> {
            arr.push('a')
            return arr
          }
          console.log(result([1,2])) // 輸出:[1, 2, 'a']
          


          這段代碼需要返回修改后的數組,不能直接return arr.push('a'),因為push的返回值是修改后數組的長度,這時候可以用逗號運算符簡化成一行代碼。

          // 簡化后
          const result=arr=> (arr.push('a'), arr)
          console.log(result([1,2])) // 輸出:[1, 2, 'a']
          


          Array.map()的簡寫

          比如想要拿到接口返回的特定字段的值,可以用解構賦值和對象的簡寫方法對map方法簡寫,詳細解釋請移步js map方法應用場景 處理對象數組。

          比如接口返回數據,此時如果只想要數據里的id和name,就可以用下面的簡寫方式。

          // 接口返回數據
          res=[{
            id: 1,
            name: 'zhangsan',
            age: 16,
            gender: 0
          }, {
            id: 2,
            name: 'lisi',
            age: 20,
            gender: 1
          }]
          
          // 第一種方法:箭頭函數、 解構賦值
          const data=res.map(({id, name})=> ({id, name}))
          // 第二種方法:箭頭函數、返回對象(相對更容易理解)
          const data=res.map(v=> ({id: v.id, name: v.name}))
          


          文字排序

          我們知道數組方法sort()默認是按照UTF-16碼元值升序排序

          我們可以使用charCodeAt()方法獲取UTF-16碼元

          對于中文或者其他語言按照拼音排序(字典順序排序)則需要用到localeCompare()方法

          ['張三', '李四', '趙五', '王二', '陳六'].sort()
          // 輸出:['張三', '李四', '王二', '趙五', '陳六']
          ['張三', '李四', '趙五', '王二', '陳六'].sort((a, b)=> a.localeCompare(b, 'zh-Hans-CN'))
          // 輸出:['陳六', '李四', '王二', '張三', '趙五']
          
          ['apple', 'Banana', 'cherry', 'Date', 'apricot', 'Banana'].sort()
          // 輸出:['Banana', 'Banana', 'Date', 'apple', 'apricot', 'cherry']
          ['apple', 'Banana', 'cherry', 'Date', 'apricot', 'Banana'].sort((a, b)=> a.localeCompare(b, 'en'))
          // 輸出:['apple', 'apricot', 'Banana', 'Banana', 'cherry', 'Date']
          


          • 'zh' 表示主語言標記,代表中文。
          • 'Hans' 是腳本子標記,表示簡體漢字。
          • 'CN' 是區域子標記,表示中國。

          如果您不提供locales參數(第二個參數),localeCompare方法通常會默認使用瀏覽器或操作系統的當前語言環境

          at()方法獲取數組最后一位值

          // 獲取arr的最后一位值
          const arr=[1, 2, 3, 4, 5]
          // 一般寫法
          const last=arr[arr.length - 1]
          // 二般寫法
          const last=arr.slice(-1)[0]
          // 終極寫法
          const last=arr.at(-1)
          



          作者:lwlcode
          鏈接:https://juejin.cn/post/7236664417308524581

          知道瀏覽器和服務端是通過 HTTP 協議進行數據傳輸的,而 HTTP 協議又是純文本協議,那么瀏覽器在得到服務端傳輸過來的 HTML 字符串,是如何解析成真實的 DOM 元素的呢,也就是我們常說的生成 DOM Tree,最近了解到狀態機這樣一個概念,于是就萌生一個想法,實現一個 innerHTML 功能的函數,也算是小小的實踐一下。

          函數原型

          我們實現一個如下的函數,參數是 DOM 元素和 HTML 字符串,將 HTML 字符串轉換成真實的 DOM 元素且 append 在參數一傳入的 DOM 元素中。

          function html(element, htmlString) {
           // 1. 詞法分析
           // 2. 語法分析
           // 3. 解釋執行
          }
          復制代碼
          

          在上面的注釋我已經注明,這個步驟我們分成三個部分,分別是詞法分析、語法分析和解釋執行。

          詞法分析

          詞法分析是特別重要且核心的一部分,具體任務就是:把字符流變成 token 流。

          詞法分析通常有兩種方案,一種是狀態機,一種是正則表達式,它們是等效的,選擇你喜歡的就好。我們這里選擇狀態機。

          首先我們需要確定 token 種類,我們這里不考慮太復雜的情況,因為我們只對原理進行學習,不可能像瀏覽器那樣有強大的容錯能力。除了不考慮容錯之外,對于自閉合節點、注釋、CDATA 節點暫時均不做考慮。

          接下來步入主題,假設我們有如下節點信息,我們會分出哪些 token 來呢。

          <p class="a" data="js">測試元素</p>
          復制代碼
          

          對于上述節點信息,我們可以拆分出如下 token

          • 開始標簽:<p
          • 屬性標簽:class="a"
          • 文本節點:測試元素
          • 結束標簽:</p>

          狀態機的原理,將整個 HTML 字符串進行遍歷,每次讀取一個字符,都要進行一次決策(下一個字符處于哪個狀態),而且這個決策是和當前狀態有關的,這樣一來,讀取的過程就會得到一個又一個完整的 token,記錄到我們最終需要的 tokens 中。

          萬事開頭難,我們首先要確定起初可能處于哪種狀態,也就是確定一個 start 函數,在這之前,對詞法分析類進行簡單的封裝,具體如下

          function HTMLLexicalParser(htmlString, tokenHandler) {
           this.token=[];
           this.tokens=[];
           this.htmlString=htmlString
           this.tokenHandler=tokenHandler
          }
          復制代碼
          

          簡單解釋下上面的每個屬性

          • token:token 的每個字符
          • tokens:存儲一個個已經得到的 token
          • htmlString:待處理字符串
          • tokenHandler:token 處理函數,我們每得到一個 token 時,就已經可以進行流式解析

          我們可以很容易的知道,字符串要么以普通文本開頭,要么以<開頭,因此 start 代碼如下

          HTMLLexicalParser.prototype.start=function(c) {
           if(c==='<') {
           this.token.push(c)
           return this.tagState
           } else {
           return this.textState(c)
           }
          }
          復制代碼
          

          start處理的比較簡單,如果是<字符,表示開始標簽或結束標簽,因此我們需要下一個字符信息才能確定到底是哪一類 token,所以返回tagState函數去進行再判斷,否則我們就認為是文本節點,返回文本狀態函數。

          接下來分別展開tagState和textState函數。tagState根據下一個字符,判斷進入開始標簽狀態還是結束標簽狀態,如果是/表示是結束標簽,否則是開始標簽,textState用來處理每一個文本節點字符,遇到<表示得到一個完整的文本節點 token,代碼如下

          HTMLLexicalParser.prototype.tagState=function(c) {
           this.token.push(c)
           if(c==='/') {
           return this.endTagState
           } else {
           return this.startTagState
           }
          }
          HTMLLexicalParser.prototype.textState=function(c) {
           if(c==='<') {
           this.emitToken('text', this.token.join(''))
           this.token=[]
           return this.start(c)
           } else {
           this.token.push(c)
           return this.textState
           }
          }
          復制代碼
          

          這里初次見面的函數是emitToken、startTagState和endTagState。

          emitToken用來將產生的完整 token 存儲在 tokens 中,參數是 token 類型和值。

          startTagState用來處理開始標簽,這里有三種情形

          • 如果接下來的字符是字母,則認定依舊處于開始標簽態
          • 遇到空格,則認定開始標簽態結束,接下來是處理屬性了
          • 遇到>,同樣認定為開始標簽態結束,但接下來是處理新的節點信息

          endTagState用來處理結束標簽,結束標簽不存在屬性,因此只有兩種情形

          • 如果接下來的字符是字母,則認定依舊處于結束標簽態
          • 遇到>,同樣認定為結束標簽態結束,但接下來是處理新的節點信息

          邏輯上面說的比較清楚了,代碼也比較簡單,看看就好啦

          HTMLLexicalParser.prototype.emitToken=function(type, value) {
           var res={
           type,
           value
           }
           this.tokens.push(res)
           // 流式處理
           this.tokenHandler && this.tokenHandler(res)
          }
          HTMLLexicalParser.prototype.startTagState=function(c) {
           if(c.match(/[a-zA-Z]/)) {
           this.token.push(c.toLowerCase())
           return this.startTagState
           }
           if(c===' ') {
           this.emitToken('startTag', this.token.join(''))
           this.token=[]
           return this.attrState
           }
           if(c==='>') {
           this.emitToken('startTag', this.token.join(''))
           this.token=[]
           return this.start
           }
          }
          HTMLLexicalParser.prototype.endTagState=function(c) {
           if(c.match(/[a-zA-Z]/)) {
           this.token.push(c.toLowerCase())
           return this.endTagState
           }
           if(c==='>') {
           this.token.push(c)
           this.emitToken('endTag', this.token.join(''))
           this.token=[]
           return this.start
           }
          }
          復制代碼
          

          最后只有屬性標簽需要處理了,也就是上面看到的attrState函數,也處理三種情形

          • 如果是字母、單引號、雙引號、等號,則認定為依舊處于屬性標簽態
          • 如果遇到空格,則表示屬性標簽態結束,接下來進入新的屬性標簽態
          • 如果遇到>,則認定為屬性標簽態結束,接下來開始新的節點信息

          代碼如下

          HTMLLexicalParser.prototype.attrState=function(c) {
           if(c.match(/[a-zA-Z'"=]/)) {
           this.token.push(c)
           return this.attrState
           }
           if(c===' ') {
           this.emitToken('attr', this.token.join(''))
           this.token=[]
           return this.attrState
           }
           if(c==='>') {
           this.emitToken('attr', this.token.join(''))
           this.token=[]
           return this.start
           }
          }
          復制代碼
          

          最后我們提供一個parse解析函數,和可能用到的getOutPut函數來獲取結果即可,就不啰嗦了,上代碼

          HTMLLexicalParser.prototype.parse=function() {
           var state=this.start;
           for(var c of this.htmlString.split('')) {
           state=state.bind(this)(c)
           }
          }
          HTMLLexicalParser.prototype.getOutPut=function() {
           return this.tokens
          }
          復制代碼
          

          接下來簡單測試一下,對于<p class="a" data="js">測試并列元素的</p><p class="a" data="js">測試并列元素的</p>HTML 字符串,輸出結果為

          看上去結果很 nice,接下來進入語法分析步驟

          語法分析

          首先們需要考慮到的情況有兩種,一種是有多個根元素的,一種是只有一個根元素的。

          我們的節點有兩種類型,文本節點和正常節點,因此聲明兩個數據結構。

          function Element(tagName) {
           this.tagName=tagName
           this.attr={}
           this.childNodes=[]
          }
          function Text(value) {
           this.value=value || ''
          }
          復制代碼
          

          目標:將元素建立起父子關系,因為真實的 DOM 結構就是父子關系,這里我一開始實踐的時候,將 childNodes 屬性的處理放在了 startTag token 中,還給 Element 增加了 isEnd 屬性,實屬愚蠢,不但復雜化了,而且還很難實現。仔細思考 DOM 結構,token 也是有順序的,合理利用棧數據結構,這個問題就變的簡單了,將 childNodes 處理放在 endTag 中處理。具體邏輯如下

          • 如果是 startTag token,直接 push 一個新 element
          • 如果是 endTag token,則表示當前節點處理完成,此時出棧一個節點,同時將該節點歸入棧頂元素節點的 childNodes 屬性,這里需要做個判斷,如果出棧之后棧空了,表示整個節點處理完成,考慮到可能有平行元素,將元素 push 到 stacks。
          • 如果是 attr token,直接寫入棧頂元素的 attr 屬性
          • 如果是 text token,由于文本節點的特殊性,不存在有子節點、屬性等,就認定為處理完成。這里需要做個判斷,因為文本節點可能是根級別的,判斷是否存在棧頂元素,如果存在直接壓入棧頂元素的 childNodes 屬性,不存在 push 到 stacks。

          代碼如下

          function HTMLSyntacticalParser() {
           this.stack=[]
           this.stacks=[]
          }
          HTMLSyntacticalParser.prototype.getOutPut=function() {
           return this.stacks
          }
          // 一開始搞復雜了,合理利用基本數據結構真是一件很酷炫的事
          HTMLSyntacticalParser.prototype.receiveInput=function(token) {
           var stack=this.stack
           if(token.type==='startTag') {
           stack.push(new Element(token.value.substring(1)))
           } else if(token.type==='attr') {
           var t=token.value.split('='), key=t[0], value=t[1].replace(/'|"/g, '')
           stack[stack.length - 1].attr[key]=value
           } else if(token.type==='text') {
           if(stack.length) {
           stack[stack.length - 1].childNodes.push(new Text(token.value))
           } else {
           this.stacks.push(new Text(token.value))
           }
           } else if(token.type==='endTag') {
           var parsedTag=stack.pop()
           if(stack.length) {
           stack[stack.length - 1].childNodes.push(parsedTag)
           } else {
           this.stacks.push(parsedTag)
           }
           }
          }
          復制代碼
          

          簡單測試如下:

          沒啥大問題哈

          解釋執行

          對于上述語法分析的結果,可以理解成 vdom 結構了,接下來就是映射成真實的 DOM,這里其實比較簡單,用下遞歸即可,直接上代碼吧

          function vdomToDom(array) {
           var res=[]
           for(let item of array) {
           res.push(handleDom(item))
           }
           return res
          }
          function handleDom(item) {
           if(item instanceof Element) {
           var element=document.createElement(item.tagName)
           for(let key in item.attr) {
           element.setAttribute(key, item.attr[key])
           }
           if(item.childNodes.length) {
           for(let i=0; i < item.childNodes.length; i++) {
           element.appendChild(handleDom(item.childNodes[i]))
           }
           }
           return element
           } else if(item instanceof Text) {
           return document.createTextNode(item.value)
           }
          }
          復制代碼
          

          實現函數

          上面三步驟完成后,來到了最后一步,實現最開始提出的函數

          function html(element, htmlString) {
           // parseHTML
           var syntacticalParser=new HTMLSyntacticalParser()
           var lexicalParser=new HTMLLexicalParser(htmlString, syntacticalParser.receiveInput.bind(syntacticalParser))
           lexicalParser.parse()
           var dom=vdomToDom(syntacticalParser.getOutPut())
           var fragment=document.createDocumentFragment()
           dom.forEach(item=> {
           fragment.appendChild(item)
           })
           element.appendChild(fragment)
          }
          復制代碼
          

          三個不同情況的測試用例簡單測試下

          html(document.getElementById('app'), '<p class="a" data="js">測試并列元素的</p><p class="a" data="js">測試并列元素的</p>')
          html(document.getElementById('app'), '測試<div>你好呀,我測試一下沒有深層元素的</div>')
          html(document.getElementById('app'), '<div class="div"><p class="p">測試一下嵌套很深的<span class="span">p的子元素</span></p><span>p同級別</span></div>')
          復制代碼
          

          聲明:簡單測試下都沒啥問題,本次實踐的目的是對 DOM 這一塊通過詞法分析和語法分析生成 DOM Tree 有一個基本的認識,所以細節問題肯定還是存在很多的。

          總結

          其實在了解了原理之后,這一塊代碼寫下來,并沒有太大的難度,但卻讓我很興奮,有兩個成果吧

          • 了解并初步實踐了一下狀態機
          • 數據結構的魅力

          這篇文章中,作者將分享 12 個非常有用的 JavaScript 技巧,可以幫助你寫出簡潔且高性能的代碼。

          1. 過濾唯一值

          ES6 引入了 Set 對象和延展(spread)語法…,我們可以用它們來創建一個只包含唯一值的數組。

          const array=[1, 1, 2, 3, 5, 5, 1]
          const uniqueArray=[...new Set(array)];
          console.log(uniqueArray); // Result: [1, 2, 3, 5]
          

          在 ES6 之前,獲得同樣的數組需要更多的代碼!

          這個技巧可以支持包含原始類型的數組:undefined、null、boolean、string 和 number。但如果你的數組包含了對象、函數或其他嵌套數組,就不能使用這種方法了。

          2. 在循環中緩存數組長度

          在我們學習使用 for 循環時,一般建議使用這種結構:

          for (let i=0; i < array.length; i++){
           console.log(i);
          }
          

          在使用這種方式時,for 循環的每次迭代都會重復計算數組長度。

          有時候這個會很有用,但在大多數情況下,如果能夠緩存數組的長度會更好,這樣只需要計算一次就夠了。我們可以把數組長度復制給一個叫作 length 的變量,例如:

          for (let i=0, length=array.length; i < length; i++){
           console.log(i);
          }
          

          這段代碼和上面的差不多,但從性能方面來看,即使數組變得很大,也不需要花費額外的運行時重復計算 array.length。

          3. 短路求值

          使用三元運算符可以很快地寫出條件語句,例如:

          x > 100 ? 'Above 100' : 'Below 100';
          x > 100 ? (x > 200 ? 'Above 200' : 'Between 100-200') : 'Below 100';
          

          但有時候三元運算符仍然很復雜,我們可以使用邏輯運算符 && 和||來替代,讓代碼更簡潔一些。這種技巧通常被稱為“短路求值”。

          假設我們想要返回兩個或多個選項中的一個,使用 && 可以返回第一個 false。如果所有操作數的值都是 true,將返回最后一個表達式的值。

          let one=1, two=2, three=3;
          console.log(one && two && three); // Result: 3
          console.log(0 && null); // Result: 0
          

          使用||可以返回第一個 true。如果所有操作數的值都是 false,將返回最后一個表達式的值。

          let one=1, two=2, three=3;
          console.log(one || two || three); // Result: 1
          console.log(0 || null); // Result: null
          

          示例 1

          假設我們想要返回一個變量的 length,但又不知道變量的類型。

          我們可以使用 if/else 來檢查 foo 是否是一個可接受的類型,但這樣會讓代碼變得很長。這個時候可以使用短路求值:

          return (foo || []).length;
          

          對于上述兩種情況,如果變量 foo 具有 length 屬性,這個屬性的值將被返回,否則將返回 0。

          示例 2

          你是否曾經在訪問嵌套對象屬性時遇到過問題?你可能不知道對象或某個子屬性是否存在,所以經常會碰到讓你頭疼的錯誤。

          假設我們想要訪問 this.state 中的一個叫作 data 的屬性,但 data 卻是 undefined 的。在某些情況下調用 this.state.data 會導致 App 無法運行。為了解決這個問題,我們可以使用條件語句:

          if (this.state.data) {
           return this.state.data;
          } else {
           return 'Fetching Data';
          }
          

          但這樣似乎有點啰嗦,而||提供了更簡潔的解決方案:

          return (this.state.data || 'Fetching Data');
          

          4. 轉換成布爾值

          除了標準的布爾值 true 和 false,在 JavaScript 中,所有的值要么是“真值”要么是“假值”。

          在 JavaScript 中,除了 0、“”、null、undefined、NaN 和 false 是假值之外,其他的都是真值。

          我們可以使用! 云算法來切換 true 和 false。

          const isTrue=!0;
          const isFalse=!1;
          const alsoFalse=!!0;
          console.log(true); // Result: true
          console.log(typeof true); // Result: "boolean"
          

          5. 轉換成字符串

          要快速將數字轉換成字符串,我們可以使用 + 運算符,然后在后面跟上一個空字符串。

          const val=1 + "";
          console.log(val); // Result: "1"
          console.log(typeof val); // Result: "string"
          

          6. 轉換成數字

          要把字符串轉成數字,也可以使用 + 運算符。

          let int="15";
          int=+int;
          console.log(int); // Result: 15
          console.log(typeof int); Result: "number"
          

          也可以使用這種方式將布爾值轉成數字,例如:

          console.log(+true); // Return: 1
          console.log(+false); // Return: 0
          

          在某些情況下,+ 運算符會被解析成連接操作,而不是加法操作。對于這種情況,可以使用兩個波浪號:~~。

          一個波浪號表示按位取反操作,例如,~15 等于 -16。

          const int=~~"15"
          console.log(int); // Result: 15
          console.log(typeof int); Result: "number"
          

          使用兩個波浪號可以再次取反,因為 -(-n-1)=n+1-1=n,所以~-16 等于 15。

          7. 快速冪運算

          從 ES7 開始,可以使用 ** 進行冪運算,比使用 Math.power(2,3) 要快得多。

          console.log(2 ** 3); // Result: 8
          

          但要注意不要把這個運算符于 ^ 混淆在一起了,^ 通常用來表示指數運算,但在 JavaScript 中,^ 表示位異或運算。

          在 ES7 之前,可以使用位左移運算符 << 來表示以 2 為底的冪運算:

          // 以下表達式是等效的:
          Math.pow(2, n);
          2 << (n - 1);
          2**n;
          

          例如,2 << 3=16 等同于 2 ** 4=16。

          8. 快速取整

          我們可以使用 Math.floor()、Math.ceil() 或 Math.round() 將浮點數轉換成整數,但有另一種更快的方式,即使用位或運算符 |。

          console.log(23.9 | 0); // Result: 23
          console.log(-23.9 | 0); // Result: -23
          

          | 的實際行為取決于操作數是正數還是負數,所以在使用這個運算符時要確保你知道操作數是正是負。

          如果 n 是正數,那么 n|0 向下取整,否則就是向上取整。它會移除小數部分,也可以使用~~ 達到同樣的效果。

          移除整數尾部數字

          | 運算符也可以用來移除整數的尾部數字,這樣就不需要像下面這樣:

          let str="1553"; 
          Number(str.substring(0, str.length - 1));
          

          相反,我們可以這樣:

          console.log(1553 / 10 | 0) // Result: 155
          console.log(1553 / 100 | 0) // Result: 15
          console.log(1553 / 1000 | 0) // Result: 1
          

          9. 自動類綁定

          在 ES6 中,我們可以使用箭頭進行隱式綁定,這樣可以為類的構造器省下一些代碼,并跟一些重復出現的表達式說再見,比如 this.myMethod=this.myMethod.bind(this)。

          import React, { Component } from React;
          export default class App extends Compononent {
           constructor(props) {
           super(props);
           this.state={};
           }
          myMethod=()=> {
           // This method is bound implicitly!
           }
          render() {
           return (
           <>
           <div>
           {this.myMethod()}
           </div>
           </>
           )
           }
          };
          

          10. 截取數組

          如果你想從一個數組尾部移除某些元素,可以使用一種比 splice() 更快的方法。

          例如,如果你知道初始數組的大小,可以像下面這樣重新定義它的 length 屬性:

          let array=[0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
          array.length=4;
          console.log(array); // Result: [0, 1, 2, 3]
          

          這顯然是一種更簡潔的解決方案。不過,我發現 slice() 的運行速度更快,所以,如果你更看重速度,可以像下面這樣:

          let array=[0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
          array=array.slice(0, 4);
          console.log(array); // Result: [0, 1, 2, 3]
          

          11. 獲取數組最后的元素

          數組的 slice() 方法可以接受負整數,并從數組的尾部開始獲取元素。

          let array=[0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
          console.log(array.slice(-1)); // Result: [9]
          console.log(array.slice(-2)); // Result: [8, 9]
          console.log(array.slice(-3)); // Result: [7, 8, 9]
          

          12. 格式化 JSON

          你之前可能使用過 JSON.stringify,但你是否知道它還可以用來給 JSON 添加縮進?

          stringify() 方法可以接受兩個額外的參數,一個是函數(形參為 replacer),用于過濾要顯示的 JSON,另一個是空格個數(形參為 space)。

          space 可以是一個整數,表示空格的個數,也可以是一個字符串(比如’\t’表示制表符),這樣得到的 JSON 更容易閱讀。

          console.log(JSON.stringify({ alpha: 'A', beta: 'B' }, null, '\t'));
          // Result:
          // '{
          // "alpha": A,
          // "beta": B
          // }'
          

          英文原文:https://medium.com/@bretcameron/12-javascript-tricks-you-wont-find-in-most-tutorials-a9c9331f169d


          主站蜘蛛池模板: 日本v片免费一区二区三区 | 狠狠做深爱婷婷综合一区| 日韩综合无码一区二区| 激情内射日本一区二区三区| 国产成人一区二区三区视频免费| 亚洲国产av一区二区三区| 国产成人无码AV一区二区在线观看| 精品伦精品一区二区三区视频| 久久青草国产精品一区| 精品国产乱码一区二区三区| 国产伦精品一区二区三区不卡 | 一区二区三区精品高清视频免费在线播放 | 性色av闺蜜一区二区三区| 国产一区二区三区不卡在线看| 中文字幕一区二区三区四区 | 国精产品一区一区三区免费视频| 亚洲爆乳精品无码一区二区三区| 亚洲综合激情五月色一区| 国产suv精品一区二区6| 国产成人AV一区二区三区无码 | 国产高清一区二区三区视频| 国产精品区AV一区二区| 亚洲一区在线观看视频| 福利一区国产原创多挂探花| 色噜噜狠狠一区二区三区果冻 | 天码av无码一区二区三区四区 | 韩国精品福利一区二区三区| 亚洲国产高清在线一区二区三区| 无码人妻久久一区二区三区免费 | 亚洲国产精品一区第二页| 在线观看中文字幕一区| 国产一区二区女内射| 北岛玲在线一区二区| 亚洲视频在线一区二区三区| 中文字幕一区二区三区精华液| 成人区人妻精品一区二区三区 | 日本一区二区三区不卡在线视频 | 国模无码视频一区| 久久国产精品亚洲一区二区| 欧洲精品码一区二区三区免费看| 亚洲AV无码一区二区三区在线 |