上一篇:作⽤域链
下一篇:getter 与 setter
主要内容:
- 闭包的基本概念
- 闭包的基本形式
- 闭包与内存
⼀般⼈认为闭包是⼀个神秘⽽⼜晦涩的内容. 实际上并⾮如此. 接下来详细说
明闭包的概念和闭包的⼀般的形式, 同时讨论闭包的有点和缺点.
闭包的基本概念
所谓的闭包, 简单说就是允许低级⼦链访问⾼级⼦链的数据的结构.
在 "作⽤域链" ⼀节中介绍过, ⾼级⼦链可以直接访问低级⼦链的数据, 但是反过来⽆法访问:
var num1 = 123;
function foo() {
var num2 = 456;
console.log("1 级⼦链允许访问 0 级⼦链 " + num1);
}
foo();
try {
console.log(num2);
} catch (e) {
console.log("0 级⼦链⽆法访问 1 级⼦链的数据");
}
但是如果允许访问这个数据, 那就构成闭包结构.
闭包的通俗含义
闭包, 从字⾯意义上来解释, 就是包裹起来的, 封闭起来的结构. 在
JavaScript
中, 函数就是⼀个封闭的结构. 在函数中定义的数据, 在外⾯是访问不到的.
(function () {
var num = 123,
obj = {},
arr = [],
isTrue = false,
str = "abc",
foo = function() {};
})();
在函数外⾯是绝对⽆法访问⾥⾯定义的数据的. 因此它就构成⼀个封闭的结构. 但是没有任何作⽤, 因此在
JavaScript
中需要将其对外开放⼀些接⼝, 以便使⽤这些数据. 这就是要求函数返回⼀个数据, 好么函数要么对象.
闭包的基本结构
要在函数外访问函数内的数据, 最简单的就是返回⼀个函数:
function foo() {
var num = 123;
return function() {
return num;
}
}
当函数返回⼀个函数的时候, 返回的函数允许返回⼀个数据. 那么在函数外就可以访问函数内的数据了. 但是仅仅是访问这个数据, 有时需要对其进⾏修改.因此既要求获得数据, 也要求可以修改数据. 因此就需要两个函数, 那可以返回⼀个对象:
function foo() {
var num = 123;
return {
get_Num: function() {
return num;
},
set_Num: function(value) {
num = value;
}
}
}
var o = foo();
console.log(o.get_Num()); // => 123
o.set_Num(456);
console.log(o.get_Num()); // => 456
⼩结⼀下闭包的这个结构:
- ⾸先有⼀个函数, 在函数中定义数据
- 同时返回⼀个函数, 允许访问函数内部的数据
- 调⽤函数, 返回允许访问内部数据的函数
- 利⽤返回的函数, 允许在外界访问函数内定义的数据
闭包的基本案例
有了闭包, 可以做到事情就变得⾮常多了. 下⾯通过⼏个案例来说明闭包的使⽤
使⽤闭包模拟私有变量
// v3 使⽤⽅法, v5 可以直接使⽤ getter
var person = function(name, age, gender) {
return {
get name() {
return name;
},
get age() {
return age;
},
get gender() {
return gender;
}
}
};
var obj = person("jk", 19, "male");
console.log(obj.name);
console.log(obj.age);
console.log(obj.gender);
使⽤闭包模拟块级作⽤域
var sum = 0;
(function () {
for (var i = 0; i <= 100; i++) {
sum += i;
}
})();
console.log(sum);
使⽤闭包创建唯⼀标识符
var uniqueInteger = (function () {
var count = 0;
return function() {
return count++;
};
})();
闭包与内存
使⽤闭包可以⾮常⽅便的组织代码, 将不需要对外公开的代码都封装起来, 保护起来. 使得代码更加紧凑, 维护更加⽅便. 但是使⽤闭包, 保存了私有数据,容易造成内存的泄露.
为了更加⽅便的解释这个问题, ⾸先讨论⼀下
JavaScript
中内存的管理.
内存管理
JavaScript
具有垃圾收集的功能. 也就是说当执⾏环境会负责管理代码的执⾏过程使⽤的内存, 包括内存的分配与释放. 在传统的程序设计中, 例如C
,C++
,需要⼿动的管理内存, 申请, 释放都需要⼿动的处理. 因此容易出现问题. 在JavaScript
中不需要这么⿇烦, 所有的事情都由JavaScript
引擎去完成.
常⻅的内存管理⽅式有两种: 标记式, 引⽤计数式.
使⽤标记式内存管理, 运⾏时在内存中会标记所有的不再使⽤的数据, 进⽽加以回收. 如今⼤部分浏览器所采⽤的是标记式内存管理. 只是进⾏内存回收的时间不同⽽已.
使⽤引⽤计数式内存管理, 运⾏时会给每⼀个对象加上编号, 凡是有变量指向该对象, 引⽤计数就
+1
, 凡是减少⼀个引⽤, 计数就-1
. 那么当引⽤计数为0
的时候, 回收内存. 但是⼀旦出现循环引⽤, 就容易造成内存泄露.
在
JavaScript
中不需要⾃⼰考虑内存维护的问题, 所有的内存维护都可以由JavaScript
引擎去处理. 但是为了更好的提升性能, 建议在不使⽤数据的时候,将其设置为 null
上一篇:作⽤域链
下一篇:getter 与 setter
⼩结
- 闭包的基本概念
- 闭包的实现⽅式
- 闭包案例