什么是闭包?
闭包是什么?闭包是Closure,这是静态语言所不具有的一个新特性。但是闭包也不是什么复杂到不可理解的东西,简而言之,闭包就是:闭包就是函数的局部变量集合,只是这些局部变量在函数返回后会继续存在。闭包就是就是函数的“堆栈”在函数返回后并不释放,我们也可以理解为这些函数堆栈并不在栈上分配而是在堆上分配,当在一个函数内定义另外一个函数就会产生闭包。
闭包 = 函数内部创建的函数(或者简称内部函数) + 该函数创建时所处环境信息
所以闭包并不等于匿名函数,虽然也有人称这些在函数内部创建的函数为闭包函数,但是我觉得其实并不准确。
我们看一下下面这段代码:
function init() { var name = "Zilongshanren"; // name 是在 init 函数里面创建的变量 // displayName() 是一个内部函数,即一个闭包。注意,它不是匿名的。 function displayName() { console.log(name); } //当 displayName 函数返回后,这个函数还能访问 init 函数里面定义的变量。 return displayName; } var closure = init(); closure(); Zilongshanren undefined
displayName 是一个在 init 函数内部创建的函数,它携带了 init 函数内部作用域的所有信息,比如这里的 name 变量。当 displayName 函数返回的时候,它本身携带了当时创建时的环境信息,即 init 函数里面的 name 变量。
闭包有什么作用?
在理解什么是闭包之后,接下来你可能会问:这东西这么难理解,它到底有什么用啊?
因为在 Js 里面是没有办法创建私有方法的,它不像 java 或者 C++有什么 private 关键字可以定义私有的属性和方法。 Js 里面只有函数可以创建出属于自身的作用域的对象,Js 并没有块作用域!这个我后面会再写一篇文章详细介绍。
编程老鸟都知道,程序写得好,封装和抽象要运用得好!不能定义私有的属性和方法,意味着封装和抽象根本没法用。。。
不能定义私有的东西,所有变量和函数都 public 显然有问题, Global is Evil!
闭包是我们的救星!
我们看一下下面这段代码:
var makeCounter = function() { var privateCounter = 0; function changeBy(val) { privateCounter += val; } return { increment: function() { changeBy(1); }, decrement: function() { changeBy(-1); }, value: function() { return privateCounter; } } }; var counter1 = makeCounter(); var counter2 = makeCounter(); console.log(counter1.value()); /* Alerts 0 */ counter1.increment(); counter1.increment(); console.log(counter1.value()); /* Alerts 2 */ counter1.decrement(); console.log(counter1.value()); /* Alerts 1 */ console.log(counter2.value()); /* Alerts 0 */ 0 2 1 0 undefined
这里面的 privateCounter 变量和 changeBy 都是私有的,对于 makeCounter 函数外部是完全不可见的。这样我们通过 makeCounter 生成的对象就把自己的私有数据和私有方法全部隐藏起来了。
这里有没有让你想到点什么?
哈哈,这不就是 OO 么?封装数据和操作数据的方法,然后通过公共的接口调用来完成数据处理。
当然,你也许会说,我用原型继承也可以实现 OO 呀。没错,现在大部分人也正是这么干的,包括我们自己。不过继承这个东西,在理解起来总是非常困难的,因为要理解一段代码,你必须要理解它的所有继承链。如果一旦代码出 bug 了,这将是非常难调试的。
扯远了,接下来,让我们看看如何正确地使用闭包。
如何正确地使用闭包?
闭包会占用内存,也会影响 js 引擎的执行效率,所以,如果一段代码被频繁执行,那么要谨慎考虑在这段代码里面使用闭包。
让我们来看一个创建对象的函数:
function MyObject(name, message) { this.name = name.toString(); this.message = message.toString(); this.getName = function() { return this.name; }; this.getMessage = function() { return this.message; };} var myobj = new MyObject(); var myobj = new MyObject();
每一次被调用生成一个新对象的时候,都会生成两个闭包。如果你的程序里面有成千上万个这样的 MyObject 对象,那么会额外多出很多内存占用。
正确的做法应该是使用原型链:
function MyObject(name, message) { this.name = name.toString(); this.message = message.toString(); } MyObject.prototype.getName = function() { return this.name; }; MyObject.prototype.getMessage = function() { return this.message; }; var myobj = new MyObject();
现在 MyObject 原型上面定义了两个方法,当我们通过 new 去创建对象的时候,这两个方法只会在原型上面存有一份。
闭包的性能如何?
闭包也是一个函数,但是它存储了额外的环境信息,所以理论上它比纯函数占用更多的内存,而且 Js 引擎在解释执行闭包的时候消耗也更大。不过它们之间的性能差别在 3%和 5%之间(这是 Google 上得到的数据,可能不是太准确)。
但是,闭包的好处肯定是大大的。多使用闭包和无状态编程,让 Bug 从此远离我们。
理解了闭包,你就能理解大部分 FP 范式的 Js 类库及其隐藏在背后的设计思想。当然仅有闭包还不够,你还需要被 FP 和无状态,lambda calculus 等概念洗脑。
关于js闭包希望大家学完本篇内容之后就有所掌握。
相关推荐:
以上是什么是 Javascript 的闭包的详细内容。更多信息请关注PHP中文网其他相关文章!