前端刷題 —— 牛客網前端題庫60道詳解(一) 目錄

目錄

  • 引言
      1. 修改 this 指向(中等)
      1. 獲取 url 參數(較難)
      1. dom 節點查找(入門)
      1. 根據包名,在指定空間中創建對象(入門)
      1. 數組去重(較難)
      1. 斐波那契數列(入門)
      1. 時間格式化輸出(中等)
      1. 獲取字符串的長度(簡單)
      1. 郵箱字符串判斷(中等)
      1. 顏色字符串轉換(中等)

引言

牛客網這個前端筆試題庫,可以說這60道是最基礎的了,也是考察的東西比較雜,有時間4天差不多就可以刷完,鞏固基礎還是有些用的。做完題回顧一上午就可以過完一遍。現在我結合我的答案和參考的其他人的答案,在這裏做一個總結,也是自己知識的整理結果。

題目

1. 修改this指向

封裝函數 f,使 f 的 this 指向指定的對象。

這裏給出三種寫法,哪種都可以。

// apply修改this作用域
function bindThis(f, oTarget) {
    return function () {
        return f.apply(oTarget,arguments)
    }
}

// call修改this作用域
function bindThis(f, oTarget) {
    return function (){
        return f.call(oTarget,...arguments)
    }
}
// bind修改this作用域
function bindThis(f, oTarget) {
    return function (){
        return f.bind(oTarget,...arguments)()
    }
}
// 或者直接簡寫
function bindThis(f, oTarget) {
    return f.bind(oTarget)
}

相關知識點:

  1. apply、call、bind區別

apply、call、bind的作用都是修改執行上下文
apply、call都是返回函數立即執行的結果,其中apply第二個參數之後是數組,call第二個參數之後是單個的值。
bind返回的是函數,需要手動執行結果。第二個參數之後是單個的值。

2. 獲取url參數

獲取 url 中的參數

  1. 指定參數名稱,返回該參數的值 或者 空字符串
  2. 不指定參數名稱,返回全部的參數對象 或者 {}
  3. 如果存在多個同名參數,則返回數組

輸入:http://www.nowcoder.com?key=1&key=2&key=3&test=4#hehe key

輸出:[1, 2, 3]

function getUrlParam(sUrl, sKey) {
        let newArr = []
        let newObj = {}
        // 獲取?號後面#號前面的值
        let query = sUrl.split('#')[0].split('?')[1]
        // 如果query存在
        if (query) {
          let arr = query.split('&')
          for(let i = 0 ; i < arr.length; i++) {
            if (arr[i]) {
              arr[i] = arr[i].split('=')
              // 數組
              if (sKey !== undefined) {
                if(arr[i][0] === sKey) {
                  newArr.push(arr[i][1])
                }
              // 對象
              } else {
                if(arr[i][0] in newObj) {
                  newObj[arr[i][0]].push(arr[i][1])
                } else {
                  newObj[arr[i][0]] = [arr[i][1]]
                }
              }
            }
          }
          // 判斷sKey有沒有值
          if(sKey !== undefined) {
            switch(newArr.length) {
              case 0 : return '';break;
              case 1 : return newArr[0]; break;
              default: return newArr;break;
            }
          } else {
            return newObj
          }
        // 如果query不存在,判斷sKey是否存在,如果存在就返回空對象,如果不存在就返回空字符串
        } else {
          return sKey !== undefined ? {} : ''
        }
      }

相關知識點:

  • url的組成部分

https://user:[email protected]:80/index.html?type=1&name=2#haha
http/https 是協議
user:pass@ 是登錄認證
www.baidu.com 是域名,服務器地址
:80 是端口號
/index.html 是請求資源文件路徑
?type=1&name=2 是查詢字符串,攜帶參數,給服務器傳的內容。
#haha 是哈希,片段標識符

  • split方法

字符串分割成數組的方法,裏面的參數是以什麼分割,如果不傳就是空字符串爲分割,返回值是一個數組。

  • query部分可以使用正則

3. dom節點查找

查找兩個節點的最近的一個共同父節點,可以包括節點自身

輸入描述:
oNode1 和 oNode2 在同一文檔中,且不會爲相同的節點

function commonParentNode(oNode1, oNode2) {
    if(oNode1.contains(oNode2)) {
        return oNode1
    } else {
        return commonParentNode(oNode1.parentNode,oNode2)
    }
}

相關知識點:

  • contains API

查看dom元素包含關係,包含返回true,不包含返回false
參考MDN

  • 遞歸(參考數據結構樹)

4. 根據包名,在指定空間中創建對象

根據包名,在指定空間中創建對象
輸入描述:
namespace({a: {test: 1, b: 2}}, 'a.b.c.d')
輸出描述:
{a: {test: 1, b: {c: {d: {}}}}}

function namespace(oNamespace, sPackage) {
    let scope = sPackage.split('.')
    let ns = oNamespace
    for(let i = 0; i< scope.length; i++) {
        // 如果對象中沒有該元素,或者不是對象,那麼就置爲空對象
       if(!ns.hasOwnProperty(scope[i]) || Object.prototype.toString.call(ns[scope[i]]) !== '[object Object]') {
            ns[scope[i]] = {}
        }
        // 然後繼續往下找
        ns = ns[scope[i]]
    }
    return oNamespace
}

考察知識點:

  • 判斷對象的自身是否有某屬性(hasOwnProperty)
  • hasOwnProperty / typeof / in / instanceof 的區別
  • hasOwnProperty 是判斷對象自身有沒有某屬性,不包含原型鏈的方法。
  • in 是判斷對象在自身和原型鏈上有沒有該方法。
  • instanceof 是判斷對象在原型鏈上有沒有該方法。
  • typeof 判斷操作數的類型,但是null也會判斷爲"object"
  • 準確判斷某值的類型

Object.prototype.toString.call(123) === "[object Number]"
Object.prototype.toString.call('aaa') === "[object String]"
Object.prototype.toString.call(true) === "[object Boolean]"
Object.prototype.toString.call(undefined) === "[object Undefined]"
Object.prototype.toString.call(null) === '[object Null]'
Object.prototype.toString.call({}) === '[object Object]'
Object.prototype.toString.call([]) === '[object Array]'
Object.prototype.toString.call(Math) === "[object Math]"
Object.prototype.toString.call(new Date()) === "[object Date]"
Object.prototype.toString.call(new RegExp) === "[object RegExp]"

  • 遞歸(對象的嵌套參考數據結構中的樹)

5. 數組去重

爲 Array 對象添加一個去除重複項的方法
輸入
[false, true, undefined, null, NaN, 0, 1, {}, {}, 'a', 'a', NaN]
輸出
[false, true, undefined, null, NaN, 0, 1, {}, {}, 'a']

這個題,狗就狗在,還有NaN

// 方法一:終極思路
Array.prototype.uniq = function () {
    return [...new Set(this)]
}

// 方法二:普通思路,遍歷之後比較值
Array.prototype.uniq = function () {
    let arr = []
    let flag = true
    this.forEach(value => {
        // == -1 有兩種情況,一種是NaN,一種是有相同值
        if(arr.indexOf(value) === -1) {
            // 如果是NaN
            if(value !== value) {
                // flag是標記,第一個NaN就進,之後的就不進去
                if(flag){
                  arr.push(value)  
                  flag = false
                }
            } else {
                arr.push(value)  
            }
        }
    })
    return arr
}

相關知識點:

  • uniq方法中的this指向哪裏?

Array構造函數的原型方法中的this指的是數組實例。

  • Set的特性

Set存儲的成員是唯一的,不是重複的,如果有重複會自動過濾掉.
ES6(七)—— Set & Map

  • (NaN === NaN) => false

NaN : is not a number,不等於自己
typeof NaN => number
Object.prototype.toString.call(NaN) => "[object Number]"
ES6 新增方法:Number.isNaN() 用來判斷是否屬於數字

6. 斐波那契數列

用 JavaScript 實現斐波那契數列函數,返回第n個斐波那契數。 f(1) = 1, f(2) = 1 等

斐波那契數列基本學js都會,1 1 2 3 5 8 13,後一個是前兩個的和。

// 方法一:遞歸思路
function fibonacci(n) {
    if(n === 0) return 0
    if(n === 1 || n === 2) return 1
    return fibonacci(n-1) + fibonacci(n-2)
}

// 方法二:迭代思路
function fibonacci(n) {
    let num1 = 1
    let num2 = 1
    let sum = 0
    for(let i = 3; i <= n; i++) {
        sum = num1 + num2
        num1 = num2
        num2 = sum
    }
    return sum
}

// 上面寫法可以過oj,但是如果數字大點就超級慢,使用緩存很可
// 方法三:遞歸優化思路
function fibonacci(n,cache = {}) {
    // 有緩存就直接讀緩存
    if(n in cache) return cache[n]
    if(n === 1 || n === 2) {
        cache[n] = 1
        return 1
    }
    // 沒有緩存算完之後存入緩存
    let temp = fibonacci(n-1, cache) + fibonacci(n-2,cache)
    cache[n] = temp
    return temp
}

相關知識點:

  • 遞歸

7. 時間格式化輸出

題目描述
按所給的時間格式輸出指定的時間
格式說明
對於 2014.09.05 13:14:20
yyyy: 年份,2014
yy: 年份,14
MM: 月份,補滿兩位,09
M: 月份, 9
dd: 日期,補滿兩位,05
d: 日期, 5
HH: 24制小時,補滿兩位,13
H: 24制小時,13
hh: 12制小時,補滿兩位,01
h: 12制小時,1
mm: 分鐘,補滿兩位,14
m: 分鐘,14
ss: 秒,補滿兩位,20
s: 秒,20
w: 星期,爲 ['日', '一', '二', '三', '四', '五', '六'] 中的某一個,本 demo 結果爲 五

輸入
formatDate(new Date(1409894060000), 'yyyy-MM-dd HH:mm:ss 星期w')
輸出
2014-09-05 13:14:20 星期五

function formatDate(t,str) {
        let year = ''+t.getFullYear()
        let month = t.getMonth() + 1
        let day = t.getDate()
        let hour = t.getHours()
        let minutes = t.getMinutes()
        let second = t.getSeconds()
        let week = ['日','一','二','三','四','五','六']
        let date = {
          'yyyy': year,
          'yy': year.slice(2),
          'MM': ten(month),
          'M': month,
          'dd': ten(day),
          'd': day,
          'HH': ten(hour),
          'H': hour,
          'hh': ten(hour % 12),
          'h': hour % 12,
          'mm': ten(minutes),
          'm': minutes,
          'ss': ten(second),
          's': second,
          'w': week[t.getDay()]
        }
        for(let key in date) { 
          str = str.replace(key,date[key])
        }
        return str
      }
      
      // 不足10的前面要加0
      let ten = num  => num >= 10 ? num : '0' + num

考察知識點:

  • 獲取年月日周時分秒的系統API
  • 格式統一處理
  • 字符串替換 (replace)

8. 獲取字符串的長度

題目描述
如果第二個參數 bUnicode255For1 === true,則所有字符長度爲 1
否則如果字符 Unicode 編碼 > 255 則長度爲 2
輸入
hello world, 牛客', false
輸出 17

function strLength(s, bUnicode255For1) {
    if(bUnicode255For1) return s.length;
    let len = s.length
    for(let i = 0; i < s.length; i++) {
        if(s[i].charCodeAt() > 255) len++
    }
    return len
}

相關知識點:

  • 獲取字符的 Unicode 編碼 API —— str.charCodeAt()

9. 郵箱字符串判斷

題目描述
判斷輸入是否是正確的郵箱格式

考察正則的一道題目,方法也是多種多樣,這裏只有一種簡單的參考。

// ^ 表示開頭
// [] 表示匹配字符的範圍
// \w 表示正常符號 [0-9a-zA-Z_]
// \. 是對任意符.進行轉義,表示字符.
// + 表示前面的表達式,一次到多次
function isAvailableEmail(sEmail) {
    return /^[\w\.]+@\w+\.\w+/.test(sEmail)
}

相關知識點:

  • 郵箱格式
  • 正則表達式的規則和匹配

10. 顏色字符串轉換

題目描述
將 rgb 顏色字符串轉換爲十六進制的形式,如 rgb(255, 255, 255) 轉爲 #ffffff

  1. rgb 中每個 , 後面的空格數量不固定
  2. 十六進制表達式使用六位小寫字母
  3. 如果輸入不符合 rgb 格式,返回原始輸入

輸入 :'rgb(255, 255, 255)'
輸出 :#ffffff

function rgb2hex(sRGB) {
        // 正則匹配獲取三個數值
        let reg = sRGB.match(/^rgb\((\d+),\s*(\d+),\s*(\d+)\)/)
        if(!reg) return sRGB;
        // 字符串拼接
        let str = '#'
        for(let i = 1; i < reg.length; i++) {
          // 將字符串轉成數字
          let m = parseInt(reg[i])
          if (m >= 0 && m <= 255) {
            // 然後轉化成16進制
            str += (m >= 16 ? m.toString(16) : '0' + m.toString(16))
          } else {
            return sRGB
          }
        }
        return str
      }

相關知識點:

  • toString的進制轉換

顏色是16進制,所以toString(16)可以得到結果

  • 字符串中如何截取數字(不限於正則)

注意:一定要理解之後去程序裏面運行一遍。

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