了解闭包

江湖上都说要了解闭包,得先了解做用域链,因此,先从做用域链开始吧。javascript

做用域链

  1. 做用域链是一个对象列表或链表,这组对象定义了这段代码“做用域”中的变量;
  2. 每当调用一个函数,这个函数会建立一个新的对象来储存它的变量(变量绑定对象),而且将这个对象添加到做用域链上;当函数返回时,就从做用域链中将这个对象删除;
  3. 当javascript须要查找一个变量时,它会沿着做用域链一级一级地搜索变量。搜索过程始终从做用域链的前端开始,而后逐级地向后回溯,直至做用域链的最顶层(全局对象)为止。
  4. 对于嵌套函数,每一个嵌套的函数都各自对应一个做用域链,而且这个做用域链都指向一个变量绑定对象

咱们来看一个栗子html

var word = " the window"
function sayWord(){
    var word = "sayWord"
     function sayHello(){
        var word = "sayHello"
        alert(word)
    }
    return sayHello
}
sayWord()();

上例的做用域链就是:
sayHello[word="sayHello"]——sayWord[word="sayWord"]——window[word="the window"];
当执行sayHello函数时,会沿着这个做用域链一级一级往上找word这个变量,直到找到为止。前端

闭包

javacript高级程序设计上说“有很多开发人员老是搞不清匿名函数和闭包这两个概念”。很遗憾,本人就是。java

@javacript高级程序设计git

闭包是指有权访问另外一个函数做用域中的变量的函数。github

@xiaotie闭包

闭包是从用户角度考虑的一种设计概念,它基于对上下文的分析,把龌龊的事情、复杂的事情和外部环境交互的事情都本身作了,留给用户一个很天然的接口。函数

@javacript权威指南this

函数对象经过做用域链相互关联起来,函数体内部的变量均可以保存在函数做用域内设计


上栗子

var word = " the window"
function sayWord(){
    var word = "sayWord"
     function sayHello(){
        alert(word)
    }
    return sayHello
}
sayWord()();

sayHello函数在sayWord函数内部,它能访问sayWord函数内部的变量。sayHello函数就是闭包,

利用闭包实现私有属性

function createCounter() {
  var counter = 0;
  function increment() {
    counter = counter + 1;
    console.log("Number of events: " + counter);
  }
  return increment;
}

var counter1 = createCounter();
var counter2 = createCounter();

counter1(); // Number of events: 1
counter1(); // Number of events: 2

counter2(); // Number of events: 1
 
counter1(); // Number of events: 3

每次调用函数都会建立变量绑定对象添加到做用域链中,因此每次调用外部函数的时候,做用域链都是不一样的。而对于嵌套函数,每次调用外部函数时,内部函数又会从新定义一遍。

闭包存在的问题

this对象的指向问题

var name = "The Window";
var object = {
    name : "My Object",
    getNameFunc : function(){
        var name = "The v";
        return function(){
            return this.name;
        };
    }
};
alert(object.getNameFunc()()); //"The Window"(在非严格模式下)

每一个函数在被调用时都会自动取得两个特殊变量:thisarguments。内部函数在搜索这两个变量时,只会搜索到其活动对象为止,所以永远不可能直接访问外部函数中的这两个变量。

这里

object.getNameFunc()()==(function(){return this.name;})()

因此其活动对象为window;

解决办法

var name = "The Window";
var object = {
    name : "My Object",
    getNameFunc : function(){
        var that = this;
        return function(){
            return that.name;
        };
    }
};
alert(object.getNameFunc()()); //"My Object"

内存泄露问题

function sayWord(){
    var word = "hello"
    add = function(){
        word = word + " world"
    }
    function sayHello(){
        alert(word)
    }
    return sayHello;
    word = null;
}
var say = sayWord();
say();  //hello
add();
say();  //hello world
  • 首先调用say();结果输出hello
  • 而后调用add,add是个全局变量,因此能够在外部调用,由于它又是闭包,因此能够访问到变量word,因此world=“hello world”;
  • 最后再调用say();发现结果输出hello world;
    这说明函数sayWord中的局部变量word一直保存在内存中,并无在sayWord调用后被自动清除。产生这个问题主要是因为匿名函数保存了一个对word的引用,因此它所占用的内存就永远不会被回收。

注意:由于闭包的这个特性,因此外部函数的变量是其内部全部闭包的共享值,所以,不能在闭包中随意的改变外部函数的变量值,牵一发而动全身。

若是以为本文不错的话,帮忙点击下面的推荐哦
>>>>点击阅读原文

相关文章
相关标签/搜索