解构的特殊情况
pipe(1, 2, 3, 4)
function pipe(...args) {
console.log(args); // [1,2,3,4]
}
function pipe(...args) {
console.log([...args]) // [1,2,3,4]
console.log(arguments)
// {
"0": 1,
"1": 2,
"2": 3,
"3": 4
}
console.log(arguments[0]) // 1
}
箭头函数什么时候可以省略return
() => a
等价于
() => {
return
}
只有当箭头函数的返回体只有一个单行表达式时,才可以省略return。其他情况不行。
reduce
reduce() 是 JavaScript Array 对象的一个内置方法,它对数组中的每个元素执行一个由您提供的reducer函数(累积器函数),结果是一个单一的返回值。这个方法会从前向后遍历数组的所有元素,逐个将它们与之前元素的累积结果相结合,直至处理完所有元素。
reduce() 方法常用于数组元素求和、拼接字符串、转换数组元素为对象等场景。
const aa = [1, 2, 3]
const b = aa.reduce((a, b) => a.push(b + 2), [])
console.log(b); //error
a.push(b + 2)实际上返回一个number, 而reduce的第二个参数是array。
reduce要求,第一个箭头函数返回的值的类型要跟第二个参数类型一致。所以应该返回
数组。之所以这样要求,是因为在第一轮,[]和a全等,导致后面的a也得是数组类型的。
const b = aa.reduce((a, b) => {
a.push(b + 2)
return a
}, [])
console.log(b); // [3,4,5]
console.log(aa);
- initialValue(可选):初始值(或累积值)。如果提供此参数,则在第一次调用回调函数时,accumulator 会是这个值;否则,accumulator 会是数组的第一个元素,currentValue 会是数组的第二个元素。
const aa = [1, 2, 3]
const b = aa.reduce((a, b) => console.log(a, b))
// 1, 2
// und, 3
// 第二次打印und,是因为reduce 函数的回调函数没有返回值,
// console.log(a, b) 是一个表达式,但它执行的结果并不是一个可以被累积的值。
// console.log 函数的作用是将信息输出到控制台,它的返回值实际上是 undefined,而不是打印的值。
// 如果没有明确的 return 语句,函数会默认返回 undefined。
如下:
function fn() {
console.log(3);
}
console.log(fn());
// 3
// und, 这个才是函数真正的返回值。
const aa = [1, 2, 3, 4]
const b = aa.reduce((a, b) => {
console.log(a, b)
return 13
})
// 1, 2
// 13, 3
// 13, 4
const aa = [1, 2, 3]
const b = aa.reduce((a, b) => a + b)
console.log(b); // 6
promise
async function tang() {
console.log(1);
await jia()
console.log(2);
}
async function jia() {
console.log(3);
console.log(3.5);
}
console.log(4);
tang()
new Promise((resolve, reject) => {
console.log(5);
resolve(6)
console.log(7);
}).then((e) => {
console.log(8);
console.log(e);
})
console.log(9);
// 4, 1, 3, 3.5, 5, 7, 9, 2, 8, 6
new Promise((resolve, reject) => {
console.log(1);
throw new Error('error') // 一旦抛出错误,当前作用域就不再继续执行了,
// 第六行也不会结束。然后怎么promise实际上是一个失败太,会去抓错误。
// 会打印第十行。
resolve(2)
}).then(a => {
console.log(a);
}, b => {
console.log(b);
})
new Promise((resolve, reject) => {
console.log(1);
setTimeout(() => {
throw new Error('error')
})
resolve(2)
}).then(a => {
console.log(a);
}, b => {
console.log(b);
}) // 1,2,error
当我想抓到上面代码中定时器里的错误,怎么抓
window.onerror = function(message, source, lineno, colno, error) {
console.error('Global error caught:', message, source, lineno, colno, error);
// 这里可以执行错误处理逻辑,比如记录错误信息等
return true; // 返回true以阻止默认的错误处理(弹出错误对话框)
};
await
async function a() {
console.log(1);
const a = await b() // 因为await。他会等b完全执行完成以后,看看情况
console.log(a); // und
}
async function b() {
console.log(3);
}
a() // 1,3,und
async function a() {
console.log(1);
const a = b()
console.log(a); // und
}
async function b() {
console.log(3);
} // 1,3, Promsie [fulfilled, undefined]
a()
async function a() {
console.log(1);
const a = b()
console.log(a); // und
}
async function b() {
console.log(3);
throw new Error('error')
} // 1,3, Promise [reject, Error: error at]
a()
async function a() {
console.log(1);
const a = await b()
console.log(a); // und
}
async function b() {
console.log(3);
throw new Error('error')
} // 1,3, Uncaught (in promise) Error: error
a()
这是因为
我可以理解成, 当调用async声明的函数,他的返回值就是一个promise。
前提条件:如果函数中没有任何 return 语句,或者 return 语句返回的是一个非 Promise 对象,那么这个 Promise 将会被解决(resolved)并带有该返回值。
那await操作符是不是就可以看作是then这个方法
一个固定的url对应的ip地址是固定的吗?
URL(Uniform Resource Locator)是互联网上资源的统一位置标识符,通常人们通过它来访问网站或网页,而实际的网络通信是基于IP地址进行的。
URL首先通过DNS(Domain Name System)系统将域名解析成IP地址。DNS服务器会返回与域名相关联的IP地址。这个IP地址可能是静态的(也称为固定IP),也就是说,只要域名所有者没有更改其DNS设置,并且ISP或托管服务提供商维持不变的情况下,同一域名通常会映射到同一个IP地址。
然而,在实际运营中,尤其对于大型网站和服务,为了负载均衡、故障转移、安全性和其他目的,可能会使用CDN(内容分发网络)或其他技术手段,导致URL对应的IP地址实际上可能是动态变化的。另外,如果网站使用了云服务或共享主机服务,它们的IP地址也可能因服务器迁移、维护等原因发生变化。
此外,一些网站可能采用DNS轮询、地理定位等技术,使得不同地区或不同时间访问同一个URL时,得到的是不同的IP地址。
负载均衡
客户端的流量首先会到达负载均衡服务器,由负载均衡服务器通过一定的调度算法将流量分发到不同的 应用服务器上面,同时负载均衡服务器也会对应用服务器做周期性的健康检查,当发现故障节点时便动 态的将节点从应用服务器集群中剔除,以此来保证应用的高可用。
负载均衡又分为四层负载均衡和七层负载均衡。
四层负载均衡工作在OSI模型的传输层,主要工作是转发,它在接收到客户端的流量以后通过修改数据包的地址信息将流量转发到应用服务器。
七层负载均衡工作在OSI模型的应用层,因为它需要解析应用层流量,所以七层负载均衡在接到客户端 的流量以后,还需要一个完整的TCP/IP协议栈。七层负载均衡会与客户端建立一条完整的连接并将应用 层的请求流量解析出来,再按照调度算法选择一个应用服务器,并与应用服务器建立另外一条连接将请 求发送过去,因此七层负载均衡的主要工作就是代理。
js闭包的场景
1,useState中,通过闭包的方式保存上一轮的值。
2,防抖节流。
3,函数柯里化
4,维护变量的状态
function createCounter() {
let count = 0;
return function () {
return count++;
};
}
const countFn = createCounter();
console.log(countFn()); // 输出0
console.log(countFn()); // 1
console.log(countFn()); // 2
组件化
组件化思想是一种现代软件开发范式,尤其是在前端开发领域得到广泛应用,它的核心理念是将一个复杂的应用程序或用户界面分割成一系列可重用、相互独立的部件或模块,这些模块通常称为“组件”(Components)。组件化思想不仅限于前端框架如Vue.js、React或Angular中,也在许多其他技术和领域中有体现。
在前端开发中,组件的具体表现形式包括HTML模板、CSS样式以及相关的行为逻辑(JavaScript),它封装了视图层的一部分或者一个完整的独立区块,如按钮、表单、卡片、导航菜单等。每个组件都有自身的数据、方法和生命周期,能够在不同的上下文中保持一致的表现和行为。
组件化的优点主要包括:
- 复用性:通过定义组件,可以在整个应用的不同位置重复使用相同的UI和功能,减少冗余代码。
- 可维护性:由于组件内部的逻辑相对独立,当需要修改或扩展特定功能时,只需要关注对应的组件即可,不会影响到其他组件或整个应用的稳定性。
- 可测试性:独立的组件易于进行单元测试,保证各个组件的质量。
- 模块化:组件间的关系清晰,可以通过组合不同组件来构建复杂的界面,便于团队分工合作和代码组织。
- 灵活性:通过组合不同组件,可以快速响应需求变化,轻松实现界面布局和功能调整。
对react进行优的策略,结合网络编译相关的
针对React应用的性能优化策略不仅限于运行时的组件渲染效率,还包括构建阶段和网络编译相关的优化措施。以下是一些建议,涵盖了从开发到部署过程中React应用性能优化的不同方面:
1. webpack相关
- 代码分割(Code Splitting) :通过Webpack或Rollup等构建工具自动将代码分割成多个小块,仅加载首屏所需的代码,其他部分在路由切换或交互触发时异步加载。
-
- chunk:
- 动态导入:利用import()函数实现按需加载,这样只有当组件或模块真正需要时才会下载和执行。
- Tree Shaking:
- scope hoisting(作用于提升):
- terser-webpack-plugin: 压缩和优化,包括删除空格、换行符和注释,以减小最终输出文件的大小
2. React懒加载
- React.lazy 和 Suspense:在React 16.6及以上版本,官方提供了React.lazy和SuspenseAPI,用于动态加载和呈现组件。
3. 性能优化API
- shouldComponentUpdate 或 React.memo:通过重写shouldComponentUpdate生命周期方法或者使用React.memo高阶组件来避免不必要的组件渲染。
- PureComponent 或 useMemo / useCallback:对于类组件,使用React.PureComponent替代React.Component以利用浅比较;对于函数组件,使用React.memo包裹组件,或者在函数组件中使用useMemo和useCallback Hook来避免计算昂贵的值或回调函数的重复创建。
4. Context API 和 Redux 优化
- 适当使用React Context API或Redux Middleware(如redux-thunk、redux-saga)来管理全局状态,同时注意避免不必要的状态变更和组件订阅。
5. 服务端渲染(SSR)和预渲染(PRerendering)
- 服务端渲染可以更快地为用户提供初始内容,并有利于SEO,预渲染则可以提前生成静态HTML文件,改善首屏加载速度。
6. 网络优化
- 使用CDN加速静态资源分发。
- HTTP/2或HTTP/3协议支持多路复用和头部压缩,减少网络延迟。
- 开启GZIP压缩,减小传输体积。
- 图片和其他媒体资源使用响应式加载和尺寸调整。
7. 性能监控与调试
- 使用React DevTools、Lighthouse等工具进行性能分析和审计,找出性能瓶颈并针对性优化。
8. 代码组织与模块化
- 前面提到的模块化处理,将React内容合理拆分成可复用的组件,尽量减少冗余代码和无用的子组件渲染。
综合运用上述策略可以显著提升React应用的性能表现,使其在网络编译、加载和运行阶段都能达到较好的效果。随着技术和工具的发展,持续关注新的最佳实践和优化方案也是非常重要的。
对于浏览器来讲,什么结构是一个进程,什么结构是一个线程 浏览器有哪些进程,线程
-
- JS引擎线程:负责解析和执行JavaScript代码,例如V8引擎在Chrome中所使用的线程。
- GUI渲染线程:负责渲染Web页面,包括HTML解析、样式计算、布局、绘制等,与JS引擎线程通常是互斥的,即同一时间只能有一个线程在工作。
- 事件触发线程:负责处理和分发各种事件,如鼠标点击、键盘输入、定时器触发等。
- 定时触发器线程:处理setTimeout和setInterval等定时任务。
- 网络线程:处理HTTP请求和响应等网络通信。
- 浏览器自身的其他线程,如垃圾回收线程、音频解码线程、Web Workers线程等。
综上所述,浏览器内部包含了多个进程,每个进程内部又包含多个线程来分别处理不同的任务。其中主要的浏览器进程包括:
- Browser(浏览器)进程:管理整个浏览器的用户界面和各渲染进程间的通信。
- 渲染进程(Renderer进程):每个标签页通常对应一个渲染进程,负责页面渲染及相关脚本执行。
- GPU进程:专门处理图形加速相关的工作,如3D CSS变换、WebGL内容绘制等。
- 插件进程:对于某些第三方插件,可能会各自运行在独立的进程中。
jsx如何转换的
在React中,JSX(JavaScript XML)是一种类似于XML的语法扩展,用于描述用户界面。虽然浏览器并不能直接理解和执行JSX,但它可以通过编译转换为普通的JavaScript代码,以便浏览器执行。这个转换过程主要依赖于编译工具,最常见的是Babel。
JSX转换过程大致如下:
- 预处理阶段:
-
- JSX中的标签会被识别并转换成一系列JavaScript表达式。
- 转换为 React.createElement() 调用:
-
- JSX中的每个元素会被转换为对React.createElement()函数的调用。例如,JSX片段 将会被转换为:
- 更复杂的嵌套结构也会相应地转换成嵌套的React.createElement()调用。
- 属性转换:
-
- JSX中的属性会转换为对象字面量的键值对,特殊属性(如className代替class)会被正确地转换。
- JSX表达式插值处理:
-
- 在JSX中嵌入的JavaScript表达式,如 {expression},会被提取出来并作为React.createElement()调用的参数传入。
- 类型检查:
-
- 在TypeScript环境中,编译器还会对JSX进行类型检查,确保类型安全。
- 优化:
-
- Babel以及其他编译工具还可能进行一些额外的优化,比如内联静态属性和方法,以便减小生成代码的大小和提升运行时性能。
React.createElement(MyComponent, {prop: "value"})
借助Babel等工具,开发人员可以编写简洁易读的JSX代码,同时确保最终生成的JavaScript代码能在浏览器中顺利执行。构建工具如Webpack等在构建流程中集成Babel转换步骤,确保源码中的JSX在上线前已被正确转换为浏览器可识别的JavaScript。
解释下微前端和微服务。
微前端和微服务是两种分别应用于前端架构和后端架构的设计模式,它们都是为了应对大规模复杂应用带来的挑战,通过模块化和独立部署的方式来提高系统的可维护性、扩展性和灵活性。
微前端(Micro Frontends) : 微前端是一种将前端应用拆分为多个较小、独立的前端应用,并将这些应用整合到一个主应用容器中的架构模式。每个微前端应用可以独立开发、测试、部署和维护,使用不同的技术栈,并且可以在运行时动态加载和组装。微前端的核心目标是将单体式的前端应用拆分为松耦合的组件,每个组件(或子应用)拥有自己的状态管理和路由,可以独立升级和扩展,降低了复杂度和风险。
例如,在微前端架构下,一个大型企业应用可以由多个团队独立开发各自的业务模块,每个模块作为一个微前端应用,通过约定的API或者框架集成到主应用中。
微服务(Microservices) : 微服务架构是一种将后端服务拆分为一组小的、互相独立的服务集合,每个服务围绕着单一业务功能进行构建,并且可以独立部署、水平扩展和更新。每个微服务都有自己的数据库和业务逻辑,服务之间通过API(如RESTful API)进行通信和协作。
微服务架构强调的是服务之间的松耦合和高内聚,每个服务专注于一个具体的业务场景,有助于提高开发速度、降低维护成本、增强扩展性和容错性。在微服务架构中,每个服务可以用不同的编程语言和技术栈实现,且可以根据需求独立地进行扩容或缩容。
总结来说,微前端关注的是浏览器端的前端应用拆分和集成,而微服务关注的是服务器端服务的拆分和集成。尽管它们各自关注不同的架构层面,但它们都体现了相同的设计原则,即通过模块化和解耦来提升复杂系统的管理和迭代效率。
微服务和微前端的选择并不是绝对的,它们解决的是不同层面的问题,且各有优劣。微服务和微前端的成本、速度等因素取决于具体的应用场景和业务需求,不能一概而论哪一个更简便或成本更低。
微服务主要用于后端服务架构,它的优势在于:
- 业务模块化:将复杂应用划分为多个小型服务,每个服务专注单一职责,便于理解和维护。
- 独立部署:每个服务可以独立部署,更新不影响其他服务,提高了部署速度和可靠性。
- 技术选型自由:每个服务可以采用最适合该业务领域的技术栈。
- 可扩展性:服务可以根据业务负载独立扩展,提高系统的弹性。
然而,微服务也有其复杂性:
- 分布式系统复杂性:需要处理服务间通信、数据一致性、服务发现、负载均衡等问题。
- 运维成本:多个服务意味着更多部署、监控和运维工作。
微前端则是针对前端应用架构,特点包括:
- 独立开发和部署:每个微前端应用可以独立开发、测试和部署,解决了大型单体前端应用难以维护的问题。
- 技术栈多样化:不同团队可以选择不同的前端框架和技术栈,减少技术冲突。
- 代码库管理:避免了单体应用中代码膨胀和耦合度过高的问题。
微前端同样存在挑战:
- 集成复杂性:需要解决多个前端应用之间的路由、样式隔离、通信等问题。
- 初始化加载:过多的微前端应用可能导致首次加载缓慢或资源重复。
所以,在决定是采用微服务还是微前端时,应考虑业务需求、团队能力、现有技术栈以及未来扩展计划等因素。有时候,微服务和微前端可以并存并相辅相成,共同实现系统的高性能和高可维护性。单纯比较两者哪个成本更低、速度更快并不准确,关键是看哪种架构更适合具体的项目背景和目标。