这是我参与更文挑战的第7天,活动详情查看: 更文挑战
紧跟上一篇 ,这一篇主要了解高阶函数(闭包)
什么是闭包?
- MDN的定义
- 一个函数和对其周围状态(lexical environment,词法环境)的引用捆绑在一起(或者说函数被引用包围),这样的组合就是闭包(closure)。也就是说,闭包让你可以在一个内层函数中访问到其外层函数的作用域。在 JavaScript 中,每当创建一个函数,闭包就会在函数创建的同时被创建出来。
- 闭包就是能够读取其他函数内部变量的函数
- 函数可以访问由函数内部定义的变量
function myFunction() {
var a = 4;
return a * a;
}
复制代码
- 函数也可以访问函数外部定义的变量
var a = 4;
function myFunction() {
return a * a;
}
复制代码
如何理解闭包?得从 变量的作用域及如何从外部读取局部变量说起
- 变量的作用域
- 变量的作用域无非就是两种:全局变量和局部变量。
- Javascript语言的特殊之处,就在于函数内部可以直接读取全局变量。
var n=999;
function f1(){
alert(n);
}
f1(); // 999
复制代码
- 另一方面,在函数外部自然无法读取函数内的局部变量。
function f1(){
var n=999;
}
alert(n); // error
复制代码
- 这里有一个地方需要注意,函数内部声明变量的时候,一定要使用var命令。如果不用的话,你实际上声明了一个全局变量!
function f1(){
n=999;
}
f1();
alert(n); // 999
复制代码
- 如何从外部读取局部变量
- 在函数的内部,再定义一个函数
function f1(){
var n=999;
function f2(){
alert(n); // 999
}
}
复制代码
- 函数f2就被包括在函数f1内部,这时f1内部的所有局部变量,对f2都是可见的。但是反过来就不行,f2内部的局部变量,对f1就是不可见的。这就是Javascript语言特有的”链式作用域”结构(chain scope),子对象会一级一级地向上寻找所有父对象的变量。所以,父对象的所有变量,对子对象都是可见的,反之则不成立。
function f1(){
var n=999;
function f2(){
alert(n);
}
return f2;
}
var result=f1();
result(); // 999
复制代码
闭包的用途
- 可以读取函数内部的变量
function module() {
var arr = [];
function add(val) {
if (typeof val == 'number') {
arr.push(val);
}
}
function get(index) {
if (index < arr.length) {
return arr[index]
} else {
return null;
}
}
return {
add: add,
get: get
}
}
var mod1 = module();
mod1.add(1);
mod1.add(2);
mod1.add('xxx');
console.log(mod1.get(2));//外部是无法直接拿到arr的只能通过get来拿
复制代码
- 可以让变量的值始终保持在内存中
- 计数器
- 我们来实现一个计数器,每调用一次计数器返回值加一:
- 计数器
var counter = 0;
function add() {
return counter += 1;
}
add();
add();
add();// 计数器现在为 3
复制代码
- 全局变量容易被其他代码改变
- 如果我需要同时用两个计数器,但这种写法只能满足一个使用,另一个还想用的话就要再写个counter2函数,再定义一个counter2的全局变量。
- 使用闭包解决
function add() {
var index = 1;
function counter() {
return index ++;
}
return counter;
}
// test
var addA = add() ;
var addB = add() ;
addA(); // 1
addA(); // 2
addB(); // 1
addB(); // 2
复制代码
- 延时打印
for (var i = 1; i <= 10; i++) {
setTimeout(function () {
console.log(i);
}, 1000);
}
复制代码
- 这样打印出来的全部都是10,原因是for循环是同步的会在延时1000毫秒的过程中一直执行
等function执行的时候变量i指向的是同一个内存地址,且值已经变成的10
- 改进,用自执行的函数创建简单的闭包,让每一次for循环的i都在不同的内存地址中且不被销毁
for (let i = 1; i <= 10; i++) {
setTimeout(function () {
console.log(i);
}, 1000);
}
复制代码
总结
-
闭包就是子函数可以有权访问父函数的变量、父函数的父函数的变量、一直到全局变量。
-
归根结底,就是利用js得词法(静态)作用域,即作用域链在函数创建的时候就确定了。
-
子函数如果不被销毁,整条作用域链上的变量仍然保存在内存中,这样就形成了闭包
-
以上所述是给大家介绍的详解JS的闭包详解及高级技巧,希望对大家有所帮助
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END