欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

前端面试题总结_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]
}