Heim > Web-Frontend > js-Tutorial > Hauptteil

Was Sie über JavaScript-Abschlüsse wissen müssen

WBOY
Freigeben: 2022-01-21 17:58:51
nach vorne
1385 Leute haben es durchsucht

Dieser Artikel enthält Studiennotizen zu JavaScript-Abschlüssen, einschließlich Abschlüssen und Methodenstapeln sowie der Rolle von Abschlüssen. Ich hoffe, er wird Ihnen hilfreich sein.

Was Sie über JavaScript-Abschlüsse wissen müssen

Per Definition ist es eine Skriptsprache, und zwar eine Skriptsprache, die relativ einfach zu erlernen ist. Ohne viel Fachwissen können Sie in gewissem Umfang auch js-Code (Abkürzung für JavaScript) verwenden.

Wenn Sie bereits über einige Front-End-Kenntnisse verfügen, sollten Sie natürlich die Rolle dieses Tools verstehen. Dies ist ein sehr praktisches Tool zum Anzeigen des Abstands zwischen Seitenelementen. Sie sehen, Sie haben gerade einige einfache Browservorgänge ausgeführt und können den Inhalt des obigen Codes nicht einmal verstehen, aber Sie haben gerade einen Teil des JS-Codes in die Seite eingebettet, auf der Sie sich befinden (offensichtlich ist es harmlos). Bitte zögern Sie nicht um es zu verwenden) Vielen Dank an das Upmaster-Mindestkursvideo von CodingStartup [Damit lässt die Webseite wie die Entwurfszeichnung aussehen] und an die Antwort von Upmaster ArcRain unter dem Video

Der Zweck dieser Studiennotiz besteht darin, mein eigenes JS-Lernen aufzuzeichnen Einige Erkenntnisse und Erfahrungen während der Reise sowie einige Tipps dienen meiner Meinung nach nicht dem Unterricht, daher kann es sein, dass ich sie nicht genau beschreiben kann dass ich es noch nicht verstanden habe und mein Niveau recht begrenzt ist. Wenn der Text Fehler enthält, können Sie ihn gerne kritisieren.

1. Die Gelegenheit, JavaScript zu lernen

Das formale Erlernen von JavaScript erfolgte in einem Schulungskurs, nicht in einem Hauptfach. Man kann sagen, dass ich sehr bodenständig bin. Als ich studierte, war der ES6-Standard noch nicht populär und die sehr traditionelle Variable wurde noch für die Benennung von Variablen verwendet. Der erste Code, den ich lernte, war der Klassiker console.log('Hello, world!'). Kursausdrücke auf der Konsole kamen heraus.

Natürlich ist der JavaScript-Inhalt in Schulungseinrichtungen sehr einfach, nur die grundlegendste Variablendefinition und -benennung, Funktionsdeklaration, Rückruffunktion, Ajax und die grundlegendsten Dom-Operationen. Offensichtlich sind diese Inhalte für die Arbeit völlig unzureichend.

Die Gelegenheit, mich weiter mit js zu beschäftigen, ergab sich aus meiner Arbeit. Bei der Arbeit lernte ich zum ersten Mal etwas über Node und lernte auch, dass sogar js als Backend verwendet werden kann (ich habe eine JAVA-Schulung gemacht), und ich begann nach und nach mit einigen ES6-Standards in Kontakt zu kommen. Natürlich sind das alles Dinge für später. Das größte Hindernis, auf das ich am Anfang gestoßen bin, war dieses Produkt.

2. „Ekelhafter“ Abschluss

Ah, ich weiß nur wenig darüber und kann den von unserem Unternehmen gekapselten JSONP-Code überhaupt nicht verstehen.

  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);
       }
     }())
Nach dem Login kopieren

Natürlich kann diese Methode nicht mehr direkt über die Konsole im Browser verwendet werden. Um XSS-Angriffe zu verhindern, hat der Browser eine solche Codeeinschleusung verboten, sie kann jedoch weiterhin auf dem Server verwendet werden. diese stehen nicht im Mittelpunkt.

Der entscheidende Punkt ist hier

    if (JSONP) {
       //dosome
 }
Nach dem Login kopieren

Wenn Sie wie ich nicht wissen, was Schließung ist, oder nur ein begrenztes Verständnis für Schließung haben, dann sollten Sie auch Fragen dazu haben In der zweiten Zeile ist kein JSONP-Wert zugewiesen. Die vierte Zeile erkennt, ob der JSONP-Wert leer ist. Ich muss es nicht später lesen. Wenn es Zeitverschwendung ist, ist es zu 100 % unmöglich, hineinzukommen!

Sehen Sie, es gibt vorher keine Aufgabe, und dann wird es direkt beurteilt, dann ist es offensichtlich null. Wenn Sie es jedoch tatsächlich verwenden, werden Sie feststellen, dass der erste Anruf an diesem Ort tatsächlich nicht in diesen Zweig gelangt, aber solange Sie ihn das zweite Mal anrufen, wird er zu 100 % in diesen Zweig gelangen.

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

Nachdem das obige Beispiel ausgeführt wurde, ist es nicht schwer, anhand von console.log() oder dem Rückgabewert zu erkennen, dass es tatsächlich in den Zweig von if(name) eingetreten ist. Dies ist die Leistung des Abschlusses. Hier ist die Definition des Abschlusses

Ein Abschluss ist eine Funktion, die die internen Variablen anderer Funktionen lesen kann.

3. Wie sieht ein Verschluss aus? Nun, da Sie gesehen haben, was ein Verschluss ist, wollen wir nicht darüber reden, ob er verwendet werden kann. Zumindest haben Sie gesehen, dass er eine Besonderheit hat: die Rückgabefunktion. (){}

Nein!

Sein Unterscheidungsmerkmal ist die Funktion innerhalb der Funktion!

Beachten Sie die folgenden Methoden

/*第一个案例*/
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的值
Nach dem Login kopieren

Diese vier sind Abschlüsse, und sie alle haben die Eigenschaften von Methoden innerhalb von Methoden.

4. Abschlüsse und Methodenstapel (Sie können es überspringen, wenn Sie sich nicht für das Prinzip interessieren)

Definition des Abschlusses, 1. Auf die Variable kann außerhalb des Gültigkeitsbereichs der Variablen zugegriffen werden. 2. Verlängern Sie den Lebenszyklus einer lokalen Variablen auf irgendeine Weise. 3. Lassen Sie die Überlebenszeit einer lokalen Variablen die Ausführungszeit ihrer Zeitschleife überschreiten

Da das Konzept der Ereignisschleife in 3 enthalten ist, werden wir später darüber sprechen. Hier diskutieren wir hauptsächlich die Definitionen der ersten beiden Methoden.

Wenn Sie wissen, was ein Methodenstapel ist, können Sie ihn überspringen

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

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

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

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

|  data5 |
|  data4 |
|  data3 |
|  data2 |
|__data1_|
Nach dem Login kopieren

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

|  | d2 |  |
|  |_d1_|  |
|  data3   |
|  data2   |
|__data1___|
Nach dem Login kopieren

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

function stack1(){
    var data1,
        data2,
        data3,
        data4,
        data5
}
function stack2(){
    var data1,
        data2,
        data3;
    function stackInner(){
        var d1,
            d2;
    }
}
Nach dem Login kopieren

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

function stack2(){
    var data1 = '1',
        data2 = {x: '2'},
        data3 = '3';
    function stackInner(){
        var d1 = '4',
            d2 = {y: '5'};
    }
    stackInner()
}
stack2()
Nach dem Login kopieren

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

运行时的方法栈的样子

            |------>{y: '5'}
            |    |->{x: '2'}
    |  | d2-|   || |
    |  |_d1='4'_|| |
    |  data3='3' | |
    |  data2 ----| |
    |__data1='1'___|
Nach dem Login kopieren

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

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

  • 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()
Nach dem Login kopieren

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

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

看看栈现在的样子

运行时的方法栈的样子

               |------>{y: '5'}
out<---|       | |----|
    |  |  | d2-| | |  |  |
    |  |--|_d1---|_|  |  |
    |     data3=&#39;3&#39;   |  |
    |     data2(略)   |  |
    |_____data1<------|__|
Nach dem Login kopieren

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

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

由于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)
Nach dem Login kopieren

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

相关推荐:javascript学习教程

Das obige ist der detaillierte Inhalt vonWas Sie über JavaScript-Abschlüsse wissen müssen. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Verwandte Etiketten:
Quelle:juejin.im
Erklärung dieser Website
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn
Beliebte Tutorials
Mehr>
Neueste Downloads
Mehr>
Web-Effekte
Quellcode der Website
Website-Materialien
Frontend-Vorlage
Über uns Haftungsausschluss Sitemap
Chinesische PHP-Website:Online-PHP-Schulung für das Gemeinwohl,Helfen Sie PHP-Lernenden, sich schnell weiterzuentwickeln!