前端面试高频考题(一)

221 阅读5分钟

一、HTML和CSS

1怎么让一个不定宽高的div垂直水平居中

keyword: flex , transform

<body>
    <div class="box"></div>
</body>
  * {
            padding: 0;
            margin: 0
        }
        html, body {
            height: 100%
        }
        body {
            /* display: flex;
            justify-content: center;
            align-items: center; */
            position: relative;

        }
        .box {
            width: 200px;
            height: 200px;
            background: pink;
            position: absolute;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%  );
        }

2 flex: 1

flex: 1 是flex-grow: 1; flex-shrink: 1; flex-basis: 0%; 的缩写;

(flex: 2 是flex-grow: 2; flex-shrink: 1; flex-basis: 0%; 的缩写;

  • flex-grow 定义项目的放大比例,默认为0,即如果存在剩余空间,也不放大
  • flex-shrink 定义项目的缩小比例,默认为1,即如果空间不足,该项目将缩小
  • flex-basis 给上面两个属性分配多余空间之前, 计算项目是否有多余空间, 默认值为 auto, 即项目本身的大小

3 localStorage.sessionStorage和cookie的区别

共同点: 都是用来存储数据的;

不同点:

  • cookie数据始终在同源的http请求中携带(即使不需要),即cookie在浏览器和服务器间来回传递。而sessionStorage和localStorage不会自动把数据发送给服务器,仅在本地保存。
  • 存储大小限制也不同,cookie数据不能超过4k,(因为每次http请求都会携带cookie,所以cookie只适合保存很小的数据,如会话标识)。而sessionStorage和localStorage的存储限制为5M。
  • 数据有效期不同,sessionStorage仅在当前浏览器窗口关闭前有效;localStorage始终有效;cookie只在设置的过期时间之前有效,即使窗口或浏览器关闭。
  • 作用域不同: sessionStorage不在不同的浏览器窗口共享,即使是同一个页面;localStorage和cookie在所有的同源窗口都是共享的。

二、JS

1 手写函数防抖、节流

// 防抖: 多次操作变为一次
// 节流: 一定时间内只调用一次
    function debounce(fn, delay) { // 防抖
            let timer = null
            return function (...args) {
                if (timer) {
                    clearTimeout(timer)
                    timer = null
                }
                timer = setTimeout(() => {
                    fn.apply(this, args)
                    clearTimeout(timer)
                    timer = null
                }, delay)
            }
        }

        function throttle(fn, delay) { // 节流
            let timer = null
            return function (...args) {
                if (timer) return false
                timer = setTimeout(() => {
                    fn.apply(this, args)
                    clearTimeout(timer)
                    timer = null
                }, delay)
            }
        }

2 手写深拷贝

    function deepCopy(source) {
           if (!source || typeof source !== 'object') return source
           const container = Array.isArray(source) ? [] : {}
           for(let key in source) {
               if (source.hasOwnProperty(key)) {
                   const value = source[key]
                   countainer[key] = value && typeof value === 'object' ? deepCopy(value) : value
               }
           }
           return container
       }

3 算法快排

   const arr = [5, 8, 9, 12, 2, 4]
       function mySort(data) {
           if (data.length <= 1) return data
           const baseVal = data[0]
           const left = []
           const right = []
           for (let i = 1, len = data.length; i < len; i++) {
               const item = data[i]
               item < baseVal ? left.push(item) : right.push(item)
           }
           return [...mySort(left), baseVal, ...mySort(right)]
       }

       const res = mySort(arr)
       console.log(res) // [2,4,5,8,9,12]

       console.log(arr.sort()) // [12, 2, 4, 5, 8, 9] 默认排序是将元素转换为字符串,然后按照它们的 UTF-16 码元值升序排序。
       console.log(arr.sort((a,b) => b - a)) //  [12, 9, 8, 5, 4, 2]
       console.log(arr.sort((a,b) => a - b)) //  [2,4,5,8,9,12]
       console.log(arr) //  [2,4,5,8,9,12]

4 去重

const arr = [5, 5,5, 8, 9, 12, 2, 4, 2, 4]
console.log([...new Set(arr)]) // [5, 8, 9, 12, 2, 4]
function unique(data) {
  return data.reduce((acc, cur) => acc.includes(cur) ? acc : [...acc, cur], [])
}
console.log(unique(arr)) // [5, 8, 9, 12, 2, 4]
console.log(arr) // [5, 5,5, 8, 9, 12, 2, 4, 2, 4]

5 一维转多维,多维转一维,A数据B数据互转

A 数据

     const list = [
            {id: 5, name: '独家', pid: 4},
            {id: 1, name: '经典奶茶', pid: 0},
            {id: 2, name: '原茶', pid: 1},
            {id: 3, name: '鲜萃', pid: 2},
            {id: 4, name: '奶茶', pid: 3},
        ]
     

B 数据

 const newList = [
            {
                id: 1,
                name: '经典奶茶',
                pid: 0,
                children: [
                    {
                        id: 2,
                        name: '原茶',
                        pid: 1,
                        children: [
                            {
                                id: 3,
                                name: '鲜萃',
                                pid: 2,
                                children: [
                                    {
                                        id: 4,
                                        name: '奶茶',
                                        pid: 3,
                                        children: {
                                            id: 5,
                                            name: '独家',
                                            pid: 4
                                        }
                                    }
                                ]
                            }
                        ]
                    }
                ]
            }
        ]
// A转B :找父级,并把自己放到父级的children中
        function toTree(data) {
            const container = []
            // step2 生成对象
            const obj = {}
            data.forEach(item => {
                obj[item.id] = item
            })
            data.forEach(item => {
                // step1 通过对象的方式找父级  
                const parent = obj[item.pid]
                // step3 pid === 0 是,parent为undefined
                if(parent) {
                    (parent.children || (parent.children = [])).push(item)
                } else {
                    container.push(item)
                }
            })
            return container
        }
        const newList = toTree(list)
        console.log(newList)
// B 转 A 
 function toFlat(data) {
            return data.reduce((acc, cur) => {
                acc.push(cur)
                if (cur.children) {
                    acc.push(...toFlat(cur.children))
                }
                return acc
            }, [])   
        }
        console.log(toFlat(newList))

6 数组扁平化

const oldArr = [
            1,
            1,
            [
                2, 2, [3],
                [4, 5,5, 6],
                [7, 8, 9],
                10,
                11
            ],
            12,
            13,
            14,
            [15, 16, 17],
        ];
 // 方法一:
 oldArr.flat(Infinity)
 // 方法二:
 function myFlat(data) {
   return data.reduce((acc, cur) => {
     return Array.isArray(cur) ? acc.concat(...myFlat(cur)) : acc.concat(cur)}, [])
 }  
console.log(myFlat(oldArr))
console.log(oldArr)

7 promise

参考文章 blog.csdn.net/qq_42033567…

const promise = new Promise((resolve, reject) => {
  console.log(1);
  console.log(2);
});
promise.then(() => {
  console.log(3);
});
console.log(4);
// 1 2 4
// promise 内部状态没有发生变化,所以不能输出3
const promise1 = new Promise((resolve, reject) => {
  console.log('promise1')
  resolve('resolve1')
})
const promise2 = promise1.then(res => {
  console.log(res)
})
console.log('1', promise1);
console.log('2', promise2);
// 结果:
// 'promise1'
// '1' Promise{<resolved>: 'resolve1'}
// '2' Promise{<pending>}
// 'resolve1'

const promise = new Promise((resolve, reject) => {
            console.log(1);
            setTimeout(() => {
                console.log("timerStart");
                resolve("success");
                console.log("timerEnd");
            }, 0);
            console.log(2);
        });
        promise.then((res) => {
            console.log(res);
        });
        console.log(4);
        // 1 2 4 timerStart timerEnd sucess
        Promise.resolve().then(() => {
            console.log('promise1');
            const timer2 = setTimeout(() => {
                console.log('timer2')
            }, 0)
        });
        const timer1 = setTimeout(() => {
            console.log('timer1')
            Promise.resolve().then(() => {
                console.log('promise2')
            })
        }, 0)
        console.log('start');

        // start 
        // promise1
        // timer1
        // promise2
        // timer2
  const promise = new Promise((resolve, reject) => {
            resolve('success1');
            reject('error');
            resolve('success2');
        });
        promise.then((res) => {
            console.log('then:', res);
        }).catch((err) => {
            console.log('catch:', err);
        })
        // then: success1

8 写出执行顺序(字节面试真题)

 async function async1() {
            console.log('async1 start'); // 2
            await async2();
            console.log('async1 end'); //微任务01  6
            await async3() 
            console.log('async3 end') // 微任务03 9
        }
        async function async2() {
            console.log('async2'); // 3
        }
        async function async3() {
            console.log('async3') // 7
        }
        console.log('script start'); //  1
        setTimeout(function () {
            console.log('setTimeout'); // 宏任务01  10
        }, 0)
        async1();
        new Promise(function (resolve) {
            console.log('promise1'); // 4
            resolve();
        }).then(function () { // 微任务02
            console.log('promise2'); // 8
        });
        console.log('script end'); // 5
        
        
        /**
 * script start
 * async1 start
 * async2
 * promise1
 * script end
 * async1 end
 * async3
 * promise2
 * async3 end
 * setTimeout
*/

9 执行结果

function test () {
  console.log(1);
  Promise.resolve().then(test);
}
test();
setTimeout(() => {console.log(2)}, 0)
// 始终输出1 不会执行setTimeout外面的回调函数,浏览器会崩溃

10 排列组合算法

  let spec = [
            ["红", "绿", "蓝"],
            ["大", "中", "小"],
            ['酸', '甜', '苦', '辣', '咸']
        ]
        function statistics(data) {
            /**
             * 思路: 
             * 先实现[['红'], ['绿'], ['蓝']]
             * 再实现[['红', '大'], ['红', '中']]
             * 最后实现[['红', '大', '酸']]
            */
           return data.reduce((acc, cur) => { // 01 [[]] ["红", "绿", "蓝"] 02 [['红'], ['绿'], ['蓝']]  ["大", "中", "小"],
                const container = []
                acc.forEach(a => { // []
                    cur.forEach(c => { // '红'
                        container.push(a.concat(c)) // ['红']
                    })
                });
                return container
           }, [[]])
        }
        console.log(statistics(spec))

11 'get-element-by-id' 变成 'getElementById'

 const str = 'get-element-by-id'
        function change(msg) {
            const arr = msg.split('-')
            const newArr = arr.map((item, index) => {
                if(index === 0) return item
                return item.charAt(0).toUpperCase() + item.slice(1)
            })
            return newArr.join('')
        }
        console.log(change(str)) // getElementById
const str = 'get-element-by-id'
        function change(msg) {
            return msg.replace(/\-(\w)/g, (all, letter) => letter.toUpperCase())
        }
        console.log(change(str)) //getElementById
        const str = 'getElementById'
        function change(msg) {
            return msg.replace(/([A-Z])/g, '-$1').toLowerCase()  
        }
        console.log(change(str)) // get-element-by-id

12判断一个字符串中出现次数最多的字符,统计这个次数

const str = 'fkdjfklajlkfjklsjfklsdjfklsjfiufhfjfghkjfbvmfbvjfjhjf'
        function count(msg) {
            const obj = {}
            for(let i = 0, len = str.length; i < len; i++) {
                const letter = str[i]
                obj[letter] ? obj[letter]++ : obj[letter] = 1
            }
            const maxVal = Math.max(...Object.values(obj))
            const arr = [] 
            for (let key in obj) {
                if (obj[key] === maxVal) {
                    arr.push(key)
                }
            }
            return `出现次数对多的字符是${
                arr.join('&')
            },值是${maxVal}`
        }
        console.log(count(str))

13 力扣算法,合并区间

以数组intervals表示若干区间的集合,其中单个区间为 intervals[i] = [start, end]。 请合并所有重叠的区间,并返回一个不重叠的区间数组,该数组需恰好覆盖输入中的所有区间。

 let arr = [[1,3],[2,6],[8,10],[15,18]]
        function merge(intervals) {
            if (!intervals || !intervals.length) return [];
            intervals.sort((a, b) => { // 按照区间第一位进行排序  
                return a[0] - b[0]
            });
            
            let result = [intervals[0]]  // 排序之后第一个是最小的  [[1,3]] 
            console.log(result)
            for (let i = 1; i < intervals.length; i++) {  // 从第二个开始比较  
                let resultLast = result.length - 1 // 0
                console.log(resultLast)
                if (result[resultLast][1] > intervals[i][0]) { // 3 > 2 判断结尾是不是大于开始               
                    result[resultLast][1] = Math.max(result[resultLast][1], intervals[i][1])  // 区间重复就进行合并了                
                } else {
                    result.push(intervals[i]) // 区间没有重复  
                }
            }
            return result
        }
        console.log(merge(arr)) // [[1,6], [8,10], [15,18]]

三、vue

1 路由守卫

  • 全局前置路由守卫:router.beforeEach,切换之前调用(比如,进入新页面前,判断是是否有权限进入),to、from、next
  • 全局后置路由守卫:router.afterEach,切换之后调用(比如到新页面后,更换页面title),to、from
  • 独享路由守卫:beforeEnter,某一个路由单独享用的
  • 组件内路由守卫:在组件内编写,beforeRouteEnter(进入前), beforeRouteLeave(离开前)

四、webpack

五、 小程序

六、其他

七、项目