js执行上下文

100 阅读7分钟

执行代码环境:
全局作用域:(第一次执行代码的默认环境),
函数作用域:(当执行流入函数体时)

上下文执行可以理解为:当前代码执行的环境与作用域

当程序调用一个函数时,会发生什么?

1.javaScript会创建一个新的执行上下文,叫本地执行上下文

2.这个本地执行上下文会有自己的变量,这些变量将是这个执行上下文的本地变量

3.新的执行上下文被推到执行堆栈中。可以将执行堆栈看作一种保存程序在其执行中的位置的容器

什么时候会结束这个函数?当它遇到一个return语句或一个结束括号}

当函数结束时会发生什么

1.这个执行上下文会在执行堆栈中弹出

2.函数将返回值返回 调用上下文。调用上下文是调用这个本地执行上下文,它可以是全局执行上下文,也可以是另外一个本地执行上下文。这取决于调用的执行上下文 来处理 此时的返回值,返回值可以是一个对象、一个数组、一个函数、一个布尔值等等,如果函数没有return语句,则返回undefined

 function fn() {}
      console.log(fn());//undefined
function fn() {
        return function () {
          console.log("你好");
        };
      }
      console.log(fn());//返回一个函数体

3.这个本地执行上下文被销毁,销毁是很重要的,这个本地执行上下文中声明的所有变量都将被删除,不再有变量,这个就是为什么称本地执行上下文中自有的变量

理解javaScript引擎是如何工作的

1let a=3
2function fn(x){
3let b=x+2
4return b
5:}
6let c=fn(a)
7console.log(c)
  1. 1行声明了一个变量,为a,并赋值为3

  2. 2行和第5行是一起的,这里发生的是,在执行上下文的时候声明了一个名为fn的新变量,给它分配了一个函数定义,两个{}之间的的内容都分配给了fn,函数内部的代码没有被求值,没有被执行,只是存储在一个变量中以备将来使用

  3. 6行,首先在全局上下文中声明一个一个新的变量,并标记为c,变量一旦声明,如果没有赋值则为undefined ,给c赋了一个新值,接着一个函数被调用,每个函数都会返回一些东西(值,对象或undefined),无论从函数中返回什么,都将赋值给变量c

  4. 我们调用了fn函数,javaScript将在其全局执行上下文中查找名为fn的变量,它找到了就在2-5行,fn变量包含了一个函数定义。注意,变量a作为参数传递给函数,javaScript将在其全局上下文内存中查找变量a,它的值为3

  5. 现在执行上下文切换,创建了一个新的本地上下文,将其命名fn执行上下文(其实也称为函数局部作用域),执行上下文被推到执行堆栈上,先看函数的参数,在fn执行上下文中声明了一个新的变量x,因为3是作为参数传递的,所以变量x被赋值为3

  6. 它首先将在fn执行上下文中寻找变量x,然后将值分配给变量b

  7. 4行,将返回变量b的内容,在fn执行上下文中寻找到值为5,返回出去,函数结束。

  8. 函数结束,fn执行上下文被销毁,变量xb被释放,它们已经不存在了,变量a没有被销毁,因为它是全局执行上下文的一部分,fn函数在执行堆栈中弹出,返回值返回给调用上下文,在这种情况下,调用上下文是全局执行上下文(也叫全局作用域),因为函数fn是从全局执行上下文调用的

  9. 继续第4步,将返回值分配给变量c,程序仍停留在第6

  10. 在第七行,c的值被打印出来

这里还没有涉及到闭包

闭包
1function fn(){
2: let a =2
3: let fn2=function(){
4: a=a+1
5: return a
6: }
7: return fn2
8: }
9: const increment=fn()
10const c1=increment()
11const c2=increment()
12const c3=increment()
13console.log(c1,c2,c3):

1.1-8行,在全局执行上下文中创建了一个新的变量fn并定义了一个函数

2.第9行定义了一个新变量increment分配了一个变量fn函数,并将其返回值赋给了变量increment

3.第1-8行,调用函数时创建了一个新的本地执行上下文

4.第2行,在本地上下文中,声明了一个变量a,并赋值为2

5.第3-6行,在本地执行上下文中声明了fn2变量,并分配了一个函数定义,还创建了一个闭包,并将其作为函数定义的一部分。闭包中包含作用域中的变量,如本地执行上下文中变量a为2

6.第7行,返回变量fn2的内容,删除fn本地执行上下文,fn与变量a将不存在。控制权交给了调用上下文(全局执行上下文,全局作用域),返回函数定义与它的闭包,闭包中包含了创建它时在的作用域内的变量

7.第9行,在调用上下(全局上下文)中,fn的返回值被指定为incrementincrement现在包含一个函数定义和(闭包),fn返回的函数定义,此时不再被定义为fn2,而是在全局执行上下文中的incrementfn的返回值fn2函数定义被分配给了全局执行上下文的变量increment

8,第10行,声明了新变量c1,分配变量increment给它,它是一个函数,调用它,它包含了一个函数4-5行(带有变量的闭包)

9.创建了一个新的本地执行上下文,没有参数

10,第4行,a=a+1,选择变量a,在寻找本地或全局执行上下文之前,检查一下闭包,闭包中包含了一个名为a的变量,值是2,在第4行表达式后,它的值为3.它将再次被存储在闭包里,闭包现在包含值为3的变量a

11.第5行,返回变量a并销毁本地执行上下文

12.回到第10行,返回值3被赋值给c1

13.第11行,声明了新变量c2,重复步骤8-12行,此时闭包中的a为4

14.第12行,声明了新变量c3,重复步骤8-12行,此时闭包中的a为5

15.第13行,打印c1,c2,c3的值

是的,任何函数都有闭包,甚至是全局函数,在全局作用域中创建的函数创建闭包,但是这些函数是在全局作用域中创建的,所以他们可以访问全局作用域中的所有变量,闭包概念变得并不重要

当函数返回函数时,闭包就变得更加重要了。返回的函数可以访问不属于全局作用域(函数作用域)的变量,因为闭包中存有这些变量,但它们仅存在其闭包中。

闭包2

let c = 4
const addX = (x) => (n) => n + x
const addThree = addX(3)
let d = addThree(c)
console.log('example partial application', d)

与上面的效果是一样的

let c = 4
function addX(x) {
  return function(n) {
     return n + x
  }
}
const addThree = addX(3)
let d = addThree(c)
console.log('example partial application', d)

柯里化函数闭包

 function getArea(w) {
        return function (h) {
          return w * h;
        };
      }
      const getTenArea = getArea(10);

      const area1 = getTenArea(20);
      const area2 = getTenArea(30);
      const area3 = getTenArea(40);
      console.log(area1, area2, area3);
总结

闭包就是在所有函数中都包含的,只是在函数返回返回时才显得重要,因为它存储了本地执行上下文的变量,而在函数返回的函数的本地上下文执行里可以获取到这些变量。