DD每週前端七題詳解-第五期

語言: CN / TW / HK

DD每週前端七題詳解-第五期

系列介紹

你盼世界,我盼望你無bug。Hello 大家好!我是霖呆呆!

呆呆每週都會分享七道前端題給大家,系列名稱就是「DD每週七題」。

系列的形式主要是:3道JavaScript + 2道HTML + 2道CSS,幫助我們大家一起鞏固前端基礎。

所有題目也都會整合至 LinDaiDai/niubility-coding-jsissues中,歡迎大家提供更好的解題思路,謝謝大家😁。

一起來看看本週的七道題吧。

正題

一、實現mask函式將"123456"轉為"##3456",只保留最後四個字元

(題目來源:github.com/30-seconds/…)

首先介紹一下題目的意思吧😄,案例🌰如下:

const mask = (str, maskChar = '#') => {
 // 程式碼
}
console.log(mask('123456')); // '##3456'
console.log(mask('lindaidai')); // '#####idai'
複製程式碼

這道題的難度應該沒有那麼大,處理方式也有很多。呆呆這邊主要是講解一下如何使用padStart來實現的。

簡單介紹一下padStart方法吧,它是ES8新增的例項函式,與它作用差不多的還有一個叫padEnd的函式:

  • String.prototype.padStart
  • String.prototype.padEnd

作用:允許將空字串或其他字串新增到原始字串的開頭或結尾。

語法

padStart(targetLength, [padString])
複製程式碼
  • targetLength: 必填,當前字串需要填充到的目標長度。如果這個數值小於當前字串的長度,則返回當前字串本身。
  • padString: 可選,填充字串。如果字串太長,使填充後的字串長度超過了目標長度,則只保留最左側的部分,其他部分會被截斷,此引數的預設值為" "

例如:

'x'.padStart(4, 'ab') // 'abax'
'x'.padEnd(5, 'ab') // 'xabab'
複製程式碼

哈哈,另外想要了解如何實現一個padStart的小夥伴可以看呆呆之前一篇文章喲:DD每週前端七題詳解-第二期

言歸正傳,讓我們回到這道題目哈,首先讓我們來處理一下輸入引數邊界的情況,例如輸入的str不存在或者長度小於4的時候:

const mask = (str, maskChar = '#') => {
 if (!str || str.length <= 4) return str;
}
複製程式碼

其次,我們可以只保留住str的末尾四個字元,然後使用padStart將這四個字元填充至str.length即可,如下:

const mask = (str, maskChar = '#') => {
  if (!str || str.length <= 4) return str;
  return str.slice(-4).padStart(str.length, maskChar);
}
複製程式碼
  • slice不會影響原本的字串
  • 使用padStart填充即可

github.com/LinDaiDai/n…

二、介紹一下NaN並實現一個isNaN

介紹一下NaN

  • NaN屬性是代表非數字值的特殊值,該屬性用於指示某個值不是數字;
  • NaN是不等於NaN的,即NaN === NaN的結果是false
  • 使用Object.is()來比較兩個NaN結果是true,即Object.is(NaN, NaN)的結果是true
  • typeof NaN"number"
  • 方法parseInt()parseFloat()在不能解析指定的字串時就返回這個值;
  • 可以使用isNaN來判斷一個變數是不是NaN,它是JS內建物件Number上的靜態方法。

(關於第三點,大家可以看一下我之前的一篇文章喲,裡面的「第二補:JS型別檢測-Object.is()和===的區別」有提到:讀《三元-JS靈魂之問》總結,給自己的一份原生JS補給(上))

實現一個isNaN:

對於isNaNpolyfill實現起來就比較簡單了,只需要利用NaN不等於它自身的這一點即可:

const isNaN = v => v !== v;
複製程式碼

github.com/LinDaiDai/n…

三、按位取反,為什麼~2 = -3?

接下來,分享一道與JavaScript原生無關的題目吧,主要也是看到群裡有小夥伴問了關於按位取反~的用法,這邊統一科普一下,😁。

正常一個數字,例如12,或者-1-2

如果我們對它們進行按位取反的話,結果會是這樣:

  • ~1 = -2
  • ~2 = -3
  • -1 = 0
  • -2 = 1

看不懂沒關係,讓我們來一步步看看實現的過程哈。

在這裡其實是分了正數和負數的,因為符號不同取反的過程也會不同。

1.1 正數按位取反

先讓我們來看看正數的按位取反。

比如先看看~1 = -2,過程如下:

1. 十進位制轉為二進位制原碼

首先將十進位制的1轉化為二進位制原碼為:0000 0001

2. 二進位制原碼按位取反

之後將原碼按位取反:

也就是將0000 0001 => 1111 1110

(取反應該知道啥意思吧?就是0換成11換成0)

3. 取反後的二進位制轉為原碼

再將取反後的二進位制碼轉為原碼二進位制:

也就是將1111 1110 => 1000 0010

這裡你估計看著都點懵了,當我們將取反後的二進位制轉為原碼二進位制的時候,其實是有以下兩步的:

  1. 需要判斷取反後的二進位制的第一個位是不是1,這個第一位我們稱之為符號位,如果是1的話就表示即將要轉成的數是一個負數,如果是0的話表示即將要轉的數是一個正數,這個符號位是不能動的;在這裡我們可以看到1111 1110的第一位是1,所以表示即將要轉的數是一個負數,同時我們不動它。
  2. 然後將除了第一位以外其它位數取反並+1。所以會有這麼兩個過程:
    • 1111 1110 => 1000 0001
    • 1000 0001 => 1000 0010 (這步是對上一步的結果+1,因為上一步的最後一個數是1,所以它再加上1就需要向前進一位了,因此變成了1000 0010)

4. 將原碼二進位制轉為十進位制

最後一步就是將我們前面得到的1000 0010這個二進位制轉化為十進位制了。

第一位符號位,是1,則表示是個負數,所以結果為-2

OK👌,搞懂了這個步驟之後再讓我們自己來轉換一下~2 = -3吧:

1. 0000 0010
2. 1111 1101
3. 1000 0011
4. -3
複製程式碼

正數按位取反總結

  1. 十進位制轉為二進位制原碼
  2. 二進位制原碼按位取反
  3. 符號位保留,其餘位取反+1
  4. 二進位制原碼轉為十進位制

1.2 負數按位取反

負數的按位取反和上面就有些不一樣了,主要是第二步和第三步調換一下順序:

  1. 十進位制轉為二進位制原碼
  2. 符號位保留,其餘位取反+1
  3. 二進位制原碼按位取反
  4. 二進位制原碼轉為十進位制

例如:~-1 =0的轉換過程:

1. 十進位制轉為二進位制原碼

這步和正數按位取反是一樣的:

-1 => 1000 0001

2. 符號位保留,其餘位取反+1

轉換過程:

  • 1000 0001 => 1111 1110 (取反)
  • 1111 1110 => 1111 1111 (取反後 + 1)

3. 二進位制原碼按位取反

將剛剛得到的再進行按位取反:

1111 1111 => 0000 0000

4. 二進位制原碼轉為十進位制

0000 0000 => 0

OK👌,現在自己來轉換一下~-2 = 1吧:

1. 1000 0010
2. 1111 1110
3. 0000 0001
4. 1
複製程式碼

這裡沒啥訣竅,關鍵就是要記住轉換的過程然後不斷的練習吧 😂。

另外關於~~的用法還可以看呆呆的另一篇文章喲《JS中按位取反運算子~及其它運算子》

github.com/LinDaiDai/n…

四、知道insertAdjacentHTML方法嗎?

這個方法是呆呆最近在看公司專案程式碼時瞭解到的,之前一直沒有注意它。

首先對於它的用法:

insertAdjacentHTML() 方法將指定的文字解析為 Element 元素,並將結果節點插入到DOM樹中的指定位置。它不會重新解析它正在使用的元素,因此它不會破壞元素內的現有元素。這避免了額外的序列化步驟,使其比直接使用innerHTML操作更快。

其次它的作用物件是一個元素element,例如const container = document.getElementById('container')

語法上呢:

element.insertAdjacentHTML(position, text);
複製程式碼
  • position:一個DOMString,也就是表示插入內容相對元素的位置,且必須是下面的字串之一:
    • 'beforebegin':元素自身的前面。
    • 'afterbegin':插入元素內部的第一個子節點之前。
    • 'beforeend':插入元素內部的最後一個子節點之後。
    • 'afterend':元素自身的後面。
  • text:是要被解析為HTML或XML元素,並插入到DOM樹中的 DOMString

案例

讓我們來看看它的用法,例如🌰現在有一個HTML的結構為:

<div id="one">我是one</div>
複製程式碼

JavaScript程式碼中加上這段話:

const one = document.getElementById('one');
one.insertAdjacentHTML('afterend', '<div id="two">我是two</div>');
複製程式碼

現在最終的渲染結果就變成了這樣:

<div id="one">我是one</div><div id="two">我是two</div>
複製程式碼

工作上的用法

在專案中,主要可以應用於這樣的場景:一個空的容器(你可以理解為一個div),開始需要一個loading的效果,在資料載入完畢之後,需要把loading取掉且清空容器內的元素並以其它方式重新渲染出容器的內容。

這裡呆呆就以定時器來模擬一下資料載入的過程,實現程式碼如下:

<body>
 <div id="container"></div>
</body>
<script>
 const container = document.getElementById('container');
  const loading = '<div id="loading">loading</div>'; // loading可能是一個元件
  container.insertAdjacentHTML('beforeend', loading);
  setTimeout(() => {
    container.innerHTML = ''
  }, 2000)
</script>
複製程式碼

(當然,我們不要為了刻意用而去用,適合自己的才是最好的)

安全問題

  • 使用 insertAdjacentHTML 插入使用者輸入的HTML內容的時候,需要轉義之後才能使用。

    例如:

    const one = document.getElementById('one');
    // 由於 encodeURI('<div id="two">我是two</div>')會被轉譯為:
    // %3Cdiv%20id=%22two%22%3E%E6%88%91%E6%98%AFtwo%3C/div%3E
    // 因此最終會被當成 "%3Cdiv%20id=%22two%22%3E%E6%88%91%E6%98%AFtwo%3C/div%3E"字串渲染
    one.insertAdjacentHTML('afterend', encodeURI('<div id="two">我是two</div>'));
    複製程式碼
  • 如果只是為了插入文字內容(而不是HTML節點),不建議使用這個方法,建議使用node.textContent 或者 node.insertAdjacentText()。因為這樣不需要經過HTML直譯器的轉換,效能會好一點。(這裡是引用的MDN-insertAdjacentHTML上的內容)

github.com/LinDaiDai/n…

五、insertAdjacentHTMLinsertAdjacentElement的區別

第二個引數的型別不同, 前者接收的是是要被解析為HTML或XML元素的字串,而後者接收的是一個element元素。

const one = document.getElementById('one');
one.insertAdjacentHTML('afterend', '<div id="two">我是two</div>');

const one = document.getElementById('one');
const two = document.createElement('div')
two.innerHTML = '我是two';
one.insertAdjacentElement('afterend', two);
複製程式碼

github.com/LinDaiDai/n…

六、實現九宮格佈局

實現效果如下:

先來看一下HTML方面的程式碼:

<div id="container">
    <div class="item item-1">1</div>
    <div class="item item-2">2</div>
    <div class="item item-3">3</div>
    <div class="item item-4">4</div>
    <div class="item item-5">5</div>
    <div class="item item-6">6</div>
    <div class="item item-7">7</div>
    <div class="item item-8">8</div>
    <div class="item item-9">9</div>
</div>
複製程式碼

還有一些item上的基礎css程式碼:

#container {
    /* css程式碼 */
}

.item {
    font-size: 2em;
    text-align: center;
    border: 1px solid #e5e4e9;
}

.item-1 {
    background-color: #ef342a;
}

.item-2 {
    background-color: #f68f26;
}

.item-3 {
    background-color: #4ba946;
}

.item-4 {
    background-color: #0376c2;
}

.item-5 {
    background-color: #c077af;
}

.item-6 {
    background-color: #f8d29d;
}

.item-7 {
    background-color: #b5a87f;
}

.item-8 {
    background-color: #d0e4a9;
}

.item-9 {
    background-color: #4dc7ec;
}
複製程式碼

方案一

第一種方案可以使用浮動+百分比:

#container {
    width: 150px;
    height: 150px;
}
.item {
    float: left;
    width: 33.33%;
    height: 33.33%;
    box-sizing: border-box;
    font-size: 2em;
    text-align: center;
    border: 1px solid #e5e4e9;
}
複製程式碼

方案二

還可以使用flex佈局的方式:

#container {
    width: 150px;
    height: 150px;
    display: flex;
    flex-wrap: wrap;
}
.item {
    width: 33.33%;
    height: 33.33%;
    box-sizing: border-box;
    font-size: 2em;
    text-align: center;
    border: 1px solid #e5e4e9;
}
複製程式碼

方案三

另外的話,也許還可以試試grid

#container {
    display: grid;
    grid-template-columns: 50px 50px 50px;
    grid-template-rows: 50px 50px 50px;
}
.item {
    font-size: 2em;
    text-align: center;
    border: 1px solid #e5e4e9;
}
複製程式碼

答案參考:github.com/haizlin/fe-…

github.com/LinDaiDai/n…

七、說說will-change

will-changeCSS3新增的標準屬性,它的作用很單純,就是"增強頁面渲染效能",當我們在通過某些行為觸發頁面進行大面積繪製的時候,瀏覽器往往是沒有準備,只能被動的使用CUP去計算和重繪,由於事先沒有準備,對於一些複雜的渲染可能會出現掉幀、卡頓等情況。

will-change則是在真正的行為觸發之前告訴瀏覽器可能要進行重繪了,相當於瀏覽器把CUP拉上了,能從容的面對接下來的變形。

常用的語法主要有:

  • whil-change: scroll-position; 即將開始滾動
  • will-change: contents; 內容要動畫或者變化了
  • will-transform; transform相關的屬性要變化了(常用)

注意:

  • will-change雖然可以開啟加速,但是一定要適度使用
  • 開啟加速的代價為手機的耗電量會增加
  • 使用時遵循最小化影響原則,可以對偽元素開啟加速,獨立渲染
  • 可以寫在偽類中,例如hover中,這樣移出元素的時候就會自動removewill-change
  • 如果使用JS添加了will-change,注意要及時remove掉,方式就是style.willChange = 'auto'

github.com/LinDaiDai/n…

參考文章

知識無價,支援原創。

參考文章:

後語

你盼世界,我盼望你無bug。這篇文章就介紹到這裡。

您每週也許會花48小時的時間在工作💻上,會花49小時的時間在睡覺😴上,也許還可以再花20分鐘的時間在呆呆的7道題上,日積月累,我相信我們都能見證彼此的成長😊。

什麼?你問我為什麼系列的名字叫DD?因為呆呆呀,哈哈😄。

喜歡霖呆呆的小夥還希望可以關注霖呆呆的公眾號 LinDaiDai 或者掃一掃下面的二維碼👇👇👇。

img
img

我會不定時的更新一些前端方面的知識內容以及自己的原創文章🎉

你的鼓勵就是我持續創作的主要動力 😊。

本文使用 mdnice 排版