50道前端面试题

121 阅读6分钟

1. 写一个 mySetInterVal(fn, a, b),每次间隔 a,a+b,a+2b 的时间,然后写一个 myClear,停止上面的 mySetInterVal

思路
  1. 从时间间隔 a, a+b, a+2b... 可以找到规律就是第一次之后每次执行时间都是多一个 b.
  2. 使用 setTimeout 定时器,时间到了执行一个函数,并且继续开启一个定时器时间加为 b。
  3. 由于第一次时间是基础,往后都是多一个 b,只要区分是否是第一次执行就可以了。
  4. 核心思路就是不断重复定时器>触发函数+开启一个新定时器,使用一个标志区分是否第一次。
代码实现
function Fn(fn, a, b) {
    // 是否第一次的标识
    this.ifFirst = true;
    this.timer = null;
    Fn.prototype.start = () => {
        // 第一次定时器使用 a,往后都是 b
        let time = this.ifFirst? a: b;
        this.ifFirst = false;
        this.timer = setTimeout(() => {
            fn();
            this.start();
        }, time)
    }
    Fn.prototype.clear = () => {
        clearTimeout(this.timer)
    }
}
function say(){
    console.log(999)
}
let obj = new Fn(say, 3000, 1000);
obj.start();
setTimeout(() => {
    obj.clear();
}, 10000)

2. 合并二维有序数组成一维有序数组

这种题肯定不是考察你库函数的用法:concat、sort 的用法,一般都会要求时间复杂度为O(n)

思路
  1. 因为已知两个数组都是有序,可以直接用双指针从分别从两个数组头部开始遍历,创建一个结果数组 result, 比较指针对应位置的大小,小的写入 result, 移动对应指针,不断重复以上步骤
  2. 当某个指针遍历完,没走完的指针对应的值都是比之前的大,继续遍历,放入 result;
代码实现
function fn(arr1, arr2) {
    let pointer1 = 0, pointer2 = 0;
    let result = [];
    while (arr1[pointer1] && arr2[pointer2]){
        let val1 = arr1[pointer1], val2 = arr2[pointer2];
        if(val1 < val2){
            result.push(val1);
            pointer1++
        }else{
            result.push(val2);
            pointer2++
        }
    }
    return result.concat(arr1.slice(pointer1), arr2.slice(pointer2));
}

console.log(fn([1,3,6,9], [1,3,6,9]))

3.斐波那契数列

3-1. 求包含指定参数的斐波那契数列

这个很简单,就是一个循环,不断往数组添加就好了

function fn(n) {
    let result = [1, 1];
    if(n === 1){
        return [1]
    }else if(n === 2){
        return [1,1]
    }
    for(let i=2;i<n;i++){
        result[i] = result[i-1] + result[i-2]
    }
    return result
}

console.log(fn(5))

3-2. 求斐波那契数列第n位是多少

最简单的写法就是递归了,求第n个就是求前一个加前前一个

function fn(n) {
    if(n === 1 || n === 2){
        return 1;
    }
    return fn(n-1) + fn(n-2)
}

console.log(fn(5))

上面的方式时间复杂度太高了, 1 + 2 + 4 + 8 ...(2n-1)。时间复杂度到了指数阶。现在做法是求n,就先求n-1,和n-2;那么我直接从1求到n不是就可以了吗。

function fn(n, pre = 1, cur = 1) {
    if(n === 1 || n === 2){
        return cur;
    }
    [pre, cur] = [cur, pre + cur]
    return fn(n-1, pre, cur )
}

console.log(fn(6))

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

思路

D3E7E3639352E775EF6573D6D9C6FA1F.jpg

  1. 要查找的一段字符串肯定是连续的,可以使用如图的窗口方式
  2. 遍历数组,当当前值在窗口中存在时,如果当前窗口长度大于之前保存的 maxLen,更新maxLen,并且移动左指针到重复值之后。
  3. 最后判断一下当前窗口长度和maxLen,返回大者
function fn(str) {
    let maxLen = 0;
    let leftIndex = 0;
    for(let i=0;i<str.length;i++){
        let preStr = str.slice(leftIndex, i);
        let preLen = preStr.length;
        let cur = str[i];
        let preIndex = preStr.indexOf(cur);
        if(preIndex > -1 ){
            if(preLen>maxLen){
                maxLen = preLen;
            }
            // 注意这里取值
            leftIndex = i - preLen + preIndex + 1
        }
    }
    return str.slice(leftIndex).length > maxLen ? str.slice(leftIndex).length : maxLen
}

console.log(fn('abcdabef'))

reduce写法如下,思路相同

function fn(str) {
   let arr = [...str];
   let maxLen = 0
   let tempArr = arr.reduce((result, cur, index) => {
       let hasIndex = result.indexOf(cur);
       if(hasIndex>-1){
           maxLen = result.length>maxLen ? result.length : maxLen;
           result = result.slice(hasIndex+1) + cur;
       }else{
           result += cur
       }
       return result
   })
    return tempArr.length>maxLen ? tempArr.length : maxLen
}

console.log(fn('abcdabe'))

5.介绍防抖节流原理、区别以及应用,并用JavaScript进行实现

  1. 防抖节流都使用了闭包,在函数a外部调用函数内部的函数b,b函数执行时候会获取保存在a里面的变量(这些变量一直都在)做相应的处理。
  2. 防抖:在事件被触发n秒后再执行回调,如果在这n秒内又被触发,则重新计时;一般用于点击提交时候防止重复提交和输入搜索只执行最后一次
  3. 节流:在事件被触发n秒内只执行一次,如果在这n秒内又被触发,不予处理;一般用于处理拖拽和窗口缩放

5-1. 防抖

思路
  1. debonce函数里面定义 timer 记录定时器。
  2. 执行内部函数时候先清空定时器(不判断timer存在与否清空也没问题)。
  3. 如果是立即执行,判断定时器是不是为空,为空说明是第一次进来或是当前操作距上一次够久了,执行执行指定函数,开启定时器,定时器到时定时器赋值为空
  4. 不是立即执行,直接开启定时器,在定时器到时执行指定函数。
代码实现
function debonce(fn, wait, immediate) {
    let timer = null;
    return function () {
        let content = this;
        let args = arguments;
        timer && clearTimeout(timer);
        if(immediate){
            if(!timer){
                fn.apply(content, args)
            }
            timer = setTimeout(() => {
                timer = null
            }, wait)
        }else{
            timer = setTimeout(() => {
                fn.apply(content, args)
            })
        }
    }
}

这里注意,clearTimeout()后,timer还是有值的。立即执行时,定时器到时需要置空处理。
content 变量和 args 变量要看你定义函数是如何的在使用,像上面这个我都是剪头函数,其实可以不用。

5-2. 节流

思路
  1. 这个可以分为定时器版本和计时版本
  2. 定时器版:如果定时器为空,设置定时器,定时器到时重置定时器为空并且执行相应函数。
  3. 计时版:调用函数时候当前时间和上一回执行时间做对比,如果大于指定时间执行。
定时器版实现
function throttle(fn, wait) {
    let timer = null;
    return function () {
        if(!timer){
            fn.apply(this, arguments)
        }
        timer = setTimeout(() => {
            timer = null
        }, wait)
    }
}
计时器版实现
function throttle(fn, wait) {
    let preTime = 0;
    return function () {
        let curTime = new Date().getTime();
        if(curTime - preTime > wait){
            preTime = curTime;
            fn.apply(this, arguments)
        }
    }
}

// 使用
let throttleFn = throttle(function () {

}, 100)

6. 判断一个数是否是快乐数

快乐数:对于一个正整数,每一次将该数替换为它每个位置上的数字的平方和,然后重复这个过程直到这个数变为 1,也可能是 无限循环 但始终变不到 1。如果 可以变为  1,那么这个数就是快乐数。

思路
  1. 这里肯定是使用循环不断判断平方和是不是等于1,还需要保存当前值,因为如果出现了当前值,那就说明开始不断的重复了,这是就不应该继续循环了,直接判断那个值是不是符合。
  2. 要快速定位到使用过某个值,就可以使用set、map、对象来记录使用过的值。
function getN(n) {
    let res = 0;
    while (n){
        res += (n%10) * (n%10);
        n = Math.floor(n/10);
    }
    return res;
}

function isHappy(n) {
    let set = new Set();
    while (n!==1 && !set.has(n)){
        set.add(n);
        n = getN(n);
    }
    return n === 1
}

console.log(isHappy(10))

7. 给定数组查找两个数之和等于指定数

给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。

思路
  1. 要查找是否存在,那么遍历数组时候,查找arr[i]前面是否存在target - arr[i]
  2. 要想快速定位某个值,就可以使用set、map、对象来记录使用过的值。
  3. 这里要返回值对应的索引,那么可以使用map。
代码实现
function fn(arr, target) {
    let map = new Map();
    for(let i=0;i<arr.length;i++){
        let need = target - arr[i];
        if(map.has(need)){
            return [map.get(need), i]
        }else{
            map.set(arr[i], i)
        }
    }
    return false
}

console.log(fn([1,2,3,4,5], [5]))

这个代码实现的是找到第一个就返回,如果需要做找到所有,就价格数组存放就好了。

待续。。。。