本文出现的问题都是在8月阶段实际面试过程中遇到的,大小公司都有涉及,希望能够助力大家金九银十,也希望自己能够顺利找到工作。现在外面大环境的确太难了……
文中部分题目提供了参考资料以及自己整理的相关答案,可能不一定是最准确的,大家酌情参考。
如涉及到其他文章内容引用没有标明出处的,可以私聊补充更新。
Javascript
1.JS基本数据类型有哪些
基本数据类型:
- 字符串(String):表示文本数据,使用引号(单引号或双引号)括起来。例如:"Hello World"。
- 数字(Number):表示数值数据,包括整数和浮点数。例如:10、3.14。
- 布尔值(Boolean):表示逻辑值,只有两个值:true(真)和false(假)。
- null:表示空值,表示一个空对象指针。
- undefined:表示未定义的值,表示变量未被赋值。
- Symbol:表示唯一的标识符,用于对象属性的键。
【扩展】说一下undefined和null的区别
当谈到 JavaScript 中的 undefined 和 null 时,它们是两个不同的值,但都表示缺少值。它们之间的区别如下:
- undefined 是表示变量声明但未赋值的默认值。当尝试访问未初始化的变量时,它的值为 undefined。全局属性 undefined 表示原始值undefined。它是一个 JavaScript 的 原始数据类型 。undefined是全局对象的一个属性。也就是说,它是全局作用域的一个变量。undefined的最初值就是原始数据类型undefined。
- 值 null 特指对象的值未设置。它是 JavaScript 基本类型 之一,在布尔运算中被认为是falsy。它是一个特殊的关键字,表示变量不指向任何对象、数组或函数。值 null 是一个字面量,不像 undefined,它不是全局对象的一个属性。null 是表示缺少的标识,指示变量未指向任何对象。把 null 作为尚未创建的对象,也许更好理解。在 API 中,null 常在返回类型应是一个对象,但没有关联的值的地方使用。
2.const、let和var的区别
【参考资料】
3.call、apply和bind的区别
- 三者都可以改变函数的this对象指向。
- 三者第一个参数都是this要指向的对象,如果如果没有这个参数或参数为undefined或null,则默认指向全局window。
- 三者都可以传参,但是apply是数组,而call是参数列表,且apply和call是一次性传入参数,而bind可以分为多次传入。
- bind 是返回绑定this之后的函数,便于稍后调用;apply 、call 则是立即执行 。
【参考】
5分钟带你搞懂 Javascript 中的this(包含apply、call、bind)
4.对闭包的理解
概念
一个函数和对其周围状态(lexical environment,词法环境)的引用捆绑在一起(或者说函数被引用包围),这样的组合就是闭包(closure)。也就是说,闭包让你可以在一个内层函数中访问到其外层函数的作用域。在 JavaScript 中,每当创建一个函数,闭包就会在函数创建的同时被创建出来。
重点的一句:闭包让你可以在一个内层函数中访问到其外层函数的作用域。
使用场景
- return一个内部函数,读取内部函数的变量;
- 函数作为参数
- IIFE(自执行函数)
- 封装对象的私有对象和私有方法;
5.new 一个构造函数的过程
用new操作符创建对象时发生的事情:
- 创建新对象
- 新对象原型[[prototype]] = 构造函数prototype
- this 指向新对象
- 执行构造函数
- 如果构造函数返回非空对象,就返回这个对象引用,不然返回创建的新对象
函数调用时前面不加new就按普通函数来 执行。加new后对其进行了相应的变动, 按构造函数来执行。
new的具体过程如下:
//例子:
function Person(name,age) {
this.userName = name;
this.userAge = age;
}
var personl = new Person('LULU',20)
1、创建一个新的空对象。(即实例对象)
obj = {}
2 、设 置 原 型 链
将新对象obj的 __proto__属性指向构造函数的prototype对象。(即所有实例对象通过__proto__可以访问原型对象。构造函数的原型被其所有实例对象共享。)
obj.__proto__= Person.prototype
3 、将构造函数的this改指向新对象obj并执行函数代码。
(即给实例对象设置属性 userName, userAge并赋值。)
var result = Person.apply(obj,['LULU',20])
4 、如果构造函数中没有人为返回一个对象类型的值,则返回这个新对象obj。否则直接返回那个对象类型值。(一般定义的构造函数中不写返回值。)
if (typeof(result) == 'object') {
return result;
}else{
return obj;
}
【扩展】手动实现一个new操作,参数为构造函数 及其传参
//构造函数
function Person(name,age) {
this.userName = name;
this.userAge = age;
}
Person.prototype.sayAge = function(){
console.Iog(this.userAge)
}
// new操作函数newFun
function newFun() {
var obj = {};
Constructor = [].shift.call(arguments);
obj.__proto__ = Constructor.prototype;
var ret = Constructor.apply(obj, arguments);
return typeof ret === 'object' ? ret: obj;
};
//调用 newFun
var s1 = newFun(Person, 'xiaoyun',20);
console.log(s1.userName) //xiaoyun
console.log(s1.sayAge()) //20
备注:[].shift.call(arguments):删除并返回参数列表arguments中第一个参数,即获得构造函数。arguments剩余参数为构数传参。arguments是类数组,没有数组方法shift,可更改shift方法的this指向从而运用到 arguments 上。
6.ES6高阶函数有哪些
ES6中的高阶函数有以下几种:
- map:对数组中的每个元素进行操作,并返回一个新的数组。
- filter:根据指定的条件过滤出数组中的元素,并返回一个新的数组。
- reduce:对数组中的元素进行累加或累积操作,并返回一个结果。
- forEach:对数组中的每个元素进行操作,没有返回值。
- some:判断数组中是否存在满足指定条件的元素,返回布尔值。
- every:判断数组中的所有元素是否都满足指定条件,返回布尔值。
- find:查找数组中满足指定条件的第一个元素,并返回该元素。
- findIndex:查找数组中满足指定条件的第一个元素的索引,并返回该索引。
- sort:对数组中的元素进行排序。
- flat:将多维数组转换为一维数组。
- map、filter、reduce等方法的变种,如flatMap、find、findIndex等。
这些高阶函数可以使代码更简洁、可读性更高,并且提供了一种函数式编程的方式来操作数组。
Typescript
1.TS对比JS的优势有哪些
CSS
1.说下什么是BFC
BFC到底是什么东西
BFC 全称:Block Formatting Context, 名为 "块级格式化上下文"。
W3C官方解释为:BFC它决定了元素如何对其内容进行定位,以及与其它元素的关系和相互作用,当涉及到可视化布局时,Block Formatting Context提供了一个环境,HTML在这个环境中按照一定的规则进行布局。
简单来说就是,BFC是一个完全独立的空间(布局环境),让空间里的子元素不会影响到外面的布局。那么怎么使用BFC呢,BFC可以看做是一个CSS元素属性
怎样触发BFC
这里简单列举几个触发BFC使用的CSS属性
- overflow: hidden
- display: inline-block
- position: absolute
- position: fixed
- display: table-cell
- display: flex
【参考】
Vue
1.Vue2、3的模版编译渲染过程有哪些区别
2.虚拟Dom的优势是什么?性能一定更好吗?
虚拟 DOM (Virtual DOM )这个概念相信大家都不陌生,从 React 到 Vue ,虚拟 DOM 为这两个框架都带来了跨平台的能力(React-Native 和 Weex)
实际上它只是一层对真实DOM的抽象,以JavaScript 对象 (VNode 节点) 作为基础的树,用对象的属性来描述节点,最终可以通过一系列操作使这棵树映射到真实环境上
在Javascript对象中,虚拟DOM 表现为一个 Object对象。并且最少包含标签名 (tag)、属性 (attrs) 和子元素对象 (children) 三个属性,不同框架对这三个属性的名命可能会有差别
创建虚拟DOM就是为了更好将虚拟的节点渲染到页面视图中,所以虚拟DOM对象的节点与真实DOM的属性一一照应
优点:
保证性能下限: 框架的虚拟 DOM 需要适配任何上层 API 可能产生的操作,它的一些 DOM 操作的实现必须是普适的,所以它的性能并不是最优的;但是比起粗暴的 DOM 操作性能要好很多,因此框架的虚拟 DOM 至少可以保证在你不需要手动优化的情况下,依然可以提供还不错的性能,即保证性能的下限; 无需手动操作 DOM: 我们不再需要手动去操作 DOM,只需要写好 View-Model 的代码逻辑,框架会根据虚拟 DOM 和 数据双向绑定,帮我们以可预期的方式更新视图,极大提高我们的开发效率;
跨平台: 虚拟 DOM 本质上是 JavaScript 对象,而 DOM 与平台强相关,相比之下虚拟 DOM 可以进行更方便地跨平台操作,例如服务器渲染、weex 开发等等。
缺点:
无法进行极致优化: 虽然虚拟 DOM + 合理的优化,足以应对绝大部分应用的性能需求,但在一些性能要求极高的应用中虚拟 DOM 无法进行针对性的极致优化。
【参考资料】
面试官:什么是虚拟DOM?如何实现一个虚拟DOM?说说你的思路
3.Vue2和Vue3的响应式区别
- Vue2中响应式是通过defineProperty实现的
- Vue3中响应式是通过ES6的Proxy实现的
- Vue3中实现响应式数据的方法是ref和reactive
【扩展内容】 盘点 Vue3 与 Vue2 的区别
4.ref和reactive的区别
reactive特点
- reactive的参数一般是对象或者数组,他能够将复杂数据类型变为响应式数据。
- reactive的响应式是深层次的,底层本质是将传入的数据转换为Proxy对象
ref特点
- ref的参数一般是基本数据类型,也可以是对象类型
- 如果参数是对象类型,其实底层的本质还是reactive,系统会自动将ref转换为reactive,例如 ref(1) ===> reactive({value:1})
- 在模板中访问ref中的数据,系统会自动帮我们添加.value,在JS中访问ref中的数据,需要手动添加.value
- ref的底层原理同reactive一样,都是Proxy
5.Vue的双向数据绑定/响应式原理
【参考资料】
6.Vue2为什么不能监控到数组/对象的属性变更
首先Object.defineProperty是可以监听部分数组的更新动作的,但是由于它是通过遍历的方式来实现,当数组数据量小的时候没有问题,但是当数据量大的时候,处理上会出现明显的性能问题。也是出于“性能代价和获得的用户体验收益不成正比”的原因,所以Vue2没有去实现监听。Vue3的Proxy的劫持方式完美解决了这个问题。
7.说下Vue中的Slot,使用场景
使用场景
通过插槽可以让用户可以拓展组件,去更好地复用组件和对其做定制化处理
如果父组件在使用到一个复用组件的时候,获取这个组件在不同的地方有少量的更改,如果去重写组件是一件不明智的事情
通过slot插槽向组件内部指定位置传递内容,完成这个复用组件在不同场景的应用
比如布局组件、表格列、下拉选、弹框显示内容等
分类
slot可以分来以下三种:
- 默认插槽
具名插槽
- 子组件用name属性来表示插槽的名字,不传为默认插槽
作用域插槽
子组件在作用域上绑定属性来将子组件的信息传给父组件使用,这些属性会被挂在父组件v-slot接受的对象上。父组件中在使用时通过v-slot:(简写:#)获取子组件的信息,在内容中使用
没传footer插槽
来⾃⼦组件数据:{{slotProps.testProps}} 来⾃⼦组件数据:{{slotProps.testProps}}
小结:
- v-slot属性只能在
上使用,但在只有默认插槽时可以在组件标签上使用
默认插槽名为default,可以省略default直接写v-slot
- 缩写为#时不能不写参数,写成#default
- 可以通过解构获取v-slot={user},还可以重命名v-slot="{user: newName}"和定义默认值v-slot="{user = '默认值'}"
- v-slot属性只能在
【参考资料】
8.为什么data 返回一个函数
- vue中组件是用来复用的,为了防止data复用,将其定义为函数。
- vue组件中的data数据都应该是相互隔离,互不影响的,组件每复用一次,data数据就应该被复制一次,之后,当某一处复用的地方组件内data数据被改变时,其他复用地方组件的data数据不受影响,就需要通过data函数返回一个对象作为组件的状态。
- 当我们将组件中的data写成一个函数,数据以函数返回值形式定义,这样每复用一次组件,就会返回一份新的data,拥有自己的作用域,类似于给每个组件实例创建一个私有的数据空间,让各个组件实例维护各自的数据。
- 当我们组件的date单纯的写成对象形式,这些实例用的是同一个构造函数,由于JavaScript的特性所导致,所有的组件实例共用了一个data,就会造成一个变了全都会变的结果。
9.JSX有了解吗,和template的区别;
10.vue里面针对错误的生命周期函数使用
11.说下Vuex or Pinia的使用
12.Vue.set的原理
Vue.set是Vue.js提供的一个全局方法,用于在响应式对象上设置响应式属性。它的原理是通过调用Vue实例的内部方法defineReactive来为对象设置新属性,并使其成为响应式属性。
具体原理如下:
- Vue.set方法接受三个参数:对象、属性名和属性值。
- 首先,它会判断对象是否是响应式的,如果不是,则直接返回。
- 然后,它会判断属性是否已经存在于对象中,如果存在,则直接更新属性的值。
- 如果属性不存在于对象中,它会通过defineReactive方法将属性设置为响应式属性。
- defineReactive方法会为属性创建一个Dep实例对象,用于收集依赖和触发更新。
- 然后,它会使用Object.defineProperty方法将属性设置为响应式,并在getter和setter中触发依赖收集和更新。
- 最后,它会触发属性的更新,通知相关的Watcher进行视图的更新。
React
1.React中setState什么时候是同步,什么时候是异步
在React中,setState有时是同步的,有时是异步的。
当在React事件处理函数(如onClick)或生命周期方法(如componentDidMount)中调用setState时,setState是异步的。这是因为React会对连续的setState调用进行批处理,以提高性能。
例如,在以下代码中,setState是异步的:
class MyComponent extends React.Component {
handleClick() {
this.setState({ count: this.state.count + 1 });
console.log(this.state.count); // 输出的是更新前的值
}
render() {
return (
);
}
}
但是,当在setTimeout、Promise、原生事件监听器等异步场景中调用setState时,setState是同步的。
例如,在以下代码中,setState是同步的:
class MyComponent extends React.Component {
componentDidMount() {
setTimeout(() => {
this.setState({ count: this.state.count + 1 });
console.log(this.state.count); // 输出的是更新后的值
}, 1000);
}
render() {
return (
{this.state.count}
);
}
}
网络/浏览器
1.说下Http1.0和Http2.0的区别
HTTP 超文本传输协议是位于 TCP/IP 体系结构中的应用层协议,它是万维网数据通信的基础。
HTTP1.1 的缺陷
- 高延迟 — 队头阻塞(Head-Of-Line Blocking)
- 无状态特性 — 阻碍交互
- 明文传输 — 不安全性
- 不支持服务端推送
http2是HTTP协议的的第二个主要版本,使用于万维网。HTTP/2是HTTP协议自1999年HTTP 1.1发布后的首个更新,主要基于SPDY协议。和HTTP1的主要区别有:
- 1、http2采用二进制格式而非文本格式。
- 2、http2是完全多路复用的,而非有序并阻塞的。
- 3、http2采用了报头压缩,降低了成本。
- 4、http2让服务器可以主动将响应推送到客户端缓存中
HTTP2 的缺陷
- TCP 以及 TCP+TLS 建立连接的延时
- TCP 的队头阻塞并没有彻底解决
- 多路复用导致服务器压力上升
- 多路复用容易 Timeout
HTTP3是一个基于 UDP 协议的 QUIC 协议,它真正“完美”地解决了“队头阻塞”问题。
HTTP3的主要特点
- 改进的拥塞控制、可靠传输
- 快速握手
- 集成了 TLS 1.3 加密
- 多路复用
- 连接迁移
【参考资料】:
2.说下HTML渲染过程
- 构建 DOM树
-
- 将 HTML 解析成许多 Tokens
- 将 Tokens 解析成 Objects
- 将 Objects 组合成为一个 DOM 树
- 构建 CSSOM - CSS对象模型
-
- 解析 CSS 文件,并构建出一个 CSSOM 树(过程类似于 DOM 构建)
- 构建 Render树
-
- 结合 DOM 和 CSSOM 构建出一颗 Render 树
构建过程遵循以下步骤:
-
-
- 浏览器从 DOM 树开始,遍历每一个“可见”节点
- 对于每一个"可见"节点,在 CSSOM 上找到匹配的样式并应用
- 生成 Render Tree
-
- Layout 布局
-
- 计算出元素相对于 viewport 的相对位置
- Paint 渲染
-
- 将 Render 树转换成像素,显示在屏幕上
完整流程:
【扩展】JS文件引入对CRP的影响
- 解析 HTML 构建 DOM 时,遇到 JavaScript 会被阻塞;
- JavaScript 执行会被 CSSOM 构建阻塞,也就是说,JavaScript 必须等到 CSS 解析完成后才会执行(这只针对在头部放置