闭包

156 阅读3分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第16天,点击查看活动详情

在此之前,我们已经了解了执行上下文的概念,包括全局执行上下文与函数执行上下文,下面,闭包。

····一个例子:

var n = 10
function fn(){
    var n =20
    function f() {
       n++;
       console.log(n)
     }
    f()
    return f
}

var x = fn()
x()
x()
console.log(n)

分析:变量x为执行函数fn后返回的结果,也就是f。

x( )相当于执行的是函数f( )。

所以结果为:

image.png

····闭包:

在 JS 忍者秘籍(P90)中对闭包的定义:闭包允许函数访问并操作函数外部的变量。红宝书上对于闭包的定义:闭包是指有权访问另外一个函数作用域中的变量的函数。 MDN 对闭包的定义为:闭包是指那些能够访问自由变量的函数。这里的自由变量是外部函数作用域中的变量。

总之大概意思是,闭包是指一个函数有权访问另一个函数作用域的函数。

闭包形成的原因:

内部的函数存在外部作用域的引用,就会导致闭包。

拿上一个例子来说,在f函数中存在引用并不属于它自己作用域内的变量n,这就产生了闭包。

····闭包变量的存储位置:堆内存。

闭包不存在于栈内存中是因为栈会把处于栈顶的变量自动回收。闭包的特性就是虽然上一个执行上下文已经被销毁,却仍然存在。所以不可以被自动回收,所以要存放在堆内存中。

拿上一个例子来说,即使fn被执行完之后就被销毁了,但是 JavaScript 依然会让fn.AO 活在内存中,f 函数依然可以通过 f 函数的作用域链找到它,正是因为 JavaScript 做到了这一点,从而实现了闭包这个概念。(这句话我觉得理解了很重要)

····闭包的作用:

保护函数私有变量不受外界干扰。形成不会被销毁的栈内存。

将一些函数的值保存下来,实现方法和属性的私有化。

····闭包的使用场景:

1.return一个函数(return f和f()构成闭包)

var n = 10
function fn(){
    var n =20
    function f() {
       n++;
       console.log(n)
     }
    return f
}

var x = fn()//20
x() // 21

2.函数作为参数

var a = '林一一'
function foo(){
    var a = 'foo'
    function fo(){
        console.log(a)
    }
    return fo //return fo,fo()就是闭包
}

function f(p){
    var a = 'f'
    p()
}
f(foo())

foo()的返回值为fo,所以f(foo())等同于f(fo),因为执行fo函数console.log的结果为a的取值,所以f(foo())的值为foo

3.自执行函数

var a = "吃饭"
(function p(){
    console.log(a)
})()

产生了闭包p()

4.循环赋值:

for(var i = 0; i<10; i++){
  (function(j){
       setTimeout(function(){
        console.log(j)
    }, 1000) 
  })(i)
}

因为存在闭包的原因导致输出1-10,闭包形成了10个互不干扰的私有作用域。将外层的自执行函数去掉后就不存在外部作用域的引用了,输出的结果就是连续的10。为什么?

因为js是单线程的。遇到异步代码会把异步代码放进栈中,等同步代码执行完再去执行微观任务。

即等i++执行完等于10之后,再去执行定时器。

image.png

5.使用回调函数就是在使用闭包。

window.name = '123'
setTimeout(function timeHandler(){
  console.log(window.name);
}, 100)

6.防抖节流。

7.函数柯里化

····一些题目:

var data = [];
for (var i = 0; i < 3; i++) {
  data[i] = function () {
    console.log(i);
  };
}
data[0]();
data[1]();
data[2]();
var data = [];

for (var i = 0; i < 3; i++) {
    (function(j){
      setTimeout( data[j] = function () {
        console.log(j);
      }, 0)
    })(i)
}

data[0]();
data[1]();
data[2]();
var data = [];

for (let i = 0; i < 3; i++) {
  data[i] = function () {
    console.log(i);
  };
}

data[0]();
data[1]();
data[2]()
var result = [];
var a = 3;
var total = 0;

function foo(a) {
    for (var i = 0; i < 3; i++) {
        result[i] = function () {
            total += i * a;
            console.log(total);
        }
    }
}

foo(1);
result[0]();  
result[1]();  
result[2]();  

后几个练习题我想留着自己以后练习,就不写答案了。