DD每週七題-第一期

語言: CN / TW / HK

DD每週七題-第一期

系列介紹

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

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

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

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

正題

一、Array(3)和Array(3, 4)的區別?

console.log(Array(3))
console.log(Array(3, 4))

console.log(new Array(3))
console.log(new Array(3, 4))

console.log(Array.of(3))
console.log(Array.of(3, 4))
複製程式碼

考察知識點:

  • Array()new Array()
  • Array()引數個數不同時的不同表現
  • Array.of()的作用

結果:

console.log(Array(3)) // [empty x 3]
console.log(Array(3, 4)) // [3, 4]

console.log(new Array(3)) // [empty x 3]
console.log(new Array(3, 4)) // [3, 4]

console.log(Array.of(3)) // [3]
console.log(Array.of(3, 4)) // [3, 4]
複製程式碼

總結:

  • Array使不使用new效果都是一樣的
  • Array方法,如果引數是一位的話,這個引數表示的是陣列的長度,並建立此長度的空陣列
  • Array方法,如果引數是多位的話則每一個引數都是陣列的一項,會按順序返回陣列
  • Array.of()接收任意個引數,將按順序成為返回陣列中的元素,並返回這個新陣列。

github.com/LinDaiDai/n…

二、請建立一個長度為100,值為對應下標的陣列

// cool點的寫法:
[...Array(100).keys()]

// 其他方法:
Array(100).join(",").split(",").map((v, i) => i)
Array(100).fill().map((v, i) => i)
複製程式碼

後面兩種方法相信大家都能看到懂,也沒啥好說的了😄,讓我們仔細看一下這個比較cool的寫法:

  • 使用Array(100)建立一個內容全為empty的陣列:[empty x 100]

  • 使用keys()方法從陣列建立一個包含陣列鍵的可迭代物件:

    可迭代物件是不是讓你想到了generator,沒錯的,這裡的keys()Array.prototype上的方法,大家可能會把它和我們之前用的比較多的Object.keys()搞混。

    說一下它的作用吧,它其實就像剛剛介紹的一樣,會建立一個可迭代物件,那麼小夥伴們應該知道,一個可迭代物件返回的資料會是這樣的:

    { value: 0, done: false }
    複製程式碼

    value為這次的返回值,done為當前可迭代物件的邏輯塊是否執行完成。

    所以你會看到以下這段程式碼是會這樣執行的:

    let it = Array(100).keys()
    console.log(it.next) // {value: 0, done: false}
    console.log(it.next) // {value: 1, done: false}
    console.log(it.next) // {value: 2, done: false}
    console.log(it.next) // {value: 3, done: false}
    複製程式碼
  • 至於[...arr]這個就是ES6的寫法了,轉化為陣列,當然你也可以直接用Array.from()。這兩種方式都支援將可迭代物件轉換為陣列。

github.com/LinDaiDai/n…

三、實現 arr[-1] = arr[arr.length - 1]

這道題的意思是:提供一個createArr()方法,用此方法建立的陣列滿足arr[-1] = arr[arr.length - 1]

function createArr (...elements) {
  // ...程式碼
  return arr
}
var arr1 = createArr(1, 2, 3)
console.log(arr1[-1]) // 3
console.log(arr1[-2]) // 2
複製程式碼

解題思路:

其實對於這類題目,我首先想到的會是Object.defineProperty()或者Proxy。因為這裡涉及到了對陣列值的獲取,顯然用Proxy是比較合適的。什麼?你問我為什麼不用Object.defineProperty()?因為這個方法是針對於物件的某一個屬性的呀,對陣列來說不合適。

所以對於這道題,我們也許可以使用Proxy代理每次傳入進來的下標,也就是重寫一下陣列的get方法,在這個方法中我們去處理這方面的邏輯,一起來看看程式碼吧😊:

function createArr (...elements) {
  let handler = {
    get (target, key, receiver) { // 第三個引數傳不傳都可以
      let index = Number(key) // 或者 let index = ~~key
      if (index < 0) {
        index = String(target.length + index)
      }
      return Reflect.get(target, index, receiver)
    }
  }
  let target = [...elements] // 建立一個新陣列
  return new Proxy(target, handler)
}
var arr1 = createArr(1, 2, 3)
console.log(arr1[-1]) // 3
console.log(arr1[-2]) // 2
複製程式碼

注意點:

  • get接收到的第二個引數key表示的是陣列下標,它是字串形式,所以需要轉為Number,當然方法有很多種了,使用Number()也可以,使用~~雙非按位取反運算子也可以。對比於Number()的好處就是Number(undefined)會轉換為NaN;但是使用~~能保證一直是數字,~~undefined === 0。(什麼?你還不知道區別?那你得看霖呆呆的這篇文章了:JS中按位取反運算子~及其它運算子)

  • 接下來只需要判斷一下傳入進來的下標是不是小於0的,小於0的話加上陣列的長度就可以了

  • 然後返回index這一項使用的是Reflect.get(target, index)。什麼?為什麼不直接用target[index]?當然這樣也可以,對比target[index]的區別就是Reflect.get(target, index)如果傳入的target不是一個Object的話(陣列也屬於物件),就會報一個型別錯誤TypeError,而target[index]返回的會是一個undefined。比如這樣:

    var obj = 5
    console.log(obj['b']) // undefined
    console.log(Reflect.get(obj, 'b')) // Uncaught TypeError: Reflect.get called on non-object
    複製程式碼

擴充套件點:

呆呆這邊主要是想擴充套件一下get的第三個引數receiver(接受者),在MDN上的解釋是:

如果target物件中指定了getterreceiver則為getter呼叫時的this值。

來看個例子理解一下😊。

案例一

例如我們開始有這麼一個物件:

var obj = {
  fn: function () {
    console.log('lindaidai')
  }
}
複製程式碼

現在使用Proxy來賦值到obj1中:

var obj = {
  fn: function () {
    console.log('lindaidai')
  }
}
var obj1 = new Proxy(obj, {
  get (target, key, receiver) {
    console.log(receiver === obj1) // true
    console.log(receiver === target) // false
    return target[key]
  }
})
obj1.fn()
複製程式碼

可以看到,receiver表示的是obj1這個新的代理物件,target表示的是被代理的物件obj

所以,receiver可以表示使用代理物件本身

案例二

另一種情況,receiver也可以表示是從其繼承的物件

var proxy = new Proxy({}, {
  get (target, key, receiver) {
    return receiver;
  }
})
console.log(proxy.getReceiver === proxy) // true
var inherits = Object.create(proxy)
console.log(inherits.getReceiver === inherits) // true
複製程式碼

這個案例中,我新建了一個空物件的代理物件proxy,使用proxy.getReceiver獲取它的receiver,發現它就是代理物件本身。

而如果我將這個代理物件作為一個原型物件,創建出一個新的物件inherits,也就是實現了原型式繼承,那麼這時候receiver的值就是這個被繼承的物件inherits

總結

  • 可以使用Proxy代理,來改變每次獲取陣列的值。
  • Proxyget中,第二個引數是字串,即使傳入的是陣列下標。
  • 對比於Number()的好處就是Number(undefined)會轉換為NaN;但是使用~~能保證一直是數字,~~undefined === 0
  • 對比target[index]的區別就是Reflect.get(target, index)如果傳入的target不是一個Object的話(陣列也屬於物件),就會報一個型別錯誤TypeError,而target[index]返回的會是一個undefined
  • Proxyget中,第三個引數receiver通常為使用代理物件本身或從其繼承的物件。

github.com/LinDaiDai/n…

四、addEventListener和attachEvent的區別?

  • 前者是標準瀏覽器中的用法,後者IE8以下
  • addEventListener可有冒泡,可有捕獲;attachEvent只有冒泡,沒有捕獲。
  • 前者事件名不帶on,後者帶on
  • 前者回調函式中的this指向當前元素,後者指向window

github.com/LinDaiDai/n…

五、addEventListener函式的第三個引數

第三個引數涉及到冒泡和捕獲,是true時為捕獲,是false則為冒泡。

或者是一個物件{passive: true},針對的是Safari瀏覽器,禁止/開啟使用滾動的時候要用到。

github.com/LinDaiDai/n…

六、文字單超出顯示省略號

div {
  width: 200px;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}
複製程式碼

github.com/LinDaiDai/n…

七、文字多行超出顯示省略號

div {
  width: 200px;
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 3;
  overflow: hidden;
}
複製程式碼

該方法適用於WebKit瀏覽器及移動端。

跨瀏覽器相容方案:

p {
  position:relative;
  line-height:1.4em;
  /* 3 times the line-height to show 3 lines */
  height:4.2em;
  overflow:hidden;
}
p::after {
  content:"...";
  font-weight:bold;
  position:absolute;
  bottom:0;
  right:0;
  padding:0 20px 1px 45px;
}
複製程式碼

github.com/LinDaiDai/n…

後語

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

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

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

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

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

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

本文使用 mdnice 排版