# lesson_zp **Repository Path**: liu-xiangping666/lesson_zp ## Basic Information - **Project Name**: lesson_zp - **Description**: AI+全栈学习仓库 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 19 - **Forks**: 0 - **Created**: 2025-10-23 - **Last Updated**: 2026-04-22 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 面试题 ## 自我介绍 - 破冰\占据主动权 1.详细介绍自己 Who What 面试官,您好!我是郑志鹏,来自东华理工大学软件学院的一名大三学生,正在找一份AI全栈开发或前端实习工作! 2.你是怎么学习的? Why? 在大学期间,接触到了学院的计算机协会,跟着学长一起学习JavaScript,阅读了《你不知道的JavaScript》,并在掘金等社区撰写了一些技术类的博客,目前4级了。LeetCode 刷了100+,数据结构等课程学的还挺好的。熟悉的前端框架有react/vue,较深入的理解了react的原理,目前在学习react源码,hooks,状态管理,react 全栈有项目实战经验。Vue 也写过AIGC类的小项目。开发中使用TypeScript,后端最近用nest.js和postgre 写了一个全栈项目。最近在focus AI Agent项目开发。 3.你怎么理解AI 最近的一两年我都ALL in AI,在B站上看过一些vibe coding的博主,了解ONE person company理念。对AI创新,mvp最小可执行产品试验感兴趣。一直使用trae/cursor 等编程Agent 高效开发。熟练掌握llm、tools、mcp、rag、n8n、coze 等AI 技术,熟悉langchain框架 Agent 开发流程。 4.我相信,我能胜任贵公司的AI全栈开发岗位。未来我将不断学习,与同事一起并肩作战,打造让用户尖叫的AI Agent 产品。成为一名合格的AI全栈开发工程师。 ------------------------------------------------------------------ ### 3.13吴俊豪竞策 1.自我介绍 2.ts interfacetype区别涉及到泛型问题 "首选 interface 定义对象契约,因为它支持声明合并且报错更友好;涉及泛型时两者都能用 约束,但 type 能直接处理联合类型和映射类型。底层原理上,interface 是 TypeScript 独有的结构化子类型检查,编译后彻底擦除;而 type 只是类型别名,本质是编译期的文本替换,不生成任何运行时代码。" 3.react props定义倾向于? react props 定义更倾向于 TypesScript的interface。因为他支持声明合并,方便第三方库扩展类型,且报错时能直接显示接口名,调试效率更高。 3.介绍一下双token机制 双ToKen机制就是用‘短命Access Token’抗流量,用长命Refresh Token做续期,核心是为了解决安全与体验的矛盾: - 安全:Access Token 短期有效,泄露风险低;Refresh Token 长期有效,存储在本地,风险低。 - 体验:用户无需频繁登录,操作顺畅;过期时自动刷新,无需人工干预。 4.多标签打开的同时,如何实现其他标签页的token获取问题? 利用localStorage 的 Storage 事件实现跨标签页通信。跨标签页通信:A页刷新Token后写入储存,此时就会触发B页的Storage 监听回调,B页就会直接读取最新值并且更新内存状态,实现毫秒按级别的同步 5.rag文本切片处理分隔符? "首选‘语义感知’的递归分隔符策略:按 ['\n\n', '\n', '。', ' ', ''] 优先级逐级切割,确保切片在 Token 限制内尽可能保持句子或段落的完整性,避免切断上下文逻辑。" 优势:最大程度保留了段落结构和句子完整性。向量模型对完整句子的编码能力远强于半句话。 6.图片懒加载的实现(有线和无线) 有线:利用IntersectionObserver API 监听图片是否进入视口,进入一定比例后加载图片,减少初始加载资源,避免一次性加载所有图片导致的性能问题。 无线:除了和有线一样利用IntersectionObserver API 监听图片是否进入视口,还会根据网络状况调整加载图片的分辨率,提升用户体验。 7.nest依赖注入在前端方面(react)有没有熟悉的应用场景。 8.react中类似axios拦截器的操作和功能实现。 在 React 中可通过自定义高阶组件或使用中间件来实现类似 axios 拦截器功能。比如在请求前添加 loading 状态,响应后处理数据格式,提升用户体验和数据处理效率 9.react query? React Query 核心是用时间换空间,本质就是把服务器状态从组件逻辑中剥离,用“后台静默更新”代替手动加载。底层靠三个核心: - 用Query Key 做为缓存的唯一索引,实现数据去重和共享。 - 基于StaleTime 判断数据新鲜度,过期后自动在后台发送请求并比对新鲜度,避免界面闪烁 - 利用React上下文和观察者模式,确保多组间共享同一份服务器时自动同步更新 10.ai工具的日常使用情况 - 日常使用trae/cursor 等编程Agent 高效开发。 - 用coze 等工具快速搭建原型,验证想法。 - 用n8n 等工具快速搭建工作流,自动化处理任务。 11.如何进行agent回答质量的评估 - 人工评估:由开发人员或测试人员手动检查回答是否符合预期,包括语法、逻辑、内容等方面。 - 自动评估:利用自然语言处理技术,对回答进行自动评估,判断其质量和符合度。常用的自动评估指标包括语法错误率、逻辑错误率、内容符合度等。 12.死循环处理 处理死循环必须实施"硬截断"和‘软收敛’双重机制。核心动作是:设置最大迭代阈值强制终止,并且引入‘状态指纹’检测,一旦检测到重复的工具调用参数或相似的思考路径时立即阻断。底层原理就是将Agent的执行过程建模为有向图,通过记录历史轨迹的哈希值判断是否成环,配合奖励函数对冗余步骤进行负向惩罚,迫使模型在有限步数内收敛到最终答案,避免无限消耗Token和算力。" 13.上线前发现内存泄露问题服务器崩溃如何排查(性能排查工具?) 14.反问 ------------------------------------------------------------------ ### 徐梦娇面试题 1.介绍一下自己 2.介绍一个最熟悉的项目,做了哪些东西 3.vue生命周期有哪些? 4.在react里学了哪些东西? 5.react生命周期? 6.reat父子子传父? 7.react路由跳转方法有哪些? 8.react更新数据用到什么?useState是同步还是异步? 9.状态管理怎么用?zustant怎么使用? 10.react路由守卫 11.闲聊 12.js/html/css基础。 13.反问 ------------------------------------------------------------------ ### 3.14曹老板 星物种 1.自我介绍 2.浏览器的渲染机制 浏览器渲染机制是,先解析HTML生成DOM树,再解析CSS生成CSSOM树,二者结合生成渲染树。通过布局计算各元素的位置大小,最后绘制到屏幕上。 3.get请求post请求的区别,每次请求都有哪些信息 GET请求用于获取资源,参数在URL中,明文传输,长度有限,安全性比较低; POST请求用于提交数据,参数在请求体,明文传输,长度没有限制,更加安全(来自于HTTPS)。 GET请求包含请求行(方法、URL、协议版本)、请求头(GET不是不可以发送请求体,只是服务器和浏览器约定不用);POST请求包含请求行、请求头和请求体。GET适合用于获取数据,POST适合用于提交数据。 - 幂等性 HTTP 是无状态的 GET请求是幂等的,多次请求相同的URL返回的结果是相同的。 POST请求不是幂等的,多次请求相同的URL可能会导致不同的结果 - 缓存性 GET请求可以被缓存,POST请求一般不能被缓存。 请求行: 方法:GET、POST等 URL:资源的路径 协议版本:HTTP/1.1等 请求体: 仅 POST/PUT 等提交类请求有,GET 请求无;格式由请求头的Content-Type决定格式可以是JSON、表单数据等。 请求头: 键值对形式,用于传递额外信息,如Content-Type、Authorization等。 4.xhr和fetch两种请求的区别 XHR(XMLHTTPREQUEST):浏览器最早提供的异步网络请求API,无Promise封装,依赖回调函数,功能很全面但是语法繁琐‘ Fetch:ES6+ 推出的现代网络请求API,基于promise设计,语法更加简洁、符合现代异步编程风格。 区别: - 异步模型和语法:XHR基于回调函数,无原生promise支持,多层回调容易产生“回调地狱”。。fetch天然基于promise,支持链式调用和async/await。 - 错误处理机制:XHR错误分类清晰,无需额外处理;fetch需要手动判断HTTP错误,必须手动通过response.ok判断并抛出错误。 5.有做过文件上传的项目吗 文件上传的**核心本质**就是:前端将本地文件的二进制数据通过HTTO协议封装并发送到后端,后端解析二进制数据后完成储存。 **核心流程** 1.文件选择:通过HTML的让用户选择本地文件。触发change事件后获取文件对象; 2.数据封装:由于文件是二进制数据,无法通过普通的json传输,需要使用FormData对象将文件数据封装起来。 3.发送请求:使用fetch或XHR发送POST请求到后端接口,将FormData对象作为请求体发送。 4.状态监控: - fetch:通过response.ok判断HTTP状态码是否成功,成功则解析响应体,失败则抛出错误。 - XHR:通过监听readystatechange事件,判断readyState是否为4(完成),status是否为200(成功),再处理响应数据。 6.有自己做过项目的经历吗 7.知道重排和重绘吗 重排:当元素的几何属性(位置、大小、布局相关)发生变化时,浏览器需要重新计算渲染树中元素的布局信息,这个过程就叫重排。因为布局是是牵一发而动全身,可能会影响父元素、子元素甚至兄弟元素的布局,所以重排的性能开销非常的大。而且重排一定会触发后续的重绘。 重绘:当元素的外观属性(不影响几何位置的属性)发生变化时,浏览器不需要重新计算布局,只需要根据现有的布局,重新绘制元素,这个过程就叫重绘。 8.全面深入讲一下promise,连续三条请求该如何处理 promise是JavaScript中处理异步操作的核心对象,它解决了传统回调函数嵌套产生的回调地狱问题,让异步代码的逻辑更清晰、更易于维护。 也可以把promise理解为一个承诺,现在发起一个异步操作(网络请求、文件读取),promise就会承诺无论成功或者失败一定会给你返回一个结果。 9.说一下async和await async和await是ES8引入的异步编程语法糖,它们基于promise,使异步代码看起来像同步代码,更加易读、易维护。 async函数: - 定义:在函数前添加async关键字,函数内部可以使用await表达式。 - 作用:将异步操作包装成promise,返回一个promise对象。 - 注意:async函数内部可以使用try/catch捕获错误,也可以返回一个值(会被包装成promise)。 await表达式: - 定义:在async函数内部使用await关键字,后面跟着一个promise对象。 - 作用:等待promise对象resolve,返回resolve的值。 - 注意:await只能在async函数内部使用,不能在普通函数中使用。 10.平时使用的开发框架,react的组件通信,用过哪些hooks,自己封装过哪些hoos和组件,表单和表格在封装时要注意哪些信息 11.了解过跨域吗,同源策略 同源策略(Same-Origin Policy SOP):同源策略是浏览器的安全基石。 它规定了:只有[协议 + 域名 + 端口]三者都完全一致,才算同源,浏览器才允许: - 读取/修改另一个页面的DOM - 读取另一个页面的Cookie、LocalStorage、IndexedDB - 发送AJAX/fetch请求并拿到响应 为什么要有同源策略 同源策略的主要目的是防止恶意网站通过获取用户的敏感信息(如Cookie、LocalStorage等)来攻击用户。如果没有同源策略,一个网站就可以在另一个网站的上下文中执行代码,从而获取用户的隐私数据。没有同源策略,浏览器就像在裸奔一样。 **跨域** 跨域(Cross-Origin Resource Sharing,CORS)是浏览器的一种安全机制,用于限制不同源之间的资源交互。只要不满足同源策略,就是跨域。 跨域的真相:是浏览器拦截,而非服务器拒绝,这是一个常见的误区,跨域错误是浏览器报出来的,而不是服务器拒绝连接。 **请求过程**: 前端发起跨域请求(例如从 a.com 请求 b.com)。 请求成功发出:网络层面,请求正常到达 b.com 服务器。 服务器正常响应:b.com 处理请求并返回数据(甚至返回了 200 OK)。 浏览器拦截:浏览器接收到响应后,检查响应头中的 CORS 配置。如果发现没有允许 a.com 访问的标识,浏览器会丢弃响应数据,并在控制台抛出 Access-Control-Allow-Origin 相关的错误。 主流的解决跨域问题的方法: 1.CORS(跨域资源共享):跨域请求能不能成功,关键看「服务器是否通过响应头明确授权」,浏览器只是执行「授权校验」的角色。 工作原理: - 服务器通过在HTTP响应头中设置特定的字段来放行跨域请求。 例子:graph TD A[前端发简单请求] --> B[浏览器直接发送请求到服务器] B --> C[服务器处理请求,返回数据+CORS授权头] C --> D[浏览器校验授权头:允许则把数据给前端,不允许则拦截] 12.浅拷贝和深拷贝 首先要理解拷贝就必须理解计算机内存中栈和堆的概念 基本数据类型(int 、bool、string等):通常存储在栈中,直接存储值 引用数据类型(对象、数组等):变量名存储在栈中,但实际的数据存储在堆中。栈中的变量保存的是转向堆内存的地址。 浅拷贝:创建一个新对象,这个对象拥有元对象属性的副本。 对于基本数据类型:直接复制值,互不影响。 对于引用数据类型:复制的是地址,指向的是同一个内存空间,所以修改其中一个对象,另一个对象也会受到影响。 深拷贝:创建一个新对象,并递归地复制原对象以及其所有的层级的子对象,确保新对象与原对象完全独立,互不影响。 他在堆中开辟了一个全新的内存空间,将原对象的数据完全原复制过去。新旧对象在内存中完全隔离,没有任何共享和引用。(代价:因为需要递归遍历,所以性能开销非常的大,内存占用高) 13.说一下数据类型,判断数据类型的方法如何判断是一个数组 数据类型: 基本数据类型:int number string bool undefined null bigint 引用数据类型:对象(Object)、数组(Array)、函数(Function) 小技巧:检测数据类型方法 1.typeof:用于检测基本数据类型(除null外,会返回object) 2.Array.isArray():专门检测数组 3.Object.prototype.toString.call ()(万能精准法,推荐) 14.数组去重有哪些方式 - ES6新增的Set集合的特性:Set中的成员都是唯一的,没有重复值。 - indexOf:适合需要兼容ES5及以下旧环境的场景,核心就是遍历数组。 15.对es6有哪些了解,平时会用到的es6新特性 2015年发布的javascript版本,新增了很多新特性,比如: - let和const:块级作用域,解决了var的一些问题。 替代 ES5 的 var,解决「变量提升」「无块级作用域」「变量覆盖」等问题,是所有 ES6 特性的基础。 let:块级作用域({} 内有效),无变量提升,不可重复声明,值可修改; const:块级作用域,必须初始化,不可重复声明,引用不可改(但引用类型的内容可改,如对象属性)。 - 箭头函数:更简洁的函数定义,没有自己的this,指向外层作用域的this。简化函数写法,解决 this 指向混乱 - 模板字符串:更方便的字符串拼接,支持多行字符串。 - 解构赋值:更方便的变量赋值,支持数组、对象的解构。 - 扩展运算符:更方便的数组操作,支持展开数组、合并数组。 - Promise:更方便的异步操作,解决了回调地狱的问题。 - 类和继承:更方便的面向对象编程,支持类的定义、继承、多态。 - 模块:更方便的代码组织,支持模块的定义、导入、导出。 16.反问 -------------------------------------------------------------------------------------------------- ### 3.25 瞿翔 江下科技 1.自我介绍 2.你认为自己作为前端开发工程师的优势是什么? 1.前端开发工程师需要有良好的沟通能力,能够与后端开发人员、产品经理等合作,理解需求。 2.前端开发工程师需要有良好的代码质量意识,能够编写符合需求的代码。 3.前端开发工程师需要有良好的性能优化意识,能够优化前端代码,提高页面加载速度。 4.前端开发工程师需要有良好的代码可维护性意识,能够编写可维护的代码。 3.你的项目都是完成的吗?有没有遇到什么难题? 4.你的项目是学校的大作业吗?我好像面过你的这个简历。 5.如果页面中有很多子元素,你怎么让其中一个子元素居中? 在有多个子元素的情况下,让其中一个特定子元素居中,以下是几种最常用且最有效的方法: 1.最推荐的 Flexbox(最灵活) 如果不介意在这个特殊的子元素外面包一层单独的父容器(div),那这就是最完美的方案,他不影响其他兄弟元素的布局。 HTML结构:
其他元素 1
其他元素 2
我是要居中的元素
> CSS样式: .center-box { display: flex; justify-content: center; /* 水平居中 */ align-items: center; /* 垂直居中 */ height: 100px; /* 父容器需要有高度才能看出垂直居中效果 */ } 2.绝对定位法(脱离文档流) 如果你想让这个元素 “浮” 在所有元素之上(即不占据原来的物理空间),或者不想改变 HTML 结构,可以使用这种方法。 CSS 样式: /* 父元素(相对定位作为参照物) */ .big-container { position: relative; } /* 需要居中的那个子元素 */ .target { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); /* 核心:向左上偏移自身宽高的一半 */ } 3.Grid布局(最简单) 如果你想让这个元素 “居中” 在父容器中,或者你想在父容器中创建一个居中的网格,可以使用这种方法。 CSS 样式: /* 父元素(Grid布局作为参照物) */ .big-container { display: grid; place-items: center; /* 居中对齐 */ } 4.绝对定位 + transform 如果你想让这个元素 “居中” 在父容器中,或者你想在父容器中创建一个居中的网格,可以使用这种方法。 CSS 样式: /* 父元素(相对定位作为参照物) */ .big-container { position: relative; } /* 需要居中的那个子元素 */ .target { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); /* 核心:向左上偏移自身宽高的一半 */ } 5.绝对定位 + margin(已知子元素尺寸) .parent { position: relative; } .child { position: absolute; top: 50%; left: 50%; width: 200px; height: 100px; margin-top: -50px; /* 高度的一半 */ margin-left: -100px; /* 宽度的一半 */ } 6.怎么背景图平铺整个盒子? 让背景图案完全覆盖(平铺)整个盒子,最核心的属性是background-size。以下是两种最常用的方案,根据你是否介意图片变形选择: 1。最推荐:保持图片比例,完全覆盖盒子(background-size: cover;) 这是最常用的方式,图片不会变形,但可能会被剪掉一部分,确保填满整个盒子。 CSS 样式: .BOX{ width:400px; // 盒子宽度 height:300px; // 盒子高度 /*1.设计背景图*/ background-image: url('你的图片路径.jpg'); /* 2.核心:让图片覆盖整个盒子,保持比例 */ background-size: cover; /* 3.让图片居中显示 */ background-position: center; background-repeat: no-repeat; } 2.强制拉伸填满盒子(100% 100%) 如果你不介意图片被拉伸变形,你可以使用100% 100%来强制拉伸图片,确保它填满整个盒子。CSS 样式: .BOX{ width:400px; // 盒子宽度 height:300px; // 盒子高度 /*1.设计背景图*/ background-image: url('你的图片路径.jpg'); /* 2.核心:宽高都设置为100%,确保图片填满整个盒子 强制拉伸 */ background-size: 100% 100%; /* 3.让图片居中显示 */ background-position: center; background-repeat: no-repeat; } 7.说说es5/6最大的区别 8.es6 class是个什么作用? 9.vue和react用函数组件,抛弃面相对象编程,这是为什么? 解决this指向,hook逻辑复用比class强 10.有一个商品单价.数量.总价,价格变动,用什么hook函数? 依赖项写单价数量,里面用useMemo 11.介绍一下rag问答流程,问题的向量化在哪一步? 12.你的大模型幻觉是在prompt里面解决的吗? 13.反问 ### 3.26 郑志鹏 2.第一个项目介绍 3.花多久时间去学习 4.你对职位和未来有什么想法 5.你对未来公司工作环境期望怎么样 6.css水平居中方法 7.怎么解决屏幕移动端适配问题 8.em、rem了解吗 9.js事件循环 10.为什么用promise,解决了什么问题11.promise.all场景题+一道执行流程题 12.一个任务失败,全链路崩盘白屏,你怎么 去解决 13.大屏,了解吗 14.怎么避免图表数据重绘(如果在大屏后台定时器请求中同时进行多定时器任务请求)15.react16引用了全新架构相比之前架构都有哪些提升 16.讲一下fiber架构 16.微前端,了解吗 18.把一个大项目拆解成多个小项目,多人协作,你做一块他做一块,你觉得大概解决了 什么问题 19.微前端对第三方导入模块了解吗 ------------------------------------------------------------------ # GIT篇 核心概念:git是分布式版本控制系统,核心是追踪文件修改,管理版本历史。 git 常用命令 1.查看文件状态:git status 2.将修改加入暂存区:git add . 3.提交修改:git commit -m "提交信息" 4.查看修改差异:git diff 5.查看提交历史:git log 6.创建新分支:git branch 分支名 7.切换分支:git checkout 分支名 ------------------------------------------------------------------ # Milvus向量数据库 篇 向量数据库不直接存放原始文件,而是存文本切片的向量嵌入。 文本切片的概念是:把文本切成[模型能处理、语义不破碎、检索精度高]的小片段。 文本切片的核心目标: 1.适配嵌入模型的长度限制:几乎所有嵌入式(embedding)模型都有最大的token限制,超长会被截断或报错。 2.保证语义完整不破碎:文本切的太碎会丢失上下文;切的太大语义模糊,检索不准。 3.提升检索效率:片段越小,检索越精确,因为检索的是向量的相似度,向量的维度越小,相似度计算就越简单。 ## 主流切片方法 1.固定长度切片 方式:按照固定的字符数切/按照固定的token数切 优点:简单、易于实现 缺点:可能会丢失上下文,语义不完整,检索精度低。 2.按自然分段切 方式:先按段落\n\n 再按句子。!?\n 最后按标点短句切 优点:语义基本完整,适合文档、说明书、知识库等 缺点:无法处理超长片段 3.递归切片(RAG/向量数据库最主流、最强的方法) 方式:按层级优先级递归切分: 1.先按最大分隔符(段落) 2.若still超长 -> 按句子切 3.若still超长 -> 按标点短句切 4.保证每段 <= 最大token数 优点:语义保留最好,自动适配长短文本 缺点:比固定长度切割稍微多消耗一些时间 4.语义相似切片(高级、精度最高) 方式:先用轻量模型把文本切成句子,计算相邻句子的相似度,如果相似度突然变低,就切分。保证同一切片内语义高度统一。 优点:检索的最准确,适合论文和法律文本 缺点:耗资源、速度慢。 **核心调用方法**:split_text方法 核心动作:所有切片最终都通过 split_text()(纯文本)或 load_and_split()(文件加载 + 切割)完成切割。 ------------------------------------------------------------------ ## vue篇 ### 路由跳转 前端路由的核心概念是:前端路由时单页应用实现页面切换的核心,本质就是监听URL变化,在不刷新整个页面的前提下,渲染对应组件。vue和react的路由跳转核心逻辑一致,但api和使用方法有差异。 一、vue路由跳转(基于vue router) 两种核心跳转方式: 1.编程式跳转(js代码触发,适合动态跳转):使用router.push()或router.replace()方法,手动指定目标路由。 2.声明式跳转(标签式,适合静态跳转):使用组件,点击链接自动跳转。 ------------------------------------------------------------------ ## react篇 ### 路由跳转(基于react router) 两种核心跳转方式: 1.编程式跳转(js代码触发,适合动态跳转):v5使用history,v6使用navigate。 2.声明式跳转(标签式,适合静态跳转):使用组件,点击链接自动跳转。 ------------------------------------------------------------------ ## JS篇 1.js基本数据类型及它们之间的区别 JavaScript一共分为两种: 基本数据类型:undefined(变量声明但是没有赋值时的默认值)、null、boolean、number、string、symbol(新增的类型,用于创建唯一的标识符) 引用数据类型:object、array、function ### js中检测数据类型的方法 typeof:typeof可检测大多数的数据类型,但不能检测数组和函数。数组、对象、null都会被判断为object,其他判断都正确。 Array.isArray():检测数组专用方法 instanceof:用于检测某个对象是否是某个构造函数的实例(基于原型链判断) Object.prototype.toString.call():万能精准法,这是js中最准确的,最通用的类型检测方法。 其中可以用于判断数组的方式有: 1.Array.isArray() 2.Object.prototype.toString.call() 3.instanceof ### 栈和堆的概念 栈:用于存放基本数据类型和引用数据类型的地址(先进后出) 堆:存放引用数据类型实际值的地方 ### javascript中的this this是执行上下文中的一种属性,他指向最后一次调用这个方法的对象。在实际开发中,this的指向可以通过四种调用模式来判断。 1.函数调用模式:当一个函数不是一个对象的属性时,直接作为普通函数调用,this指向window对象。在严格模式下,this指向undefined。 2.方法调用模式:当一个函数是一个对象的属性时,this指向调用该方法的对象。 3.构造器调用模式:如果一个函数用new调用时,函数执行前会新创建一个对象,this指向新创建的对象。 4.apply、call、bind调用模式:这三个方法都可以显示指定调用函数的this指向。其中,apply和call是立即调用的,而bind是返回一个新的函数。 ### 箭头函数与普通函数有什么区别 1.箭头函数比普通函数更加的简洁 如果没有参数直接写一个空括号 如果有多个参数,用逗号隔开 如果只有一个参数,可以省略括号 2.箭头函数没有自己的this 箭头函数不会创建自己的this、,所以他没有自己的this,它只会在自己的作用域的上一层作用域中继承this。所以箭头函数中this的指向在它定义时已经确定了,后续不会改变 3.箭头函数继承过来的this指向永远不会改变 箭头函数中的this指向是静态的,不会因为调用模式而改变。 4.箭头函数不能作为构造函数使用 箭头函数不能用new关键字调用,否则会报错。 箭头函数没有自己的this,且this指向外层的执行环境,且不能改变方向,所以不能当作构造函数使用。 5.箭头函数没有prototype ### AMD和commonjs的区别 AMD支持异步加载,commonjs不支持异步加载。 Commonjs的设计是为服务器端开发的,而AMD是为浏览器开发的。 AMD语法很复杂,很长,而commonjs语法上跟接近于Node编写模块的风格。 ### ES6模块commonJS模块有什么异同 commonJS是对模块的浅拷贝,而ES6模块是对模块的引用。也就是说ES6模块只存只读,不会改变模块的值。 ### let、const、var的区别 1.块级作用域:let和const是块级作用域,而var不是。块级作用域解决了ES5中的两个问题:1.内层变量可能覆盖外层变量。2.用于计数的循环变量泄露为全局变量。 2.变量提升:var会变量提升而let和const不存在变量提升,即变量在声明之后才能使用,否则会报错。 3.给全局添加属性:浏览器的全局对象是window,而node的全局对象是global。var声明的变量为全局变量,并且会将该变量添加为全局对象的属性,但是let和const不会。 4.重复声明:var声明变量时可以重复声明变量,后声明的同名变量会覆盖之前声明的遍历。const和let不允许重复声明变量。 5.暂时性死区:在使用let和const命令声明变量之前,该变量都是不可用的,这在语法上称为暂时性死区。使用var声明的变量,不会存在暂时性死区。 6.初始值设置:在变量声明时,var和let可以不用设置初始值默认值为undefined,而const必须设置初始值,而且声明后不能再重新赋值。 ### new操作符的实现原理 1.首先创建了一个新的空对象 -> 2.设置原型,将对象的原型设置为函数的prototype对象 -> 3.让函数的this指向这个对象 -> 4.执行函数体,返回对象 ### 数组有哪些原生方法 1.增上改查类方法: push():在数组的末尾添加一个或多个元素,并返回新的数组长度。 pop():删除数组的最后一个元素,并返回该元素。 shift():删除数组的第一个元素,并返回该元素。 unshift():在数组的开头添加一个或多个元素,并返回新的数组长度。 2.遍历迭代类方法: forEach():对数组的每个元素执行一次提供的函数。 map():对数组的每个元素执行一次提供的函数,返回一个新的数组。 filter():对数组的每个元素执行一次提供的函数,返回一个新的数组,数组中的元素是提供的函数返回true的元素。 reduce():对数组的每个元素执行一次提供的函数,返回一个值。 3.排序与反转类方法: sort():对数组的元素进行排序,返回排序后的数组。 reverse():将数组的元素顺序反转,返回反转后的数组。 4.转换与拼接类方法: join():将数组的元素连接成一个字符串,返回连接后的字符串。 slice():将数组转换为字符串,返回转换后的字符串。 ### for in和for of的区别 1.for in遍历的是对象的属性名,而for of遍历的是对象的属性值。 2.for of遍历的是数组的元素,而for in遍历的是数组的索引。 ### 原型和原型链 1.对原型、原型链的理解: 在JavaScript中原型(prototype)就是构造函数给所有实例对象添加的属性和方法。原型链就是实例找资源的路径:先找自己的属性,没有再找原型,没有再找原型的原型,直到找到Object.prototype。原型链的终点都是null。 2.如何获取非原型链上的属性: obj.hasOwnProperty(属性名) → 返回 true 说明是对象自身属性(非原型链),然后直接 obj.属性名 获取即可。 ### 对执行上下文、作用域、作用域链和闭包的理解 1.对闭包的理解: 比如,函数 A 内部有一个函数 B,函数 B 可以访问到函数 A 中的变量,那么函数 B 就是闭包。 构成条件(缺一不可):有嵌套函数(外层加内层) + 内层函数引用了外层函数的局部变量 + 内层函数被(返回/暴露)到外层函数外部。 核心效果:外层函数执行完后,他的局部变量不会销毁(因为内层函数还在引用) 外部无法直接访问外层的局部变量,只能通过内层函数来间接访问(实现变量私有化)。 2.闭包的两个常用的用途: 1.闭包的第一个用途就是是我们在函数外部能够访问到函数内部的变量。通过闭包,我们可以实现变量的私有化,即在函数外部不能直接访问到函数内部的变量,只能通过内层函数来间接访问。 2.已经运行结束的函数上下文中的变量对象继续留在内存中,因为闭包函数保留了这个变量对象的引用,所以这个变量对象就不会回收。 3.作用域链: 在当前作用域中查找所需变量,但是该作用域没有这个变量,那这个变量就是自由变量。如果在自己作用域找不到该变量就去父级作用域查找,依次向上级作用域查找,直到访问到window对象就被终止,这一层层的关系就是作用域链。 4.对执行上下文的理解: 执行上下文是JavaScript中代码执行的环境与核心运行机制,是理解变量提升、this指向、作用域链的基石。 可以把执行上下文理解为:JS代码运行的专属舞台,任何一段可执行的JS代码,都必须在自己的执行上下文中才能运行;没有这个舞台,代码就无法执行。 1.执行上下文的三种类型: js中只有三种执行上下文,日常开发只需要注意前两种: 全局执行上下文: - 默认上下文,代码加载后第一个被创建的环境; - 整个程序只有一个; - this指向全局(浏览器:window,Node.JS:global); - 管理全局变量、全局函数 函数执行上下文 - 函数调用时才会创建(定义时不创建); - 函数调用几次,就会创建几个执行上下文; - 拥有独立的变量、作用域、this指向; eval函数执行上下文(废弃,不建议使用): - 运行在eval()函数中的代码,极少使用,因为eval函数存在安全风险。 二、执行上下文的生命周期(核心) 每个执行上下文都会经历两个阶段:创建阶段 -> 执行阶段 这是理解变量提升、this指向的最关键知识点。 1.创建阶段(代码执行前) JS引擎会为上下文做三件事: 1.绑定this 确定当前上下文中this的指向(全局 -> window,函数看调用方式)。 2.创建词法环境 储存:let/const 声明的变量、函数声明、作用域链。 3.创建变量环境 储存:var声明的变量(变量提升的根源) 关键:变量提升的本质 - var:创建阶段直接声明 + 赋值 (undefined),所以可以提前访问; - let/const:创建阶段只声明,不赋值,存在暂时性死区,提前访问会报错。 2.执行阶段 JS引擎会逐行执行代码: - 给变量赋值 - 执行函数调用 - 修改变量环境/词法环境中的值 三、执行上下文栈(调用栈) JavaScript是一门单线程语言,同一时间只能执行一个上下文。 引擎用执行上下文栈(调用栈)管理所有上下文,遵行后进先出(LIFO)规则: 1.全局上下文首先入栈,永远在栈底; 2.调用函数 -> 函数上下文入栈,成为当前执行上下文; 3.函数执行完毕 -> 函数上下文出栈,回到上一个上下文; 4.程序关闭 -> 全局上下文出栈。 四、this指向与上下文的关系 this的值在上下文创建阶段就确定了,和代码位置无关,只和调用方式有关: