共计 4842 个字符,预计需要花费 13 分钟才能阅读完成。
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 的正确姿态等视频;也能够进前端开发交换群,和大家一块聊天学习哦:点击这进入学习圈