目錄0 / 題(1)第一題(2)第二題1 / 引用數據類型:object2 / 引用數據類型:function(1)第二題,簡圖(2)創建函數(3)執行函數(4)閉包3 / 練習題(1)第一題(2)第二題(3)第三題
0 / 題
(1)第一題
var a = {n: 1}; var b = a; a.x = a = {n: 2}; console.log(a.x); console.log(b);
△ 引用數據類型:object
(2)第二題
var x = [12, 23]; function fn(y) { y[0] = 100; y = [100]; y[1] = 200; console.log(y); } fn(x); console.log(x);
△ 引用數據類型:function
這些題是不是很簡單?我們主要看邏輯:
1 / 引用數據類型:object
在Web瀏覽器中執行JS代碼,會開辟一塊棧內存來作為執行環境:ECStack
(Execution Context Stack)
會開辟一塊棧內存供全局代碼執行:全局執行上下文 EC(G)
(Execution Context Global),還有其他的上下文:函數私有執行上下文、塊級私有上下文…… 自己管好自己那一攤的代碼執行內容
形成的執行上下文都會 進棧 到執行環境棧中運行.私有上下文會在不被占用時出棧釋放,瀏覽器的回收機制GC
.當瀏覽器關閉時,全局執行上下文就會出棧釋放了
△ 圖2.1_第一題,簡圖
GO:全局對象 Global Object ,并不是VO(G)全局變量對象 Variable Object Global
全局對象,它是個對象,它就是個堆內存,瀏覽器打開一加載頁面就默認開辟的堆內存。
瀏覽器提供的一些供JS調用的API,在Web瀏覽器中,全局對象可以通過window
來訪問的
?注意:運算符優先級,要多看看多比劃比劃
注意:基本數據類型值直接存儲在棧內存中,引用數據類型值存在堆內存中
2 / 引用數據類型:function
var x = [12, 23]; function fn(y) { y[0] = 100; y = [100]; y[1] = 200; console.log(y); } fn(x); console.log(x);
△ 函數執行
(1)第二題,簡圖
△ 圖2.2_函數執行
△ 圖2.3_數組的格式:鍵值對
(2)創建函數
創建函數的步驟:【和創建變量區別不是很大,函數名就是變量名】
① 單獨開辟一個堆內存:16進制地址,函數堆內存中存儲的是函數體中的代碼字符串
② 創建函數的時候,就聲明了它的作用域[[scope]],也就是所在的上下文環境
③ 把16進制地址(16進制以0x
開頭)存放到棧中,供函數名變量名關聯引用即可
只創建函數,不執行函數,沒啥意義,那就是一堆字符串。
函數執行的目的:把創建函數的時候在堆內存中存儲的 代碼字符串 變為代碼執行
代碼執行一定會有一個執行的環境,它的上級執行上下文,是函數創建的地方
函數執行會形成一個全新的、私有的執行上下文,在私有上下文中,也有存放自己變量的對象:AO(Active Object 活動對象),它是VO的一種。
變量對象: ① 在全局上下文中:VO ?② 在私有上下文中:AO
實參都是值。形參是變量。
fn(x)
:執行函數fn,把全局上下文中存儲的x
變量關聯的值(0x000001),作為實參傳遞給函數的形參變量
(3)執行函數
執行函數做了哪些事情:
1、形成了一個全新的、私有的執行上下文EC(xxx)
2、當前私有的上下文中,有一個存放此上下文內聲明的變量的地方 AO(xxx)
私有變量對象
① 形參變量
② 當前上下文中聲明的變量
3、進棧執行
4、代碼執行之前還要處理很多事情:
① 初始化作用域鏈
[[scope-chain]]:
(作用域鏈有兩頭,一頭是自己執行的上下文,另一頭是自己創建時所在的上下文)
即:當前函數的上級上下文是創建函數所在的上下文,就是作用域
以后再遇到函數內的代碼執行,遇到一個變量,首先看是否為自己上下文中的私有變量(看AO中有沒有,有,是自己私有的;沒有,不是自己私有的)。如果是私有的變量,則當前變量的操作和外界環境中的變量互不干擾(沒有直接關系);如果不是自己的私有變量,則按照作用域鏈,查找是否為其上級上下文中的私有變量.....一直找到EC(G)全局上下文為止:作用域鏈查找機制
② 初始化this....
③ 初始化arguments....
④ 形參賦值:形參都是私有變量,放在AO中的。如果不傳遞實參,默認值是undefined
⑤ 變量提升....
5、代碼自上而下執行
6、.....
7、一般情況下,函數執行所形成的私有上下文,進棧執行完后,會默認出棧釋放掉
【私有上下文中存儲的私有變量和一些值都會被釋放掉,目的:為了優化內存空間,減少棧內存的消耗,提高頁面或者計算機的處理速度......】
不能出棧釋放:當前上下文中某些內容(一般是堆內存地址)被當前上下文的外部的事物占用了,則無法出棧釋放。一旦被釋放,后期外部事物就無法找到對應的內容了
注意:多次函數執行,會形成多個全新的、私有執行上下文,這些上下文之間沒有直接的關系
(4)閉包
一般,很多人認為:大函數返回小函數是閉包。
這只是閉包機制中的一種情況。
閉包:函數執行形成一個私有的執行上下文,此上下文中的私有變量,與此上下文以外的變量互不干擾;也就是當前上下文把這些變量保護起來了,我們把函數的這種保護機制稱為閉包。
閉包不是具體的代碼,而是一種機制。
一般情況下,形成的私有上下文很容易被釋放掉,這種保護機制存在時間太短了,不是嚴謹意義上的閉包。有人認為,形成的上下文不被釋放,才是閉包。此時,不僅保護了私有變量,而且這些變量和存儲的值也不會被釋放掉,保存起來了。
閉包的作用:① 保護 ② 保存
利用閉包的兩個作用,可以實現高階編程技巧,以后再說~
3 / 練習題
(1)第一題
var x = 100; function fn() { var x = 200; return function(y) { console.log(y + x++); } } var f = fn(); f(10); f(20);
△ 第一題
i++
后加
△ 圖2.4_后加
(2)第二題
let a=0, b=0; function A(a){ A=function(b){ alert(a+b++); }; alert(a++); } A(1); A(2);
△ 第二題
(3)第三題
let x = 5; function fn(x) { return function(y) { console.log(y + (++x)); } } let f = fn(6); f(7); fn(8)(9); f(10); console.log(x);
△ 第三題
- end -
從“你”到“更好的你”
有無限可能
好啦,好啦,碎碎念了很多:
全局執行上下文、創建函數、作用域、執行函數、私有執行上下文、AO和VO、實參、形參、作用域鏈
△ 圖2.5_練習題,第一題