js的垃圾回收机制与闭包

一、执行环境和作用域链

执行环境定义了函数或变量有权访问的数据范围,包括全局执行环境与局部执行环境,每个函数都有一个自己的局部执行环境。当执行到一个函数时,函数的环境会被推入到一个环境栈中。当函数执行完后,栈将其环境弹出,该环境被销毁,保存在其中的变量和函数定义也随之销毁。
当代码在一个环境中执行时,会创建变量对象的一个作用域链,保证环境内所有变量和函数的有权访问。需要注意的是,当我们在找一个变量的时候,是从当前执行环境一层层向上寻找,内部环境可以通过作用域链访问外部环境,而外部环境不能访问内部环境中的任何变量和函数。

var name = 'pzx'
function changename(){
    var anothername = 'pwj'
    function swapname(){
        var tempname = anothername
        anothername = name
        name = tempname
    }
    swapname()
}
changename()

上面的例子中,一共有三个执行环境,分别为全局执行环境、changename函数局部执行环境,swapname函数局部执行环境。swapname局部环境中可以访问到全局变量name和changename环境中的anothername变量。

二、垃圾回收机制

执行环境会负责管理代码执行过程中使用的内存,js中常用的垃圾回收机制包括标记清除和引用计数,当变量被标识为无用后就会自动清除,下面介绍一个这两种垃圾回收机制。
1、标记清除
垃圾收集器在运行的时候会给存储在内存中的所有变量都加上标记。然后,去除掉环境中的变量以及被环境引用的变量的标记。然后剩下的仍有标记的变量就是准备删除的变量,销毁这些值并回收他们占用的内存空间。
2、引用计数
这个方法会跟踪记录每个值被引用的次数,当引用次数变为0时,就可以将其占用的内存空间回收。但这个方法存在一个问题,当两个变量互相引用时,他们的引用次数永远不会是0,导致这部分内存永远不会回收,示例如下:

function fn(){
	var objA = new Object()
	var objB = new Object()
	objA.otherobj = objB
	objB.otherobj = objA
}

三、闭包

闭包是指在一个函数内部有权访问另一个函数作用域中的变量

function createComparisonFunction(propertyName){
    return function(object1,object2){
        var value1 = object1[propertyName]
        var value2 = object2[propertyName]
        if(value1<value2){
            return -1
        } else if(value1 > value2){
            return 1
        } else {
            return 0
        }
    }
}
var compare = createComparisonFunction('name')
var result = compare({name:'pjh'},{name:'pwj'})

上面的代码中匿名函数使用了外层函数中的propertyName,就形成了闭包。过程中形成的作用域链如图所示:
在这里插入图片描述
由此我们可以看出闭包存在的内存泄漏问题。当createComparisonFunction函数执行完后,其执行环境的作用域链会被销毁,但是,由于闭包的活动对象仍引用了这个函数的活动对象,它的活动对象就会保留在内存中。直到执行conpare=null解除对匿名函数的引用,匿名函数被销毁后,内存才被释放。
关于闭包的两个问题:

function createFunctions(){
    var result = new Array()
    for(var i=0;i<10;i++){
        result[i] = function(){
            return i
        }
    }
    return result
}
console.log(createFunctions())  //10个函数调用都返回10

上面的代码10个函数调用都返回10的原因在于每个函数的作用域链中都包含createFunctions的活动对象,因此它们引用的都是用一个变量i。要想达到每个函数调用返回不同的值,要按如下所示:

function createFunctions(){
    var result = new Array()
    for(var i=0;i<10;i++){
        result[i] = function(num){
            return function(){
                return num
            }
        }(i)
    }
    return result
}
console.log(createFunctions()[0]())  //第一个函数调用返回0

第二个问题是关于闭包函数的this指向问题。内部函数在搜索this和arguments这两个变量的时候有些不同,只会在自身的活动对象上搜索,不会访问到外部函数的这两个变量。因此,如果想访问外部作用域的this,就要先将外部作用域的this保存到一个闭包能访问的变量里。

var name = 'window'
var obj = {
    name:'myobj',
    getnamefunc: function(){
        var that = this
        return function(){
            return that.name
        }
    }
}
console.log(obj.getnamefunc()())  //myobj

四、闭包的作用及应用场景

1、通过匿名函数的自动执行来模拟块级作用域

function outputnum(count){
    (function (){
        for(var i=0;i<count;i++){
            console.log(i)
        }
    })()
    console.log(i)    //报错 匿名函数外访问不到i
}
outputnum(3)

2、实现方法和属性的私有化
一种方法是在构造函数内定义私有变量和函数,并在构造函数内利用闭包创建用于访问私有变量的公有方法。

function Person(name){
    //name为私有属性
    this.getName = function(){
        return name
    }
    this.setName = function(value){
        name = value
    }
}
var p = new Person('pjh')

在Person这个构造函数内,name为私有属性,getName和setName为公有属性。当我们创建了Person的实例,想改变实例的name属性只能通过公有方法来改变。这种方法针对每个实例都会创建一组同样的方法。
第二种方法是使用静态私有变量

(function(){
	//私有变量
    var name = ""
    //没有var声明 是全局构造函数
    Person = function(value){
        name = value
    }
    Person.prototype.getName = function(){
        return name
    }
    Person.prototype.setName = function(value){
        name = value
    }
})()
var p1 = new Person('pjh')
console.log(p1.getName())   //pjh
var p2 = new Person('pwj')
console.log(p2.getName())   //pwj
console.log(p1.getName())   //pwj

这种方法与构造函数方法不同的是,私有变量name是有实例共享的,一个实例改变了那么,另一个实例的name也会改变。
还可以给单例创建私有变量

var application = function(){
    //私有变量
    var components = new Array()
    components.push(new BaseComponent())
    var app = new BaseComponent()
    //公共接口
    app.getComponentCount = function(){
        return components.length
    }
    return app
}()
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值