牛客网前端挑战编程题解


title: 牛客网前端挑战编程题解
toc: true
date: 2018-09-20 14:33:15
categories:

  • Web

tags:

  • JavaScript

OJ链接:https://www.nowcoder.com/ta/front-end

注意:在牛客不能用ES6的东西!

修改this指向

封装函数 f,使 f 的 this 指向指定的对象

代码

function bindThis(f, oTarget) {
  // 考虑浏览器兼容
  if (f.bind) {
    return f.bind(oTarget);
  } else {
    // apply()为立即执行函数,因此需要放在一个函数里,到需要使用的时候再立即执行
    return function() {
      // arguments不能省略,在执行f时可能会传入参数
      return f.apply(oTarget, arguments);
    }
  }
}

获取url参数

获取 url 中的参数

  1. 指定参数名称,返回该参数的值 或者 空字符串
  2. 不指定参数名称,返回全部的参数对象 或者 {}
  3. 如果存在多个同名参数,则返回数组

输入

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

输出

[1, 2, 3]

代码

function getUrlParam(sUrl, sKey) {
  var result = {};
  // 先取?和#之间的字符串,然后按照&分割
  var querys = ((sUrl.split('?')[1]).split('#')[0]).split('&');
  for (var i = 0; i < querys.length; i++) {
    var key = querys[i].split('=')[0];
    var value = querys[i].split('=')[1];
    // 如果result[key]未定义,则创建一个含有value的数组
    if (result[key] == undefined) result[key] = [value];
    else result[key].push(value);
  }
  // 不指定参数名称,返回全部的参数对象
  if (sKey == undefined) return result;
  if (result[sKey] == undefined) return "";
  else if (result[sKey].length == 1) return result[sKey][0];
  else return result[sKey];
}

还有大神的正则的方法...直接贴出来吧:

// 链接:https://www.nowcoder.com/questionTerminal/a3ded747e3884a3c86d09d88d1652e10
// 来源:牛客网

function getUrlParam(sUrl, sKey) {
    var obj = {};
    var reg = /[?&](w+)=(w+)/g;
    while(reg.exec(sUrl))
        obj[RegExp.$1] ? obj[RegExp.$1]=[].concat(obj[RegExp.$1],RegExp.$2) : obj[RegExp.$1]=RegExp.$2;
    return sKey ? obj[sKey]||"" : obj;
}

dom节点查找

查找两个节点的最近的一个共同父节点,可以包括节点自身

输入描述

oNode1 和 oNode2 在同一文档中,且不会为相同的节点

代码

function commonParentNode(oNode1, oNode2) {
    for (;oNode1;oNode1 = oNode1.parentNode) {
        if (oNode1.contains(oNode2)) {
            return oNode1;
        }
    }
}

还是对DOM的相关属性啊什么的不了解。。红宝书刚看到DOM。。。要加油鸭

根据包名,在指定空间中创建对象

输入描述

namespace({a: {test: 1, b: 2}}, 'a.b.c.d')

输出描述

{a: {test: 1, b: {c: {d: {}}}}}

代码

function namespace(oNamespace, sPackage) {
    var tmpPoint = oNamespace;
    var keys = sPackage.split('.');
    for (var i = 0; i < keys.length; i++) {
        if (tmpPoint[keys[i]] == undefined) tmpPoint[keys[i]] = {};
        tmpPoint = tmpPoint[keys[i]];
    }
    return oNamespace;
}

记住,对象名就像是指针一样。

数组去重

为 Array 对象添加一个去除重复项的方法

输入

[false, true, undefined, null, NaN, 0, 1, {}, {}, 'a', 'a', NaN]

输出

[false, true, undefined, null, NaN, 0, 1, {}, {}, 'a']

代码

Array.prototype.uniq = function () {
    var hasNaN = false;
    for (var i = 0; i < this.length; i++) {
        if ((hasNaN === false) && (this[i] != this[i])) {
            hasNaN = true;
            continue;
        }
        if (this.indexOf(this[i]) < i) this.splice(i--,1);
    }
    return this;
}

关于NaN的问题调试了好几次。。

使用this.indexOf(this[i]) < i判断的时候,

因为NaN和任何值都不相等,因此index是-1,因此在这里一定会被删掉,

因此需要先判断是不是已经遇到过NaN了,遇到过的话再次遇到就直接删掉,没有遇到的话遇到时就把hasNaN设为true,然后continue来跳过splice。

斐波那契数列

用 JavaScript 实现斐波那契数列函数,返回第n个斐波那契数。 f(1) = 1, f(2) = 1 等

emmmmm,不知道为什么会有斐波那契这种题。。。。

// 暴力递归
function fibonacci(n) {
    if (n <= 0) return 0;
    if (n == 1) return 1;
    return fibonacci(n-1) + fibonacci(n-2);
}
// 本来想用callee,结果严格模式不让用。。。红宝书一直说callee多么多么好。。。搞得我不用好难受
// 循环
function fibonacci(n) {
    if (n <= 0) return 0;
    if (n == 1) return 1;
    var a = 0, b = 1, c = 1;
    for (var i = 2; i <= n; i++) {
        c = a + b;
        a = b;
        b = c;
    }
    return c;
}

时间格式化输出

按所给的时间格式输出指定的时间
格式说明
对于 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(date, format) {
    var myDate = {
        yyyy: date.getFullYear(),
        yy: date.getFullYear()%100,
        MM: ("0" + (date.getMonth()+1)).slice(-2),
        M: date.getMonth()+1,
        dd: ("0" + (date.getDate())).slice(-2),
        d: date.getDate(),
        HH: ("0" + date.getHours()).slice(-2),
        H: date.getHours(),
        hh: ("0" + (date.getHours() % 12)).slice(-2),
        h: date.getHours() % 12,
        mm: ("0" + date.getMinutes()).slice(-2),
        m: date.getMinutes(),
        ss: ("0" + date.getSeconds()).slice(-2),
        s: date.getSeconds(),
        w: ['日', '一', '二', '三', '四', '五', '六'][date.getDay()]
    }
    return format.replace(/([a-z]+)/ig,function($1){return myDate[$1];});
}

参考了大神的代码。。。自己差点就十几个if了。。。

关于代码说明几点:

getFullYear()为年份四位表示

getMonth()为月份0-11

getDate()为日期1-31

getHours()为小时0-23

getMinutes()为分钟0-59

getSeconds()为秒数0-59

getDay()为0-6表示['日', '一', '二', '三', '四', '五', '六']

RegExp.$1...RegExp.$9表示用于存储第一、……第九个匹配的捕获组,至于什么是捕获组可以看下边这个例子:

var text = "this has been a short summer";
var pattern = /(..)or(.)/g;

if (pattern.exec(text)) {
  alert(RegExp.$1); // "sh"
  alert(RegExp.$2); // "t"
}

获取字符串长度

如果第二个参数 bUnicode255For1 === true,则所有字符长度为 1
否则如果字符 Unicode 编码 > 255 则长度为 2

输入

'hello world, 牛客', false

输出

17

代码

function strLength(s, bUnicode255For1) {
    if (bUnicode255For1 === true) {
        return s.length;
    } else {
        var result = 0;
        for (var i = 0; i < s.length; i++) {
            if (s.charCodeAt(i) > 255) result+=2;
            else result+=1;
        }
        return result;
    }
}

刚开始一直没看懂题。。。这道题应该有点问题,js默认utf-16编码,所以不是所有字符都长度为1,有的字符是长度为2的。。。忽略这个写这个题吧。。。

邮箱字符串判断

输入描述

邮箱字符串

输出描述

true表示格式正确

代码

function isAvailableEmail(sEmail) {
    var reg = /^[w.]+@[w.]+.[w.]+$/i;
    return reg.test(sEmail);
}
  • w 匹配字母或数字或下划线或汉字 等价于 '[^A-Za-z0-9_]'。

  • s 匹配任意的空白符

  • d 匹配数字

  •  匹配单词的开始或结束

  • ^ 匹配字符串的开始

  • $ 匹配字符串的结束

  • w能不能匹配汉字要视你的操作系统和你的应用环境而定

颜色字符串转换

将 rgb 颜色字符串转换为十六进制的形式,如 rgb(255, 255, 255) 转为 #ffffff

  1. rgb 中每个 , 后面的空格数量不固定
  2. 十六进制表达式使用六位小写字母
  3. 如果输入不符合 rgb 格式,返回原始输入

输入

'rgb(255, 255, 255)'

输出

#ffffff

代码

function rgb2hex(sRGB) {
    if(sRGB.slice(0,4)!=='rgb(' || sRGB.slice(-1)!==')') return sRGB;
    var tmp = (sRGB.split('(')[1]).split(')')[0];
    var colors = tmp.split(',');
    if (colors.length != 3) return sRGB;
    var re = "#";
    for (var i = 0; i < colors.length; i++) {
        var value =  parseInt(colors[i].substring(colors[i].lastIndexOf(' ')));
        if (value < 0 || value > 255) return sRGB;
        re += ("0" + (value).toString(16)).slice(-2);
    }
    return re;
}

又使用了最水的办法。。。

看一下大佬的正则的方法:

//链接:https://www.nowcoder.com/questionTerminal/80b08802a833419f9c4ccc6e042c1cca
//来源:牛客网

function rgb2hex(sRGB) {
   return sRGB.replace(/^rgb((d+)s*\,s*(d+)s*\,s*(d+))$/g, function(a, r, g, b){
       return '#' + hex(r) + hex(g) + hex(b);
   });
}
function hex(n){
    return n < 16 ? '0' + (+n).toString(16) : (+n).toString(16);
}

a代表正则匹配的整个字符串, r ,g, b代表红绿蓝三个通道, 分别是正则中的三个括号匹配的字符串. 通常用的$0, $1, $2, $3。

但是吧,可能是用例不全,这个方法并没有判断是不是超出0-255也能AC。。

可以在hex函数里加个判断,如果规范才返回字符串,然后在replace里的那个函数里加个判断hex(r)、hex(g)、hex(b)是否为undefined,像这样:

function rgb2hex(sRGB) {
  return sRGB.replace(/^rgb((d+)s*\,s*(d+)s*\,s*(d+))$/g, function(a, r, g, b){
    if (hex(r) == undefined || hex(g) == undefined || hex(b) == undefined) return sRGB;
    return '#' + hex(r) + hex(g) + hex(b);
  });
}
function hex(n){
  if (n > -1 && n < 256) {
    return n < 16 ? '0' + (+n).toString(16) : (+n).toString(16);
  }
}

这样就可以啦!

将字符串转换为驼峰模式

css 中经常有类似 background-image 这种通过 - 连接的字符,通过 javascript 设置样式的时候需要将这种样式转换成 backgroundImage 驼峰格式,请完成此转换功能

  1. 以 - 为分隔符,将第二个起的非空单词首字母转为大写
  2. -webkit-border-image 转换后的结果为 webkitBorderImage

输入

'font-size'

输出

fontSize

代码

function cssStyle2DomStyle(sName) {
    if (sName[0] == "-") sName = sName.slice(1);
    var words = sName.split('-');
    var re = words[0];
    for (var i = 1; i < words.length; i++) {
        re = re + words[i][0].toUpperCase() + words[i].slice(1);
    }
    return re;
}

大神的代码:

// 链接:https://www.nowcoder.com/questionTerminal/2ded24e34ec34325a62d42d0c8479bae
// 来源:牛客网

return sName.replace(/-[a-z]/g , function(a, b){
 
    return b == 0 ? a.replace('-','') : a.replace('-','').toUpperCase();
 
});

其中b是offset。

replace第二个参数函数的参数表:

变量名 代表的值
match 匹配的子串。(对应于上述的$&。)
p1,p2, ... 假如replace()方法的第一个参数是一个RegExp 对象,则代表第n个括号匹配的字符串。(对应于上述的$1,$2等。)
offset 匹配到的子字符串在原字符串中的偏移量。(比如,如果原字符串是“abcd”,匹配到的子字符串是“bc”,那么这个参数将是1)
string 被匹配的原字符串。

字符串字符统计

统计字符串中每个字符的出现频率,返回一个 Object,key 为统计字符,value 为出现频率

  1. 不限制 key 的顺序
  2. 输入的字符串参数不会为空
  3. 忽略空白字符

输入

'hello world'

输出

{h: 1, e: 1, l: 3, o: 2, w: 1, r: 1, d: 1}

大神的代码:

function count(str) {
    var obj = {};
    str.replace(/S/g,function(s){
        !obj[s]?obj[s]=1:obj[s]++;
    })
    return obj;
}

S匹配非空字符串,function(s)里的s代表匹配到的每一项。


哎总结一句就是我好菜。。对JS了解还是太少。。。

原文地址:https://www.cnblogs.com/zmj97/p/10180763.html