第2-8行,我们在全局执行上下文中声明了一个名为createAdder的变量,并为其分配了一个函数定义。第3至7行是这个函数的定义,这个时候我们并没有跳进那个函数,只是将函数定义存储到该变量(createAdder)中。
第9行,我们在全局执行上下文中声明一个名为adder的新变量,并暂时赋值为undefined。
第9行,我们看到了括号()。我们需要执行或调用函数。我们在全局执行上下文的内存中查找名为createAdder的变量,它是在第2步中创建的。找到它,然后调用它。
第2行,调用一个函数,创建了一个新的本地执行上下文。我们可以在新的执行上下文中创建局部变量,引擎将新上下文添加到调用栈。这个函数没有参数,所以直接进入它的函数体。
3-6行,我们有一个新的函数声明,我们在本地执行上下文中创建变量addNumbers,这很重要。addNumbers仅在本地执行上下文中存在,我们将函数定义存储在名为addNumbers的局部变量中。
第7行,我们返回变量addNumbers的内容。引擎查找名为addNumbers的变量,这是一个函数定义。一个函数可以返回任何东西,包括函数定义,所以我们返回addNumbers的定义。第4行和第5行的括号之间的任何内容构成了函数定义,我们还从调用栈中删除了本地执行上下文。
在return语句之后,本地执行上下文被销毁,addNumbers变量也不复存在,但函数定义仍然存在,它从函数返回并赋给变量adder,这是我们在第3步中创建的变量。
第10行,我们在全局执行上下文中定义了一个新的变量sum,临时赋值undefined。
接下来我们需要执行一个函数,哪个函数?在名为adder的变量中定义的函数。我们在全局执行环境中查找它,这是一个带两个参数的函数。
我们先拿到两个参数,这样就可以调用函数并将正确的参数传给它。第一个是变量val,在步骤1中定义的,它代表数值7,第二个是数值8。
现在我们要执行这个函数,函数体是在第3-5行定义的。创建一个新的本地执行上下文。在本地上下文中,创建了两个新变量:a和b。它们分别被赋值为7和8,因为它们是在上一步传给函数的参数。
第4行,在本地执行上下文中声明一个新变量ret。
第4行,执行加法运算,其中我们变量a的内容和变量b的内容相加,再将相加的结果(15)赋给变量ret。
从函数返回变量ret,本地执行上下文被销毁,并从调用栈中删除,变量a、b和ret不再存在。
返回的值被赋给我们在步骤9中定义的sum变量。
我们将sum的值打印到控制台。
正如预期的那样,控制台将打印出15。我想说明几点:首先,函数定义可以存储在变量中,函数定义在被调用之前对程序是不可见的。其次,每次调用函数时,就会临时创建一个本地执行上下文,当函数执行结束时,执行上下文就被销毁。函数在遇到return语句或结束括号}时执行结束。
现在来说说闭包看看下面的代码,并试着弄清楚会发生什么。
1: function createCounter() { 2: let counter = 0 3: const myFunction = function() { 4: counter = counter + 1 5: return counter 6: } 7: return myFunction 8: } 9: const increment = createCounter() 10: const c1 = increment() 11: const c2 = increment() 12: const c3 = increment() 13: console.log('example increment', c1, c2, c3)现在让我们来看看这段代码将如何执行:
第1-8行,我们在全局执行上下文中创建了一个新变量createCounter,它包含了一个函数定义。
第9行,我们在全局执行上下文中声明了一个名为increment的新变量。
第9行,我们调用createCounter函数并将其返回值赋给increment变量。
第1-8行,调用函数,创建新的本地执行上下文。
第2行,在本地执行上下文中,声明一个名为counter的新变量,并赋值为0。
第3-6行,在本地执行上下文中声明名为myFunction的新变量。变量的内容是另一个函数定义,也就是第4行和第5行。
第7行,返回myFunction变量的内容。删除本地执行上下文,myFunction和counter不再存在,控制权返回到调用上下文。