Maison  >  Article  >  interface Web  >  Une explication détaillée des portées et des fermetures JavaScript

Une explication détaillée des portées et des fermetures JavaScript

小云云
小云云original
2017-12-05 10:40:311869parcourir

En tant que programmeur, la portée et la fermeture sont très importantes en JavaScript. Peut-être qu'il nous a été difficile de comprendre ce que sont la portée et la fermeture au début, et comment les utiliser en JavaScript ? Dans cet article, nous présenterons en détail les portées et les fermetures JavaScript.

Portée

La portée en JavaScript limite les variables auxquelles vous pouvez accéder. Il existe deux types de portée : la portée globale et la portée locale.

Portée globale

Les variables définies en dehors de toutes les déclarations de fonction ou accolades sont dans la portée globale.

Cependant, cette règle n'est valable que dans JavaScript exécuté dans le navigateur. Si vous êtes dans Node.js, les variables dans la portée globale sont différentes, mais cet article ne discutera pas de Node.js.

const globalVariable = 'some value'`

Une fois que vous avez déclaré une variable globale, vous pouvez l'utiliser n'importe où, y compris dans des fonctions.

const hello = 'Hello CSS-Tricks Reader!'

function sayHello () {
  console.log(hello)
}

console.log(hello) // 'Hello CSS-Tricks Reader!'
sayHello() // 'Hello CSS-Tricks Reader!'

Bien que vous puissiez définir des variables dans la portée globale, nous vous déconseillons de le faire. Des conflits de noms pouvant survenir, deux variables ou plus utilisent le même nom de variable. Si vous utilisez const ou let lors de la définition d'une variable, vous recevrez une erreur en cas de conflit de nom. Ce n'est pas conseillé.

// Don't do this!
let thing = 'something'
let thing = 'something else' // Error, thing has already been declared

Si vous utilisez var lors de la définition d'une variable, la deuxième définition écrasera la première définition. Cela rend également le code plus difficile à déboguer et n’est pas souhaitable.

// Don't do this!
var thing = 'something'
var thing = 'something else' // perhaps somewhere totally different in your code
console.log(thing) // 'something else'

Donc, vous devriez essayer d'utiliser des variables locales au lieu de variables globales

Portée locale

Dans une portée spécifique de votre code Variables utilisé dans peut être défini dans la portée locale. Il s'agit d'une variable locale .

Il existe deux types de portées locales en JavaScript : la portée de la fonction et la portée au niveau du bloc.

Nous commençons par la portée de la fonction.

Portée de la fonction

Lorsque vous définissez une variable dans une fonction, elle peut être utilisée n'importe où dans la fonction. En dehors de la fonction, vous ne pouvez pas y accéder.

Par exemple, dans l'exemple suivant, la variable hello dans la fonction sayHello :

function sayHello () {
  const hello = 'Hello CSS-Tricks Reader!'
  console.log(hello)
}

sayHello() // 'Hello CSS-Tricks Reader!'
console.log(hello) // Error, hello is not defined

Portée au niveau du bloc

Lorsque vous utilisez accolades , lorsque vous déclarez une variable const ou let, vous ne pouvez utiliser cette variable qu'à l'intérieur d'accolades.

Dans l'exemple suivant, hello ne peut être utilisé qu'entre accolades.

{
  const hello = 'Hello CSS-Tricks Reader!'
  console.log(hello) // 'Hello CSS-Tricks Reader!'
}

console.log(hello) // Error, hello is not defined

La portée au niveau du bloc est un sous-ensemble de la portée des fonctions, car les fonctions doivent être définies avec des accolades (sauf si vous utilisez explicitement des instructions de retour et des fonctions fléchées).

Promotion et portée de la fonction

Lorsqu'elle est définie à l'aide d'une fonction, la fonction sera promue en haut de la portée actuelle. Par conséquent, le code suivant est équivalent :

// This is the same as the one below
sayHello()
function sayHello () {
  console.log('Hello CSS-Tricks Reader!')
}

// This is the same as the code above
function sayHello () {
  console.log('Hello CSS-Tricks Reader!')
}
sayHello()

Lorsqu'elle est définie à l'aide d'une expression de fonction, la fonction n'est pas hissée en haut de la portée de la variable.

sayHello() // Error, sayHello is not defined
const sayHello = function () {
  console.log(aFunction)
}

Comme il y a deux variables ici, la promotion de fonction peut prêter à confusion, elle ne prendra donc pas effet. Assurez-vous donc de définir la fonction avant de l'utiliser.

La fonction ne peut pas accéder à la portée des autres fonctions

Lorsque différentes fonctions sont définies séparément, bien qu'une fonction puisse être appelée dans une fonction, une fonction ne peut toujours pas accéder à la portée des autres fonctions internes.

Dans l'exemple suivant, second ne peut pas accéder à la variable firstFunctionVariable.

function first () {
  const firstFunctionVariable = `I'm part of first`
}

function second () {
  first()
  console.log(firstFunctionVariable) // Error, firstFunctionVariable is not defined
}

Portée imbriquée

Si une fonction est définie à l'intérieur d'une fonction, alors la fonction interne peut accéder aux variables de la fonction externe, mais vice versa non. L’effet est une portée lexicale.

La fonction externe ne peut pas accéder aux variables de la fonction interne.

function outerFunction () {
  const outer = `I'm the outer function!`

  function innerFunction() {
    const inner = `I'm the inner function!`
    console.log(outer) // I'm the outer function!
  }

  console.log(inner) // Error, inner is not defined
}

Si vous visualisez le mécanisme de la lunette, vous pouvez imaginer un miroir sans tain (verre transparent d'un seul côté). Vous pouvez voir de l’intérieur, mais les personnes extérieures ne peuvent pas vous voir.

Une explication détaillée des portées et des fermetures JavaScript

La portée des fonctions est comme un miroir sans tain. Vous pouvez regarder de l’intérieur, mais vous ne pouvez pas être vu de l’extérieur.

Les lunettes emboîtées ont un mécanisme similaire, mais elles sont équivalentes à davantage de miroirs sans tain.

Une explication détaillée des portées et des fermetures JavaScript

Plusieurs couches de fonctions signifient plusieurs miroirs bidirectionnels.

Comprenez la partie précédente sur la portée et vous comprendrez ce qu'est une fermeture.

Fermeture

Lorsque vous créez une autre fonction au sein d'une fonction, cela équivaut à créer une fermeture. Les fonctions internes sont des fermetures. Normalement, afin de rendre accessibles les variables internes de la fonction externe, cette fermeture est généralement renvoyée.

function outerFunction () {
  const outer = `I see the outer variable!`

  function innerFunction() {
    console.log(outer)
  }

  return innerFunction
}

outerFunction()() // I see the outer variable!

Étant donné que les fonctions internes renvoient des valeurs, vous pouvez simplifier la partie déclaration de fonction :

function outerFunction () {
  const outer = `I see the outer variable!`

  return function innerFunction() {
    console.log(outer)
  }
}

outerFunction()() // I see the outer variable!

Parce que les fermetures peuvent accéder aux variables de fonctions externes, elles ont donc généralement deux utilisations :

  1. Réduire les effets secondaires

  2. Créer des variables privées

Utilisez des fermetures pour contrôler les effets secondaires

Lorsque vous faites quelque chose lorsqu'une fonction renvoie une valeur, certains effets secondaires se produisent généralement. Des effets secondaires se produisent dans de nombreuses situations, telles que les appels Ajax, les délais d'attente ou même les instructions de sortie console.log :

function (x) {
  console.log('A console.log is a side effect!')
}

当你使用闭包来控制副作用时,你实际上是需要考虑哪些可能会混淆代码工作流程的部分,比如Ajax或者超时。

要把事情说清楚,还是看例子比较方便:

比如说你要给为你朋友庆生,做一个蛋糕。做这个蛋糕可能花1秒钟的时间,所以你写了一个函数记录在一秒钟以后,记录做完蛋糕这件事。

为了让代码简短易读,我使用了ES6的箭头函数:

function makeCake() {
  setTimeout(_ => console.log(`Made a cake`, 1000)
  )
}

 

如你所见,做蛋糕带来了一个副作用:一次延时。

更进一步,比如说你想让你的朋友能选择蛋糕的口味。那么你就给做蛋糕 makeCake 这个函数加了一个参数。

function makeCake(flavor) {
  setTimeout(_ => console.log(`Made a ${flavor} cake!`, 1000))
}

 

因此当你调用这个函数时,一秒后这个新口味的蛋糕就做好了。

makeCake('banana')
// Made a banana cake!

 

但这里的问题是,你并不想立刻知道蛋糕的味道。你只需要知道时间到了,蛋糕做好了就行。

要解决这个问题,你可以写一个 prepareCake 的功能,保存蛋糕的口味。然后,在返回在内部调用 prepareCake 的闭包 makeCake 。

从这里开始,你就可以在你需要的时调用,蛋糕也会在一秒后立刻做好。

function prepareCake (flavor) {
  return function () {
    setTimeout(_ => console.log(`Made a ${flavor} cake!`, 1000))
  }
}

const makeCakeLater = prepareCake('banana')

// And later in your code...
makeCakeLater()
// Made a banana cake!

 

这就是使用闭包减少副作用:你可以创建一个任你驱使的内层闭包。

私有变量和闭包

前面已经说过,函数内的变量,在函数外部是不能访问的既然不能访问,那么它们就可以称作私有变量。

然而,有时候你确实是需要访问私有变量的。这时候就需要闭包的帮助了。

function secret (secretCode) {
  return {
    saySecretCode () {
      console.log(secretCode)
    }
  }
}

const theSecret = secret('CSS Tricks is amazing')
theSecret.saySecretCode()
// 'CSS Tricks is amazing'

 

这个例子里的 saySecretCode 函数,就在原函数外暴露了 secretCode 这一变量。因此,它也被成为特权函数。

使用DevTools调试

Chrome和Firefox的开发者工具都使我们能很方便的调试在当前作用域内可以访问的各种变量一般有两种方法。

第一种方法是在代码里使用 debugger 关键词。这能让浏览器里运行的JavaScript的暂停,以便调试。

下面是 prepareCake 的例子:

function prepareCake (flavor) {
  // Adding debugger
  debugger
  return function () {
    setTimeout(_ => console.log(`Made a ${flavor} cake!`, 1000))
  }
}

const makeCakeLater = prepareCake('banana')

 

打开Chrome的开发者工具,定位到Source页下(或者是Firefox的Debugger页),你就能看到可以访问的变量了。

Une explication détaillée des portées et des fermetures JavaScript

使用debugger调试 prepareCake 的作用域。

你也可以把 debugger 关键词放在闭包内部。注意对比变量的作用域:

function prepareCake (flavor) {
  return function () {
    // Adding debugger
    debugger
    setTimeout(_ => console.log(`Made a ${flavor} cake!`, 1000))
  }
}

const makeCakeLater = prepareCake('banana')

 

Une explication détaillée des portées et des fermetures JavaScript

调试闭包内部作用域

第二种方式是直接在代码相应位置加断点,点击对应的行数就可以了。

Une explication détaillée des portées et des fermetures JavaScript

通过断点调试作用域

闭包和作用域并不是那么难懂。一旦你使用双向镜的思维去理解,它们就非常简单了。当你在函数里声明一个变量时,你只能在函数内访问。这些变量的作用域就被限制在函数里了。如果你在一个函数内又定义了内部函数,那么这个内部函数就被称作闭包。它仍可以访问外部函数的作用域。希望本文能帮助到大家。

相关推荐:

JS的解析顺序和作用域以及严格模式的简单介绍

javascript中关于作用于作用域链的详解

JavaScript作用域与闭包的分析讲解

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn