一、讲讲封装继承多态
封装是基础:通过封装创建独立的类单元。
继承是拓展:通过继承建立类之间的关系。
多态是表现:通过多态实现接口的统一和行为的多样化。(同一接口,不同实现)
二、垃圾回收机制如何实现
自动释放不再使用的内存。
标记-清除算法:
1.标记阶段:垃圾回收器会遍历所有对象,从根对象(如全局对象)开始,将所有可达对象标记为活动的。
2.清除阶段:没有标记的对象将其内存释放。
缺点:1.期间需要暂停程序运行,会影响性能。2。需遍历整个对象图,对大对象图效率较低。
引用计数法:
1.计数2.增加引用3.减少引用4.释放内存(计数器为0)
缺点:两个对象互引/每次更新计数带来性能开销。
分代垃圾回收:
1.标记-清除:对新生代对象进行快速标记和清除。2.标记-压缩:老生代进行标记和压缩。3.增量标记:将垃圾回收拆分成多个小步骤,穿插在正常的程序执行过程中,减少程序暂停时间。
预防措施:1.避免使用全局变量:用var、let和const声明变量。2.移除不再使用的时间监听器。3.清除定时器。
4.及时清理过多的缓存。5.避免使用大量的闭包。
三、闭包的定义、使用场景
闭包是指有权访问另一个函数作用域中的变量的函数。
1.当函数执行时,会创建一个执行上下文(包含变量对象、作用域链等)
2.正常情况下,函数执行完毕后,其执行上下文会被销毁
3.但如果函数内部定义了另一个函数,并且这个内部函数引用了外部函数的变量
4.即使外部函数执行完毕,由于内部函数仍然可能被调用,JavaScript 引擎会保留外部函数的变量对象
5.这样就形成了闭包
闭包的使用场景
1.数据封装与私有状态
在 Vue 中,闭包可以用来封装组件的私有状态,避免外部直接访问和修改内部数据。
例如,使用闭包可以隐藏组件内部的逻辑,只暴露必要的接口。
const MyComponent = {
data() {
return {
privateData: 'This is private'
};
},
methods: {
getPrivateData() {
return this.privateData;
}
}
};
在这个例子中,privateData 是组件内部的私有数据,外部无法直接访问,但可以通过 getPrivateData 方法获取。
2.事件处理与回调函数
闭包常用于事件处理和回调函数中,捕获外部变量并在事件触发时使用。
例如,在 Vue 的事件绑定中,闭包可以捕获 this 的上下文。
<template>
<button @click="handleClick">Click me</button>
</template>
<script>
export default {
data() {
return {
count: 0
};
},
methods: {
handleClick() {
this.count++;
console.log(`Button clicked ${
this.count} times`);
}
}
};
</script>
在这个例子中,handleClick 方法捕获了 this.count,即使事件触发时外部函数已经执行完毕,handleClick 仍然可以访问和修改 this.count。
3.异步操作与生命周期钩子
在 Vue 的生命周期钩子中,闭包可以捕获组件的状态,并在异步操作完成时使用。
例如,在 mounted 钩子中发起异步请求,并在请求完成时更新组件的状态。
<template>
<button @click="handleClick">Click me</button>
</template>
<script>
export default {
data() {
return {
count: 0
};
},
methods: {
handleClick() {
this.count++;
console.log(`Button clicked ${
this.count} times`);
}
}
};
</script>
在这个例子中,fetch 的回调函数捕获了 this.userData,并在异步请求完成后更新组件的状态。
4.高阶组件与函数式组件
在 Vue 中,高阶组件(HOC)和函数式组件(Functional Components)中也经常使用闭包来封装逻辑。
例如,使用高阶组件封装通用逻辑。
function withUser(Component) {
return {
data() {
return {
userData: null
};
},
mounted() {
fetch('/api/user')
.then(response => response.json())
.then(data => {
this.userData = data;
});
},
render() {
return <Component userData={
this.userData} />;
}
};
}
const UserProfile = withUser({
props: ['userData'],
render() {
return <div>{
this.userData.name}</div>;
}
});
在这个例子中,withUser 是一个高阶组件,它捕获了 userData 并将其传递给子组件。
闭包解决的问题
1.数据封装与隐藏
闭包可以隐藏内部状态,防止外部直接访问和修改,从而保护数据的完整性。
例如,组件内部的私有数据可以通过闭包封装,只暴露必要的接口。
2.异步操作与状态管理
闭包可以捕获异步操作中的状态,确保在异步操作完成时仍然可以访问和修改这些状态。
例如,在 Vue 的生命周期钩子中,闭包可以捕获组件的状态,并在异步请求完成后更新状态。
3.事件处理与回调
闭包可以捕获事件处理函数中的上下文,确保在事件触发时可以访问和修改组件的状态。
例如,在 Vue 的事件绑定中,闭包可以捕获 this 的上下文,确保事件处理函数可以正常工作。
4.代码复用与模块化
闭包可以封装通用逻辑,使其可以在多个地方复用。
例如,高阶组件可以通过闭包封装通用逻辑,并将其传递给子组件。
注意事项
1.内存泄漏
闭包会捕获外部变量,如果这些变量占用大量内存且没有被正确释放,可能会导致内存泄漏。
例如,在 Vue 中,如果组件被销毁但事件监听器没有被移除,可能会导致内存泄漏。
2.上下文问题
在闭包中使用 this 时,需要注意上下文的变化,确保 this 指向正确。
例如,在 Vue 的事件处理函数中,可以使用箭头函数或 bind 方法确保 this 指向组件实例。
3.性能问题
闭包可能会导致函数执行效率降低,尤其是在捕获大量变量或嵌套闭包的情况下。
例如,在 Vue 中,避免在渲染函数中频繁创建闭包,以免影响性能。
四、foreach和map的区别
foreach:用于遍历集合,执行操作,没有返回值。
map:用于转换集合中的元素,返回一个新的集合。
五、js的基本数据类型和typeof后的结果
1.原始类型:
· undefined
· Null
· Boolean
· Number
· String
· Symbol(用于表示唯一的、不可变的值。它常用于对象的属性键,以确保属性名的唯一性,避免命名冲突)
· BigInt(使用 BigInt() 构造函数或在数字后面加上 n 表示。INT 是4字节,BIGINT 是8字节)
存储位置
原始类型的值直接存储在栈内存中。栈内存用于存储变量的值,访问速度快,但空间有限。
2.引用类型(Object)
· Array
· Function
· Date
· RegExp
· 其他:包括Map、Set、WeakMap(必须输入对象、内存回收)、WeakSet(键必须是对象、内存回收)等等
存储位置
引用类型的值存储在堆内存中,变量存储的是指向堆内存中对象的引用(指针)。堆内存用于存储对象的实际数据,访问速度相对较慢,但空间较大。
| 类型 | 返回值 | 备注 |
|---|---|---|
| Undefined | “undefined” | 当变量未被定义或未赋值时,返回此值。 |
| Null | “object” | 历史遗留问题,null 被错误地识别为对象。 |
| Boolean | “boolean” | 适用于 true 或 false 值。 |
| Number | “number” | 适用于整数和浮点数(包括特殊值 NaN 和 Infinity)。 |
| String | “string” | 适用于字符串(例如 “hello”)。 |
| BigInt | “bigint” | 适用于任意大的整数(例如 10n)。 |
| Symbol | “symbol” | 适用于 Symbol 类型。 |
| Function(classes) | “function” | 适用于可调用的对象(如函数和类定义)。 |
| 其他对象 | “object” | 包括数组、普通对象、日期对象、正则表达式等非函数对象。 |
注意:
1.typeof null === “object” 在 JavaScript 最初的实现中,JavaScript 中的值是由一个表示类型的标签和实际数据值表示的。对象的类型标签是 0。由于 null 代表的是空指针(大多数平台下值为 0x00),因此,null 的类型标签是 0,typeof null 也因此返回 “object”
2.实际使用 对于更复杂的类型检测,可以使用工具函数,如 Object.prototype.toString.call() 或第三方库(如 lodash)。
六、什么是内存泄漏?内存泄露的常见原因?如何排查?
内存泄露是指在程序运行过程中,程序未能释放不再使用的内存空间,导致内存资源被浪费。
常见原因:
1.意外的全局变量:忘记使用 var,let,const 声明变量时,变量会被挂载到全局对象上
2.闭包:闭包中引用了不再需要的外部变量,导致这些变量无法被垃圾回收
3.未清理的 DOM 引用:删除 DOM 元素时,未能清理相关的事件监听器或引用
4.定时器和回调:未能清理不再需要的 setInterval(循环执行) 或 setTimeout(延迟执行)回调
排查手段:
1.使用内存分析工具
· 浏览器开发者工具:Chrome 的 DevTools 提供了内存分析工具 Memory,可以监控内存使用情况
· 也可以结合 setInterval 使用 console.memory 查看内存使用的快照
2.代码审查
· 检查代码中是否有未释放的事件监听器,定时器,全局变量,确保不再需要某对象时,即使解除引用
3.性能监控
· 监控应用程序的内存使用情况,观察是否有持续增长的趋势
· 使用日志记录内存使用情况,帮助识别内存泄露的模式
七、Promise方法
Promise 是 JavaScript 中用于处理异步操作的对象。它代表了一个可能尚未完成的操作的最终完成(或失败)及其结果值。Promise 有三种主要状态:
- Pending(进行中)
描述:这是 Promise 的初始状态,表示异步操作尚未完成。
特点:在这个状态下,Promise 既不是成功也不是失败。 - Fulfilled(已成功)
描述:表示异步操作成功完成。
特点:在这个状态下,Promise 的结果值被确定,并且可以通过 .then() 方法获取。 - Rejected(已失败)
描述:表示异步操作失败。
特点:在这个状态下,Promise 的失败原因被确定,并且可以通过 .catch() 方法捕获。
状态流转
Promise 的状态流转是单向的,具体规则如下:
- Pending → Fulfilled
当异步操作成功完成时,Promise 的状态从 Pending 转变为 Fulfilled。
一旦变为 Fulfilled 状态,Promise 的结果值被确定,不会再改变。 - Pending → Rejected
当异步操作失败时,Promise 的状态从 Pending 转变为 Rejected。
一旦变为 Rejected 状态,Promise 的失败原因被确定,不会再改变。
重要特性
不可逆性:Promise 的状态一旦从 Pending 转变为 Fulfilled 或 Rejected,就不能再改变。也就是说,Promise 的状态是不可逆的。

最低0.47元/天 解锁文章
1552

被折叠的 条评论
为什么被折叠?



