关于闭包
关于闭包
一,闭包的概念
维基百科的解释:
在计算机科学中,闭包(英語:Closure),又稱词法闭包(Lexical Closure)或函數閉包(function closures),是引用了自由变量的函数。这个被引用的自由变量将和这个函数一同存在,即使已经离开了创造它的环境也不例外。所以,有另一种说法认为闭包是由函数和与其相关的引用环境组合而成的实体。闭包在运行时可以有多个实例,不同的引用环境和相同的函数组合可以产生不同的实例。
简而言之,闭包就是能够读取其他函数内部数据(变量/函数)的函数。
话不多说,举个例子
function makeFunc() {
var name = "小旭";
function displayName() {
alert(name);
}
return displayName;
}
var myFunc = makeFunc();
myFunc();
第一眼看上去,也许不能直观的看出这段代码能够正常运行。在一些编程语言中,函数中的局部变量仅在函数的执行期间可用。一旦 makeFunc() 执行完毕,我们会认为 name 变量将不能被访问。然而,因为代码运行得没问题,所以很显然在 JavaScript 中并不是这样的。
这个谜题的答案是,JavaScript中的函数会形成闭包。 闭包是由函数以及创建该函数的词法环境组合而成。这个环境包含了这个闭包创建时所能访问的所有局部变量。在我们的例子中,myFunc 是执行 makeFunc 时创建的 displayName 函数实例的引用,而 displayName 实例仍可访问其词法作用域中的变量,即可以访问到 name 。由此,当 myFunc 被调用时,name 仍可被访问,其值 小旭 就被传递到alert中。
二,闭包的写法
1,原型调用写法
function Circle(r) {
this.r = r;
}
Circle.PI = 3.1415926;
Circle.prototype.area = function() {
return Circle.PI * this.r * this.r;
}
var c = new Circle(1.0);
console.log(c.area());
2,函数原型调用写法
var Circle = function() {
var obj = new Object();
obj.PI = 3.1415926;
obj.area = function(r) {
return this.PI * r * r;
}
return obj;
};
var c = new Circle();
console.log(c.area(1.0));
3,对象赋值调用写法(最常用)
var Circle = new Object();
Circle.PI = 3.1415926;
Circle.area = function(r) {
return this.PI * r * r;
};
consol.log(Circle.area(1.0));
4,声明对象调用写法
var Circle={
PI : 3.1415926,
area : function(r){
return this.PI * r * r;
}
};
console.log(Circle.area(1.0))
5,函数对象调用写法(比较少见)
var Circle = new Function("this.PI = 3.1415926;this.area = function( r ) {return r*r*this.PI;}");
var c = new Circle();
console.log(c.area(1.0));
6,匿名函数调用写法(常见)
(function(r){
var PI = 3.1415926;
var area = PI * r * r;
console.log(area);
})(1.0)
7,函数返回值写法(常见)
function Circle(r){
var PI = 3.1415926;
function area(){
return PI * r * r;
}
return area();
}
console.log(Circle(1.0));
三,闭包的作用
闭包很有用,因为它允许将函数与其所操作的某些数据(环境)关联起来。这显然类似于面向对象编程。在面向对象编程中,对象允许我们将某些数据(对象的属性)与一个或者多个方法相关联。
1,为响应事件而执行的函数
假如,我们想在页面上添加一些可以调整字号的按钮。一种方法是以像素为单位指定 body 元素的 font-size,然后通过相对的 em 单位设置页面中其它元素(例如header)的字号:
body {
font-family: Helvetica, Arial, sans-serif;
font-size: 12px;
}
h1 {
font-size: 1.5em;
}
h2 {
font-size: 1.2em;
}
文本尺寸调整按钮可以修改 body 元素的 font-size 属性,由于我们使用相对单位,页面中的其它元素也会相应地调整。
以下是 JavaScript:
function makeSizer(size) {
return function() {
document.body.style.fontSize = size + 'px';
};
}
var size12 = makeSizer(12);
var size14 = makeSizer(14);
var size16 = makeSizer(16);
size12,size14 和 size16 三个函数将分别把 body 文本调整为 12,14,16 像素。我们可以将它们分别添加到按钮的点击事件上。如下所示:
document.getElementById('size-12').onclick = size12;
document.getElementById('size-14').onclick = size14;
document.getElementById('size-16').onclick = size16;
它的HTML文本如下:
<p>Some paragraph text</p>
<h1>some heading 1 text</h1>
<h2>some heading 2 text</h2>
<a href="#" id="size-12">12</a>
<a href="#" id="size-14">14</a>
<a href="#" id="size-16">16</a>
2,用闭包模拟私有方法
我们可以使用闭包来模拟私有方法。私有方法不仅仅有利于限制对代码的访问:还提供了管理全局命名空间的强大能力,避免非核心的方法弄乱了代码的公共接口部分。
下例展现了如何使用闭包来定义公共函数,并令其可以访问私有函数和变量。
var Counter = (function() {
var privateCounter = 0;
function changeBy(val) {
privateCounter += val;
}
return {
increment: function() {
changeBy(1);
},
decrement: function() {
changeBy(-1);
},
value: function() {
return privateCounter;
}
}
})();
console.log(Counter.value()); /* logs 0 */
Counter.increment();
Counter.increment();
console.log(Counter.value()); /* logs 2 */
Counter.decrement();
console.log(Counter.value()); /* logs 1 */
我们只创建了一个词法环境,为三个函数所共享:Counter.increment,``Counter.decrement 和 Counter.value。
该共享环境创建于一个立即执行的匿名函数体内。这个环境中包含两个私有项:名为 privateCounter 的变量和名为 changeBy 的函数。这两项都无法在这个匿名函数外部直接访问。必须通过匿名函数返回的三个公共函数访问。
四,闭包的缺点
如果不是某些特定任务需要使用闭包,在其它函数中创建函数是不明智的,因为闭包在处理速度和内存消耗方面对脚本性能具有负面影响。
例如,在创建新的对象或者类时,方法通常应该关联于对象的原型,而不是定义到对象的构造器中。原因是这将导致每次构造器被调用时,方法都会被重新赋值一次(也就是,每个对象的创建)。
函数执行完后, 函数内的局部变量没有释放,占用内存时间会变长,容易造成内存泄露。
闭包引起内存泄漏的例子:
<script type="text/javascript">
function fn1() {
var a = 4;
function fn2() {
console.log(++a)
}
return fn2
}
var f = fn1()
f()
// f = null //让内部函数成为垃圾对象-->回收闭包
</script>
简而言之,闭包是反垃圾回收机制的。

浙公网安备 33010602011771号