2022 前端高频面试题

214 阅读3分钟

CSS 面试题目

1、padding和margin有什么不同

  • 作用的对象不同,margin作用对象是外部,padding作用于自身
  • 视距上,margin改变位置,padding改变大小

2、vw,vh和百分比的区别

  • vw是根据视窗大小决定,百分比根据父级决定

3、行内元素和块级元素区别

  • 行内元素大小由内容决定,不能改变大小,块级元素会占满整行(可以改变大小)

4、如何让谷歌支持小字体

  • 谷歌默认支持最小12px,可以通过transform:scale()变小

JavaScript 面试题目

1、let和var

  • var变有声明提升的问题
  • var没有局部作用域
function fn(){

    for(var i=0;i<5;i++){

    }
    console.log(i) // i === 5
}

  • var会有声明覆盖的问题
var name ="aaa"
var name = "bbb"
console.log(name) // bbb

2、深拷贝和浅拷贝

// 引用类型才有有深拷贝浅拷贝的问题

// 1、数组和对象的赋值都叫做 浅拷贝
let arr = [1,2,3]
let newArr = arr;
newArr.push(4)
console.log(arr,newArr) // [1,2,3,4],[1,2,3,4]  藕断丝连

// 2、结构赋值不会影响到原数组,对象
let arr2 = [1,2,3]
let newArr2 =[...arr2]
newArr2.push(4)
console.log(arr2,newArr2)  // [1,2,3],[1,2,3,4] 

// 3、解构赋值也会影响到原数组,说明它也是浅拷贝
// 针对一维数组对象,不会影响原数组对象,但是多维就不行了
let arr3 = [[1,2,3],[4,5,6]]
let newArr3 =[...arr3]
newArr3[0].push(999)
console.log(arr3,newArr3) // [[1,2,3,999],[4,5,6]]  [[1,2,3],[4,5,6]]

// 深拷贝用法

let list=[
    {id:1,stuName:"小明",age:11},
    {id:2,stuName:"小红",age:12},
    {id:3,stuName:"小黄",age:13}
]

let newList = JSON.parse(JSON.strignify(list)) // json 深拷贝 ;没办法转换function
newList.push({id:888})
console.log(list,newList) // 不会影响原数组,只有newList加了id:888


function deepClose(source){
    // [] => Array 基类 {} => Object 
    const targetObj = source.constructor === Array ? [] : {}
    for(let keys in source){
        if(source.hasOwnProperty(keys)){
            // keys => 1.基础数据类型,2.对象,3.数组
            if(source[keys] && typeof source[keys] === 'object' ){
                 targetObj[keys]  = deepClose(source[keys])
            }else {
                // 基本数据类型,直接赋值
                targetObj[keys] = source[keys]
            }
        }
    }

    return targetObj
}

性能优化

1、浏览器输入URL做了什么

  • 输入 www.baidu.com // 统一资源定位符,俗称网址
  • 1、第一次访问
  • 2、解析URL,并且去DNS域名系统匹配
  • 3、拿到真实的IP ,建立连接(TCP3次握手)
  • 4、拿到数据,渲染页面
  • 5、拿到数据后,断开连接(四次挥手)
html->dom树
HTML和css并行构建       -> render tree  -> 计算布局信息 -> UI引擎渲染 -> 用户见到的页面
css->css结构体                               回流           重绘
  • 6、第二次访问
  • 7、浏览器会将解析的IP地址存放在本地 => 读取浏览器缓存的IP

2、从哪些点做性能优化

  • 1、加载上的优化
    • 减少http请求(精灵图,文件的合并)
    • 减小文件大小(资源压缩,图片压缩,代码压缩)
    • CND(第三方库引用CDN)
    • 预加载(SSR服务端渲染)
    • 懒加载
    • 分包
  • 2、性能方面
    • 减少dom操作,避免回流,文档碎片(使用transform:translate -> 脱离正常文档流,避免回流)

3、真正的性能优化

  • 页面加载性能(加载时间,用户体验)
  • 动画与操作性能 是否流畅
  • 内存占用 内存占用过大,浏览器崩溃
  • 电量消耗

4、懒加载

<body>
    <img src="./img/loading.gif" data-src="./img/turePicture.jpg" alt="src为备胎图片,data-src为真实图片" />
    <img src="./img/loading.gif" data-src="./img/turePicture.jpg" alt="src为备胎图片,data-src为真实图片" />
    <img src="./img/loading.gif" data-src="./img/turePicture.jpg" alt="src为备胎图片,data-src为真实图片" />
    <img src="./img/loading.gif" data-src="./img/turePicture.jpg" alt="src为备胎图片,data-src为真实图片" />
    <img src="./img/loading.gif" data-src="./img/turePicture.jpg" alt="src为备胎图片,data-src为真实图片" />
    <img src="./img/loading.gif" data-src="./img/turePicture.jpg" alt="src为备胎图片,data-src为真实图片" />
    <img src="./img/loading.gif" data-src="./img/turePicture.jpg" alt="src为备胎图片,data-src为真实图片" />
    <img src="./img/loading.gif" data-src="./img/turePicture.jpg" alt="src为备胎图片,data-src为真实图片" />
    <img src="./img/loading.gif" data-src="./img/turePicture.jpg" alt="src为备胎图片,data-src为真实图片" />
    <img src="./img/loading.gif" data-src="./img/turePicture.jpg" alt="src为备胎图片,data-src为真实图片" />
</body>

<script>
    let num = document.getElementsByTagName("img").length; // 9
    let img = document.getElementsByTagName("img")
    let n = 0
    lazyLoad()
    function lazyLoad (){
        let windowsHeight = document.documentElement.clientHeight //可见区域
        let scrollTop = document.documentElement.scrollTop || document.body.scrollTop // 滚动距离
         
        for(let i=n;i<num;i++){
            if(img[i].offsetTop < windowsHeight+scrollTop){
                if(img[i].getAttribute("src") === "./img/loading.gif"){
                    img[i].src = img[i].getAttribute("data-src") // 备胎转正
                }
            }
        }
    }

</script>

5、this的指向问题

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <script>
        console.log(this) // window
        function a(){
            console.log(this) // window
        }
        a()

    const obj ={
        a:1,
        b:{
            a:1,
            fn:function(){
                 console.log(this) // b ;this指向上一个调用者
            }
        }
    }
    obj.b.fn()
    // call、apply、bind可以改变this指向
    // call 和 apply 会立即执行 apply 不会
     


    </script>
</body>
</html>

6、闭包

  • 避免变量被污染
  • 私有化
  • 保存变量,常驻内存
  • 闭包:方法里放一个方法
// 闭包应用 => 处理私有数据
let makeCounter = function(){
    let privateCounter = 0;
    function changeBy(val){
        privateCounter += val
    }
    return {
        increment: function () {
            changeBy(1)
        },
        decrement: function () {
            changeBy(-1)
        },
        value: function () {
            return privateCounter
        }
    }
}

let c = makeCounter
c.increment()
c.decrement()
c.value()

7、new

function Person(){
    this.name = "xiaohong"
    this.fn = function(){
        console.log(`名字是:${this.name}`)
    }
}

let person1 = new Person(); // new 的时候Person才是构造函数
person1.fn()

// new的过程
// 1、创建一个空对象
let obj = new Person()
// 2、设置原型链 __proto__ 指向new 出来的类
obj._proto_ = Person.prototype
// 3、改变this的指向
let result = Person.call(obj)
// 4、判断返回值类型
// 构造函数默认返回新创建的对象
// 普通函数默认返回undefined 
if(typeof result === "object"){
    person1 = result
}else {
    person1 = obj
}

8、vue底层原理

  • 如何实现VUE2.x的响应式

  • 发布订阅模式+双向数据绑定 = 基本的响应式

    <!-- 1、 订阅器模型 -->
      let Dep = {
          clientList:{},
          <!-- 添加订阅 -->
          lister:function(key,fn){
             (this.clientList[key] || (this.clientList[key] = [])).push(fn) 
          },
          <!-- 推送方法 -->
          trigger:function(){
              let key = Array.prototype.shift.call(arguments),
              fns = this.clientList[key];
              if(!fns || fns.length === 0) return false
              for(let i = 0,fn;fn = fns[i++]){
                  fn.apply(this,arguments)
              }
          }
      }
      <!-- 数据劫持 -->
      let dataHijack = function({data,tag,datakey,selector}){
          let value = "",el = document.querySelector(selector)
          Object.defineProperty(data,datakey,{
              get:function(){
                  console.log("取值")
                  return value
              },
              set:function(val){
                  value = val;
                  Dep.trigger(tag,val)
              }
          })
          <!-- 添加订阅者 -->
          Dep.listen(tag,function(text){
              el.innerHTML = text
          })
      }
    

9、Vue3 的响应式原理

let obj = {
    name:"小明",
    age:18
}
const p = new Proxy(obj,{
    // target:原数据 
    get(target,propName){
        console.log(`读取P的${propName}属性`)
        return target[propName]
    },
    set(target,propName,value){
        console.log(`修改P的${propName}属性`)
        target[propName] = value
    },
    deleteProperty(target,propName){
        console.log(`删除P的${propName}属性`)
        return delete target[propName]
    }
})

9、手写Promise

function myPromise (excutor){
    // 1、执行结构
    let self = this
    self.status = 'pending'; // 状态
    self.value = null; // 成功结果
    self.reason = null; // 失败原因

    // 6、添加缓存数组
    self.onFulfilledCallbacks = []
    self.onRejectedCallbacks = []

    // 4、判断状态,做出相应的处理
    function resolve(value){
        if(self.status === "pending"){
            self.value = value // 保存成功结果
            self.status = "fulfilled"

            // 7、状态改变,依次取出
            self.onFulfilledCallbacks.forEach(item=>item(value))
        }
    }
    function reject(reason){
        if(self.status === "pending"){
            self.reason = reason // 保存失败原因
            self.status = "reject"

            // 7、状态改变,依次取出
            self.onRejectedCallbacks.forEach(item=>item(value))
        }
    }

    // 3、new的时候执行一次
    try{
        excutor(resolve,reject)
    }catch{
        reject(err)
    }
}
// 2、then方法
myPromise.prototype.then = function (onFulfilled,onRejected){
//    5、 状态改变后 调用.then方法
    onFulfilled = typeof onFulfilled === "function"?  onFulfilled : function(data){resolve(data)}
    onRejected = typeof onRejected === "function"?  onRejected :function(err){throw(err)}

    let self = this
    if(self.status === "pending"){
        self.onFulfilledCallbacks.push(onFulfilled)
        self.onRejectedCallbacks.push(onRejected)
    }
}
let demo = new myPromise((resolve,reject)=>{

})