title: 牛客网前端挑战编程题解
toc: true
date: 2018-09-20 14:33:15
categories:
Web
tags:
JavaScript
注意:在牛客不能用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 中的参数
指定参数名称,返回该参数的值 或者 空字符串
不指定参数名称,返回全部的参数对象 或者 {}
如果存在多个同名参数,则返回数组
输入
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 匹配数字
\b 匹配单词的开始或结束
^ 匹配字符串的开始
$ 匹配字符串的结束
\w能不能匹配汉字要视你的操作系统和你的应用环境而定
颜色字符串转换
将 rgb 颜色字符串转换为十六进制的形式,如 rgb(255, 255, 255) 转为 #ffffff
rgb 中每个 , 后面的空格数量不固定
十六进制表达式使用六位小写字母
如果输入不符合 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 驼峰格式,请完成此转换功能
以 - 为分隔符,将第二个起的非空单词首字母转为大写
-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 为出现频率
不限制 key 的顺序
输入的字符串参数不会为空
忽略空白字符
输入
'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了解还是太少。。。