javascript是基于词法作用域的语言
名词释义
作用域:程序源代码中定义变量的区域。
作用域分为 静态作用域 和 动态作用域。
**静态作用域:**函数的作用域在函数“定义”的时候就决定了,又叫 词法作用域
动态作用域:函数的作用域是在函数“调用“的时候才决定的
举个例子🌰
function foo() {
var a = 2;
return a;
}
function bar() {
var a =3;
retun foo(); // 此处的foo()就是闭包
}
// var a = 2;
bar();
上面的代码在Chrome控制台返回的是什么? 是 _2,根据_词法作用域_的定义,函数的作用域在定义的时候就决定了,调用顺序是 bar() => foo() => a => 2, 但是在_动态作用域_中返回的会是_3。
_相同的代码,解释器对代码的解释不一样,返回的结果就会有差异,_此处只是为了展示差异,不用纠结。
闭包
函数对象可以通过作用域链互相关联起来,函数体内部的变量都可以保存在函数的作用域内,这种特性在计算机科学中称为闭包(closure)
作用域链:一个函数内部的作用域对于函数外部作用域的引用的集合,调用关系采用链表的形式进行记录,称为****作用域链。
下面说说闭包的使用场景
1. 代码逻辑的封装/模块化
看两个例子
🌰 简单的累加
var i = (function(){
var counter = 0;
return function() { return counter ++;} // 这个匿名function就是闭包
})();
i(); // 0
i(); // 1
i(); // 2
🌰🌰 实现累加和重置
function counter() {
var n = 0;
return {
count: function() { return n ++; },
reset: function() { n = 0; }
};
}
var c = counter(), d = counter();
c.count(); // 0
d.count(); // 0
c.reset(); // 思考🤔这步会影响 d 吗?
c.count(); // 0
d.count(); // 1 ,并不会
通过上面两个例子分别将逻辑封装在方法中,然后赋值给另外一个对象,运行时,不同的对象运行相同的属性方法,各自的运行都是独立的,累加的数值都单独存储在各自对象方法的内部参数中,不会互相影响。这在我们开发的过程中,都是常用的方式。
2.共享私有状态/数据缓存
看两个例子
🌰 不允许值变小
function counter(n) {
return {
get count() { return n ++; },
set count(m) {
if(m > n) n = m;
else throw Error("出错");
}
};
}
var c = counter(1000);
c.count; // 1000
c.count; // 1001
c.count = 2000;
c.count; // 2000
c.count = 2000; // 思考一下这一步会发生什么?为什么?
🌰🌰实现私有属性存储器get set方法
function addPrivateProperty(o, name, predicate){
var value;
o["get"+name] = function(){ return value}; // get<Name>, 直接返回值
o["set"+name] = function(v){ // set<Name> , 根据 predicate,判断是否符合条件
if(predicate && !predicate(v))
throw Error("set"+ name +": invalid value " + v)
else
value = v;
}
}
var o = {};
addPrivateProperty(o,"Name",function(x){ return typeof x == "string"});
o.setName("Leon");
o.getName(); // Leon
o.setName(0); // Error: setName: invalid value 0
上面的两个例子中,都是闭包共享了私有变量或者变量,进行数据缓存,然后进行逻辑的实现。
仔细思考一下,这是一种很重要的技术,是JS的基础方法。这里需要注意,有时候不希望被共享的变量,也可能被错误的传递。
看下面的例子
function constfuncs() {
var funcs = [];
for(var i= 0; i< 10; i ++) {
funcs[i] = function(){return i;};
}
return funcs;
}
var funcs = constfuncs();
funcs[5](); // 返回什么 ? 10
//--------------//
function constfuncs(v) {
return function() { return v };
}
var funcs = [];
for(var i= 0; i< 10; i ++) {
funcs[i] = constfuncs(i);
}
funcs[5](); // 返回什么 ? 5
很明显,第二个方法,才是我们想要的结果,返回值与对应的下标相等。第一个方法所有个返回值都是10,这些闭包都在同一个函数中定义,因此它们共享了变量 i,当constfuncs()返回时,变量i是10,它们共享这一个值。
再思考一下,如果滥用闭包的数据缓存,导致缓存的数据过大,就可能会影响web界面的加载,查阅资料发现,在IE中还可能导致内存泄露。
总结一下
闭包:简单理解就是嵌套在函数中的方法。
优点:1. 有利于逻辑的封装,可以进行模块化 2. 私有变量的共享。
缺点:滥用的话,会导致缓存过大,影响网页加载性能,甚至可能导致内存泄漏。
以上就是最近学习闭包的理解,有更好的理解可以在评论区交流,觉得有用就点个赞吧!!