博客链接:https://bottle.moe/post-274.html
最近在重打JavaScript基础...看廖雪峰博客的基础教程时看到了闭包这个字眼,但有点意思的是文章并没有更进一步去探讨闭包到底是什么,而是列出代码让大家去体会。
但既然是打基础,我觉着有疑惑的概念还是得摸清楚为好,遂决定写这样一篇小笔记。有可能会有理解错误的地方,各位请多指教。
闭包(Closure)的话其实一个简单的代码示例就能展示出来:
function test() {
var testvar = 1;
function innerTest() {
console.log(testvar);
}
innerTest();
}
test();
testvar是test()函数作用域里的一个局部变量,而innerTest()这个函数可以访问到testvar这个test()作用域内的局部变量。
也就是说testvar这个变量对于innerTest()来说是外部的变量,而innerTest这个函数内部却又能访问到这个变量,由此,innerTest这个函数和其所访问的这个testvar变量便形成了一个闭包,innerTest就是一个闭包函数。
说起形成,我觉得闭包其实有点偏向一个动词,指的是一个函数访问另外一个函数的_函数作用域_中的局部变量的过程。
之所以中文翻译里有个**“闭”,我认为是因为这样把变量给“闭”进去了,除非调用能访问到这个变量的函数**,没有其他办法能改变这个变量,比如下面这个例子:
function test() {
var testvar = 1;
function innerTest() {
console.log(testvar);
testvar++;
}
return innerTest;
}
var inner = test();
inner(); //1
inner(); //2
inner(); //3
inner(); //4
除了调用test函数返回的作用域中的函数innerTest外,无法对testvar这个局部变量进行修改。
关于这个例子我整了个小动图(做的比较仓促):
了解到这些我再回头看了一下,发现本博客程序-O-初始化源码中也是有用到闭包的:
也就是说其实在很多不经意的时候我已经在写JavaScript代码时使用了所谓的“闭包”。为什么会这样呢?其实更进一步想,在看这些函数和变量的过程中我们脑中总是会模拟变量作用的范围。没错,更为重要的是JS的作用域相关的知识!
网上搜索了一下,闭包从来不是某个语言独有的概念,不如说是一种现象的统称。既然我在重摸JavaScript基础,其实打好作用域的知识便也足够了~
廖雪峰的这篇闭包相关的JavaScript教程还提到了个有趣的脑洞:只使用函数来完成简单的计算。
第一眼看上去心生疑惑,这算出来是什么单位呢...看到后面,原来是函数的嵌套重复次数。我把参数名改的详细了点,试试看能不能增加点可读性:
// 定义数字0:
var zero = function (func) {
return function (x) {
return x;
}
};
// 定义数字1:
var one = function (func) {
return function (x) {
return func(x);
}
};
// 定义加法:
function add(para1, para2) {
return function (func) {
return function (x) {
return para1(func)(para2(func)(x));
}
}
}
这三个函数里后两个都非常典型地形成了闭包,因为zero函数内部的函数没有调用局部变量(参数func),所以zero内未形成闭包,由此通过参数传入的函数不会被执行。
暂且当这里的x参数是为了增加可读性的(不过我是真的给这个x看花眼了),再改一下:
var zero = function (func) {
return function () {
return '';
}
};
// 定义数字1:
var one = function (func) {
return function () {
return func();
}
};
// 定义加法:
function add(para1, para2) {
return function (func) {
return function () {
return para1(func)(para2(func)());
}
}
}
很明显能发现,add函数内部嵌套的第一层匿名函数往后function(func)其实就是zero,one这类函数的构造模式。接下来咱试着算一下"1"+"1"
var two = add(one, one);
实际上就等价于:
var two = function (func) {
var exec_one = one(func);
return function () {
return exec_one(exec_one());
}
};
var exec_two = two(function () {
console.log('print 2 times');
});
exec_two(); //输出两次
所谓的输出两次其实就是把one函数再次执行嵌套了一次。可想而知如果再算一下 "1"+"2" ,得出的three函数核心代码将会是exec_one(exec_one(exec_one()));
,俗称套娃
如果把x给还原进去,就相当于从零开始不停加1:
var exec_two = two(function (val) {
return val + 1;
});
console.log(exec_two(0)); //输出2
虽然但是,读清楚这段代码真的耗了我好长时间...看来用了太多return和闭包的结果就是代码可读性会降低,所以凡事要适度嘛...
最后要感叹一下,我竟然把这篇博文写出来了,也是第一次尝试写这种小笔记今后随着学习的感悟变多,我有可能会持续更新下去加油吧!不要停滞不前!
橘佬对此还有一点补充: #1