基础
数据类型
原始类型(7种):
undefined, null, boolean, number, string, symbol, bigint
引用类型(1种):
object(含 Array、Function、Date 等)
判断类型
function getType(value) {
if (value == null) return value + ''; // 'null' or 'undefined'
return Object.prototype.toString.call(value).slice(8, -1).toLowerCase();
}
作用域
作用域就是变量的活动范围,决定了谁可以访问它。
- 词法作用域: 是 作用域的查找规则,函数在定义时所在的位置决定了它能访问那些变量,而不是在调用时的位置。作用域在代码书写时就确定了。
作用域的边界单位:
- 全局作用域:通常指的是window对象
- 函数作用域:变量在函数执行时创建、在执行结束时销毁,除非被闭包引用,函数外部无法访问。
- 块级作用域:在大括号{} if、for、switch 或使用let、const生命的变量,只在该代码块内可见
作用域链
作用域链是js引擎查找变量的路径机制,当访问一个变量的时候,会从当前作用域开始,如果找不到会逐层向外查找,直到全局作用域,找不到则报错。
作用域链的结构是在函数定义时就确定的,而不是在调用时,所以变量的查找路径是基于代码的书写结构。
闭包
首先 闭包的定义是:当函数可以记住并访问所在的词法作用域时,就产生了闭包,即使函数是在当前词法作用域之外执行。
更通俗地说:当内部函数引用了外部函数的变量,并且这个内部函数在外部函数执行完后仍然存在(比如被返回或传递出去),就形成了闭包。
闭包的产生有几个条件:
1.存在嵌套函数
2.内部函数引用了外部函数的变量
3.外部函数返回了内部函数,或以某种方式使内部函数在外部函数执行结束后仍可被访问。
总结就是 闭包 = 内部函数 + 引用外部变量 + 被外部持有引用
闭包有什么作用?
- 封装私有状态
- 回调函数中保持上下文状态
- 模块模式用IIFE+闭包实现命名空间和接口暴露
- 防抖/节流函数也是通过闭包缓存定时器ID和上一次执行时间
- 包括Reacthooks,useState的状态之所以能在re-render之间保持,也是靠闭包保存Fiber节点的状态。
闭包的缺点?
- 内存泄漏:闭包会阻止被引用的变量被垃圾回收,如果闭包长时间存在,会导致内存占用持续增长。
- 调试困难:由于闭包中的变量不在当前作用域可见,调试时可能难以追踪变量来源,尤其在复杂潜逃中。
内存泄漏
内存泄漏是指程序分配的内存无法被回收,导致可用内存逐渐减少,最终可能引发性能问题或程序崩溃。
导致内存泄漏的几种方式
- 意外的全局变量,在函数里面忘记使用var/let/const 导致变量挂在到全局对象,或者函数内部使用this,但函数被当作普通函数调用,this指向全局对象。
- 定时器、事件监听器等 没有被清除。
- 脱离DOM的引用,比如将DOM元素存储在对象中。
- 未清理的websocket或者EventSource
- 持有大对象或数组的引用,切不需要时未释放。
如何避免内存泄漏
- 使用严格模式
- 及时清除定时器和事件监听器、Websocket、EventSource等连接
- 谨慎使用闭包,并且注意释放
- 平时还可以用工具检测比如chrome DevTools的Memory面板