了解闭包

江湖上都说要了解闭包,得先了解作用域链,所以,先从作用域链开始吧。

作用域链

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

我们来看一个栗子

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高级程序设计上说“有不少开发人员总是搞不清匿名函数和闭包这两个概念”。很遗憾,本人就是。

[@javacript高级程序设计](javascript:void(0))

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

@xiaotie

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

[@javacript权威指南](javascript:void(0))

函数对象通过作用域链相互关联起来,函数体内部的变量都可以保存在函数作用域内


上栗子

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的引用,所以它所占用的内存就永远不会被回收。

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

如果觉得本文不错的话,帮忙点击下面的推荐哦
>>>>点击阅读原文

posted @ 2015-12-22 17:43  Bigdots  阅读(686)  评论(3编辑  收藏  举报