前端面试全家桶

HTML 面试题

如何理解 HTML 语义化?

点击查看答案
先解释,之后举例,然后说利弊。
解释:使用正确的 HTML 标签来搭建结构,可以清晰明了的表示页面内容。
比如:我们头部用 header 标签,导航用 nav 标签,独立的文章内容用 article 标签,页面底部。
这样不但可以让我们更容易理解代码结构,增加代码可读性和可维护性。还可以提高搜索引擎的 SEO 效果,提高网站排名,增加网站访问。

默认情况下,哪些 HTML 标签是块级元素?哪些是内联元素?

点击查看答案
先解释,后举例。
块级元素独占一行,可以设置宽高;内联元素不能独占一行,也不能设置宽高。还有一个内联块元素,也可以称作可替换元素,它具有内联元素不独占一行的特性,也有块级元素能设置宽高的特性。
块级元素:div、h1-h6、ul、ol、li、dl、dd、dt、table、th、tr、td、p等
内联元素:a、span、strong、em、small、sub、sup、mark
内联块元素:img、select、button、input、video、audio、canvas等

什么叫可替换元素?

点击查看答案
先解释,后举例
是因为这些元素展示的效果和内容不是 css 直接控制的,而是由元素的标签和属性,或外部资源来决定的。这类元素的内容可以被外部资源所替换,而无需改变元素的标签结构。
以 img 标签来说,它的内容是由 src 属性指定的图片资源来决定的,而不是通过 css 来控制。

CSS 面试题

布局

盒模型的宽度如何计算?

点击查看答案
标准盒模型是 box-sizing: content-box,组成部分是:margin + border + padding + content
怪异盒模型是 box-sizing: border-box,组成部分是:margin + content(border + padding + content)

下面代码中的 div 的 offsetWith 有多大?

<style>
#div {
width: 100px;
padidng: 10px;
border: 1px solid #ccc;
margin: 10px;
}
</style>

<div id="div"></div>
点击查看答案
offsetWidth:122px
offsetWidth = (内容宽度 + 内边距 + 边框),无外边距

如何让 offsetWidth 等于 100px?

点击查看答案
css 中加一句 box-sizing: border-box;

下面代码 AAA 和 BBB 之间的距离是多少?

<style>
p {
margin-top: 10px;
margin-bottom: 15px;
font-size: 16px;
line-height: 1;
}
</style>

<p>AAA</p>
<p></p>
<p></p>
<p></p>
<p>BBB</p>
点击查看答案
15px
需要注意的是:相邻元素的 margin-top 和 margin-bottom 会重叠;空白内容的 p 标签也会重叠

margin 纵向重叠的问题

这个是父子外边距塌陷,可以开启 BFC,来解决塌陷问题。让其两个内容处于独立的层级中,互不影响。可以使用;

  • overflow:hidden;
  • 直接给父元素添加高度;
  • 子元素添加 border;

margin 负值的问题

margin 的 left、top、right、bototm 为负值会如何?
点击查看答案
margin-top 为负值,元素向上移动。
margin-left 为负值,元素向左移动。
margin-right 为负值,右侧元素左移,自身不受影响。
margin-bottom 为负值,下方元素上移,自身不受影响。

BFC 理解和应用

点击查看答案
Block format context,块级格式化上下文,开启后,形成一块独立的渲染区域,与外界元素隔离开,内部元素的渲染不会影响到边界以外的元素。
形成 BFC 的条件:
  • float 不是 none
  • position 是 absolute 或 fixed
  • overflow 不是 visible
  • display 是 flex、inline-block 等
BFC 的应用:
  • 清除浮动
  • 解决外边距合并
  • 解决高度塌陷
  • 双栏布局

可以解决高度塌陷,外边距合并问题。

可以通过:

  • overflow: hidden;
  • position 不为 relative;
  • display 为 flex、grid 等开启

float 布局

如何实现圣杯布局和双飞翼布局?

三栏布局,中间一栏先加载和渲染,随宽度自适应

点击查看答案
技术总结:
  • 使用 float 布局
  • 两侧使用 margin 赋值,以便和中间内容横向重叠
  • 防止中间内容被两侧覆盖,圣杯用 padidng 留白,双飞翼用 margin 留白

手写 clearfix

点击查看答案
        .clearfix:after {
            content: '';
            display: table;
            clear: both;
        }
        .clearfix: {
            *zoom: 1; /* 兼容 IE 低版本 */
        }
      

flex 布局

flex 实现一个三点的色子

点击查看答案
            

定位

absolute 和 relative 分别依据什么定位?

点击查看答案
absolute 根据离它最近一层的定位元素进行定位。定位元素包含:absolute、relative、fixed 或 找到 body。
relative 根据自身进行定位。

居中对齐有哪些实现方式?

水平居中

点击查看答案
inline 元素:text-align: center
block 元素:margin: auto
absolute 元素:
  • left: 50% + margin-left 负值,需要知道子元素宽度
  • left: 50% + transformX(-50%),不需要子元素宽度

垂直居中

点击查看答案
inline 元素:line-height 的值等于 hegiht 值
absolute 元素:
  • top: 50% + margin-top 负值,需要知道子元素宽度
  • top: 50% + transformY(-50%),不需要子元素宽度
  • top,left,bottom,right = 0 + margin: auto,不需要子元素宽度

图文样式

下面代码,line-height 如何继承?

p 标签的行高是多少

<style>
body {
font-size: 20px;
line-height: 200%;
}
p {
font-size: 16px;
}
</style>

<body>
<p>AAA</p>
</body>
点击查看答案
40,body 的 font-size * line-height
写具体数值,如 30px,则继承该值。
写比例,如 2/15,则继承该比例,line-height 比例 * 子 font-size。
写百分比,如 200%,则继承计算出来的值,font-size 父 * line-height 父比例

响应式

rem 是什么?

点击查看答案
rem 是相对长度单位,相对于根元素。

rem 与 px、em 对比

点击查看答案
rem 是相对长度单位,相对于根元素,常用语响应式布局。
px 是绝对长度单位。
em 是相对长度单位,相当于父元素。

网页视口尺寸有哪些?

点击查看答案
window.innerHeight // 网页视口高度,显示内容的哪个高度
window.screen.height // 屏幕高度
document.body.clientHeight // body 高度

介绍一下 vw、vh

点击查看答案
vm 网页视口宽度的 1/100
vh 网页视口高度的1/100
vmax 取两者最大值;vmin 取两者最小值

如何实现响应式?

点击查看答案
可以使用 @media 来动态设置不同屏幕宽度下的根元素的 font-size,结合 rem。

CSS3

动画 – 不是重点

JS 面试题

值类型 与 引用类型

点击查看答案

typeof 能判断哪些类型?

点击查看答案
识别所有值类型、识别函数、判断是否为引用类型(不能细分)

何时使用 === 何时使用 ==?

点击查看答案
除了 == null 之外,其他的都用 ===
xxx == null 相当于 xxx === null || xxx === undefined

值引用和引用类型的区别

下面代码输出什么?

const obj1 = { x: 100, y: 200}
const obj2 = obj1
let x1 = obj1.x
obj2.x = 101
x1 = 102
console.log(obj1)
点击查看答案
101

手写深拷贝

原型

原型关系

点击查看答案
每个 class 都有显示原型 prototype
每个实例都有隐式原型__proto__
实例的__proto__指向对应 class 的 prototype

基于原型的执行规则

点击查看答案
获取属性或执行方法时,先找自身的属性和方法,没有则自动通过__proto__查找

如何准确判断一个变量是不是数组?

手写一个简单的 jQuery,考虑插件和扩展性

class 的原型本质,怎么理解?

闭包

作用域有哪些?

点击查看答案
全局作用域、函数作用域、块级作用域

this 的不同应用场景,如何取值?

点击查看答案
this 的取值是在函数执行时确定的,不是定义时确定的。

手写 bind 函数

实际开发闭包的应用场景,举例说明

点击查看答案
隐藏数据

点击时能否弹出对应序号?若不能,弹出的序号是什么?如何修改?

let i,a
for (i = 0; i < 10; i++) {
a = document.createElement('a')
a.innerHTML = i +'<br>'
a.addEventListener('click', (e) => {
e.preventDefault()
alert(i)
})
document.body.appendChild(a)
}
点击查看答案
10
在for 循环中定义i,不在外部,这样每次循环都会创建一个块作用域。

下面两道代码输出什么?

// 函数最为返回值
function create() {
const a = 100
return function () {
console.log(a)
}
}
const fn = create()
const a = 200
fn()
点击查看答案
100
闭包中所有的自由变量的查找,是在函数定义的地方向上层作用域查找,而不是在执行的地方。
// 函数作为参数
function print(fn) {
const a = 200
fn()
}
const a = 100
function fn() {
console.log(a)
}
print(fn)
点击查看答案
100
闭包中所有的自由变量的查找,是在函数定义的地方向上层作用域查找,而不是在执行的地方。

异步

同步和异步的区别是什么?

手写用 Promise 加载一张图片

前端

event loop

请描述 event loop(事件循环/事件轮询)的机制,可画图。

点击查看答案
JS 是单线程,异步需要基于回调来实现。而 event loop 就是异步回调的实现原理。
每次 Call Stack 清空(每次轮询结束),即同步任务执行完;都是 DOM 重新渲染的机会,DOM 结构如果有改变则重新渲染;然后再去触发下一次 Event Loop。
event loop执行顺序:Call Stack 清空、执行当前的微任务、尝试 DOM 渲染、触发 Event Loop。

promise

promise 有哪三种状态?如何变化?

点击查看答案
pending 进行中,fulfilled 已完成,rejected 已拒绝
pending -> fulfilled 或 pending -> rejected,变化不可逆。
then 正常返回 fulfilled,里面有报错返回 rejected。
catch 正常返回 fulfiled,里面有报错则返回 rejected

下面三道代码输出结果是什么?

题目一
Promise.resolve().then(() => {
console.log(1)
}).catch(() => {
console.log(2)
}).then(() => {
console.log(3)
})
点击查看答案
1、3
then 中未报错,不运行后面的 catch,而运行后面的 then。
题目二
Promise.resolve().then(() => {
console.log(1)
throw new Error('error2')
}).catch(() => {
console.log(2)
}).then(() => {
console.log(3)
})
点击查看答案
1、2、3
then 中是报错,运行第一个 catch,catch 中未报错,结果是 resolved,运行后面的 then。
题目三
Promise.resolve().then(() => {
console.log(1)
throw new Error('error1')
}).catch(() => {
console.log(2)
}).catch(() => {
console.log(3)
})
点击查看答案
1、2
then 中是报错,运行第一个 catch,catch 中未报错,而后面跟着的是 catch,不运行。

手写 Promise

async/await

async/await 是什么?

点击查看答案
它们是一个语法糖,归根到底还是需要进行 event loop,只是写法是同步的。它们是消灭异步回调的终极武器。
执行 async 函数,返回的是 Promise 对象。
await 相当于 Promise 的 then。
try...catch 可以捕获异常,代替了 Promise 的 catch。
async/await 是微任务
注意:await 处理成功的结果,try...catch 处理错误的结果。当 await 的结果为 rejected 时,后面代码就不会走了,需要 try...catch 捕获异常。

下面三道代码输出什么?

题目一
async function async1 () {
console.log('async1 start')
await async2()
console.log('async1 end')
}
async function async2() {
console.log('async2')
}
console.log('script start')
async1()
console.log('script end')
点击查看答案
        script start
        async1 start
        async2
        script end
        async1 end
     
题目二
async function fn() {
return 100
}
(async function () {
const a = fn()
const b = await fn()
})()
点击查看答案
Promise 对象,值为100、100
题目三
(async function () {
console.log('start')
const a = await 100
console.log('a', a)
const b = await Promise.resolve(200)
console.log('b', b)
const c = await Promise.reject(300)
console.log('c', c)
console.log('end')
})()
点击查看答案
start、'a' 100、'b' 200
c 时 reject 导致程序出现异常,程序中断,后面的代码不执行。

宏任务/微任务

什么是宏任务和微任务,两者有什么区别?

点击查看答案
宏任务在 DOM 渲染后触发,比如 setTimeout;微任务在 DOM 渲染前触发,比如 Promise。
宏任务:setTimeout、setInterval、Ajax、DOM 事件
微任务:Promise、async/await

为什么微任务在宏任务之前执行?

点击查看答案
因为每次 Call Stack 清空(每次轮询结束),即同步任务执行完;都是 DOM 重新渲染的机会,DOM 结构如果有改变则重新渲染;然后再去触发下一次 Event Loop。
而宏任务在 DOM 渲染后触发,比如 setTimeout;微任务在 DOM 渲染前触发,比如 Promise。

promise 和 setTimeout 的顺序

console.log(100)
setTimeout(() => {
console.log(200)
})
Promise.resolve().then(() =>{
console.log(300)
})
console.log(400)
点击查看答案
100、400、300、200

async、await、setTimeout的顺序

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

console.log('script start')

setTimeout(function() {
console.log('setTimeout')
}, 0)

async1()

new Promise(function (resolve) {
console.log('promise1')
resolve()
}).then(function() {
console.log('promise2')
})

console.log('script end')
点击查看答案
        script start
        async1 start
        async2
        promise1
        script end
        async1 end
        promise2
        setTimeout
     

DOM 面试题

DOM 是哪种数据结构

点击查看答案
树(DOM 树)

DOM 操作的常用 API

点击查看答案
有 DOM 节点操作和结构操作,还有 attr 和 property 的操作。getElementById、getElementsByTagName、getElementsByClassName、querySelectorAll、querySelector、getAttribute、createElement、appendChild、removeChild

attr 和 property 的区别

点击查看答案
property 修改对象属性,不会体现到 html 结构中;attribute 修改 html 属性,会改变html 结构。两者都有可能引起 DOM 重新渲染。

一次性插入多次 DOM 节点如何考虑性能

点击查看答案
先使用 document.createDocumentFrament() 创建一个文档片段,等都遍历插入到文档碎片中后,最后将文档碎片插入到 DOM 中。

BOM 面试题

如何识别浏览器的类型

分析拆解 url 各个部分

事件

编写一个通用的事件监听函数

事件代理

代码简洁、减少浏览器内存占用,但不要滥用。

描述事件冒泡的流程

无限下拉的图片列表,如何监听每个图片的点击?

git

git 的常用命令

点击查看答案
        git diff -- 查看修改了的文件信息
        git diff 文件名 -- 查看某个文件的修改信息
        git config -- 设置提交的信息
        git config use.name xxx -- 设置提交的用户名
        git config use.email xxx -- 设置提交的邮箱
        git log -- 查看提交日志
        git show + git log 查询出来日志索引 -- 查看提交详细内容
        git checkout 文件名 -- 撤销该文件修改
        git checkout . -- 撤销所有文件的修改
        git checkout -b 分支名 -- 切换分支
        git branch -- 查看分支
        get fetch -- 拉取远程的所有分支
    

浏览器

谈谈浏览器

浏览器是多进程架构,采用多进程架构可以解决多个线程之间可能存在的恶意修改或获取非授权数据等复杂的安全问题。

单进程浏览器

  1. 不稳定。单进程中的插件、渲染线程崩溃会导致整个浏览器崩溃。
  2. 不流畅。脚本(死循环)或插件会使浏览器卡顿。
  3. 不安全。插件和脚本可以获取到操作系统的任何资源。

多进程浏览器

  1. 解决不稳定。进程相互隔离,一个页面或插件崩溃时,影响仅仅当前插件或页面,不会影响到其他页面。
  2. 解决不流畅。脚本阻塞当前页面渲染进程,不会影响到其他页面。
  3. 解决不安全。采用多进程架构,使用沙箱。

沙箱(安全沙箱)

将渲染进程和 OS 隔离开的这道墙就是安全沙箱。沙箱是利用操作系统的安全技术给进程上了一把锁。沙箱的程序是可以运行的,但在渲染进程执行过程中是无法访问或修改 OS 中的数据的;如果需要在渲染进程中访问系统资源,那么需要通过浏览器内核来实现,将访问结果通过 IPC 转发给渲染进程。

IPC 是什么?

为什么单进程无法被沙箱保护?

因为最小的保护单位是进程。但单进程浏览器需要频繁的访问或修改 OS 的数据,所以单进程浏览器是无法被安全沙箱保护的。

浏览器的主要进程有哪些?

浏览器是多进程,主要分为:

知识深度

知识深度 1-2 个就行了。深度、广度不能兼得!!!

如何检测 JS 内存泄露?JS 内存泄露场景有哪些?(JS 垃圾回收是什么算法?)

什么是垃圾回收?

正常情况下,一个函数执行完,里面的变量都会被 JS 垃圾回收。

function fn() {
const a = 'aaa' // 执行完,a 销毁
console.log(a)

const obj = {
x: 100
} // 执行完,obj 销毁
console.log(obj)
}
fn()

但某些情况下,变量不能被回收,因为可能被再次使用。比如变量在函数中挂载到了 window 上或闭包。

function fn() {
const obj = {
x: 100
}
window.obj = obj // 全局变量,obj 销毁不了
}
fn()
function genDataFns() {
const data = {} // 闭包,data 销毁不了
return {
get(key) {
return data[key]
},
set(key, val) {
data[key] = val
}
}
}
const { get, set } = genDataFns()

变量销毁不了,不一定是内存泄露。

Vue 每个生命周期都做了什么?

Vue2 Vue3 React 三者的 diff 算法有何区别?

冉星 Web Developer,rising star

个人简介:脆弱的种子在温室也会死亡,坚强的种子,在沙漠也能发芽。