前端场景应用类面试题,赶紧收藏系列(二)

217 阅读1分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第23天,点击查看活动详情

  • 实现斐波那契数列
  • 字符串出现的不重复最长长度
  • 使用setTimeout实现setInterval
  • 实现jsonp
  • 判断对象是否存循环引用

1. 实现斐波那契数列

 // 递归
 function fn (n){
     if(n==0) return 0
     if(n==1) return 1
     return fn(n-2)+fn(n-1)
 }
 // 优化
 function fibonacci2(n) {
     const arr = [1, 1, 2];
     const arrLen = arr.length;
 ​
     if (n <= arrLen) {
         return arr[n];
     }
 ​
     for (let i = arrLen; i < n; i++) {
         arr.push(arr[i - 1] + arr[ i - 2]);
     }
 ​
     return arr[arr.length - 1];
 }
 // 非递归
 function fn(n) {
     let pre1 = 1;
     let pre2 = 1;
     let current = 2;
 ​
     if (n <= 2) {
         return current;
     }
 ​
     for (let i = 2; i < n; i++) {
         pre1 = pre2;
         pre2 = current;
         current = pre1 + pre2;
     }
 ​
     return current;
 }

2. 字符串出现的不重复最长长度

用一个滑动窗口装没有重复的字符,枚举字符记录最大值即可。用 map 维护字符的索引,遇到相同的字符,把左边界移动过去即可。挪动的过程中记录最大长度:

 var lengthOfLongestSubstring = function (s) {
     let map = new Map();
     let i = -1
     let res = 0
     let n = s.length
     for (let j = 0; j < n; j++) {
         if (map.has(s[j])) {
             i = Math.max(i, map.get(s[j]))
         }
         res = Math.max(res, j - i)
         map.set(s[j], j)
     }
     return res
 };

3. 使用 setTimeout 实现 setInterval

setInterval 的作用是每隔一段指定时间执行一个函数,但是这个执行不是真的到了时间立即执行,它真正的作用是每隔一段时间将事件加入事件队列中去,只有当当前的执行栈为空的时候,才能去从事件队列中取出事件执行。所以可能会出现这样的情况,就是当前执行栈执行的时间很长,导致事件队列里边积累多个定时器加入的事件,当执行栈结束的时候,这些事件会依次执行,因此就不能到间隔一段时间执行的效果。

针对 setInterval 的这个缺点,我们可以使用 setTimeout 递归调用来模拟 setInterval,这样我们就确保了只有一个事件结束了,我们才会触发下一个定时器事件,这样解决了 setInterval 的问题。

实现思路是使用递归函数,不断地去执行 setTimeout 从而达到 setInterval 的效果

 function mySetInterval(fn, timeout) {
   // 控制器,控制定时器是否继续执行
   var timer = {
     flag: true
   };
   // 设置递归函数,模拟定时器执行。
   function interval() {
     if (timer.flag) {
       fn();
       setTimeout(interval, timeout);
     }
   }
   // 启动定时器
   setTimeout(interval, timeout);
   // 返回控制器
   return timer;
 }

4. 实现 jsonp

 // 动态的加载js文件
 function addScript(src) {
   const script = document.createElement('script');
   script.src = src;
   script.type = "text/javascript";
   document.body.appendChild(script);
 }
 addScript("http://xxx.xxx.com/xxx.js?callback=handleRes");
 // 设置一个全局的callback函数来接收回调结果
 function handleRes(res) {
   console.log(res);
 }
 // 接口返回的数据格式
 handleRes({a: 1, b: 2});

5. 判断对象是否存在循环引用

循环引用对象本来没有什么问题,但是序列化的时候就会发生问题,比如调用JSON.stringify()对该类对象进行序列化,就会报错: Converting circular structure to JSON.

下面方法可以用来判断一个对象中是否已存在循环引用:

const isCycleObject = (obj,parent) => {
    const parentArr = parent || [obj];
    for(let i in obj) {
        if(typeof obj[i] === 'object') {
            let flag = false;
            parentArr.forEach((pObj) => {
                if(pObj === obj[i]){
                    flag = true;
                }
            })
            if(flag) return true;
            flag = isCycleObject(obj[i],[...parentArr,obj[i]]);
            if(flag) return true;
        }
    }
    return false;
}


const a = 1;
const b = {a};
const c = {b};
const o = {d:{a:3},c}
o.c.b.aa = a;

console.log(isCycleObject(o))