前端基础面试题

254 阅读21分钟

CSS

1. 标准盒模型&&怪异盒模型

标准盒模型: 以谷歌浏览器为标准的盒模型,其 width = content,高度计算相同;

怪异盒模型: 以IE浏览器为标准的盒模型,其 width = content + padding,高度计算相同;

当没有设置box-sizing属性时默认是content-box,也就是标准盒模型,当我们需要转变成怪异盒模型时,只需要设置box-sizing: border-box

2. CSS选择器有哪些,他们的优先级(权重)?

!important(Infinity(无穷大)) > 行内样式(内联样式)(1000)> ID选择器(100) > 类选择器、属性选择器、伪类选择器(10) > 标签选择器、伪元素(1) > 通配符选择器 (0)> 浏览器自定义的属性和继承

3. 伪类和伪元素有哪些,他们的区别是什么?

  • 伪类:以冒号(:)开头,用于选择处于特定状态的元素。

  • 伪元素:以双冒号(::)开头,用于在文档中插入虚构的元素。

  • 伪类用于向某些已经存在的选择器添加特殊效果(当状态改变时)

  • 伪元素用于将特殊效果添加到不存在的虚拟元素中(浏览器自动创建)

也就是说伪类的本质还是类(class),作用于标签本身,只不过限定了状态条件;而伪元素的本质是元素(element),作用于该虚拟元素的内容本身。

常用的伪类:

  • 动态伪类::visited(链接已访问时)、:focus(元素已获取焦点时)、:hover(元素处于悬浮状态时)`等
  • 状态伪类::disabled(表单项是否禁用)、:empty(元素处于没有子元素状态时)、:required(表单项是否必填 等
  • 结构伪类::first-child(元素是首个子元素时)、:nth-of-type(元素是第 n 个特定类型的子元素时)等
  • 其他伪类::target(元素 id 匹配到哈希值时)、:lang(匹配到指定语言时)、:not()(不满足指定条件时)等

常用的伪元素:

::first-letter:选中块状元素中的首字母

::first-line:选中首行

::before:在之前创建一个不在文档树中的元素

::after:在之后创建一个不在文档树中的元素

::placeholder:选中表单元素的占位文本

::file-selector-button:选中类型为 file 的 input 里面的 button

::selection:选中被用户高亮的部分

::backdrop:选中视觉聚焦元素后面的背景元素

::marker:选中 list 的 marker

4. display: none; opacity: 0;visibility: hidden;的区别是什么?

display:none

DOM 结构:浏览器不会渲染 display 属性为 none 的元素,不占据文档流;
事件监听:无法进行 DOM 事件监听;
性能:动态改变此属性时会引起重排,性能较差;
继承:不会被子元素继承,毕竟子类也不会被渲染;
transitiontransition 不支持 display

visibility: hidden;

DOM 结构:元素被隐藏,但是会被渲染不会消失,占据文档流;
事件监听:无法进行 DOM 事件监听;
性 能:动态改变此属性时会引起重绘,性能较高;
继 承:会被子元素继承,子元素可以通过设置 visibility: visible; 来取消隐藏;
transitiontransition 支持 visibility。
复制代码

opacity: 0;

DOM 结构:透明度为 100%,元素隐藏,占据文档流;
事件监听:可以进行 DOM 事件监听;
性 能:提升为合成层,不会触发重绘,性能较高;
继 承:会被子元素继承,且,子元素并不能通过 opacity: 1 来取消隐藏;
transition:transition 支持 opacity。

5. 什么是BFC容器?

BFC(Block Formatting Context):块级格式化上下文。

BFC决定了元素如何对其内容进行定位,以及与其他元素的关系和相互作用。当设计到可视化布局的时候,BFC提供了一个环境,HTML元素在这个环境中按照一定的规则进行布局。一个环境中的元素不会影响到其他环境中的布局。

渲染规则

  • BFC元素垂直方向的边距会发生重叠。属于不同BFC外边距不会发生重叠
  • BFC的区域不会与浮动元素的布局重叠。
  • BFC元素是一个独立的容器,外面的元素不会影响里面的元素。里面的元素也不会影响外面的元素。
  • 计算BFC高度的时候,浮动元素也会参与计算(清除浮动)

如何创建BFC

  • overflow不为visible;
  • float的值不为none;
  • position的值不为static或relative;
  • display属性为inline-blocks,table,table-cell,table-caption,flex,inline-flex;

6. 清除浮动的方法有哪些?

  • 给定浮动元素的父容器高度,只要浮动元素在一个有高度的盒子里,那么这个浮动就不会影响后面的元素了,也就是清除了浮动带来的影响;(理想状态,一般不推荐)
  • 在被浮动影响的容器上添加清除浮动 clear: both; )(影响性能,代码的可读性不强)clear属性不允许被清除浮动的元素的左边/右边挨着浮动元素,底层原理是在被清除浮动的元素上边或者下边添加足够的清除空间。不要在浮动元素上清除浮动
  • 借助BFC容器,BFC能够清除浮动主要就是因为其“包含浮动“这条特性。
  • 伪元素标签法清除浮动(最佳实践)
// 单伪元素标签法
// 现代浏览器clearfix方案,不支持IE6/7
.clearfix:after {
    display: table;
    content: " ";
    clear: both;
}
// 全浏览器通用的clearfix方案
// 引入了zoom以支持IE6/7
.clearfix:after {
    display: table;
    content: " ";
    clear: both;
}
.clearfix{
    *zoom: 1;
}

// 双伪元素标签法
// 全浏览器通用的clearfix方案【推荐】
// 引入了zoom以支持IE6/7
// 同时加入:before以解决现代浏览器上边距折叠的问题
.clearfix:before,
.clearfix:after {
    display: table;
    content: " ";
}
.clearfix:after {
    clear: both;
}
.clearfix{
    *zoom: 1;
}

7. px、rem、em、vw的区别是什么?

  • 绝对长度 px - 像素值,是一个固定长度
  • 相对长度 rem、em、vw、vh
  • rem的值是px的倍数,默认情况下font-size = 16px,那么1rem = 16px,rem是根据html的跟字体大小来决定的,当我们把html的font-size改为32px,那么1rem = 32px
  • em的值是px的倍数,默认情况下font-size = 16px,那么1em = 16px,em是根据父元素身上的的font-size来决定的。
  • vh 和 vw 就是根据窗口的宽高,分成100等份,100vh就表示满高,50vh就表示一半高。vh 和 vw 始终是针对窗口的宽高。

8. 父元素宽高未知,如何让子元素水平居中?

  • 父容器:display:flex; 子容器:margin:auto;
  • 父容器:display:flex; justify-content : center; align-item : center;
  • 父容器:display:grid; 子容器:margin:auto;
  • 父容器:display:grid; 子容器:align-self:center justify-self:center
  • 父容器:display: table-cell text-align: center vertical-align: middle;子容器:display: inline-block;
  • 父容器:position: relative 子容器:position: absolute; top: 50%; left: 50%; transform: translate(-50%,-50%);

9. display:inline-block元素之间空隙的产生原因和解决办法?

产生原因:

元素被当成行内元素排版的时候,元素之间的空白符(空格、回车换行等)都会被浏览器处理,根据white-space的处理方式(默认是normal,合并多余空白),原来HTML代码中的回车换行被转成一个空白符,在字体不为0的情况下,空白符占据一定宽度,所以inline-block的元素之间就出现了空隙。这些元素之间的间距会随着字体的大小而变化,当行内元素font-size:16px时,间距为8px。

解决方法:

  • 解决元素之间的空白符(代码可读性变差)
  • 为父元素中设置font-size: 0,在子元素上重置正确的font-size。(inline-block元素必须设定字体,不然行内元素中的字体不会显示。 增加了代码量。)
  • 为inline-block元素添加样式float:left(float布局会有高度塌陷问题)
  • 设置父元素,display:table和word-spacing

10. transition,transform,animation的区别?

transition属性是transition-propertytransition-durationtransition-timing-functiontransition-delay的一个简写属性。用来定义某个css属性或者多个css属性的变化的过渡效果

transition - CSS:层叠样式表 | MDN (mozilla.org)

transform主要用于给元素做变换,主要由以下几种变换,rotate(旋转),scale(缩放),skew(扭曲),translate(移动)和matrix(矩阵变换)。

transform - CSS:层叠样式表 | MDN (mozilla.org)

animation动画的定义,先通过@(-webkit-)keyframes定义动画名称及动画的行为,然后再通过animation的相关属性定义动画的执行效果.

animation - CSS:层叠样式表 | MDN (mozilla.org)

JS

1. CSS下载时会不会阻塞JS的执行?

css 文件没下载并解析完成之前,后续的 js 脚本不能执行。

<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.4.1/dist/css/bootstrap.min.css">
<script>
    alert('ok')
</script>

2. 如何使JS的加载不会阻塞页面的渲染?

  • 在script标签添加async属性,有 async,会在 HTML 文档解析时并行下载文件,并在下载完成后立即执行(暂停 HTML 解析)。
  • 在script标签添加defer属性,有 defer,加载后续文档元素的过程将和 script.js 的加载并行进行(异步),但是 script.js 的执行会在所有元素解析完成之后,DOMContentLoaded 事件触发之前完成。

Defer 和 async 的相同点

  • 加载文件时不阻塞页面渲染
  • 对于 inlinescript 无效
  • 使用这两个属性的脚本中不能调用 document.write 方法
  • 有脚本的 onload 的事件回调

Defer 和 async 的区别

  • html4.0 中定义了 defer;html5.0 中定义了 async

  • 浏览器支持不同

  • 加载时机:

    • 具有 async 属性的脚本都在它下载结束之后立刻执行,同时会在 window 的 load 事件之前执行。所以就有可能出现脚本执行顺序被打乱的情况;
    • 具有 defer 属性的脚本都是在页面解析完毕之后,按照原本的顺序执行,同时会在 document 的 DOMContentLoaded 之前执行。

3. JS中的数据类型有哪些?判断数据类型的方法有哪些?

  • 基础数据类型
    • String
    • Number
    • Boolean
    • null
    • undefined
    • BigInt
    • Symbol
  • 引用数据类型
    • Object
    • Function
    • Array
    • Date
    • ...
  • 判断数据类型方法
    • typeof 常用于判断基础类型(除了null,typeof通过机器码判断,null机器码全为0,会被判定为object类型),引用类型只能准确判断出Function
    • instanceof 常用于判断引用数据类型,通过判断原型是否相等来识别数据类型(不能判断基础类型,原型可以被修改,判断可能不准确)
    • Object.prototype.toString.call() 可以判断几乎所有的数据类型。
    • isArray()
    • isNaN() (NaN === NaN // false)
    • ......

4. 原型和原型链是什么?

原型([[Prototype]])  是javascript中函数function对象的一个特殊内置属性,其实就是对于其他对象的引用,它定义了构造函数制造出来的对象的公共祖先,通过构造函数产生的对象,可以继承到该原型的属性和方法。原型也是对象。

实例对象的隐式原型 === 构造函数的显式原型。

在原型上加一个原型,再加一个原型...把原型连成链,访问顺序和这个链的顺序一致,与作用域链类似,叫做原型链

image.png

5. 深浅拷贝?当拷贝对象中有一个属性是函数或者正则应该怎么解决?

浅拷贝会创建一个新的对象,浅拷贝得到的对象有着原对象属性的一份精确拷贝,如果属性是基本数据类型,拷贝的就是基本数据类型的值,如果属性是引用类型,拷贝的就是内存地址,无论我们修改拷贝之后的对象或者是原对象共有的属性时,另一个对象也会受到改变。

深拷贝会拷贝所有属性,并拷贝属性指向的动态分配的内存。当对象和它所引用的对象一起拷贝时即发生深拷贝。深拷贝相比于浅拷贝速度较慢并且花销较大。拷贝前后两个对象互不影响。

手写浅拷贝

function shallowCopy(obj) {
  if (!obj instanceof Object) return obj;

  let newObj = obj instanceof Array ? [] : {};

  for (let key in obj) {
    if (obj.hasOwnProperty(key)) {
      newObj[key] = obj[key]
    }
  }
  return newObj;
}

手写深拷贝(较为完整版)

function deepClone(obj) {
  // 处理null
  if (obj === null) return null;
  // 处理原始值
  if (typeof obj != "object" && typeof obj != "function") return obj;
  // 处理函数
  if (typeof obj === "function") {
    let func = obj + ''
    // 返回匿名函数
    // new Function('...')()  直接执行引号中的内容,与eval
    return new Function('return ' + func)();
  }
  // 处理正则
  if (obj instanceof RegExp) {
    return new RegExp(obj);
  }
  // 处理Set对象
  if (obj instanceof Set) {
    return new Set(obj);
  }
  // 处理Map对象
  if (obj instanceof Map) {
    return new Map(obj);
  }
  // 处理日期
  if (obj instanceof Date) {
    return new Date(obj);
  }
  // 用被克隆对象的构造函数创建一个实例,这样克隆出来的对象也是那个构造函数的实例,保持相同的所属类
  let newObj = new obj.constructor;
  for (let key in obj) {
    if (obj.hasOwnProperty(key)) {
      newObj[key] = deepClone(obj[key]);
    }
  }

  return newObj;
}

6. 聊一聊你所知道的异步编程

因为js是单线程语言,所以会导致代码阻塞问题,为了代码的执行效率于是乎有了异步代码的概念,最早是回调函数,再到ES6新增的Promise,再到Promise语法糖 async await,都能有效的解决大部分的异步编程问题。(个人回答)

7. 什么是闭包?你平时使用到的闭包有哪些?

当js执行引擎中,一个函数执行完毕后该函数的执行上下文就应该被浏览器的垃圾回收机制回收掉,但是当一个函数内部的函数被返回出来执行,那么内部函数对外部函数存在引用,引用的这些变量的集合称之为闭包。这些集合不会随外部函数的执行上下文的回收而消失。

关于闭包你该了解这些 - 掘金 (juejin.cn)

当面试官问到平时有哪些使用的闭包时,我没有回答上来,但是React的hooks函数和Vue的Composition API其实都是闭包的实际应用。

8. JS中的最大值和最大安全值?如果两个数相加大于最大值,应该怎么解决才能正常显示正常的值?

在js中最大值为 2 ^ 53,通常称 2 ^ 53 - 1为最大安全值,如果两个数相加大于最大值,正常显示的解决方法:

  • 将两个数字转成BigInt类型,bigint类型都是ES6为解决最大值而定义出的基础数据类型;
  • 将两个数字转成字符串,从最后一位开始遍历,个位个位相加,满10进1,最后转回数字;

9. Array.map 、 Array.every 、 Array.some的区别?

  • map方法接收一个回调函数,回调函数里面有三个参数,第一个参数为遍历到的每一项、第二个参数为遍历到的下标、第三个参数为原数组,其作用是将每一项的回调执行结果存到一个新的数组中返回出来;
  • every方法是返回一个布尔值,只有当数组中的每一项都满足every中的条件时返回true,否则返回false;
  • some方法与every方法刚好相反,只需要数组中有一项满足条件则返回true,否则返回false;

10. 讲一讲call、apply、bind的区别

这三个方法都是用于显示绑定作用域的,都可以改变this的指向

  • call、apply、bind第一个参数都是this要指向的对象,如果如果没有这个参数或参数为undefinednull,则默认指向全局window
  • call第二个参数之后都是参数列表,但是apply是将call后面的所有参数都放在一个数组中作为第二个参数,bind可以多次传参;
  • call、apply绑定之后立即执行,bind返回绑定this之后的函数。

11. requestAnimationFrame 和 requestIdleCallback 了解过吗?区别是什么?

window.requestAnimationFrame() 会在浏览器重绘前调用传入的回调函数回调函数的执行次数与浏览器的最大 fps 相等

window.requestIdleCallback()方法将在浏览器的空闲时段内调用的函数排队。这使开发者能够在主事件循环上执行后台和低优先级工作,而不会影响延迟关键事件,如动画和输入响应;  

如果一直没有 空闲时间,requestIdleCallback() 的回调就会一直得不到调用。我们可以通过设置第二个的 timeout 属性,让 requestIdleCallback() 在 timeout 时间之后,必定执行。

Vue

1. 聊一聊Vue的生命周期

image.png

通常来说我们都会两个两个来记忆

Vue2.0

  • beforeCreate:在实例初始化之后,进行数据侦听和事件/侦听器的配置之前同步调用。
  • created:在实例创建完成后被立即同步调用。在这一步中,实例已完成对选项的处理,意味着以下内容已被配置完毕:数据侦听、计算属性、方法、事件/侦听器的回调函数。然而,挂载阶段还没开始,且 $el property 目前尚不可用。
  • beforeMount:在挂载开始之前被调用:相关的 render 函数首次被调用。
  • mounted:实例被挂载后调用,这时 el 被新创建的 vm.$el 替换了。如果根实例挂载到了一个文档内的元素上,当 mounted 被调用时 vm.$el 也在文档内。
  • beforeUpdate:在数据发生改变后,DOM 被更新之前被调用。这里适合在现有 DOM 将要被更新之前访问它,比如移除手动添加的事件监听器。
  • updated:在数据更改导致的虚拟 DOM 重新渲染和更新完毕之后被调用。
  • activated:被 keep-alive 缓存的组件激活时调用。
  • deactivated:被 keep-alive 缓存的组件失活时调用。
  • beforeDestroy:实例销毁之前调用。在这一步,实例仍然完全可用。
  • destroyed:实例销毁后调用。该钩子被调用后,对应 Vue 实例的所有指令都被解绑,所有的事件监听器被移除,所有的子实例也都被销毁。
  • errorCaptured:vue2.5.0新增,在捕获一个来自后代组件的错误时被调用。此钩子会收到三个参数:错误对象、发生错误的组件实例以及一个包含错误来源信息的字符串。此钩子可以返回 false 以阻止该错误继续向上传播。

Vue3.0

  • 将beforeDestroy更名为beforeUnmount
  • 将destroyed更名为unmounted

钩子函数在其生命周期前加上on-前缀,且从onMounted开始,之前的证明周期没有钩子函数。

2. Vue中nextTick的效果是什么?

Vue中等待下一次 DOM 更新刷新的工具方法。

当你在 Vue 中更改响应式状态时,最终的 DOM 更新并不是同步生效的,而是由 Vue 将它们缓存在一个队列中,直到下一个“tick”才一起执行。这样是为了确保每个组件无论发生多少状态改变,都仅执行一次更新。

nextTick() 可以在状态改变后立即使用,以等待 DOM 更新完成。你可以传递一个回调函数作为参数,或者 await 返回的 Promise。

3. Vue中阻止事件冒泡的方法有哪些?

  • event.stopPropagation();
  • v-on:click.stop

4. V-if、V-show的区别?

  • V-if当条件不满足时不渲染其DOM结构
  • V-show其效果相当于display: none,渲染DOM结构时需要加载

5. 子组件如何调用父组件的方法(组件通信)

  • 父组件在子组件身上v-bind绑定需要调用的方法,子组件通过props接受并调用
  • provide/reject

6. CSS如何样式隔离?

  • scoped样式隔离
  • 使用css预处理器(基本不会样式影响)

浏览器

1. 从浏览器地址栏输入url到页面渲染都发生了什么?

  1. 解析域名

  2. 缓存判断 --有资源直接返回、否则向服务器发起新的请求

  3. DNS解析( 客户端 --> 本地域名服务器 --> 根域名服务器 --> 顶级域名服务区 --> XXX服务器 --> 返回给本地域名服务器(并缓存在本地))

  4. 获取MAC地址

  5. 会把请求的内容存储到dns

  6. TCP三次握手

  7. HTTPS握手

  8. 返回数据

  9. TCP四次挥手

2. 讲一讲浏览器的缓存机制

浏览器出于优化http请求于是会将一些长期不会更改的资源缓存到本地,这样就不需要每次都向服务器请求数据,是否缓存取决于后端返回头中是否包含缓存的字段。

首先缓存也就是我们所说的HTTP缓存机制,分为强制缓存协商缓存强制缓存有两个字段,一个是Expires,Expires是HTTP/1.0控制网页缓存的字段,其值为服务器返回该请求结果缓存的到期时间,即再次发起该请求时,如果客户端的时间小于Expires的值时,直接使用缓存结果。

到了HTTP/1.1,Expire已经被Cache-Control替代,原因在于Expires控制缓存的原理是使用客户端的时间与服务端返回的时间做对比,那么如果客户端与服务端的时间因为某些原因(例如时区不同;客户端和服务端有一方的时间不准确)发生误差,那么强制缓存则会直接失效,这样的话强制缓存的存在则毫无意义。设置max-age=xxx (xxx is numeric):缓存内容将在xxx秒后失效。

而协商缓存是为了协助强制缓存,来实现更好的缓存效果,协商缓存也有两个字段,一个是后端会返回一个字段Last-Modified,是服务器响应请求时,返回该资源文件的最后修改时间,If-Modified-Since则是客户端再次发起该请求时,携带上次请求返回的Last-Modified值,通过此字段值告诉服务器该资源上次请求返回的最后被修改时间。服务器收到该请求,发现请求头含有If-Modified-Since字段,则会根据If-Modified-Since的字段值与该资源在服务器的最后被修改时间做对比,若服务器的资源最后被修改时间大于If-Modified-Since的字段值,则重新返回资源,状态码为200;否则则返回304,代表资源无更新,可继续使用缓存文件。

Etag是服务器响应请求时,返回当前资源文件的一个唯一标识(由服务器生成),If-None-Match是客户端再次发起该请求时,携带上次请求返回的唯一标识Etag值,通过此字段值告诉服务器该资源上次请求返回的唯一标识值。服务器收到该请求后,发现该请求头中含有If-None-Match,则会根据If-None-Match的字段值与该资源在服务器的Etag值做对比,一致则返回304,代表资源无更新,继续使用缓存文件;不一致则重新返回资源文件,状态码为200。

详情可参考彻底理解浏览器的缓存机制 - 掘金 (juejin.cn)

3. http和https的区别是什么?了解过https的加密方式吗?

HTTP协议也就是超文本传输协议,是一种使用明文数据传输的网络协议。一直以来HTTP协议都是最主流的网页协议,HTTP协议被用于在Web浏览器和网站服务器之间传递信息,以明文方式发送内容,不提供任何方式的数据加密,如果攻击者截取了Web浏览器和网站服务器之间的传输报文,就可以直接读懂其中的信息。

为了解决HTTP协议的这一缺陷,需要使用另一种协议:安全套接字层超文本传输协议HTTPS,为了数据传输的安全,HTTPS在HTTP的基础上加入了SSL/TLS协议,SSL/TLS依靠证书来验证服务器的身份,并为浏览器和服务器之间的通信加密。HTTPS协议可以理解为HTTP协议的升级,就是在HTTP的基础上增加了数据加密。在数据进行传输之前,对数据进行加密,然后再发送到服务器。这样,就算数据被第三者所截获,但是由于数据是加密的,所以你的个人信息仍然是安全的。这就是HTTP和HTTPS的最大区别。

彻底搞懂HTTPS的加密原理 - 知乎 (zhihu.com)