美文网首页
前端刷题 —— 牛客网前端题库60道详解(一)

前端刷题 —— 牛客网前端题库60道详解(一)

作者: 顽皮的雪狐七七 | 来源:发表于2021-04-13 14:40 被阅读0次

    目录

    • 引言
        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:pass@www.baidu.com: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)可以得到结果

    • 字符串中如何截取数字(不限于正则)

    注意:一定要理解之后去程序里面运行一遍。

    相关文章

      网友评论

          本文标题:前端刷题 —— 牛客网前端题库60道详解(一)

          本文链接:https://www.haomeiwen.com/subject/xpkxlltx.html