Closure 闭包学习总结
什么是闭包
JavaScript中的函数会形成闭包。 闭包是由函数以及创建该函数的词法环境组合而成。这个环境包含了这个闭包创建时所能访问的所有局部变量。 在一些编程语言中,函数中的局部变量仅在函数的执行期间可用,然而在javascript当中,若函数返回内层某个函数,无论该内层函数是否需要访问外层变量,返回函数都能正常执行,可见javascript自有其机制。
观察闭包
个人观察总结,闭包即闭包域(Closure Scope),是存放内层函数所需变量的引用的集合。
考虑以下代码。
var Soldier = function (ak) {
var ammo = '.762';
var Grenades = 0;
this.name = "John";
this.getName = function () {
console.log(this.name + '\'s');
}
this.getWeapon = function () {
console.log(ak);
function fire() {
let round = "30 ";
this.noAmmon = function () {
console.log('needs ' + round + ammo + ' ammo');
}
}
var shot = new fire();
shot.noAmmon();
console.log(shot);
}
}
var p = new Soldier('ak');
p.getName();
p.getWeapon();
console.dir(p);
复制代码
上图可知。
1.Closure(Soldier)中没有this.name
。虽然getName访问了this.name却没有出现在Closure(Soldire)中,可见闭包的作用是为了存放变量的引用,对象内部变量引用没有必要存在闭包中。
2.无论Soldier的getName,getWeapon还是fire的noAmmon它们的都有Closure(Soldire) 但是getName却没有访问过任何外部变量(甚至为空函数)。由此可推断函数会自动引用闭包。
3.Grenades不存在于Closure(Soldier)当中。内部函数都没有访问的变量不会加到闭包中。
4.如果把所有外部变量的访问全部注释,将不会存在任何闭包。不存在空闭包。
5.noAmmon有两个闭包Closure(fire)和Closure(Soldier)且Closure(fire)比Closure(Soldier)排序低。可见外层函数闭包可共内层使用,反之不可,且函数由内向层闭包向外曾闭包依次查找变量直到Golbol,如果没有则会报错。
由以上几点总结:闭包是一个引用的集合
,存放内层函数所需变量的引用,闭包也实现了作用域的控制,函数如何自内而外的查找变量。
闭包的作用举例
使用闭包来定义公共函数,并令其可以访问私有函数和变量。这个方式也称为 模块模式(module pattern)
var Soldier = (function () {
var ammo = 30;
var reload = function () {
ammo = 30;
}
var fire = function (round) {
ammo -= round;
}
var check = function () {
console.log(ammo);
}
return {
reload: reload,
fire: fire,
check: check
}
})();
Soldier.check(); //output:30
Soldier.fire(15);
Soldier.check(); //output:15
Soldier.reload();
Soldier.check(); //output:30
复制代码
每个闭包都有它自己的词法环境,上例环境在一个立即执行的匿名函数内。该环境包含私有变量ammo且它无法从函数外部直接访问。必须通过该匿名函数返回的三个公共函数访问。 也正是因为环境被内层函数所共享,所以上例中的Soldier.reload,Soldier.fire,Soldier.check才可以访问且共享私有变量ammo。
var makeSoldier = function(){
var Soldier = (function () {
var ammo = 30;
var reload = function () {
ammo = 30;
}
var fire = function (round) {
ammo -= round;
}
var check = function () {
console.log(ammo);
}
return {
reload: reload,
fire: fire,
check: check
}
})();
return Soldier;
}
var s1 = makeSoldier();
var s2 = makeSoldier();
s1.fire(10);
s1.check();//output:20
s2.check();//output:30
复制代码
观察s1,s2的output可知,它们各自的ammo是独立性的。每个闭包只引用自己词法作用域内的变量,在一个闭包内对变量的修改,不会影响到另外一个闭包中的变量。
优点:以这种方式使用闭包,提供了许多与面向对象编程相关的好处 —— 特别是数据隐藏和封装。
缺点:消耗更多的内存,影响处理速度。例如,在创建新的对象或者类时,方法通常应该关联于对象的原型,而不是定义到对象的构造器中。原因是这将导致每次构造器被调用时,方法都会被重新赋值一次。