垃圾回收
栈内存释放
函数上下文执行完成后,若不存在闭包区域,则释放 全局上下文只有在关闭页面、刷新页面时才会释放,(eg:vuex、redux的全局变量存储)
堆内存释放
- IE方案:引用计数 可能会产生计数混乱从而泄露内存 让所有引用堆内存地址的变量全部赋值为null(或改指向),使得没有变量来引用它,此时这个堆内存就会被浏览器GC回收,从而释放堆内存
- 谷歌方案:查找引用 浏览器会在空闲的时间段内,检查所有堆内存是否被引用,若没有被任何一个变量引用,则释放
闭包问题
闭包垃圾回收
let x = 10;
function fn(){
function f(){}
// fn执行时,fn内部的f会被外部的window引用,形成不销毁的EC(fn)
window.f = f;
}
// fn执行产生一个EC(fn)且不被销毁
fn();
// 取消堆中函数的引用,fn指向的堆内存释放
fn = null;
// f仍然能正常执行,虽然fn存储在堆内存中的描述字符串被回收了所以不能fn()了,但EC(fn)还存在
f();
// f堆内存释放,EC(fn)栈内存不存在闭包区域,则栈内存释放
// window.f = null
闭包编程技巧
JS单例
高级单例模式,在全局下进存储命名空间,只要命名空间不冲突,则其内部属性方法一定不冲突。使用一块独立的空间进行某种活动称为单例(体现为一个大对象管理信息)。
// utils_a为一个单例
let utils_a = (function () {
//此处的fn是AO(IIFEA)的私有变量
let a = 0
function fn () {}
……
// 外部命名空间utils_a引用此处暴露的接口,可以供别的模块(命名空间)调用
return {
a,
fn
}
})()
// utils_b为一个单例
let utils_b = (function () {
//此处的fn是AO(IIFEB)的私有变量
function fn () {}
……
return {
fn
}
})()
惰性函数
惰性函数不能具名(因为具名后,进行重定向操作时,找到的函数名不是全局的,而是具名的函数签名,这个签名不能更改导致重定向失败)
/**需求:
* 获取元素的样式,使用window.getComputedStyle(element).属性名获取,但不兼容IE678
* 若不兼容,则使用element.currentStyle(属性名)
*/
function get_css(element, attribute_name) {
// 多次调用get_css会多次进行校验,实际需求只需一次校验即可
if('getComputedStyle' in window) {
return window.getComputedStyle(element)[attribute_name]
}
else {
return element.currentStyle(attribute_name)
}
}
//改造(不能写成let get_css = function get_css(){})
let get_css = function (element, attribute_name){
// 得到校验结果直接重定向get_css,出第一次外的函数执行都将跳过校验
if('getComputedStyle' in window) {
// 重定向的function被全局变量get_css引用,则形成闭包
get_css = function (element, attribute_name){
return window.getComputedStyle(element)[attribute_name]
}
}else {
get_css = function (element, attribute_name){
return element.currentStyle(attribute_name)
}
}
//第一次走了校验的函数执行也需要有结果
return get_css(element, attribute_name)
}
// 使用
get_css(box, height);
get_css(box, width);
柯里化
核心思想是“预存储,预处理”。造成闭包在本级上下文中保存一些信息,供下级上下文调用。
// ==========================柯里化实现链式调用=========================
// curring返回的函数能实现链式调用求和
let curring = () => {
// 此处的上下文空间可以作为add函数的闭包数据存储空间
let storeArr = [];
// 柯里化返回
const add = (...args) => {
storeArr = [...storeArr, ...args];
return add;
}
// 不知道链式次数,最后返回的还是一个函数,当使用返回值时,意味着需要数组的求和值,
// 可以控制add函数的转化输出来得到求和值
add[Symbol.toPrimitive] = function () {
return storeArr.reduce((total,next) => total + next);
}
return add;
}
let add1 = curring();
console.log(add(1)(2)(3)) // 6
let add2 = curring();
console.log(add(1, 2, 3)(4)) // 10
compose组合函数
const add = x => x + 1;
const minus = x => x - 1;
const multiply = x => x * 2;
// 组合函数,依次对操作对象handle执行用户指定的函数序列
const compose = (...funcs) => {
return function (handle) {
return funcs.reduce((res, next) => {
res = next(res);
return res;
}, handle)
}
}
let op = compose(add, add, multiply, minus);
debugger;
let res = op(3)
console.log(res)