2021-03-29 | UNLOCK

前端经典面试题总结

今天浏览知乎上的一篇文章web前端大厂10道经典面试题汇总(含答案详解),自己照着思路尝试着写一版。

1、写一个js函数,实现对一个数字每3位加一个逗号,如输入100000, 输出100,000(不考虑负数,小数)—百度前端面试题

解题思路

将数字调用toString方法变成字符串,然后用split方法切割成数组;用reverse方法对数组进行倒序,用reduce方法对数组每3位后面添加“,”,最后再用reverse方法将数组倒序回来,并用join方法进行拼接。

1
2
3
4
5
6
const num = 100000
const arr = num.toString().split('')
const str = arr.reverse().reduce((acc, cur, index) => {
if (index % 3 === 0) { return `${acc},${cur}` } else { return acc + cur }
}).split('').reverse().join('')
console.log(str) // 100,000

拓展

题目虽然说不考虑负数和小数,但如果考虑进去需要多做什么处理呢?

1、考虑负数:由于负数只会在最前面出现一次,所以只需要在整除3余数是否为零的条件下再多一个当前值cur是否为“-”即可,代码如下:

1
2
3
4
5
6
const num = -100000
const arr = num.toString().split('')
const str = arr.reverse().reduce((acc, cur, index) => {
if (index % 3 === 0 && cur !== '-') { return `${acc},${cur}` } else { return acc + cur }
}).split('').reverse().join('')
console.log(str) // -100,000

2、考虑小数:以小数点为界限,切分整数部分和小数部分,整数部分按照原来的思路方法,最后把小数部分拼接回去即可,代码如下:

1
2
3
4
5
6
7
const num = -100000.12
const arr = num.toString().split('.')
const prevArr = arr[0].split('')
const str = prevArr.reverse().reduce((acc, cur, index) => {
if (index % 3 === 0 && cur !== '-') { return `${acc},${cur}` } else { return acc + cur }
}).split('').reverse().join('') + (arr[1] ? `.${arr[1]}` : '')
console.log(str) // -100,000.12

总结

以上功能其实就是toLocalString方法的简单实现,实验中我发现,关于小数点后面如果是“.00”,在使用toString和toLocalString方法后都会自动去掉,并不会保留小数部分,代码如下:

1
2
3
4
5
6
7
8
const num = -100000.00
const arr = num.toString().split('.')
const prevArr = arr[0].split('')
const str = prevArr.reverse().reduce((acc, cur, index) => {
if (index % 3 === 0 && cur !== '-') { return `${acc},${cur}` } else { return acc + cur }
}).split('').reverse().join('') + (arr[1] ? `.${arr[1]}` : '')
console.log(num.toLocaleString()) // -100,000
console.log(str) // -100,000

这点不知道是不是我多虑了,不过我也试着想了一个不完美解决办法,一般这种情况可以直接调用toFixed方法保留小数点后几位(比如两位),再做下一步处理,代码如下:

1
2
3
4
5
6
7
const num = -100000.00
const arr = num.toFixed(2).split('.')
const prevArr = arr[0].split('')
const str = prevArr.reverse().reduce((acc, cur, index) => {
if (index % 3 === 0 && cur !== '-') { return `${acc},${cur}` } else { return acc + cur }
}).split('').reverse().join('') + (arr[1] ? `.${arr[1]}` : '')
console.log(str) // -100,000.00

2、给定一个字符串,找出其中无重复字符的最长子字符串长度—字节跳动前端面试题

解题思路

将字符串调用split方法切割成数组,用reduce方法对数组进行判断无重复拼接,通过判断当前字符是否存在,如果不存在则拼接,并保存字符串长度最大值,存在则删除重复字符串之前的字符,重新拼接新的字符串,最终获得最长的字符串长度。

1
2
3
4
5
6
7
8
9
10
11
12
13
const str = 'abbbcbd'
let len = 1
str.split('').reduce((acc, cur) => {
const idx = acc.indexOf(cur)
if (idx === -1) {
const noRepeatStr = acc + cur
len = Math.max(len, noRepeatStr.length)
return noRepeatStr
} else {
return acc.substring(idx + 1) + cur
}
})
console.log(len) // 3

拓展

题目只要最长字符串长度,如果需要找出这些最长字符串呢?

其实只需要在保存记录长度的时候,顺便保存一下字符串数组即可,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
const str = 'abbbcbd'
let len = 1
let arr = []
str.split('').reduce((acc, cur) => {
const idx = acc.indexOf(cur)
if (idx === -1) {
const noRepeatStr = acc + cur
if (noRepeatStr.length > len) { // 保存字符串
arr.push(noRepeatStr)
}
len = Math.max(len, noRepeatStr.length)
return noRepeatStr
} else {
return acc.substring(idx + 1) + cur
}
})
console.log(len) // 3
console.log(arr.filter(v => v.length === len)) // ['cbd']

总结

拓展里面返回的字符串是一个数组,这里说明一下,有可能存在长度相同的多个最长无重复字符串,因此需要一个数组来存储会比较合适。

实现超出整数存储范围的两个大正整数相加—腾讯前端面试题

解题思路

将两个大整数进行比较,对不足的位数进行补零,然后将其用split方法拆解成数组,并用reverse方法反转数组顺序,接着按位一一对应相加,注意进位即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
let a = '123123231231234444435', b = '434235656455454543'
const n1 = a.length, n2 = b.length
const n = Math.max(n1, n2)
for (let i = 0; i < n - Math.min(n1, n2); i++) {
if(n1 > n2) b = '0' + b
if(n1 < n2) a = '0' + a
}
a = a.split('').reverse()
b = b.split('').reverse()
let res = Array.from({ length: n }, v => 0)
for (let k = 0; k < n; k++) {
const temp = parseInt(a[k]) + parseInt(b[k])
if (temp > 9) {
res[k] += temp - 10
res[k + 1] = 1
} else {
res[k] += temp
}
}
res = res.reverse().join('')
console.log(res) // 123557466887689898978

拓展

首先了解超出存储范围的大数字概念,每种数据类型可存储数据量都是存在范围的

数字类型的范围:

Number.MAX_VALUE = 1.7976931348623157e+308

Number.MIN_VALUE = 5e-324

整数类型的范围:-2^-53 - 2^53

总结(摘至JavaScript 教程 数值

根据国际标准 IEEE 754,JavaScript 浮点数的64个二进制位,从最左边开始,是这样组成的。

第1位:符号位,0表示正数,1表示负数
第2位到第12位(共11位):指数部分
第13位到第64位(共52位):小数部分(即有效数字)
符号位决定了一个数的正负,指数部分决定了数值的大小,小数部分决定了数值的精度。

指数部分一共有11个二进制位,因此大小范围就是0到2047。IEEE 754 规定,如果指数部分的值在0到2047之间(不含两个端点),那么有效数字的第一位默认总是1,不保存在64位浮点数之中。也就是说,有效数字这时总是1.xx…xx的形式,其中xx..xx的部分保存在64位浮点数之中,最长可能为52位。因此,JavaScript 提供的有效数字最长为53个二进制位。

1
(-1)^符号位 * 1.xx...xx * 2^指数部分

这里还有一篇关于IEEE 754的详细介绍,有兴趣的可以看看IEEE 754浮点数标准详解

4、任意二维数组的全排列组合—阿里巴巴前端面试题

解题思路

把多项数组逐步两两相乘的方式,第一次先取二维数组前两项组合,把组合的结果在与第三项组合以此类推。这种递归做法简单易懂,把复杂的多项问题简化成两项问题的逐渐递增。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const exchange = (arr) => {
const len1 = arr[0].length
const len2 = arr[1].length
const lenBoth = len1 * len2
let items = new Array(lenBoth), index = 0
for (let i = 0; i < len1; i++) {
for (let j = 0; j < len2; j++) {
items[index] = arr[0][i] + ',' + arr[1][j]
index++
}
}
arr.shift() // 删除其中一个
arr[0] = items // 将第一个变成运算结果
if (arr.length > 1) { return exchange(arr) } else { return arr[0] }
}
const array = [['A', 'B', 'C'], ['A1', 'B1', 'C1'], ['A2', 'B2']]
console.log(exchange(array)) // ["A,A1,A2", "A,A1,B2", "A,B1,A2", "A,B1,B2", "A,C1,A2", "A,C1,B2", "B,A1,A2", "B,A1,B2", "B,B1,A2", "B,B1,B2", "B,C1,A2", "B,C1,B2", "C,A1,A2", "C,A1,B2", "C,B1,A2", "C,B1,B2", "C,C1,A2", "C,C1,B2"]

5、公司最近新研发了一种产品,共生产了n件。有m个客户想购买此产品,已知每个顾客出价。为了确保公平,公司决定要以一个固定的价格出售产品。每一个出价不低于要价的客户将会得到产品(每人只买一个),余下的将会被拒绝购买。请你找出能让公司利润最大化的售价。—京东前端面试题

解题思路

本题是京东的业务演变题,首先要理清思路。本题中,固定出价,以及出价低于产品的顾客会被拒绝购买是解题核心。

条件中已知产品总个数,顾客出价。这里有个小陷阱,会出现N < M供不应求的情况,要特殊考虑。依据题目,我们首先需要对顾客出价排序,这里按升序排列。当供不应求出现时,我们截取出价高的顾客。然后把每个顾客的出价当做最终售价循环,得出最大化利润下的售价。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const getOptimal = (n, m, arr) => {
let key = 0, max = 0
arr = arr.sort((a, b) => a - b)
if (n < m) arr = arr.slice(m - n)
for (let i = 0; i < arr.length; i++) {
if (max < arr[i] * (arr.length - i)) {
max = arr[i] * (arr.length - i)
key = arr[i]
}
}
return key
}
const n = 3, m = 4, arr = [2, 8, 10, 7]
console.log(getOptimal(n, m, arr)) // 7

6、计算出字符串中出现次数最多的字符是什么,出现了多少次?—华为前端面试题

解题思路

计算出全部字符出现次数,并留下最大的。首先利用filter()与indexOf()的方法连用字符串去重,再将得到的作为索引,利用split()分割字符串,得到字符出现次数,比较得出结果。

1
2
3
4
5
6
7
8
9
10
11
const str = 'abbbcbd'
let count = 0, char = ''
const arr = [...new Set(str.split(''))]
for (let i = 0; i < arr.length; i++) {
const n = (str.split(arr[i])).length - 1
if (count < n) {
count = n
char = arr[i]
}
}
console.log(count, char) // 4 "b"

7、”123456789876543212345678987654321…”的第n位是什么?—小米面试题

解题思路

利用数学中最小循环节的概念解题,找到最小循环节后,利用余数查找第n位数字。

1
2
3
4
5
const k = '1234567898765432'
const getNum = (n) => {
return k.charAt(n % k.length - 1)
}
console.log(getNum(20)) // 4

8、请编写一个 JavaScript 函数 parseQueryString,它的用途是把 URL 参数解析为一个对象—淘宝面试题

解题思路

淘宝这道题是很常用的场景题,这里需要处理好分段次序,首先把?分离,然后按&分割最后按=分割,主要考察字符串的函数运用以及对象的创建。

1
2
3
4
5
6
7
8
9
10
const parseQueryString = (url) => {
const arr = url.split('?')[1].split('&')
let obj = {}
for (let i = 0; i < arr.length; i++) {
const value = arr[i].split('=')
obj[value[0]] = value[1]
}
return obj
}
console.log(parseQueryString('https://www.baidu.com/s?wd=shiyuanjieyi&rsv_spt=1&rsv_iqid=0xb198d7d800011de6&issp=1&f=8&rsv_bp=1&rsv_idx=2&ie=utf-8&tn=baiduhome_pg&rsv_enter=1&rsv_dl=tb&rsv_sug3=14&rsv_sug1=13&rsv_sug7=101&rsv_sug2=0&rsv_btype=i&inputT=29439&rsv_sug4=29439')) // {wd: "shiyuanjieyi", rsv_spt: "1", rsv_iqid: "0xb198d7d800011de6", issp: "1", f: "8", …}

9、如果给定的字符串是回文,返回true,反之,返回false。回文:如果一个字符串忽略标点符号、大小写和空格,正着读和反着读一模一样,那么这个字符串就是palindrome(回文)。—网易前端面试题

解题思路

去掉字符串多余的标点符号和空格,然后把字符串转化成小写来验证此字符串是否为回文。

1
2
3
4
5
6
const palindrome = (str) => {
const str1 = str.replace(/[^0-9a-zA-Z]/g, '').toLowerCase()
const str2 = str1.split('').reverse().join('')
return str1 === str2
}
console.log(palindrome('aBc,./1d42--==EFG0 00 h0 :00gfE24d 1cBA')) // true

10、确保字符串的每个单词首字母都大写,其余部分小写。——搜狐前端面试题

解题思路

  • 字符串转化成小写;
  • 分割成字符串数组;
  • 新组合字符串元素=首字母转大写+其余小写。
1
2
3
4
5
const upperCase = (str) => {
const arr = str.toLowerCase().split(' ')
return arr.map(v => v[0].toUpperCase() + v.slice(1)).join(' ')
}
console.log(upperCase("I'm a title Case.")) // I'm A Title Case.

(完)

评论加载中