日常我們在工作時(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ù)據(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è)。
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è)問題:
對于第一個(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í)行上下文的類型,主要有兩類:
調(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)需要注意:
當(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還添加了:
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() }
如下步驟:
下面調(diào)用堆棧的執(zhí)行示意圖:
創(chuàng)建全局上下文,全局變量和函數(shù)。
來源:https://mp.weixin.qq.com/s/4JmQTcvXBew8Eiz8hkYsWQ
作者:前端小智
*請認(rèn)真填寫需求信息,我們會(huì)在24小時(shí)內(nèi)與您取得聯(lián)系。