目录
JSON.parse(JSON.stringify(obj))
十一、exports和module.exports有什么区别
十三、Commonjs、AMD、CMD、UMD、ESM 都有什么区别
一、内存泄露
1、是什么
不再用的内存没有被及时释放出来,导致该段内存无法被使用就是内存泄漏
2、导致的原因
我们无法在通过js访问某个对象,而垃圾回收机制却认为该对象还在被引用,因此垃圾回收机制不会释放该对象,导致该块内存永远无法释放,积少成多,系统会越来越卡以至于崩溃
二、垃圾回收机制的策略
1、标记清除法
垃圾回收机制获取根并标记他们,然后访问并标记所有来自它们的引用,然后在访问这些对象并标记它们的引用…如此递进结束后若发现有没有标记的(不可达的)进行删除,进入执行环境的不能进行删除
2、引用计数法
当声明一个变量并给该变量赋值一个引用类型的值时候,该值的计数+1,当该值赋值给另一个变量的时候,该计数+1,当该值被其他值取代的时候,该计数-1,当计数变为0的时候,说明无法访问该值了,垃圾回收机制清除该对象
缺点: 当两个对象循环引用的时候,引用计数无计可施。如果循环引用多次执行的话,会造成崩溃等问题。所以后来被标记清除法取代。
三、浅拷贝和深拷贝
1、浅拷贝
只会对对象本身进行复制,不会复制对象中的属性(或元素),对象与被复制的对象指向的还是同一个内存地址。
数组方法:
-
.slice()
-
...展开运算符
对象方法:
-
Object.assign(目标对象, 被复制的对象)
-
...展开运算符
- Object.assign({}, obj1)
2、深拷贝
深拷贝指不仅复制对象本身,还复制对象中的属性和元素
方法:
-
structuredClone() 浏览器提供
-
JSON.parse(JSON.stringify(obj))
手写深拷贝: 见 =》
四、为什么JS是单线程
因为JS里面有可视的Dom,如果是多线程的话,这个线程正在删除DOM节点,另一个线程正在编辑Dom节点,导致浏览器不知道该听谁的
五、Promise 的原理
六、宏任务和微任务都有哪些
- 宏任务:
script
、setTimeOut
、setInterval
、setImmediate
- 微任务:
promise.then、
process.nextTick
、Object.observe
、MutationObserver
- 注意:Promise是同步任务
七、宏任务和微任务都是怎样执行
- 执行宏任务script,
- 进入script后,所有的同步任务主线程执行
- 所有宏任务放入宏任务执行队列
- 所有微任务放入微任务执行队列
- 先清空微任务队列,
- 再取一个宏任务,执行,再清空微任务队列
- 依次循环
八、变量和函数怎么进行提升的?优先级是怎么样的?
- 对所有函数声明进行提升(除了函数表达式和箭头函数),引用类型的赋值
- 开辟堆空间
- 存储内容
- 将地址赋给变量
- 对变量进行提升,只声明,不赋值,值为
undefined
九、var let const 有什么区别
var
- var声明的变量可进行变量提升,let和const不会
- var可以重复声明
- var在非函数作用域中定义是挂在到window上的
var
命令和function
命令声明的全局变量,依旧是顶层对象的属性;另一方面规定,let
命令、const
命令、class
命令声明的全局变量,不属于顶层对象的属性
(顶层对象,在浏览器环境指的是window
对象,在 Node 指的是global
)
let
- let声明的变量只在局部起作用
- let防止变量污染
- 不可在声明
const
- 具有let的所有特征
- 不可被改变:不可改变只适用于直接地址。如果使用const声明的是对象的话,是可以修改对象内部的值的
const
声明的变量不得改变值,const
一旦声明变量,就必须立即初始化,不能留到以后赋值const
实际上保证的,并不是变量的值不得改动,而是变量指向的那个内存地址所保存的数据不得改动:对于简单类型的数据(数值、字符串、布尔值),值就保存在变量指向的那个内存地址,因此等同于常量。但对于复合类型的数据(主要是对象和数组),变量指向的内存地址,保存的只是一个指向实际数据的指针,const
只能保证这个指针是固定的(即总是指向另一个固定的地址),至于它指向的数据结构是不是可变的,就完全不能控制了
十、模块化
1、为什么要使用模块化
- 防止命名冲突
- 更好的分离,按需加载
- 更好的复用性
- 更高的维护性
2、实现模块化的方法
早期:命名空间、闭包
现在:CommonJS、AMD模块、ES6模块
十一、exports
和module.exports
有什么区别
- 导出方式不一样
exports.xxx='xxx'
module.export = {}
exports
是module.exports
的引用,两个指向的是用一个地址,而require能看到的只有module.exports
十二、ESM 和 commonjs 的区别
- commonjs 是运行时加载 ;ESM 是编译时加载
- commonjs 是同步加载模块;ESM 是异步加载模块
- commonjs 是对值的浅拷贝;ESM 是对值的引用,而且不可修改(直接地址不可修改,类似于 const)
十三、Commonjs、AMD、CMD、UMD、ESM 都有什么区别
1、Commonjs:是同步执行的,不适合前端,后端 nodejs 可以使用 commonjs。
2、AMD/CMD/UMD 适用前端 ,异步执行。
3、ESM使用 export 、 export default 来导出模块,使用 import 来引入模块
4、AMD 和 CMD 的差别是
AMD 是依赖前置(把依赖放在前面)、提前执行(即使没有用到某个模块,也会提前执行)
CMD依赖就近、延时执行(用到的时候在声明依赖)
十四、require 和 import的区别
调用时机
- require 是运行时调用,所以其实是可以放在任何地方的
- Import 是编译时调用,所以必须放在文件的开头
使用时
- require 需要使用 module.exports = fs 或者exports.fs = xxx
- import 用 export default 或 export const xx
解构赋值
- require 是赋值的过程
- import 是解构的过程
十五、箭头函数和普通函数的区别
- 箭头函数是普通函数的简写,但是它不具备很多普通函数的特性
- 箭头函数的this指向它定义时所在的对象,而不是调用它的对象
- 不会进行函数提升
- 没有arguments对象,不能使用arguments,如果要获取参数的话可以使用rest运算符
- 没有yield属性,不能作为生成器Generator使用
十六、箭头函数可以当做构造函数 new 吗
不能new。因为:没有自己的this,不能调用call和apply。没有prototype,new关键字内部需要把新对象的_proto_
指向函数的prototype。