Maison > interface Web > js tutoriel > Ce que vous devez savoir sur les fermetures JavaScript

Ce que vous devez savoir sur les fermetures JavaScript

WBOY
Libérer: 2022-01-21 17:58:51
avant
1458 Les gens l'ont consulté

Cet article vous apporte des notes d'étude sur les fermetures JavaScript, y compris les fermetures, les piles de méthodes et le rôle des fermetures. J'espère qu'il vous sera utile.

Ce que vous devez savoir sur les fermetures JavaScript

Par définition, c'est un langage de script, et c'est un langage de script relativement facile à apprendre. Sans grande expertise, vous pouvez également utiliser le code js (abréviation de JavaScript) dans une certaine mesure.

Bien sûr, si vous avez déjà acquis quelques connaissances en front-end, vous devriez être capable de comprendre le rôle de cet outil. Il s'agit d'un outil très pratique pour afficher l'espacement entre les éléments de la page. Vous voyez, vous venez d'effectuer quelques opérations simples du navigateur, et vous ne comprenez peut-être même pas le contenu du code ci-dessus, mais vous venez d'intégrer un morceau de code js dans la page sur laquelle vous vous trouvez (c'est évidemment inoffensif, n'hésitez pas à utilisez-le) Grâce à la vidéo du cours minimum up master CodingStartup [Avec elle, faites ressembler la page Web au dessin de conception] et à la réponse de up master ArcRain sous la vidéo

Le but de cette note d'étude est d'enregistrer mon propre apprentissage js. des idées et des expériences au cours du voyage, ainsi que quelques conseils qui, je pense, ne sont pas à des fins pédagogiques, je ne donnerai donc pas de réponses aux principes de certains contenus. Il se peut que je ne puisse pas le décrire avec précision, c'est peut-être moi. Je ne l'ai pas encore compris et mon niveau est assez limité. S'il y a des erreurs dans le texte, vous pouvez critiquer.

1. L'opportunité d'apprendre JavaScript

L'apprentissage formel de JavaScript s'est fait dans un cours de formation Oui, je suis sorti d'un cours de formation, pas d'une spécialisation. Lorsque j'étudiais, le standard ES6 n'était pas encore devenu populaire et le très traditionnel var était encore utilisé pour nommer les variables. Le premier morceau de code que j'ai appris était le classique console.log('Hello, world!'), which of. les impressions de cours sur la console sont sorties.

Bien sûr, le contenu JavaScript dans les établissements de formation est très simple, uniquement la définition et la dénomination des variables les plus élémentaires, la déclaration de fonction, la fonction de rappel, ajax et les opérations dom les plus élémentaires. Ces contenus sont évidemment totalement insuffisants pour le travail.

L'opportunité de poursuivre mes études en js est venue de mon travail. C'est au travail que j'ai découvert Node pour la première fois, et j'ai également appris que même js peut être utilisé comme backend (je suivais une formation JAVA), et j'ai a commencé à entrer progressivement en contact avec certaines normes ES6. Bien sûr, tout cela sera pour plus tard. Le plus gros obstacle que j’ai rencontré au début était ce produit.

2. Fermeture « dégoûtante »

Ah, je n'en ai qu'une petite connaissance, et je ne comprends pas du tout le code jsonp encapsulé par notre entreprise. Cela ressemble à ceci.

  var jsonp = (function(){
        var JSONP;
       return function(url){
           if (JSONP) {
             document.getElementsByTagName("head")[0].removeChild(JSONP);
          }
         JSONP = document.createElement("script");
          JSONP.type = "text/javascript";
          JSONP.src = url;
          document.getElementsByTagName("head")[0].appendChild(JSONP);
       }
     }())
Copier après la connexion

Bien sûr, cette méthode ne peut plus être utilisée directement via la console du navigateur. Afin de prévenir les attaques XSS, le navigateur a interdit une telle injection de code, mais elle peut toujours être utilisée sur le serveur. ce n’est pas là l’objet de l’attention.

Le point clé est ici

    if (JSONP) {
       //dosome
 }
Copier après la connexion

Si vous êtes comme moi et ne savez pas ce qu'est la fermeture ou si vous n'avez qu'une compréhension limitée de la fermeture, alors vous devriez également avoir des questions à ce sujet. L'idée est à peu près la suivante

Le. La deuxième ligne est définie. JSONP n'a aucune valeur attribuée. Maintenant, la valeur JSONP est nulle. La quatrième ligne détecte si la valeur JSONP est vide. Si elle n'est pas vide, elle fait quelque chose. Je n’ai pas besoin de le lire plus tard. Si c’est une perte de temps, il est impossible à 100 % d’y accéder !

Ecoute, il n'y a pas d'affectation avant, et puis c'est jugé directement, alors c'est évidemment nul. Mais lorsque vous l'utilisez réellement, vous constaterez que le premier appel à cet endroit n'entrera effectivement pas dans cette succursale, mais tant que vous l'appelez la deuxième fois, il entrera à 100 % dans cette succursale.

// 这个是一个可以在控制台输出的闭包版本,你可以自己试一下
var closedhull = (function() {
    let name = null; // 这里直接赋值为null
    return function(msg){
        if(name) {
            console.log('name:', name)
            return name += msg;
        }
        return name = msg;
    }
}())
closedhull('我是第一句。') //我是第一句。
closedhull('我是第二句。') //我是第一句。我是第二句。
Copier après la connexion

Après avoir exécuté l'exemple ci-dessus, il n'est pas difficile de voir à partir de console.log() ou de la valeur de retour qu'il est bien entré dans la branche de if(name). Voici la définition de la fermeture

Une fermeture est une fonction qui peut lire les variables internes d'autres fonctions.

3. À quoi ressemble une fermeture

D'accord, j'ai vu ce qu'est une fermeture. Ne disons pas si elle peut être utilisée. Au moins, je l'ai vue. {}

Non !

Sa particularité, c'est la fonction dans la fonction !

Observez les méthodes suivantes

/*第一个案例*/
function test1(){
    // a应该在方法运行结束后销毁
    let a = 1;
    return {
        add: function(){
            return ++a;
        }
    }
}
let a = test1();
a.add()//2
a.add()//3
/*第二个案例*/
(function(){
    // b应该在方法运行结束后销毁
    let b = 1,
        timer = setInterval(()=>{
        console.log(++b)
    }, 2000)
    setTimeout(()=>{
        clearInterval(timer)
    }, 10000)
})()// 2 3 4 5 6
/*第三个案例*/
function showMaker(obj){
    // obj应该在方法运行结束后销毁
    return function(){
        console.log(JSON.stringify(obj))
    }
}
let shower = showMaker({a:1})
// 显然这里你还能看到他
shower(); // {"a":1}
/*第四个案例*/
let outObj = (function(){
    let c = 'hello',
        obj = {};
    Object.defineProperty(obj, 'out', {
        get(){
            return c;
        },
        set(v){
            c = v;
        }
    });
    return obj
})()
outObj.out // 可以读取并设置c的值
Copier après la connexion

Ces quatre sont des fermetures, et elles ont toutes les caractéristiques de méthodes dans les méthodes.

4. Fermetures et piles de méthodes (vous pouvez l'ignorer si le principe ne vous intéresse pas)

Définition de la fermeture, 1. La variable est accessible en dehors de la portée de la variable. 2. Prolongez le cycle de vie d'une variable locale par certains moyens. 3. Laissez le temps de survie d'une variable locale dépasser son temps d'exécution de boucle temporelle

Puisque la notion de boucle d'événement est impliquée dans 3, nous en reparlerons plus tard. Nous aborderons ici principalement les définitions des deux premières méthodes.

Si vous savez ce qu'est une pile de méthodes, vous pouvez l'ignorer

局部作用域:在ES6之前,一般指一个方法内部(从参数列表开始,到方法体的括号结束为止),ES6中增加let关键字后,在使用let的情况下是指在一个{}中的范围内(显然,你不能在隐式的{}中使用let,编译器会禁止你做出这种行为的,因为没有{}就没有块级作用域),咱们这里为了简化讨论内容,暂且不把let的块级作用域算作闭包的范畴(其实应该算,不过意义不大,毕竟,你可以在外层块声明它。天啊,JS的命名还没拥挤到需要在一个方法内再去防止污染的程度。)

局部变量:区别于全局变量,全局变量会在某些时候被意外额创造和使用,这令人非常的...恼火和无助。局部变量就是在局部作用域下使用变量声明关键字声明出来的变量,应该很好理解。

局部变量的生命周期:好了,你在一个局部作用域中通过关键字(var const let等)声明了一个变量,然后给它赋值,这个局部变量在这个局部作用域中冒险就开始了,它会被使用,被重新赋值(除了傲娇的const小姐外),被调用(如果它是个方法),这个局部变量的本质是一个真实的值,区别在于如果它是个对象(对象,数组,方法都是对象)那么,它其实本质是一个地址的指针。如果它一个基础类型,那么它就是那个真实的值。它之所以存活是因为它有个住所。内存。

局部作用域与内存:每当出现一个局部作用域,一个方法栈就被申请了出来,在这个方法栈大概长这样子

|  data5 |
|  data4 |
|  data3 |
|  data2 |
|__data1_|
Copier après la connexion

当然,它是能够套娃的,长这个样子

|  | d2 |  |
|  |_d1_|  |
|  data3   |
|  data2   |
|__data1___|
Copier après la connexion

如果上面的东西是在太过于抽象,那么,我可以用实际案例展示一下

function stack1(){
    var data1,
        data2,
        data3,
        data4,
        data5
}
function stack2(){
    var data1,
        data2,
        data3;
    function stackInner(){
        var d1,
            d2;
    }
}
Copier après la connexion

如果方法栈能够直观的感受的话,大约就是这个样子,咱们重点来分析stack2的这种情况,同时写一点实际内容进去

function stack2(){
    var data1 = '1',
        data2 = {x: '2'},
        data3 = '3';
    function stackInner(){
        var d1 = '4',
            d2 = {y: '5'};
    }
    stackInner()
}
stack2()
Copier après la connexion

显然其中data1,data3,d1持有的是基本类型(string),data2,d2持有的是引用类型(object),反应到图上

运行时的方法栈的样子

            |------>{y: '5'}
            |    |->{x: '2'}
    |  | d2-|   || |
    |  |_d1='4'_|| |
    |  data3='3' | |
    |  data2 ----| |
    |__data1='1'___|
Copier après la connexion

画有点抽象...就这样吧。具体对象在哪呢?他们在一个叫堆的地方,不是这次的重点,还是先看方法栈内的这些变量,运行结束后,按照先进后出的原则,把栈内的局部变量一个一个的销毁,同时堆里的两个对象,由于引用被销毁,没了继续存在的意义,等待被垃圾回收。

接下来咱们要做两件事情:

  • d1不再等于4了,而是引用data1

  • return stackInner 而不是直接调用

这样闭包就完成了

function stack2(){
    var data1 = {msg: 'hello'},
        data2 = {x: '2'},
        data3 = '3';
    function stackInner(){
        var d1 = data1,
            d2 = {y: '5'};
    }
    return stackInner
}
var out = stack2()
Copier après la connexion

这里有一个要点,d2赋值给data1一定是在stackInner中完成的,原因?因为再stackInner方法中d2才被声明出来,如果你在stack2中d1 = data1那么恭喜你,你隐式的声明了一个叫d1的全局变量,而且在stackInner由于变量屏蔽的原因,你也看不到全局上的d2,原本计划的闭包完全泡汤。

变量屏蔽:不同作用域中相同名称的变量就会触发变量屏蔽。

看看栈现在的样子

运行时的方法栈的样子

               |------>{y: '5'}
out<---|       | |----|
    |  |  | d2-| | |  |  |
    |  |--|_d1---|_|  |  |
    |     data3=&#39;3&#39;   |  |
    |     data2(略)   |  |
    |_____data1<------|__|
Copier après la connexion

好了,这个图可以和我们永别了,如果有可能,我后面会用画图工具替代,这么画图实在是太过邪典了。

这里涉及到了方法栈的一个特性,就是变量的穿透性,外部变量可以在内部的任意位置使用,因为再内部执行结束前,外部变量会一直存在。

由于stackInner被外部的out引用,导致这个对象不会随着方法栈的结束而销毁,接下来,最神奇的事情来了,由于stackInner这对象没有销毁,它内部d1依然保有data1所对应数据的引用,d1,d2一定会活下来,因为他们的爸爸stackInner活下来了,data1也以某种形式活了下来。

为什么说是某种形式,因为,本质上来说data1还是被销毁了。没错,只不过,data1所引用的那个对象的地址链接没有被销毁,这个才是本质。栈在调用结束后一定是会销毁的。但是调用本体(方法对象)只要存在,那么内部所引用的链接就不会断。

这个就是闭包的成因和本质。

5.闭包有什么用

OK,我猜测上一个章节估计很多人都直接跳过了,其实,跳过影响也不多,这个部分描述一下结论性的东西,闭包的作用。

它的最大作用就是给你的变量一个命名空间,防止命名冲突。要知道,你的框架,你export的东西,你import进来的东西,在编译的时候都会变成闭包,为的就是减少你变量对全局变量的污染,一个不依赖与import export的模块的代码大概长这个样子

(function(Constr, global){
    let xxx = new Constr(env1, env2, env3)
    global.NameSpace = xxx;
})(function(parm1, parm2, parm3) {
    //dosomeing
    reutrn {
        a: &#39;some1&#39;,
        b: &#39;some2&#39;,
        funcC(){
            //dosome
        },
        funcD(){
            //dosome
        }
    }
}, window)
Copier après la connexion

当然这种封装代码的风格有多种多样的,但是大家都尽量把一套体系的内容都放到一个命名空间下,避免与其他框架产生冲突

相关推荐: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!

Étiquettes associées:
source:juejin.im
Déclaration de ce site Web
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
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal