前端面试题

前端面试题 每日一道

这个flag从好几个星期前就开始了 但是就一直没有实施 我觉得问题就是我的时间点不对 要把面试题提前到第一件事情做 后面的事情再慢慢来吧 好 那就开始⑧

这里是整理出来的几个面试题库网址

🍔这个掘金里的 感觉挺好 实在是太多了 还是要慢慢啃

🍔这个是鱼皮的面试题网站 可以看看 反正都不会

🍔关于Html 5 的东西

关于面试题型

1.前端页面加载流程 TCP请求到页面交互

2.前端框架对比 React 和 Vue

3.理解函数式组件的产生背景和优势 理解js class逐渐被react和vue抛弃的原因

4. js 基础知识

5.优化方案 TCP请求 接口请求 页面绘制

正文👇

1)关于盒子模型的正确表述

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sHvtbglG-1646491405264)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\1643254629136.png)]

这题选A

找到了一个方便快捷的解释 就是标准盒模型和IE盒模型的总宽都是一样的,区别在于width IE盒的width包括了content padding border 而标准盒的width包括的只有content

标准盒模型content-box **= width border padding margin ; width = content **

IE盒模型border-box **= width margin ; width= border + content **

**应该选择“标准 w3c 盒子模型(就是标准盒模型)”了 。假如加上了 doctype 声明,那么所有浏览器都会采用标准 w3c 盒子模型去解释你的盒子,网页就能在各个浏览器中显示一致了。 **


2)关于Object.defineProperty

Vue 的响应式原理中 Object.defineProperty 有什么缺陷?为什么在 Vue3.0 采用了 Proxy,抛弃了 Object.defineProperty?

其实看到这个题目的时候我甚至不知道ObjectProperty是什么东西 然后就去搜了一下👇

Object.defineProperty()语法说明

Object.defineProperty()的作用就是直接在一个对象上定义一个新属性,或者修改一个已经存在的属性

Object.defineProperty(obj, prop, desc)
  1. obj 需要定义属性的当前对象
  2. prop 当前需要定义的属性名
  3. desc 属性描述符

一般通过为对象的属性赋值的情况下,对象的属性可以修改也可以删除,但是通过Object.defineProperty()定义属性,通过描述符的设置可以进行更精准的控制对象属性。

所以言归正传 问题的答案是什么呢?👇

  1. Object.defineProperty无法监控到数组下标的变化,导致通过数组下标添加元素,不能实时响应;
  2. Object.defineProperty只能劫持对象的属性,从而需要对每个对象,每个属性进行遍历,如果,属性值是对象,还需要深度遍历。Proxy可以劫持整个对象,并返回一个新的对象。
  3. Proxy不仅可以代理对象,还可以代理数组。还可以代理动态增加的属性。

3)html5新特性、语义化

HTML 5的页面结构和HTML 4或早先的HTML有什么不同?

一个典型的Web页面有页眉(header),页脚(footer),导航(navigation),正文(central area)和侧栏(side bar)。现在如果是在HTML 4中,HTML部分中的上述这些专用名词需要使用DIV标签来描述。

但是,如果是在HTML 5,可以专门为这些区域创建特定的元素名,让HTML更具可读性。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-shTYepzE-1646491405266)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\1643257242043.png)]

以下是形成页面结构的HTML 5元素的更多细节。

<header>:表现HTML的标题数据。
<footer>:页面的页脚部分。
<nav>:页面中的导航元素。
<article>:正文内容。
<section>:用在正文中定义section或区段内容。
<aside>:表现页面侧边栏内容。

4)浏览器渲染机制、重绘、重排

网页生成过程:

  • HTML被HTML解析器解析成DOM
  • css则被css解析器解析成CSSOM
  • 结合DOM树和CSSOM树,生成一棵渲染树(Render Tree)
  • 生成布局(flow),即将所有渲染树的所有节点进行平面合成
  • 将布局绘制(paint)在屏幕上

重排(也称回流):DOM的变化影响了元素的几何信息(DOM对象的位置和尺寸大小),浏览器需要重新计算元素的几何属性,将其安放在界面中的正确位置,这个过程叫做重排。 触发:

  1. 添加或者删除可见的DOM元素
  2. 元素尺寸改变——边距、填充、边框、宽度和高度

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

  • 改变元素的color、background、box-shadow等属性

重排优化建议:

  1. 分离读写操作
  2. 样式集中修改
  3. 缓存需要修改的DOM元素
  4. 尽量只修改position:absolutefixed元素,对其他元素影响不大
  5. 动画开始GPU加速,translate使用3D变化

transform 不重绘,不回流 是因为**transform属于合成属性**,对合成属性进行transition/animate动画时,将会创建一个合成层。这使得动画元素在一个独立的层中进行渲染。当元素的内容没有发生改变,就没有必要进行重绘。浏览器会通过重新复合来创建动画帧。


5)Css样式优先级

important > style > id > class


6)什么是BFC?BFC的布局规则是什么?如何创建BFC?BFC应用?

BFCBlock Formatting Context的缩写,即块格式化上下文。BFC是CSS布局的一个概念,是一个环境,里面的元素不会影响外面的元素

布局规则:Box是CSS布局的对象和基本单位,页面是由若干个Box组成的。元素的类型和display属性,决定了这个Box的类型。不同类型的Box会参与不同的Formatting Context

创建:浮动元素 display:inline-block position:absolute

应用: 1.分属于不同的BFC时,可以防止margin重叠 2.清除内部浮动 3.自适应多栏布局


7)DOM, BOM对象

**BOM(Browser Object Model)**是指浏览器对象模型,**可以对浏览器窗口进行访问和操作。**使用 BOM,开发者可以移动窗口、改变状态栏中的文本以及执行其他与页面内容不直接相关的动作。 使 JavaScript 有能力与浏览器"对话"。

**DOM (Document Object Model)**是指文档对象模型,通过它,可以访问HTML文档的所有元素。 DOMW3C(万维网联盟)的标准。DOM 定义了访问 HTMLXML 文档的标准: “W3C 文档对象模型(DOM)是中立于平台和语言的接口,它允许程序和脚本动态地访问和更新文档的内容、结构和样式。” W3C DOM 标准被分为 3 个不同的部分:

  • 核心 DOM - 针对任何结构化文档的标准模型
  • XML DOM - 针对 XML 文档的标准模型
  • HTML DOM - 针对 HTML 文档的标准模型
什么是XML DOM

XML DOM 定义了所有 XML 元素的对象和属性,以及访问它们的方法。

什么是 HTML DOM?

HTML DOM 定义了所有 HTML 元素的对象和属性,以及访问它们的方法。


8)暂时性死区

//情景一、
if (true) {
  // TDZ开始
  tmp = 'abc'; // ReferenceError
  console.log(tmp); // ReferenceError


  let tmp; // TDZ结束
  console.log(tmp); // undefined


  tmp = 123;
  console.log(tmp); // 123
}

上面代码中,在let命令声明变量tmp之前,都属于变量tmp的“死区”。

//情景二
var tmp = 123;
if (true) {
  tmp = 'abc'; // ReferenceError
  let tmp;
}

上面代码中,存在全局变量tmp,但是块级作用域内let又声明了一个局部变量tmp,导致后者绑定这个块级作用域,所以在let声明变量前,对tmp赋值会报错。

ES6明确规定,如果区块中存在letconst命令,这个区块对这些命令声明的变量,从一开始就形成了封闭作用域。凡是在声明之前就使用这些变量,就会报错。

//情景三
// 不报错
var x = x;
// 报错
let x = x;
// ReferenceError: x is not defined

上面代码报错,也是因为暂时性死区。**使用let声明变量时,只要变量在还没有声明完成前使用,就会报错。**上面这行就属于这个情况,在变量x的声明语句还没有执行完成前,就去取x的值,导致报错”x 未定义“

总结

var和函数声明在执行上下文中的变量环境里,在编译阶段,当JS遇到var声明的变量时,会自动先赋值一个undefined,当遇到函数时,会先预编译函数,检查是否存在闭包(如果存在,会将闭包引入的变量从栈存放在堆中作为持久引用),然后将函数放在变量环境里,函数是会放在变量前面的。

let,const的变量名都是存储在词法环境里的,词法环境里的变量是不会事先赋值一个undefined的,没有赋值自然就会报错了。


9)闭包

闭包是指**有权访问另一个函数作用域中的变量的函数 **

当函数可以记住并访问所在的词法作用域时,就产生了闭包
即使函数是在当前词法作用域之外执行
  • 闭包用途:
    1. 能够访问函数定义时所在的词法作用域(阻止其被回收)
    2. 私有化变量
    3. 模拟块级作用域
    4. 创建模块
  • 闭包缺点:

    ​ 会导致函数的变量一直保存在内存中,过多的闭包可能会导致内存泄漏


10)Diff算法

​ 好早之前就想学这个东西了 好像说什么这个是要会的 刚好今天看到这篇文章 就来学一哈吧 ~

关键词:patch sameVnode patchNode updateChildren

什么是虚拟DOM?

虚拟DOM其实就是一个对象 一个用来表示真实DOM的对象

<ul id="list">
    <li class="item">哈哈</li>
    <li class="item">呵呵</li>
    <li class="item">嘿嘿</li>
</ul>

对应的虚拟DOM为👇

let vDOM = { // 旧虚拟DOM
        tagName: 'ul', // 标签名
        props: { // 标签属性
            id: 'list'
        },
        children: [ // 标签子节点
            {
                tagName: 'li', props: { class: 'item' }, children: ['哈哈']
            },
            {
                tagName: 'li', props: { class: 'item' }, children: ['呵呵']
            },
            {
                tagName: 'li', props: { class: 'item' }, children: ['嘿嘿']
            },
        ]
    }

虚拟DOM把真实DOM解析成一个对象 包含标签名 标签属性和标签子节点

解释一下 👇
props是属性
效率问题

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fRIJdi27-1646491405267)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\1646101497991.png)]

虚拟DOM算法操纵真实DOM性能高于操作真实DOM

虚拟DOM算法 = 虚拟DOM + Diff算法

正文

​ 其实Diff算法就是一种对比算法 ,对比两者是旧虚拟DOM和新虚拟DOM,对比出是哪个虚拟节点更改了,找出这个虚拟节点,并只更新这个虚拟节点所对应的真实节点,而不用更新其他数据没发生改变的节点,实现精准地更新真实DOM,进而提高效率

  • 使用虚拟DOM算法的损耗计算: 总损耗 = 虚拟DOM增删改+(与 Diff 算法效率有关)真实DOM差异增删改+(较少的节点)排版与重绘

  • 直接操作真实DOM的损耗计算: 总损耗 = 真实DOM完全增删改+(可能较多的节点)排版与重绘

Diff算法的原理

新旧虚拟DOM对比的时候,Diff算法比较只会在同层级进行, 不会跨层级比较。 所以Diff算法是:深度优先算法。 时间复杂度:O(n)

当数据改变时,会触发setter,并且通过Dep.notify去通知所有订阅者Watcher,订阅者们就会调用patch方法,给真实DOM打补丁,更新相应的视图。

patch方法

这个方法作用就是,对比当前同层的虚拟节点是否为同一种类型的标签(同一类型的标准,下面会讲)

  • 是:继续执行patchVnode方法进行深层比对
  • 否:没必要比对了,直接整个节点替换成新虚拟节点

核心代码👇

function patch(oldVnode, newVnode) {
  // 比较是否为一个类型的节点
  if (sameVnode(oldVnode, newVnode)) {
    // 是:继续进行深层比较
    patchVnode(oldVnode, newVnode)
  } else {
    // 否
    const oldEl = oldVnode.el // 旧虚拟节点的真实DOM节点
    const parentEle = api.parentNode(oldEl) // 获取父节点
    createEle(newVnode) // 创建新虚拟节点对应的真实DOM节点
    if (parentEle !== null) {
      api.insertBefore(parentEle, vnode.el, api.nextSibling(oEl)) // 将新元素添加进父元素
      api.removeChild(parentEle, oldVnode.el)  // 移除以前的旧元素节点
      // 设置null,释放内存
      oldVnode = null
    }
  }

  return newVnode
}

sameVnode 方法

判断是否为同一类型节点

核心原理代码👇
function sameVnode(oldVnode, newVnode) {
  return (
    oldVnode.key === newVnode.key && // key值是否一样
    oldVnode.tagName === newVnode.tagName && // 标签名是否一样
    oldVnode.isComment === newVnode.isComment && // 是否都为注释节点
    isDef(oldVnode.data) === isDef(newVnode.data) && // 是否都定义了data
    sameInputType(oldVnode, newVnode) // 当标签为input时,type必须是否相同
  )
}
复制代码
如果通过 sameVnode(a, b)对比之后还是不一样的话 就直接用新的节点替换掉之前的节点
patchVnode方法

这个函数做了以下事情:

  • 找到对应的真实DOM,称为el
  • 判断newVnodeoldVnode是否指向同一个对象,如果是,那么直接return
  • 如果他们都有文本节点并且不相等,那么将el的文本节点设置为newVnode的文本节点。
  • 如果oldVnode有子节点而newVnode没有,则删除el的子节点
  • 如果oldVnode没有子节点而newVnode有,则将newVnode的子节点真实化之后添加到el
  • 如果两者都有子节点,则执行updateChildren函数比较子节点,这一步很重要

updateChildren就是使用首尾指针法进行对比的

⭐最终的渲染结果都要以newVDOM为准

原理图如下

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xrw801Iq-1646491405267)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\1646488628620.png)]

11)在 Vue 中为什么不推荐用 indexkey

具体diff解释可以看看第十题

Vue3.0中 在 patchChildren 方法中有这么一段源码

patchChildren 根据是否存在 key 进行真正的 diff 或者直接 patch(也就是直接用新的节点替换掉旧的节点)

  • 同步头部节点

    第一步的事情就是从头开始寻找相同的 vnode,然后进行 patch ,如果发现不是相同的节点,那么立即跳出循环。

    isSameVNodeType 作用就是判断当前 vnode 类型 和 vnodekey 是否相等

    key 在 diff 算法的作用,就是用来判断是否是同一个节点

  • 同步尾部节点

    第二步从尾开始同前 diff

    经历第一步操作之后,如果发现没有 patch 完,那么立即进行第二步,从尾部开始遍历依次向前 diff。如果发现不是相同的节点,那么立即跳出循环。

  • 添加新的节点

    第三步如果老节点全部 patch,新节点没有被 patch 完,创建新的 vnode

  • 删除多余节点

    第四步如果新节点全部被 patch,老节点有剩余,那么卸载所有老节点

为什么不要用index
  • 性能消耗

    使用 index 做 key,破坏顺序操作的时候, 因为每一个节点都找不到对应的 key导致部分节点不能复用,所有的新 vnode 都需要重新创建。

    当我们在前面加了一条数据时 index 顺序就会被打断,导致新节点 key 全部都改变了,所以导致我们页面上的 数据都被重新渲染了。

用唯一值作为 key 可以节约开销

  • 数据错位

    往 input 里面输入一些值,添加一位同学效果: 采用 index 作为 key 时,当在比较时,发现虽然文本值变了,但是当继续向下比较时发现 DOM 节点还是和原来一摸一样,就复用了,但是没想到 input 输入框残留输入的值,这时候就会出现输入的值出现错位的情况

  • 解决方案

    既然知道用 index 在某些情况下带来很不好的影响,那平时我们在开发当中怎么去解决这种情况呢?其实只要保证 key 唯一不变就行,一般在开发中用的比较多就是下面三种情况。

  1. 在开发中最好每条数据使用唯一标识固定的数据作为 key,比如后台返回的 ID,手机号,身份证号等唯一值
  2. 可以采用 Symbol 作为 key,Symbol 是 ES6 引入了一种新的原始数据类型 Symbol ,表示独一无二的值,最大的用法是用来定义对象的唯一属性名
let a = Symbol('测试')
let b = Symbol('测试')
console.log(a===b)//false
  1. 可以采用 uuid 作为 keyuuid 是 Universally Unique Identifier 的缩写,它是在一定的范围内(从特定的名字空间到全球)唯一的机器生成的标识符
⭐总结

关键词:破坏顺序-----输入类的DOM-----唯一标识的key

  • 用 index 作为 key 时,在对数据进行,逆序添加,逆序删除等破坏顺序的操作时,会产生没必要的真实 DOM更新,从而导致效率低
  • 用 index 作为 key 时,如果结构中包含输入类的 DOM,会产生错误的 DOM 更新
  • 在开发中最好每条数据使用唯一标识固定的数据作为 key,比如后台返回的 ID,手机号,身份证号等唯一值
  • 如果不存在对数据逆序添加,逆序删除等破坏顺序的操作时,仅用于渲染展示用时,使用 index 作为 key 也是可以的(但是还是不建议使用,养成良好开发习惯)。
  • key作为虚拟DOM对象的唯一标识diff算法中起到了至关重要的作用

12)

13)

14)

15)

16)

17)

18)

19)

20)

🛫

不懂的知识点

1)Flexbox

Flexbox可以简单快速的创建一个具有弹性功能的布局,当在一个小屏幕上显示的时候,Flexbox可以让元素在容器(伸缩容器)中进行自由扩展和收缩,从而容易调整整个布局。它的目的是使用常见的布局模式,比如说三列布局,可以非常简单的实现。

一个Flexbox布局是由一个伸缩容器(flex containers)和在这个容器里的伸缩项目(flex items)组成。 伸缩容器(flex containers)是一个HTML标签元素,并且“display”属性显式的设置了“flex”属性值。在伸缩容器中的所有子元素都会自动变成伸缩项目(flex items)。

display:flex

ey作为虚拟DOM对象的唯一标识** 在diff算法中起到了至关重要的作用

12)

13)

14)

15)

16)

17)

18)

19)

20)

🛫

不懂的知识点

1)Flexbox

Flexbox可以简单快速的创建一个具有弹性功能的布局,当在一个小屏幕上显示的时候,Flexbox可以让元素在容器(伸缩容器)中进行自由扩展和收缩,从而容易调整整个布局。它的目的是使用常见的布局模式,比如说三列布局,可以非常简单的实现。

一个Flexbox布局是由一个伸缩容器(flex containers)和在这个容器里的伸缩项目(flex items)组成。 伸缩容器(flex containers)是一个HTML标签元素,并且“display”属性显式的设置了“flex”属性值。在伸缩容器中的所有子元素都会自动变成伸缩项目(flex items)。

display:flex
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值