文章目录
笔试题
1. 实现一个深拷贝
function deepcopy(obj)
{
if(typeof obj!=='Object'||obj===null)
return obj
else
{
let newobj=Array.isArray(obj)? []:{}
for(let key in obj)
{
if(obj.hasOwnProperty(key))
{
newobj[key]=deepcopy(obj[key])
}
}
return newobj
}
}
2. 扁平化数组
指将嵌套的多维数组转换为一个一维数组
function flattenArray(arr)
{
let newArray=arr.reduce((acc,curr)=>
{
if(Array.isArray(curr))
{
return acc.concat(flattenArray(curr))
}
else
return acc.concat(curr)
},[])
return newArray
}
const nestedArray = [1, [2, [3, 4], 5], 6];
const flattenedArray = flattenArray(nestedArray);
console.log(flattenedArray); // 输出 [1, 2, 3, 4, 5, 6]
3. 数组去重
//方法1
function removeRepeatArray(arr)
{
let newArray=arr.reduce((acc,curr)=>
{
if(!acc.includes(curr))
acc.push(curr)
return acc
},[])
return newArray
}
//方法2
function removeRepeatArray(arr)
{
return Array.from(new Set(arr))
}
4. 实现一个闭包
function closeFunc()
{
let a=1
function hh()
{
a++
console.log(a)
}
return hh
}
const innerFunc = closeFunc(); // 调用 closeFunc,得到内部函数的引用
innerFunc(); // 执行返回的内部函数 hh,输出 2
innerFunc(); // 继续执行内部函数,输出 3
一面
1. 计算一个字符在字符串中出现的次数
//方法1
function countString(char,str)
{
let count=0
for(let i=0;i<str.length;i++)
{
if(char===str.charAt(i))
count++
}
return count
}
//方法2
function countString(char,str)
{
const regex=new RegExp(char,'g')
let arr =[]
arr=str.match(regex)
return arr.length
}
const char = 'a';
const str = 'banana';
const count = countString(char, str);
console.log(count); // 输出 3
2. for of和for in 的区别
for...of
和 for...in
是 JavaScript 中两种不同的迭代循环语句,它们在使用上有一些区别。
-
for...of
循环:-
用于遍历可迭代对象(例如数组、字符串、Set、Map、Generator 等)的值。
-
循环体内直接访问每个元素的值,而不是索引或键。
-
不适用于遍历普通对象(Plain Object)。
-
语法:
for (const value of iterable) { // 循环体 }
-
示例:
const arr = [1, 2, 3]; for (const num of arr) { console.log(num); // 输出 1, 2, 3 }
-
-
for...in
循环:-
用于遍历普通对象(Plain Object)的可枚举属性。
-
循环体内访问的是每个属性的键(属性名)。
-
也可以用于遍历数组或类数组对象,但可能会出现意外结果,因为它遍历的是对象的键而不是索引。
-
语法:
for (const key in object) { // 循环体 }
-
示例:
//遍历对象 const obj = { a: 1, b: 2, c: 3 }; for (const key in obj) { console.log(key); // 输出 a, b, c } //也可以遍历数组(会有问题) const arr = [8, 5, 9]; for (const index in arr) { console.log(index); // 输出 0, 1, 2 } //但是会有问题。 //问题如下 Array.prototype.customMethod = function() { console.log('Custom method'); }; const arr = [8, 5, 9]; for (const index in arr) { console.log(index); // 输出 0, 1, 2, "customMethod" } //解答:因为for...in 循环遍历了数组的原型链上的属性 //解决 Array.prototype.customMethod = function() { console.log('Custom method'); }; const arr = [8, 5, 9]; for (const index in arr) { if(arr.hasOwnProperty(index)) //判断是否是自身的属性,而不是原型上的属性 { console.log(index); // 输出 0, 1, 2 } }
-
总结:
for...of
用于遍历可迭代对象的值。for...in
用于遍历对象的可枚举属性,也可用于遍历数组的键。- 在处理数组时,推荐使用
for...of
,而在处理对象时,推荐使用for...in
。
3. let const 的区别
- 可变性:
let
声明的变量是可变的(mutable),意味着你可以重新赋值给这个变量。const
声明的变量是不可变的(immutable),意味着一旦被赋值就不能再改变。
- 作用域:
- 无论是
let
还是const
声明的变量都具有块级作用域,它们在定义它们的块中可见,并且在该块结束后将被销毁。
- 无论是
- 重复声明:
- 在同一个作用域内,
let
允许你重新声明同名变量,而const
则不允许。
- 在同一个作用域内,
- 初始化:
let
声明的变量可以在声明后稍后初始化。const
声明的变量必须在声明时进行初始化,且一旦初始化后就不能再重新赋值。
- 全局对象属性:
- 用
let
或const
声明的全局变量不会成为全局对象的属性(在浏览器中是window
对象),而用var
声明的全局变量会成为全局对象的属性。
- 用
4. filter使用方法
是 JavaScript 数组的一个高阶函数,用于筛选数组中满足条件的元素,返回一个新的数组。
const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
let evenNumbers=numbers.filter((ele,index,Arry)=>
{
console.log(ele,index,Arry)
//ele 为当前元素值,index为索引,Arry为原数组
return ele%2===0
})
console.log(evenNumbers);
5. 数组遍历有哪些方法
- for循环
- for of
- for in 需要使用hasProperty判断
- forEach
- map
- reduce
- filter
6. vue2 vue3 响应式原理分析
Vue 2 和 Vue 3 的响应式原理有所不同。下面分别对 Vue 2 和 Vue 3 的响应式原理进行简要分析。
Vue 2 的响应式原理
Vue 2 使用了基于 Object.defineProperty
的劫持(Object.defineProperty-based Observer)来实现响应式。
- 数据劫持:Vue 2 在组件初始化时,会对组件的
data
数据进行递归遍历,并通过Object.defineProperty
将每个属性转化为 getter/setter。这样一来,当访问或修改这些属性时,Vue 2 就能够监听到,并触发相应的依赖追踪和更新操作。 - 依赖追踪:Vue 2 通过在 getter 中收集依赖,在访问属性时将依赖关系建立起来。每个属性都有一个对应的依赖收集器(Dep),用于存储依赖关系。
- Watcher:在模板编译过程中,Vue 2 会解析模板中的表达式,并创建对应的 Watcher。Watcher 会在初始化时触发一次属性的 getter,从而建立起属性与 Watcher 之间的关联。当属性发生变化时,会通知相关的 Watcher 进行更新。
- 派发更新:当属性发生变化时,会触发对应的 setter,setter 中会通知属性对应的依赖收集器(Dep)派发更新。Dep 会遍历所有相关的 Watcher,并调用 Watcher 的更新函数,进而更新视图。
Vue 3 的响应式原理
Vue 3 使用了基于 ES6 的 Proxy 对象来实现响应式。相比 Vue 2 的劫持方式,Vue 3 的响应式原理更加简洁高效。
- Proxy 对象:Vue 3 在组件初始化时,创建了一个根级的响应式代理对象,该对象会代理组件的
data
数据。Proxy 对象可以拦截对代理对象的访问和修改操作。 - 依赖追踪:Vue 3 使用了一个全新的响应式引擎,利用了 JavaScript 语言自身的特性。在访问代理对象时,会收集依赖关系,并建立起属性与依赖之间的映射关系。
- Reactivity API:Vue 3 提供了一组 API(如
reactive
、ref
、computed
等)用于创建响应式数据,这些 API 在内部会使用 Proxy 对象来实现响应式。 - 副作用追踪:Vue 3 使用了类似于 React Hooks 的方式,通过
effect
函数来追踪副作用。effect
函数会在组件渲染时执行,并自动追踪其中访问的响应式数据,并建立起响应式数据与副作用之间的关联。 - 派发更新:当响应式数据发生变化时,会自动触发更新,重新执行相关的副作用函数,同时通知组件进行局部更新,以保持视图与数据的同步。
总的来说,Vue 2 和 Vue 3 都使用了不同的技术手段来实现响应式。Vue 2 使用了 Object.defineProperty
进行数据劫持,而 Vue 3 则利用了 ES6 的 Proxy 对象来实现响应式,使得代码更加简洁高效。
7. vue3升级优化分析
Vue 3 相对于 Vue 2 在性能和开发体验上都有一系列的优化和改进。以下是一些主要的升级优化分析:
- 更好的性能:
- 虚拟 DOM 重写:Vue 3 对虚拟 DOM 进行了重写,使其更加高效。新的虚拟 DOM 实现减少了许多不必要的操作,并提高了渲染性能。
- 静态提升:Vue 3 在编译阶段能够识别出静态节点,并将其提升为常量,减少了运行时的开销。
- Tree-shaking 支持:Vue 3 支持更好的 Tree-shaking,使得打包工具能够更好地优化代码,减少打包体积。
- 更小的体积:
- 模块化构建:Vue 3 的代码更加模块化,可以按需加载,从而减少了整体的体积。
- Composition API 的模块化引入:Vue 3 的 Composition API 具有模块化的特性,使得开发者可以更加灵活地组织代码,从而减少了代码量。
- 更好的 TypeScript 支持:
- 全面支持 TypeScript:Vue 3 的代码基本上是用 TypeScript 编写的,因此对 TypeScript 的支持更加全面和友好。
- 更好的类型推断:Vue 3 在编译时会生成更加准确的类型定义,使得开发时能够获得更好的类型推断和代码提示。
- 更灵活的组件设计:
- Composition API:Vue 3 引入了 Composition API,使得组件的逻辑可以更加灵活地组织,提高了代码的可维护性和可复用性。
- Teleport 和 Suspense:Vue 3 新增了 Teleport 和 Suspense 这两个特性,使得开发者能够更灵活地控制组件的渲染位置和加载状态。
- 更好的开发体验:
- 更快的开发速度:通过 Composition API 和 TypeScript 的支持,使得开发者能够更快地编写和调试代码。
- 更好的错误提示:Vue 3 对错误提示进行了改进,使得开发者能够更容易地定位和解决问题。
总的来说,Vue 3 在性能、体积、开发体验和灵活性等方面都有明显的优化和改进,是一个更加成熟和强大的框架。因此,对于使用 Vue 的项目来说,考虑升级到 Vue 3 是一个非常值得的选择。