JavaScript——闭包理解

JavaScript——闭包理解

一、闭包是什么,如何使用?

闭包指的是函数对象能够经过做用域链相互关联起来,函数体内部的变量均可以保存在函数做用域内,也就是说闭包有权访问另外一个函数做用域中的变量的函数。
下面是一个简单闭包的函数性能优化

function mackFn() {
    var name = "Husky"
    function sayName () {
        console.log('name:' + name)
    }
    return sayName
}
var myFn = mackFn()
myFn()

mackFn()建立了一个局部变量name和一个名为sayName()的函数。sayName()是定义在mackFn()里的内部函数,sayName()能够访问到外部函数的变量,因此sayName()可使用父函数mackFn()函数中声明的变量name。myFn是执行了mackFn时建立的sayName函数实例的引用,且mackFn()函数造成闭包,因此sayName实例仍可访问其词法做用域的变量,便可以访问name。闭包

二、闭包的做用有哪些?

闭包其实用处很大,经过上面的例子,能够了解到闭包容许将数据与其所操做的某些数据关联起来,所以但咱们使用只有一个方法的对象的地方,就可使用闭包。闭包能够用来访问私有变量和模拟私有方法
  • 经过闭包能够访问私有变量的共有方法

闭包技术能够用来共享私有变量,下面的例子建立了一个addPrivateProperty()函数来实现私有属性存取器方法。这个函数给对象o增长了属性存取器方法,方法名称为'get' + name'set' + name。若是提供了一个断定函数, setter方法就会用它来检测参数的合法性,而后再存储它, 若是断定函数返回false,setter方法抛出异常。对于两个存取器方法来讲value这个变量是私有的,没有办法绕过存取器方法来设置或修改这个值。函数

function addPrivateProperty(o, name, predicate){
    var value   // 私有变量
    
    // 私有函数
    o['get' + name] = function() {
        return value
    }
    o['set' + name] = function(v) {
        if (predicate && !predicate(v)){
            throw Error('set' + name + ': invalid value' + v)
        } else {
            value = v
        }
    }
}

var o = {}   //设置一个空对象
addPrivateProperty(o, 'Name',function(x){
    return typeof x == 'string'
})
o.setName('Frank')  //设置属性值
console.log(o.getName())   // => 'Frank'

上述的例子中,在同一个做用域链中定义了两个闭包,这两个闭包共同享用一样的私有变量或变量。性能

三、闭包存在的问题

  • 闭包中的循环陷阱

经过循环建立多个闭包会产生必定的缺陷,即闭包只能取得包含函数中任何变量的最后一个值, 下面的第一个例子中,定时器函数返回的是10, 由于每一个函数的做用域都保存着createFn()函数的活动对象。因此它们引用的都是同一个变量n。当createFn()函数中,执行完循环以后变量n的值是10,此时每一个定时器函数都应用这保存变量n的同一个变量对象,因此在每一个函数内部n的值都是10优化

function createFn() {
  for (var n = 0; n < 5; n++) {
    setTimeout(function() {
      return console.log(n);
    }, 100 * n);
  }
}
createFn()   // => 5 5 5 5 5

能够经过定义了一个匿名函数并将当即执行该匿名函数,在调用每一个匿名函数的时候,传入了变量num,因为函数参数是按值传递的,因此就会将变量k的当前值赋值给匿名函数的参数num,而在这个匿名函数的内部,又会建立并返回了一个访问k的闭包,所以定时器函数中都有本身k变量的一个副本,即可以返回不一样的数值。code

function createFn() {
  for (var k = 0; k < 10; k++) {
    (function(num) {
      setTimeout(function() {
        return console.log(num);
      }, 100 * k);
    })(k);
  }
}
createFn();  // => 1 2 3 4 5
  • 闭包占用大量内存

一般,函数的做用域及其全部的变量都会在函数执行结束后被销毁。可是,当函数一旦返回了闭包,这个函数的做用域将会一直在内存中保存到闭包不存在为止。
可是能够经过模仿块级做用域来实现。经过建立并当即调用一个函数,函数内部的全部变量都会被当即销毁(除某些变量赋值给了包含做用域中的变量),这样既能够执行其中的代码,又不会在内存中留在对该函数的引用。对象

function Fn(count) {
    (function() {
        for(var i = 0; i < count; i++) {
            console.log(i)
        }
    })()
    console.log('i:' + i)  // 抛出错误
}
Fn(5)

经过建立函数Fn(),在for循环外部插入一个块级做用域(私有做用域),在匿名函数中定义的任何变量,都会在执行结束的时候被销毁。所以,变量i只能坐在循环中使用,使用后就被销毁。而在使用做用域中可以访问count变量,是由于这个匿名函数是一个闭包,它可以访问包含做用域中的全部变量。ip

四、性能优化以内存管理

经过建立私有做用域,在全局做用域中被函数外部使用,从而限制向全局做用域中添加过多的变量和函数。经过此方法不只可使用本身变量,并且不用担忧搞乱全局做用域。也就是说,能够经过闭包建立私有做用域将某些变量做为局部变量,避免使用全局变量而占用过多的内存。内存

总结

闭包有着其优势,能够利用其优势实现不少功能,如闭包能够用来访问私有变量和模拟私有方法(访问私有属性的模式有不少,之后总结)等等,可是由于建立闭包必须维护额外的做用域,因此过渡使用闭包会占用大量的内存。作用域

相关文章
相关标签/搜索