说到JavaScript的闭包,须要先说一说JavaScript的做用域。javascript
JavaScript在ECMA6以前,做用域是只有全局做用域跟函数做用域的。(这里先不涉及let和const)html
函数内部能够读取器外部做用域定义的变量,而外部做用域不能直接读取到函数中定义的变量。java
eg:闭包
var n = 0;
function f1() {
console.log(n); // 0
}
f1();
function f2() {
var a = 1;
}
console.lot(a); // throw error .... undefind
复制代码
上述例子中,函数f1在运行时能够访问到全局做用域定义的变量n。而全局做用域中没法访问到函数f2中定义的变量a。app
那么还有一个例子:函数
var n = 0;
function f1() {
console.log(n); // 0
var a = 1;
function f2() {
console.log(n); // 0
console.log(a); // 1
}
f2();
}
f1();
复制代码
上述例子中的,函数f1中定义了函数f2,f2可以访问到f1中定义的变量a以及全局做用域中定义的变量n。那么,这能够说明,更里层的函数可以访问到更外层的做用域中定义的变量。而更外层的代码,没法访问到更里层的函数做用域定义的变量。这是JavaScript链式做用域的做用。oop
那么,若是咱们有须要在全局做用域中访问到函数做用域中定义的变量,那应该怎么作呢?学习
咱们发现函数f2是可以访问到f1中定义的变量的。那么将他返回到全局做用域:ui
function f1() {
var a = 1;
return function f2() {
console.log(a);
}
}
var function2 = f1();
function2(); // 1
复制代码
那么,上述的函数f2,就是一个闭包。this
闭包是用于读取其余函数内部变量的函数(取自@阮一峰老师的博客)
1.闭包能够读取其余函数内部变量。
这个特性这里再也不赘述
2.闭包会将函数内部的局部变量存储在内存中
如上一节最后一个例子,函数f2将变量a保存在内存中,而后在被调用时,就可以访问到了。
那么,利用这个特性,咱们能够作一个相似于计数器的东西:
eg:
function counter() {
var a = 1;
return function f1() {
return a++;
}
}
var counter1 = f1();
counter1(); // 1
counter1(); // 2
counter1(); // 3
复制代码
这个特性也会致使一个问题,就是若是滥用闭包的话,内存消耗会比较多。
利用闭包的特性,咱们能够利用闭包作私有变量:
var ClassA = function () {
var a = 0;
this.getA = function() {
return a;
}
this.setA = function(val) {
a = val;
}
}
var objectA = new ClassA();
console.log(objectA.getA()); // 0
objectA.setA(111);
console.log(objectA.getA()); // 111
objectA.a; // error
复制代码
这里的a变量只能被ClassA以及getA和setA访问到。
最近学习到了一个在JavaScript中实现函数重载的方法,感受比较有趣。
众所周知JavaScript的函数的参数是不固定的,因此没办法作函数重载。若是要实现相似于函数重载的方法,比较日常的作法是使用if-else
或switch
来判断参数,而后根据传入的参数不一样,输出不一样的结果。
而最近学习了一个,利用闭包处理的比较干净一些的作法。
1.准备阶段先撸一下须要存放重载函数的类/对象
var object = {};
复制代码
2.定义须要重载的函数。
function sum0(val0) {
return val0;
}
function sum1(val0, val1) {
return val0 + val1;
}
function sum2(val0, val1, val2) {
return val0 + val1 + val2;
}
复制代码
3.而后定义装载重载函数的函数(核心):
function addMethod(object, name, f) {
// 1. 从object中获取名字为name的函数做为old备用
var old = object[name];
// 2. 将object[name]定义为一个新的函数
object[name] = function() {
// 3. f.length为函数定义时的参数个数
// arguments.length为函数调用时的参数个数
// 若是调用的函数的形参和实参一致,则直接调用。不然,调用old函数
if (f.length === arguments.length) {  
return f.apply(this, arguments);    
} else if (typeof old === "function") {
return old.apply(this, arguments);    
}  
};
}
复制代码
4.装载函数:
addMethod(object, "sum", sum0);
addMethod(object, "sum", sum1);
addMethod(object, "sum", sum2);
console.log(object.sum(1)); // 1
console.log(object.sum(1, 2)); // 3
console.log(object.sum(1, 2, 3)); // 5
复制代码
上述代码使用old存储上一次调用addMethod方法加入的函数。而后经过闭包存储在内存中。使得每次调用add函数时,出现参数不等的状况时可以调用到old,以查找到上一个add进来的函数。最终达到函数重载的目的。