蔚来前端实习二面
第一次二面碰到小姐姐面试官,问的都不难,没有手撕。答的不太好,应该是寄了来判断一个值是否为 null。严格相等运算符不会进行类型转换,只有在值的类型和值本身都相同时才会返回 true。因此,可以通过以下方式来判断一个数据类型是否为 null:
function isNull(value) {
return value === null;
}
console.log(isNull(null)); // 输出 true
console.log(isNull(undefined)); // 输出 false
console.log(isNull(0)); // 输出 false
console.log(isNull("null")); // 输出 false
console.log(isNull({})); // 输出 false
上面的代码中,isNull 函数接受一个参数 value,如果 value 的值严格等于 null,则返回 true,否则返回 false。
3.事件循环
JavaScript 中的事件循环(Event Loop)是一种处理异步操作的机制,确保代码能够按照预期的顺序执行。事件循环是浏览器或 Node.js 运行时提供的一种机制,用于处理任务队列中的任务。
事件循环的基本原理如下:
-
执行栈(Execution Stack): JavaScript 引擎使用执行栈来管理代码的执行顺序。当代码被执行时,它会被添加到执行栈中,并按照先入后出(LIFO)的顺序执行。
-
任务队列(Task Queue): 任务队列是用来存储异步操作的队列。当异步操作完成后,它会被添加到任务队列中等待执行。
-
事件循环(Event Loop): 事件循环是一个持续运行的循环,负责检查执行栈和任务队列的状态。如果执行栈为空并且任务队列中有任务,事件循环会从任务队列中取出一个任务,将其添加到执行栈中执行。这个过程会一直重复,直到执行栈和任务队列都为空。
事件循环的过程可以简单描述为:
- 执行全局同步代码,将其添加到执行栈中执行。
- 当遇到异步操作时,将其放入任务队列中等待执行。
- 当执行栈为空时,事件循环从任务队列中取出任务,将其添加到执行栈中执行。
- 重复上述过程,直到执行栈和任务队列都为空。
JavaScript 中的异步操作包括定时器(setTimeout、setInterval)、事件监听(addEventListener)、Promise 等,它们会被添加到任务队列中等待执行。事件循环确保异步操作按照预期的顺序执行,并且不会阻塞主线程。
4.闭包的应用场景,除了返回函数其他的实现方法
闭包是指一个函数可以访问其外部函数作用域的变量,即使外部函数已经执行完毕。闭包在JavaScript中有许多应用场景,其中一些常见的包括:
- 私有变量和函数: 使用闭包可以创建私有变量和函数,这些变量和函数只能在闭包内部访问,外部无法直接访问。这种方式常见于模块模式和单例模式的实现中,可以隐藏内部实现细节,避免全局命名冲突。
function Counter() {
let count = 0;
return {
increment: function() {
count++;
},
decrement: function() {
count--;
},
getCount: function() {
return count;
}
};
}
const counter = Counter();
counter.increment();
console.log(counter.getCount()); // 输出:1
- 函数柯里化(Currying): 使用闭包可以实现函数柯里化,将一个多参数的函数转换成一系列接受单一参数的函数,从而可以更灵活地应用函数。
function add(x) {
return function(y) {
return x + y;
};
}
const add5 = add(5);
console.log(add5(3)); // 输出:8
- 事件处理函数: 在事件监听函数中,闭包可以访问定义时的变量,使得事件处理函数可以访问到事件触发时的上下文信息。
function handleClick() {
let message = 'Button clicked';
return function() {
alert(message);
};
}
const button = document.getElementById('myButton');
button.addEventListener('click', handleClick());
- 定时器和回调函数: 在异步编程中,闭包可以用于保存定时器或回调函数的上下文,以便在定时器或回调函数执行时访问定义时的变量。
function printNumbers() {
for (var i = 0; i < 5; i++) {
setTimeout(function() {
console.log(i);
}, 1000 * i);
}
}
printNumbers(); // 输出:5 5 5 5 5
以上是闭包的一些常见应用场景。除了返回函数外,还可以通过立即执行函数表达式(IIFE)等方式来创建闭包。
5.react组件什么时候重新渲染,事件流的方式
React组件重新渲染的时机取决于组件的状态或属性发生变化,以及父组件重新渲染导致子组件也需要重新渲染等情况。一般来说,以下情况会触发组件重新渲染:
-
状态(State)变化: 当组件的状态通过
setState()方法发生变化时,React 会重新渲染该组件及其子组件。 -
属性(Props)变化: 当组件的属性发生变化时,例如父组件传入的属性值发生改变,React 会重新渲染该组件。
-
Context 变化: 当使用 Context API 时,如果 Provider 的值发生变化,与之相连的 Consumer 组件也会重新渲染。
-
forceUpdate 方法调用: 使用
forceUpdate()方法可以强制组件重新渲染,不考虑组件的状态或属性是否发生变化。 -
父组件重新渲染: 如果父组件重新渲染,其子组件也会跟着重新渲染,除非子组件通过
shouldComponentUpdate或React.memo(函数式组件)进行了性能优化。
事件流的方式指的是事件在组件树中传递和触发的方式。React 中事件流主要分为两种:
-
合成事件(SyntheticEvent): React 封装了一套合成事件系统,使得事件处理函数的兼容性更好,并且可以屏蔽浏览器的差异性。合成事件是通过事件委托的方式,将事件绑定在顶层进行处理,然后通过冒泡机制逐层向下传递,直到目标元素。
-
事件冒泡(Event Bubbling): 在 React 中,事件会沿着组件树的层级结构向上传播,即从子组件向父组件传递。当子组件触发了一个事件时,如果父组件也监听了该事件,那么事件会依次传递给父组件,直到根组件或者事件被阻止。
React的事件系统使得事件处理更加高效和灵活,而事件冒泡机制则允许开发者在组件之间传递和共享状态,提供了一种有效的组件通信方式。
6.useContext,memo
useContext 和 memo 是 React 中的两个钩子函数,用于优化组件性能和简化组件间的状态传递。
- useContext:
useContext是 React 提供的钩子函数,用于在函数组件中访问 Context(上下文)对象。- Context 允许您将数据传递给组件树的所有组件,而不必在组件之间显式传递 props。
useContext接受一个 Context 对象(通过React.createContext创建),并返回该 Context 的当前值。- 当 Context 中的值发生变化时,使用
useContext的组件会重新渲染以反映最新的值。
import React, { useContext } from 'react';
import MyContext from './MyContext';
function MyComponent() {
const value = useContext(MyContext);
return <div>{value}</div>;
}
- memo:
memo是 React 提供的高阶组件,用于对函数组件进行记忆化(Memoization)处理,以提高性能。- 当函数组件的输入属性(props)未发生变化时,
memo会记住(缓存)上一次渲染的结果,并且在下一次渲染时,如果输入属性没有变化,则直接返回缓存的结果,避免重新渲染组件。 - 默认情况下,
memo只会对组件的 props 进行浅层比较(Shallow Comparison),如果需要更精细的比较,可以使用第二个参数进行自定义比较函数。
import React, { memo } from 'react';
function MyComponent(props) {
// Component logic...
}
export default memo(MyComponent);
总的来说,useContext 用于获取 Context 中的值,memo 用于优化函数组件的性能,避免不必要的重新渲染。在开发中,可以根据需要灵活地使用这两个钩子函数来提高 React 应用的性能和开发效率。
7.为什么要用状态管理库,主要解决了什么问题,和本地缓存的区别
状态管理库的主要作用是帮助管理应用程序中的状态(数据),以便在不同组件之间共享和管理状态,从而解决以下几个问题:
-
组件通信:在大型应用中,组件之间需要频繁地进行状态共享和通信。使用状态管理库可以方便地管理共享状态,避免了 props drilling(属性逐层传递)的问题,使组件之间的通信更加清晰和高效。
-
全局状态管理:某些状态可能需要在整个应用程序中共享,例如用户登录状态、主题样式等。使用状态管理库可以将这些全局状态统一管理,便于在任何组件中访问和修改。
-
状态持久化:有些状态需要持久化存储,以便在页面刷新或用户关闭应用后能够保持。状态管理库通常提供了与本地存储(如 localStorage 或 sessionStorage)集成的功能,方便实现状态的持久化存储和恢复。
-
应用状态调试:状态管理库通常提供了开发者工具,可以方便地查看当前应用程序的状态树、状态变化历史记录等,有助于开发和调试复杂的状态逻辑。
相比之下,本地缓存通常是指将数据存储在浏览器的本地存储(如 localStorage 或 sessionStorage)中,以便于在页面刷新或关闭后能够保持数据。本地缓存通常用于存储用户的偏好设置、表单数据等,而状态管理库则更专注于管理应用程序中的动态状态,如用户登录状态、购物车数据、主题样式等。状态管理库提供了更灵活、更强大的状态管理能力,可以更好地应对复杂的应用程序状态管理需求。
8.说一下发布订阅模式
发布-订阅模式(Publish-Subscribe Pattern)是一种常见的设计模式,用于解耦发布者和订阅者之间的关系。在该模式中,发布者(又称为主题、事件源)维护一个订阅者列表,当发布者的状态发生变化时,会通知所有订阅者。订阅者则通过订阅发布者来获取状态更新,并在接收到通知后执行相应的操作。
这种模式有以下几个核心组件:
- 发布者(Publisher):负责发布事件或状态更新,并维护订阅者列表。
- 订阅者(Subscriber):订阅发布者的事件或状态更新,并在收到通知时执行相应的操作。
- 事件(Event):发布者发出的通知,可以携带相关的数据或信息。
- 订阅(Subscription):订阅者与发布者之间的关联关系,表示订阅者对发布者的事件或状态的关注。
发布-订阅模式的优点包括:
- 解耦性:发布者和订阅者之间松耦合,彼此不需要直接通信,降低了系统的复杂度。
- 灵活性:可以动态地添加或移除订阅者,发布者不需要关心订阅者的具体实现。
- 可扩展性:可以轻松地添加新的发布者或订阅者,不会影响现有的组件。
一个常见的应用场景是前端的事件系统,例如浏览器中的DOM事件就是一种发布-订阅模式的实现。另外,在前端开发中,常常会使用事件总线(Event Bus)来实现发布-订阅模式,用于组件间的通信和状态管理。
9.redux和zustand比较一下
Redux和Zustand都是用于状态管理的库,它们在一些方面有相似之处,但也有一些区别:
-
复杂度:
- Redux是一个功能强大的状态管理库,提供了丰富的功能和灵活的架构,适用于大型和复杂的应用程序。
- Zustand更轻量级,简单易用,适用于中小型的应用程序,尤其是那些状态管理需求相对简单的项目。
-
API:
- Redux使用了比较复杂的API,包括Action、Reducer、Store等概念,需要一定的学习成本。
- Zustand的API非常简单,只需定义状态和操作状态的方法,非常直观和易于理解。
-
Immutability:
- Redux鼓励使用不可变数据来更新状态,通常需要使用Immutable.js等库来实现。
- Zustand不需要显式地处理不可变性,因为它内部使用了Immer库,可以直接在更新状态时进行可变操作。
-
性能:
- Redux使用了严格的数据流管理和中间件机制,可能在一些情况下会引入额外的性能开销。
- Zustand采用了更简洁的状态更新机制,性能可能更高一些,尤其在小型应用中表现更好。
-
生态系统:
- Redux拥有庞大而活跃的生态系统,有大量的插件、中间件和工具可供选择,适用于各种不同的场景。
- Zustand相对较新,生态系统相对较小,但仍然拥有一些扩展和周边工具,适用于简单的状态管理需求。
综上所述,Redux适用于复杂的应用程序,提供了丰富的功能和可扩展的架构,但学习成本较高;而Zustand更轻量级、简单易用,适合于小型应用或对状态管理要求不高的项目。选择合适的状态管理库应根据项目的规模、复杂度和开发团队的技术水平进行综合考量。
10.react项目可以做哪些优化
在React项目中,可以通过多种方式来优化性能和用户体验,以下是一些常见的优化方法:
-
组件级优化:
- 使用
React.memo对纯函数组件进行优化,减少不必要的重新渲染。 - 使用
shouldComponentUpdate或React.PureComponent对类组件进行性能优化。 - 将长列表组件使用虚拟滚动技术进行优化,如
react-window或react-virtualized。 - 避免在渲染函数内部创建新的对象或函数,使用
useMemo和useCallback来缓存值和函数。
- 使用
-
状态管理:
- 合理使用状态管理库,如Redux或MobX,对于大型应用程序有助于管理状态。
- 将全局状态分割成较小的片段,避免不必要的状态更新。
-
代码分割:
- 使用
React.lazy和Suspense组件对代码进行分割,按需加载页面和组件,减少初始加载时间。
- 使用
-
资源优化:
- 使用Webpack等打包工具进行代码分割和资源压缩,减小文件大小。
- 使用CDN加速静态资源加载,提高页面加载速度。
-
网络请求优化:
- 减少不必要的网络请求,合并和压缩静态资源。
- 使用HTTP缓存和CDN加速来优化资源加载速度。
-
性能监控和调优:
- 使用性能监控工具,如Chrome DevTools或React DevTools,识别和解决性能瓶颈。
- 对于大型应用程序,可以使用React Profiler来分析组件渲染性能。
-
渲染优化:
- 避免在
render方法中进行昂贵的计算或操作,尽量保持render方法的简洁和高效。 - 使用
React.StrictMode进行严格模式检查,帮助识别潜在的性能问题和不安全的操作。
- 避免在
-
SEO优化:
- 使用合适的HTML标记和语义化的结构,提高页面的可访问性和搜索引擎的识别性。
- 使用服务端渲染(SSR)或静态网站生成(SSG)技术来改善搜索引擎爬取效率。
通过综合运用以上优化方法,可以显著提升React项目的性能和用户体验。
11.usecallback缓存函数的目的是什么
useCallback Hook 的主要目的是在渲染过程中,避免创建新的回调函数实例,从而减少不必要的重新渲染。
在 React 中,当父组件的状态或属性发生变化时,子组件可能会重新渲染。如果子组件中存在依赖于父组件状态或属性的回调函数,并且每次重新渲染都会创建新的回调函数实例,这样就可能导致子组件的不必要重新渲染,尤其是当子组件是函数组件时。
通过使用 useCallback,可以确保在依赖不变的情况下,返回的回调函数实例也不会发生变化,从而避免不必要的重新渲染。useCallback 接收一个回调函数和一个依赖数组作为参数,只有当依赖数组中的值发生变化时,才会重新创建回调函数实例。
总而言之,useCallback 的缓存函数的目的是为了优化性能,减少不必要的组件重新渲染。
12.说说vue2中什么时候用到$set
在Vue.js 2中,当你需要在响应式对象中添加新的属性时,通常会使用$set方法。Vue.js在创建响应式对象时会对已经存在的属性进行响应化处理,但是对于动态添加的属性,Vue.js默认不会对其进行响应化处理。这时候就需要使用$set方法手动触发响应式更新。
下面是一个示例:
// 假设data中已经存在一个名为user的对象
this.$set(this.user, 'age', 25);
在上面的例子中,user对象已经存在于data中,但是age属性是动态添加的,因此需要使用$set来确保Vue.js能够监测到这个属性的变化并触发相应的更新。
需要注意的是,Vue.js 3 中已经没有了 $set 方法,因为 Vue.js 3 使用了 Proxy 来实现响应式,能够自动监听动态添加的属性。
13.说一个遇到的项目难点,怎么解决的
14.接触了解过ai吗,大模型,gpt,了解涉及什么 AI(人工智能)是指通过模拟人类智能过程的计算机系统,其中包括机器学习、深度学习、自然语言处理等技术。在AI领域,大模型是指参数量庞大的深度学习模型,通常包含数十亿甚至数百亿个参数。GPT(Generative Pre-trained Transformer)是一种常见的大型预训练模型,由OpenAI开发,用于自然语言处理任务,如文本生成、摘要生成、对话系统等。
涉及AI技术的应用包括但不限于:
- 自然语言处理(NLP):包括语音识别、语言翻译、情感分析等。
- 机器学习:用于模式识别、分类、回归等任务。
- 计算机视觉:包括图像识别、目标检测、人脸识别等。
- 自动驾驶:利用深度学习技术实现自动驾驶汽车。
- 医疗诊断:利用机器学习模型辅助医生进行疾病诊断和预测。
- 金融风控:利用机器学习模型进行风险评估和欺诈检测。
- 推荐系统:利用用户行为数据和机器学习算法进行个性化推荐。
- 游戏智能:开发具有智能行为的游戏NPC和对手。
- 智能物联网:利用机器学习技术实现智能家居、智能城市等场景。
AI技术的发展和应用对社会产生了深远的影响,涉及到了伦理、法律、隐私等方面的问题,需要综合考虑技术发展和社会影响,做出合理的决策和规范。
15.了解我们这边的业务吗
作者://鲨鱼辣椒
链接:www.nowcoder.com/discuss/594…
来源:牛客网