1.在本地从0开始创建一个vue项目要求实现三个vue组件一个父组件两个子组件在父组件中使用子组件子组件分别为按钮组件和计数器组件要求按下按钮计数器组件中显示的值加1.!-- 父组件App.vue -- template div idapp h1计数器示例/h1 Counter :countcount / Button incrementincrementCount / /div /template script setup import { ref } from vue import Counter from ./components/Counter.vue import Button from ./components/Button.vue // 定义计数器的数值 const count ref(0) // 增加计数的方法 const incrementCount () { count.value } /script !-- 子组件1计数器组件 Counter.vue -- template div classcounter h2当前计数: {{ count }}/h2 /div /template script setup // 定义接收父组件传递的count属性 defineProps([count]) /script !-- 子组件2按钮组件 Button.vue -- template div classbutton-container button clickhandleClick点击加1/button /div /template script setup // 定义触发父组件事件 const emit defineEmits([increment]) // 处理按钮点击向父组件发送事件 const handleClick () { emit(increment) } /script2.手撕一下flex居中、flex 50%布局、数组扁平化.parent { display: flex; justify-content: center; /* 水平居中 */ align-items: center; /* 垂直居中 */ /* 或者使用 margin: auto 在子元素上 */ } 、、、、、、、、、、、、、、、、、、、、、、、、、、、、、 .parent { display: flex; width: 100%; } .child { flex: 1; /* 或者 flex: 1 1 0% */ /* 等价于 flex-grow: 1; flex-shrink: 1; flex-basis: 0%; */ } /* 或者更明确的写法 */ .child { flex-basis: 50%; /* 但要注意内容溢出问题 */ } 、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、 function flatten(arr) { let result []; for (let i 0; i arr.length; i) { if (Array.isArray(arr[i])) { result result.concat(flatten(arr[i])); } else { result.push(arr[i]); } } return result; }3.数组api数组 API 主要分为两类会改变原数组的和不会改变原数组的。会改变原数组的方法有push、pop、unshift、shift、splice最强大的增删改、sort、reverse、fill和copyWithin使用时需要注意它们会直接修改原数组可能引发副作用。不会改变原数组的方法则更常用包括遍历类的forEach仅遍历、map映射生成新数组、filter过滤、reduce累积计算可实现求和、去重、分组等、some/every判断、find/findIndex查找元素或索引还包括拼接类的concat、slice浅拷贝切片、join转字符串、flat/flatMap扁平化以及查找类的indexOf/lastIndexOf和includes。日常开发中map、filter、reduce是最常用的函数式编程工具splice和slice虽然名字相似但要牢记前者改原数组、后者不改而 ES6 之后新增的flat和includes也极大简化了操作。4.foreach map区别面试官forEach 和 map 的主要区别体现在以下几个方面**第一返回值不同。** forEach 返回 undefined没有返回值它只是单纯地遍历数组并执行回调函数而 map 会返回一个**新数组**这个新数组由回调函数的返回值组成原数组保持不变。**第二用途不同。** forEach 适合用于**副作用操作**比如修改外部变量、打印日志、操作 DOM 等不需要返回结果的情况map 适合用于**数据转换**当你需要基于原数组生成一个新的数组时使用比如将数组中的每个数字翻倍、提取对象的某个属性组成新数组等。**第三链式调用能力不同。** forEach 因为返回 undefined所以无法进行链式调用而 map 返回新数组可以继续调用其他数组方法比如 arr.map(x x * 2).filter(x x 5).reduce((a,b) a b, 0)。**第四性能方面。** forEach 通常比 map 稍快因为 map 需要创建并填充新数组有额外的内存开销。但除非在极大规模数据下这个差异通常可以忽略优先考虑代码可读性。**第五回调函数参数相同。** 两者回调函数都接收三个参数当前元素、索引、原数组本身这一点是一致的。**简单总结** 需要返回值、需要生成新数组、需要链式调用时用 map只是遍历执行操作、不需要结果时用 forEach。还有一个常见的坑forEach 中无法用 break 跳出循环也不能用 return 跳过当前迭代到下一次而 map 同样不能如果需要中断循环应该用 for 循环或 some/every。5.原型和原型链面试官原型和原型链是 JavaScript 实现继承的核心机制。**先说原型** 每个函数都有一个 prototype 属性指向一个对象这个对象就是该函数的原型对象。而每个通过该函数创建出来的实例对象内部都有一个 __proto__ 属性现代规范中是 [[Prototype]]指向其构造函数的 prototype。也就是说实例.__proto__ 构造函数.prototype。**原型的作用** 实现属性和方法的共享。挂载在 prototype 上的属性和方法可以被所有实例共享访问而不需要每个实例都单独存储一份。比如 Array.prototype.push 是所有数组实例共享的方法。**再说原型链** 当访问一个对象的属性时如果对象本身没有这个属性JS 引擎就会沿着 __proto__ 向上查找一直找到 Object.prototype 为止。如果还找不到就返回 undefined。这条由 __proto__ 串联起来的链条就是原型链。**举个例子** const arr [1, 2, 3]调用 arr.push(4)。JS 会先在 arr 自身找 push找不到就去 arr.__proto__即 Array.prototype找找到了就执行。如果调用 arr.toString()Array.prototype 上没有就继续向上到 arr.__proto__.__proto__即 Object.prototype在那里找到 toString。**原型链的顶层** Object.prototype.__proto__ 是 null这是原型链的终点。**几个关键点**- 所有函数包括构造函数都是 Function 的实例所以 函数.__proto__ Function.prototype- Function.prototype.__proto__ Object.prototype- 所有对象最终都继承自 Object.prototype**实际应用** 我们可以通过修改原型来实现继承或方法扩展比如 Array.prototype.myMap function() {...}。但要注意不要污染原生原型以免造成命名冲突或性能问题。**总结一句话** 原型是存放共享属性和方法的对象原型链是实现查找的机制JS 通过它实现了基于原型的继承。6.js数据类型面试官JavaScript 中的数据类型分为两大类**原始类型Primitive Types** 和 **引用类型Reference Types**总共 8 种。1原始类型7种** 存储在栈内存中按值访问赋值时是值的拷贝。包括- undefined声明未赋值的变量以及不返回任何值的函数的返回值- null表示空对象指针注意 typeof null object 这是 JS 语言的 bug- booleantrue 和 false- number整数和浮点数包括 Infinity、-Infinity、NaN遵循 IEEE 754 标准- string字符串不可变- symbolES6创建唯一且不可变的值常用于对象属性的唯一键- bigintES2020表示任意精度的整数以 n 结尾如 9007199254740991n2引用类型1种但包含多种具体结构** 存储在堆内存中变量存储的是内存地址的指针赋值时是引用的拷贝。包括- Object普通对象、数组、函数、日期、正则、Map、Set 等都属于 Object 类型- 特别注意typeof null objecttypeof 函数 functionfunction 本质是 object 的可调用子类型3判断数据类型的方法**- typeof能区分除了 null 以外的原始类型以及函数但不能区分具体对象类型typeof [] 和 typeof {} 都返回 object- instanceof判断对象是否是某个构造函数的实例可以区分数组和对象但不同 iframe 或 window 间可能失效- Object.prototype.toString.call()最准确的方法返回 [object Array]、[object Object]、[object Date] 等- Array.isArray()专门判断数组4原始类型和引用类型的核心区别**- 存储位置不同栈 vs 堆- 赋值行为不同值拷贝 vs 引用拷贝- 比较方式不同值比较 vs 引用地址比较{} {} 为 false5常见的坑**- typeof null 是 object判断 null 应该用 null- NaN ! NaN判断 NaN 要用 isNaN() 或 Number.isNaN()- 原始类型调用方法时JS 会临时用包装对象String、Number、Boolean将其包装成对象执行完再销毁以上就是我对 JavaScript 数据类型的理解。7.this指向8.堆和栈面试官栈Stack和堆Heap是 JavaScript 引擎管理内存的两种不同数据结构理解它们的区别对写出高性能、无内存泄漏的代码很重要。**先说栈Stack**栈是**先进后出**的结构主要用来存储**原始类型的数据**以及**引用类型的引用地址**指针。栈内存由系统自动分配和释放空间大小相对固定但读取速度非常快。举个例子javascriptlet a 10; // 数字10直接存在栈里let b hello; // 字符串存在栈里let c { name: Tom }; // 对象在堆里栈里存的是指向堆的地址当一个函数被调用时它的参数、局部变量、返回地址都会被压入栈中函数执行完毕后会弹出销毁这就是为什么函数内的局部变量在函数结束后就无法访问了。**再说堆Heap**堆是一种**树形结构**主要用来存储**引用类型的数据**比如对象、数组、函数、Date、RegExp 等。堆内存的空间更大、更灵活可以动态分配内存但读取速度比栈慢并且不会自动销毁需要靠垃圾回收机制来回收。当我们创建一个对象时对象本身存在堆里而栈里只存一个指向这个堆内存的地址。**核心区别**1. **存储内容不同** 栈存原始值 引用地址堆存实际的对象数据2. **分配方式不同** 栈自动分配大小固定堆动态分配空间更大3. **访问速度不同** 栈比堆快得多4. **生命周期不同** 栈数据随函数执行完毕自动销毁堆数据需要垃圾回收才能释放5. **内存大小限制** 栈较小通常几 MB堆较大可达几百 MB 甚至更高**赋值和拷贝的区别最能体现这一点**javascript// 原始类型栈赋值是值的拷贝互不影响let x 10;let y x; // y 获得 10 的拷贝y 20; // x 仍然是 10// 引用类型堆赋值是引用的拷贝指向同一个堆内存let obj1 { count: 10 };let obj2 obj1; // obj2 拿到了栈里的地址和 obj1 指向同一个堆对象obj2.count 20; // obj1.count 也变成 20// 浅拷贝 vs 深拷贝let obj3 { ...obj1 }; // 浅拷贝新对象在堆里但内部嵌套对象仍共享let obj4 JSON.parse(JSON.stringify(obj1)); // 深拷贝完全独立的堆对象**垃圾回收相关**当一个对象没有任何变量引用它时即栈里没有地址指向堆中的这个对象它就会被垃圾回收机制标记并清理释放堆内存。这就是为什么我们常说“手动将不再使用的对象置为 null”可以帮助垃圾回收。**容易混淆的点**闭包之所以能记住外部函数的变量是因为那些变量虽然存在栈中但被闭包引用后会从栈转移到堆中保存从而延长生命周期。总结一句话**栈存小且快自动管理存原始值和指针堆存大且灵活手动管理垃圾回收存实际对象。** 理解这个区别有助于写出更高效、更可靠的代码。9.typeof和instanceof:面试官typeof 和 instanceof 都是 JavaScript 中用来判断数据类型的方法但它们的原理、适用场景和返回值有很大不同。**先说 typeof**typeof 是一元运算符返回一个字符串表示操作数的类型。它适合判断**原始类型**但有局限性javascripttypeof undefined // undefinedtypeof true // booleantypeof 123 // numbertypeof hello // stringtypeof Symbol() // symboltypeof 123n // biginttypeof (() {}) // function// 以下两个是坑typeof null // object —— 这是 JS 遗留的 bugtypeof [] // object —— 无法区分数组和普通对象typeof {} // objecttypeof new Date() // object总结typeof 能正确判断除 null 以外的所有原始类型以及函数但对于引用类型数组、对象、日期、正则等一律返回 object无法进一步区分。**再说 instanceof**instanceof 是二元运算符返回布尔值用于检测**构造函数的 prototype 属性**是否出现在某个实例对象的**原型链**上。它适合判断**引用类型**的具体类型// 原始类型判断会失效hello instanceof String // false字面量不是 String 实例123 instanceof Number // false注意instanceof 右侧必须是一个构造函数并且它沿着原型链查找所以对于数组[] instanceof Object 也会返回 true这有时会造成误判。**两者的核心区别**1. **判断依据不同** typeof 检查内部 [[Type]] 标记instanceof 检查原型链2. **适用类型不同** typeof 适合原始类型instanceof 适合引用类型3. **返回值不同** typeof 返回字符串instanceof 返回布尔值4. **对 null 的处理** typeof null object 是错误的null instanceof Object 为 false反而更准确5. **跨 iframe/window 问题** instanceof 在不同执行环境如 iframe中会失效因为每个环境有独立的 Array、Object 构造函数typeof 不受影响// 判断某个对象是否是特定构造函数的实例且考虑不同 iframefunction isInstanceOf(obj, constructor) {return Object.prototype.toString.call(obj) [object ${constructor.name}]}**实际开发中的使用建议**- 判断原始类型用 typeof但记得单独处理 null- 判断数组用 Array.isArray()- 判断具体对象类型如 Date、RegExp、Map、Set用 Object.prototype.toString.call()- 需要沿着原型链判断继承关系时用 instanceof- 在框架或库代码中避免单纯依赖 instanceof 判断来自不同 iframe 的对象总结一句话**typeof 是“是什么类型”适合原始类型instanceof 是“是谁的实例”适合引用类型两者配合使用必要时用 Object.prototype.toString.call() 兜底。**10.typeof null返回的是字符串object。11.typeof function返回的是字符串function。12.promise面试官Promise 是 JavaScript 中用来处理异步操作的对象它有三种状态pending进行中、fulfilled成功、rejected失败且状态一旦从 pending 变为 fulfilled 或 rejected 后就不可逆通过 new Promise 创建时传入执行器函数内部调用 resolve 将状态改为 fulfilled调用 reject 改为 rejected然后可以用 .then() 处理成功结果、.catch() 捕获错误、.finally() 执行收尾操作并且 .then() 会返回新 Promise 从而支持链式调用解决了传统回调地狱的问题此外还提供了 Promise.all所有成功才成功、Promise.race返回最快完成的、Promise.allSettled等待全部有结果等静态方法用于并发控制而 async/await 本质上就是 Promise 的语法糖让异步代码写起来更像同步。13.promise常用方法面试官Promise 的常用方法我按**实例方法**和**静态方法**两类来介绍**实例方法在 Promise 实例上调用**1. **then()**最核心的方法接收两个可选参数成功回调onFulfilled和失败回调onRejected。返回一个新的 Promise支持链式调用。如果回调中返回一个值会传给下一个 then如果返回一个 Promise会等待其完成。2. **catch()**专门处理失败的回调等价于 then(null, onRejected)。推荐统一用 catch 捕获错误因为它能捕获到前面任意一个 then 中抛出的异常。3. **finally()**ES2018无论 Promise 最终是成功还是失败都会执行的回调。常用于收尾操作如隐藏 loading 动画、关闭数据库连接等。它不接收任何参数也无法知道 Promise 的结果。**静态方法直接在 Promise 构造函数上调用**4. **Promise.resolve()**返回一个成功的 Promise。如果传入的是普通值则直接包装为 resolved Promise如果传入的是 Promise则原样返回如果传入的是 thenable 对象有 then 方法会将其转换为 Promise。5. **Promise.reject()**返回一个失败的 Promise通常用于快速返回一个错误状态。6. **Promise.all()**接收一个 Promise 数组或可迭代对象等**所有** Promise 都成功时返回一个包含所有结果的数组只要有一个失败立即返回那个失败的 Promise。适合并发请求且需要全部成功的场景。7. **Promise.race()**返回**最先完成**无论成功或失败的那个 Promise 的结果。适合超时控制场景一个正常请求和一个定时器 reject 赛跑。8. **Promise.allSettled()**ES2020等待**所有** Promise 都有结果成功或失败返回每个 Promise 的状态和结果/原因。即使有失败也会等全部完成适合不关心失败、需要知道所有结果的场景。9. **Promise.any()**ES2021只要有一个 Promise 成功就返回那个成功的值如果**全部失败**才返回一个 AggregateError聚合错误。适合多个备选接口谁先成功就用谁的场景。**常用场景速记**- 串行异步任务链式调用 .then()- 并行且全部成功Promise.all()- 并行且只要最快结果Promise.race()- 并行且需要所有结果忽略部分失败Promise.allSettled()- 并行且只要一个成功Promise.any()- 统一错误处理.catch()- 无论成功失败都执行.finally()**注意点**- 不写 .catch() 的 Promise 错误会静默失败现代浏览器会警告 Unhandled Rejection- .then() 中的回调如果显式返回一个值它会被 Promise.resolve() 包裹- 如果不返回任何值下一个 .then() 会收到 undefined以上就是 Promise 的常用方法。14.