前端开发必须掌握哪些基础知识?大厂笔面试必备问题!

在当今技术日新月异的时代,前端开发作为互联网产品的核心组成部分,其重要性不言而喻。随着大厂们对技术要求的不断提升,程序员们不仅需要掌握扎实的基础知识,更需要在实践中不断磨砺自己的技能。为了更好地应对笔面试的挑战,通过一系列基础知识问题自检自己的前端开发能力,无疑是一个高效且实用的方法。本文将围绕前端开发的基础知识,精选几个常见问题,旨在帮助大家巩固基础,提升竞争力。

一、前端开发考前有哪些必备基础问答?

1. 在页面上隐藏元素的方法有哪些?

 1、display: none; 2、visibility: hidden; 3、 opacity: 0; 4、transform: scale(0);

2. 数组的 slice 和 splice 有哪些区别?

slice: 截取数组, 不会改变原来的数组, 返回一个新的数组, 语法:

slice(start, end) // 不包含 end 索引

splice:可以对数组进行增删改操作, 会修改原数组,第一个是数组的起始位置, 第二个代表要删除多少个元素,传 0 代表不删除,后面的参数代表要添加的元素, 返回值包含了删除的元素的数组 , 语法:

splice(start, deleteCount, item1, item2, ...)

3. Map, Set 和 weakMap 的区别

Map: 对像保存键值对, 可以接收任何类型的数据作为键和值, 键是唯一的,且键必须是 Object 或 继承至 Object, 如果添加相同的键会覆盖原来键所对应的值,内部通过 SameValueZero 判断, js 无法访问该方法,类似于 ===。

Set:对象允许你存储任何数据类型的唯一值(是 Map 的一种增强 ),内部通过 SameValueZero 判断,类似于 ===。

weakMap: 与 Map 相似, 其中的键必须是对象或非全局注册的符号, 他是不可迭代的,不具备遍历等方法, 因为他的键是弱引用, 随时可能被回收,weak 的弱是指他不阻止 javascript 的垃圾回收。

4. 什么是深拷贝和浅拷贝? 有哪些手段实现了深拷贝?

深浅拷贝一般用于对象

浅拷贝: 创建一个新的引用,指向原引用指向的地址, 只拷贝对象的顶层属性

深拷贝: 创建一个新的引用指向一块新的地址, 将原引用地址的数据拷贝,存储在这个新的地址中

实现深拷贝:

1、JSON.parse(JSON.stringfy(obj)) — 原理: JSON.stringify() 方法将一个 JavaScript 对象或值转换为 JSON 字符串, 字符串是基本数据类型,创建他时会创建一个新的引用指向存放字符串的地址(字符串的值存放在堆区)。JSON.parse() 方法用来解析 JSON 字符串,构造由字符串描述的 JavaScript 值或对象。 最终新的值指向这个新的地址, 使用这种方法会丢失 undefined 和 函数值。

2、lodash 库中的 _.cloneDeep(obj)

3、递归函数

function deepCopy(newObj, oldObj) {
       for (let k in oldObj) {
         if (oldObj[k] instanceof Array) { //  typeof []   ---> Object
           newObj[k] = [];
           deepCopy(newObj[k], oldObj[k]);
         } else if (oldObj[k] instanceof Object) {
           newObj[k] = {};
           deepCopy(newObj[k], oldObj[k]);
         } else {
           newObj[k] = oldObj[k];
         }
       }
}

5. Git 回退代码的指令是什么?

git revert commitId — 会用一个新的 commit 来记录这次的撤回

git reset –hard commitId — 回退所有, commit , index, workspacetree

git reset –mixed commitId — 回退 commit, index

git reset –soft commitId — 回退 commit

6. 箭头函数和普通函数的区别?

箭头函数比普通函数简洁

箭头函数没有自己的 this

箭头函数没有自己的参数 arguments

箭头函数没有 prototype

箭头函数继承的 this 方向永远不会改变

apply、call、bind不能改变箭头函数的this指向

箭头函数不能作为构造函数使用

7. Promise 有几种状态, Promise.all 和 Promise.reace 有什么区别

3 种:pendding、reject 、 fullfill

Promise.all() 静态方法接受一个 Promise 可迭代对象作为输入,并返回一个 promise。当所有输入的 Promise 都被兑现时,返回的 Promise 也将被兑现(即使传入的是一个空的可迭代对象),并返回一个包含所有兑现值的数组。如果输入的任何 Promise 被拒绝,则返回的 Promise 将被拒绝,并带有第一个被拒绝的原因。

Promise.reace() 返回最快响应的, 不管成功还是失败

8. 请给出以下代码输出结果, 如果有问题, 可以如何改写?

for (var i= 0; i< 5; i++) {
    setTimeOut(() => {
        console.log(i)
    }, 0)
}
// 输出 5 个 5
// 将 var ----> let 
for (let i= 0; i< 5; i++) {
    setTimeOut(() => {
        console.log(i)
    }, 0)
}

执行原理:

前端开发必须掌握哪些基础知识?大厂笔面试必备问题!

二、 前端开发常见代码如何实现?

1. typeOf 和 instanceOf 有什么区别? 实现一个 instanceOf

1、typeOf 返回一个字符串, 表示操作数的类型。检测类型一般有七种: number、 string、 boolean、 function、 Object、 undefined、 symbol), 新加了一种 bigint。 但是这种方式对 null 的类型也识别为 object ,因为在 JavaScript 最初的实现中,JavaScript 中的值是由一个表示类型的标签和实际数据值表示的。对象的类型标签是 0。由于 null 代表的是空指针,因此,null 的类型标签是 0,typeof null 也因此返回 “Object”

2、instanceOf : 沿着作用域链查找,判断值的__proto__ 属性是否出现在某个实例对象的原型链上。但是有缺陷,如果是多全局对象,那他不一定准确, 因为多窗口意味着他们内置的构造函数并不相同

3、实现一个 instanceOf

function instanceOf (obj, Constructor) {
    let temp = obj.__proto__
    
    while(temp) {
       if (temp === Constuctor.prototype) {
           return true
       } 
       temp = temp.__proto__
    }
    return false
}

2. bind、 call、 apply 有什么区别? 如何实现一个 bind?

都是 Function 函数原型上的方法, 用来修改函数的 this 指向。

1、bind: 创建一个新的函数返回, 不会立即执行,第一参数是函数 this 的指向, 后面的参数以, 分割

2、call : 会以给定的 this 值和逐个提供的参数调用函数

3、apply: 会以给定的 this 值和给定的数组类型的参数调用函数

// 实现一个 bind
Function.prototype.myBind (context, ...bindData) {
    if (typeof this !== 'function') {
        throw new Error('type Error')
    }
    const self = this // 这个 this 指向的实例对象, 是一个函数
    return function (...args) { // 返回的函数也可以接受参数
        const paramsArr = bindData.concat(args)
        return self.apply(context, paramsArr)
    }
}

// 实现一个 call 
Function.prototype.myCall (context, ...bindData) {
    if (typeof this !== 'function') {
       throw new Error('type Error')
    }
    const self = this // 指向掉用的函数实例, 函数本质也是一个对象, Function.__proto__ === Object.prototype
    context = context === null || context === undefined ? globalThis : Object(context)
    // 最终的目的是要将函数的 this 指向传入的对象
    const key = Symbol()
    context[key] = self // 将函数作为对象中的成员
    const res = context[key](...bindData) // 需要执行这个函数,并将数据传入函数
    delete context[key]
    return res // 返回执行结果
}

// 实现一个 apply
Functiion.prototype.myApply(context, paramsArr) {
    if (typeof this !== 'function') {
       throw new Error('type Error')
    }
    const self = this
    context = context = null || undefined ? globalThis : Object(context)
    const key = Symbol()
    context[key] = self
    const res = context[key](...paramsArr)
    delete context[key]
    return res
}

// call 和 apply 实现基本相同

3. 写一个防抖或节流函数

// 防抖函数: 一般用于点击事件等,在规定时间内, 多次点击,只执行最后一次
function antiShake(fn, delay) {
    let timer = null
    return function (...args) {
        if (timer) {
            clearTimeout(timer) // 这个时候的定时器还没有执行
        }
        timer = setTimeOut(() => {
            fn.apply(this, args)
        }, delay)
    }
}

// 节流函数, 一般用在滚动事件等, 在规定时间内, 只触发一次
// 1. 使用 setTimeout 实现
function throttle (fn, delay) {
    let timer = null 
    return function (...args) {
        if (timer) return 
        timer = setTimeout(() => {
           fn.apply(this, args)
           timer = null // 不要使用 clearTimeout 清除, 因为正在执行中
        }, delay) 
    }
}
// 2. 使用时间差实现
function throttle1 (fn, delay) {
    let prev = 0
    return function (...args) {
        let now = new Date()
        if (now - prev >= delay) {
            prev = now
            fn.apply(this, args)
        }
    }
    
}

4. 使用 reduce 将二维数组扁平化

// reduce() 方法对数组中的每个元素按序执行一个提供的 reducer 函数,每一次运行 reducer 会将先前元素的计算结果作为参数传入,最后将其结果汇总为单个返回值。
function flatArr(arr) {
   if (Array.isArray(arr)) {
       throw new Error('type Error')
   }
   return arr.reduce((pre, cur) => {
       if (Array.isArray(cur)) {
           pre = pre.concat(cur)
       } else {
           pre.push(cur)
       }
       return pre
   }, [])
}

5. 手写一个快速排序

// 快速排序
  const arr = [5, 2, 7, 8, 34, 7, 39, 12, 56, 9, 1]

  function quickSort(arr) {
    // 4.结束递归(当ary小于等于一项,则不用处理)
    if (arr.length <= 1) {
      return arr
    }
    // 1. 找到数组的中间项,在原有的数组中把它移除
    const middleIndex = Math.floor(arr.length / 2)
    const middle = arr.splice(middleIndex, 1)[0]
    // 2. 准备左右两个数组,循环剩下数组中的每一项,比当前项小的放到左边数组中,反之放到右边数组中
    const leftArr = [], rightArr = []
    for (let i = 0; i < arr.length; i++) {
      const current = arr[i]
      current < middle ? leftArr.push(current) : rightArr.push(current)
    }
    // 3. 递归方式让左右两边的数组持续这样处理,一直到左右两边都排好序为止。
    //(最后让左边+中间+右边拼接成最后的结果)
    return quickSort(leftArr).concat(middle, quickSort(rightArr))
  }

  console.log(quickSort(arr))        // [1, 2,  5,  7,  7, 8, 9, 12, 34, 39, 56]
  

6. 请使用 ES6 的 proxy 实现一个数据绑定给实例


const obj = {}
const instance = new Proxy(obj, {
    set: function (target, prop, value) { // target 原对象
        target[prop] = value
        return true
    }
})

7. 实现一个三列布局, 左右宽度固定, 中间自适应(两种方式以上)

// 1. 圣杯布局
 <div class="container">
        <div class="content">中间</div>
        <div class="left">左边</div>
        <div class="right">右边</div>
 </div>
 
 <style>
  .container {
            height: 200px;
            padding: 0 200px;
        }

        .left, .right {
            width: 200px;
            height: 200px;
            background-color: #6d9b2f;
        }

        .content {
            width: 100%;
            height: 200px;
            background-color: #d8a7a7;
        }

        .container div {
            float: left;
        }

        .left {
            margin-left: -200px; 
            position: relative;  
            left: -100%; 
        }

        .right {
            margin-left: -200px; 
            position: relative; 
            right: -200px;
        }
 </style>
 
 // 2. 双飞翼布局
 <div class="page">
    <div class="content">
        <div class="inner">主体内容</div>
    </div>
    <div class="left">左侧广告位</div>
    <div class="right">右侧广告位</div>
</div>

<style>
        *{
            margin: 0;
            padding: 0;
        }
        .page{
            height: 200px;
        }
         .left,.right{
            height: 200px;
            width: 200px;
            background-color: #7aa74d;
        }
        .content{
            width: 100%;
            height: 200px;
            background-color: #d5adad;
        }
        .page > div{
            float: left;
        }
        
        .inner{
            margin: 0 200px;  /*上下0 左右两百 */
            height: 100%; 
            background-color: #e21616;
        }
        
        .left{
            margin-left: -100%;
        }
        .right{
            margin-left: -200px; 
        } 
</style>

// 3. flex 布局
<div class="container">
        <div class="left">左侧内容</div>
        <div class="center">中间内容</div>
        <div class="right">右侧内容</div>
</div>

<style>
        .container {
            display: flex;
        }
        .left {
            width: 200px; /* 左侧宽度固定 */background-color: lightblue;
        }
        .right {
            width: 200px; /* 右侧宽度固定 */background-color: lightgreen;
        }
        .center {
            flex-grow: 1; /* 中间自适应 */background-color: lightcoral;
        }
</style>

三、如何结合代码写结果

async function async1() {
    console.log('async1 start') // 2
    await async2()
    console.log('async1 end') // 6
}

async function async2() {
    console.log('async2') // 3
}

console.log('script start') // 1

setTimeOut(function() {
    console.log('setTimeout') // 8
})

async1()

new Promise(function (resolve) {
    console.log('promise1') // 4
    resolve()
}).then(function () {
    console.log('promise2') // 7
})
console.log('script end') // 5

执行原理:

前端开发必须掌握哪些基础知识?大厂笔面试必备问题!

根据事件循环机制来执行, 最后顺序为:

script start

async1 start

async2

promise1

script end

async1 end

promise2

setTimeout

结语

通过以上问题的梳理与解答,我们不仅回顾了前端开发中的一些基础知识,还深入探讨了如数组操作、数据结构选择、深拷贝与浅拷贝、Git操作、函数差异、Promise状态与用法等关键知识点。这些知识点不仅是笔面试中的高频考点,也是日常开发中不可或缺的技能。

延展阅读:

数据标注师如何提升自己的能力?有哪些优秀的数据标注工具使用案例?

软件质量管理有什么提高效率的常用工具?

软件开发时必须使用设计模式吗?观察者模式是什么?

咨询方案 获取更多方案详情                        
(0)
研发专家-luck_luo研发专家-luck_luo
上一篇 2024年9月21日 上午10:20
下一篇 2024年9月21日 上午11:00

相关推荐