js面試題-代碼實現

新 API

最新的 url 參數獲取的 API?

URLSearchParams

// 有如下一個url: http://localhost?a=1&b=2

function getUrlParam(name) {
  let paramStr = location.search.substr(1)
  let params = new URLSearchParams(paramStr)
  return params.get(name)
}

console.log(getUrlParam('a')) // 1

數組

實現數組去重?

現在有如下一個數組:

let arr = [1, 1, 'true', 'true', true, true, false, false, undefined, undefined, null, null, NaN, NaN, 'NaN', 0, 0, 'a', 'a', {}, {}]
  • 使用 ES6 的 Set 方法

利用 Set 數據類型的特性,集合內部不會有重複的元素 不能過濾對象

Array.from(new Set(arr));

// 等於

[...new Set(arr)];
  • 使用對象鍵值唯一的特性

不能過濾對象

function unique(arr) {
  let obj = {}
  return arr.filter((item) => {
    let key = typeof item + item
    return obj.hasOwnProperty(key) ? false : (obj[key] = true)
  })
}

因爲對象的 key 是 string 類型,無論你傳入什麼他都會解析成字符串,所以就導致了數字 1 和字符串 1,在對象中的 key 是相同的。 所以會把它們當成相同的數據過濾掉,所以可以使用 typeof 簡單判斷下數據類型

  • 使用 Map
function unique(arr) {
  let map = new Map()
  return arr.filter((item) => {
    return map.has(item) ? false : map.set(item, true)
  })
}
  • indexOf 或 includes

不能過濾對象、NaN

function unique(arr) {
  let list = []
  if (!Array.isArray(arr)) {
    throw new Error('type error')
  }
  for (let i = 0, l = arr.length; i < l; i++) {
    let item = arr[i]
    if (list.indexOf(item) === -1) {
      list.push(item)
    }
  }
  return list
}
  • 雙層 for 循環

不能過濾對象、NaN

function unique(arr) {
  // let l = arr.length
  for (let i = 0; i < arr.length; i++) {
    for (let j = i + 1; j < arr.length; j++) {
      if (arr[i] === arr[j]) {
        arr.splice(j, 1)
        j--
      }
    }
  }
  return arr
}

實現扁平數組(降維數組)

  • 重寫 concat 方法

只能處理二維數組

let arr = [[1, 2], [3, 4]]

Array.prototype.concat.apply([], arr) // [1, 2, 3, 4]
  • 使用 flat 方法

可以處理多維數組

let arr = [[1, 2], [3, 4]]

arr.flat(Infinity) // [1, 2, 3, 4]
  • 使用 toString + split

這種方法有些侷限性,扁平化後的數組的每一項會變成字符串

let arr = [[1, 2], [3, 4]]

arr.toString().split(',') // [1, 2, 3, 4]
  • 遞歸
let source = [[1, 2], [3, 4]]
let arr = []

function flatFn(source) {
  source.forEach(v => {
    if(Array.isArray(v)){
        flatFn(v)
    } else {
        arr.push(v)
    }
  })
}
flatFn(source)
console.log(arr) // [1,2,3,4]

實現取數組的最大值?

  • Math.max.apply(null, [4, 1, 2, 3])
  • Math.max(...[4, 1, 2, 3])

實現一個Flat?

let source = [[1, 2], [3, 4, [5, 6]]]

function flatFn(source, depth=1){
  return source.reduce((pre,cur) => {
      return Array.isArray(cur) && depth > 0 ? [...pre, ...flatFn(cur, depth-1)] : [...pre, cur]
  }, [])
}

console.log(flatFn(source, 1)) // [1, 2, 3, 4, Array(2)] 
console.log(flatFn(source, 2)) // [1, 2, 3, 4, 5, 6]

工具類

實現深拷貝?

有如下的一個數據:

let obj = {
  a: undefined,
  b: 1.7976931348623157e10308,
  c: -1.7976931348623157e10308,
  d: new Date(),
  e: new RegExp('\\w+'),
  f: function () {
    console.log(1)
  }
}
  • 使用 JSON.parse(JSON.stringify(obj))
    弊端如下:

    • 如果 obj 裏面有時間對象,時間將只是字符串的形式。而不是時間對象
    • 如果 obj 裏有 RegExp、Error 對象,則序列化的結果將只得到空對象
    • 如果 obj 裏有函數,undefined,則序列化的結果會把函數或 undefined 丟失
    • 如果 obj 裏有 NaN、Infinity(正無窮大)和-Infinity(負無窮大),則序列化的結果會變成 null
    • 如果 obj 中的對象是有構造函數生成的,會丟棄對象的 constructor
  • 手寫迭代拷貝

function deepClone(source) {
  if (!source || typeof source !== 'object') {
    return source
  }
  if (source instanceof Date) {
    return new Date().setTime(source.getTime())
  }
  let newObj = Array.isArray(source) ? [] : {}
  for (let key in source) {
    if (source[key] && typeof source[key] === 'object') {
      newObj[key] = deepClone(source)
    } else {
      newObj[key] = source[key]
    }
  }
  return newObj
}

排序算法

手寫冒泡

名字的由來: 越大(或越小)的元素經過交換慢慢浮到數列的頂端。就如同飲料中的氣泡最終會上浮到頂端一樣,故名叫“冒泡排序”

規則如下:

  1. 外層循環和內層循環只需要循環 length - 1

兩兩比較,最後的兩個數只需要比較一次就能確定兩者的位置 比如 [2, 1, 3, 4, 5],2 和 1 只需要比較一次就能確定位置了[1, 2, 3, 4, 5]

  1. 內層循環在 length - 1 的基礎上減去 i,也就是 length - 1 - i

每當外層 for 成功循環一次之後,內層 for 就可以少循環一次,這裏注意是當外層 for 循環完畢之後

  1. 內層循環:用當前的值 a(arr[j])和下一個值 b(arr[j + 1])比較大小,如果 a 大於 b,則交換兩數的位置,最終的目的是將最大的數移到數組最後面
let arr = [5, 4, 3, 2, 1]

function bubbleSort(arr) {
  let len = arr.length
  for (let i = 0; i < len - 1; i++) {
    for (let j = 0; j < len - 1 - i; j++) {
      if (arr[j] > arr[j + 1]) {
        let temp = arr[j]
        arr[j] = arr[j + 1]
        arr[j + 1] = temp
      }
    }
  }
}

bubbleSort(arr)

快排

規則如下:

  1. 在數組中,找到一個元素作爲“基準”(pivot)
  2. 小於基準的元素,就移到“基準”的左邊;大於“基準”的元素,就移到“基準”的右邊
  3. 對“基準”左右兩邊的集合,進行遞歸,不斷重複第一步和第二步,直到所有子元素只剩一個元素爲止
function quickSort(arr) {
  if (arr.length === 0) return arr
  const pivotInx = Math.floor(arr.length / 2)
  const pivot = arr.splice(pivotInx, 1)[0]
  const left = []
  const right = []
  for (let i = 0; i < arr.length; i++) {
    const item = arr[i]
    if (item < pivot) {
      left.push(item)
    } else {
      right.push(item)
    }
  }
  return quickSort(left).concat([pivot]).concat(quickSort(right))
}

看代碼

看以下代碼會輸出什麼?

let a = { name: 'Jone' }
let b = a
a = { name: 'Sasa' }
console.log(b)

解答:
輸出:{ name: 'Jone' }
這道題主要是考引用數據類型的特性 當 b = a 時,ab 指向同一個堆地址。 當 a = {name: 'Sasa'} a 被重新賦值,所以 a 指向新的堆地址,b 仍然是原來的堆地址

看以下代碼會輸出什麼?

let a = 1
b = a
b = 2
console.log(a)

解答:
這道題主要是考基本數據類型的特性

其他

let a = null 有什麼作用?

let a = null 一般用作手動釋放該變量的內存,而不是等到離開作用域後被自動回收

如何阻止冒泡事件

function stopBubble(e) {
  if (e && e.stopPropagation) {
    // 非IE瀏覽器
    e.stopPropagation()
  } else {
    //IE瀏覽器
    window.event.cancelBubble = true
  }
}

如何阻止瀏覽器默認事件

function stopDefault(e) {
  //標準瀏覽器
  if (e && e.preventDefault) {
    e.preventDefault()
  }
  //個別IE
  else {
    window.event.returnValue = fale
    return false
  }
}

獲取非行間樣式

function getCss(curEle, attr) {
  var val = null
  try {
    val = window.getComputedStyle(curEle, null)[attr]
  } catch (e) {
    val = curEle.currentStyle[attr]
  }
  return val
}

getCss(div, 'width')
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章