JavaScript中的内存管理
前端为什么关注内存
- 防止页面内存过大,引起客户端卡顿甚至无响应。
- Node.js使用V8引擎,需要后端持久性支持,后端更容易造成内存溢出。
JS数据类型与JS内存机制
- 原始数据类型: String, Boolean, Number, Null, Undefined, Symbol
- 引用数据类型: Object
- 内存空间:Stack, Heap
原始数据类型都存在栈内存中。 引用数据类型存储在堆内存中。
引用数据类型我们拿到的只是它储存在堆中的地址,即代码字符串;如果想要使用它,就需要加上()。比如声明的一个函数。
垃圾回收
JavaScript引擎找出那些不再使用的变量,然后释放其占用的内存,垃圾回收器会按照一定的时间周期执行这一操作。
-
引用计数
跟踪记录每个值被引用的次数,每被引用一次加一,释放一次减一,当这个值为0时,表示这个值不会被用到了,因此这块内存就会释放。 -
标记清除
当变量进入环境时被标记为“进入环境”,离开环境时标记为“离开环境”,垃圾回收器会清除那些标记为“离开环境”的值所占用的内存空间。
V8内存管理机制
v8限制内存的原因
- v8最初为浏览器设计,不太可能遇到大量内存的使用场景。
- 防止因为垃圾回收所导致的线程暂停而执行的时间过长
解决方法:
- 新生代垃圾回收:两个部分。From空间存放不需要回收的对象, To空间存放需要回收的对象, 当垃圾回收运行时,将To空间的对象全部进行回收。
- 老生代垃圾回收:是一个连续的结构。有标记清除和标记合并两种方案。
常用函数
memoize 缓存函数
缓存函数是将上次的计算结果缓存起来,当下次调用时,如果遇到相同参数, 就直接返回缓存中的数据。
let memoize = function(func, hasher){
var memoize = function(key){
var cache = memoize.cache;
var address = '' + (hasher? hasher.apply(this, arguments) : key);
if(!cache[address]) cache[address] = func.apply(this, arguments);
return cache[address];
}
memoize.cache = {}
return memoize;
}
缓存函数可以用于大量重复计算(依赖先前计算的结果),比如斐波拉契数列。
curry 柯里化函数
在数学与计算机技术中,柯里化是一种将使用多个参数的一个函数转化为使用一系列使用一个参数的函数的技术。
可以使用loadsh引入curry来实现柯里化。
柯里化是将一个多参函数转换为多个单参数函数,也就是将一个n元函数转换为n个一元函数。
f(a, b, c) = f(a)(b)©
偏函数
偏函数则是固定一个函数的一个或多个参数,也就是将一个n元函数转换为n-x函数。
f(a, b, c) = f(a, b)©
偏函数可以使用bind去实现,bind就是使函数拥有预设的初始参数。 当绑定参数被调用时,bind中的参数会被插入到绑定参数的开始位置。
高阶函数
高阶函数就是一个函数, 是将函数作为参数或者将函数作为输出返回。
map/reduce/filter/flat:
- map会返回新创建的数组。
- reduce接收一个函数作为累加器,为数组中的每一个元素 依次执行回调函数。reduce可用于数组去重。
- filter可以过滤并创建新的数组。
- flat可将数组扁平化。arr.flat(Infinity) 用于不知多少层级的扁平化处理。
高阶函数可以提高代码抽象度,实现最大程度的代码重用。
防抖与节流
防抖
防抖(debounce):当持续触发事件时,一定时间内没有再触发事件,事件处理函数才会执行一次;如果设定时间到来之前,又一次触发了事件,就重新开始延时。关键在于,在一个事件发生特定时间之后,才会执行特定事件。
let debounce = (fn, delay) => {
let timer = null;
return function(...args){
if(timer){
clearTimeout(timer);
}
//timer = setTimeout(function(){ 此时绑定在window上
timer = setTimeout(()=>{ //此时绑定在指定元素上
fn(...args);
},delay);
}
}
如果一直触发事件, 且每次触发事件时间间隔都小于delay?
节流
节流(throttle):当持续触发事件时,保证一定时间间隔内只触发一次事件处理函数。
若都是持续触发事件,防抖侧重于只执行最后一次,节流侧重于一段事件执行一次。
let throttle = (fn, delay)=>{
let flag = true;
return function(...args){
if(!flag) return;
flag = false;
setTimeout(()=>{
fn(...args);
falg = true;
}, delay);
}
}
防抖可用于:用于输入查询时防止多次请求数据造成资源的浪费。
节流可用于:对于鼠标移动和窗口滚动,鼠标的移动和窗口的滚动会带来大量的事件,但是在一段时间内又必须看到页面的效果。例如:对于可以拖动的div,如果使用debounce,那么div会在拖动停止后突然跳到目标位置;这时就需要使用节流。