前端面试(更新中)

181 阅读16分钟

一、CSS

1.display:none、visibility:hidden和opacity: 0的区别?

  • display: none
    DOM 结构:浏览器不会渲染 display 属性为 none 的元素,不占据空间;
    事件监听:无法进行 DOM 事件监听;
    性能:动态改变此属性时会引起重排,性能较差;
    继承:不会被子元素继承,毕竟子类也不会被渲染;
    transition:transition 不支持 display。
  • visibility: hidden
    DOM 结构:元素被隐藏,但是会被渲染不会消失,占据空间;
    事件监听:无法进行 DOM 事件监听;
    性 能:动态改变此属性时会引起重绘,性能较高;
    继 承:会被子元素继承,子元素可以通过设置 visibility: visible; 来取消隐藏;
    transition:visibility 会立即显示,隐藏时会延时。
  • opacity: 0
    DOM 结构:透明度为 100%,元素隐藏,占据空间;
    事件监听:可以进行 DOM 事件监听;
    性 能:提升为合成层,不会触发重绘,性能较高;
    继 承:会被子元素继承,且子元素并不能通过 opacity: 1 来取消隐藏;
    transition:opacity 可以延时显示和隐藏

二、JavaScript

1.说一说JS数据类型有哪些,区别是什么?

  • JS数据类型分为两类:
    一类是基本数据类型,也叫简单数据类型,包含7种类型,分别是Undefined、String、Symbol、Number 、Null、BigInt、Boolean; 一类是引用数据类型也叫复杂数据类型,通常用Object代表,普通对象,数组,正则,日期,Math数学函数都属于Object。 (即为“u so nb”)
  • 数据分成两大类的本质区别:
    基本数据类型和引用数据类型它们在内存中的存储方式不同。 基本数据类型是直接存储在栈中的简单数据段,占据空间小,属于被频繁使用的数据。
    引用数据类型是存储在堆内存中,占据空间大。引用数据类型在栈中存储了指针,该指针指向堆中该实体的起始地址,当解释器寻找引用值时,会检索其在栈中的地址,取得地址后从堆中获得实体。
  • Symbol是ES6新出的一种数据类型,这种数据类型的特点就是没有重复的数据,所以 Symbol() != Symbol(),数据的创建方法Symbol(),因为它的构造函数不够完整,所以不能使用new Symbol()创建数据。
  • BigInt也是ES6新出的一种数据类型,这种数据类型的特点就是数据涵盖的范围大,能够解决超出普通数据类型范围报错的问题。 使用方法: -整数末尾直接+n:647326483767797n -调用BigInt()构造函数:BigInt("647326483767797")

2.说一说你对闭包的理解?

闭包形成的原理:作用域链,当前作用域可以访问上级作用域中的变量; 闭包解决的问题:能够让函数作用域中的变量在函数执行结束之后不被销毁,同时也能在函数外部可以访问函数内部的局部变量。 (“闭包”关住了它所需要的变量,并将其保存起来) 闭包不会导致内存泄露! 内存泄露是指你用不到(访问不到)的变量的变量,依然占居着内存空间,不能被再次利用起来。 闭包里面的变量就是我们需要的变量,不能说是内存泄露。 解释看这里

3.Ajax和axios的区别是什么?

Ajax是对原生XHR,实现异步数据交互的技术,axios是对这种技术的封装。

  • Ajax是使用XMLHttpRequest (XHR)对象与服务器进行交互的实例,实现异步数据交互的技术。在不刷新页面的情况下请求特定 URL,获取数据。这允许网页在不影响用户操作的情况下,更新页面的局部内容。 axios是用Promise实现的对原生XHR的封装,是目前最流行的Ajax请求库。
  • Ajax原理:有客户端请求Ajax引擎,再由Ajax引擎请求服务器,服务器做出一系列相应之后返回Ajax引擎,由Ajax引擎决定将这个结果写入到客户端的什么位置,实现页面无刷新更新数据; axios特性:在浏览器中创建XHR,在nodejs中则请求http请求,支持Promise API,支持拦截请求和响应数据,取消请求,自动转换成JSON数据格式,客户端支出防御XSRF。

4.说一说promise是什么与使用方法?

(1)概念:异步编程的一种解决方案,解决了地狱回调的问题;
(2)使用方法:new Promise((resolve,reject) => { resolve(); reject(); })里面有多个resovle或者reject只执行第一个。如果第一个是resolve的话后面可以接.then查看成功消息。如果第一个是reject的话,.catch查看错误消息。
(new Promise同步代码,加入执行栈,Promise的回调是异步微任务,加入微任务队列,当执行栈中为空时执行)

5.说一说JavaScript有几种方法判断变量的类型?

(1)typeof(null和array返回的是“object”、NaN返回的是“number”);
(2)instanceof:主要用于区分引用数据类型(检测方法是检测的类型在当前实例的原型链上)
(3)Object.prototype.toString().call(),Object.prototype.toString 表示一个返回对象类型的字符串,call()方法可以改变this的指向,那么把Object.prototype.toString()方法指向不同的数据类型上面,返回不同的结果.各种数据类型都能检测且检测精准,打印结果为[Object xxx]。

6.说一说map 和 forEach 的区别?

(1)forEach是针对数组中每一个元素,提供一个可执行的函数操作,因此它(可能)会改变原数组中的值,不会返回有意义的值,或者说会返回undefined;
(2)map是会分配内存空间创建并存储一个新的数组,新数组中的每一个元素由调用的原数组中的每一个元素执行所写的函数得来,返回的就是新数组,因此不会改变原数组的值;
(3)map的处理速度比forEach快,而且返回一个新的数组,方便链式调用其他数组新方法,比如filter、reduce let arr = [1, 2, 3, 4, 5]; let arr2 = arr.map(value => value * value).filter(value => value > 10); // arr2 = [16, 25]。

7.js获取数组最后一个元素的方法?

(1)数组的pop方法,这个方法用来删除数组的最后一个方法,并返回该元素,但是如果原数组还有价值,不妨先深拷贝一份数据;
(2)数组的arr[length - 1];
(3)数组的slice方法,这个方法可以指定数组的起始索引,截取出对应元素,返回有这些元素组成的新数组,当使用负数作为参数时,就表示从数组得到最后一个元素开始计数,所以可以使用arr.slice(-1);
(4)数组的at方法,这个方法接收一个整数作为索引值,并返回索引对应的元素,所以arr.at(-1)。

8.原型和原型链

(1)构造函数虽然好用,但是存在浪费内存的问题。但创建实例对象的时候,若是复杂数据类型,则会单独再开辟一个空间来存放该复杂数据类型。 (2)js中每个构造函数都有一个 prototype 属性,称之为原型,因为这个属性的值是一个对象,所以又称原型对象(包含了所有实例共享的属性和方法)。可以把不变的方法直接定义在prototype对象上,这样所有对象的实例就可以共享这些方法原型是一个对象,我们也可以称prototype为原型对象,原型的作用是实现方法的共享

(3)对象都有一个属性__proto__指向构造函数的prototype原型对象,之所以我们对象可以使用构造函数prototype原型对象的属性和方法,就是因为对象有__proto__的存在。 (ldh.proto===Star.prototype)
原型分为显式原型prototype、隐式原型__proto__,实例对象的隐式原型指向的是原型对象,隐式原型的作用在于当前对象中没有找到我们需要的属性,会基于__proto__指向的对象prototype中进行查找。 在这里插入图片描述 (4)对于原型对象来说有一个constructor属性,指向构造函数,指向构造函数本身,主要用于记录该对象引用于那个构造函数。如果我们修改了原来的原型对象,给原型对象赋值的是一个对象,则必须手动的利用constructor指回原来的构造函数。 在这里插入图片描述

原型对象也是对象也就意味着它也有_proto_隐式属性 ,指向Object.prototype。可以理解成Person.prototyoe = new Object(),这样就可以解释为什么_proto_指向的是Object.prototype,但是Object.prototype原型对象的_proto_比较特殊,它指向的是null,这就是我们所谓的原型链。 在这里插入图片描述

  • 总结

image.png

三、前端性能优化

1.细说浏览器渲染的重排与重绘

秒懂

2.动画的优化:为什么transform 比 margin的性能好?

(1)margin属于布局属性,该属性变化会导致页面重排;对布局属性进行动画,需要不断计算动画元素的布局并更新它的像素信息,浏览器需要为每一帧进行重回并上传到CPU中进行渲染。
(2)tansform是合成属性,既不影响布局,也不会触发重绘,这就是它高性能的原因。浏览器会为元素创建一个独立的复合层,当元素内容没有发生变化,该层不会被重绘,通过重新复合来创建动画。transform和opacity为什么高性能

四、Vue

1.Vue2响应式数据原理

# 面试官的步步紧逼:Vue2 和 Vue3 的响应式原理比对

2.hash和history路由的区别?

  • hash 模式:
    • 把前端路由的路径用井号 # 拼接在真实 url 后面的模式,井号 # 后面的路径发生变化时,浏览器并不会重新发起请求,而是会触发 onhashchange 事件;
    • hash变化会触发网页跳转,即浏览器的前进和后退;
    • hash 可以改变 url ,但是不会触发页面重新加载(hash的改变是记录在 window.history 中),即不会刷新页面。也就是说,所有页面的跳转都是在客户端进行操作。因此,这并不算是一次 http 请求,所以这种模式不利于 SEO 优化。hash 只能修改 # 后面的部分,所以只能跳转到与当前 url 同文档的 url 。
    • hash 通过 window.onhashchange 的方式,来监听 hash 的改变,借此实现无刷新跳转的功能。
    • hash 永远不会提交到 server 端。
  • History模式:
    • 利用H5的 history中新增的两个API pushState() 和 replaceState() 来实现无刷新跳转的功能和一个事件onpopstate监听URL变化;
    • 新的 url 可以是与当前 url 同源的任意 url ,也可以是与当前 url 一样的地址,但是这样会导致的一个问题是,会把重复的这一次操作记录到栈当中;
    • history每次刷新会重新像后端请求整个网址,也就是重新请求服务器。如果后端没有及时响应,就会报错404!
  • 两者选择
    • to B 的系统推荐用 hash ,相对简单且容易使用,且因为 hash 对 url 规范不敏感;(面向企业用户)
    • to C 的系统,可以考虑选择 H5 history ,但是需要服务端支持(面向个体用户,如淘宝)

3.原生微信小程序数据双向绑定和vue实现原理的区别?

(1)原生微信小程序:

<input value="{{value}}" />
  • 在WXML中,普通的属性的绑定都是单向的,this.data用来获取页面data对象,使用this.setData()函数将数据从逻辑层发送到视图层,同时改变对应的this.data的值,实现页面的更新。直接修改this.data无效,只是逻辑层的改变;
    • this.setData中设置的key如果只有key没有value,则从所在函数内找这个变量,直接渲染到前台并修改原data中的数据;若Page对象的data中没有定义该key,则setData自动创建。
  • 实现简易数据的双向绑定,可以使用model: 前缀,但是只能实现一个单一字段的绑定;且目前,尚不能 data 路径,如
<input model:value="{{ a.b }}" />

是目前暂不支持。

  • 在自定义组件中实现数据双向绑定:
Component({
  properties: {
    myValue: String
  }
})
<input model:value="{{myValue}}" />

这个自定义组件将自身的 myValue 属性双向绑定到了组件内输入框的 value 属性上。 这样,如果页面这样使用这个组件:

<custom-component model:my-value="{{pageValue}}" />

当输入框的值变更时,自定义组件的 myValue 属性会同时变更,这样,页面的 this.data.pageValue 也会同时变更,页面 WXML 中所有绑定了 pageValue 的位置也会被一同更新。

  • 在自定义组件中触发双向绑定更新
    自定义组件还可以自己触发双向绑定更新,做法就是:使用 setData 设置自身的属性。例如:
// custom-component.js
Component({
  properties: {
    myValue: String
  },
  methods: {
    update: function() {
      // 更新 myValue
      this.setData({
        myValue: 'leaf'
      })
    }
  }
})

如果页面这样使用这个组件:

<custom-component model:my-value="{{pageValue}}" />

当组件使用 setData 更新 myValue 时,页面的 this.data.pageValue 也会同时变更,页面 WXML 中所有绑定了 pageValue 的位置也会被一同更新。 (2)Vue:

  • 响应式原理:
    Vue响应原理的核心是数据劫持和依赖收集,主要是利用Object.defineProperty()实现对数据存取操作的拦截,我们把这个实现称为数据代理;同时我们通过对数据get方法的拦截,可以获取到对数据的依赖,并将出所有的依赖收集到一个集合中;而在我们给属性赋值(修改属性)时,会触发这里定义的setter函数,在次函数中会去通知集合中的依赖更新,做到数据变更驱动视图变更。 在vue3中,核心思想一致,只不过数据的劫持使用Proxy而不是Object.defineProperty,只不过Proxy相比Object.defineProperty在处理数组和新增属性的响应式处理上更加方便。
  • 数据双向绑定
    Vue通过v-model指令为组件添加上input事件处理和value属性的赋值,实现数据的双向绑定。
<template>
    <input v-model='localValue'/>
   <!-- 相当于这里添加了input时间的监听和value的属性绑定 -->
   <input @input='onInput' :value='localValue' />
   <span>{{localValue}}</span>
</template>
<script>
  export default{
    data(){
      return {
        localValue:'',
      }
    },
    methods:{
      onInput(v){
         //在input事件的处理函数中更新value的绑定值
         this.localValue=v.target.value;
         console.log(this.localValue)
      }
    }
  }
</script>

因此当我们修改input输入框中的值时,我们通过v-model绑定的值也会同步修改

4.Vue中style标签的scoped属性怎么实现的

scoped属性的效果主要是通过PostCss实现的

  • 为组件实例生成一个唯一标识,给给<style scoped>中的每一个选择器的最后一个选择器添加一个标签属性data-v-xxx

  • 样式穿透:在实际开发过程中,我们常常用到一些第三方的组件库,当需要修改这些组件的默认样式的时候,又不想去除scoped污染全局,就可以使用样式穿透。

    • css原生直接使用 >>>
    • 项目中使用了less、Sass、Scss预处理器>>>可能导致无法编译而报错,可以使用/deep/(vue-cli3以上版本不可使用)
    • ::deep

五、计算机网络

1.DNS查找解析域名的过程

大佬NewBee

六、操作系统

1.什么是进程、线程,进程和线程的区别?

(1)进程 进程就是正在执行的程序,是操作系统资源分配的基本单位;
(2)线程 线程是进程内部的不同的执行路径,是操作系统独立调度的基本单位; 一个进程中可以有多个线程,它们共享进程资源。比如说,微信和浏览器是两个进程,浏览器进程里面有很多线程,例如 HTTP 请求线程、事件响应线程、渲染线程等等,线程的并发执行使得在浏览器中点击一个新链接从而发起 HTTP 请求时,浏览器还可以响应用户的其它事件;
(3)区别 进程是资源分配的基本单位,但是线程不拥有资源,线程可以访问隶属于进程的资源; 线程是独立调度的基本单位,在同一进程中,线程的切换不会引起进程切换,从一个进程中的线程切换到另一个进程中的线程时,会引起进程切换....

2.进程有哪些状态?

  • 进程一共有5中状态,分别是创建、就绪、运行、终止、阻塞;
  • 运行状态就是进程正在CPU上运行,在单处理机环境下,每一时刻最多只有一个进程处于运行状态;
  • 就绪状态就是说进程已处于准备运行的状态,即进程获得了除CPU之外的一切所需资源,一旦得到CPU即可运行;
  • 阻塞状态就是进程正在等待某一事件而暂停运行,比如等待某资源为可用或等待I/O完成,即使CPU空闲,该进程也不能运行。 运行态→阻塞态:往往是由于等待外设,等待主存等资源分配或等待人工干预而引起的。

阻塞态→就绪态:则是等待的条件已满足,只需分配到处理器后就能运行。

运行态→就绪态:不是由于自身原因,而是由外界原因使运行状态的进程让出处理器,这时候就变成就绪态。例如时间片用完,或有更高优先级的进程来抢占处理器等。

就绪态→运行态:系统按某种策略选中就绪队列中的一个进程占用处理器,此时就变成了运行态

七、浏览器

1.浏览器的垃圾回收机制

从 4 个面试题了解「浏览器的垃圾回收」

八、打包构建工具

1.webpack和Vite的区别?

  • webpack:分析依赖=> 编译打包=> 交给本地服务器进行渲染 首先分析各个模块之间的依赖,然后进行打包,在启动开发服务器,请求服务器时,直接显示打包结果。webpack打包之后存在的问题:随着模块的增多,会造成打出的 bundle 体积过大,进而会造成热更新速度明显拖慢。
  • vite: 启动服务器=> 请求模块时按需动态编译显示 vite相较于webpack没有打包过程,而是直接启动了一个开发服务器,vite劫持了浏览器的http请求,在后端进行响应的处理,将项目中使用的文件通过简单地分解与整合,然后再返回给浏览器,整个过程没有对文件进行打包编译,所以编译速度很快,需要打包的时候vite使用Rollup配置。