2022前端面试题总结

2022前端面试题整理持续补充中

  • js
      • JS为什么是单线程?
      • 什么是HTTPS
      • 什么是事件循环
      • toLocaleString() 方法
  • vue
      • Object.defineProperty方法(详解)
  • react
  • 其他相关问题
      • 什么是websocket
      • 线程和进程的概念

js

JS为什么是单线程?

什么是进程?
  进程:是cpu分配资源的最小单位;(是能拥有资源和独立运行的最小单位)

什么是线程?
  线程:是cpu调度的最小单位;(线程是建立在进程的基础上的一次程序运行单位,一个进程中可以有多个线程)

浏览器是多进程的
  放在浏览器中,每打开一个tab页面,其实就是新开了一个进程,在这个进程中,还有ui渲染线程,js引擎线程,http请求线程等。 所以,浏览器是一个多进程的。

大家都在说js是单线程的,但是为什么要设计成单线程?
  这主要和js的用途有关,js是作为浏览器的脚本语言,主要是实现用户与浏览器的交互,以及操作dom;这决定了它只能是单线程,否则会带来很复杂的同步问题。 举个例子:如果js被设计了多线程,如果有一个线程要修改一个dom元素,另一个线程要删除这个dom元素,此时浏览器就会一脸茫然,不知所措。所以,为了避免复杂性,从一诞生,JavaScript就是单线程,这已经成了这门语言的核心特征,将来也不会改变。
  为了利用多核CPU的计算能力,HTML5提出Web Worker标准,允许JavaScript脚本创建多个线程,但是子线程完全受主线程控制,且不得操作DOM。所以,这个新标准并没有改变JavaScript单线程的本质。

什么是HTTPS

HTTPS与HTTP一样都是应用层协议,与HTTP不同的是:HTTP的协议内容都是按照文本方式进行明文传输的,这导致在传输过程第三方者能够轻易获取传输的内容,而HTTPS在HTTP协议基础上引入一个加密以防止传输内容泄露或被篡改。
什么是‘加密’
加密就是指将明文(要传输的信息)按照指定的方式进行变换,生成密文。
解密就是指将密文按照指定的方式进行变换,还原成为明文。
在加密和解密的过程中,一般需要一种或者多个中间转换的数据,来辅助这过程的正常进行,这种数据被叫做密钥。
其作用就是为了防止他人获取其中的明文,造成隐患。

加密的方式有哪些
加密的方式有很多,但是在整体上分为两大类:对称加密和非对称加密。

对称加密
对称加密:指的是明文在加密和暗文在解密过程中都使用同一个密钥。
比如:一个简单的对称加密, 按位异或
明文a=1314,密钥key=1314,
那么密文为a^1314 =b,在解密密文过程中也使用异或运算b^1314就获得了原来的明文1314
当明文引入对称加密后,即使黑客入侵网络设备,让数据被截获,但不知道密钥是什么,也无法进行准确的解密。

但是:服务器在同一时刻会给不止一位的客户端提供服务,这些客户端与服务器的密文必须不一样否则就容易被黑客获取,因此一般的方法就是客户端在与服务器建立连接的时候就应该协商这次的密钥是什么。

注意:如果客户端直接将密钥进行明文传输给服务器,那么黑客也能获得密钥,那么后续的加密操作就没有任何用处了。因此,客户端在传输密钥的时候必须要给密钥进行加密。

非对称加密
非对称加密:一般使用两个密钥,一个是称为‘公钥’,另一个叫做‘私钥’。
公钥和私钥是配对的,其最大的缺点就是运算速度慢,比对称加密慢许多。
私钥就可以对密文解密也可以对明文进行加密,公钥也一样。一般情况来说客户端可以获得公钥,而服务器一般使用私钥。

当客户端拥有公钥时,向服务器发送密钥时步骤如下:

  1. 客户端中先用公钥将密钥进行加密形成密文,再将密文发送给服务器
  2. 服务器接收到密文后,通过私钥对密文进行解密从而获取客户端给的密钥
  3. 再将收到密钥的信息发给客户端,让客户端知道自己已经收到密钥了
  4. 之后客户端就使用自己生成的密文进行对称加密来传递明文

注意:对称加密的效率比非对称加密高很多, 因此只是在开始阶段协商密钥的时候使用非对称加密, 后续的传输仍然使用对称加密.

总结HTTPS传输过程

  1. 客户端先从服务器获取到证书,证书中包含公钥
  2. 客户端将证书进行校验
  3. 客户端生成一个对称密钥,用证书中的公钥进行加密,发送给服务器
  4. 服务器得到这个请求后用私钥进行解密,得到该密钥
  5. 客户端以后发出后续的请求,都使用这个对称密钥进行加密。
  6. 服务器收到这个密文也用这个密钥进行解密。

搬运文章,此篇更详细https://blog.csdn.net/weixin_49830664/article/details/122527783

什么是事件循环

JS 引擎是单线程的,直白来说就是一个时间点下 JS 引擎只能去做一件事情,而 Java 这种多线程语言,可以同时做几件事情。

JS 做的任务分为同步和异步两种,所谓 “异步”,简单说就是一个任务不是连续完成的,先执行第一段,等做好了准备,再回过头执行第二段,第二段也被叫做回调;同步则是连贯完成的。

像读取文件、网络请求这种任务属于异步任务:花费时间很长,但中间的操作不需要 JS 引擎自己完成,它只用等别人准备好了,把数据给他,他再继续执行回调部分。

如果没有特殊处理,JS 引擎在执行异步任务时,应该是存在等待的,不去做任何其他事情。用一个图来展示这个过程,可以看出,在执行异步任务时有大量的空闲时间被浪费。

实际上这是大多数多线程语言的处理办法。但对于 JS 这种单线程语言来说,这种长时间的空闲等待是不可接受的:遇到其他紧急任务,Java 可以再开一个线程去处理,JS 却只能忙等

在等待异步任务准备的同时,JS 引擎去执行其他同步任务,等到异步任务准备好了,再去执行回调。这种模式的优势显而易见,完成相同的任务,花费的时间大大减少,这种方式也被叫做非阻塞式。

而实现这个“通知”的,正是事件循环,把异步任务的回调部分交给事件循环,等时机合适交还给 JS 线程执行。事件循环并不是 JavaScript 首创的,它是计算机的一种运行机制。

事件循环是由一个队列组成的,异步任务的回调遵循先进先出,在 JS 引擎空闲时会一轮一轮地被取出,所以被叫做循环。

根据队列中任务的不同,分为宏任务和微任务。

宏任务和微任务

事件循环由宏任务和在执行宏任务期间产生的所有微任务组成。完成当下的宏任务后,会立刻执行所有在此期间入队的微任务。

这种设计是为了给紧急任务一个插队的机会,否则新入队的任务永远被放在队尾。区分了微任务和宏任务后,本轮循环中的微任务实际上就是在插队,这样微任务中所做的状态修改,在下一轮事件循环中也能得到同步。

常见的宏任务有:
script(整体代码)
setTimout
setInterval
setImmediate(node 独有)
requestAnimationFrame(浏览器独有)
IO
UI render(浏览器独有)
常见的微任务有:
process.nextTick(node 独有)
Promise.then()
Object.observe
MutationObserver

浏览器的事件循环

浏览器的事件循环由一个宏任务队列+多个微任务队列组成。

首先,执行第一个宏任务:全局 Script 脚本。产生的的宏任务和微任务进入各自的队列中。执行完 Script 后,把当前的微任务队列清空。完成一次事件循环。

接着再取出一个宏任务,同样把在此期间产生的回调入队。再把当前的微任务队列清空。以此往复。

宏任务队列只有一个,而每一个宏任务都有一个自己的微任务队列,每轮循环都是由一个宏任务+多个微任务组成

Node 的事件循环

node 的事件循环比浏览器复杂很多。由 6 个宏任务队列+6 个微任务队列组成
宏任务按照优先级从高到低依次是:
2022前端面试题总结_第1张图片
其执行规律是:在一个宏任务队列全部执行完毕后,去清空一次微任务队列,然后到下一个等级的宏任务队列,以此往复。

一个宏任务队列搭配一个微任务队列。六个等级的宏任务全部执行完成,才是一轮循环。

其中需要关注的是:Timers、Poll、Check 阶段,因为我们所写的代码大多属于这三个阶段。

1.Timers:定时器 setTimeout/setInterval;
2.Poll :获取新的 I/O 事件, 例如操作读取文件等;
3.Check:setImmediate 回调函数在这里执行;

除此之外,node 端微任务也有优先级先后:

1.process.nextTick;
2.promise.then 等;
清空微任务队列时,会先执行 process.nextTick,然后才是微任务队列中的其他
浏览器执行时是一个宏任务+一个微任务队列,而 node 是一整个宏任务队列 + 一个微任务队列。

node11.x 前后版本差异

node11.x 之前,其事件循环的规则就如上文所述:先取出完一整个宏任务队列中全部任务,然后执行一个微任务队列。

但在 11.x 之后,node 端的事件循环变得和浏览器类似:先执行一个宏任务,然后是一个微任务队列。但依然保留了宏任务队列和微任务队列的优先级。

搬运文章,此篇更详细https://tzy1997.com/articles/js0320ub/

toLocaleString() 方法

  1. 定义和用法
    可根据本地时间把 Date 对象转换为本地格字符串。
    可把一个 Number 对象转换为本地格字符串。
    可把一个 Array 对象转换为本地字符串。

注意:如果是 String 对象,那么 toLocaleString 的一切方法都对其不起作用,输出结果都是它本身。

let d = new Date()
console.log(d.toLocaleString()) // 2020/12/31 上午11:36:33

let num=12345678;
console.log(num.toLocaleString()); // 12,345,678

let arr = ['a', 'b', 'c'];
console.log(arr.toLocaleString()) // 'a', 'b', 'c'

let str = '124';
console.log(str.toLocaleString()) // '123'
console.log(str.toLocaleString('zh-u-nu-hanidec')) // '123'

  1. 参数
    object.toLocaleString([locales [, options]])

locales 参数用于指定格式化对象时使用的语言环境,默认为当前环境的语言,可以不传。

const date = new Date();

date.toLocaleString('zh');  // "2020/12/31 上午11:51:01"
date.toLocaleString('en'); // "12/31/2020, 11:51:01 AM"
date.toLocaleString('ja'); // "2020/12/31 11:51:01"

useGrouping 是否使用分组分隔符,如千位分隔符或千/万/亿分隔符。默认值是 true。

const num = 123.56;
num.toLocaleString('zh', { style: 'decimal' }); // "123.56"
num.toLocaleString('zh', { style: 'percent' }); // "12,356%"
num.toLocaleString('zh', { style: 'currency', currency: 'CNY' });  //"¥123.56"
num.toLocaleString('zh', { style: 'currency', currency: 'cny', currencyDisplay: 'code' }); //"CNY 123.56"
num.toLocaleString('zh', { style: 'currency', currency: 'cny', currencyDisplay: 'name' }); //"123.56人民币"     

简单列举参考文章链接: http://t.csdn.cn/4a1S0

vue

Object.defineProperty方法(详解)

Object.defineProperty这个方法有什么用呢?
这个方法接收三个参数:

1.属性所在的对象
2.属性的名字
3.一个描述符对象

这个描述符对象是个什么东西呢?
他可以是 数据属性:

1.configurable:表示能否通过delete删除属性从而重新定义属性,能否修改属性的特性,或者能否把属性修改为访问器属性,默认值为false。

2.enumerable:表示能否通过for in循环访问属性,默认值为false。

3.writable:表示能否修改属性的值。默认值为false。

4.value:包含这个属性的数据值。默认值为undefined。

方法使用:

Object.defineProperty(p1,"name",{
    configurable : false,
})
console.log(p1); //{ name: 'lisi' }
delete p1.name;
console.log(p1); //{ name: 'lisi' }

通过这个方法设置好configurable 这个属性,delete就不能把name属性给删除掉了。

Object.defineProperty(p1,"age",{
    writable :false,
    value : 15,
})
console.log(p1.age); //15
p1.age = 20;
console.log(p1.age); //15

然后我们给p1这个对象新加了一个age属性,并且设置成只读的。
这样我们就无法修改这个age属性了。
Object.defineProperty(p1,“age”,{
enumerable:false
})
for(var i in p1){
console.log(p1[i]);
} // lisi
在通过这个方法给enumerable设置为false,这样对象就不能通过迭代器遍历出age这个属性的值了。

通过这几个例子,我们基本那就了解Object.defineProperty这个方法的使用以及数据属性是做什么的了。
那该方法的第三个参数除了可以是数据属性,也可以是访问器属性。

1.get:在读取属性时调用的函数,默认值是undefined
2.set:在写入属性的时候调用的函数,默认值是undefined

var book = {
    _year : 2004,
    edition : 1
}

Object.defineProperty(book,"temp",{
    get: function(){
        return this._year
    },
    set: function(newYear){
        if(newYear > 2004){
            this._year = newYear;
            this.edition += newYear - 2004
        }
    }
})

book.temp= 2005;
console.log(book.edition); // 2
console.log(book._year); //2005

由于get方法返回_year的值,set方法通过计算来确定正确的版本。
因此把year的值设置为2005会导致edition的值变为2.

OK,关于Object.defineProperty方法的第三个参数的两种属性也就说完了。
参考文章链接:https://blog.csdn.net/weixin_46726346/article/details/115913752

react

其他相关问题

什么是websocket

WebSocket是HTML5下一种新的协议(websocket协议本质上是一个基于tcp的协议)
它实现了浏览器与服务器全双工通信,能更好的节省服务器资源和带宽并达到实时通讯的目的
Websocket是一个持久化的协议

websocket的原理
websocket约定了一个通信的规范,通过一个握手的机制,客户端和服务器之间能建立一个类似tcp的连接,从而方便它们之间的通信
在websocket出现之前,web交互一般是基于http协议的短连接或者长连接
websocket是一种全新的协议,不属于http无状态协议,协议名为"ws"

websocket与http的关系
相同点:

  1. 都是基于tcp的,都是可靠性传输协议
  2. 都是应用层协议

不同点:

  1. WebSocket是双向通信协议,模拟Socket协议,可以双向发送或接受信息
  2. HTTP是单向的
  3. WebSocket是需要浏览器和服务器握手进行建立连接的
  4. 而http是浏览器发起向服务器的连接,服务器预先并不知道这个连接

联系:

  1. WebSocket在建立握手时,数据是通过HTTP传输的。但是建立之后,在真正
  2. 传输时候是不需要HTTP协议的

总结(总体过程):

  • 首先,客户端发起http请求,经过3次握手后,建立起TCP连接;http请求里存放WebSocket支持的版本号等信息,如:Upgrade、Connection、WebSocket-Version等;
  • 然后,服务器收到客户端的握手请求后,同样采用HTTP协议回馈数据;
  • 最后,客户端收到连接成功的消息后,开始借助于TCP传输信道进行全双工通信。

websocket解决的问题

1.http存在的问题

  • http是一种无状态协议,每当一次会话完成后,服务端都不知道下一次的客户端是谁,需要每次知道对方是谁,才进行相应的响应,因此本身对于实时通讯就是一种极大的障碍
  • http协议采用一次请求,一次响应,每次请求和响应就携带有大量的header头,对于实时通讯来说,解析请求头也是需要一定的时间,因此,效率也更低下
  • 最重要的是,需要客户端主动发,服务端被动发,也就是一次请求,一次响应,不能实现主动发送

2.long poll(长轮询)

  • 对于以上情况就出现了http解决的第一个方法——长轮询
  • 基于http的特性,简单点说,就是客户端发起长轮询,如果服务端的数据没有发生变更,会 hold
    住请求,直到服务端的数据发生变化,或者等待一定时间超时才会返回。返回后,客户端又会立即再次发起下一次长轮询
  • 优点是解决了http不能实时更新的弊端,因为这个时间很短,发起请求即处理请求返回响应,实现了“伪·长连接”

张三取快递的例子,张三今天一定要取到快递,他就一直站在快递点,等待快递一到,立马取走

从例子上来看有个问题
假如有好多人一起在快递站等快递,那么这个地方是否足够大,(抽象解释:需要有很高的并发,同时有很多请求等待在这里)

总的来看:
推送延迟。服务端数据发生变更后,长轮询结束,立刻返回响应给客户端。
服务端压力。长轮询的间隔期一般很长,例如 30s、60s,并且服务端 hold 住连接不会消耗太多服务端资源。

3.Ajax轮询

  • 基于http的特性,简单点说,就是规定每隔一段时间就由客户端发起一次请求,查询有没有新消息,如果有,就返回,如果没有等待相同的时间间隔再次询问
  • 优点是解决了http不能实时更新的弊端,因为这个时间很短,发起请求即处理请求返回响应,把这个过程放大n倍,本质上还是request =
    response

举个形象的例子(假设张三今天有个快递快到了,但是张三忍耐不住,就每隔十分钟给快递员或者快递站打电话,询问快递到了没,每次快递员就说还没到,等到下午张三的快递到了,but,快递员不知道哪个电话是张三的,(可不是只有张三打电话,还有李四,王五),所以只能等张三打电话,才能通知他,你的快递到了)

从例子上来看有两个问题
假如说,张三打电话的时间间隔为10分钟,当他收到快递前最后一次打电话,快递员说没到,他刚挂掉电话,快递入库了(就是到了),那么等下一次时间到了,张三打电话知道快递到了,那么这样的通讯算不算实时通讯?很显然,不算,中间有十分钟的时间差,还不算给快递员打电话的等待时间(抽象的解释:每次request的请求时间间隔等同于十分钟,请求解析相当于等待)
假如说张三所在的小区每天要收很多快递,每个人都采取主动给快递员打电话的方式,那么快递员需要以多快的速度接到,其他人打电话占线也是问题(抽象解释:请求过多,服务端响应也会变慢)

总的来看,Ajax轮询存在的问题:

  1. 推送延迟。
  2. 服务端压力。配置一般不会发生变化,频繁的轮询会给服务端造成很大的压力。
  3. 推送延迟和服务端压力无法中和。降低轮询的间隔,延迟降低,压力增加;增加轮询的间隔,压力降低,延迟增高

4.websocket的改进

一旦WebSocket连接建立后,后续数据都以帧序列的形式传输。在客户端断开WebSocket连接或Server端中断连接前,不需要客户端和服务端重新发起连接请求。在海量并发及客户端与服务器交互负载流量大的情况下,极大的节省了网络带宽资源的消耗,有明显的性能优势,且客户端发送和接受消息是在同一个持久连接上发起,实现了“真·长链接”,实时性优势明显。

原文链接:https://blog.csdn.net/qq_54773998/article/details/123863493

线程和进程的概念

进程
进程是资源(CPU、内存等)分配的基本单位,它是程序执行时的一个实例。
程序运行时系统就会创建一个进程,并为它分配资源,然后把该进程放入进程就绪队列
进程调度器选中它的时候就会为它分配CPU时间,程序开始真正运行。

线程
线程是程序执行时的最小单位,它是进程的一个执行流,是CPU调度和分派的基本单位。
一个进程可以由很多个线程组成,线程间共享进程的所有资源,每个线程有自己的堆栈和局部变量。
线程由CPU独立调度执行,在多CPU环境下就允许多个线程同时运行。同样多线程也可以实现并发操作,每个请求分配一个线程来处理。

二者关系
线程和进程的关系是:线程是属于进程的,线程运行在进程空间内,同一进程所产生的线程共享同一内存空间,当进程退出时该进程所产生的线程都会被强制退出并清除。线程可与属于同一进程的其它线程共享进程所拥有的全部资源,但是其本身基本上不拥有系统资源,只拥有一点在运行中必不可少的信息(如程序计数器、一组寄存器和栈)。

你可能感兴趣的:(前端,javascript,开发语言)