关于javascript:分享JS几个有意思的面试题

1.斐波那契数列

斐波那契数列都不生疏吧?让大家写马上就能够写出:

// 斐波那契数列
function fib(n) {
  if(n < 2) return n
  return fib(n - 1) + fib(n - 2) 
}

有些敌人可能会想到如果n<0就会出问题而后给它加上:

// 斐波那契数列
function fib(n) {
  console.count('总计') // 总计: 13529
  if(n < 0) return
  if(n < 2) return n
  return fib(n - 1) + fib(n - 2) 
}
// 这样写会更好一些,抛出一个谬误通知用户
function fib(n) {
  console.count('总计') // 总计: 13529
  if(n < 0) throw Error('传入参数必须大于等于0')
  if(n < 2) return n
  return fib(n - 1) + fib(n - 2) 
}

到这里可能就完结了,然而如果只是这样的话,那么你传的值如果是20、30甚至90的时候,你的电脑就当场逝世了,所以须要给它一个优化让它不要无脑的计算。

于是咱们能够给他进一步的优化,而后就有了:

var obj = {},
    arr = []
function fib(n) {
  console.count('总计') // 总计:39
  if(n < 0) throw Error('传入参数必须大于等于0')
  if(n < 2) return n
  if(obj.hasOwnProperty(n)) return obj[n]
  let num = fib(n - 1) + fib(n - 2)
  arr.push(num)// ---------------->有展现数组的需要
  obj[n] = num
  return num
}
// 6765 (20) [1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765]
console.log(fib(20), arr);

通过obj来贮存数值,而后在调用fib()的时候,通过hasOwnProperty(n)判断外面是否有曾经被计算过并贮存在外面,如果有就间接从外面拿这样就不用浪费工夫、算力去做早就做过的事件了。
通过这样子可能显著缩小计算次数,电脑也示意我又行了。然而变量放在里面看起来不够优雅,然而放进去的话这个obj就失去了它的作用,那咱们能够尝试在fib函数外面做一个递归函数看看能不能解决这个问题。

进行进一步的优化

// 不在里面申明变量
function fib(n) {
  let arr = []
  if(n < 0) throw Error('传入参数必须大于等于0')
  function computed(n, pre, next) {
    console.count('总计') // 总计:21
    arr.push(pre)
    if(n === 0) return pre
    return computed(n - 1, next, pre + next)
  }
  // 如果只是要求展现其中一个就不须要变量间接return就好了
  let obj = {
    a: arr,
    b: computed(n, 0, 1)
  }
  return obj
}
// obj: {
//   a: [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765]
//   b: 6765
// }
console.log(fib(20));

通过在函数内做了一个递归函数,能够不必申明一个变量来贮存数值了(arr=[]这个是展现数组用的),这样还省去遍历对象的工夫,性能貌似又失去了一丝丝进步,算是一个意外播种。
computed每次被调用的时候都会n-1,越来越小,而后将上一次的next的值给pre;而next的值则将他们两个pre、next相加,即0+1=1,1+1=2这样子。

如果各位还有更好的方法,欢送指出,让大家一起提高。

2.将数组[1,2,3,[4,5]]转换成以下格局,确保之后往里增加数据也能展现成以下格局

// 为了节俭空间只好这样摆了,请见谅!
{
  children:[
    { value:1 },
    { value:2 },
    { value:3 },
    { children: [ { value:4 }, { value:5 } ] },
  ]
}

很多人一看到这里第一想到的应该是用for或者while循环遍历将它们转换成上述的格局吧。

应用while循环

// -----应用while循环-----
function transition(arr) {
  var newArr = []
  var index = 0
  while (index < arr.length) {
    if(typeof arr[index] === 'number') {
      newArr.push({
        value: arr[index]
      })
    } else if(Array.isArray(arr[index])) {
      newArr.push({
        children: [
          transition(arr[index])
        ]
      })
    }
    index++
  }
  return newArr
}
console.log(transition([1,2,3,[4,5]]));

应用for循环

// -----应用for循环-----
function transition(arr) {
  var newArr = []
  for(let i = 0; i<arr.length;i++) {
    if(typeof arr[i] === 'number') {
      newArr.push({
        value: arr[i]
      })
    } else if(Array.isArray(arr[i])) {
      newArr.push({
        children: [
          transition(arr[i])
        ]
      })
    }
  }
  return newArr
}
console.log(transition([1,2,3,[4,5]]));

这两种办法的确都能无论怎么加,加多少都能够实现,然而这里就有一个问题了。置信大家都晓得了,是不是太多行了,为了实现格局转换while用了20行左右、for循环用了18行。那用map试试?

应用map高阶函数

// -----应用map高阶函数-----
function transition(arr) {
  return arr.map(i => {
    if(typeof i === 'number') {
      return{
        value: i
      }
    } else if(Array.isArray(i)) {
      return {
        children: [
          transition(i)
        ]
      }  
    }
  })
}
console.log(transition([1,2,3,[4,5]]));

只有16行了看起来也比后面两个办法要清晰明了,咱们都晓得其实map函数也是通过遍历数组只是它是把遍历的过程给暗藏起来不让咱们看到,以此缩小代码量。

方才咱们是先map遍历数组再进行判断的,那么咱们试试看是否先判断再进行map遍历数组?
尝试进一步压缩

// -----尝试进一步压缩-----
function transition(arr) {
  if(typeof arr === 'number') {
    return {
      value: arr
    }
  } else if(Array.isArray(arr)) {
    return {
      children: arr.map(i => {
        return transition(i)
      })
    }
  }
}
console.log(transition([1,2,3,[4,5]]));

看起来是能够的,而且还很胜利的把代码进一步压缩到15行。鉴于自己技术水平无限,对于如何缩小循环次数晋升性能方面目前还没有脉络,望各位大佬轻喷。

如果各位还有更好的方法,欢送在上面评论,让大家一起提高。

3.写一个 setInterVal(fn, a, b),每次距离 a,a+b,a+2b 的工夫,而后写一个 stop,进行下面的 setInterVal

这道题第一次我没应用构造函数,写了个一般函数,调用的时候只有把参数写入就主动执行了。

function mySetInterVal(fn, a, b, end = 2) {
  let time = 0                      ↓
  let handle = null       // 这是想要满足除了a,a+b,a+2b以外的工夫         
  function stop () {      // 才加的,没要求就不必加它。
    console.log(`进行刷新,当初是${a + b * time}`);
    clearTimeout(handle)
    time = 0
  }
  function setTime(a, b) {
    if(time > end) return stop()
    handle = setTimeout(() => {
      time++
      fn()
      console.log(`当初是${a + b*time}`);
      setTime(a, b)
    }, a + b * time)
  }
  return setTime(a, b)
}
let test2 = mySetInterVal(() => {console.log('我是函数')}, 1000, 500)

的确满足了要求,然而感觉不是特地好的赶脚。所以把它换成了构造函数看看。
换成构造函数

// -----换成构造函数-----
function mySetInterVal(fn, a, b, end = 2) {
  this.num = 0
  this.handle = null
  this.end = () => {
    this.num = 0
    clearTimeout(this.handle)
  }
  this.start = () => {
    this.handle = setTimeout(() => {
      if(this.num > end) {
        console.log(`进行刷新,当初刷新工夫为${a+b*this.num}`);
        return this.end()// <------------------如果要求要本人调用才停的话,就正文掉就好了
      } 
      this.num++
      this.start()
      fn()
      console.log(`当初是${a}+${b*this.num}`);
    }, a + b * this.num)
  }
}
let test = new mySetInterVal(() => {console.log('我是函数')}, 1000, 500)
test.start()

尽管如同没啥变动,然而应用了构造函数听起来是不是比一般的函数牛逼一点。让面试官晓得你会用构造函数应该也是一个小小的加分项吧(也可能不会有加分hhh,总之能用高级一些的写法尽量应用,总比用最简略的办法实现的印象要好一些)。
或者通过创立一个类class实现也是能够的。

创立一个类class

// -----创立一个类class-----
class mySetInterVal {
  constructor(fn, a, b, end=2) {
    this.a = a
    this.b = b
    this.fn = fn
    this.time = 0
    this.end = end
    this.handle = null
  }
  timeStart() {
    if(this.time > this.end) {
      return this.timeEnd()
    }
    this.handle = setTimeout(() => {
      console.log(`当初是${this.a}+${this.b*this.time}`);
      this.time++
      this.timeStart()
    }, this.a + this.b * this.time)
  }
  timeEnd() {
    clearTimeout(this.handle)
    this.time = 0
  }
}

let test = new mySetInterVal(() => {console.log('我是函数')}, 1000, 500)
test.timeStart()

创立一个类来实现的话,看起来应该会比其他人用函数实现要来的耳目一新一些吧。至多让面试官晓得你是会用类的人。

如果各位还有其它的办法,请多多指出,大家一起提高呀!

到这里就完结了,如果前面遇到这些相似题目置信各位应该有各种姿态去解决问题了吧!

前端视频方面大家能够关注我的b站,搜寻“焖豆不闷”,下面上传了前端入门到精通(1000大合集)、0根底玩转微信小程序、5G时代应用Webview的正确姿态等视频;也能够进前端开发交换群,和大家一块聊天学习哦:点击这进入学习圈

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理