個(gè)人筆記,如有錯(cuò)誤煩請(qǐng)指正
以下面代碼的運(yùn)行舉例,一行行進(jìn)行運(yùn)行的解析
var x = [12, 23];
function fn(y) {
y[0] = 100;
y = [100];
y[1] = 200;
console.log(y);
}
fn(x);
console.log(x);
var x = [12, 23];運(yùn)行如下
- 開辟堆內(nèi)存,創(chuàng)建數(shù)組值,假設(shè)堆內(nèi)存的地址為0x000000
- 聲明變量
x
- 賦值,即將
x指向堆內(nèi)存的地址0x000000

接著
function fn(y) {
y[0] = 100;
y = [100];
y[1] = 200;
console.log(y);
}
運(yùn)行如下
上面這段代碼是創(chuàng)建一個(gè)函數(shù)的過(guò)程。和創(chuàng)建一個(gè)變量類似:
- 都是聲明一個(gè)變量存儲(chǔ)值
- 步驟一樣:第一步也是先創(chuàng)建一個(gè)堆內(nèi)存,里面存的是函數(shù),這個(gè)堆內(nèi)存有一個(gè)地址,然后把地址賦值給變量
- 聲明:方式類似函數(shù)名也算變量,當(dāng)我們聲明函數(shù)
function fn(y){...}時(shí),相當(dāng)于我們聲明了一個(gè)變量,只不過(guò)值是函數(shù)。類似于var fn = function (y){...}的函數(shù)表達(dá)式。最終把一個(gè)函數(shù)作為值賦值給一個(gè)變量或者其他
所以創(chuàng)建一個(gè)函數(shù),詳細(xì)的執(zhí)行順序如下
- 首先開辟一個(gè)堆內(nèi)存,存儲(chǔ)函數(shù)的值(假設(shè)地址為0x000001)
- 對(duì)象的值在堆內(nèi)存當(dāng)中,存儲(chǔ)的是它的鍵值對(duì)
- 函數(shù)的值在堆內(nèi)存當(dāng)中,存儲(chǔ)的是它的代碼,而且是以字符串的形式存儲(chǔ)的
- 創(chuàng)建函數(shù)的時(shí)候,就聲明了它的作用域(scope),scope值是當(dāng)前創(chuàng)建函數(shù)的時(shí)候所處的上下文,即在哪個(gè)上下文中創(chuàng)建的,作用域就是誰(shuí)

- 接著聲明變量
fn,并且指向堆內(nèi)存地址(假設(shè)為0x000001)

函數(shù)執(zhí)行的步驟
fn(x);運(yùn)行如下(函數(shù)執(zhí)行的步驟)
- 函數(shù)執(zhí)行時(shí),永遠(yuǎn)傳的是值,
fn(x)傳的是x的值,即x指向的0x000000堆內(nèi)存地址
fn(0x000000)形成一個(gè)全新的私有上下文EC(fn)
- 在函數(shù)形成的新的上下文中,生成一個(gè)私有化變量對(duì)象AO,用來(lái)存儲(chǔ)當(dāng)前上下文中聲明的變量(Active Object活動(dòng)對(duì)象,簡(jiǎn)稱AO,變量對(duì)象的一種,類似全局上下文中的全局變量)

- 內(nèi)部代碼執(zhí)行之前發(fā)生的事
- 初始化作用域鏈scope-chain <EC(fn1),EC(G)>,鏈的兩頭是 <當(dāng)前自己的私有上下文,函數(shù)的作用域(創(chuàng)建函數(shù)的時(shí)候所在的上下文)>,鏈的右側(cè)也叫當(dāng)前上下文的'上級(jí)上下文'
- 初始化this
- 初始化argument
- 在當(dāng)前上下文中,聲明一個(gè)形參變量,并且把傳遞的實(shí)參值賦值給它
- 變量提升

- 進(jìn)棧執(zhí)行代碼
- 出棧釋放
函數(shù)進(jìn)棧執(zhí)行代碼的詳細(xì)步驟
接著說(shuō)說(shuō)上面第5步的詳細(xì)步驟
把之前創(chuàng)建的函數(shù),在堆內(nèi)存中存儲(chǔ)的代碼字符串拿出來(lái)轉(zhuǎn)換為代碼一行一行的執(zhí)行執(zhí)行。
私有上下文中代碼執(zhí)行中如果遇到一個(gè)變量,首先看是否為自己的'私有變量',如果是'私有'的,則操作自己的,和外界沒(méi)有必然的關(guān)系,如果不是自己私有的,則基于作用域鏈,向其上級(jí)上下文中查找,看是否為上級(jí)上下文中私有的,如果也不是,繼續(xù)向上查找......一直找到EC(G)全局上下文為止,我們把這種查找過(guò)程稱之為 作用域鏈查找機(jī)制
所以
y[0] = 100
y = [100]
y[1] = 200
是這樣的執(zhí)行的:
-
y[0] = 100,y現(xiàn)在存儲(chǔ)的內(nèi)存地址為0x000000,所以修改這個(gè)地址下的值:

-
y = [100],出現(xiàn)了新的對(duì)象值,所以要開辟新的堆內(nèi)存0x000002,創(chuàng)建值,賦值

-
y[1] = 200,將0x000002地址對(duì)應(yīng)的對(duì)象的值進(jìn)行修改

-
console.log(y);這個(gè)y就是0x000002對(duì)應(yīng)的值[100,200]操作的是私有變量y

fn函數(shù)至此執(zhí)行完畢。
接著執(zhí)行外面的console.log(x);,此時(shí)的x為全局變量對(duì)象中的x,對(duì)應(yīng)的地址為0x000000,所以直接進(jìn)行輸出[100,23]

如果fn執(zhí)行完之后,繼續(xù)執(zhí)行其他函數(shù),同樣會(huì)經(jīng)歷這樣的流程。形成新的上下文,進(jìn)棧執(zhí)行...如果函數(shù)非常多,會(huì)一直進(jìn)棧,占內(nèi)存會(huì)越來(lái)越大。所以為了優(yōu)化,瀏覽器會(huì)默認(rèn)做出很多回收機(jī)制
結(jié)果與總體流程
結(jié)果

總體流程圖

其他說(shuō)明點(diǎn)
js上下文分類
js上下文(哪一個(gè)區(qū)域下執(zhí)行)分類
- 全局上下文EC(G)
- 函數(shù)執(zhí)行形成的私有上下文
- 塊級(jí)私有上下文
什么是私有變量
私有變量是私有上下文聲明的變量,包含
- 形參
- 代碼執(zhí)行的時(shí)候聲明的變量
var / let / const / function ...
注意與全局變量區(qū)別,沒(méi)有直接關(guān)系,但是可能會(huì)存在一些間接關(guān)系,比如下面這段代碼下全局變量x的值是0x000000,通過(guò)函數(shù),將0x000000傳給了私有變量y,y也是0x000000
function fn(y) {
y[0] = 100;
y = [100];
y[1] = 200;
console.log(y);
}
fn(x)

|