前端自我知识梳理

107 阅读17分钟

一、HTML

1. script标签中defer和async属性的区别?

  • 共同点:它们都是去异步加载外部的js脚本文件,不会阻塞页面的解析
  • 区别点:
    • 执行顺序:async不能保证加载的顺序,defer按照加载顺序执行
    • 脚本是否并行执行:async属性表示后续文档的加载和执行与js脚本的加载和执行是并行进行的,即异步执行;defer属性,加载后续文档和js脚本的加载是并行的,但是js脚本要等到文档解析完毕之后才执行,发生在DOMContentLoaded执行之前。

2. 常用的meta标签有哪些

  • charset:文档的编码类型
  • keywords:关键词
  • description:页面描述
  • viewport:控制视口属性

二、CSS

1. display:none和visibility:hidden的区别

  • display会让元素从渲染树中消失,visibility不会让元素从渲染树中消失
  • display为非继承属性,visibility为继承属性,可以修改子孙节点的visibility让他们显示
  • 修改display会造成回流,而修改visibility只会造成重绘

2. 单行,多行文本溢出的隐藏?

单行文本溢出的隐藏

overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;

多行文本溢出的隐藏

overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-lin-clamp: 3;

3. 两栏布局的实现?

  • 利用flex布局,左边设置200px,右边设置flex:1;
  • 利用浮动,左边设置200px,左浮动,右边设置overflow:hidden,形成BFC
  • 利用浮动,左边200px,左浮动,右边margin-left:200px,宽度为auto
  • 利用绝对定位,父元素设置为相对定位,左边设置为absolute定位,且宽度为200px,右边设置为margin-left:200px
  • 利用绝对定位,父元素设置相对定位,左边元素设置为200px,右边元素设置为绝对定位,left:200px,其余方向为0

4. 三栏布局

  • 绝对定位,左右两栏设置固定宽度,中间设置为对应方向的margin值
  • 利用flex布局,左右两栏设置固定宽度,中间一栏设置为flex:1
  • 利用浮动,左右两栏设置固定宽度,并设置对应方向的浮动,中间一栏放在最后,且设置左右两个方向的margin值
  • 圣杯布局,利用浮动和负边距实现。父元素设置左右的padding,三列均设置为向左浮动,中间一列放最前面,设置宽度为100%,把后面两列挤到下一行,然后通过设置margin的负值来将它们移到上一行,再设置相对定位,移到两边。

5. 水平垂直居中

  • 利用绝对定位,设置四个方向的定位为0,然后设置宽度为auto
  • 利用绝对定位,设置top:50%;left:50%;然后利用margin-left,margin-top
  • 利用绝对定位,设置top,left,最后利用transform:translate(-50%,-50%)
  • 利用flex布局,align-items:center;justify-content:center

6. 为什么需要清除浮动,清除浮动的方式?

  • 由于子元素浮动,脱离文档流,而容器没有设置固定的高度,其高度不能被内容撑开,此时内容会溢出到容器之外影响布局。
  • 父级元素定义height
  • 最后一个浮动元素之后添加div空标签,设置clear:both
  • 使用:after伪元素
  • 给父元素添加overflow:hidden或auto

7. 如何创建BFC?

  • body元素
  • 父元素设置浮动:float不为none就行
  • 元素设置绝对定位:absolute,fixed
  • overflow: hidden,auto,scroll
  • display: table-cell, inline-block, flex

三、javaScript

1. new操作符的实现原理

  • 创建一个新的对象
  • 设置原型,将对象的原型设置为函数的prototype
  • 让函数的this指向这个对象,并且执行构造函数
  • 判断函数的返回值类型,如果是值类型,则返回创建的对象。如果是引用类型,则返回这个引用类型的对象。

2. 对ajax的理解,实现一个ajax请求?

  • ajax是通过js的异步通信,从服务器获取xml文档的数据,从而实现刷新部分页面而不用刷新整个网页
  • 实现步骤:(1)新建一个XMLHttpRequest对象(2)使用open方法新建一个http请求(3)设置头部信息(4)监听函数(5)onreadystatechange,readyState为4,请求处理完成,根据状态码status(6)sent发起请求

3. ES6与CommonJS有什么异同

  • 相同点:都可以对引入的对象进行赋值
  • 不同点:CommonJS是对模块的浅拷贝,ES6是对模块的引用。即es6不能改变指针指向。但是CommonJS可以重新赋值。

4. for of 和 for ... in的区别

  • for...of遍历获取对象的键值,for...in遍历获取对象的键名
  • for...in会遍历对象的整个原型链,for...of只遍历当前对象
  • 对于数组的遍历,for...in遍历所有可枚举的属性,包括原型链上的可枚举属性;for...of只会遍历数组的下标对应的属性值

5. 对原型,原型链的理解

在js中都是通过构造函数来新建一个对象,每个构造函数内部都有一个prototype属性,它的值是一个对象,包含了属性和方法。对象内部指向构造函数的prototype的指针也就是对象的原型。如果这个对象没有属性,就会去它的原型对象里去找这个属性,原型对象又会有自己的原型,一直找下去也就是原型链的概念。

6. 异步编程的实现方式

  1. 回调
  2. promise
  3. generator
  4. async/await

7. async/await对比promise的优势

  • 代码读起来更加同步,Promise虽然摆脱了回调地狱,但是then的链式调用也会带来额外的阅读负担
  • Promise传递中间值比较麻烦,而async/await几乎是同步的写法
  • 错误处理更加友好,async/await可以通过try catch方式捕获错误,而Promise的错误捕获比较冗余
  • 调试更加友好,Promise不方便设置断点

四、计算机网络

1. OPTIONS请求方法及使用场景

  • 它是在客户端资源请求前的预检请求
  • 主要用途:
    • 获取服务器支持的所有HTTP请求方法
    • 用来检查访问权限。尤其是CORS时候,options用来判断是否有对资源访问的权限

2. HTTP1.0和HTTP1.1的区别?

  • 连接方面:http1.0默认使用非持久连接,而http1.1默认使用持久连接来使多个http服用一个TCP,避免每次都要重新建立的时延
  • 资源请求方面:http1.0会传输整个对象,而http1.1在请求头引入了range字段,允许请求某个部分,而且支持断点续传
  • 缓存方面:http1.0主要使用expires作为缓存判断,而http1.1新增了etag等缓存策略
  • http1.1新增了host字段,用来指定服务器域名
  • http1.1支持了更多的请求方法,比如put,head,options

3. TCP与UDP的区别?

  • UDP面向无连接,TCP面向连接
  • UDP是不可靠传输,不使用流量控制和拥塞控制;TCP是可靠传输,使用流量控制和拥塞控制
  • UDP支持单播,多播和广播;TCP支持一对一通信
  • UDP面向报文,TCP面向字节流
  • UDP首部开销小,仅8字节;TCP首部开销大,至少20字节
  • UDP适用于实时场景,TCP适用于传输文件

五、性能优化

1. 回流与重绘的概念及触发条件

  1. 回流

    • 当渲染树中的部分或全部元素的尺寸,结构等发生变化时,浏览器会重新渲染部分或者全部文档的过程就称为回流。
    • 导致发生回流的操作:页面首次渲染,浏览器的窗口大小发生变化,元素的内容发生变化,元素的尺寸或者位置发生变化,添加可见的DOM元素
  2. 重绘

    • 当页面中某些元素的样式发生变化,但是不会影响其在文档流中的位置时,浏览器就会对元素进行重新绘制,这个过程就是重绘。
    • 导致发生重绘的操作:color,background等属性变化,border-radius等属性,visibility属性

2. 如何避免回流与重绘?

  • 操作DOM时,尽量在低层级的节点进行操作
  • 不要使用table布局
  • 不要频繁操作元素的样式
  • 避免频繁操作DOM,可以创建一个documentFragment
  • 事件代理
  • 可以先设置display:none,操作结束后再把它显示出来

3. 对节流与防抖的理解

  • 防抖是指在2事件被触发n秒后再执行回调,如果在这n秒之内事件又被触发,则重新计时。通常用在用户的按钮点击上
  • 节流是指在一定时间内,只有一次触发事件的回调函数执行,通常用在scroll上。

防抖函数的使用场景:

  • 按钮提交,防止多次提交,只会执行最后一次的提交
  • 服务端验证场景:input输入搜索功能

节流函数的使用场景:

  • 拖拽场景:固定时间内只执行一次,防止超高频次触发位置变动
  • 缩放场景:监控浏览器的resize
  • scrol滚动

4. 防抖和节流的实现

函数防抖的实现

function debounce(fn, wait) {
    let timer = null;
    return function () {
        let context = this, args = [...arguments];
        if (timer) {
            clearTimeout(timer);
            timer = null;
        }
        timer = setTimeout(() => {
            fn.apply(context, args);
        }, wait)
    }
}

函数节流的实现

function throttle(fn, wait) {
    let preTime = Date.now();
    
    return function () {
        let context = this, args = [...arguments];
        let nowTime = Date.now();
        
        if (nowTime - preTime >= wait) {
            preTime = Date.now();
            return fn.apply(context, args);
        }
    }
}

5. 如何用webpack优化前端性能

  • 压缩代码:可以使用uglifyjsplugin压缩js文件,css-loader的minimize来压缩css代码
  • 利用CDN:将静态资源放在CDN上
  • Tree Shaking:将代码中不会用到的片段删除掉。
  • Code Spliting:将代码按照组件分块,可以做到按需加载
  • 提取公共的第三方库:利用splitchunksplugin插件可以进行公共模块的抽取,利用浏览器缓存可以长期缓存公共代码

六、Vue

1. Vue的基本原理

当一个Vue实例创建的时候,Vue会遍历data中的属性,用Object.defineProperty(vue3使用Proxy)劫持它们的setter和getter,并且在内部追踪相关依赖,在属性被访问和修改时通知变化。每个组件实例都有对应的wtcher实例,它会在组件渲染过程中把属性记录为依赖,之后当依赖项的setter被调用时,会通知watcher重新计算,从而致使相关组件得到更新。

2. 双向数据绑定的原理

Vue.js 是采用数据劫持结合发布者-订阅者模式的方式,通过Object.defineProperty()来劫持各个属性的setter,getter,在数据变动时发布消息给订阅者,触发相应的监听回调。主要分为以下几个步骤:

  • 对需要observe的数据对象进行递归遍历,都加上setter和getter,就能监听数据变化
  • compile解析模板指令,将模板中的变量替换成数据,然后初始化页面视图,并将每个指令对应的节点绑定更新函数,添加监听数据的订阅者,一旦数据变动,收到通知后会更新视图
  • watcher订阅者是observe和compile之间的桥梁,主要做的事情有:(1)在自身实例化时往dep中添加自己(2)自身有一个update更新函数(3)属性变动的时候dep.notice(),调用自身的update方法,触发compile中绑定的回调。

3. nextTick的原理及作用

  • 原理:基于js中的eventloop实现的,核心是利用了Promise,MutationObserver,setImmediate,setTimeout之类的方法来模拟微任务和宏任务,实现Vue内部的异步回调队列
  • Vue引入该机制的原因:(1)如果是同步更新,多次对一个或多个属性赋值,会频繁触发UI的渲染,异步可以减少无用的渲染(2)vue内部使用虚拟dom,每次更新状态都需要进行diff算法,而这种无用功也将浪费更多的性能,所以异步渲染变得更加至关重要
  • 作用:(1)created中进行dom操作,需要用到nextTick(2)数据变化后,需要同步更新dom时候,操作方法可以放在nextTick中进行

4. 对React和Vue的理解,它们的一同

相似之处:

  • 都有丰富的社区库
  • 都有自己的构建工具
  • 都使用了Vitrual DOM
  • 都有props的概念,允许组件间的数据传递
  • 都有组件化思想,将应用拆分成一个个功能明确的模块

不同之处:

  1. Vue是双向数据绑定,React支持单向数据流

  2. 虚拟DOM:

    • Vue可以更快计算出Virtual DOM的差异,主要是因为它在实例化时,已经对数据进行了依赖收集,会跟踪每个组件的依赖关系,不必重新渲染整个组件树
    • React,每次状态改变时,都会刷新整个组件树。当然也可以通过PureComponent/shouldComponentUpdate进行控制,优化渲染
  3. 组件化

    • Vue支持特定的template模板的编写
    • React推荐jsx,更加灵活
  4. 监听数据变化的实现原理不同

  5. 构建工具:Vue-Vue-cli React-Create React app

  6. 高阶组件

5. Vue3.0有什么更新

  1. 监测机制的改变:基于Proxy的observer实现,数据更全面覆盖,解决了Vue2中的很多限制
  2. 支持监听属性的添加和删除;支持监听数组的小标和长度的变化;支持map,set,weakmap,weakset数据结构的监听
  3. 模板:写法上的区别,composition api 相对于options api更加结构清晰,复用性增强
  4. 基于Tree-shaking优化
  5. 支持fragment

七、React

1. React事件机制

  • React并不是将事件直接绑定在真实的dom上,而是在document处监听了所有的事件,当事件发生并且冒泡到document处的时候,React将事件内容封装并交给真正的处理函数执行。
  • React的合成事件,阻止冒泡的方式是event.preventDefault方法

实现合成事件的目的:

  • 合成事件可以解决浏览器之间的兼容问题
  • 原生浏览器事件,每个监听对象都会有一个处理函数,会造成高额的内存分配

2. React事件与普通的html事件的区别

区别:

  • 对于事件名称命名方式,原生事件为全小写,react事件采用小驼峰
  • react事件不能通过return false方法来阻止浏览器的默认行为,需调用preventDefault来阻止默认行为

优点:

  • 兼容所有浏览器,更好的跨平台
  • 将事件统一存放在一个数组,避免频繁的新增与删除
  • 方便react统一管理和事务机制

3. React Fiber的理解,它解决了什么问题?

在React15,渲染时,会递归对比虚拟dom树,找出需要变动的节点,然后同步更新它们。这样一旦需要大量的节点,会导致用户触发的事件得不到响应,导致用户感觉卡顿。 通过React Fiber架构,让这个执行过程变成可中断,让出CPU执行权,让位给高优先级的任务,等浏览器空闲后再恢复渲染。requestAnimation

4. 类组件与函数组件有什么异同?

相同点:都可以复用,类组件可以改写成函数组件,函数组件也可以改写成类组件 不同点:

  • 开发时的心智模型不同,类组件是面向对象编程,有继承,生命周期等核心概念,而函数组件是函数式编程
  • 以前,需要使用生命周期的组件主推类组件,而现在组合优于继承,函数式组件也可以有生命周期的模拟,更加推崇函数组件
  • 性能优化上,类组件依靠shouldComponentUpdate/PureComponent来阻止渲染提升性能,而函数组件依靠memo,callback来缓存结果提升性能
  • 函数组件提供了更加细粒度的复用,更适应React的发展

5. React setState调用之后发生了什么? 是同步还是异步?

  1. React中setState后发生了什么 (1)调用setState之后,React会将传入的参数与组件当前的状态进行合并,然后会以高效的方式根据新的状态构建DOM树 (2)React得到新的DOM树会与旧的DOM进行diff比较,得到差异的节点,实现最小化重新渲染 (3)如果在短时间内频繁触发setState,React会将state的改变放入队列中,在合适地时机进行批量更新state和视图。

  2. setState是同步还是异步?

  • 在React可以控制的地方,是异步,比如React的生命周期事件和合成事件中,都会走合并操作,延迟更新的策略
  • 在React无法控制的地方,比如原生事件,也就是setTimeout,setInterval等事件中,就只能同步更新

6. React的工作流程

以store为核心,它可以看作一个数据存储中心,但是不能直接改变,修改数据需要通过action,组件中通过主动发送action,然后dispatch这个action方法,通过reducers去更新数据,从而重新渲染页面

7. redux中的异步请求怎么处理

  1. redux-thunk
  2. redux-saga

8. React的diff算法

React没有采用传统的遍历dom的方式,而是采用了分治的方式,将单一节点对比转为3种类型的节点的对比,分别是基于树的,基于组件的,基于元素节点的,以此来提升效率

  • 树对比:对两棵虚拟dom树进行同层级的比较
  • 组件对比:如果是同一组件类型,则进行树的对比;否则,直接放入到补丁中
  • 元素对比:同一层级的子节点,通过key进行对比

自从react16引入了fiber之后,整个更新过程可以随时暂停恢复,节点和树就分别采用了FiberNode和FiberTree进行重构。FiberNode采用了双链表的结构,可以直接找到兄弟节点和子节点。

八、手写代码

1. 手写instanceof

function myInstance(left, right) {
    let proto = Object.getPrototypeOf(left),
        prototype = right.prototype;
    while (true) {
        if (!proto) return false;
        if (proto === prototype) return true;
        proto = Object.getPrototypeOf(proto);
    }
}

2. 手写new操作符

function objectFactory() {

}

九、Node相关知识

1. Node中的EventLoop和浏览器中的EventLoop区别?

image.png

事件循环原理

  1. node初始化

    • 初始化node环境
    • 执行输入代码
    • 执行process.nextTick回调
    • 执行microtasks
  2. 进入EventLoop

    • timers阶段
      • 检查timer队列是否有到期的timer(setTimeout/setInterval)回调,有的话将到期的回调按照timerId升序执行
    • IO callback阶段
      • 检查是否有等待的IO回调,有的话执行
    • idle,prepare阶段
      • nodejs内部调用
    • poll阶段
      • 检查是否存在尚未完成的回调,如果存在,分为两种情况:
        • 如果队列不为空,执行可用回调
        • 如果队列为空,检查是否有setImmediate回调,如果有,则退出poll阶段,进入check阶段;如果没有,超时之前node阻塞在这,等待新的事件通知
      • 如果不存在尚未完成的回调,直接退出poll阶段
    • check阶段
      • 如果有setImmediate回调,执行回调
    • closing callback阶段
  3. 检查是否有活跃的handlers(定时器,IO事件句柄)

    • 如果有,继续下一轮循环
    • 没有,就退出事件循环,退出程序

2. 架构

对比下浏览器与Node的架构,如下图: image.png

Chrome的中间层能力是有限的,而Node中,我们可以随意的操控文件,甚至搭建各种服务,虽然Node不处理UI层,但是与浏览器以相同的机制和原理运行,并且在中间层这里有着自己强大的功能

所以,简单直观的来讲 Node 就是脱离了浏览器的,但仍然基于 Chrome V8 引擎的一个 JavaScript 的运行环境。

轻量、高效、事件驱动、非阻塞 I/O是Node几个很重要的特性

基础架构可以大致分为下面三层:

image.png