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

語言: CN / TW / HK

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

系列介紹

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

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

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

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

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

正題

一、[,,,]的長度

(題目來源:https://github.com/CavsZhouyou/Front-End-Interview-Notebook)

咋了小夥伴們,感覺這道題目很簡單是嗎?哈哈,數一數逗號的間隙好像就能得出答案了,比如這樣:

但是這道題的答案並不是4喲,而是3

console.log([,,,].length) // 3
複製程式碼

所以最終我們是需要把它想象成這樣的:

也就是最後一個逗號的後面是不算一項的。

這裡其實涉及到了一個名為:尾後逗號的概念,或者說是叫做終止逗號。上面👆這道題好像看不出它有什麼作用,讓我來看看實際上為什麼會有這個用法。

比如現在你的專案中這麼一個檔案:

config.js:

const types = [
  {
    name: '帥'
  },
  {
    name: '陽光'
  },
]
export { types };
複製程式碼

大家可以看到,我在陽光這一項的的後面是多加了一個","的,此時我將這個程式碼提交到git上並標記為版本1

如果這時候types中又要新增一項名為"可愛"的配置項,我只需要在它下面再加上就行了,不需要去改動到原來程式碼,此時你提交的程式碼的diff是長這樣的:

const types = [
  {
    name: '帥'
  },
  {
    name: '陽光'
  },
+ {
+   name: '可愛'
+ },
]
export { types };
複製程式碼

大家可以看到,只有簡單的三行增量程式碼,如果你沒有使用尾後逗號的話,你提交的程式碼的diff會是這樣:

const types = [
  {
    name: '帥'
  },
  {
    name: '陽光'
- }
+ },
+ {
+   name: '可愛'
+ }
]
export { types };
複製程式碼

因此我們可以得出尾後逗號它的作用:

使得版本控制更加清晰,以及程式碼維護麻煩更少。

(當然,這種用法在.json字尾的檔案中是不能用的哈,因為JSON它嚴格遵循它自己的語法要求)

所以迴歸到這道題中來,像這種使用了多於一個尾後逗號的陣列,我們就稱之為稀疏陣列,稀疏陣列它的長度是等於逗號的數量的。

因此:

console.log([,,,].length) // 3
複製程式碼

github.com/LinDaiDai/n…

二、如何判斷當前指令碼執行在瀏覽器還是 node 環境中?

這道題呆呆其實在很多地方都看到了,但是有的回答好像並不那麼靠譜。

回答這道題首先我們需要知道一個概念:

瀏覽器環境:全域性物件為window;而在node環境下,是有一個名為global的物件,它的內部Class屬性是為"global"

內部Class屬性也就是我們通過Object.prototype.call(obj)這種方式來獲取到的內容,比如:

console.log(Object.prototype.toString.call([1, 2, 3])); // "[object Array]"
複製程式碼

(關於它的用法呆呆在《【精】從206個console.log()完全弄懂資料型別轉換的前世今生(上)》中的toString用法時說的也很詳細咯)

因此我們可以得出這種判斷方式:

var isBrowser = typeof window !== 'undefined'
    && ({}).toString.call(window) === '[object Window]';

var isNode = typeof global !== "undefined"
    && ({}).toString.call(global) == '[object global]';
複製程式碼

({}).toString.call()Object.prototype.toString.call()用法一致,只不過在{}的外面最好加上一個(),也是為了預防JS將大括號{}認為是一個空的程式碼塊(額,呆呆試了一下貌似也沒有這方面的問題)。

github.com/LinDaiDai/n…

三、reduce方法有初始值和沒有初始值的區別?

reduce函式的第一個引數是一個回撥函式,第二個引數為可選的初始值。

如果有初始值的話,回撥函式就會從陣列的第0項開始執行,也就是會執行arr.length次;

但是如果沒有初始值的話,會預設取陣列的第0項為初始值,回撥函式會從陣列的第1項開始執行,也就是會執行arr.length - 1次。

這點從我們手寫一個reduce的實現就可以看出來,程式碼如下:

Array.prototype.MyReduce = function (fn, initialValue) {
  var arr = Array.prototype.slice.call(this);
  var pre, startIndex;
  pre = initialValue ? initialValue : arr[0];
  startIndex = initialValue ? 0 : 1;
  for (var i = startIndex; i < arr.length; i++) {
    pre = fn.call(null, pre, arr[i], i, this)
  }
  return pre
}
複製程式碼

過程分析:

  • 首先,map、reduce這種方法都是陣列原型物件上的方法,所以我將MyReduce定義在Array.prototype 上,這樣你就可以直接使用ary.MyReduce()這樣的方式呼叫它了(ary是一個類似於這樣的陣列[1, 2, 3])。
  • 對於引數,我們參考原生reduce,它接收的第一個引數是一個回撥函式,第二個是初始值
  • var arr = ...的作用是獲取呼叫MyReduce函式的那個變數,也就是說this會指向那個變數,例如ary.MyReduce(),那麼此時this就為ary
  • 至於為什麼不使用var arr = this;的方式而是使用Array.prototype.slice.call(this),算是實現一個淺拷貝吧,因為reduce是不會改變原陣列的。
  • 然後就是定義傳入reduce中的回撥函式的第一個引數pre,也就是上一次執行結果的返回值,可以看到這裡就用到了初始值initialValue,如果存在初始值就取初始值,不存在則預設取陣列第0項。(當然這裡直接用initialValue ?來判斷存不存在並不準確,因為我們知道0也會被判斷為false)
  • 接著是定義迴圈開始的下標startIndex,若是不存在初始值,則初始值是會取陣列中的第0項的,相當於第0項並不需要執行,所以startIndex會是1,而如果有初始值的話則需要將陣列的每一項都經過fn執行一下。
  • 最後,for迴圈中使用fn.call()來呼叫fn函式,並且最後一個引數是要把原來的陣列傳遞到回撥函式中,也就是這裡的this

github.com/LinDaiDai/n…

四、form表單中的label標籤的作用?

label標籤不會向用戶呈現任何特殊效果,它的作用是為滑鼠使用者改進了可用性。

也就是說當你使用了一個label標籤和一個input繫結起來之後,點選label標籤上的文字就會自動聚焦到input上。

如下:

繫結的方式:

  • label標籤上設定for屬性
  • input標籤上設定和for屬性一樣的id

例如:

<label for="username">username:</label>
<input type="text" name="username" id="username"/>
複製程式碼

這裡有兩點需要注意的:

  • 這兩個標籤不一定非要在form標籤內才會生效
  • for是和id對應的,並不是和name

github.com/LinDaiDai/n…

五、HTML5中的自動完成功能autocomplete是做什麼的?

大家在聽到自動完成這個詞,會有一點迷糊,自動完成什麼?

其實這個功能的作用是這樣的:

首先,autocomplete是一個屬性,這個屬性可以設定在form標籤上,也可以設定在其它的input標籤上。

被設定了自動完成的標籤,會允許瀏覽器預測對欄位的輸入。也就是說瀏覽器會根據你在這個輸入框中已經輸入過的值,留有一個"歷史記錄",方便我們下次還想要輸入同樣的值。

例如下面這段程式碼:

<form autocomplete="on">
  testaccount: <input type="text" name="testaccount" /><br />
  testpassword: <input type="text" name="testpassword" autocomplete="off" /><br />
  <input type="submit" />
</form>
複製程式碼
  • 我把form標籤的autocomplete開啟,那麼這個表單下的所有元素都開啟了autocomplete
  • 接著我把testpassword這一項的autocomplete關閉。

此時testaccout是有自動完成功能的,而testpassword沒有。那麼當我們第一次在這兩個輸入框中輸入了內容並提交後。再次點選testaccout輸入框,就會出現我們上一次輸入並提交的那個值,而點選testpassword時卻沒有。

效果如下:

所以我們來做下總結吧😊:

  • autocomplete屬性規定輸入欄位是否應該啟用自動完成功能;
  • 它的預設值是啟用,也就是"on",另一個值是"off"關閉;
  • 作用是:允許瀏覽器預測對欄位的輸入。當用戶在欄位開始鍵入時,瀏覽器基於之前鍵入過的值,應該顯示出在欄位中填寫的選項。
  • autocomplete 屬性適用於<form>,以及下面的 <input> 型別:text, search, url, telephone, email, password, datepickers, range , color

github.com/LinDaiDai/n…

六、CSS中的visibility有個collapse屬性值是幹嘛用的?

visibility會有這麼幾個個屬性值:

  • visible
  • hidden
  • collapse
  • inherit

比較常用的可能是前面兩個,用於控制元素的顯示隱藏。且我們知道,設定為hidden是會隱藏元素,但是其他元素的佈局不改變,相當於此元素變成透明。

inherit則是從父元素繼承visibility屬性的值。

而對於collapse,可能用的不是特別多,我們先來看下它的介紹:

  • 對於一般的元素,它的表現跟hidden是一樣的;
  • 如果這個元素是table相關的元素,例如table行,table group,table列,table column group,它的表現卻跟display: none一樣,也就是說,它們佔用的空間也會釋放。

下面一起來看看這個案例😊:

html程式碼

<table>
  <tr class="tr1">
    <td>
      tr1
    </td>
    <td>
      tr1
    </td>
  </tr>
  <tr>
    <td>
      tr2
    </td>
    <td>
      tr2
    </td>
  </tr>
</table>
複製程式碼

css程式碼

table {
  border: 1px solid red;
}
td {
  border: 1px solid blue;
}
.tr1 {

}
複製程式碼

我在沒給.tr1設定任何屬性的時候,頁面呈現的效果是這樣的,很正常:

如果設定了visibility: hidden

.tr1 {
  visibility: hidden;
}
複製程式碼

效果如下:

雖然第一行被隱藏了,但是它的空間還是在的,就像是"隱身"了一樣。

而如果設定了visibility: collapse

.tr1 {
  visibility: collapse;
}
複製程式碼

效果如下:

最終的效果會和display: none;一樣。

如果你問呆呆這屬性有什麼實際的用處沒有...咳咳,抱歉,好像還真沒有😅。也可能是呆呆不知道,知道的小夥伴還請提出喲,一起學習一哈。

github.com/LinDaiDai/n…

七、塊狀元素width:100%與width:auto的區別?

這兩個屬性值相信大家都不陌生了,先讓我們來看個案例理解一下:

html程式碼

<div class="super">
  <div class="sub1">
    我是呆呆的第一個崽
  </div>
  <div class="sub2">
    我是呆呆的第二個崽
  </div>
  <div class="sub3">
    我是呆呆的第三個崽
  </div>
</div>
複製程式碼

css程式碼:

.super {
  width: 200px;
  height: 100px;
  background: skyblue;
}
.sub1 {
  background: #d0e4a9;
}
.sub2 {
  width: 100%;
  background: #c077af;
}
.sub3 {
  width: auto;
  background: #f8d29d;
}
複製程式碼

最開始時,三個崽表現的效果是一樣的,並無很大差別:

此時如果我們對後面兩個崽做一下改動:

.sub2 {
  width: 100%;
  padding-left: 10px;
  margin-left: 10px;
  background: #c077af;
}
.sub3 {
  width: auto;
  padding-left: 10px;
  margin-left: 10px;
  background: #f8d29d;
}
複製程式碼

給他們都加上左內邊距和左外邊距,此時的效果就變成了這樣:

大家會發現,設定了width: 100%的崽,它的寬度會和父級的一樣,此時如果再給他設定了額外的padding,那它就會比父級還要寬了,也就是會超出父級容器。而因為還設定了margin-left,所以左邊也會有一段距離。

但是設定了width: auto的崽就比較乖了,無論怎樣它都不會超出父級,而是選擇壓縮自己的寬度。

因此我們可以得出結論:

  • 兩者的計算方式不同(這裡的計算規則都是基於box-sizing: content-box;的情況):
    • 對於width: auto;它的總寬度是等於父寬度的(這裡的父寬度是指父級內容的寬度不包括padding、border、margin),即使給元素設定了padding、border、margin等屬性,它也會自動分配水平空間。
    • 對於width: 100%;它表示的是元素內容的寬度等於父寬度,所以它的總寬度是有可能超過父級的,因為如果設定了額外的padding、border,就可能比父級寬了。
  • 無論是width:100%還是auto,其計算的參照都是父級內容區width值,而非總寬度值,也就是不包括padding、border、margin
  • 一般width:auto使用的多一些,因為這樣靈活;而width:100%使用比較少,因為在增加padding或者margin的時候,容易使其突破父級框,破壞佈局。

github.com/LinDaiDai/n…

參考文章

知識無價,支援原創。

參考文章:

後語

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

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

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

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

img
img

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

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

往期題目可以戳下面👇:

或者你也可以檢視github上的issues「我是issues」

本文使用 mdnice 排版