前端面试题

目录

1.JS中数组查找元素的时间复杂度

2.从数组头部 弹出一个元素的时间复杂度是多少(就把下面这些时间复杂度都记一下)

修改数组 

访问数组

3.作用域和作用域链

4.cookie

5.后端怎么传cookie

注意事项

6.HTTP里面AES是对称加密还是非对称加密

7.HTTP网络安全协议有哪些(扩展)

8.vue2为什么不建议用箭头函数


1.JS中数组查找元素的时间复杂度

在JavaScript中,数组查找元素的时间复杂度主要取决于你使用的查找方法。以下是几种常见的查找方法及它们的时间复杂度:

  1. 线性查找(Linear Search)
    • 时间复杂度:O(n),其中n是数组的长度。
  2. 线性查找会遍历数组中的每个元素,直到找到目标元素或遍历完整个数组
function linearSearch(arr, target) {  
    for (let i = 0; i < arr.length; i++) {  
        if (arr[i] === target) {  
            return i; // 返回目标元素的索引  
        }  
    }  
    return -1; // 如果未找到目标元素,则返回-1  
}  
  
// 示例  
const arr = [3, 5, 7, 9, 11];  
const target = 7;  
console.log(linearSearch(arr, target)); // 输出: 2

indexof方法查找,indexof方法区分大小写

// 定义一个数组  
const fruits = ['apple', 'banana', 'cherry', 'date', 'elderberry'];  
  
// 使用 indexOf 方法查找元素 'banana'  
const index = fruits.indexOf('banana');  
  
// 输出索引位置  
console.log(index);
  1. 二分查找(Binary Search)(前提:数组已排序):
    • 时间复杂度:O(log n),其中n是数组的长度。
    • 二分查找通过不断地将搜索区间减半来查找目标元素,但它要求数组是有序的。
function binarySearch(arr, target) {  
    let left = 0;  
    let right = arr.length - 1;  
  
    while (left <= right) {  
        const mid = Math.floor((left + right) / 2);  
        if (arr[mid] === target) {  
            return mid; // 找到目标元素,返回其索引  
        } else if (arr[mid] < target) {  
            left = mid + 1; // 调整左边界  
        } else {  
            right = mid - 1; // 调整右边界  
        }  
    }  
    return -1; // 未找到目标元素,返回-1  
}  
  
// 示例(注意:数组需要是有序的)  
const sortedArr = [3, 5, 7, 9, 11];  
const target = 9;  
console.log(binarySearch(sortedArr, target)); // 输出: 3
  1. 哈希查找(Hashing)(如果数组元素被用作哈希表的键):
    • 理想情况下的时间复杂度:O(1)。哈希查找通过将键映射到哈希表的槽位上来快速定位元素。但是,如果哈希函数设计得不好或者哈希表发生了大量碰撞,则查找时间可能会增加。
    • 在最坏情况下的时间复杂度:可能退化到O(n),这通常发生在哈希表填充率过高且解决碰撞的策略不够高效时。
function hashSearch(hashMap, target) {  
    return hashMap[target] !== undefined ? hashMap[target] : -1; // 如果存在目标元素,则返回其值(或自定义的索引/信息),否则返回-1  
}  
  
// 示例  
const hashMap = {  
    'apple': 1,  
    'banana': 2,  
    'cherry': 3  
};  
const target = 'banana';  
console.log(hashSearch(hashMap, target)); // 输出: 2  
  
// 注意:这里的返回值是哈希表中目标键对应的值,而不是索引。如果你想要索引,可以在插入时自定义一个索引映射。

2.从数组头部 弹出一个元素的时间复杂度是多少(就把下面这些时间复杂度都记一下)

修改数组 
  1. push(参数1,参数2,……):在数组末尾增加一个或者多个元素
  2. pop():删除数组的最后一个元素
  3. unshift(参数1,参数2,……):在数组开头添加一个或者多个元素
  4. shift():删除数组的第一个元素,数组长度减1,无参数修改原数组

push和pop时间复杂度为O(1)unshift和shift时间复杂度为O(n)

访问数组

直接通过数组的索引访问数组的时间复杂度是O(1)

find() 方法的时间复杂度是 O(n)

3.作用域和作用域链

作用域分为全局作用域,局部作用域,块级作用域(ES6新增)

作用域链
当JavaScript引擎需要查找一个变量时,它会从当前作用域开始查找,如果没有找到,就会继续向上查找父级作用域,直到找到全局作用域。这个逐级向上查找的过程形成了一条作用域链。

4.cookie

https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Cookies

用户识别:Cookie 允许网站在用户的不同访问之间识别用户。当用户在浏览器中首次访问网站时,服务器可以在响应中包含一个 Set-Cookie 头部,该头部指示浏览器存储一个小段的数据(即 Cookie)。在随后的请求中,浏览器会在请求头部中包含这些 Cookie,这样服务器就可以知道这是来自同一个用户的请求。

node.js中cookie的使用

https://nodejs.org/dist/latest-v8.x/docs/api/http.html#http_response_setheader_name_value

在node.js里面

response.setHeader('Set-Cookie', ['type=ninja', 'language=javascript']);
Set-Cookie: <cookie-name>=<cookie-value>

这指示服务器发送标头告知客户端存储一对 cookie:

HTTP/1.0 200 OK
Content-type: text/html
Set-Cookie: yummy_cookie=choco
Set-Cookie: tasty_cookie=strawberry

[页面内容]

node.js标头

// returns content-type = text/plain
const server = http.createServer((req, res) => {
  res.setHeader('Content-Type', 'text/html');
  res.setHeader('X-Foo', 'bar');
  res.writeHead(200, { 'Content-Type': 'text/plain' });
  res.end('ok');
});

 会话管理:Cookie 常用于管理用户的会话。在用户登录网站后,服务器可以生成一个会话标识符(Session ID)并将其存储在 Cookie 中。之后,每当用户发送请求时,服务器就可以通过读取 Cookie 中的会话标识符来识别用户的会话,从而提供个性化的内容或服务。
个性化设置:网站可以利用 Cookie 存储用户的偏好设置,如语言选择、页面布局、字体大小等。这样,当用户再次访问网站时,网站可以根据 Cookie 中的信息自动调整页面设置,以满足用户的个性化需求。
跟踪用户行为:通过 Cookie,网站可以跟踪用户的浏览行为,包括用户访问的页面、停留的时间、点击的链接等。这些信息有助于网站分析用户的行为模式,从而优化网站结构和内容,提高用户体验。
跨站请求伪造(CSRF)防护:虽然 Cookie 本身不是用于 CSRF 防护的,但通过将 Cookie 与其他安全措施(如 CSRF 令牌)结合使用,可以提高网站的安全性。

5.后端怎么传cookie

 1. 创建Cookie对象
首先,你需要在后端代码中创建一个或多个Cookie对象。这些对象将包含你想要发送给客户端的信息,如用户ID、会话标识符等。

import javax.servlet.http.Cookie;  
import javax.servlet.http.HttpServletResponse;  
  
public class CookieHandler {  
    public void setCookie(HttpServletResponse response) {  
        // 创建一个Cookie对象  
        Cookie cookie = new Cookie("user", "John Doe");  
          
        // 设置Cookie的有效期(秒),例如设置为3600秒(1小时)  
        cookie.setMaxAge(3600);  
          
        // 设置Cookie的路径,这里设置为根目录,表示对整个网站有效  
        cookie.setPath("/");  
          
        // 将Cookie添加到HTTP响应中  
        response.addCookie(cookie);  
    }  
  
    // 如果需要传递多个Cookie  
    public void setCookies(HttpServletResponse response) {  
        Cookie cookie1 = new Cookie("user", "John Doe");  
        cookie1.setMaxAge(3600);  
        cookie1.setPath("/");  
  
        Cookie cookie2 = new Cookie("language", "en");  
        cookie2.setMaxAge(3600);  
        cookie2.setPath("/");  
  
        response.addCookie(cookie1);  
        response.addCookie(cookie2);  
    }  
}

2. 将Cookie添加到HTTP响应中
在创建了Cookie对象之后,你需要通过HTTP响应将这些对象发送给客户端。这通常是通过调用HttpServletResponse对象的addCookie(Cookie cookie)方法完成的。

注意事项

有效期:你可以通过setMaxAge(int expiry)方法设置Cookie的有效期(以秒为单位)。如果设置为0,则表示Cookie仅在当前浏览器会话中有效,浏览器关闭时Cookie将被删除。如果设置为负值,则表示该Cookie为临时Cookie,不会被存储在客户端的硬盘上,仅在当前浏览器会话中有效。
路径:通过setPath(String uri)方法可以设置Cookie的路径。默认情况下,Cookie的路径与创建它的请求的路径相同。但是,你可以将其设置为更通用的路径,比如网站的根目录,这样该Cookie就可以在整个网站中访问。
安全性:对于敏感信息,不建议直接存储在Cookie中。如果必须存储敏感信息,请考虑使用HTTPS来加密Cookie的传输,并设置Cookie的HttpOnly和Secure标志以提高安全性。
跨域问题:如果你正在处理跨域请求,并希望携带Cookie,你需要在发送请求时设置withCredentials为true(在使用如XMLHttpRequest或Fetch API时),并且服务器需要设置适当的CORS(跨源资源共享)头部以允许携带Cookie。

6.HTTP里面AES是对称加密还是非对称加密

在HTTP中,AES(Advanced Encryption Standard,高级加密标准)属于对称加密算法。

对称加密的特点
密钥相同:加密和解密使用同一个密钥。这意味着数据的发送方和接收方必须事先共享这个密钥,才能确保加密的数据能够被正确解密。
效率高:由于加密和解密使用相同的密钥,并且算法相对简单,因此对称加密的运算速度较快,适合处理大量数据的加密和解密。
安全性:虽然对称加密的密钥必须保密,但只要密钥不被泄露,加密的数据就是安全的。
AES算法
AES是目前广泛使用的对称加密算法之一,其加密强度高、运算速度快,并且有多种密钥长度可供选择(如128位、192位和256位)。AES算法被广泛应用于各种需要数据加密的场景,包括网络通信、数据存储等。

在HTTP中的应用
在HTTP通信中,由于HTTP本身不提供加密功能,因此通常需要使用其他机制来确保数据的安全性。HTTPS(HTTP Secure)就是HTTP的一个安全版本,它通过SSL/TLS协议来实现数据的加密传输。在SSL/TLS协议中,通常会结合使用对称加密和非对称加密来确保数据的安全性。具体来说,在通信开始时,双方会使用非对称加密算法(如RSA)来交换一个对称加密的密钥(如AES密钥),然后在后续的通信中,就使用这个对称密钥来进行数据的加密和解密。

7.HTTP网络安全协议有哪些(扩展)

1. HTTPS(HTTP Secure)
简介:HTTPS是HTTP的安全版本,通过在HTTP协议的基础上加入SSL(Secure Sockets Layer,安全套接层)或TLS(Transport Layer Security,传输层安全)协议来实现数据的加密传输和身份验证。
功能:
数据加密:通过SSL/TLS协议对数据进行加密,确保数据在传输过程中的机密性和完整性。
身份验证:通过数字证书对服务器进行身份验证,防止中间人攻击。
完整性校验:通过消息认证码(MAC)确保数据在传输过程中未被篡改。
默认端口:HTTPS默认使用端口443。
2. SSL/TLS
简介:SSL和TLS是用于在两个通信应用程序之间提供保密性和数据完整性的安全协议。SSL是早期的版本,而TLS是SSL的继任者,目前广泛使用的是TLS 1.2和TLS 1.3版本。
功能:
加密通信:使用公钥和私钥进行加密和解密,确保数据的机密性。
身份验证:通过数字证书验证通信双方的身份。
密钥协商:通过协议中的握手过程协商加密通信所需的密钥。
3. 数字证书
简介:数字证书是一种用于身份验证的电子文档,它包含了证书持有者的公钥信息、身份信息以及证书颁发机构的签名。
功能:
身份验证:验证通信双方的身份,防止中间人攻击。
密钥分发:通过数字证书分发公钥,确保加密通信的顺利进行。
4. HTTP/2
简介:HTTP/2是HTTP协议的最新版本,旨在提高网页的加载速度和性能。
安全相关功能:
头部压缩:减小了请求和响应的头部大小,减少了带宽消耗和加载时间,虽然不直接增加安全性,但提高了效率。
多路复用:允许多个请求和响应同时在单个连接上传输,减少了建立新连接的开销,提高了整体的安全性(因为减少了暴露于潜在攻击的时间)。
5. 其他安全协议和机制
HTTP Strict Transport Security (HSTS):要求浏览器仅通过HTTPS与服务器建立连接,防止中间人攻击。
内容安全策略 (CSP):减少跨站脚本(XSS)等攻击的风险。
HTTP认证:如基本认证(Basic Authentication)和摘要认证(Digest Authentication),虽然它们本身并不加密传输的数据,但可以提供一种身份验证机制。

8.vue2为什么不建议用箭头函数

箭头函数的this绑定特性

  • 箭头函数不绑定自己的this,而是继承自外围作用域的this值。
  • 这意味着,如果你在Vue组件的methods中使用箭头函数,那么这些函数中的this将不会指向Vue组件实例,而是指向定义这些箭头函数时所在的外围作用域(通常是Vue组件的methods对象本身,但在严格模式下,它可能是undefined)。

Vue 2中不建议使用箭头函数的具体原因

  • 无法访问组件实例的属性和方法:

由于箭头函数中的this不指向Vue组件实例,因此在箭头函数内部无法直接访问组件的data、computed属性、methods等。这会导致在尝试访问这些属性或方法时出现错误。

  • 生命周期钩子函数和选项中的函数:

在Vue 2中,生命周期钩子函数(如created、mounted等)以及配置选项中的函数(如watch、computed中的函数)都应该使用普通函数来定义,以确保this能够正确指向Vue组件实例。如果使用箭头函数,则会导致this指向错误,从而无法访问组件的属性和方法。

  • 代码可读性和维护性:

虽然这不是一个直接的技术原因,但使用箭头函数可能会降低Vue组件代码的可读性和维护性。因为Vue组件的开发者通常期望在组件的方法中能够直接通过this来访问组件的属性和方法,而箭头函数打破了这一期望。

9.TS相比JS的优点

类型安全性:TS的静态类型系统可以在编译时捕获潜在的类型错误,提前发现并修复问题,减少运行时错误和调试时间。
更好的代码可维护性:类型注解和类型推断提高了代码的可读性和可维护性,使得维护和重构代码更加容易。
面向对象编程支持:TS支持类和接口,以及泛型等面向对象编程的特性,使得代码更加结构化和可扩展。
更好的开发工具和IDE支持:由于TS提供了类型信息,IDE可以更好地进行代码补全、代码导航和错误提示等功能,提高开发效率。
新特性提前使用:TS支持最新的ECMAScript(ES)规范,包括ES6、ES7、ES8等,开发者可以更早地使用这些新特性。
渐进式采用:TS可以与现有的JS项目无缝集成,可以逐步将JS代码迁移到TS,而不需要一次性重写整个项目。
大型项目和团队开发:TS特别适用于大型项目和团队开发,通过提高代码质量和开发效率,促进团队之间的协作和代码复用。

10.xhr和fetch的区别

1. 定义与起源
XHR(XMLHttpRequest):是浏览器提供的原生API,最早由IE5引入,用于在JavaScript中异步地发送HTTP请求。它允许开发者在不刷新页面的情况下与服务器交换数据。
Fetch API:是ES6中新增的全局函数,提供了一种新的、现代的、基于Promise的异步网络请求方法。Fetch API的设计理念是为了解决XHR的一些缺陷,提供更简洁易用的API。
2. 使用方式与语法
XHR:使用XMLHttpRequest对象来处理请求和响应。开发者需要创建XHR对象,配置请求(如URL、方法、头部等),发送请求,并监听响应事件来处理数据。XHR的语法和实现比较复杂,需要编写一些回调函数来处理请求的各个阶段。
Fetch:基于Promise,返回一个Promise对象,使得异步编程更加简洁。Fetch的语法更简洁,通常只需要一行代码即可发起请求,并通过Promise的.then()和.catch()方法来处理响应和错误。Fetch还支持链式调用和多种解析方法(如.json()、.text()等)来处理响应数据。
3. 功能特性
Cookies处理:XHR请求会自动携带cookies,而Fetch默认不会携带cookies,需要手动设置credentials属性为'include'。
请求取消:XHR支持通过调用abort()方法来取消一个正在进行的请求,而Fetch原生不支持请求取消功能,但可以通过一些技巧(如使用AbortController)来实现。
进度监听:XHR可以通过监听onprogress事件来获取上传和下载的进度信息,而Fetch则不支持此功能。
响应类型处理:XHR的responseType属性可以设置响应的类型(如text、json、blob等),而Fetch需要手动解析响应数据(如使用.json()、.text()等方法)。
4. 兼容性
XHR:兼容性较好,支持旧版本的浏览器。
Fetch:主要是现代浏览器支持,但在一些旧版本的浏览器中可能无法使用。不过,随着浏览器的不断更新,Fetch的兼容性也在逐渐提高。
5. 错误处理
XHR:在出现错误时会reject Promise,无论是网络错误还是其他错误(如超时、abort等)。
Fetch:只会在遇到网络错误时reject Promise,其他错误(如解析错误)都需要开发者手动判断和处理。
6. 跨域请求
XHR:需要手动设置withCredentials属性来处理跨域请求中的cookies,同时支持CORS跨域请求的设置和JSONP实现。
Fetch:支持跨域请求,但判断更为严格,需要服务器进行CORS配置。Fetch API提供了更好的CORS支持,并且可以通过Promise的.then()和.catch()方法来处理跨域请求的成功和失败。

11.敏感信息的加密过程

1. 选择加密算法
在前端加密敏感信息时,常见的加密算法包括对称加密(如AES)和非对称加密(如RSA)两种。

对称加密:使用相同的密钥进行加密和解密,加解密速度快,但密钥的安全传输是挑战。
非对称加密:使用一对密钥(公钥和私钥),公钥用于加密,私钥用于解密。这种方式更安全,但加密解密速度相对较慢。
2. 生成密钥
对于对称加密,需要在前端和后端之间安全地生成和共享一个密钥。这个密钥需要足够复杂,以抵抗破解。
对于非对称加密,后端生成一对公钥和私钥,并将公钥安全地传递给前端。私钥则保存在后端,用于解密前端加密的数据。
3. 加密数据
前端在获取到密钥(对于对称加密)或公钥(对于非对称加密)后,使用这些密钥对用户输入的敏感信息(如密码、个人信息等)进行加密。加密过程通常遵循以下步骤:

数据输入:用户在前端页面输入敏感信息。
加密处理:使用选定的加密算法和密钥对输入的数据进行加密,生成密文。
传输密文:将加密后的密文通过HTTP请求发送给后端服务器。
4. 密钥的安全传输
对于对称加密,密钥的安全传输是关键。常见的做法是通过HTTPS等安全协议将密钥和初始向量(如果需要的话)从后端传输到前端。HTTPS协议可以确保数据传输过程中的安全,防止密钥被截获。

5. 后端解密和验证
后端接收到前端发送的加密数据后,使用对应的密钥进行解密操作。对于非对称加密,后端使用私钥对加密数据进行解密;对于对称加密,则使用之前生成的密钥进行解密。解密后,后端可以对解密出的数据进行进一步的处理和验证(如密码验证、数据完整性校验等)。

6. 注意事项
加密算法的选择:应根据具体的安全需求、性能要求和资源限制来选择合适的加密算法。
密钥的安全管理:密钥是加密过程的核心,必须妥善保管,防止泄露。
前端代码的安全性:前端代码可能被恶意用户查看或篡改,因此应确保前端加密代码的安全性,避免泄露密钥或加密算法的实现细节。
HTTPS协议的使用:在数据传输过程中使用HTTPS协议可以确保数据的安全性和完整性。

12.session存储什么时候会用到

用户认证和授权:当用户登录系统时,用户的登录信息(如用户名、角色等)可以存储在 Session 中。这样,每当用户访问受保护的资源时,系统都可以通过检查 Session 来验证用户的身份和权限。
购物车:在电子商务网站中,购物车中的商品信息通常存储在 Session 中。这是因为用户可能在不同页面之间浏览商品,并希望将这些商品添加到购物车中。通过 Session 存储,无论用户跳转到哪个页面,购物车中的商品信息都可以保持不变。
用户偏好设置:一些网站允许用户设置偏好(如语言、字体大小等)。这些偏好可以存储在 Session 中,以便在用户浏览网站时自动应用这些设置。
表单验证:在处理多步骤表单时,用户可能在填写过程中被重定向到另一个页面或由于某种原因中断了填写。通过 Session 存储表单数据,可以在用户返回时恢复他们的输入,提高用户体验。
会话管理:在需要跟踪用户会话的任何情况下,Session 存储都是必不可少的。它可以帮助应用程序了解用户的当前状态,并根据该状态做出相应的响应。
安全控制:除了用户认证和授权外,Session 存储还可以用于实施其他安全控制。例如,通过检查 Session 中的特定信息,可以限制对敏感资源的访问,或防止用户进行未经授权的操作。

13.vite为什么快

基于ES Modules:
Vite充分利用了ES Modules(ESM)的特性,通过原生的JavaScript模块机制进行开发。这种方式避免了传统打包工具中大量的模块重复打包、解析和编译等过程,从而显著加快了启动速度。
在开发模式下,Vite直接利用浏览器对ESM的原生支持,允许开发者直接在浏览器中运行未经打包的代码,进一步减少了开发环境中的构建时间。
按需编译:
Vite采用了按需编译的策略,即只有当请求某个模块时,才会对该模块进行编译。这种策略极大地减少了编译时间,因为它避免了编译和加载未使用的模块。
相比之下,传统的打包工具(如Webpack)在构建时会对整个项目进行扫描和分析,无论模块是否被使用,都会被打包进最终的输出文件中,这增加了构建的时间。
缓存机制:
Vite在开发过程中会缓存已经编译过的模块。当模块发生改动时,Vite只重新编译改动的模块,而不是重新编译整个项目。这种缓存机制有效地减少了不必要的重复编译,提高了开发效率和启动速度。
热模块替换(HMR):
Vite使用了微型的开发服务器,并在开发过程中启用了热模块替换(HMR)。这意味着当代码发生变化时,Vite可以实时更新页面,而无需刷新整个页面。这种即时的反馈机制减少了等待时间,加快了开发效率。
Esbuild预构建:
在生产模式下,Vite利用Esbuild进行预构建。Esbuild是一个使用Go语言编写的打包工具,其构建速度比以Node.js编写的打包器要快得多。通过Esbuild的预构建,Vite能够更快速地完成依赖的处理和优化,从而缩短构建时间。
网络优化
Vite还通过一系列网络优化手段来提升性能,如HTTP/2、DNS预解析、Preload等。这些优化手段有助于减少网络延迟和加载时间,进一步提高应用的响应速度和用户体验。

14.JS如何实现泛型

1. 使用函数重载(纯 JavaScript)
虽然这不是真正的泛型,但你可以通过函数重载来模拟对不同类型参数的处理。

function identity(arg) {  
    if (typeof arg === 'string') {  
        // 处理字符串  
        return arg.toUpperCase();  
    } else if (typeof arg === 'number') {  
        // 处理数字  
        return arg * 2;  
    }  
    // 其他情况  
    return arg;  
}  
  
console.log(identity("hello")); // HELLO  
console.log(identity(42)); // 84

2.使用类与构造函数

在 JavaScript 中,你可以通过构造函数来模拟泛型类的行为,但这也需要你在运行时检查类型或者使用类型断言(在 TypeScript 中)。

class GenericContainer {  
    constructor(value) {  
        this.value = value;  
    }  
  
    getValue() {  
        // 可以在这里根据 value 的类型来执行不同的逻辑  
        return this.value;  
    }  
}  
  
let stringContainer = new GenericContainer("Hello");  
let numberContainer = new GenericContainer(42);  
  
console.log(stringContainer.getValue()); // Hello  
console.log(numberContainer.getValue()); // 42  
  
// 如果需要更复杂的类型检查,可以添加类型守卫等方法

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值