五四青年节,今天要学习。汇总5道难度不高但可能遇到的JS手写编程题
壹 ❀ 引
时间一晃,今天已是五一假期最后一天了,没有出门,没有太多惊喜与意外。今天五四青年节,脑子里突然想起鲁迅先生以及悲欢并不相通的话,我的五一经历了什么呢,忍不住想说那大概是,父母教育孩子大声嚷嚷,隔壁装修电钻嗡嗡作响,戴上耳机敲着键盘书写每个白天晚上。
矫情完,那么回归本文正题,我在之前其实已经更新了大部分JS常考手写题,比如手写bind new apply call promise
,节流防抖等等,这些知识中有些较为复杂,所以都用了单独的篇幅去介绍,那自然还剩一些好理解一点的手写题,考虑到一个一篇文章浪费篇幅,也没必要,这里就统一整理,我们直接开始。
贰 ❀ 实现trim方法
String.prototype.trim()
方法能移除字符串前后的所有空格,这在于我们做表单验证时非常实用,那么现在要求手动实现trim
方法应该怎么做?
我们知道,String
上有一个replace
,用于将指定规则的字符串替换成我们想要的字符,因为空格也是字符,所以本质上只用将空格替换成空即可,但需要注意的是,像 hello echo 这种我们其实只需要将前后的空格去掉,而不能去除两个单词之间的空格。
现在的问题就是怎么匹配前后的空格的问题了,这里就需要借用正则表达式了,先上实现:
const trim = (s) => s.replace(/^\s+|\s+$/g, '');
console.log(trim(' hello echo ')) // 'hello echo'
console.log(trim('echo ')) // 'echo'
因为replace
接受一个正则用于表示目标字符的规则,所以假设大家有疑问,肯定就是疑问这段正则是什么意思,这里我给大家解释下:
\s
表示匹配空格,而+
表示量词,等价于{1,}
(至少一次或者多个),因此\s+
表示一个或者多个空格。- 管道符
|
,你可以理解成JS中的||
,表示满足前后条件其一均可 - 脱字符
^
和美元符$
在这里表示匹配开头和匹配尾部,毕竟我们也说了不要匹配字符串中间的空格 - 修饰符
g
表示全局匹配,毕竟一个字符串可能很长,我就是希望全局替换。
因此这段正则意思就是,全局匹配,匹配目标是字符串开头或结尾的1个或者多个空格,用图来表示:
另外,若大家对于^$
有疑惑,可以读一读博主JS 正则表达式$详解,脱字符与美元符$同时写表示什么意思?一文。若对于正则感兴趣,可以阅读博主从零开始学正则系列文章,学好正则在某些时刻真的非常非常有帮助。
另外,除了replace
之外,其实还有个replaceAll
方法,从一个最基本的例子来区分两者的区别:
' ec ho '.replace(' ', '');//'ec ho ' 只替换了开头第一个空格
' ec ho '.replaceAll(' ', '');//'echo' 开头结尾中间所有空格全没了,自带全局匹配
叁 ❀ 实现字符串翻转(实现reverse)
本题其实是leetcode 344. 反转字符串,题目要求给定:
输入:s = ["h","e","l","l","o"]
输出:["o","l","l","e","h"]
我:return s.reverse()
面试官:滚~
言归正传,本题可以理解为如何实现一个reverse
方法,首先可以想到的就是创建一个空数组,倒序遍历塞到新数组,然后返回:
const reverse = (arr) => {
const ans = [];
while (arr.length) {
ans.push(arr.pop())
};
return ans;
}
console.log(reverse(["h", "e", "l", "l", "o"])) // ['o', 'l', 'l', 'e', 'h']
当然本题其实有个限制,题目其实是希望你直接修改原数组,不要新建额外的数组去返回,也不卖关子,其实本题的原意是考核双指针的用法,我们来看两个简单的例子:
比如1,2,3
要变成3,2,1
,是不是只用交换1,3
的位置即可:
而像1,2,3,4
变成4,3,2,1
,我们其实只用做1,4
与2,3
互换即可:
所以我们完全可以定义两个指针,一个从索引0
开始,一个从length-1
开始,两两互换,然后一个递增一个递减:
const arr = ["h", "e", "l", "l", "o"];
var reverseString = function (s) {
var i = 0,
j = s.length - 1;
for (; i < j; i++, j--) {
// 解构赋值
[s[i], s[j]] = [s[j], s[i]];
};
console.log(s === arr)
};
reverseString(arr)
console.log(arr); // ['o', 'l', 'l', 'e', 'h']
肆 ❀ 数组去重
老生常谈的问题,这里不做太多赘述,ES5我们可以通过filter + indexOf
,实现如下:
const arr = [1, 2, 3, 3, 4, 4];
const unique = (arr) => arr.filter((ele, index) => index === arr.indexOf(ele));
console.log(unique(arr)) // [1,2,3,4];
我们知道filter
前两个参数,第一个表示当前元素,第二个是当前元素的索引,而indexOf
只能找到一个元素第一次出现的索引,所以我们通过index === arr.indexOf(ele))
来找到第一次出现的每个元素。
打个比方,以上面的arr
的例子,第一个3
出现时index
为2
,而arr.indexOf(3)
也是2
,符合条件所以把这个3
加入到了新数组。
遍历到第二个3
时index
是3
,而indexOf
找出的还是2
,不相等,这个3
就不要了。
ES6的Set
实现就更简单了:
const unique = (arr) => [...new Set(arr)]
这里就不多解释了。
伍 ❀ 实现flat方法
我们知道数组flat
方法可用于数组降维,比如:
[[1],[2]].flat(); // [1,2]
同时flat
接受一个参数,表示要降维几次,默认是1,像[[[1]]]
很明显需要降维2次,因此可以传递2
达到效果:
[[[1]]].flat(2); // [1]
那假设我也不清楚这个数组是几维,但是我就是要打瓶怎么办?这里可以传递Infinity
,表示无限大,多少维度都给你降了:
[[[[[[[[[[1]]]]]]]]]].flat(Infinity); //[1]
那么我们自己怎么实现false
呢?考虑到数组的元素也可能是数组,这里我们就可以考虑递归,只要当前元素是数组,开始递归,直到访问到真正的元素后,再塞入到新数组,递归完成一层层往上返回即可。
我知道,对于递归陌生的同学可能脑子里知道是怎么回事,但就是写不出来,这里我们再回忆下递归的准则:
- 我们需要定义一个函数,它会自己调用自己
- 只用想好当前需要做什么,递归会帮你做相同的事情
- 想好你递归的条件,什么时候跳出,毕竟你不可能一直递归下去
- 需要做返回值吗?不返回能不能解决
我们假定有个数组[1,2,3]
,需要你拷贝一份,可以怎么做?不假思索,新建数组,遍历后一一塞进去:
const traverse = (arr) => {
const arr_ = [];
arr.forEach((ele) => {
arr_.push(ele)
})
return arr_;
}
结合上面的例子比递归的特性,我们提炼下信息,注意,你永远只需要考虑当前需要做什么,递归的操作它自然会帮你做相同的操作,我们需要做什么?
递归要做什么操作?很显然是遍历数组,如果当前元素不是数组,直接push
,如果是数组,递归一次,到这就不用继续在脑子里递归了,递归自然会帮你做好。
递归跳出条件是什么?上面也说了,只有是数组时才会递归,不需要考虑跳出。
需要考虑返回吗?需要,因为我们每次都新建了一个数组,用于装当前普通元素,如果递归了需要返回给上一层。
直接写代码:
const flat = (arr) => {
const arr_ = [];
arr.forEach((ele) => {
// 是数组吗?
Array.isArray(ele) ?
arr_.push.apply(arr_, flat(ele)) :// 是数组就递归,不断降维
arr_.push(ele);
})
return arr_;
}
或者:
const flat = (arr) => {
const arr_ = [];
arr.forEach((ele) => {
arr_.push.apply(arr_,
Array.isArray(ele) ?
flat(ele) : [ele]
)
})
return arr_;
}
为啥要用apply
呢,因为flat
递归后返回一个数组,push
不能直接压入数组啊,我是希望压入一个个的元素,而apply
参数正好是个数组,所以才用了这种写法。
陆 ❀ 实现千位分隔符
千位分隔符在银行账号上非常常见,比如12345678
分隔后就是12,345,678
,相当于从后往前数,每三位加一个逗号。怎么做呢?一种做法是将数字转为字符串,之后分割成数组,然后倒序遍历,每三个塞一个逗号即可,思路比较简单,直接上代码:
const fn = (num) => {
const s = (num + '').split('');
const ans = [];
let len = s.length - 1;
// 用于统计遍历次数,每三次塞一个逗号
let n = 0;
while (len >= 0) {
ans.unshift(s[len]);
n += 1;
// 考虑123456789的情况
if (n % 3 === 0 && len !== 0) {
ans.unshift(',');
};
len--;
};
return ans.join('');
};
console.log(fn(12345678)); // 12,345,678
console.log(fn(123456789)); // 123,456,789
需要注意塞逗号这里的len !== 0
,这是因为不加这个判断123456789
会变成,123,456,789
,这里就不多说了。
第二种做法当然还是我们的正则,同理,我们从后往前数,每三个位置塞一个逗号,同时过滤开始的位置:
const fn = (num) => (num + '').replace(/(?!^)(?=(\d{3})+$)/g, ',');
关于正则如何实现千位运算符,我在从零开始学正则(三),理解正则的分组与反向引用文章开头有讲,如果摊开讲,又涉及到正则位置概念的普及,反向负向先行断言等,比较复杂,所以我还是建议大家自行学习,有问题可以留言问我,我再做解释。
对了,我突然想到,千位运算符得考虑小数点的情况,这里我说说正则怎么做,毕竟数组的实现本质上还是截取小数点前面的做相同的操作最后再拼接而已,对于正则,我们还是一样先匹配小数点前面的部分,在对这一步做我们上面的千位运算替换即可,实现为:
// 先匹配小数点前面的数字部分,再执行千位运算符替换,两个replace搞定
const fn = (num) => (num + '').replace(/\d+/, (n) => n.replace(/(?!^)(?=(\d{3})+$)/g, ','));
console.log(fn(12345678.123)); // 12,345,678.123
console.log(fn(123456789.123)); // 123,456,789.123
柒 ❀ 总
那么到这里,五道简单的手写编程题搞定,今天是五四,因为想起了鲁迅先生,所以借用他的话做文章的结尾。
愿中国青年都摆脱冷⽓,只是向上⾛,不必听⾃暴⾃弃者流的话。能做事的做事,能发声的发声。有⼀分热,发⼀分光,就令萤⽕⼀般,也可以在⿊暗⾥发⼀点光,不必等候炬⽕。----鲁迅
那么到这里,本文结束。
五四青年节,今天要学习。汇总5道难度不高但可能遇到的JS手写编程题的更多相关文章
- 常见的JS手写函数汇总(代码注释、持续更新)
最近在复习面试中常见的JS手写函数,顺便进行代码注释和总结,方便自己回顾也加深记,内容也会陆陆续续进行补充和改善. 一.手写深拷贝 <script> const obj1 = { name ...
- 50道经典的JAVA编程题(汇总)
这是一次不可思议的编程历程.从2013年的最后一天开始做这份题,中间连续好几天的考试,包括java考试(今天考试的JAVA编程题),直到今天完成了.挺有成就感的...废话不多说了,来电实质性的吧. 全 ...
- 50道经典的JAVA编程题 (6-10)
50道经典的JAVA编程题 (6-10),今晚做了10道了,累死了...感觉难度不是很大,就是不知道是不是最好的实现方法啊!希望大神们能给指点哈... [程序6]GCDAndLCM.java 题目:输 ...
- 识别手写数字增强版100% - pytorch从入门到入道(一)
手写数字识别,神经网络领域的“hello world”例子,通过pytorch一步步构建,通过训练与调整,达到“100%”准确率 1.快速开始 1.1 定义神经网络类,继承torch.nn.Modul ...
- 汤姆大叔的6道javascript编程题题解
看汤姆大叔的博文,其中有篇(猛戳这里)的最后有6道编程题,于是我也试试,大家都可以先试试. 1.找出数字数组中最大的元素(使用Math.max函数) var a = [1, 2, 3, 6, 5, 4 ...
- 50道经典的JAVA编程题(46-50)
50道经典的JAVA编程题(46-50),最后五道题了,这是一个美妙的过程,编程真的能让我忘掉一切投入其中,感觉很棒.今天下午考完微机原理了,大三上学期就这样度过了,这学期算是解放了,可是感觉我还是没 ...
- 50道经典的JAVA编程题(41-45)
50道经典的JAVA编程题(41-45),苦逼的程序猿,晚上睡不着了编程吧~今天坚持做10道题!发现编程能是我快乐...O(∩_∩)O哈哈~能平静我烦乱的心,剩下5道题留到考试完了再做吧!该睡觉了.. ...
- 50道经典的JAVA编程题(36-40)
50道经典的JAVA编程题(36-40),今天晚上心情压抑,不爽,继续做题,管它明天考试,我继续我的java,一个周末都在看微机原理看得的很头疼啊~明天该挂科就挂吧,不在乎了~~~ [程序36] Ar ...
- 50道经典的JAVA编程题(31-35)
50道经典的JAVA编程题(31-35),今天考完了java,在前篇博客里面贴出了题了,见:<今天考试的JAVA编程题>.考完了也轻松了,下个星期一还考微机原理呢,啥都不会,估计今天就做到 ...
随机推荐
- org.apache.kafka.common.errors.SerializationException: Error deserializing... Caused by: org.apache.kafka.common.errors.SerializationException: Size of data received by IntegerDeserializer is not 4
原因,最近开发的kafka消息接收,突然报如下错: org.apache.kafka.common.errors.SerializationException: Error deserializing ...
- Java 中 notify 和 notifyAll 有什么区别?
notify() 方法不能唤醒某个具体的线程,所以只有一个线程在等待的时候它才有 用武之地.而 notifyAll()唤醒所有线程并允许他们争夺锁确保了至少有一个线程 能继续运行.
- JdbcTemplate ?
JdbcTemplate 类提供了很多便利的方法解决诸如把数据库数据转变成基本数据 类型或对象,执行写好的或可调用的数据库操作语句,提供自定义的数据错误处 理.
- sudo rosdep init 出现 ERROR: cannot download default sources list from:错误解决方法
关于安装ROS时出现的rosdep init错误 sudo rosdep init ERROR: cannot download default sources list from: https:// ...
- c、c++中-int型以float或者float型以int输出问题
1.将浮点型以整形的类型输出问题 用VC6.0,会把以整形输出形式的浮点数输出为0: 1 #include"stdio.h" 2 int main() 3 { 4 float x= ...
- Altium design使用日常故障总结
1.altiumdesigner09如何将不同的板子拼在一起发给工厂?打开这两个图,其中一个图ctrl+a,ctrl+c,打开另一个图pastespecial.放置时选取一边对齐.制版时告诉厂家做个V ...
- Kurento安装与入门08——Group Call
Group Call 本示例展示了一个视频聊天室的功能,不同的聊天室之间互相隔离. 官网文档 Group Call 首先从github上获取代码(如果已经获取可以跳过,获取的代码已经包括后面的示例代码 ...
- DOM节点的使用(常用方法+代码)
DOM节点的应用 学习总结 1. 什么是 DOM 2. HTMLDOM 3. 元素获取 元素获取方式 元素节点的属性操作 4. Node 对象的属性和方法 常用属性 常用方法 5. 事件处理 事件驱动 ...
- executeFind()方法和execute()方法区别
返回类型不同:executeFind()方法返回集合,execute()方法返回对象 executeFind @Override public List<TCpContact> getCp ...
- 在vue中实现点击哪个哪个区域变化背景色和字体颜色,其他默认(uni-app框架中也可以使用)
template: 1 <view class="wrap"> 2 <view class="total" :class="{ se ...