深入 JavaScript 闭包,让人困惑的闭包

前言

对于那些有一点 JavaScript 使用经验但从未真正理解闭包概念的人来说,理解闭包可以看 作是某种意义上的重生,但是需要付出非常多的努力和牺牲才能理解这个概念。 回忆我前几年的时光,大量使用 JavaScript 但却完全不理解闭包是什么。总是感觉这门语 言有其隐蔽的一面,如果能够掌握将会功力大涨,但讽刺的是我始终无法掌握其中的门 道。还记得我曾经大量阅读早期框架的源码,试图能够理解闭包的工作原理。现在还能回 忆起我的脑海中第一次浮现出关于“模块模式”相关概念时的激动心情。

这一段出自《你不知的JavaScript》中,作者对闭包的描述。在这段文本中,作者描述了他对闭包的概念的深刻体验和反思。从侧面印证了想要真正搞懂闭包可不是一件易事。

函数是一等公民

在了解闭包之前,而外补充一个知识点。在 JavaScript 中函数是一等公民,这也意味着函数在 JavaScript 中具有广泛性和灵活性。如函数可以作为参数、函数返回值等。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 函数可以作为参数使用
function calc(num1, num2, calcFn) {
console.log(calcFn(num1, num2));
}

function add(num1, num2) {
return num1 + num2;
}

function sub(num1, num2) {
return num1 - num2;
}

m = 20;
n = 10;

calc(m, n, add); // 30
calc(m, n, sub); // 20
1
2
3
4
5
6
7
8
9
// 函数作为返回值使用
function sum(count) {
return function add(num) {
return count + num
}
}

const sum10 = sum(10)
console.log(sum10(20)) // 结果为 30

高阶函数:当一个函数如果接受另外一个函数作为参数,或函数会返回另一个函数的返回值的函数,那么这个函数就称之为高阶函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 数组中的函数使用
const nums = [10, 55, 100, 22];
const newNum = [];
for (let i = 0; i < nums.length; i++) {
let num = nums[i];
if (num % 2 === 0) {
newNum.push(num);
}
}

const newNums = nums.filter(item => {
return item % 2 === 0;
});

console.log(newNum);
console.log(newNums);

console.log(newNum);

//
  • filter:过滤
  • map:映射
  • reduce:累加
  • find / findIndex:查找

函数和方法的区别:

  1. 当一个独立的函数就叫函数

    1
    2
    3
    function fn() {
    console.log('别看我,我只是一个函数。')
    }
  2. 一个函数属于某一个对象时,这个函数就是这个对象的方法。

    1
    2
    3
    4
    5
    const obj = {
    foo:() => {
    conslog.log('我是obj对象的方法')
    }
    }

闭包总结

  • 一个函数在函数体里访问外部自由变量,那么这个函数就是闭包函数。