整合營銷服務(wù)商

          電腦端+手機(jī)端+微信端=數(shù)據(jù)同步管理

          免費(fèi)咨詢熱線:

          Javascript前端加密解密

          日常我們在工作時(shí)做安全測試或者日常的漏洞挖掘中,往往會(huì)遇到請求加密,參數(shù)加密的情況,而且絕大部分都是前端加密的情況,那么我們就可以手動(dòng)調(diào)試跟蹤去解決這個(gè)加密問題,對我們自己要發(fā)送的數(shù)據(jù)進(jìn)行編碼來幫助我們進(jìn)行測試

          前端加密定位方法

          一般定位都會(huì)采用關(guān)鍵函數(shù)跟蹤的方式,流程如下:

          選擇需要定位的標(biāo)簽,一般都是提交、登錄、保存等按鈕,通過這些按鈕再去查找其中對應(yīng)的事件函數(shù),然后到調(diào)試器欄ctrl+shift+f全局搜索函數(shù)名定位,定位到關(guān)鍵函數(shù)后,對其打個(gè)斷點(diǎn),然后跟一跟基本就能確定走了什么加密流程。

          隨后就需要手工在控制臺(tái)對函數(shù)進(jìn)行調(diào)用對自己要測試的數(shù)據(jù)進(jìn)行編碼,當(dāng)然也可以把這些js文件保存下來,通過python腳本調(diào)用js函數(shù)。

          burp也有一個(gè)jsEncrypter插件,詳情可以去github主頁瞅瞅,是把js保存下來然后使用phantomjs調(diào)用加密函數(shù)對數(shù)據(jù)進(jìn)行加密。

          加密繞過實(shí)例

          登錄時(shí)抓包查看到數(shù)據(jù)包如下,可以看到passwd字段進(jìn)行了明顯的加密

          一般密碼加密會(huì)在登錄事件進(jìn)行加密處理,那么我們從這里開始跟蹤,選中登錄按鈕右鍵檢查元素定位。

          可以看有一個(gè)checkusernew()函數(shù),轉(zhuǎn)到調(diào)試器,直接ctrl+sgift+f全局搜索這個(gè)函數(shù)。

          然后轉(zhuǎn)到函數(shù)定義處,直接點(diǎn)擊右側(cè)加個(gè)斷點(diǎn)

          然后重新走登錄流程,自動(dòng)運(yùn)行到斷點(diǎn)處,然后F10 不過、F11 步入、shift+F11 步出。

          對關(guān)鍵函數(shù)如hex_md5和Encrypt需要跟進(jìn)去看下做了什么。

          跟進(jìn)encrypt函數(shù)

          我們需要去跟進(jìn)獲取加密的key和iv么,當(dāng)然是不需要的,我們只需要在控制臺(tái)調(diào)用執(zhí)行即可

          但是如何批量呢?有兩種方案,一個(gè)直接在控制臺(tái)寫js代碼或者拉取相應(yīng)JS,調(diào)用python的PyExecJS模塊。

          我更傾向于直接在控制臺(tái)調(diào)用

          let arr = ['a', 'b', 'c', 'd', 'e'] //定義一個(gè)列表
          var a = "" //定義一個(gè)空字符串
          for (let i in arr) {a = a + Encrypt(hex_md5(arr[i])) + "\n"} //循環(huán)跑
          最后輸出字符a即可

          其他情況

          在測試過程遇到的情況往往可能要復(fù)雜得多,跟了半天都沒有跟到關(guān)鍵函數(shù)也有可能發(fā)生。再加上web前端項(xiàng)目打包導(dǎo)致函數(shù)更加不好找。

          這個(gè)時(shí)候就要果斷放棄,打開下一個(gè)。

          小白從頭開始學(xué)所做的筆記)

          Web標(biāo)準(zhǔn)構(gòu)成

          主要包括:結(jié)構(gòu)、表現(xiàn)和行為三個(gè)方面

          <!DOCTYPE>文檔類型聲明,作用就是告訴瀏覽器使用哪種HTML版本來顯示網(wǎng)頁;

          lang用來定義當(dāng)前文檔顯示的語言;

          字符集(cahracter set)是多個(gè)字符的集合,以便計(jì)算機(jī)能夠識(shí)別和存儲(chǔ)各種文字;

          charset常用的值有:GB2312,BIG5,GBK和UTF-8,其中UTF-8也被稱為萬國碼,基本包含了全世界所有國家需要用到的字符集。

          HTML常用標(biāo)簽

          標(biāo)題標(biāo)簽:<h1></h1>,<h2></h2>,<h3></h3>,<h4></h4>,<h5></h5>,<h6></h6>(字體大小依次來變小)

          段落標(biāo)簽:<p></p>

          換行標(biāo)簽:<br/>

          加粗標(biāo)簽:<strong></strong>或<b></b>(推薦使用strong標(biāo)簽,因?yàn)閍trong效果更好)

          傾斜標(biāo)簽:<em></em>;<i></i>(推薦使用em標(biāo)簽,因?yàn)閑m效果更好)

          刪除線標(biāo)簽: <del></del>或<s></s>(推薦使用del標(biāo)簽,因?yàn)閐el效果更好)

          下劃線標(biāo)簽:<ins></ins>或<u></u>(推薦使用ins標(biāo)簽,因?yàn)閕ns效果更好)

          div和span標(biāo)簽:是一個(gè)盒子,用來裝內(nèi)容,div是塊級(jí)標(biāo)簽,span是行內(nèi)標(biāo)簽

          圖像標(biāo)簽:<img src="圖像 URL"/>

          水平線標(biāo)記:<hr/>

          圖像路徑

          相對路徑:以引用文件所在位置為參考基礎(chǔ),而建立出的目錄路徑

          絕對路徑:是指目錄下的絕對位置,直接到達(dá)目標(biāo)位置,通常是從盤符開始的路徑

          超鏈接標(biāo)簽:<a href="跳轉(zhuǎn)地址" target="目標(biāo)窗口的彈出方式"> </a>作用從一個(gè)頁面跳轉(zhuǎn)到另外一個(gè)頁面

          描點(diǎn)連接:配合id選擇器使用

          注釋:<!-- --!>

          /**/

          //

          特殊字符: (空格)等

          表格標(biāo)簽:<table></table> 作用于展示數(shù)據(jù)

          <th></th>----表頭單元格

          <tr></tr> ----行

          <td></td>----單元格,列

          row="行數(shù)" cols="每行的字?jǐn)?shù)"

          <thead></thead>標(biāo)簽用于表格的頭部區(qū)域;<tbody></tbody>標(biāo)簽用于表格的主題區(qū)域

          無序列表:<ul><li></li></ul>

          有序列表:<ol><li></li></ol>

          自定義列表:<dl><dt><dd></dd></dt></dl>-----------dt是用于標(biāo)題,dd是內(nèi)容

          表單的組成:

          在HTML中,表單通常由表單域、表單控件和表單信息

          from用于定義表單域,以實(shí)現(xiàn)用戶信息的收集和傳遞

          表單域標(biāo)簽:<from action="url地址" method="提交方式" name="表單域名稱"></form>

          <input type="屬性值"/>

          其中name屬性是表單的名字,相同的名字可以實(shí)現(xiàn)多選一

          <lable>標(biāo)簽:用于綁定一個(gè)表單元素,當(dāng)點(diǎn)擊《lable》標(biāo)簽內(nèi)的文本時(shí),瀏覽器就會(huì)自動(dòng)將焦點(diǎn)(光標(biāo))轉(zhuǎn)到或者選擇對應(yīng)的表單元素上,用來增加用戶體驗(yàn)

          <select>表單元素:下拉表單元素,

          <select>

          <option></option>

          </select>

          select中至少包含一對option

          textarea表單元素:<textarea></textarea>-----文本域標(biāo)簽

          做登錄界面可以用表格來做,能直接對齊

          后續(xù)會(huì)更新后面的知識(shí)

          自己是一名從事了多年開發(fā)的web前端老程序員,目前辭職在做自己的web前端私人定制課程,今年年初我花了一個(gè)月整理了一份最適合2019年學(xué)習(xí)的web前端學(xué)習(xí)干貨,各種框架都有整理,送給每一位前端小伙伴,想要獲取的可以關(guān)注我的頭條號(hào)并在后臺(tái)私信我:前端,即可免費(fèi)獲取。

          函數(shù)是什么

          函數(shù)是完成某個(gè)特定功能的一組語句。如沒有函數(shù),完成任務(wù)可能需要五行、十行、甚至更多的代碼。這時(shí)我們就可以把完成特定功能的代碼塊放到一個(gè)函數(shù)里,直接調(diào)用這個(gè)函數(shù),就省重復(fù)輸入大量代碼的麻煩。

          函數(shù)可以概括為:一次封裝,四處使用。

          函數(shù)的定義

          函數(shù)的定義方式通常有三種:函數(shù)聲明方式、函數(shù)表達(dá)式、 使用Function構(gòu)造函數(shù) 。

          函數(shù)聲明方式

          語法:

          function 函數(shù)名(參數(shù)1,參數(shù)2,...){ 
           //要執(zhí)行的語句 
          } 
          

          例:

          // 聲明
          function sum(num1, num2) {
           return num1 + num2;
          }
          // 調(diào)用
          sum(1, 2) // 3
          

          函數(shù)表達(dá)式

          語法:

          var fn = function(參數(shù)1,參數(shù)2,...){ 
           //要執(zhí)行的語句 
          };
          

          例:

          // 聲明
          var sum = function(num1,num2){ 
           return num1+num2; 
          };
          // 調(diào)用
          sum(1, 2) // 3
          

          使用Function構(gòu)造函數(shù)

          Function構(gòu)造函數(shù)可以接收任意數(shù)量的參數(shù),最后一個(gè)參數(shù)為函數(shù)體,其他的參數(shù)則枚舉出新函數(shù)的參數(shù)。其語法為:

          new Function("參數(shù)1","參數(shù)2",...,"參數(shù)n","函數(shù)體"); 
          


          例:

          // 聲明
          var sum = new Function("num1","num2","return num1+num2"); 
          // 調(diào)用
          sum(1, 2) // 3
          

          三種定義方式的區(qū)別

          三種方式的區(qū)別,可以從作用域、效率以及加載順序來區(qū)分。

          從作用域上來說,函數(shù)聲明式和函數(shù)表達(dá)式使用的是局部變量,而 Function()構(gòu)造函數(shù)卻是全局變量,如下所示:

          var name = '我是全局變量 name';
          // 聲明式
          function a () {
           var name = '我是函數(shù)a中的name';
           return name;
          }
          console.log(a()); // 打印: "我是函數(shù)a中的name"
          // 表達(dá)式
          var b = function() {
           var name = '我是函數(shù)b中的name';
           return name; // 打印: "我是函數(shù)b中的name"
          }
          console.log(b())
          // Function構(gòu)造函數(shù)
          function c() {
           var name = '我是函數(shù)c中的name';
           return new Function('return name')
          }
          console.log(c()()) // 打?。?我是全局變量 name",因?yàn)镕unction()返回的是全局變量 name,而不是函數(shù)體內(nèi)的局部變量。
          

          從執(zhí)行效率上來說,F(xiàn)unction()構(gòu)造函數(shù)的效率要低于其它兩種方式,尤其是在循環(huán)體中,因?yàn)闃?gòu)造函數(shù)每執(zhí)行一次都要重新編譯,并且生成新的函數(shù)對象。

          來個(gè)例子:

          var start = new Date().getTime()
          for(var i = 0; i < 10000000; i++) {
           var fn = new Function('a', 'b', 'return a + b')
           fn(i, i+1)
          }
          var end = new Date().getTime();
          console.log(`使用Function構(gòu)造函數(shù)方式所需要的時(shí)間為:${(end - start)/1000}s`)
          // 使用Function構(gòu)造函數(shù)方式所需要的時(shí)間為:8.646s
          start = new Date().getTime();
          var fn = function(a, b) {
           return a + b;
          }
          for(var i = 0; i < 10000000; i++) {
           fn(i, i+1)
          }
          end = new Date().getTime();
          console.log(`使用表達(dá)式的時(shí)間為:${(end - start)/1000}s`)
          // 使用表達(dá)式的時(shí)間為:0.012s
          

          由此可見,在循環(huán)體中,使用表達(dá)式的執(zhí)行效率比使用 Function()構(gòu)造函數(shù)快了很多很多。所以在 Web 開發(fā)中,為了加快網(wǎng)頁加載速度,提高用戶體驗(yàn),我們不建議選擇 Function ()構(gòu)造函數(shù)方式來定義函數(shù)。

          最后是加載順序,function 方式(即函數(shù)聲明式)是在 JavaScript 編譯的時(shí)候就加載到作用域中,而其他兩種方式則是在代碼執(zhí)行的時(shí)候加載,如果在定義之前調(diào)用它,則會(huì)返回 undefined:

          console.log(typeof f) // function
          console.log(typeof c) // undefined
          console.log(typeof d) // undefined
          function f () {
           return 'JS 深入淺出'
          }
          var c = function () {
           return 'JS 深入淺出'
          }
          console.log(typeof c) // function
          var d = new Function('return "JS 深入淺出"')
          console.log(typeof d) // function
          

          函數(shù)的參數(shù)和返回值

          函數(shù)的參數(shù)-arguments

          JavaScript 中的函數(shù)定義并未指定函數(shù)形參的類型,函數(shù)調(diào)用也未對傳入的實(shí)參值做任何類型檢查。實(shí)際上,JavaScript 函數(shù)調(diào)用甚至不檢查傳入形參的個(gè)數(shù)。

          function sum(a) {
           return a + 1;
          }
          console.log(sum(1)); // 2
          console.log(sum('1')); // 11
          console.log(add()); // NaN
          console.log(add(1, 2)); // 2
          

          當(dāng)實(shí)參比形參個(gè)數(shù)要多時(shí),剩下的實(shí)參沒有辦法直接獲得,需要使用即將提到的arguments對象。

          JavaScript中的參數(shù)在內(nèi)部用一個(gè)數(shù)組表示。函數(shù)接收到的始終都是這個(gè)數(shù)組,而不關(guān)心數(shù)組中包含哪些參數(shù)。在函數(shù)體內(nèi)可以通過arguments對象來訪問這個(gè)參數(shù)數(shù)組,從而獲取傳遞給函數(shù)的每一個(gè)參數(shù)。arguments對象并不是Array的實(shí)例,它是一個(gè)類數(shù)組對象,可以使用方括號(hào)語法訪問它的每一個(gè)元素。

          function sum (x) {
           console.log(arguments[0], arguments[1], arguments[2]); // 1 2 3
          }
          sum(1, 2, 3)
          

          arguments對象的length屬性顯示實(shí)參的個(gè)數(shù),函數(shù)的length屬性顯示形參的個(gè)數(shù)。

          function sum(x, y) {
           console.log(arguments.length); // 3
           return x + 1;
          }
          sum(1, 2, 3)
          console.log(sum.length) // 2
          

          函數(shù)的參數(shù)-arguments

          JavaScript 中的函數(shù)定義并未指定函數(shù)形參的類型,函數(shù)調(diào)用也未對傳入的實(shí)參值做任何類型檢查。實(shí)際上,JavaScript 函數(shù)調(diào)用甚至不檢查傳入形參的個(gè)數(shù)。

          function sum(a) {
           return a + 1;
          }
          console.log(sum(1)); // 2
          console.log(sum('1')); // 11
          console.log(add()); // NaN
          console.log(add(1, 2)); // 2
          

          函數(shù)的參數(shù)-同名參數(shù)

          在非嚴(yán)格模式下,函數(shù)中可以出現(xiàn)同名形參,且只能訪問最后出現(xiàn)的該名稱的形參。

          function sum(x, x, x) {
           return x;
          }
          console.log(sum(1, 2, 3)) // 3
          

          而在嚴(yán)格模式下,出現(xiàn)同名形參會(huì)拋出語法錯(cuò)誤。

          function sum(x, x, x) {
           'use strict';
           return x;
          }
          console.log(sum(1, 2, 3)) // SyntaxError: Duplicate parameter name not allowed in this context
          

          函數(shù)的參數(shù)-參數(shù)個(gè)數(shù)

          當(dāng)實(shí)參比函數(shù)聲明指定的形參個(gè)數(shù)要少,剩下的形參都將設(shè)置為undefined值。

          function sum(x, y) {
           console.log(x, y);
          }
          sum(1); // 1 undefined
          

          函數(shù)的返回值

          所有函數(shù)都有返回值,沒有return語句時(shí),默認(rèn)返回內(nèi)容為undefined。

          function sum1 (x, y) {
           var total = x + y
          } 
          console.log(sum1()) // undefined
          function sum2 (x, y) {
           return x + y
          }
          console.log(sum2(1, 2)) // 3
          

          如果函數(shù)調(diào)用時(shí)在前面加上了new前綴,且返回值不是一個(gè)對象,則返回this(該新對象)。

          function Book () {
           this.bookName = 'JS 深入淺出'
          }
          var book = new Book();
          console.log(book); // Book { bookName: 'JS 深入淺出' }
          console.log(book.constructor); // [Function: Book]
          

          如果返回值是一個(gè)對象,則返回該對象。

          function Book () {
           return {bookName: JS 深入淺出}
          }
          var book = new Book();
          console.log(book); // { bookName: 'JS 深入淺出' }
          console.log(book.constructor); // [Function: Book]
          

          函數(shù)的調(diào)用方式

          JS 一共有4種調(diào)用模式:函數(shù)調(diào)用、方法調(diào)用、構(gòu)造器調(diào)用和間接調(diào)用。

          函數(shù)調(diào)用

          當(dāng)一個(gè)函數(shù)并非一個(gè)對象的屬性時(shí),那么它就是被當(dāng)做一個(gè)函數(shù)來調(diào)用的。對于普通的函數(shù)調(diào)用來說,函數(shù)的返回值就是調(diào)用表達(dá)式的值

          function sum (x, y) {
           return x + y;
          }
          var total = sum(1, 2);
          console.log(total); // 3
          

          使用函數(shù)調(diào)用模式調(diào)用函數(shù)時(shí),非嚴(yán)格模式下,this被綁定到全局對象;在嚴(yán)格模式下,this是undefined

          // 非嚴(yán)格模式
          function whatIsThis1() {
           console.log(this);
          }
          whatIsThis1(); // window
          // 嚴(yán)格模式
          function whatIsThis2() {
           'use strict';
           console.log(this);
          }
          whatIsThis2(); // undefined
          

          方法調(diào)用

          當(dāng)一個(gè)函數(shù)被保存為對象的一個(gè)屬性時(shí),稱為方法,當(dāng)一個(gè)方法被調(diào)用時(shí),this被綁定到該對象。

          function printValue(){
           console.log(this.value); 
          }
          var value=1;
          var myObject = {value:2};
          myObject.m = printValue;
          //作為函數(shù)調(diào)用
          printValue();
          //作為方法調(diào)用
          myObject.m();
          

          咱們注意到,當(dāng)調(diào)用printValue時(shí),this綁定的是全局對象(window),打印全局變量value值1。但是當(dāng)調(diào)用myObject.m()時(shí),this綁定的是方法m所屬的對象Object,所以打印的值為Object.value,即2。

          構(gòu)造函數(shù)調(diào)用

          如果函數(shù)或者方法調(diào)用之前帶有關(guān)鍵字new,它就構(gòu)成構(gòu)造函數(shù)調(diào)用。

          function fn(){
           this.a = 1;
          };
          var obj = new fn();
          console.log(obj.a);//1
          

          參數(shù)處理:一般情況構(gòu)造器參數(shù)處理和函數(shù)調(diào)用模式一致。但如果構(gòu)造函數(shù)沒用形參,JavaScript構(gòu)造函數(shù)調(diào)用語法是允許省略實(shí)參列表和圓括號(hào)的。

          如:下面兩行代碼是等價(jià)的。

          var o = new Object();
          var o = new Object;
          

          函數(shù)的調(diào)用上下文為新創(chuàng)建的對象。

          function Book(bookName){
           this.bookName = bookName;
          }
          var bookName = 'JS 深入淺出';
          var book = new Book('ES6 深入淺出');
          console.log(bookName);// JS 深入淺出
          console.log(book.bookName);// ES6 深入淺出
          Book('新版JS 深入淺出');
          console.log(bookName); // 新版JS 深入淺出
          console.log(book.bookName);// ES6 深入淺出
          

          1.第一次調(diào)用Book()函數(shù)是作為構(gòu)造函數(shù)調(diào)用的,此時(shí)調(diào)用上下文this被綁定到新創(chuàng)建的對象,即 book。所以全局變量bookName值不變,而book新增一個(gè)屬性bookName,值為'ES6 深入淺出';

          2.第二次調(diào)用Book()函數(shù)是作為普通函數(shù)調(diào)用的,此時(shí)調(diào)用上下為this被綁定到全局對象,在瀏覽器中為window。所以全局對象的bookNam值改變?yōu)? 新版JS 深入淺出',而book的屬性值不變。

          間接調(diào)用

          JS 中函數(shù)也是對象,函數(shù)對象也可以包含方法,call()和apply()方法可以用來間接地調(diào)用函數(shù)。

          這兩個(gè)方法都允許顯式指定調(diào)用所需的this值,也就是說,任何函數(shù)可以作為任何對象的方法來調(diào)用,哪怕這個(gè)函數(shù)不是那個(gè)對象的方法。兩個(gè)方法都可以指定調(diào)用的實(shí)參。call()方法使用它自有的實(shí)參列表作為函數(shù)的實(shí)參,apply()方法則要求以數(shù)組的形式傳入?yún)?shù)。

          var obj = {};
          function sum(x,y){
           return x+y;
          }
          console.log(sum.call(obj,1,2));//3
          console.log(sum.apply(obj,[1,2]));//3
          

          詞法(靜態(tài))作用域與動(dòng)態(tài)作用域

          作用域

          通常來說,一段程序代碼中所用到的名字并不總是有效/可用的,而限定這個(gè)名字的可用性的代碼范圍就是這個(gè)名字的作用域。

          詞法作用域

          詞法作用域,也叫靜態(tài)作用域,它的作用域是指在詞法分析階段就確定了,不會(huì)改變。而與詞法作用域相對的是動(dòng)態(tài)作用域,函數(shù)的作用域是在函數(shù)調(diào)用的時(shí)候才決定的。

          來個(gè)例子,如下代碼所示:

          var blobal1 = 1;
          function fn1 (param1) {
           var local1 = 'local1';
           var local2 = 'local2';
           function fn2(param2) {
           var local2 = 'inner local2';
           console.log(local1)
           console.log(local2)
           }
           function fn3() {
           var local2 = 'fn3 local2';
           fn2(local2)
           }
           fn3()
          }
          fn1()
          

          當(dāng)瀏覽器看到這樣的代碼,不會(huì)馬上去執(zhí)行,它會(huì)先生成一個(gè)抽象語法樹。上述代碼生成的抽象語法樹大概是這樣的:


          執(zhí)行fn1函數(shù),fn1中調(diào)用 fn3(),從fn3函數(shù)內(nèi)部查找是否有局部變量 local1,如果沒有,就根據(jù)抽象樹,查找上面一層的代碼,也就是 local1 等于 'local1' ,所以結(jié)果會(huì)打印 'local1'。

          同樣的方法查找是否有局部變量 local2,發(fā)現(xiàn)當(dāng)前作用域內(nèi)有l(wèi)ocal2變量,所以結(jié)果會(huì)打印 'inner local2。

          思考

          有如下的代碼:

          var a = 1;
          function fn() {
           console.log(a)
          }
          

          兩個(gè)問題:

          1. 函數(shù) fn 里面的變量 a, 是不是外面的變量 a。
          2. 函數(shù) fn 里面的變量 a的值, 是不是外面的變量 a的值。

          對于第一個(gè)問題:

          分析一個(gè)語法,就能確定函數(shù) fn里面的 a 就是外面的 a。

          對于第二個(gè)問題:

          函數(shù) fn 里面的變量 a的值, 不一定是外面的變量 a的值,假設(shè)咱們這樣做:

          var a = 1;
          function fn() {
           console.log(a)
          }
          a = 2
          fn()
          

          這時(shí)候當(dāng)咱們執(zhí)行 fn() 的時(shí)候,打印 a 的值為 2。所以如果沒有看到最后,一開始咱們是不知道打印的 a 值到底是什么。

          所以詞法作用域只能確定變量所在位置,并不能確定變量的值。

          調(diào)用棧(Call Stack)

          什么是執(zhí)行上下文

          執(zhí)行上下文就是當(dāng)前JavaScript代碼被解析和執(zhí)行是所在環(huán)境的抽象概念,JavaScript中運(yùn)行任何的代碼都是在執(zhí)行上下文中運(yùn)行。

          執(zhí)行上下文的類型,主要有兩類:

          • 全局執(zhí)行上下文:這是默認(rèn)的,最基礎(chǔ)的執(zhí)行上下文。不在任何函數(shù)中的代碼都位于全局執(zhí)行上下文中。共有兩個(gè)過程:1.創(chuàng)建有全局對象,在瀏覽器中這個(gè)全局對象就是window對象。2.將this指針指向這個(gè)全局對象。一個(gè)程序中只能存在一個(gè)執(zhí)行上下文。
          • 函數(shù)執(zhí)行上下文:每次調(diào)用函數(shù)時(shí),都會(huì)為該函數(shù)創(chuàng)建一個(gè)新的執(zhí)行上下文。每個(gè)函數(shù)都擁有自己的執(zhí)行上下文,但是只有在函數(shù)被調(diào)用的時(shí)候才會(huì)被創(chuàng)建。一個(gè)程序中可以存在多個(gè)函數(shù)執(zhí)行上下文,這些函數(shù)執(zhí)行上下文按照特定的順序執(zhí)行一系列步驟,后文具體討論。

          調(diào)用棧

          調(diào)用棧,具有LIFO(Last in, First out 后進(jìn)先出)結(jié)構(gòu),用于存儲(chǔ)在代碼執(zhí)行期間創(chuàng)建的所有執(zhí)行上下文。

          當(dāng)JavaScript引擎首次讀取腳本時(shí),會(huì)創(chuàng)建一個(gè)全局執(zhí)行上下文并將其push到當(dāng)前執(zhí)行棧中。每當(dāng)發(fā)生函數(shù)調(diào)用時(shí),引擎都會(huì)為該函數(shù)創(chuàng)建一個(gè)新的執(zhí)行上下文并push到當(dāng)前執(zhí)行棧的棧頂。

          引擎會(huì)運(yùn)行執(zhí)行上下文在執(zhí)行棧棧頂?shù)暮瘮?shù),根據(jù)LIFO規(guī)則,當(dāng)此函數(shù)運(yùn)行完成后,其對應(yīng)的執(zhí)行上下文將會(huì)從執(zhí)行棧中pop出,上下文控制權(quán)將轉(zhuǎn)到當(dāng)前執(zhí)行棧的下一個(gè)執(zhí)行上下文。

          看看下面的代碼:

          var myOtherVar = 10;
          function a() {
           console.log('myVar', myVar);
           b();
          }
          function b() {
           console.log('myOtherVar', myOtherVar);
           c();
          }
          function c() {
           console.log('Hello world!');
          }
          a();
          var myVar = 5;
          

          有幾個(gè)點(diǎn)需要注意:

          • 變量聲明的位置(一個(gè)在上,一個(gè)在下)
          • 函數(shù)a調(diào)用下面定義的函數(shù)b, 函數(shù)b調(diào)用函數(shù)c

          當(dāng)它被執(zhí)行時(shí)你期望發(fā)生什么?是否發(fā)生錯(cuò)誤,因?yàn)閎在a之后聲明或者一切正常?console.log打印的變量又是怎么樣?

          以下是打印結(jié)果:

          "myVar" undefined
          "myOtherVar" 10
          "Hello world!"
          

          1. 變量和函數(shù)聲明(創(chuàng)建階段)

          第一步是在內(nèi)存中為所有變量和函數(shù)分配空間。但請注意,除了undefined之外,尚未為變量分配值。因此,myVar在被打印時(shí)的值是undefined,因?yàn)镴S引擎從頂部開始逐行執(zhí)行代碼。

          函數(shù)與變量不一樣,函數(shù)可以一次聲明和初始化,這意味著它們可以在任何地方被調(diào)用。

          所以以上代碼在創(chuàng)建階段時(shí),看起來像這樣子:

          var myOtherVar = undefined
          var myVar = undefined
          function a() {...}
          function b() {...}
          function c() {...}
          

          這些都存在于JS創(chuàng)建的全局上下文中,因?yàn)樗挥谌肿饔糜蛑小?/p>

          在全局上下文中,JS還添加了:

          • 全局對象(瀏覽器中是 window 對象,NodeJs 中是 global 對象)
          • this 指向全局對象

          2. 執(zhí)行

          接下來,JS 引擎會(huì)逐行執(zhí)行代碼。

          myOtherVar = 10在全局上下文中,myOtherVar被賦值為10

          已經(jīng)創(chuàng)建了所有函數(shù),下一步是執(zhí)行函數(shù) a()

          每次調(diào)用函數(shù)時(shí),都會(huì)為該函數(shù)創(chuàng)建一個(gè)新的上下文(重復(fù)步驟1),并將其放入調(diào)用堆棧。

          function a() {
           console.log('myVar', myVar)
           b()
          }
          

          如下步驟:

          • 創(chuàng)建新的函數(shù)上下文
          • a 函數(shù)里面沒有聲明變量和函數(shù)
          • 函數(shù)內(nèi)部創(chuàng)建了 this 并指向全局對象(window)
          • 接著引用了外部變量 myVar,myVar 屬于全局作用域的。
          • 接著調(diào)用函數(shù) b,函數(shù)b的過程跟a一樣,這里不做分析。

          下面調(diào)用堆棧的執(zhí)行示意圖:




          創(chuàng)建全局上下文,全局變量和函數(shù)。

          • 每個(gè)函數(shù)的調(diào)用,會(huì)創(chuàng)建一個(gè)上下文,外部環(huán)境的引用及 this。
          • 函數(shù)執(zhí)行結(jié)束后會(huì)從堆棧中彈出,并且它的執(zhí)行上下文被垃圾收集回收(閉包除外)。
          • 當(dāng)調(diào)用堆棧為空時(shí),它將從事件隊(duì)列中獲取事件。

          來源:https://mp.weixin.qq.com/s/4JmQTcvXBew8Eiz8hkYsWQ

          作者:前端小智


          主站蜘蛛池模板: 亚洲高清日韩精品第一区| 精品一区精品二区| 一区二区三区精密机械| 一区二区无码免费视频网站| 国产精品合集一区二区三区| 国产SUV精品一区二区四| 日本一区午夜爱爱| 国产在线aaa片一区二区99| 日韩免费无码视频一区二区三区 | 视频一区二区精品的福利| 国产福利电影一区二区三区,亚洲国模精品一区| 国产成人一区二区三区精品久久| 三上悠亚国产精品一区| 亚洲天堂一区在线| 亚洲成a人一区二区三区| 人妻体内射精一区二区三区| 怡红院美国分院一区二区 | 日韩精品无码人妻一区二区三区| 久久一区二区三区免费| 亚洲一区二区三区免费视频| 精品综合一区二区三区| 亚洲AV无码一区二区三区系列| 国产精品久久久久久一区二区三区| 亚洲国产精品一区二区第一页免| 人妻激情偷乱视频一区二区三区| 国产一区二区三区在线观看影院 | 精品综合一区二区三区| 久久99热狠狠色精品一区| 无码av中文一区二区三区桃花岛| 久久一区二区免费播放| 亚洲AV无码一区二区三区鸳鸯影院| 国产裸体歌舞一区二区| 日本免费一区二区三区最新vr| 无码精品一区二区三区免费视频 | 亚洲国产精品无码第一区二区三区| 亚洲AV日韩精品一区二区三区| 欲色aV无码一区二区人妻| 夜夜添无码试看一区二区三区 | 免费观看日本污污ww网站一区| 亚洲一区二区三区夜色| 亚洲一区二区视频在线观看|