詭異例子:
在綏陽等地區(qū),都構(gòu)建了全面的區(qū)域性戰(zhàn)略布局,加強(qiáng)發(fā)展的系統(tǒng)性、市場前瞻性、產(chǎn)品創(chuàng)新能力,以專注、極致的服務(wù)理念,為客戶提供網(wǎng)站設(shè)計(jì)制作、成都做網(wǎng)站 網(wǎng)站設(shè)計(jì)制作定制網(wǎng)站開發(fā),公司網(wǎng)站建設(shè),企業(yè)網(wǎng)站建設(shè),成都品牌網(wǎng)站建設(shè),成都全網(wǎng)營銷推廣,成都外貿(mào)網(wǎng)站建設(shè)公司,綏陽網(wǎng)站建設(shè)費(fèi)用合理。
function DelayExe() {
var a = 10;
setTimeout( function Print() { console.log(a);}, 1000 );
}
以C++的觀點(diǎn)來看:一旦函數(shù)DelayExe()執(zhí)行完畢,變量 a 就不復(fù)存在,函數(shù) Print() 根本引用不到變量a,必然拋出異常。
但是,在 js 中,在 DelayExe() 執(zhí)行完畢 1000ms 后執(zhí)行 Print() 函數(shù),Print() 函數(shù)居然能正確引用 a。是不是很詭異?
概念和原理:
舉個(gè)例子:
一個(gè)64位整型變量,在 C++ 程序中,只占8字節(jié)。
一個(gè)64位整型變量,在 js 中,占有以下空間:
M1:8字節(jié),表達(dá)數(shù)值
M2:引用計(jì)數(shù)器,若是 5,表示有 5 個(gè)函數(shù)引用該變量。
每當(dāng)一個(gè)函數(shù)引用該變量時(shí),js 引擎就遞增變量的引用計(jì)數(shù)器
每當(dāng)一個(gè)函數(shù)用完該變量后,js 引擎就遞減變量的引用計(jì)數(shù)器。若變量的引用計(jì)數(shù)器遞減為 0,說明已沒有函數(shù)用到該變量,該變量才會被刪除。
由此可見:
C1:js 中的變量,不僅包含數(shù)值,還包含引用計(jì)數(shù)器。
C2:變量是否存在,取決于其引用計(jì)數(shù)器的值。
C3:js 引擎根據(jù)引用計(jì)數(shù)器的值決定是否刪除變量,是否從內(nèi)存中清除變量。
實(shí)際用例:
function DelayExe() {
var a = 10;
setTimeout( function Print() { console.log(a);}, 1000 );
}
以C++的觀點(diǎn)來看:
變量 a 是函數(shù)DelayExe() 的局部變量,函數(shù) DelayExe() 執(zhí)行完畢,變量 a 就被刪除,不再占用存儲空間。
但是,js 的情況截然不同:
S1:執(zhí)行函數(shù) DelayExe()
S2:執(zhí)行到語句 setTimeout() 時(shí),做如下處理:
S2.1:在內(nèi)存中分配一塊空間,存儲函數(shù) Print(),
S2.2:由于函數(shù) Print() 引用了變量 a,遞增變量 a 的引用計(jì)數(shù)器
S2.3:安排函數(shù) Print() 在 1000ms 后被放入任務(wù)隊(duì)列調(diào)度執(zhí)行
S3:函數(shù) DelayExe() 執(zhí)行完畢。由于此時(shí)變量 a 的引用計(jì)數(shù)器 > 0,所以 js 引擎不將其不刪除,變量 a 依然存在。
S4:1000ms 后執(zhí)行函數(shù) Print(),執(zhí)行完畢后,函數(shù) Print() 不再引用變量 a,js 引擎遞減變量 a 的引用計(jì)數(shù)器。此時(shí),變量 a 的引用計(jì)數(shù)器遞減為 0,不再被任何函數(shù)引用,于是 js 引擎才將其從內(nèi)存中刪除。
結(jié)論:
C1:js 變量的存在性,與變量的定義位置無關(guān)。
C2:js 變量是否存在,是否占用存儲空間,取決于有沒有被函數(shù)引用。
C2.1:若沒有被任何函數(shù)引用,變量就被刪除。
C2.2:若至少被一個(gè)函數(shù)引用,變量就存在。
C3:若一個(gè)變量被一個(gè)函數(shù)引用,但這個(gè)函數(shù)一直未被調(diào)用執(zhí)行,那么這個(gè)變量就一直存在。