Maison  >  Article  >  interface Web  >  Une brève discussion sur les fonctions et les fermetures JS

Une brève discussion sur les fonctions et les fermetures JS

青灯夜游
青灯夜游avant
2019-11-30 16:58:352157parcourir

Chaque fois qu'une fonction est déclarée, une portée est créée. La portée extérieure ne peut pas accéder à la portée intérieure (masquer les variables et les fonctions à l'intérieur de ), mais la portée intérieure peut accéder à la portée extérieure. Une technique très utile pour masquer des variables et des fonctions.

Une brève discussion sur les fonctions et les fermetures JS

La méthode basée sur le masquage de la portée est appelée Autorisation minimale ou Principe d'exposition minimale.

Ce principe signifie que dans la conception de logiciels, le contenu nécessaire doit être exposé au minimum et son contenu doit être masqué, comme la conception API d'un certain module ou objet. Les variables et fonctions cachées peuvent résoudre les conflits entre les identifiants portant le même nom, ce qui peut conduire à un écrasement accidentel des variables.

Par exemple :

var a = 2;
function foo(){
  var a = 3;
  console.log(a);
}
foo();
console.log(a);

Bien que cette technique puisse résoudre certains problèmes, elle n'est pas idéale et entraînera des problèmes supplémentaires. Tout d'abord, une fonction nommée foo() doit être déclarée, ce qui signifie foo Le. le nom lui-même "pollue" la portée, et deuxièmement, cette fonction doit être explicitement appelée via le nom de la fonction foo() pour y exécuter le code.

Il serait plus idéal si la fonction ne nécessite pas de nom de fonction et peut être exécutée automatiquement. Heureusement, js fournit une solution à ces deux problèmes en même temps - (IIFE) Expression de fonction immédiatement invoquée - Exécuter immédiatement la fonction

var a = 2;
(function foo(){
    var a = 3;
    console.log(a);
})()
console.log(a);

Tout d'abord, la fonction immédiatement exécutée n'est pas traitée comme une déclaration de fonction mais est traitée comme comme une expression de fonction.

Distinguer déclaration de fonction ou expression de fonction : Vérifiez si la fonction est le premier mot de la déclaration. Si c'est le premier mot, c'est une déclaration de fonction, sinon c'est une. expression de fonction. La fonction " (fonction " est exécutée immédiatement, pas " fonction ", c'est donc une expression de fonction.

Entre la déclaration de fonction et expression de fonctionLa différence la plus importante est l'endroit où leurs identifiants de nom seront liés

Le nom de fonction déclaré par la fonction sera lié dans la portée actuelle Si vous créez. une déclaration de fonction dans la portée globale, vous pouvez accéder au nom de la fonction et l'exécuter dans la portée globale, et le nom de fonction de l'expression de fonction sera lié à la fonction elle-même, pas à la portée actuelle. Vous créez une expression de fonction globalement. Si vous exécutez directement le nom de fonction de l'expression de fonction que vous avez créée, une erreur sera signalée car il n'y a pas d'identifiant de ce type dans la portée actuelle et vous accédez à cette fonction dans la portée à l'intérieur de l'expression de fonction. Le nom renverra une référence à. cette fonction

Portée fermeture, eh bien, les deux mots fermeture sont un peu difficiles à comprendre, (vous pouvez imaginer qu'un colis est fermé. , il y a des choses mystérieuses cachées dedans) La définition de fermeture dit ceci : Lorsqu'une fonction peut mémoriser et accéder au scope dans lequel elle se trouve, une fermeture est générée, même si la fonction est exécutée en dehors du scope actuel

par exemple (en anglais, haha).

function foo() {
    var a = 2;
    function bar() {
        console.log(a);
    }
    bar();
}
foo();
La barre de code ci-dessus () peut accéder aux variables dans la portée externe. D'après la définition ci-dessus, est-ce une fermeture ? Peut-être que c'est le cas, mais ce que nous comprenons, c'est que si la portée recherche des variables dans la portée actuelle, il continuera à chercher vers le haut, et s'il est trouvé, il continuera à chercher jusqu'à ce qu'il soit trouvé dans la portée globale - et ce sont les fermetures Part. La fonction bar() a une fermeture qui couvre la portée de foo(. ). Le code ci-dessus montre mieux la fermeture de la fonction

bar(). Exécutée en dehors de la portée (exécutée dans la portée globale à ce moment-là, une fois la fonction foo() exécutée, elle est généralement attendue). que toute la portée interne de foo() sera détruite, car nous savons que le moteur a un

garbage collector

Utilisé pour libérer l'espace mémoire inutilisé Puisque foo() a été exécuté, il semble que le contenu le sera. ne peut plus être utilisé, il est donc naturel d'envisager l'alignement pour le recyclage. Après le recyclage, cela signifie que les fonctions et les variables à l'intérieur ne sont plus accessibles. Après l'exécution de foo(), la variable baz contient une référence à la fonction bar. je ne comprends pas les fermetures peuvent penser qu'une erreur sera signalée. En fait, 2 est imprimé ;? La portée de la fonction

n'est-elle pas toujours accessible ?

当foo()执行后,bar函数被返回全局作用域下,但是bar函数还保留着当时的词法作用域(当时写代码是的顺序就已经定义了作用域,这个作用域叫词法作用域--外面函数套着里面的函数的那种)甚至直到全局作用域。所以bar还留有foo()函数的引用。使得foo()函数没有被回收。

闭包可以说不出不在,只是你没有发现认出他。在定时器,事件监听器,ajax请求,跨窗口通信或者任何其他的异步(或者同步)任务中,只要使用了回调函数,实际上就是使用闭包。

for instance

function wait(message) {
    setTimeout(function timer() {
        console.log(message);
    }, 1000);
}
wait("hello");

在上面的代码中将一个内部函数(名为timer)传递给setTimerout(...).timer具有涵盖wait(...)的作用域的闭包。因此还保有对变量message的引用。wait()执行1000毫秒后,它的内部作用域不会消失,timer函数依然保有wait()作用域的闭包。

而闭包和立即执行函数息息相关。

循环和闭包

for(var i = 1; i <= 5; i++){
    setTimeout(function timer(){
        console.log(i);
    },i*1000);
}

上面代码我们以为输出的会是1-5,可事实上输出的是5个6,这是为啥啊 -- 闭包啊。

延迟函数的回调会在循环结束时执行。事实上,当定时器运行时即使每个迭代的是setTimerout(...,0),所有的回调函数依然是循环结束后才会执行。我猜是跟js执行机制有关系吧。至于为什么都是6. 因为即使5个函数是在各个迭代中分别定义的,但是他们又被封闭在一个共享的全局作用域中因此实际上只有一个i.而怎么解决呢,立即执行函数来了!!!

for (var i = 1; i <= 5; i++) {
    (function (i) {
        setTimeout(function timer() {
            console.log(i);
        }, i * 1000);
    })(i)

}

打印出来1,2,3,4,5了欧,这回是你想要的数了。解释一下,5次循环创建了5个立即执行函数,这5个函数的作用域都不相同,立即函数接收的参数是当前循环的i.所以当timer执行时访问的就是自己立即执行函数对应的作用域。也就是说5个timer函数分别对应5个作用域,每个作用域保存的变量i都不同,解决啦!!!

你懂闭包了吗?

js执行机制

JavaScript语言的一大特点就是单线程,也就是说,同一个时间只能做一件事。那么,为什么JavaScript不能有多个线程呢?这样能提高效率啊。JavaScript的单线程,与它的用途有关。作为浏览器脚本语言,JavaScript的主要用途是与用户互动,以及操作DOM。这决定了它只能是单线程,否则会带来很复杂的同步问题。比如,假定JavaScript同时有两个线程,一个线程在某个DOM节点上添加内容,另一个线程删除了这个节点,这时浏览器应该以哪个线程为准所以,为了避免复杂性,从一诞生,JavaScript就是单线程,这已经成了这门语言的核心特征,将来也不会改变。

单线程就意味着,所有任务需要排队,前一个任务结束,才会执行后一个任务。如果前一个任务耗时很长,后一个任务就不得不一直等着。JavaScript语言的设计者意识到这个问题,将所有任务分成两种,一种是同步任务(synchronous),另一种是异步任务(asynchronous)。同步任务指的是,在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务;异步任务指的是,不进入主线程、而进入"任务队列"(task queue)的任务,只有"任务队列"通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行。

主线程从"任务队列"中读取事件,这个过程是循环不断的,所以整个的这种运行机制又称为Event Loop(事件循环)。只要主线程空了,就会去读取"任务队列",这就是JavaScript的运行机制。

哪些语句会放入异步任务队列及放入时机一般来说,有以下四种会放入异步任务队列:setTimeout 和 setlnterval  ,DOM事件,ES6中的Promise,Ajax异步请求

 本文来自 js教程 栏目,欢迎学习!

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:
Cet article est reproduit dans:. en cas de violation, veuillez contacter admin@php.cn Supprimer