前端面试题总结_01
程序员文章站
2022-03-04 12:03:45
...
/**
1. 实现一个 sleep 函数
function sleep(ms) {
// start here
}
// 调用方式
async () => {
await sleep(500);
// 500ms 以后
console.log('xxx');
}
*/
function sleep(ms) {
return new Promise((resolve)=>{
setTimeout(() => {
resolve()//需要调用resolve才能执行接下到的函数
}, ms);
})
}
a=async () => {
await sleep(1000)
console.log('111')
}
/**
* 相关知识点:异步操作无法捕获异常,Promise的用法
* 2. 下列操作中,异步的错误无法被 try… catch 埔获,请修改
const url = ‘/sample/api’
try {
asyncAjaxGet(url, (response) => {
response.data.children = 1; // 可能有异常,期望被捕获
})
} catch(e) {
// ....
}
*/
function asyncAjaxGet(url,r) {
}
const url = '/sample/api'
function handle(url) {
return new Promise((resolve) => asyncAjaxGet(url, resolve))
}
const response = handle(url)
try {
response.data.propA // 这里如果response是undefined的话就能被catch到
} catch {
// 错误处理
}
/**
* 3. 扩展异步请求函数 ajax() 增加最大重传次数参数,是的只有当重试次数大于最大重传次数后,
才从失败回调中返回,否则直接返回成功的回调
retryAjax(url, {
onSuccess, // 成功的回调
onFail, // 失败的回调
maxRetryTimes // 最大重传次数
});
*/
function retryAjax(url, {onSuccess,onFail,maxRetryTimes}) {
ajax(url).then(()=>{
onSuccess()
}).catch(()=>{
if (maxRetryTimes>1) {
maxRetryTimes--
retryAjax(url, {onSuccess,onFail,maxRetryTimes})
} else {
onFail()
}
})
}
// tips: async await + 循环
// 答案:
async function retryAjax(url, {onSuccess, onFail, maxRetryTimes}){
let result;
let isSuccess = false;
for(let i=0; i<maxRetryTimes && !isSuccess; i++){
try{
result = await ajax(url);
onSuccess(result);
isSuccess = true;
} catch(e){
}
}
if(!isSuccess){
onFail();
}
}
/**
* 4. 实现一个 arrange 函数,可以进行时间和工作调度
* 注意,这里的 wait do 均可以无限调用
*
* - 具体功能参考下列示例
* - 在示例中调用到的方法都需要实现
* - 下面示例中 `>` 表示在控制台中输出 (console.log)
*
* --- 示例 ---
*
* 示例一:
* `arrange('William').execute();`
* > William is notified
*
* 示例二:
* `arrange('William').wait(5).do('commit').wait(5).do('push').execute();`
* > William is notified
* 等待 5s...
* > Start to commit
* 等待 5s
* > Start to push
*
*/
// 实现 wait,do,execute
function arrange(str) {
return new Arrange(str);
}
class Arrange {
constructor(str){
this.str = str
this.delay = 0
}
execute = function () {
console.log(this.str)
return this
}
wait = function (delay) {
// console.log(this,delay)
this.delay += delay*1000
return this
}
do = function (str2) {
setTimeout(() => {
console.log(str2)
}, this.delay);
return this
}
}
// arrange('William').wait(5).do('commit').wait(5).do('push').execute();
// // arrange('William').execute();
//类的多个参数
class A {
constructor(...props){
this.props = props
}
c = function () {
console.log(this.props)
}
}
a = new A(1,2,3,45,6,'232',{1:1},[1,23])
// a.c()// [1, 2, 3, 45, 6, "232", {…}, Array(2)]
/**
* 5.蚂蚁金服面试题目:实现一个可以通过参数缓存任意函数返回值的函数
* 同一个参数下次调用不计算直接拿缓存,缓存数据不能太多要有清理策略
* 相关知识点:Map,缓存策略
*/
// LRU策略
function record(func, maxRecordSize) {
let map = new Map();
return function () {
let key = Array.prototype.slice.call(arguments)[0];
let maxFrequency = 0;
let minFrequency = Infinity;
let minFrequencyKey = '';
map.forEach((value, key) => {
maxFrequency = Math.max(maxFrequency, value.frequency);
if (value.frequency < minFrequency) {
minFrequency = value.frequency;
minFrequencyKey = key;
}
})
if (map.has(key)) {
let item = map.get(key);
map.set(key, { ...item, frequency: maxFrequency + 1 }); // 调整权重
return item.result;
} else {
console.log("compute!")
let result = func.apply(this, arguments);
if (map.size >= maxRecordSize) { // 删除一个
console.log("DEL", minFrequencyKey)
map.delete(minFrequencyKey);
}
map.set(key, { result: result, frequency: maxFrequency + 1 });
console.log("SET", key, { result: result, frequency: maxFrequency + 1 })
return result;
}
}
}
/**
* 6.实现转化为驼峰命名的函数
* 将 aa-bb_cc 形式的以 -或_连接的字符串转化成驼峰命名,首字母大小写不变
* replace第二个参数可以接收一个函数,并将函数返回值作为替换的newValue
*/
// Convert string to camel case
function toCamelCase_1(str) {
let reg = /(^|(-|_)[a-zA-Z])/g;
return str.replace(reg, match => {
if (match.length > 1) {
console.log(match)
return match.toUpperCase().substring(1);
} else {
return match.toUpperCase();
}
})
}
// 更好的解法
function toCamelCase_2(str){
let regExp=/[-_]\w/ig;
return str.replace(regExp,function(match){
console.log(match)
return match.charAt(1).toUpperCase();
});
}
// console.log(toCamelCase_1('aa-bb_cc'))
let r = /[A-z]/g
let str = 'aavcac5465acas'
let b=str.replace(r,m=>{
console.log(m)
return m.toUpperCase()
})
// console.log(b)//AAVCAC5465ACAS
/**
* 7.实现公共区间merge的方法
* 腾讯面试题目
* [[0, 1], [2, 5], [3, 7]] => [[0, 1], [2, 7]]
* 假设原数组按照每个数组的第一个元素大小排好序
* 使用reduce解决
* arr.reduce(callback(accumulator, currentValue[, index[, array]])[, initialValue])
* @param {Array} arr
*/
function merge(arr) {
return arr.reduce((preValue,curValue) => {
if (preValue === [] || preValue[preValue.length-1][1] < curValue[0] ) {
preValue.push(curValue)
} else {
if (preValue[preValue.length-1][1] < curValue[1]) {
preValue[preValue.length-1] = [preValue[preValue.length-1][0],curValue[1]]
}
}
},[])
}
//使用reduce实现map
if (!Array.prototype.mapUsingReduce) {
Array.prototype.mapUsingReduce = function(callback, thisArg) {
return this.reduce(function(mappedArray, currentValue, index, array) {
mappedArray[index] = callback.call(thisArg, currentValue, index, array)
return mappedArray
}, [])
}
}
// [1, 2, , 3].mapUsingReduce(
// (currentValue, index, array) => currentValue + index + array.length
// ) // [5, 7, , 10]
/**
* 树形解构的递归遍历
* 蚂蚁金服面试题目
*
*/
// 题目: 数据查找根据id,查找树中的任意一项,例如给出输入:
var data = [{
id: '100',
name: '上海',
children: [{
id: '101',
name: '浦东',
children: []
},{
id: '102',
name: '浦西',
children: []
},]
},{
id:'200',
name: '杭州',
children: [{
id: '201',
name: '西湖区',
children: [{
id: '221',
name: '黄龙时代广场',
children: []
}]
},{
id: '202',
name: '余杭区',
children: []
},]
}]
// find(data, '101'),输出 浦东
// find(data, '201'),输出 西湖区
// 答案:递归查找
function find(data, id) {
for (let o of data) {
if (o && o.id === id) {
return o.name;
}
}
for (let o of data) {
if (o.children && o.children.length > 0) {
let res = find(o.children, id);
if (res) {
return res;
}
}
}
return null;
}
/**
* 找到整数数组中出现次数为奇数次的整数
* Map
* Map.prototype.delete(key):
* 如果 Map 对象中存在该元素,则移除它并返回 true;否则如果该元素不存在则返回 false。随后调用 Map.prototype.has(key) 将返回 false 。
*
* Map.prototype.has(key):
* 返回一个布尔值,表示Map实例是否包含键对应的值。
*
* Map.prototype.set(key, value):
* 设置Map对象中键的值。返回该Map对象。
*
* @param {Array} arr
*/
function findOdd(arr) {
let res = arr.reduce((accumulator, currentValue) => {
if (accumulator.has(currentValue)) {
accumulator.delete(currentValue)
} else {
accumulator.set(currentValue, 1)
}
return accumulator
}, new Map())
// console.log(res.keys())
for (const a of res.keys()) {
console.log(a)
}
}
//若有唯一一个奇数次的数,可用异或,相同的数偶数次异或为0
findOdd2 = (arr) => arr.reduce((a, b) => a ^ b)
// let arr = [1,1,1,1,2,2,3,4,4,5,5,5,5,6,6]
// findOdd(arr)
// console.log(findOdd2(arr))//3
/**
* 实现一个函数,判断数字是否连续,出现连续数字时以'-'输出
* 如: const arr = [ 2, 3, 4, 7, 8, 9, 10,13,15]
* 期望结果:["2-4", "7-10", 13, 15]
* @param {Array} arr
*/
function merge(arr) {
let temp = arr.reduce((a, c) => {
if (a.length === 0) {
a.push([c])
} else {
let p = a[a.length - 1]
if (p[p.length - 1] === c - 1) {
a[a.length - 1].push(c)
} else {
a.push([c])
}
}
return a
}, [])
return temp.map(x => x.length > 1 ? `${x[0]}-${x[x.length-1]}` : x[0])
}
//方法二:双指针
function merge2(arr) {
let i = 0,
j = 0,
res = []
while (i < arr.length) {
while (j < arr.length && arr[j + 1] === arr[j] + 1) {
j++
}
if (i === j) {
res.push(arr[i])
} else {
res.push(`${arr[i]}-${arr[j]}`)
}
j++
i = j
}
return res
}
// let arr = [1,2,3,5,6,7,9,11,21,22,23]
// console.log(merge(arr))
// console.log(merge2(arr))
/**
* 判断输出1
* 腾讯面试题目
*
* 因为函数Page()的返回值是一个对象,这个对象是在Page.prototype.hosts = ['h2'];这一句时被赋值的,如果没有这一句,
* 那么this.hosts是undefined,p1就还是个Page类型的对象,而现在p1是['h2'],因此p1.hosts为undefined;
* p2没有用new操作符,Page只是被当作普通函数调用,因此p2的值就是undefined;
* 相关知识点:对象,构造函数,原型
*/
function Page() {
return this.hosts
}
Page.hosts = ['1']
Page.prototype.hosts = ['2']
const p1 = new Page()
const p2 = Page()
// console.log(p1.hosts)//undefined
// console.log(p2.hosts)//报错:Uncaught TypeError: Cannot read property 'hosts' of undefined
/**
* 判断输出2
* 腾讯
*
* var的变量提升
* 用let声明的变量value之所以会报错,是因为let/const造成的“暂时性死区”的问题;
* 用let/const声明的变量,在包含它们的上下文被创建时就会被创建,
* 但是,只有在变量的词法绑定已经被求值运算后(赋值),才能够被访问;
* 所以严格来讲,let/const也会和var一样被变量提升,在赋值语句之前访问会报错;
*
* 在本题中,由于let value = 21;一句,在function的上下文中,value已经在上下文创建之初存在了,
* 所以它的存在屏蔽了对外层value的访问,然而,由于let的暂时性死区的限制,它虽然存在但是无法被访问,所以这里会报错;
*
* 相关知识点:let,暂时性死区,作用域,变量提升
*/
var value = 20;
(function () {
console.log(name) //undefined
console.log(value) // 报错: Uncaught ReferenceError: Cannot access 'value' before initialization
var name = '1'
let value = '21'
})()
/**
* 将每个单词首字母移到单词最后并添加ay
* pigIt('Pig latin is cool'); // igPay atinlay siay oolcay
* 相关知识点:正则表达式,正则表达式捕获匹配结果
* @param {String} str
*/
function pigIt(str) {
let reg = /\w+/gi
let list = str.split(' ').map(item => {
if (item.match(reg)) {
return item.substr(1) + item.charAt(0) + 'ay'
} else {
return item
}
})
return list.join(' ')
}
function pigIt2(str) {
return str.replace(/(\w)(\w*)(\s|$)/g, '\$2\$1ay\$3')
}
function pigIt3(str) {
return str.replace(/(\w)(\w*)(\s|$)/g, '$2$1ay$3')
}
let str = 'Pig latin is cool'
// console.log(pigIt(str))
// console.log(pigIt2(str))
// console.log(pigIt3(str))
//一个正则表达式模式使用括号,将导致相应的子匹配被记住。
// var r = /(\w+)\s(\w+)\s(\w+)\s(\w+)/
// let res = str.replace(r,'$4,$3,$2,$1')
// console.log(res)//cool,is,latin,Pig
/**
* 十进制数转化为N进制数的字符串,2<=N<=16
* 腾讯面试
* let a = 5;a.trans(3)//'12'
*/
Number.prototype.trans = function (base) {
const map = ['A', 'B', 'C', 'D', 'E', 'F']
let res = []
let num = this
while (num > 0) {
let q = num % base
num = Math.floor(num / base)
res.unshift(q > 9 ? map[q - 10] : q)
}
return res.join('')
}
// let n = 100
// console.log(n.trans(16))//64
/**
* 求岛屿的周长
* 字节面试
* 输入m*n的矩阵,1代表岛屿,0代表路径,输入一个坐标,求坐标所在的岛屿的周长
* 递归
*/
function getBorderLength(a, b, arr, borderLength = 0) {
if (arr[a][b] === 0) {
return 0
}
// let borderLength = 0
arr[a][b] = -1
//top
if (a > 0 && arr[a - 1][b] === 1) {
borderLength += getBorderLength(a - 1, b, arr, borderLength)
} else if (a === 0 || a > 0 && arr[a - 1][b] === 0) {
borderLength += 1
}
//right
if (b < arr[0].length - 1 && arr[a][b + 1] === 1) {
borderLength += getBorderLength(a, b + 1, arr, borderLength)
} else if (b === arr[0].length - 1 || b < arr[0].length - 1 && arr[a][b + 1] === 0) {
borderLength += 1
}
//bottom
if (a < arr.length - 1 && arr[a + 1][b] === 1) {
borderLength += getBorderLength(a + 1, b, arr, borderLength)
} else if (a === arr.length - 1 || a < arr.length - 1 && arr[a + 1][b] === 0) {
borderLength += 1
}
//left
if (b > 0 && arr[a][b - 1] === 1) {
borderLength += getBorderLength(a, b - 1, arr, borderLength)
} else if (b === 0 || b > 0 && arr[a][b - 1] === 0) {
borderLength += 1
}
return borderLength
}
/**
* 求最长增路径
* 字节面试
* 深度优先遍历 + 动态规划
*/
function findPath(matrix) {
const dp = []; // 与 matrix 结构相同
let max = 0;
for (let i = 0; i < matrix.length; i++) {
for (let j = 0; j < matrix[i].length; j++) {
max = Math.max(max, dfs(matrix, dp, i, j, Number.MIN_VALUE));
}
}
return max;
}
function dfs(matrix, dp, i, j, preVal) {
// 边界判断
if (i < 0 || j < 0 || i > matrix.length - 1 || j > matrix[i].length - 1) {
return 0;
}
// 是否递增判断
if (matrix[i][j] <= preVal) {
return 0;
}
// 如果该点已经判断过,就返回结果
if (dp[i][j] !== 0) {
return dp[i][j];
}
// 以上情况均不存在,递归上下左右方向的值
let left = dfs(matrix, dp, i - 1, j, matrix[i][j]);
let right = dfs(matrix, dp, i + 1, j, matrix[i][j]);
let up = dfs(matrix, dp, i, j - 1, matrix[i][j]);
let down = dfs(matrix, dp, i, j + 1, matrix[i][j]);
// dp[i][j] = 1 + max(left, right, up, down)
dp[i][j] = 1 + Math.max(left, right, up, down)
return dp[i][j]
}