范围和闭包在JavaScript中非常重要。但是当我第一次学习JavaScript的时候,很难理解。本文将使用一些例子来帮助您理解它们。
先说范围。
行动范围
JavaScript的范围限制了您可以访问的变量。有两个范围:全局范围和局部范围。
全球范围
在所有函数声明或大括号之外定义的变量都在全局范围内。
但是,这条规则只在浏览器中运行的JavaScript中有效。如果您在Node.js中,全局范围内的变量是不同的,但是本文不会讨论node.js。
Const globalVariable='some value ' `一旦声明了全局变量,就可以在任何地方使用它,包括在函数内部。
const hello=“你好CSS-技巧阅读器!”函数sayHello(){ console . log(Hello)} console . log(Hello)/' Hello CSS-ticks Reader!'sayHello() //“你好CSS-技巧阅读器!”虽然您可以在全局范围内定义变量,但我们不建议这样做。两个或多个变量使用相同的变量名,因为这可能会导致命名冲突。如果在定义变量时使用const或let,当命名发生冲突时,您将收到一条错误消息。这是不可取的。
//不要这样!Let thing='某物' let thing=' other thing '/error,thing已经声明如果在定义变量时使用var,第二个定义将覆盖第一个定义。这也会使代码更难调试,这也是不可取的。
//不要这样!Var thing='某物' var thing='其他东西'/也许是你的代码控制台中完全不同的东西. log (thing)/'其他东西',所以你应该尝试使用局部变量而不是全局变量
本地范围
在代码的特定范围内使用的变量可以在局部范围内定义。这是局部变量。
JavaScript中有两种局部作用域:函数作用域和块级作用域。
让我们从函数作用域开始。
功能范围
当您在函数中定义变量时,它可以在函数中的任何地方使用。在函数之外,您无法访问它。
例如,在以下示例中,sayHello函数中的hello变量:
函数sayHello(){ const Hello=' Hello CSS-ticks Reader!'console . log(Hello)} say Hello()//“Hello CSS-技巧阅读器!”console . log(hello)//错误,hello未定义块级范围
当您在使用花括号时声明一个常量或字母的变量时,您只能在花括号内使用这个变量。
在下面的示例中,hello只能在大括号内使用。
{ const Hello=' Hello CSS-ticks Reader!'console.log(hello) //“你好CSS-技巧阅读器!”} console . log(hello)//错误,hello未定义块级作用域是函数作用域的子集,因为函数需要用大括号定义(除非明确使用return语句和arrow函数)。
功能提升和范围
使用函数定义时,此函数将被提升到当前范围的顶部。因此,以下代码是等效的:
//这与下面的一个相同:sayHello()函数sayHello(){ console . log(' Hello CSS-Ticks Reader!')}//这和上面的代码函数sayHello(){ console . log(' Hello CSS-ticks Reader!')}sayHello()是由函数表达式定义的,函数不会提升到变量作用域的顶部。
问好()//错误,问好未定义const问好=function(){ console . log(a function)}因为这里有两个变量,函数提升可能会导致混淆,所以不会生效。所以在使用之前一定要定义函数。
函数不能访问其他函数的范围
当分别定义不同的函数时,虽然一个函数中可以调用一个函数,但是一个函数仍然不能访问其他函数的范围。
在以下示例中,second无法访问firstFunctionVariable。
函数first(){ const firstfunctionvariable=`我是first的一部分` }函数second(){ first()console . log(first functionvariable)//错误,first functionvariable未定义}嵌套作用域
如果函数是在函数内部定义的,那么内部函数可以访问外部函数的变量,但不能反过来。这种影响是词汇范围。
外部函数不能访问内部函数的变量。
函数outfunction(){ const outer=`我是外函数!` function inner function(){ const inner=`我是内部函数!` console.log(outer) //我是外函数!} console . log(inner)//错误,inner未定义}如果将scope的机制可视化,可以想象有一个双向镜(单面透视玻璃)。你可以从里面看到外面,但是外面的人看不到你。
功能范围就像一面双向镜。你可以从里面往外看,但是从外面看不到你。
嵌套作用域是一种类似的机制,但它相当于有更多的双向镜像。
多层功能意味着多个双向反射镜。
通过理解前面关于范围的部分,您可以理解什么是闭包。
关闭
当您在另一个函数中创建一个新函数时,它相当于创建一个闭包。内部函数是一个闭包。通常,为了使外部函数的内部变量可访问,通常会返回这个闭包。
function outfunction(){ const outer=`我看到了外部变量!` function innerFunction(){ console . log(outer)}返回innerFunction } outer function()()//我看到了outer变量!因为内部函数是返回值,所以可以简化函数声明的部分:
function outfunction(){ const outer=`我看到了外部变量!` return function innerFunction(){ console . log(outer)} } outer function()()//我看到了outer变量!因为闭包可以访问外部函数的变量,所以它们通常用于两个目的:
减少副作用,创建私有变量,并使用闭包来控制副作用
当您在函数返回值时执行某些操作时,通常会出现一些副作用。副作用可能在许多情况下发生,例如Ajax调用、超时处理,甚至console.log的输出语句:
函数(x) { console.log('A console.log是副作用!')}当您使用闭包来控制副作用时,您实际上需要考虑什么可能会混淆代码工作流,例如Ajax或超时。
要把事情说清楚,看例子更方便:
例如,你应该为你朋友的生日做一个蛋糕。制作这个蛋糕可能需要一秒钟,所以你写了一个函数来记录一秒钟后的蛋糕。
为了使代码简短易读,我使用了ES6的箭头函数:
函数make cake(){ settimeout(_=console . log(` make a cake `,1000))}可以看到,做蛋糕会带来一个副作用:延迟。
再比如,你想让你的朋友选择蛋糕的味道。然后给makeCake函数添加一个参数。
函数makeCake(风味){ setTimeout(_=console.log(`做了一个$ {风味}蛋糕!`,1000))。
做蛋糕('香蕉')//做了一个香蕉蛋糕!但这里的问题是你不想马上知道蛋糕的味道。你只需要知道时间到了,蛋糕做好了。
要解决这个问题,可以写一个prepareCake的函数来保存蛋糕的味道。然后,在返回时在内部调用prepareCake的闭包makeCake。
从这里开始,你需要的时候可以叫它,蛋糕马上就好了。
function prepareCake(风味){ return function(){ setTimeout(_=console . log(`做了一个$ {风味}蛋糕!`,1000))。makecakelate()//做了一个香蕉蛋糕!这是为了使用闭包来减少副作用:您可以创建一个可以驱动的内部闭包。
私有变量和闭包
如前所述,函数内部的变量不能在函数外部访问。因为它们不能被访问,所以它们可以被称为私有变量。
然而,有时您确实需要访问私有变量。此时,我们需要闭包的帮助。
函数secretCode(secret code){ return { saySecretCode(){ console . log(secretCode)} } const theSecret=secret(' CSS Tricks太神奇了')secret code示例中的saySecretCode函数。saysecretCode()//“CSS的技巧太神奇了”暴露了原始函数之外的变量secretCode。因此,它也被称为特权函数。
使用开发工具调试
Chrome和Firefox开发工具使我们可以轻松调试当前范围内可以访问的各种变量。
第一种方法是在代码中使用调试器关键字。这允许在浏览器中运行的JavaScript被挂起进行调试。
以下是prepareCake的示例:
函数prepareCake(风味){ //添加调试器调试器返回函数(){ setTimeout(_=console.log(`做了一个$ {风味}蛋糕!`,1000))。
使用调试器调试prepareCake的范围。
您也可以将调试器关键字放在闭包中。注意对比变量的范围:
函数prepareCake(风味){ return function () { //添加调试器调试器setTimeout(_=console.log(`做了一个$ {风味}蛋糕!`,1000))。
调试闭包内部范围
第二种方式是直接在代码对应的位置添加断点,点击对应的行数。
通过断点调试范围
综上
闭包和作用域并不难理解。一旦用双向镜的思维去理解它们,它们就很简单了。
在函数中声明变量时,只能在函数中访问它。这些变量的范围仅限于函数。
如果在函数中定义了内部函数,则该内部函数称为闭包。它仍然可以访问外部函数的范围。
以上就是本文的全部内容。希望对大家的学习有帮助,支持我们。