原文链接: 掘金文章
笔试部分请翻阅上上篇文章
1. 盒模型
- 盒模型属性有:
margin、padding、border、content
- 标准盒模型:
width/height
=> 指的是content
部分的宽/高 - 怪异盒模型(IE浏览器)width/height => 指的是 border + padding + content
2. BFC
- 介绍: 块格式化上下文
- 特性(功能):
- 内部的盒会在垂直方向一个接一个排列
- 处于同一个
BFC
中的元素相互影响,可能会发生重叠 BFC
就是页面上的一个隔离的独立容器,容器里面的子元素不会影响到外面的元素- 计算
BFC
的高度时,考虑BFC
所包含的所有元素,连浮动元素也参与计算
- 触发 BFC 特性:
- 浮动元素:
float
除none
以外的值 - 绝对定位元素:
position (absolute、fixed)
overflow
除了visible
以外的值(hidden、auto、scroll)
display
的值为table-cell, table-caption, inline-block, flex
, 或者inline-flex
中的其中一个
- 浮动元素:
BFC
有什么作用:- 避免外边距合并
- 防止正常文档流中元素占据浮动元素位置
- 消除浮动
- 实现自适应布局
3. 你工作开发中 ES6 及以上常用特性
- 注:记些工作中常用的就好, 以下均为粗略的介绍,详细介绍请自行百度
- 一总结吓自己一跳,有好多方法其实我们都在用,但是不知道是Es6 及以上的功能;ES7以上有兼容性问题,要注意
- 有点乱, ES6-ES11之间相互穿插了一些功能
ES6
详情 菜鸟教程很完善的文档https://www.runoob.com/w3cnote/es6-tutorial.html
let
与const
:块级作用域- 模板字符串:
${}
- 解构赋值
Symbol
:新的原始数据类型Symbol
,表示独一无二的值,最大的用法是用来定义对象的唯一属性名Map:Map
对象保存键值对。任何值(对象或者原始值) 都可以作为一个键或一个值。Set
:对象允许你存储任何类型的唯一值,无论是原始值或者是对象引用。Proxy
:可以对目标对象的读取、函数调用等操作进行拦截,它不直接操作对象,而是通过代理模式,通过对象的代理对象进行操作;vue3.0重要方法Reflect
:可以用于获取目标对象的行为,它与Object
类似,但是更易读,为操作对象提供了一种更优雅的方式。它的方法与Proxy
是对应的- 字符串拓展的方法:
includes()
:返回布尔值,判断是否找到参数字符串;startsWith()
:返回布尔值,判断参数字符串是否在原字符串的头部;endsWith()
:返回布尔值,判断参数字符串是否在原字符串的尾部。repeat()
:返回新的字符串,表示将字符串重复指定次数返回。padStart()
:前置填充字符串; 经典常用 --> 前置补0。padEnd()
:后置填充字符串; 经典常用 --> 后置补0 。
Number
对象新方法Number.isFinite()
: 检查一个数值是否为有限的(finite
),即不是Infinity
Number.parseInt()
: 将给定字符串转化为指定进制的整数
Math
对象的扩展: ... 一大堆,请查看文档- 对象的新方法:
Object.assign(target, source_1, ···)
: 将源对象的所有可枚举属性复制到目标对象中Object.is(value1, value2)
:比较两个值是否严格相等
- 数组的拓展:
Array.of()
:将参数中所有值作为元素形成数组。Array.from()
: 将类数组对象或可迭代对象转化为数组。find()
:查找数组中符合条件的元素,若有多个符合条件的元素,则返回第一个元素findIndex()
:查找数组中符合条件的元素索引,若有多个符合条件的元素,则返回第一个元素索引。fill()
:将一定范围索引的数组元素内容填充为单个指定的值。copyWithin()
:将一定范围索引的数组元素修改为此数组另一指定范围索引的元素。entries()
:遍历键值对。keys()
:遍历键名。values()
:遍历键值。includes()
:数组是否包含指定值。
- 函数扩展
- 箭头函数
- 默认参数
- 不定参数
for...of
循环:迭代常规的数据类型,如Array 、 String 、 Map 和 Set 等等
(有点强大)Class
类的定义extends
继承- 导出(
export
)、导入(import
)两个模块 Promise
对象异步操作pending
(进行中)、fulfilled
(已成功)和rejected
(已失败)。除了异步操作的结果,任何其他操作都无法改变这个状态- 从
pending
变为fulfilled
和从pending
变为rejected
的状态改变。只要处于fulfilled
和rejected
,状态就不会再变了即resolved
(已定型)
Generator
函数:可以通过yield
关键字,把函数的执行流挂起,为改变执行流程提供了可能,从而为异步编程提供解决方案
ES7
Array.prototype.includes
:表示某个数组是否包含给定的值,与字符串的includes
方法类似- 指数运算符(幂运算):
ES8
Async/Await
:异步函数有效避免回调地狱Object.values()
:遍历对象的属性值,无需使用使用属性名Object.entries()
:遍历对象的属性名和属性值Object.getOwnPropertyDescriptors()
:获取一个对象的所有自身属性的描述符。
ES9
- 异步迭代:
iterator
(迭代器)除了next()
方法返回一个Promise
。因此await
可以和for...of
循环一起使用,以串行的方式运行异步操作 Promise.finally()
: 返回一个Promise
,无论结果是fulfilled
或者是rejected
,在执行then()
和catch()
后,都会执行finally
指定的回调函数Rest/Spread
属性- 正则表达式命名捕获组
- 正则表达式反向断言
- 正则表达式Unicode属性转义
- 异步迭代:
ES10
Array.prototype flat, flatMap
: 扁平化数组Object.fromEntries
:它可以将键值对数组还原成对象结构, 相对应Es6
的Object.entries
方法 遍历对象的属性名和属性值String.protope.{trimstart, trimEnd}
:可以分别去除头和尾上的空格、换行符Symbol.protoype.description
:获取Symbol
类型数据的描述信息- 可选的catch参数
Function.prototype.toString:
之前函数对象调用toString
方法,会将它定义过程中的注释等信息去掉,现在会原样进行输出Array.prototype.sort()
:稳定的数组排序; V8的先前实现,对包含10个以上项的数组使用了不稳定的快速排序算法(快排和插入排序算法,默认是将数组元素转为字符串,然后根据Unicode
字符集编号的大小排序)。
ES11
BigInt
数据类型: 表示一个任意精度的整数,可以表示超长数据,可以超出2的53次方- 私有变量: 通过在变量或函数前面添加一个哈希符号#,可以将它们设为私有属性,只在类内部可用
Promise.allSettled
: 其参数接受一个Promise
的数组, 返回一个新的Promise
, 其不会进行短路, 当Promise
全部处理完成后我们可以拿到每个Promise
的状态, 而不管其是否处理成功;Promise.allPromise.rae
任何一个失败都会造成短路- 可选链操作符 ?.
- 空值合并运算符 ??
import
支持动态加载模块,加载模块成功以后,这个模块会作为一个对象,当作then回调的参数。因此,可以使用对象解构赋值的语法,获取输出接口globalThis
:一种标准化的方式去访问全局对象,这时候可以在任意上下文中获取全局对象自身,并且不用担心环境的问题String.protype.matchAll()
: 返回所有与正则表达式匹配字符串的结果的迭代器,包括捕获组
4. map
和 object
的区别
key
必须是简单数据类型(整数,字符串或者是symbol
),但一个Map
的键可以是任意数据类型任意值。Map
中的键值是有序的(FIFO 原则),而添加到对象中的键则没有这一特性。Map
的键值对个数可以从size
属性获取,而Object
的键值对个数只能手动计算。new Map()
有set、get
方法Object
都有自己的原型,原型链上的键名有可能和你自己在对象上的设置的键名产生冲突, Map 继承自 Object 对象。
5. Typescript
和 JavaScript
的不同, 常用类型有哪些
JavaScript
特点、特性:
- 解释性脚本语言(不进行预编译,跟java一样);
- 在html页面上提供交互行为、既可以写成单独的js文件,也可以嵌入在html中
- 可跨平台,因为受到了各种浏览器的支持,这使得js可以在各种平台上运行
- 作为客户端脚本语言,独立运行于用户的浏览器,不需要服务器的支持,减少对服务器的负担
- 不安全性,显然这个特性和上一个特性相关
- 事件驱动、异步、动态化,和大部分脚本语言一样,类型与值而不是与变量关联
typescript
是微软开发的用于开发大型应用的编程语言,其为javascript
的严格超集,并添加了可选的静态类型和基于类的面向对象编程
typescript
完全兼容javascript
,且最终编译成javascript
运行typescript
有编译时类型检查,这为程序的编写带来了极大的方便javascript
是一门动态语言,而typescript
添加了可选的静态类型typescript
在javascript
的基础上增加了不少特性(类型批注、编译时类型检查、类型推断、接口、枚举、混入、泛型编程、命名空间、元组、类、可选参数、默认参数 .........)- ++常用类型有++:7大基本数据类型、
any
(任意值 --- 顶级类型)、unknown
(任意值 --- 顶级类型)、never(
永不存在的值的类型)、void
(空值)、Tuple
(元组)、enum
(枚举)、interface
接口类型、泛型
6. vue
和 react
有哪些区别 (自由扩展,能扩展到很远)
- 监听数据变化的实现原理不同
Vue
通过getter/setter
以及一些函数的劫持,每个组件都有自己的渲染watcher
,它掌管了当前组件的视图更新,但是并不会掌管ChildComponent
的更新, 能精确知道数据变化React
在类似的场景下是 自顶向下的进行递归更新的;就是说,React
中假如ChildComponent
里还有十层嵌套子元素,那么所有层次都会递归的重新render
(在不进行手动优化的情况下),在事务结束时触发;更新是一个同步的过程,会影响渲染,造成卡顿,这是性能上的灾难。React
因为他们遵从Immutable
的设计思想,永远不在原对象上修改属性,由于没有响应式的收集依赖,React
只能递归的把所有子组件都重新render
一遍,然后再通过 diff算法 决定要更新哪部分的视图,这个递归的过程叫做reconciler
;React
在初始化的时候,会把真实的dom
转化成虚拟的dom,vnode
是fiber
对象节点;(因此,React
创造了Fiber
,创造了异步渲染,其实本质上是弥补被自己搞砸了的性能)React
默认通过比较引用的方式(diff
)进行的,如果不优化可能导致大量不必要的VDOM
的重新渲染;原因:Vue
使用的是可变数据,而React
更强调数据的不可变,两者没有好坏之分,Vue
更加简单,而React
构建大型应用的时候更加有利Vue
可以做到更小粒度的更新,而react
做不到,这是由数据驱动决定的,vue
的底层diff
算法参考linux
的文件比较算法,在效率上比react
的diff
更好一点,但是react
实现了requsetIdleCallback
,让计算不影响渲染。更加流畅
- 数据流的不同:
vue
使用v-model
实现双向数据绑定,react
需手动调用setState
方法实现 HoC
和mixins
:vue
使用mixin
实现混合,react
有mixins
转向了HoC
(高阶组件)(觉得mixins
觉得这种方式对组件侵入太强会导致很多问题)- 模板渲染方式的不同:
React
是通过JSX
渲染模板都,通过原生JS
实现模板中的常见语法,比如插值,条件,循环等,都是通过JS
语法实现的,更加纯粹更加原生;Vue
是通过一种拓展的HTML
语法进行渲染,通过指令来实现各种js
逻辑,有些独特,但会把HTML
弄得很乱
- 渲染过程不同:
Vue
可以更快地计算出Virtual DOM
的差异,这是由于它在渲染过程中,会跟踪每一个组件的依赖关系,不需要重新渲染整个组件树React
在应用的状态被改变时,全部子组件都会重新渲染。通过shouldComponentUpdate
这个生命周期方法可以进行控制,但Vue
将此视为默认的优化- 如果应用中交互复杂,需要处理大量的
UI
变化,那么使用Virtual DOM
是一个好主意。如果更新元素并不频繁,那么Virtual DOM
并不一定适用,性能很可能还不如直接操控DOM
- 框架本质不同:
Vue
本质是MVVM
框架,由MVC
发展而来;React
是前端组件化框架,由后端组件化发展而来 - 状态管理
Vuex
和Redux
的区别:
Redux
使用的是不可变数据;每一个组件都需要显示的用connect
把需要的props
和dispatch
连接起来;只能进行dispatch
,不能直接调用reducer
进行修改Vuex
的数据是可变的;$store
被直接注入到了组件实例中,因此可以比较灵活的使用dispatch、commit
提交更新,可通过mapState、mapActions、mapGetters、mapMutations
功能函数或者直接通过this.$store
来读取数据和操作函数
7. vue3.0
新特性,和vue2.0
的主要区别
- 开发构建工具的重构
vite
Vue团队也推出了自己的开发构建工具
Vite
,可以在一定程度上取代vue-cli
和webpack-dev-server
的功能;Vite
在开发环境下基于浏览器原生ES Modules
开发,在生产环境下基于Rollup
打包;Vite
主要有以下特性:
- 快速的冷启动
- 即时的模块热更新
- 真正的按需编译
- 重构了
Virtual DOM
Vue2.x
版本会遍历template
模板中的所有内容,并根据这些标签生成对应的虚拟DOM
,当有内容改变时,遍历虚拟DOM
来diff
找到对应的标签元素所对应的DOM
节点,实现双向数据绑定;但是对于那些纯静态的节点进行diff
其实是比较浪费资源的,当节点的数量很少时,表现并不明显,但是一旦节点的数量过大,在性能上就会慢很多;vue3.0
在此基础上优化有:
- 标记静态内容,并区分动态内容
- 更新时只
diff
动态的部分
- 基于Proxy的响应式对象,替换了
Object.defineProperty()
Proxy
可以直接监听对象和数组(而非属性)的变化,并且有多达13种拦截方法。并且作为新标准将受到浏览器厂商重点持续的性能优化Proxy
返回的是一个新对象,我们可以只操作新的对象达到目的,而Object.defineProperty
只能遍历对象属性直接修改;Object.defineProperty
无法监听变异数组(会修改原来数组的方法:push、pop、shift、unshift、splice、sort、reverse
等,是无法触发set
的)的变化, 需要单独处理; 必须遍历对象的每个属性并添加劫持;必须深层遍历嵌套的对象,直到把每个对象的每个属性都调用Object.defineProperty()
为止;Proxy
只会代理对象的第一层,Vue3
是怎样处理这个问题的呢?
- 判断当前
Reflect.get
的返回值是否为Object
,如果是则再通过reactive
方法做代理, 这样就实现了深度观测。- 监测数组的时候可能触发多次
get/set
,那么如何防止触发多次呢?我们可以判断key是否为当前被代理对象target
自身属性,也可以判断旧值与新值是否相等,只有满足以上两个条件之一时,才有可能执行trigger
。
composition-api
composition-api
(配置式API开发)提供了一种创建响应式对象的方法reactive
,选择性的监听属性,这样就可以不用针对每个属性来一一进行添加,减少开销提升性能;
Tree shaking
支持
移除
JavaScript
上下文中的未引用代码不会被打包;一定程度上减少了资源的大小, 提升了构建、运行速度
TypeScript
的支持
Vue3
中直接采用了typescript
来进行重写,从源码层面来提升项目的可维护性Typescript
能够在开发时及时发现问题,而非运行时,大大的提升了系统的稳定性- 静态类型检查很大程度的统一了团队之间的开发习惯,易于项目的维护,提高开发人员的工作效率节约开发成本
- 更加丰富的类型支持及开发便利
- 移除的一些API和方法
- 取消
KeyboardEvent.keyCode
: 使用别名代替- 移除
$on,$off
和$once
方法:EventBus --> mitt
方案来代替- 移除
filters
: 使用methods
的或者computed
来进行替代
- Vue Router 的变化
- 构建选项
mode
--> 由原来的mode "history"
更改为history: createWebHistory()
- 构建选项
base
--> 传给createWebHistory()
的第一个参数作为base
。- 捕获所有路由 ( /* ) 时,现在必须使用带有自定义正则表达式的参数进行定义:/:catchAll(.*)
push
或者resolve
一个不存在的命名路由时,将会引发错误,而不是导航到根路由 "/" 并且不显示任何内容- r
outer.match
(匹配路径参数的对象) 与router.resolve
(页面跳转) 合并在一起为router.resolve
8. react hook
有用么, 说说对react hook
理解
- 优点:
- 让你在不编写
class
的情况下使用state
以及其他的React
特性- 函数式编程组件开发,高度解耦,状态保存在运行环境、每个功能都包裹在函数中,整体风格更美观、优雅;
- 组件树层级变浅、组件粒度越细更容易复用代码,通过自定义
hooks
来复用状态,从而解决了类组件有些时候难以复用逻辑的问题useEffect、useMemo、useCallback
让优化手段更加的简单(利用hooks
钩子)、useState
不用再去考虑this
的指向问题、useContext
让父子组件传值更加简单
- 缺点:
- 响应式的
useEffect
: 需要精确掌握上下文的useEffect
的触发时机。当逻辑较复杂的时候,useEffect
容易触发多次- 状态不同步: 最大的缺点。函数的运行是独立的,每个函数都有一份独立的作用域。函数的变量是保存在运行时的作用域里面,当我们有异步操作的时候,经常会碰到异步回调的变量引用的是旧状态
- 解决及避免:
- 不要在
useEffect
里面写太多的依赖项,划分这些依赖项成多个单一功能的useEffect
。其实这点是遵循了软件设计的“单一职责模式”;- 如果你碰到状态不同步的问题,可以考虑下手动传递参数到函数;
- 复杂业务的时候,使用
Component
代替hooks
9. 工作中优化方向有哪些,依据什么来确定是否需要优化
内容方向有点多;详见博客文章
https://www.cnblogs.com/ljx20180807/p/13543487.html
10. 工作中有没有遇到比较难的需求,怎么处理的,性能怎么样,你觉得有没有可以优化的地方
根据实际情况自由发挥
11.带团队过程中,团队和自己所花费时间比例,主要体现在哪些方面**
根据实际情况自由发挥