B站前端实习一面
发个面经积攒人品
时长:40min
上来拷打项目,问的挺细的,好久没看了说的都不怎么全,甚至还有的说不上来。
然后是非常简单的几个八股
async/defer区别
async 和 defer 是用来控制脚本的加载和执行顺序的两个 HTML 属性。它们通常用于 <script> 标签中。
- async:
- 当浏览器遇到带有
async属性的脚本时,它会异步地下载该脚本,同时不阻止 HTML 解析。 - 下载完成后,脚本会立即执行,不管 HTML 是否解析完成。
- 多个带有
async属性的脚本,执行顺序不确定,谁先下载完成谁先执行。 - 适用于不需要按顺序执行的脚本,且不依赖页面的其他内容。
- 当浏览器遇到带有
<script src="script.js" async></script>
- defer:
- 当浏览器遇到带有
defer属性的脚本时,它会异步地下载该脚本,同时不阻止 HTML 解析。 - 脚本会在 HTML 解析完成后、
DOMContentLoaded事件触发之前执行,按照在页面中出现的顺序依次执行。 - 多个带有
defer属性的脚本,执行顺序与它们在页面中的顺序相同。 - 适用于需要按照顺序执行,但又不需要等待 HTML 解析完成的脚本。
- 当浏览器遇到带有
<script src="script.js" defer></script>
综上所述,async 和 defer 都用于异步加载脚本,但它们的执行时机和顺序略有不同,具体使用取决于脚本的依赖关系和执行顺序要求。
get/post区别
GET 和 POST 是 HTTP 协议中最常见的两种请求方法,它们在数据传输、安全性和语义上有一些区别。
-
传输方式:
- GET 请求通过 URL 参数传输数据,数据会附加在 URL 后面,可以在浏览器地址栏中看到。
- POST 请求通过请求体(Request Body)传输数据,数据不会暴露在 URL 中,而是在请求体中发送。
-
请求大小限制:
- GET 请求对 URL 的长度有限制,因为 URL 的长度有浏览器和服务器的限制,通常是 2KB 到 8KB 之间。
- POST 请求没有限制,可以发送大量数据,但服务器可能会设置请求体的大小限制。
-
安全性:
- GET 请求的数据会暴露在 URL 中,不适合发送敏感信息,例如密码等。
- POST 请求的数据在请求体中,不会暴露在 URL 中,更适合发送敏感信息。
-
幂等性:
- GET 请求是幂等的,即多次重复调用不会产生副作用。
- POST 请求不是幂等的,即多次重复调用可能会产生不同的结果。
-
缓存:
- GET 请求可以被缓存,适合获取静态资源等不会产生副作用的请求。
- POST 请求默认不会被缓存,因为可能会产生副作用,但可以通过设置 Cache-Control 头字段来实现缓存。
综上所述,GET 和 POST 请求在数据传输方式、大小限制、安全性、幂等性和缓存等方面有所不同,应根据实际需求选择合适的请求方法。通常情况下,GET 适合用于获取数据,POST 适合用于提交数据。
vue v-if和v-for
v-if 和 v-for 是 Vue.js 中两个常用的指令,用于在模板中控制元素的显示和循环渲染。
- v-if:
v-if是 Vue.js 提供的条件渲染指令,用于根据表达式的真假来决定是否渲染某个元素。- 当表达式为真时,元素被渲染到 DOM 中;当表达式为假时,元素从 DOM 中移除。
v-if可以单独使用或与v-else、v-else-if指令配合使用,实现复杂的条件判断。
<div v-if="isShow">显示内容</div>
- v-for:
v-for是 Vue.js 提供的列表渲染指令,用于根据数据源的内容重复渲染元素。- 可以使用
v-for遍历数组、对象或数字范围。 - 可以通过
v-for的第二个参数获取索引值。
<ul>
<li v-for="(item, index) in items" :key="index">{{ item }}</li>
</ul>
- v-if 和 v-for 的组合使用:
- 在同一个元素上,不推荐同时使用
v-if和v-for。 - 原因是
v-for的优先级比v-if高,每次都会先遍历数据,然后再根据条件进行渲染,可能会导致不必要的性能损耗。 - 如果需要根据条件过滤数据并循环渲染,可以使用计算属性或方法先处理数据,然后再在模板中使用
v-for。
- 在同一个元素上,不推荐同时使用
<ul>
<li v-for="item in filteredItems" :key="item.id">{{ item }}</li>
</ul>
computed: {
filteredItems() {
return this.items.filter(item => item.condition);
}
}
综上所述,v-if 用于条件渲染,根据表达式的真假来控制元素的显示与隐藏;v-for 用于列表渲染,根据数据源的内容重复渲染元素;在使用时应注意避免在同一元素上同时使用 v-if 和 v-for,并根据实际需求选择合适的指令。
css实现多行文本省略文字
要实现多行文本的文字溢出省略,可以使用 CSS 的 text-overflow 和 -webkit-line-clamp 属性。具体步骤如下:
- 设置容器的高度和样式,确保文本溢出时会产生省略号。
- 使用
-webkit-line-clamp属性限制文本显示的行数。 - 设置
overflow: hidden;和text-overflow: ellipsis;属性来隐藏溢出的文本并显示省略号。
下面是一个示例:
<style>
.text-container {
display: -webkit-box;
-webkit-box-orient: vertical;
overflow: hidden;
-webkit-line-clamp: 3; /* 显示行数 */
text-overflow: ellipsis;
max-height: 3em; /* 控制显示行数 */
}
</style>
<div class="text-container">
<!-- 这里放置要显示的文本 -->
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla nec dui eu velit lacinia bibendum.
</div>
在上面的示例中,.text-container 类将文本限制为显示三行,超出的部分会被省略号代替。请注意,-webkit-line-clamp 属性在某些浏览器中可能不被支持,因此使用时应注意浏览器兼容性。
问我会ts吗,我说了解,要实现一个类型,他的值类型为枚举类型的key,说了思路,应该是对了。
要实现一个类型,其值的类型为枚举类型的 key,可以使用 TypeScript 的 keyof 关键字。keyof 可以用于获取指定类型的所有键名,然后将其用作值的类型。
以下是一个示例:
enum MyEnum {
A = 'ValueA',
B = 'ValueB',
C = 'ValueC'
}
type MyType = keyof typeof MyEnum;
// MyType 的值只能是 MyEnum 的键名,即 'A' | 'B' | 'C'
let value: MyType = 'A'; // 正确
value = 'D'; // 错误,'D' 不是 MyEnum 的键名
在上面的示例中,MyType 类型的值只能是 MyEnum 枚举类型的键名,即 'A'、'B' 和 'C'。这样可以确保类型的值限制在枚举类型的键名范围内,从而提高代码的类型安全性。
最后手写promise.all
下面是一个简单的手写 Promise.all 的实现:
function customPromiseAll(promises) {
return new Promise((resolve, reject) => {
if (!Array.isArray(promises)) {
return reject(new TypeError('Promises must be an array'));
}
const results = [];
let completedCount = 0;
promises.forEach((promise, index) => {
Promise.resolve(promise)
.then(result => {
results[index] = result;
completedCount++;
if (completedCount === promises.length) {
resolve(results);
}
})
.catch(reject);
});
});
}
该实现的核心思想是:
- 创建一个新的 Promise 对象,用于包装所有传入的 Promise。
- 遍历传入的 Promise 数组,对每个 Promise 调用
Promise.resolve方法,确保将非 Promise 对象转换为 Promise 对象。 - 对每个 Promise 调用
then方法,将返回的结果存储到结果数组中,直到所有 Promise 都完成。 - 当所有 Promise 都完成时,将结果数组传递给外部 Promise 的
resolve方法。
这样就实现了一个简单的 Promise.all 方法,它能够等待所有的 Promise 都完成,并将所有 Promise 的结果组成一个数组返回。
反问:自认答的很烂,问了问学习建议,不过面试官说懂得还挺多的,可能需要深入某个点。一些问题忘了很正常,解决问题的时候知道咋解决就行。
总结:一些基础的八股没咋看,脑子卡壳答的像答辩,应该是挂了,寄。
作者:俺家猪有形
链接:www.nowcoder.com/feed/main/d…
来源:牛客网
腾讯前端实习一面_牛客网 (nowcoder.com)
2. token和 cookie
Token和Cookie都是用于在Web应用程序中进行身份验证和授权的常见机制,但它们有一些不同之处。
-
Token:
- Token是一种无状态的认证方式,通常被用于API的认证授权。
- 在Web应用中,常见的Token有JWT(JSON Web Token)和OAuth2 Token。
- Token通常存储在客户端,比如LocalStorage或SessionStorage中,或者通过HTTP请求的Header中进行传递。
- 服务器在接收到Token后,会解析Token中的信息,验证用户的身份和权限,并生成对应的响应。
-
Cookie:
- Cookie是一种用于在客户端和服务器之间传递信息的小型文本文件。
- Cookie通常用于存储会话信息、用户首选项、跟踪用户行为等。
- Cookie存储在客户端的浏览器中,每次向服务器发送请求时,都会自动将对应的Cookie信息包含在请求头中。
- Cookie有一些特性,比如过期时间、域名限制、路径限制等,可以通过设置这些特性来控制Cookie的行为。
总的来说,Token通常用于API的认证授权,适用于无状态的分布式系统,而Cookie则是传统的Web应用程序中常用的身份验证和会话管理机制。在实际开发中,可以根据具体需求和场景选择合适的认证授权方式。
4.如何使用原生JS绑定点击事件:addEventListener的第三个参数
addEventListener 方法的第三个参数是一个布尔值,用于指定事件是否在捕获阶段进行处理。它的作用是确定事件是在捕获阶段还是冒泡阶段触发。
如果该参数为 true,则表示事件在捕获阶段处理;如果为 false,则表示事件在冒泡阶段处理。通常情况下,我们使用冒泡阶段来处理事件,因为这样可以更灵活地控制事件的触发顺序和行为。
以下是一个示例:
// 获取元素
const myButton = document.getElementById('myButton');
// 添加点击事件监听器,事件在冒泡阶段进行处理
myButton.addEventListener('click', handleClick, false);
// 点击事件处理函数
function handleClick(event) {
console.log('Button clicked!');
}
在上面的示例中,addEventListener 方法的第三个参数设置为 false,表示事件在冒泡阶段进行处理。这是 addEventListener 方法的默认行为,因此在大多数情况下,我们可以省略第三个参数或直接传入 false。
事件捕获和事件冒泡
事件捕获和事件冒泡是事件传播的两种不同方式,它们描述了事件在DOM树中传播时的顺序和方向。
-
事件捕获:
- 在事件捕获阶段,事件从DOM树的根节点向目标元素传播。
- 事件捕获阶段的顺序是从外到内,即从根节点逐级向下直到目标元素。
- 在捕获阶段,事件处理程序是按照父元素到子元素的顺序触发的。
-
事件冒泡:
- 在事件冒泡阶段,事件从目标元素向DOM树的根节点传播。
- 事件冒泡阶段的顺序是从内到外,即从目标元素逐级向上直到根节点。
- 在冒泡阶段,事件处理程序是按照子元素到父元素的顺序触发的。
综上所述,事件捕获和事件冒泡描述了事件在DOM树中的不同传播方向,但在实际开发中,大多数情况下都使用事件冒泡阶段来处理事件,因为它更符合直觉,而且兼容性更好。事件捕获阶段在某些特定场景下可能有用,但使用相对较少。
防抖和节流
防抖(Debouncing)和节流(Throttling)是用于控制事件触发频率的两种常见技术,它们在实际开发中经常用于优化性能或避免不必要的资源浪费。
-
防抖(Debouncing):
- 防抖的基本思想是在事件触发后等待一段时间再执行回调函数,如果在等待时间内又有事件触发,则重新计时。
- 典型应用场景包括搜索框输入、滚动事件等,避免频繁触发事件而导致不必要的资源消耗。
- 实现方式是使用定时器,在事件触发后设定一个延迟时间,在延迟时间内如果再次触发事件,则取消之前的定时器并重新设定新的定时器。
-
节流(Throttling):
- 节流的基本思想是在一段时间内只允许触发一次事件回调函数,即无论事件触发多频繁,都在固定的时间间隔内执行一次回调函数。
- 典型应用场景包括滚动事件、resize事件等,控制事件触发频率,减少资源消耗。
- 实现方式是使用时间戳或定时器,在每次事件触发时判断当前时间与上次触发时间的间隔,如果间隔超过设定的阈值,则执行回调函数并更新上次触发时间。
综上所述,防抖和节流都是常用的性能优化技术,但它们适用于不同的场景,选择合适的技术取决于具体需求和事件特性。
js事件循环
JavaScript事件循环(Event Loop)是JavaScript引擎用于处理异步任务的一种机制,它负责维护一个事件队列(Event Queue)和一个调用栈(Call Stack),并按照一定规则从事件队列中取出任务执行。
事件循环的基本流程如下:
-
同步任务:JavaScript代码在执行过程中,同步任务会直接进入调用栈中执行。
-
异步任务:异步任务包括定时器回调、事件监听、Ajax请求等,它们不会立即执行,而是会被放入事件队列中等待执行。
-
事件循环:事件循环是一个持续的过程,不断地从事件队列中取出任务执行,直到事件队列为空。
具体的执行过程如下:
- 当调用栈为空时,事件循环会检查事件队列是否为空。
- 如果事件队列为空,则等待新的任务加入。
- 如果事件队列不为空,则取出一个任务并放入调用栈中执行。
- 执行完当前任务后,检查调用栈是否为空,如果不为空则继续取出下一个任务执行,否则回到第一步。
JavaScript事件循环的机制保证了异步任务的执行顺序和可靠性,同时确保了单线程的执行模型。在事件循环中,微任务(Microtask)会在每次执行栈清空后立即执行,而宏任务(Macrotask)则会等待当前执行栈清空后才执行。这样可以保证微任务先于宏任务执行,从而确保了一些关键的异步操作(比如Promise的then方法、DOM的更新等)在宏任务之前执行,提高了响应速度和用户体验。
7.http状态码。如果用户输入错误,后端
应该返回什么状态码 HTTP状态码是HTTP协议用于表示客户端与服务器之间请求-响应过程中的状态的数字代码。对于用户输入错误的情况,通常应返回以下几种状态码之一:
-
400 Bad Request:
- 表示客户端发送的请求存在语法错误,服务器无法理解。
-
401 Unauthorized:
- 表示客户端未经身份验证,需要进行身份验证后才能访问资源。
-
403 Forbidden:
- 表示服务器理解客户端的请求,但拒绝执行该请求,通常是因为客户端没有访问权限。
-
404 Not Found:
- 表示服务器未找到客户端请求的资源,通常用于表示客户端请求的URL地址不存在。
-
422 Unprocessable Entity:
- 表示服务器理解客户端发送的请求,但是请求中包含的数据格式正确,但无法处理,通常用于表示输入数据校验失败等情况。
具体选择哪种状态码取决于具体的业务逻辑和错误类型。通常情况下,应该选择与错误情况最匹配的状态码,以便客户端能够根据状态码更好地理解发生了什么错误,从而采取适当的处理措施。
vue和 react在循环渲染时绑定key有什么作用
在Vue和React中,循环渲染(例如使用v-for或map()函数)时绑定key的作用是帮助框架识别列表中的每个元素,从而在更新DOM时进行有效的重用、更新和删除。
具体作用包括:
-
提高性能:使用key可以帮助Vue和React更准确地识别出列表中的每个元素,从而在更新DOM时减少不必要的操作,提高渲染性能。
-
保持状态:通过key的唯一性,Vue和React可以更好地跟踪列表中各个元素的状态,确保在重新渲染时不会丢失用户输入、组件状态等重要信息。
-
避免重复渲染:有了唯一的key,Vue和React可以更容易地识别出哪些元素是新增的、哪些是更新的,避免重复渲染已存在的元素,提高渲染效率。
总的来说,绑定key可以帮助Vue和React更高效地管理列表中的元素,提高渲染性能和用户体验。因此,在进行循环渲染时,应该始终为列表中的每个元素提供一个唯一的key值。
讲讲redux在项目中的使用
Redux 是一种用于 JavaScript 应用程序的状态管理库,它可以帮助你管理应用程序的状态并使状态变化可预测。Redux 的核心概念是单一数据源和状态不可变性,它的使用通常包括以下几个方面:
-
单一数据源(Single Source of Truth):
- Redux 使用单一的 JavaScript 对象来存储整个应用程序的状态,称为状态树或存储(Store)。
- 这个状态树对整个应用程序是唯一的,任何一个组件都可以访问到整个应用程序的状态。
-
状态不可变性(Immutable State):
- Redux 要求状态是不可变的,即不能直接修改原始状态,而是通过创建新的状态对象来更新状态。
- 这样做的好处是可以追踪状态的变化,并且可以方便地实现时间旅行、状态回滚等功能。
-
Action:
- Action 是一个包含描述事件的普通 JavaScript 对象,它必须包含一个表示事件类型的
type属性。 - 通过派发(dispatch)Action,可以通知 Redux 应用程序状态发生了变化。
- Action 是一个包含描述事件的普通 JavaScript 对象,它必须包含一个表示事件类型的
-
Reducer:
- Reducer 是一个纯函数,它接收当前状态和一个 Action,并返回一个新的状态。
- Reducer 的作用是根据接收到的 Action 更新应用程序的状态。
-
Store:
- Store 是 Redux 应用程序的核心,它包含了应用程序的状态树和一些方法用于状态的管理。
- 通过创建 Store,可以让应用程序中的各个组件共享状态,并且可以订阅状态的变化。
-
Middleware:
- Middleware 是一个函数,它可以在派发 Action 和 Reducer 之间执行自定义的逻辑。
- Middleware 可以用于实现日志记录、异步操作、路由等功能。
在项目中使用 Redux 通常需要进行以下步骤:
- 定义 Action 类型和 Action 创建函数。
- 定义 Reducer 函数来处理各种 Action 类型。
- 创建 Store,并将 Reducer 和 Middleware 应用到 Store 中。
- 在组件中使用
connect方法连接 Redux Store,并通过mapStateToProps和mapDispatchToProps方法将组件的状态和操作与 Redux Store 关联起来。
总的来说,Redux 可以帮助你管理应用程序的状态并实现状态的可预测性,但在使用时需要考虑到其复杂性和适用性,避免过度使用。
10.useContext的使用 11.useEffect的使用 12.自定义hook 13.三道不太难的编程题目
10. useContext 的使用
useContext 是 React 提供的一个 Hook,用于在函数组件中获取上下文(Context)的值。它接收一个 Context 对象(通过 React.createContext 创建),并返回该 Context 的当前值。
import React, { useContext } from 'react';
// 创建一个 Context
const ThemeContext = React.createContext('light');
// 在父组件中提供 Context 的值
function App() {
return (
<ThemeContext.Provider value="dark">
<Toolbar />
</ThemeContext.Provider>
);
}
// 在子组件中使用 useContext 获取 Context 的值
function Toolbar() {
const theme = useContext(ThemeContext);
return <div>当前主题:{theme}</div>;
}
11. useEffect 的使用
useEffect 是 React 提供的一个 Hook,用于在函数组件中执行副作用操作(如数据获取、订阅、手动操作 DOM 等)。它在组件渲染完成后执行,默认情况下在每次渲染后都会执行,可以通过第二个参数来控制执行的条件。
import React, { useEffect, useState } from 'react';
function Example() {
const [count, setCount] = useState(0);
// useEffect 接收一个回调函数作为参数
useEffect(() => {
// 在组件渲染完成后执行的副作用操作
document.title = `你点击了 ${count} 次`;
}, [count]); // 依赖数组,只有当 count 改变时才执行副作用
return (
<div>
<p>你点击了 {count} 次</p>
<button onClick={() => setCount(count + 1)}>点击增加</button>
</div>
);
}
12. 自定义 Hook
自定义 Hook 是一个函数,其名称以 "use" 开头,可以在函数内部使用其他 Hook。它可以用来复用状态逻辑、副作用等。自定义 Hook 通常用来解决组件之间共享状态逻辑复用的问题。
import { useState, useEffect } from 'react';
function useFetch(url) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
async function fetchData() {
try {
const response = await fetch(url);
const result = await response.json();
setData(result);
setLoading(false);
} catch (error) {
console.error('Error fetching data:', error);
setLoading(false);
}
}
fetchData();
// 清除副作用
return () => {
setData(null);
setLoading(true);
};
}, [url]); // 依赖数组,只有当 url 改变时才重新请求数据
return { data, loading };
}
export default useFetch;
使用自定义 Hook:
import React from 'react';
import useFetch from './useFetch';
function MyComponent() {
const { data, loading } = useFetch('https://api.example.com/data');
if (loading) {
return <div>Loading...</div>;
}
return <div>{JSON.stringify(data)}</div>;
}
export default MyComponent;
通过自定义 Hook,可以将逻辑与 UI 分离,提高代码的复用性和可维护性。