前端面经真题与解析1:字节/抖音/实习

收录于https://www.nowcoder.com/discuss/353159322232365056

一面:

1. 介绍项目

2. jwt介绍,实现


什么是jwt

JSON Web Token (JWT) 是一种用于身份验证和授权的加密字符串。它是一种轻量级的协议,用于在 Web 应用程序之间传输数据。JWT 包含以下三个主要部分:

  • header:令牌头部 (Header):包括 JWT 的元数据,如颁发者 (Issuer)、验证者 (Validator)、有效期 (Expiration) 等。

  • payload:令牌主体 (Body):包含实际数据,如用户信息、权限等。

  • Signature: 签名,使用对称加密算法或哈希算法对令牌主体进行加密,以便确保令牌的真实性和完整性。一般情况下,设置一个secretKey,对头部和主题的结构进行签名加密。如果前面两个部分的数据被篡改,只要secreKey没有泄漏,签名肯定会不一样,就可以检验出来。
    三部分以.进行拼接。


JWT 具有以下优点:

  • 轻量级:JWT 是一种轻量级协议,不包含大量数据,因此可以非常快速地传输。

  • 安全性:JWT 使用加密算法来确保令牌的安全性,防止未经授权的访问。

  • 跨平台支持:JWT 可以在不同平台上使用,包括 Web 应用程序、移动应用程序和服务器端应用程序。

  • 易于使用:JWT 具有简单而易于使用的 API,使开发人员可以轻松地集成 JWT 到他们的应用程序中。

  • 可以防护CSRF攻击。

  • 服务端无需保存会话信息。

JWT的缺点:

  • payload主题部分只是进行了简单的编码,不能存储敏感信息,和较大量的信息
  • 为了避免token被劫持,最好使用https协议。

JWT 广泛用于 Web 应用程序的身份验证和授权,例如登录、权限验证和 API 访问控制等。


jwt的实现

在nodejs中,可以使用npm或者yarn安装jwt-decodejsonwebtoken库来实现。

创建jwt

const jsonwebtoken = require('jsonwebtoken');  
const secret = 'my-secret-key';

const token = jsonwebtoken.sign({ id: '123' }, secret, { expiresIn: '1h' });  

验证jwt

try {
	const decodedBody = jwt.verify(token, secret);
} catch (e) {
}

此外,在koa中也可以使用koa-jwt中间件进行验证

/ 注意:放在路由前面
// 登录,注册, 公共api等一些路由可以设为白名单。
app.use(jwt({
  secret: 'test_token'
}).unless({ // 配置白名单
  path: [/\/api\/register/, /\/api\/login/]
}))

3. tcp与udp区别?应用场景

TCP(传输控制协议) 和 UDP(用户数据报协议) 是两种网络传输协议,它们在网络通信中扮演不同的角色和提供不同的服务。以下是它们的主要区别和应用场景:

  • 连接性和可靠性:TCP 是面向连接的协议,而 UDP 是无连接的协议。TCP 通过三次握手建立连接,并提供可靠的数据传输机制,确保数据的完整性和正确性,即使在数据传输过程中发生错误,TCP 也会自动重传数据,保证数据的完整性。UDP 则没有这些特性,它传输的数据不受连接影响,也不提供可靠的数据传输机制。

  • 流量控制:TCP 提供流量控制机制,可以防止过多的数据注入到网络中,使网络拥塞。而 UDP 没有提供流量控制机制。

  • 应用场景:TCP 适用于对数据完整性要求较高的应用场景,如文件传输、电子邮件等。UDP 适用于实时传输的应用场景,如语音、视频等。

  • 数据包大小:TCP 和 UDP 的数据包大小是没有限制的,但是过大的数据包容易导致网络拥塞,而过小的数据包又可能导致传输效率低下。

  • 网络拓扑:TCP 支持任何类型的网络拓扑,包括总线型、环型、星型、树型和网状型网络。而 UDP 仅支持总线型网络拓扑。

  • 总的来说,TCP 适用于对数据完整性和可靠性要求较高的应用场景,而 UDP 适用于实时传输的应用场景。在选择使用哪种协议时,需要根据具体的应用场景和需求进行综合考虑。

4. get与post区别,应用场景


get与post介绍

get请求:从指定的资源请求数据,用于获取数据,一般用于搜索排序和筛选之类的操作。

post请求:向指定的资源提交要被处理的数据,用于将数据发送给服务器,一般用于修改和写入数据。

get请求和post请求本质上就是TCP链接,并无差别。但是由于HTTP的规定和浏览器/服务器的限制,导致他们在应用过程中体现出一些不同。


post请求和get请求的区别

(1)post请求更安全(不会作为url的一部分,不会被缓存、保存在服务器日志、以及浏览器浏览记录中,get请求的是静态资源,则会缓存,如果是数据,则不会缓存)

(2)post请求发送的数据更大(get请求有url长度限制,http协议本身不限制,请求长度限制是由浏览器和web服务器决定和设置)

(3)post请求能发送更多的数据类型(get请求只能发送ASCII字符)

(4)传参方式不同(get请求参数通过url传递,post请求放在request body中传递)

(5)get请求产生一个TCP数据包;post请求产生两个TCP数据包(get请求,浏览器会把http header和data一并发送出去,服务器响应200返回数据;post请求,浏览器先发送header,服务器响应100 continue,浏览器再发送data,服务器响应200 返回数据)

注意:在发送 POST 的时候都没有带 Expect 头,server 也自然不会发 100 continue。


post请求的过程:

(1)浏览器请求tcp连接(第一次握手)

(2)服务器答应进行tcp连接(第二次握手)

(3)浏览器确认,并发送post请求头(第三次握手,这个报文比较小,所以http会在此时进行第一次数据发送)

(4)服务器返回100 Continue响应

(5)浏览器发送数据

(6)服务器返回200 OK响应


get请求的过程:

(1)浏览器请求tcp连接(第一次握手)

(2)服务器答应进行tcp连接(第二次握手)

(3)浏览器确认,并发送get请求头和数据(第三次握手,这个报文比较小,所以http会在此时进行第一次数据发送)

(4)服务器返回200 OK响应

网络环境好的情况下,发一次包和发两次包的时间差别基本可以忽略。而在网络环境差的情况下,两次包的TCP在验证数据包完整性上,有非常大的优点。


总结

除了 GET 和 POST 之外,HTTP 协议中还有许多其他请求方法,如HEAD、 PUT、DELETE、PATCH 等。这些请求方法都有不同的用途和特点,需要根据具体的情况选择合适的请求方法。
3.HEAD:和Get请求相一致,只不过不会返回响应体,这一方法可以再不必传输整个响应内容的情况下,就可以获取包含在响应小消息头中的元信息。(用于获取报头)

  • OPTIONS: 询问支持的请求方法。请求成功后,http头中包含一个名为“Allow”的头,值是支持的方法,如get,post,put等

  • HEAD: 类似于get,通常用来测试某个资源是否存在。

  • PUT:向指定资源位置上传最新的内容。和post很类似,但是put通常指定来资源存放的位置,而post则没有。

  • DELETE:请求服务器删除Request-URI所标识的资源

  • TRACE:(极少使用)回显服务器收到的请求,主要用于测试或诊断

  • CONNECT:(极少使用)HTTP/1.1协议中预留给能够将连接改为管道方式的代理服务器。

5. http缓存控制,协商缓存?

浏览器缓存策略可以分为强缓存和协商缓存。

  • 强缓存是指客户端向服务器发送的请求中包含了强缓存控制头,如“Cache-Control”和“Expires”等,服务器根据这些头中的信息来决定是否使用缓存数据。如果请求中包含了强缓存控制头,服务器将直接使用缓存数据,而不需要进行协商。

  • 协商缓存是指客户端向服务器发送的请求中不包含强缓存控制头,而是通过缓存协商的方式,由客户端和服务器之间进行协商,来确定是否使用缓存数据和缓存的有效期等信息。在协商缓存中,客户端会向服务器发送一个缓存请求,服务器根据请求中的信息来决定是否使用缓存数据,如果使用缓存数据,服务器也会返回一个缓存版本号给客户端,以便客户端判断是否需要更新缓存数据。

相比强缓存,协商缓存需要更多的网络交互和服务器资源,但是可以更好地利用缓存,避免缓存数据被重复使用,从而提高网站的性能和响应速度。

在实际应用中,强缓存和协商缓存可以结合使用,以实现更好的缓存控制效果。同时,需要注意缓存数据的安全和完整性,以避免缓存数据被篡改或失效。


强缓存:

服务器通知浏览器一个缓存时间,在缓存时间内,下次请求,直接用缓存,不在时间内,执行比较缓存策略。

  • 强缓存命中直接读取浏览器本地资源,在network中显示的是from memory 或者 from disk,如在微信小程序开发中,命中图片缓存,显示from disk。

  • 控制强缓存的字段有:Cache-Control(http1.1)和Expires(http1.0)

  • 协商缓存,让客户端与服务器之间能实现缓存文件是否更新的验证、提升缓存的复用率,将缓存信息中的Etag和Last-Modified
    通过请求发送给服务器,由服务器校验,返回304状态码时,浏览器直接使用缓存。

  • Cache-control是一个相对时间,用以表达自上次请求正确的资源之后的多少秒的时间段内缓存有效。

  • Expires是一个绝对时间。用以表达在这个时间点之前发起请求可以直接从浏览器中读取数据,而无需发起请求

  • Cache-Control的优先级比Expires的优先级高。前者的出现是为了解决Expires在浏览器时间被手动更改导致缓存判断错误的问题。
    如果同时存在则使用Cache-control。

强缓存-expires:

  • 优势特点
    1、HTTP 1.0 产物,可以在HTTP 1.0和1.1中使用,简单易用。
    2、以时刻标识失效时间。
  • 劣势问题
    1、时间是由服务器发送的(UTC),如果服务器时间和客户端时间存在不一致,可能会出现问题。
    2、存在版本问题,到期之前的修改客户端是不可知的。
    HTTP缓存都是从第二次请求开始的:

强缓存-cache-conche:

  • 优势特点
    1、HTTP 1.1 产物,以时间间隔标识失效时间,解决了Expires服务器和客户端相对时间的问题。
    2、比Expires多了很多选项设置。
  • 劣势问题
    1、存在版本问题,到期之前的修改客户端是不可知的。

协商缓存:

  • 协商缓存的状态码由服务器决策返回200或者304
  • 当浏览器的强缓存失效的时候或者请求头中设置了不走强缓存,并且在请求头中设置了If-Modified-Since 或者 If-None-Match 的时候,会将这两个属性值到服务端去验证是否命中协商缓存,如果命中了协商缓存,会返回 304 状态,加载浏览器缓存,并且响应头会设置 Last-Modified 或者 ETag 属性。
  • 对比缓存在请求数上和没有缓存是一致的,但如果是 304 的话,返回的仅仅是一个状态码而已,并没有实际的文件内容,因此 在响应体体积上的节省是它的优化点。
  • 协商缓存有 2 组字段(不是两个),控制协商缓存的字段有:Last-Modified/If-Modified-since(http1.0)和Etag/If-None-match(http1.1)
  • Last-Modified/If-Modified-since表示的是服务器的资源最后一次修改的时间;Etag/If-None-match表示的是服务器资源的唯一标
    识,只要资源变化,Etag就会重新生成。
  • Etag/If-None-match的优先级比Last-Modified/If-Modified-since高。

协商缓存-协商缓存-Last-Modified/If-Modified-since

  • 1.服务器通过 Last-Modified 字段告知客户端,资源最后一次被修改的时间,例如 Last-Modified: Mon, 10 Nov 2018 09:10:11 GMT
  • 2.浏览器将这个值和内容一起记录在缓存数据库中。
  • 3.下一次请求相同资源时时,浏览器从自己的缓存中找出“不确定是否过期的”缓存。因此在请求头中将上次的 Last-Modified 的值写入到请求头的 If-Modified-Since 字段
  • 4.服务器会将 If-Modified-Since 的值与 Last-Modified 字段进行对比。如果相等,则表示未修改,响应 304;反之,则表示修改了,响应 200 状态码,并返回数据。

优势特点

  • 1、不存在版本问题,每次请求都会去服务器进行校验。服务器对比最后修改时间如果相同则返回304,不同返回200以及资源内容。
    劣势问题
    1、只要资源修改,无论内容是否发生实质性的变化,都会将该资源返回客户端。例如周期性重写,这种情况下该资源包含的数据实际上一样的。
  • 2、以时刻作为标识,无法识别一秒内进行多次修改的情况。 如果资源更新的速度是秒以下单位,那么该缓存是不能被使用的,因为它的时间单位最低是秒。
  • 3、某些服务器不能精确的得到文件的最后修改时间。
  • 4.、如果文件是通过服务器动态生成的,那么该方法的更新时间永远是生成的时间,尽管文件可能没有变化,所以起不到缓存的作用。

协商缓存-Etag/If-None-match

为了解决上述问题,出现了一组新的字段 Etag 和 If-None-Match

Etag 存储的是文件的特殊标识(一般都是 hash 生成的),服务器存储着文件的 Etag 字段。之后的流程和 Last-Modified 一致,只是 Last-Modified 字段和它所表示的更新时间改变成了 Etag 字段和它所表示的文件 hash,把 If-Modified-Since 变成了 If-None-Match。服务器同样进行比较,命中返回 304, 不命中返回新资源和 200。

浏览器在发起请求时,服务器返回在Response header中返回请求资源的唯一标识。在下一次请求时,会将上一次返回的Etag值赋值给If-No-Matched并添加在Request Header中。服务器将浏览器传来的if-no-matched跟自己的本地的资源的ETag做对比,如果匹配,则返回304通知浏览器读取本地缓存,否则返回200和更新后的资源。
Etag 的优先级高于 Last-Modified。

优势特点

  • 1、可以更加精确的判断资源是否被修改,可以识别一秒内多次修改的情况。
  • 2、不存在版本问题,每次请求都回去服务器进行校验。
    劣势问题
  • 1、计算ETag值需要性能损耗。
  • 2、分布式服务器存储的情况下,计算ETag的算法如果不一样,会导致浏览器从一台服务器上获得页面内容后到另外一台服务器上进行验证时现ETag不匹配的情况。

第一次请求资源时,服务器返回资源,并在response header中回传资源的缓存策略;

第二次请求时,浏览器判断这些请求参数,击中强缓存就直接200,否则就把请求参数加到request header头中传给服务器,看是否击中协商缓存,击中则返回304,否则服务器会返回新的资源。


6. https如何保证安全性,加密方式和过程

HTTPS(Hyper Text Transfer Protocol Secure) 是一种加密的 HTTP 协议,用于在客户端和服务器之间进行安全通信。HTTPS 通过使用 SSL / TLS(Secure Sockets Layer / Transport Layer Security) 加密协议来保护数据传输过程中的隐私和安全。

下面是 HTTPS 保证安全性的加密过程:

1.客户端发起 HTTPS 请求。客户端向服务器发起一个 HTTPS 请求,请求的 URL 以 https:// 开头。

2.服务器发送证书。服务器向客户端发送数字证书,证书包含了服务器的公钥、域名等信息。

3.客户端验证证书。客户端验证证书的有效性,主要包括以下两个方面:一是验证证书是否由可信的第三方机构颁发;二是验证证书中包含的域名是否与当前请求的域名一致。

4.客户端生成随机密钥。客户端生成一个随机的对称密钥,并用服务器的公钥进行加密,然后将加密后的密钥发送给服务器。

5.服务器解密随机密钥。服务器使用自己的私钥解密客户端发送的密钥。

6.客户端和服务器使用密钥进行通信。客户端和服务器都拥有了相同的对称密钥,接下来的通信就可以使用该密钥进行加密和解密,保证通信的机密性和完整性。

通过以上过程,HTTPS 保证了客户端和服务器之间通信的安全性,防止了数据被窃取、篡改和伪装。

7. 跨域是什么,产生条件呢?在微信小程序中的应用

跨域和同源策略是 Web 开发中涉及到的基本概念,用于限制或保护 Web 应用程序的资源和数据访问。

同源策略是指 Web 浏览器对于不同源之间的交互采取的一种安全策略。同源指的是
1. HTTP 协议中的协议版本,2.域名,3.端口都相同,例如,http://example.com 和 http://example.com/ 是不同的源。浏览器会针对每个请求检查其来源,如果请求来源与当前页面的源不同,则拒绝请求,以避免恶意网站利用跨域请求盗取用户信息。

8. jsonp介绍

介绍
JSONP(JSON with Padding)是一种用于解决跨域请求的技术。在同源策略的限制下,浏览器通常不允许通过JavaScript从一个域名获取另一个域名的数据。然而,通过动态创建<script>标签来加载外部JavaScript文件的方式不受同源策略的限制,这为实现跨域请求提供了一个解决方案。

JSONP利用这个特性,通过在请求的URL中添加一个回调函数名的参数,服务器在返回数据时将数据作为参数传递给回调函数,并以可执行的JavaScript代码的形式返回。这样,客户端就能够接收到跨域的数据并进行处理。

下面是一个使用JSONP的示例:

function handleResponse(data) {
    // 处理接收到的数据
    console.log(data);
}

var script = document.createElement('script');
script.src = 'http://example.com/data?callback=handleResponse';
document.body.appendChild(script);

在上述代码中,我们通过动态创建一个<script> 标签设置其src属性为带有回调函数名的URL。服务器返回的响应会作为JavaScript代码执行,并调用指定的回调函数,将数据作为参数传递给它。

需要注意的是,JSONP存在一些安全风险。因为JSONP返回的数据是可执行的JavaScript代码,如果不进行严格的输入验证和过滤,可能会导致安全漏洞,如跨站脚本攻击(XSS)。因此,在使用JSONP时,必须确保从服务器返回的数据是可信的,并且进行适当的数据处理和验证。

举个例子
当使用JSONP进行跨域请求时,通常涉及两个角色:客户端(浏览器)和服务器。以下是一个详细的示例来说明JSONP的工作原理:

假设有两个域名:http://example.com 和 http://api.example.com。我们想要在 http://example.com 的页面上获取来自 http://api.example.com/data 的数据。

1.客户端代码(http://example.com 页面上的 JavaScript 代码):

function handleResponse(data) {
    console.log(data);
}

var script = document.createElement('script');
script.src = 'http://api.example.com/data?callback=handleResponse';
document.body.appendChild(script);

在客户端代码中,我们定义了一个名为handleResponse的回调函数。然后,我们动态创建了一个<script>标签,并将其src属性设置为http://api.example.com/data?callback=handleResponse,其中callback=handleResponse是用于指定回调函数的参数。

2.服务器端代码(http://api.example.com 上的服务器代码):
在服务器端,当接收到请求http://api.example.com/data?callback=handleResponse时,服务器将生成一个包含数据的响应,例如:

handleResponse({"name": "John", "age": 30});

服务器将上述响应作为JavaScript代码返回给客户端,浏览器将其解析并执行。由于服务器返回的是可执行的JavaScript代码客户端代码中定义的handleResponse函数将被调用,并且将响应数据作为参数传递给它。在此示例中,将在控制台中打印出**{“name”: “John”, “age”: 30}。**

这样,通过JSONP,我们成功地在http://example.com页面上获取了来自http://api.example.com的跨域数据。

需要注意的是,JSONP的安全性高度依赖于服务器端的实现。服务器应该确保只返回受信任的数据,并且对传递给回调函数的参数进行适当的验证和过滤,以防止潜在的安全漏洞。

9. 现场代码分析

/

10. 算法:力扣 剑指 Offer 09. 用两个栈实现队列

传送门

1.题目

用两个栈实现一个队列。队列的声明如下,请实现它的两个函数 appendTail 和 deleteHead ,分别完成在队列尾部插入整数和在队列头部删除整数的功能。(若队列中没有元素,deleteHead 操作返回 -1 )

示例 1:

输入:
[“CQueue”,“appendTail”,“deleteHead”,“deleteHead”,“deleteHead”]
[[],[3],[],[],[]]
输出:[null,null,3,-1,-1]
示例 2:

输入:
[“CQueue”,“deleteHead”,“appendTail”,“appendTail”,“deleteHead”,“deleteHead”]
[[],[],[5],[2],[],[]]
输出:[null,-1,null,null,5,2]
提示:

1 <= values <= 10000
最多会对 appendTail、deleteHead 进行 10000 次调用

2.解答

var CQueue = function() {
    this.stack1 = [],
    this.stack2 = []
};

/** 
 * @param {number} value
 * @return {void}
 */
CQueue.prototype.appendTail = function(value) {
    this.stack1.push(value);
};

/**
 * @return {number}
 */
CQueue.prototype.deleteHead = function() {
    if (this.stack2.length) {
        return this.stack2.pop();
    } else {
        while (this.stack1.length) {
            this.stack2.push(this.stack1.pop());
        }
        return this.stack2.pop() || -1;
    }
};

二面:

1. 介绍项目,看某个具体代码

2. 实习经历介绍

3.浏览器不同标签页面通信?同源,跨域情况

同源通信:

同源是指两个页面具有相同的协议(例如http或https),主机名和端口号。当标签页处于同源时,它们可以通过以下方法进行通信:

(1)window.postMessage:
这是一种安全的跨窗口通信机制,可以在不同标签页之间发送消息。一个标签页可以使用postMessage方法将消息发送给另一个标签页,并通过添加事件侦听器来接收消息。

示例代码:

// 发送消息的标签页
window.postMessage('Hello', 'https://www.example.com');

// 接收消息的标签页
window.addEventListener('message', function(event) {
  if (event.origin === 'https://www.example.com') {
    console.log('Received message:', event.data);
  }
});

该API接受两个参数,第一个参数是要传递的消息,第二个参数是目标窗口的origin(协议、域名和端口号)。

(2)共享本地存储:
标签页可以使用浏览器的本地存储(如LocalStorage或SessionStorage)来存储和读取数据。其他同源的标签页可以读取相同的存储,并实现数据共享。

示例代码:

// 在标签页1中存储数据
localStorage.setItem('sharedData', 'Hello from tab 1');

// 在标签页2中读取数据
var data = localStorage.getItem('sharedData');
console.log('Shared data:', data);

跨域通信:

当标签页处于不同源时,由于安全策略的限制,直接进行通信是不被允许的。以下是一些常用的跨域通信方法:
(1) JSONP(JSON with Padding):
JSONP允许在不同源的标签页之间获取数据。它通过动态创建<script>标签,将数据作为回调函数的参数返回到页面上。

function handleData(data) {
  console.log('Received data:', data);
}

var script = document.createElement('script');
script.src = 'https://www.example.com/data?callback=handleData';
document.body.appendChild(script);

(2) 跨文档消息(Cross-Document Messaging):
使用window.postMessage方法可以在跨域的标签页之间发送消息,前面的同源通信示例中有相关代码。

(3) WebSocket:
WebSocket是一种全双工通信协议,可以在浏览器和服务器之间建立持久性的连接。它不受同源策略的限制,因此可以用于跨域通信。

// 在标签页1中创建WebSocket连接
var socket = new WebSocket('wss://www.example.com/socket');

// 发送消息给服务器
socket.send('Hello from tab 1');

// 在标签页2中接收消息
socket.onmessage = function(event) {
  console.log('Received message:', event.data);
};

(4) CORS:
使用CORS(跨域资源共享)技术,可以在服务器端设置响应头,从而允许跨域访问。CORS技术需要服务器端支持,需要在服务器端设置相关的响应头才能生效。

4. localStorage和sessionStorage的区别

介绍及对比:

LocalStorage和SessionStorage是HTML5中提供的两种用于在浏览器端存储数据的机制,它们在以下几个方面有所不同:

(1)数据生命周期:

LocalStorage:数据在浏览器中始终保留,除非显式删除或清除浏览器缓存。
SessionStorage:数据仅在当前会话期间有效。当用户关闭标签页或浏览器时,数据将被删除。

(2)作用域:
LocalStorage:数据在同源的所有标签页和窗口之间共享。
SessionStorage:数据仅在同一标签页或窗口中共享。

(3)存储大小限制:
LocalStorage:通常可以存储较大量的数据(通常为几MB)。
SessionStorage:存储大小较小(通常为几十KB到几MB),因为它的生命周期较短,不需要长时间保存大量数据。

(4)数据访问方式:
LocalStorage和SessionStorage都使用JavaScript的localStorage和sessionStorage对象进行访问。
使用setItem(key, value)方法可以将数据存储到LocalStorage或SessionStorage中。
使用getItem(key)方法可以从LocalStorage或SessionStorage中获取存储的数据。
使用removeItem(key)方法可以删除LocalStorage或SessionStorage中的特定数据。
使用clear()方法可以清空LocalStorage或SessionStorage中的所有数据。
根据你的需求,选择LocalStorage还是SessionStorage取决于你需要在会话期间共享数据还是需要在不同会话之间持久保存数据。

使用方式:

localtorage使用代码:

// localstorage使用
localStorage.setItem('key', 'value'); //存储数据
var value = localStorage.getItem('key'); //获取特定数据
console.log(value); // 输出存储的值
localStorage.removeItem('key'); //删除特定数据
localStorage.clear(); //清空所有数据

sessionstorage使用同localstorage类似,函数名和参数相同。

需要注意的是,存储的数据都是以字符串形式存储的。如果需要存储对象或其他数据类型,可以使用JSON.stringify()进行序列化,然后使用JSON.parse()进行反序列化。

5. css实现三角形

<div class="triangle"></div>

方法一:使用边框

.triangle {
  width: 0;
  height: 0;
  border-left: 50px solid transparent;
  border-right: 50px solid transparent;
  border-bottom: 100px solid red;
}

方法二:使用伪元素

.triangle {
  position: relative;
  width: 100px;
  height: 100px;
}

.triangle::before {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  border-width: 0 100px 100px 0;
  border-style: solid;
  border-color: transparent red;
}

方法三:使用旋转

.triangle {
  width: 100px;
  height: 100px;
  background-color: red;
  transform: rotate(45deg);
}

以上这些方法都可以创建一个等腰直角三角形,可以根据需要调整宽度、高度、颜色和旋转角度等属性来实现不同样式的三角形。

6. css的position属性有哪些,区别是什么

7. 看输出,解释原因

this指向、臂包、作用域、宏任务、微任务

8. 查找json中的children路径

现有如下json(简化为对象),已知每个节点id唯一,编写findNode(id),返回路径,如findNode(5) 输出 1->4->5

{
  id: 1,
  children: [
    { id: 2, children: [{ id: 3, children: [] }] },
    {
      id: 4,
      children: [
        { id: 5, children: [] },
        { id: 6, children: [] },
      ],
    },
    { id: 7, children: [] },
  ],
};

9. 算法:合并有序数组,力扣88题

传送门

三面

1. 项目整体设计思路,逻辑架构

2. 前端是怎么学的

3. promise原理与实现

Promise是JavaScript中用于处理异步操作的一种机制。它通过提供一种更优雅、更易于理解和管理的方式来处理异步操作,避免了回调地狱的问题。Promise的原理基于一种称为**“承诺”**的概念,表示某个操作的最终结果(可能是一个值或错误)

Promise的基本原理可以通过以下步骤来理解:

(1)创建Promise对象:
使用new Promise()来创建一个Promise对象,并传入一个执行器函数(executor function),该函数接受两个参数:resolve和reject。

(2)执行器函数的执行:
执行器函数会立即执行,并传递给它两个回调函数:resolve和reject。你可以在执行器函数中进行一些异步操作,当异步操作完成时,调用resolve函数来表示操作成功并传递结果,或调用reject函数来表示操作失败并传递错误信息。

(3)状态的改变:
Promise有三种可能的状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)。当调用resolve函数时,Promise的状态会从pending变为fulfilled;当调用reject函数时,Promise的状态会从pending变为rejected。一旦Promise的状态改变,它就变为不可变的。

(4)处理结果:
可以通过Promise实例的then()方法来处理Promise的结果。then()方法接受两个参数:onFulfilled和onRejected,它们分别是在Promise成功或失败时被调用的回调函数。可以链式调用多个then()方法来处理多个异步操作。

一个简单的promise例子:

function asyncOperation() {
  return new Promise((resolve, reject) => {
    // 异步操作
    setTimeout(() => {
      const success = true;
      if (success) {
        resolve("Operation succeeded.");
      } else {
        reject("Operation failed.");
      }
    }, 1000);
  });
}

asyncOperation()
  .then((result) => {
    console.log(result); // 成功的结果
  })
  .catch((error) => {
    console.error(error); // 失败的错误信息
  });

在这个示例中,asyncOperation()函数返回一个Promise对象。当异步操作成功时,调用resolve()函数并传递结果;当异步操作失败时,调用reject()函数并传递错误信息。然后可以通过then()方法来处理成功的结果,或通过catch()方法来处理失败的情况。

需要注意的是,上述是一个简化的Promise实现示例,真正的Promise规范包含更多的细节和功能,例如Promise.all()、Promise.race()等方法,以及关于Promise链的处理等。

4. 已经有发布订阅模式了,为什么还要promise

虽然发布-订阅模式(Pub-Sub)和Promise都可以用于处理异步操作,但它们之间存在一些区别和适用场景上的差异。

发布-订阅模式(Pub-Sub)的特点:

  • 解耦:发布者(发布消息的一方)和订阅者(接收消息的一方)之间解耦,彼此不直接依赖。
  • 多对多:一个发布者可以有多个订阅者,一个订阅者可以同时订阅多个发布者。
  • 事件驱动:基于事件的模式,发布者通过发布事件来通知订阅者,订阅者通过订阅事件来接收通知。

Promise的特点:

  • 单向性:Promise是一种单向的处理方式,从异步操作到最终结果的流向是单一的。
  • 一次性结果:Promise只能处理一次结果,一旦Promise的状态改变,它的结果就确定了,无法再次修改。

Promise的优点:

  • 可链式调用:Promise提供了链式调用的语法,使异步操作的处理更加清晰和易于理解。
  • 异常处理:Promise具备异常处理机制,可以使用.catch()捕获并处理异步操作中的错误。
  • 并发控制:Promise提供了一些工具函数(如Promise.all()、Promise.race()等)来处理多个异步操作的并发和控制流程。

虽然发布-订阅模式可以用于处理事件和消息的发布和订阅,但Promise更适合用于处理单个异步操作和控制流程。它具有更清晰的链式调用语法和异常处理能力,适用于较为简单和线性的异步操作场景。当涉及到并发控制、依赖关系管理和复杂的异步流程时,Promise提供了更强大的功能和灵活性,使代码更易于维护和扩展。因此,在实际开发中,根据具体需求,可以灵活选择使用发布-订阅模式、Promise或它们的组合来处理异步操作。

发布订阅模式补充介绍:

在前端开发中,发布-订阅模式(Publish-Subscribe Pattern)是一种设计模式,用于解耦发布者和订阅者之间的关系。它建立在一种事件驱动的模式基础上,允许对象(发布者)发布事件并将其发送给多个对象(订阅者)进行处理。

在发布-订阅模式中,发布者和订阅者之间不存在直接的依赖关系,发布者只关注发布事件,而不需要知道具体有哪些订阅者在监听它的事件。订阅者只关注订阅感兴趣的事件,而不需要知道事件的来源。

下面是发布-订阅模式的几个核心角色:

  • 发布者(Publisher):负责发布事件。它提供了注册订阅者、发布事件和取消订阅等方法。
  • 订阅者(Subscriber):监听并处理感兴趣的事件。它提供了订阅事件、取消订阅和处理事件的回调函数等方法。
  • 调度中心(Event Bus):充当发布者和订阅者之间的中介,负责管理事件的订阅和分发。它维护了一个事件的订阅列表,将发布者发布的事件分发给所有订阅者。

发布-订阅模式的工作流程如下:

  • 发布者(Publisher)发布事件,将事件传递给调度中心。
  • 调度中心(Event Bus)接收到事件后,遍历订阅列表,将事件分发给所有订阅者。
  • 订阅者(Subscriber)收到事件后,执行相应的回调函数进行处理。

通过发布-订阅模式,可以实现组件之间的解耦和灵活性。多个订阅者可以独立订阅和处理感兴趣的事件,而发布者不需要关心具体有哪些订阅者存在,从而提高了代码的可维护性和扩展性。

在前端开发中,发布-订阅模式被广泛应用于事件系统、状态管理、消息传递等场景。常见的应用包括浏览器的DOM事件机制、Vue.js中的事件总线、Redux中的事件分发等。通过使用发布-订阅模式,可以构建出更灵活、可扩展的前端应用程序架构。

发布订阅模式简单实现

// 创建一个事件调度中心
const eventBus = {
  subscribers: {},
  // 订阅事件
  subscribe(event, callback) {
    if (!this.subscribers[event]) {
      this.subscribers[event] = [];
    }
    this.subscribers[event].push(callback);
  },
  // 发布事件
  publish(event, data) {
    if (this.subscribers[event]) {
      this.subscribers[event].forEach(callback => {
        callback(data);
      });
    }
  },
  // 取消订阅
  unsubscribe(event, callback) {
    if (this.subscribers[event]) {
      this.subscribers[event] = this.subscribers[event].filter(cb => cb !== callback);
    }
  }
};

// 订阅者1
function subscriber1(data) {
  console.log('Subscriber 1:', data);
}

// 订阅者2
function subscriber2(data) {
  console.log('Subscriber 2:', data);
}

// 订阅事件
eventBus.subscribe('event1', subscriber1);
eventBus.subscribe('event1', subscriber2);

// 发布事件
eventBus.publish('event1', 'Hello, World!');

// 取消订阅
eventBus.unsubscribe('event1', subscriber1);

// 再次发布事件
eventBus.publish('event1', 'Another message');

在这个例子中,我们创建了一个名为eventBus的事件调度中心对象。它具有subscribe、publish和unsubscribe方法,用于订阅事件、发布事件和取消订阅。

我们创建了两个订阅者subscriber1和subscriber2,它们是两个简单的函数,用于处理接收到的事件数据。

通过调用eventBus.subscribe方法,我们让订阅者订阅了event1事件。然后通过调用eventBus.publish方法,我们发布了一个event1事件,并传递了一条消息。订阅者函数会被调用,并打印相应的消息。

最后,我们使用eventBus.unsubscribe方法取消了subscriber1对event1事件的订阅。再次发布event1事件时,只有subscriber2会接收到消息。

这个简单的例子展示了如何使用自定义的事件调度中心来实现发布-订阅模式。在实际开发中,可以根据需求和复杂性,进一步优化和扩展该模式,以满足具体的业务需求。

5. 事件队列保存在哪里,执行栈呢

**在前端中,任务队列(Task Queue)和执行栈(Call Stack)是两个重要的概念,**它们分别用于管理异步任务和同步任务的执行顺序。

1.任务队列

任务队列保存在浏览器的内部,具体来说是在浏览器的 JavaScript 引擎中。JavaScript 引擎负责解析和执行 JavaScript 代码,并管理任务队列。

在浏览器环境中,任务队列通常被分为宏任务队列(Macro Task Queue)和微任务队列(Micro Task Queue),用于保存不同类型的任务。

  • 宏任务队列:
    宏任务队列保存了由浏览器提供的 API 触发的异步任务,比如 setTimeout、setInterval、XMLHttpRequest 等。当这些异步任务完成时,相关的回调函数会被添加到宏任务队列中等待执行。

  • 微任务队列:
    微任务队列保存了由 JavaScript 语言本身提供的异步任务,比如 Promise 的回调函数、MutationObserver 等。微任务队列中的任务会在当前宏任务执行结束后、渲染之前执行。

任务队列的**具体实现和细节由浏览器厂商提供的 JavaScript 引擎决定。**不同的浏览器可能会有不同的任务队列实现,但其目的都是为了管理和执行异步任务,保证任务的执行顺序和时机。

事件循环(Event LoopJavaScript 引擎中负责协调任务队列和执行栈的机制。它会持续监听任务队列和执行栈的状态,根据一定的规则将任务从任务队列中取出并推入执行栈中执行。

总结来说,任务队列保存在浏览器的 JavaScript 引擎中,不同类型的任务会被添加到不同的队列中,而事件循环负责管理任务队列和执行栈之间的交互,以确保任务按照正确的顺序被执行。

2. 执行栈:

执行栈(Execution Stack),也被称为调用栈(Call Stack),是在浏览器中的 JavaScript 引擎中保存的一种数据结构,用于追踪代码的执行位置和控制函数的调用顺序。

执行栈保存在浏览器的内存中,具体来说是在 JavaScript 引擎的运行时内存中。每当 JavaScript 代码执行时,执行栈会跟踪函数调用的层级和顺序,将函数调用帧(Execution Context)依次压入执行栈,以便在函数执行完毕后能够按正确的顺序返回到上一层调用。

执行栈是用来保存同步任务的栈结构,也称为调用栈。它是一种后进先出(LIFO)的数据结构。执行栈用于追踪代码的执行位置,每当调用一个函数时,该函数的调用帧(execution context)会被压入执行栈,函数执行结束后,调用帧会被弹出执行栈。

执行栈中的同步任务按照顺序执行,函数调用形成嵌套的执行上下文,其中包含了局部变量、参数和函数调用等信息。当所有同步任务执行完成后,执行栈将为空。

在事件循环(Event Loop)的机制下,任务队列和执行栈相互配合,实现了异步任务的执行和同步任务的顺序控制。事件循环不断地从任务队列中取出任务,将其放入执行栈中执行。当执行栈为空时,事件循环会检查微任务队列是否有任务,如果有则依次执行所有微任务,直到微任务队列为空。然后事件循环会再次检查宏任务队列,重复上述过程。

需要注意的是,任务队列和执行栈在不同的JavaScript运行环境下可能有一些差异,比如Node.js环境和浏览器环境可能存在不同的任务队列实现和任务处理策略。但它们的基本概念和工作原理是相似的。

解释2:
执行栈的主要作用是控制函数的调用顺序和追踪代码的执行位置。它在处理同步代码时起到关键作用,确保函数按照正确的顺序被执行,并能够正确返回到调用者。如果执行栈过深(调用层级过多),可能会导致栈溢出的错误。

需要注意的是,执行栈仅用于同步代码的执行。对于异步代码,如定时器回调、事件回调等,其执行顺序由事件循环(Event Loop)和任务队列(Task Queue)来管理。执行栈和任务队列共同协作,实现了 JavaScript 的事件驱动和异步执行机制。

总结来说,执行栈保存在 JavaScript 引擎的运行时内存中,用于追踪函数调用的层级和顺序。它的主要作用是控制同步代码的执行顺序和追踪代码的执行位置。


hr面:

为什么选择前端,学习方法,实习学习工作经历,读研吗,职业规划,实习时间,公司待遇

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值