欢迎您访问 最编程 本站为您分享编程语言代码,编程技术文章!
您现在的位置是: 首页

前端八股文总结

最编程 2024-02-13 22:37:52
...

CSS

display: none; 和 opacity: 0; visibility: hidden;都可以使元素隐藏,有什么区别?

(1) display: none; 使元素隐藏不会占据原来的空间,元素本身的事件无法触发

(2) visibility: hidden; 使元素隐藏时会占据原来的空间,但是元素本身的事件无法触发

(3) opacity: 0; 使元素隐藏时会占据原来的空间,元素本身的事件可以触发

CSS 有哪些选择器?

id 选择器,类选择器,伪类选择器,伪元素选择器,标签选择器,通配符选择器,属性选择器,子代选择器,后代选择器

权重:

(1)第一优先级:!important

(2)第一等:内联样式 权值(1000)

(3)第二等: ID选择器 权值(0100)

(4)第三等: 类选择器 权值(0010)

(5)第四等: 标签选择器、伪元素选择器 权值(0001)

(6)通配符,子选择器

怪异盒模型

box-sizing: border-box; 设置之后设置的宽高包括content,padding,border

box-sizing: content-box; 设置之后的设置的宽高为content的宽高,当添加padding和border之后,容器的总宽高会增加

隐藏元素的5种方法

(1) display:none

(2) visibility:hidden

(3)opciaty: 0

(4)overflow:hidden

(5)position:absolute; left:-9999px; top:-9999px; // 使元素移出屏幕

CSS中有哪些属性是可以被继承,哪些属性不能被继承?

有继承性的属性:

1、字体系列属性: font、font-family、font-weight、font-size、font-style、font-variant、font-stretch、font-size-adjust

2、文本系列属性: text-indent、text-align、line-height、word-spacing、letter-spacing、text-transform、direction、color

3、元素可见性: visibility

4、表格布局属性: caption-side、border-collapse、border-spacing、empty-cells、table-layout

5、列表布局属性:list-style-type、list-style-image、list-style-position、list-style

6、生成内容属性:quotes

7、光标属性:cursor

8、页面样式属性: page、page-break-inside、windows、orphans

9、声音样式属性: speak、speak-punctuation、speak-numeral、speak-header、speech-rate、volume、voice-family、pitch、pitch-range、stress、richness、、azimuth、elevatio

无继承性的属性:

1、display

2、文本属性: ​ vertical-align、text-decoration、text-shadow、white-space、unicode-bidi

3、盒子模型的属性: width、height、margin 、margin-top、margin-right、margin-bottom、margin-left、border、border-style、border-top-style、border-right-style、border-bottom-style、border-left-style、border-width、border-top-width、border-right-right、border-bottom-width、border-left-width、border-color、border-top-color、border-right-color、border-bottom-color、border-left-color、border-top、border-right、border-bottom、border-left、padding、padding-top、padding-right、padding-bottom、padding-left

4、背景属性: background、background-color、background-image、background-repeat、background-position、background-attachment

5、定位属性: float、clear、position、top、right、bottom、left、min-width、min-height、max-width、max-height、overflow、clip、z-index

6、生成内容属性: content、counter-reset、counter-increment

7、轮廓样式属性: outline-style、outline-width、outline-color、outline

8、页面样式属性: size、page-break-before、page-break-after

9、声音样式属性: pause-before、pause-after、pause、cue-before、cue-after、cue、play-during

什么是BFC

BFC的概念:BFC就是块级格式化上下文(block formatting context),它是一个独立的块级渲染区域,该区域的样式不会受到外部样式的影响,内部的样式也不会影响到外部的样式。

如何创建BFC:

1.display: inline-block;(flex, table-ceil等等)

2.overflow:hidden;

3.position:absolute;(fixed)

4.float 的值不为 none

BFC的作用:

1.清除元素受到浮动的影响

2.防止父容器内部元素浮动后父容器发生高度塌陷

3.防止外边距塌陷

flex:1 是什么的缩写

flex 是 flex-grow, flex-shrink, flex-basis 的缩写,默认值是 0, 1, auto。

flex: 1 是将 1 赋值给 flex-grow,也就是 flex: 1 相当于flex-grow: 1; flex-shrink: 1; flex-basis: auto;

flex-grow 默认为0, 只能是正整数。即父元素有剩余空间也不放大元素。如果为 1,则把剩余空间的一份加给自己的宽度。

flex-shrink 默认为1,只能是正整数。即父元素空间不足则按比例收缩。如果为 0,则不收缩

flex-basis 默认为 auto, 即元素本身的大小。这个属性定义了在分配多余空间之前,元素占据的主轴空间,浏览器根据这个属性计算是否有多余空间。可以设置为和 width 和 height 属性一样的值,比如 220px,则元素占据固定空间

重排和重绘

重排/回流:当DOM的变化影响了元素的几何信息,浏览器需要重新计算元素的几何属性,将其放在界面中的正确位置,这个过程叫做重排。表现为重新生成布局,重新排列元素。

重绘:当一个元素的外观发生改变,但没有改变布局,重新把元素外观绘制出来的过程,叫做重绘,表现为某些元素的外观被改变。

单单改变元素的外观,肯定不会引起页面的重新布局;但当浏览器完成重排之后,将会重新绘制受到此次重排影响的部分,也就是:重绘不一定会引起重排,重排必然会引起重绘。

JS

JS中的基本数据类型和引用数据类型

基本数据类型:Number String Boolean Null Undefined (ES6还有Symbol、BigInt)

引用数据类型:Object

基本数据类型和引用数据类型的区别

基本数据类型存储在栈空间中,当进行赋值的是将值复制一份给新的地址,两个变量之间没有关联,当其中一个值改变时另一个的值不会改变

引用数据类型的内容存储在堆空间中,在栈中存放的是对象在堆空间中的地址,当进行赋值时是将地址赋值给另一个新的变量,当一个值改变时,另一个也会改变

判断变量数据类型有几种方法?

(1) typeof ( typeof a)

(2) instanceOf ( a instanceOf B)

(3) Object.prototype.toString.call ( Object.prototype.toString.call(a) )

(4) 变量.constructor ( a.constructor )

var、let、const 的区别

(1) var 没有局部作用域,并且会变量提升,可以被修改,可以暂时不赋初始值

(2) let 有局部作用域,没有变量提升,定义的变量可以被修改,可以暂时不赋初始值

(3) const 有局部作用域,没有变量提升,定义的变量不可以被修改,必须赋初始值

call, apply, bind 的区别

(1) call 方法的第一个参数是函数调用时 this 指向的对象,后面的参数是函数执行时所需要的参数,例子:fn.call(obj, a, b, c)

(2) apply 方法的第一个参数是函数调用时 this 指向的对象,第二个参数是函数执行时所需要的参数数组,例子:fn.apply(obj, [a, b, c])

(3) bind 方法传递的参数与 call 一样,但是 bind 方法会返回一个函数,需要自己手动去调用该函数,例子:let result = fn.bind(obj, a, b, c) result() // 作用与 fn.call(obj, a, b, c) 一致

箭头函数和普通函数的区别

(1)普通函数能当作构造函数,箭头函数不能当作构造函数

(2)普通函数有自己的 this 指向,并可以通过 call、apply、bind 修改 this 的指向,箭头函数没有自己的 this,this 指向定义该函数时的作用域

(3)箭头函数没有 arguments 参数,普通函数有 arguments 参数

(4)箭头函数没有 prototype 和 __ proto__,普通函数有 prototype 和 __ proto __

new 过程中进行了什么操作

(1)创建一个空对象

(2)让空对象的原型对象指向构造函数的原型对象

(3)通过 call 或 apply 调用该函数,使其 this 指向新创建的对象

(4)若函数调用后的返回值为对象,则 return 该对象;若返回值不为对象,则 return 创建的对象

闭包

1、什么是闭包?

网上关于闭包的定义,可以是一个函数,因为闭包的形成是外部函数定义了一个变量,同时这个变量被内部函数引用;也可以是一个对象,在谷歌浏览器中闭包就是一个存储着被引用数据的一个对象。

2、闭包的形成

外部函数定义的变量被内部函数引用,就会形成闭包

3、闭包的作用:

延长变量的生命周期:当函数调用结束后,内存也会被清空,对应的局部变量也会被删除,但如果变量被内部函数引用,清空内存时该变量由于存在引用关系不会被清除。

避免变量污染环境:局部定义的变量只会在局部生效,不会影响其他变量的命名。

给函数外部提供操作函数内部变量的机会:外部函数内部 return 一个内部函数,内部函数对外部函数定义的变量进行操作,当调用外部函数就会返回一个内部函数,调用内部函数就可以对函数内部的变量进行操作。

function add() {
    var count = 0
    return function() {
        count ++
    }
}
const Add = add()
Add() // 通过调用这个函数可以实现对函数内部 count 的操作

4、闭包的缺点:由于闭包的使用导致被引用的变量无法清除,当过多使用闭包时会导致内存的浪费以及内存的泄漏。

5、解决方案:1.尽量避开不使用闭包,使用其他技术代替相同的功能。2.当闭包不需要使用时手动清除闭包,使内部函数为 null,外部变量没有被内部函数引用,就会被回收机制清除。

事件流

给一个 DOM 元素绑定事件,当事件触发时,会有一个事件流的过程:

(1)事件捕获阶段:在此阶段会从触发事件的节点的祖先节点向触发事件节点(从外往内)依次触发在捕获阶段绑定的事件(可以通过 addEventListener() 的第三个参数为 true 来在捕获阶段绑定事件)

(2)触发该节点的事件

(3)事件冒泡阶段:在此阶段事件会从内往外依次触发相同事件,正常绑定事件都是在冒泡阶段绑定的

script 的 defer 和 async

(1)当页面代码解析遇到 script 标签时,会打断页面渲染去加载执行 js 代码

(2)当给 script 标签加上 async 时,当遇到 script标签,不会打断页面渲染,会异步加载 js 代码,等 js 代码加载完毕之后才会打断页面渲染去执行 js 代码

(3)当给 script 标签加上 defer 时,当当遇到 script标签,不会打断页面渲染,会异步加载 js 代码,js 代码加载完毕之后不会打断页面渲染,会等页面渲染完毕之后再去执行 js 代码

JS哪些操作可能造成内存泄漏

(1)意外的全局变量引起的内存泄露

function leak(){
  leak="xxx";//leak成为一个全局变量,不会被回收
}

(2)闭包引起的内存泄露

function bibao(){
  var a = 1
  return function() {
      return a + 1 // 内部函数引用了外部函数定义的变量,函数执行完变量不会被回收
  }
}

(3)没有清理的DOM元素引用

var elements={
    button: document.getElementById("button"),
    image: document.getElementById("image"),
    text: document.getElementById("text")
};
function doStuff(){
    image.src="http://some.url/image";
    button.click():
    console.log(text.innerHTML)
}
function removeButton(){
    document.body.removeChild(document.getElementById('button'))
}

(4)被遗忘的定时器或者回调

var someResouce=getData();
setInterval(function(){
    var node=document.getElementById('Node');
    if(node){
        node.innerHTML=JSON.stringify(someResouce)
    }
},1000)

如何获取对象身上的属性

1.Object.keys() 返回值是包含可以枚举的属性数组

2.Object.getOwnPropertyNames() 跟 Object.keys() 一样

3.for...in 方法,每一项都是对象的属性,只能访问可以遍历的属性

4.Object.getOwnPropertySymbols() 返回值是对象中属性为 Symbol 类型的数组

5.Reflect.ownProperty() 返回值是所有属性的数组,不管是否可以遍历

什么是事件代理(事件委托) 有什么好处?

事件委托的原理:不给每个子节点单独设置事件监听器,而是设置在父节点上,然后利用冒泡原理设置每个子节点。

优点:减少内存消耗和DOM操作,提高性能。(不需要获取大量的DOM节点)

新增的节点也能触发同样的事件,不需要额外绑定。

VUE

v-show 和 v-if 的区别

v-show 是通过控制元素是否添加 display:none; 来控制元素是否显示或隐藏,而 v-if 则是控制元素是否存在。当某个元素需要频繁进行显示与隐藏时,使用 v-show 可以减少重复渲染元素的代价, v-if 适用于只进行一次显示或隐藏。

为什么使用 v-for 的时候要加 key,为什么 key 最好不要是数组的下标?

v-for 加 key 值可以方便 diff 算法进行最小量化更新,当数据修改后页面进行重新渲染,可以根据 key 值对内容进行比较,只重新渲染数据发生更新的元素。 key 值可以是数组的下标,但是对 v-for 使用的数组执行逆序操作等打乱顺序的操作时,页面可能会出现混乱,也会使 diff 算法效率降低

组件间的传参方式有哪些?

(1) props: 父组件给子组件传递参数,子组件通过 props 接收父组件传递过来的数据;子组件给父组件传递数据:父组件给子组件传递一个函数,在子组件使用 props 接收父组件给子组件传递一个函数,子组件使用 props 接收之后调用,可以通过函数参数的形式将数据传递给父组件

(2) 自定义事件:父组件给子组件绑定一个自定义事件,在子组件中通过 this.$emit 触发自定义事件,将数据以参数的形式传递给父组件

(3) 全局事件总线:在 main.js 创建 Vue 实例的 beforeCreate 生命周期钩子中给 Vue.prototype 上添加一个 this 实例,然后其他组件就可以给这个事件总线绑定自定义事件

(4)VueX:在 VueX 的 state 中定义数据,通过 this.$store.state 读取或者通过 mapState 语法糖读取

(5) provide, inject:在上级组件通过 provide( key, value )提供数据,在下级组件可以通过 inject( key ) 注入数据

data 为什么要写成一个函数?

当 data 为一个对象时,组件复用时不同位置的组件使用的是同一份数据,数据改变时会相互影响,不能实现真正意义上的组件复用;当 data 写成一个函数并 return 一个对象时,组件复用时数据都是独立的,不会相互影响,满足组件复用的条件

Vue项目性能优化的方法有哪些?

1.对第三方的依赖,在打包之前在 vue.config.js 文件中配置不打包第三方依赖,在 index.html 中引入线上CDN资源。

2.第三方组件库采用按需加载。

3.除了首次展示的页面组件之外,其他组件采用路由懒加载的方式,可以加快首页渲染的速度。

4.对于不需要改动的页面,可以采用组件进行缓存。

5.对于不需要响应式的数据(只用于展示的数据),可以使用 Object.freeze() 方法对数据进行冻结,Vue 在底层会通过 Object.isFrozen() 进行判断是否冻结,如果是冻结的数据,则不会递归遍历数据添加响应式,减少性能开销。(Vue2)

vue-router 中的导航钩子有哪些?

1、全局守卫: router.beforeEach 2、全局解析守卫: router.beforeResolve 3、全局后置钩子: router.afterEach 4、路由独享的守卫: beforeEnter 5、组件内的守卫: beforeRouteEnter、beforeRouteUpdate (2.2 新增)、beforeRouteLeave

父子组件之间的生命周期顺序

父组件 beforeCreate => 父组件 created => 父组件 beforeMount => 子组件 beofreCreate => 子组件 created => 子组件 beofreMount => 子组件 mounted => 父组件 mounted

当父组件给子组件通过 props 传递数据时,子组件在 created 生命周期钩子就可以接收到父组件传递过来的数据

什么是 nextTick,有什么作用?

nextTick是一个微任务。

  • nextTick中的回调是在下次Dom更新循环结束之后执行的延迟回调
  • 可以用于获取更新后的Dom
  • Vue中的数据更新是异步的,使用nextTick可以保证用户定义的逻辑在更新之后执行

为什么要使用虚拟 DOM

  • 虚拟dom就是用js对象来描述真实Dom,是对真实Dom的抽象
  • 由于直接操作Dom性能低,但是js层的操作效率高,可以将Dom操作转化成对象操作。最终通过diff算法比对差异进行更新Dom
  • 虚拟Dom不依赖真实平台环境,可以实现跨平台

浏览器

从输入 url 到浏览器页面显示经历了什么过程

1.发送请求给 DNS 域名服务器,进行域名解析,将域名解析为 IP 地址

2.查找 IP 缓存,查找顺序是:浏览器 => 操作系统 => 路由器 => ISP

3.查看浏览器缓存,如果是强缓存阶段,则不发送请求,直接从缓存中拿数据;如果是协商缓存,则会携带 if-Modified-since,if-NoneMatch 发送请求到对应 IP 地址的服务器。(发送请求之前需要先建立链接)

建立连接的过程:1、三次握手(SYN的作用是用于建立连接,ACK是确认信号,表示收到了上一个信号)

(1)首先由客户端发起 SYN 包,请求建立连接

(2)服务器收到 SYN 包之后会回复一个 SYN 和 ACK 包,表示收到上一个请求并同意建立连接

(3)浏览器收到之后会回复一个 ACK 确认包,连接建立完成,双方开始传输数据,传输完数据之后断开连接。

断开连接过程:2、四次挥手(FIN的作用是用于断开连接)

(1)首先由客户端发起 FIN 包,请求断开连接

(2)服务器收到 FIN 包之后,会回复一个 ACK 确认包

(3)服务器发起 FIN 包给客户端,表示同意断开连接

(4)客户端收到 FIN 包之后会回复一个 ACK 确认包,连接断开

4.如果是 http 请求,建立连接之后就会进行数据传输;如果是 https,则会先进行加密,加密完成之后再进行数据传输。

5.开始页面渲染:首先根据 HTML 结构渲染出一棵 DOM 树,再根据 CSS 渲染出一棵 CSSOM 树,然后将这两棵树进行合并成渲染树,再根据渲染树生成页面。

构建过程:首先对 HTML 标签进行解析,将开始标签压入一个栈中,当解析到结束标签时,就会弹出栈顶元素,将该元素以及内容挂载到 DOM 树上;解析 HTML 页面构建 DOM 树相当于树的深度优先遍历,会先把一个节点的所有子节点都解析完,再去解析兄弟节点。解析完 DOM 树就会去解析 CSS 文件生成 CSSOM 树,将两棵树进行合并,渲染树会将一些不需要渲染的节点去掉,比如像 header 标签以及 display:none 的节点等等。合并成渲染树之后,根据渲染树渲染页面,渲染过程中如果遇到 style 标签,则会马上执行 style 标签中的代码,如果遇到 link 标签,则异步加载,并且不会打断页面渲染;如果需要像 img, audio, vedio 这些需要请求资源的标签,页面渲染不会被打断,异步请求这些数据,页面渲染继续;如果遇到 script 标签,则会马上执行 JS 代码或马上请求 js 文件,该过程会打断页面渲染,可以在 请求外部 JS 文件的 script 标签中加入 async 或 defer 标签进行异步加载内部执行 script 标签加 async 和 defer 无效。这也是为什么要将 link 标签放在头部请求,script 标签要放在 body 底部的原因。

sessionStorage,localStorage 和 cookie 的区别

sessionStorage 是会话存储,存储空间较大,当会话窗口关闭时自动清空,发送请求时不会自动携带在请求头上。在不同的浏览器之间不能共享,当从当前页面跳转到另一个页面可以共享,新建页面则不进行共享。

localStorage 是本地存储,存储空间较大,不会自动清空,需要手动删除或者清空,发送请求时不会自动携带在请求头上。不管是不同的浏览器或者不同的标签页,只要是同源的就可以进行共享。

cookie 存储空间较小,大概 4k 左右,具有有效期,并且发送请求会自动携带在请求头上。不管是不同的浏览器或者不同的标签页,只要是同源的就可以进行共享。

如何监视 localStorage 的变化

(1) 原生 JS 提供一个 storage 事件,当时在该页面中绑定的 storage 事件,只能在另一个同域的页面中修改了 localStorage 才能触发,即该事件的触发需要两个页面,正常情况下不符合开发要求。

window.addEventListener('storage', () => {
  // callback
})

(2)自己封装一个 storage ,在很多开源项目中都是使用该方法监视 localStorage 的变化的,实用性较高。

class CommonLocalStorage {
  constructor() {
    this.storage = window.localStorage;
  }
  set(key, value) {
    // 执行监听的操作
    return this.storage.setItem(`${key}`, value);
  }
  get(key) {
    // 执行监听的操作
    return this.storage.getItem(`${key}`);
  }
  del(key) {
    // 执行监听的操作
    return this.storage.removeItem(`${key}`);
  }
  clear() {
    // 执行监听的操作
    this.storage.clear();
  }
}
​
const commonStorage = new CommonLocalStorage();
​
export default commonStorage

浏览器强缓存和协商缓存

跟缓存相关的HTTP头部配置项:expires,cache-control,Last-Modified/if-Modified-since,ETag/if-NoneMatch

expires 和 cache-control:max-age=xxx 都是控制缓存有效时间的,max-age 优先级高于 expires,expires的值是北京时间,而 max-age 的值是 xxx秒

浏览器请求数据,不仅会返回数据,还会返回 Last-Modified(表示数据最后修改的时间),ETag(文件内容的标识,当内容发送修改,ETag也会改变),ETag 和 Last-Moidified 存在于响应头

如果在缓存有效期内再次需要数据,则客户端不会再次发送请求给服务器,而是直接从浏览器缓存中获取数据(强缓存)

如果过了有效期,浏览器需要数据时,发送请求时会携带上上次请求返回的 ETag 和 Last-Moidified,ETag 对应 if-NoneMatch(存在于请求头),Last-Modified 对应 if-Modified-since(存在于请求头),与服务器的资源进行对比,如果内容没有发生改变,则继续使用缓存中的数据,响应状态码为304;如果数据发生变化,则由服务器返回新的数据,响应状态码为200(协商缓存)

ETag 和 if-NoneMatch 的优先级最高,对比如果没有发生变化则使用浏览器缓存的数据;Last-Modified 对应 if-Modified-since的局限性时没办法关注到毫秒时间内的修改,可能会导致无法获取最新的数据。

浏览器请求跨域

跨域是由于浏览器的同源策略引起的,要求协议名、域名、端口号一致才能相互发送请求传输数据,有一个不同则就跨域。

如何解决跨域:

1、JSONP:通过 JS 创建 script 标签,编写 JS 函数,script 标签的 src 值为跨域请求的地址,并将函数作为地址的参数携带在 url 地址中,将 script 标签插入页面中。后端接收后执行函数并将数据作为函数的参数返回。

缺点:需要后端的配合;只能处理 GET 请求的跨域问题

// 创建 script 标签
var script = document.createElement('script')
// 设置回调函数
function getData(data) {
    console.log(data)
}
// 设置 script 标签的 src 属性值
script.src = 'http://localhost:3000/?callback=getData'
// 将 script 标签插入页面
document.body.appendChild(script)

2、代理服务器:在开发过程中配置一台代理服务器,代理服务器地址指向需要跨域的地址,然后发请求只需要发给本地,代理服务器会进行转发。如 vue 中在 vue.config.js 中配置代理服务器。

缺点:只能在开发环境使用,生产环境无法使用。

module.exports = {
    devServer: {
      overlay: {
        warnings: false,
        errors: false
      },
      // 开启代理服务器(方式一)
      // proxy: 'http://localhost:8000'
      // 开启代理服务器(方式二)
      proxy: {
        // /api为请求前缀,当请求路径有/api则发送ajax请求;若请求路径不包括/api则不转发
        '/api': {
          target: 'http://localhost:8000',
          // 路径重写,将路径中的/api重写为空字符串,防止请求路径被改变
          pathRewrite: {'^/api':''},
          // ws: true, // 用于支持websocket
          // changeOrigin: true  // 用于控制请求头中的host值
        },
      }
    },
}

3、服务器配置响应头:服务器配置响应头的 Access-Control-Allow-Origin 的值为允许跨域的地址,或为 *

const express = require('express')
const app = express()
​
/* 
    res.setHeader('Access-Control-Allow-Origin', '*')
      Access-Control-Allow-Origin 允许跨域,若允许任何地址的跨域请求,则第二个参数为 *
      若有指定的允许跨域的地址,则第二个参数为指定的 url
*/
​
app.use((req, res, next) => {
    res.setHeader('Access-Control-Allow-Origin', '*')
    next()
})
​
app.use(express.json())
​
app.use(express.urlencoded({ extended: false }))
​
app.get('/user', (req, res) => {
    res.send(req.body)
})
​
app.listen(80, () => {
    console.log('serve run at http://127.0.0.1');
})

跨域拦截

浏览器的跨域拦截是发生在浏览器还是服务器?

浏览器。跨域拦截是由同源策略引起的一种保护机制,当浏览器和服务器不满足同源策略时,如果浏览器发送请求,服务器可以正常接收并返回数据,但是浏览器在接收返回的数据时,由于存在跨域问题会把响应体的内容截去。

跨域拦截浏览器是可以正常发送请求的,服务器也可以接收到请求并返回数据,只是数据在浏览器接收的时候被截取了。