执行代码环境:
全局作用域:(第一次执行代码的默认环境),
函数作用域:(当执行流入函数体时)
上下文执行可以理解为:当前代码执行的环境与作用域
当程序调用一个函数时,会发生什么?
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引擎是如何工作的
1:let a=3
2:function fn(x){
3:let b=x+2
4:return b
5:}
6:let c=fn(a)
7:console.log(c)
-
在
1行声明了一个变量,为a,并赋值为3 -
第
2行和第5行是一起的,这里发生的是,在执行上下文的时候声明了一个名为fn的新变量,给它分配了一个函数定义,两个{}之间的的内容都分配给了fn,函数内部的代码没有被求值,没有被执行,只是存储在一个变量中以备将来使用 -
第
6行,首先在全局上下文中声明一个一个新的变量,并标记为c,变量一旦声明,如果没有赋值则为undefined,给c赋了一个新值,接着一个函数被调用,每个函数都会返回一些东西(值,对象或undefined),无论从函数中返回什么,都将赋值给变量c -
我们调用了
fn函数,javaScript将在其全局执行上下文中查找名为fn的变量,它找到了就在2-5行,fn变量包含了一个函数定义。注意,变量a作为参数传递给函数,javaScript将在其全局上下文内存中查找变量a,它的值为3 -
现在执行上下文切换,创建了一个新的本地上下文,将其命名为
fn执行上下文(其实也称为函数局部作用域),执行上下文被推到执行堆栈上,先看函数的参数,在fn执行上下文中声明了一个新的变量x,因为3是作为参数传递的,所以变量x被赋值为3 -
它首先将在
fn执行上下文中寻找变量x,然后将值分配给变量b -
第
4行,将返回变量b的内容,在fn执行上下文中寻找到值为5,返回出去,函数结束。 -
函数结束,
fn执行上下文被销毁,变量x和b被释放,它们已经不存在了,变量a没有被销毁,因为它是全局执行上下文的一部分,fn函数在执行堆栈中弹出,返回值返回给调用上下文,在这种情况下,调用上下文是全局执行上下文(也叫全局作用域),因为函数fn是从全局执行上下文调用的 -
继续第
4步,将返回值分配给变量c,程序仍停留在第6行 -
在第七行,
c的值被打印出来
这里还没有涉及到闭包
闭包
1:function 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()
10:const c1=increment()
11:const c2=increment()
12:const c3=increment()
13:console.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的返回值被指定为increment,increment现在包含一个函数定义和(闭包),fn返回的函数定义,此时不再被定义为fn2,而是在全局执行上下文中的increment,fn的返回值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);
总结
闭包就是在所有函数中都包含的,只是在函数返回返回时才显得重要,因为它存储了本地执行上下文的变量,而在函数返回的函数的本地上下文执行里可以获取到这些变量。