内存管理,函数,闭包

认识内存管理

  • 不管什么样的编程语言,在代码的执行过程中都是需要给它分配内存的,不同的是某些编程语言需要我们自己手动的管理内存,某些编程语言会可以自动帮助我们管理内存:
  • 不管以什么样的方式来管理内存,内存的管理都会有如下的生命周期
    1. 第一步:分配申请你需要的内存(申请);
    2. 第二步:使用分配的内存(存放一些东西,比如对象等);
    3. 第三步:不需要使用时,对其进行释放;
  • 不同的编程语言对于第一步和第三步会有不同的实现
    手动管理内存:比如C、C++ ,包括早期的OC,都是需要手动来管理内存的申请和释放的(malloc和free函
    数);
    自动管理内存:比如Java、JavaScript、Python、Swift、Dart等,它们有自动帮助我们管理内存;

JS的内存管理

JavaScript会在定义变量时为我们分配内存。

  • JS对于基本数据类型内存的分配会在执行时,直接在栈空间进行分配;
  • JS对于复杂数据类型内存的分配会在堆内存中开辟一块空间,并旦将这块空间的地址给变量引用;
    在这里插入图片描述

JS的垃圾回收

  • 因为内存的大小是有限的,所以当内存不再需要的时候 ,我们需要对其进行释放,以便腾出更多的内存空间
  • 在手动管理内存的语言中,我们需要通过一些方式自己来释放不再需要的内存,比如free函数:
    但是这种管理的方式其实非常的低效 ,影响我们编写逻辑的代码的效率;
    并县这种方式对开发者的要求也很高,并且一不小心就会产生内存泄露;
  • 所以大部分现代的编程语言都是有自己的垃圾回收机制
    垃圾回收的英文是Garbage Collection , 简称GC;
    对于那些不再使用的对象,我们都称之为是垃圾,它需要被回收,以释放更多的内存空间;
    而我们的语言运行环境,比如Java的运行环境IM,Javascript的运行环境js引擎都会内存 垃圾回收器
    垃圾回收器我们也会简称为GC,所以在很多地方你看到GC其实指的是垃圾回收器;
  • GC通过GC的算法知道哪些对象不再使用;常见的GC算法有两个:
    1. 引用计数
    • 当一个对象有一个引用指向它时 ,那么这个对象的引用就+1,当一个对象的引用为0时,这个对象就可以被销毁掉;
    • 这个算法存在一个弊端就是会产生循环引用
      在这里插入图片描述
    1. 标记清除
    • 这个算法是设置一个根对象( rootobject),垃圾回收器会定期从这个根开始,找所有从根开始有引用到的对象,对于那些没有引用到的对象,就认为是不可用的对象(如下图的M,N);
    • 这个算法可以很好的解決循环引用的问题
      在这里插入图片描述
  • JS引擎比较广泛的采用的是标记清除算法,类似于V8引擎为了进行更好的优化,它在算法的实现细节上也会结合一些其他的算法。

JS函数

1. JS中函数是一等公民

那么就意味着函数的使用是非常灵活的。

  • 它可以作为另外一个函数的参数;
function calc(num1, num2, calcFn){
	console.log(calcFn(num1, num2))
}
// 加法
function add(num1, num2){
	return num1 + num2
}
// 减法
function sub(num1, num2){
	return num1 - num2
}
// 乘法
function mul(num1, num2){
	return num1 * num2
}
var m = 20
var n = 10
calc(m, n, add)  // 调用加法,打印30
calc(m, n, sub)  // 调用减法,打印10
calc(m, n, mul)  // 调用乘法,打印200
  • 也可以作为另外一个函数的返回值来使用;
function makeAdder(count){
	function add(num){
		return count + num
	}
	return add
}
var add10 = makeAdder(10)
var add200 = makeAdder(200)
console.log(add10(20)) // 10 + 20 = 30
console.log(add10(30)) // 10 + 30 = 40
console.log(add200(20)) // 200 + 20 = 220
console.log(add200(30)) // 200 + 30 = 230
2. 高阶函数

如果一个函数接受另外一个函数作为参数(上面的calc函数),或者返回值是一个函数(上面的makeAdder函数),那么这个函数就称为高阶函数。
例如:数组中的函数使用
filter:过滤

var nums = [5, 10, 11, 100, 55]
var newNums = nums.filter(function(item){
	return item % 2 === 0
})
console.log(newNums)  // [10, 100]

map:映射

var nums = [5, 10]
var newNums = nums.map(function(item){
	return item * 10
})
console.log(newNums)  // [50, 100]

forEach:迭代(没有返回值;不能被return或break打断,除非抛异常;for循环可以被打断)

var nums = [5, 10, 11, 100, 55]
nums.forEach(function(item){
	console.log(item)
})

find / findIndex:查找

var friends = [
	{ name: "why", age: 18 },
	{ name: "james", age: 30 },
	{ name: "lucy", age: 20 },
]
var findFriend = friends.find(function(item){
	return item.name === 'lucy'
})
console.log(findFriend)  // { name: "lucy", age: 20 }
// findIndex:查找索引值
var friendIndex = friends.findIndex(function(item){
	return item.name === 'lucy'
})
console.log(friendIndex)  // 2

reduce:累加

var nums = [5, 10, 11, 100, 55]
var total = 0
// prevValue:上一次的值
var newNums = nums.reduce(function(prevValue,item){
	// prevValue: 0,item:5
	// prevValue: 5,item:10
	// prevValue: 15,item:11
	// prevValue: 26,item:100
	// prevValue: 126,item:55
	return preValue + item
}, 0)  // 这个0代表第一次的prevValue默认值,不写的话默认也是0
console.log(total)  // 181

闭包

由两部分组成:函数 + 可以访问的自由变量
JS中一个函数,如果访问了外层作用域的变量,那么它是一个闭包;
例如下面代码:

var name = "foo"
function bar() {
	console.log(name)  // 访问了外层作用域的变量
}
function foo() {
	var name = "foo"  // 可以访问的自由变量
	function bar() {  // 函数
		console.log("bar", name) // 访问了外层作用域的变量
	}
	return bar
}
var fn = foo()
fn()

闭包的内存泄漏

function foo() {
	var name = "foo"
	var age = 18
	function bar() {
		console.log(name)
		console.log(age)
	}
	return bar
}
var fn = foo()
fn()

执行过程解析:
var fn = foo()
这句代码 return 了bar函数,将bar赋值给fn,所以GO里面的 fn就指向了bar的内存地址0xb00(箭头3);
函数foo执行完之后,那foo的执行上下文就会从调用栈移除(下图第二个红框),按理说 foo函数对象(0xa00) 以及 foo的AO对象(0x200)都会同时被销毁,但是由于以下的引用关系,所以 0xa00 和 0x200 并不会销毁,而是继续存在内存中;

GO.foo -> 0xa00
GO.fn -> 0xb00 -> 0x200

执行fn()就是调用bar()函数,输出name和age,由于bar的AO对象是空的(下图没有画),所以向父作用域 0x200 查找,找到并输出;执行完之后 bar的函数执行上下文就会从调用栈移除(下图第一个红框)。
在这里插入图片描述
总结:
从以上过程可以看出,使用闭包函数的时候有可能会造成循环引用,导致了foo和bar无法释放内存,这就造成了内存泄漏。
如果想要解决内存泄漏的问题,只需要将fn 和 foo 指向一个空地址(null)即可。当垃圾收集器下次运行时,就会回收它们占用的内存。

fn = null
foo = null
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值