阿里经典闭包面试题

参考视频:https://www.bilibili.com/video/BV1xf4y1R7AH

一、小试牛刀

先来回顾一下JS的两种数据类型:基本数据类型和引用数据类型。

  • 基本数据类型:number 、null、boolean、string、undefined、symbol(es6)
  • 引用数据类型:object(array)、function

两道小题感受一下

(1)基本数据类型

题:

let a = 22;
let b = a;
b = 33;
console.log(a);   //打印22

解析:

先创建基本类型的值22(存在栈内存当中),然后再赋值给a(将a和22关联在一起)。

=的作用:先创建值,接着创建变量,最后将变量和值关联。

image-20210917092601510

(2)引用数据类型

题:

let a = {
    
    n:12};
let b = a;
b['n'] = 13;  //b通过地址找到对应的堆内存,把这个地址的值修改
console.log(a.n);

解析:

先创建一个对象,是引用类型的值(不能存在栈里面,要单独开辟一个堆内存,使用地址去寻找它),把这个地址放到栈里边,供变量来调用。

image-20210917093143023

总结:基本数据类型是存到栈里边直接进行操作,而引用数据类型则是存在堆里面,并提供一个地址供栈内的变量去寻找。

二、大餐

闭包:当前函数执行,形成一个私有的上下文,函数执行完,当前私有上下文中的某些内容,被上下文以外的内容所占用,那么当前上下文就不能被释放 ==> 闭包。

闭包的作用:保护和保存。

let a = 0,b = 0;
function A(a){
    
    
    A = function (b) {
    
    
        console.log(a + b++);
    };
    console.log(a++);
}
A(1);   //结果:1
A(2);   //结果:4

图解:

image-20210917095614772

解析:

  1. 先有全局执行上下文EC(G),然后有全局变量的环境VO(G),a,b,function A都属于全局的变量。
  2. 给a、b创建基本数据类型值并赋值,但A是个函数,函数是引用类型的值,所以会开辟一个函数堆,有个地址AAAFFF000,然后将这个地址和函数A关联在一起。
  3. 创建A函数的时候会声明一个作用域 [[scope]]:EC(G)(在全局作用域下),形参是a,里面有代码字符串…
  4. A(1)执行。函数执行就是把函数里面的代码执行。那每个函数的执行都会形成一个全新的私有上下文(环境EC(A1))。AO(A1)是私有变量对象。函数执行会先初始化作用域链:<EC(A1),EC(G)>(左边是自己的上下文,右端是A这个函数创建时所在的作用域即为全局作用域EC(G))。在代码执行过程中遇了一个变量,首先看是否是自己的私有变量,如果是自己的私有变量则找私有的,否则就找其函数所在的作用域EC(G)中的全局变量。此时的进行形参赋值 a=1(形参变量也是私有变量,让a和1进行关联)。让代码执行,A = function(b){....}(这时要创建一个新函数堆内存(BBBFFF000),声明作用域:[[scope]]:EC(A1)。形参是b,代码字符串"console.log(a+b++)"),因为A不是EC(A1)私有的,故去EC(G)里面找,那么就要修改原来A对应的函数地址(将AAAFFF000改为BBBFFF000,即把全局下的A修改为BBBFFF000)。接着执行console.log(a++)(先输出a=1,然后再++,即做完后修改a的值为2,这里的a是EC(A1)里面的)。执行完毕。(此时EC(A1)不能被释放,因为全局中A的地址为BBBFFF000,占用了EC(A1)中的内容,这就形成了闭包)
  5. A(2)执行。此时的A对应的为BBBFFF000,也就是要进行那个小函数的执行(即执行 console.log(a + b++))。这时又会形成一个全新的私有上下文EC(A2)。作用域链:<EC(A2),EC(A1)>。形参赋值:b=2。代码继续执行:a + b++,因为当前上下文中没有a,故往上级上下文EC(A1)找,有a,此时a=2。b在自己的上下文EC(A2)中有,所以b=2,故a+b++=4,然后b++,修改当前上下文中的b为3。执行完毕。(因为执行完后,这个EC(A2)上下文中没有东西被外部东西占用,所以默认就会被释放掉)

三、总结

自己的错误点

A = function (b) {
    
    
        console.log(a + b++);
    };

会把上面那个代码看成是立即执行,其实并不是。老是犯这个错误,谨记!!!

猜你喜欢

转载自blog.csdn.net/weixin_48931875/article/details/121526211