這里給大家分享我在網上總結出來的一些知識,希望對大家有所幫助

1. 對閉包的理解
閉包是指有權訪問另一個函式作用域中變數的函式,創建閉包的最常見的方式就是在一個函式內創建另一個函式,創建的函式可以訪問到當前函式的區域變數,
閉包有兩個常用的用途;
- 閉包的第一個用途是使我們在函式外部能夠訪問到函式內部的變數,通過使用閉包,可以通過在外部呼叫閉包函式,從而在外部訪問到函式內部的變數,可以使用這種方法來創建私有變數,
- 閉包的另一個用途是使已經運行結束的函式背景關系中的變數物件繼續留在記憶體中,因為閉包函式保留了這個變數物件的參考,所以這個變數物件不會被回收,
比如,函式 A 內部有一個函式 B,函式 B 可以訪問到函式 A 中的變數,那么函式 B 就是閉包,
function A() {
let a = 1
window.B = function () {
console.log(a)
}
}
A()
B() // 1
在 JS 中,閉包存在的意義就是讓我們可以間接訪問函式內部的變數,經典面試題:回圈中使用閉包解決 var 定義函式的問題
for (var i = 1; i <= 5; i++) {
setTimeout(function timer() {
console.log(i)
}, i * 1000)
}
首先因為 setTimeout 是個異步函式,所以會先把回圈全部執行完畢,這時候 i 就是 6 了,所以會輸出一堆 6,解決辦法有三種:
- 第一種是使用閉包的方式
for (var i = 1; i <= 5; i++) {
;(function(j) {
setTimeout(function timer() {
console.log(j)
}, j * 1000)
})(i)
}
在上述代碼中,首先使用了立即執行函式將 i 傳入函式內部,這個時候值就被固定在了引數 j 上面不會改變,當下次執行 timer 這個閉包的時候,就可以使用外部函式的變數 j,從而達到目的,
- 第二種就是使用
setTimeout的第三個引數,這個引數會被當成timer函式的引數傳入,
for (var i = 1; i <= 5; i++) {
setTimeout(
function timer(j) {
console.log(j)
},
i * 1000,
i
)
}
- 第三種就是使用
let定義i了來解決問題了,這個也是最為推薦的方式
for (let i = 1; i <= 5; i++) {
setTimeout(function timer() {
console.log(i)
}, i * 1000)
}
2. 對作用域、作用域鏈的理解
1)全域作用域和函式作用域
(1)全域作用域
- 最外層函式和最外層函式外面定義的變數擁有全域作用域
- 所有未定義直接賦值的變數自動宣告為全域作用域
- 所有window物件的屬性擁有全域作用域
- 全域作用域有很大的弊端,過多的全域作用域變數會污染全域命名空間,容易引起命名沖突,
(2)函式作用域
- 函式作用域宣告在函式內部的變零,一般只有固定的代碼片段可以訪問到
- 作用域是分層的,內層作用域可以訪問外層作用域,反之不行
2)塊級作用域
- 使用ES6中新增的let和const指令可以宣告塊級作用域,塊級作用域可以在函式中創建也可以在一個代碼塊中的創建(由
{ }包裹的代碼片段) - let和const宣告的變數不會有變數提升,也不可以重復宣告
- 在回圈中比較適合系結塊級作用域,這樣就可以把宣告的計數器變數限制在回圈內部,
作用域鏈:
在當前作用域中查找所需變數,但是該作用域沒有這個變數,那這個變數就是自由變數,如果在自己作用域找不到該變數就去父級作用域查找,依次向上級作用域查找,直到訪問到window物件就被終止,這一層層的關系就是作用域鏈,
作用域鏈的作用是保證對執行環境有權訪問的所有變數和函式的有序訪問,通過作用域鏈,可以訪問到外層環境的變數和函式,
作用域鏈的本質上是一個指向變數物件的指標串列,變數物件是一個包含了執行環境中所有變數和函式的物件,作用域鏈的前端始終都是當前執行背景關系的變數物件,全域執行背景關系的變數物件(也就是全域物件)始終是作用域鏈的最后一個物件,
當查找一個變數時,如果當前執行環境中沒有找到,可以沿著作用域鏈向后查找,
3. 對執行背景關系的理解
1. 執行背景關系型別
(1)全域執行背景關系
任何不在函式內部的都是全域執行背景關系,它首先會創建一個全域的window物件,并且設定this的值等于這個全域物件,一個程式中只有一個全域執行背景關系,
(2)函式執行背景關系
當一個函式被呼叫時,就會為該函式創建一個新的執行背景關系,函式的背景關系可以有任意多個,
(3)eval函式執行背景關系
執行在eval函式中的代碼會有屬于他自己的執行背景關系,不過eval函式不常使用,不做介紹,
2. 執行背景關系堆疊
- JavaScript引擎使用執行背景關系堆疊來管理執行背景關系
- 當JavaScript執行代碼時,首先遇到全域代碼,會創建一個全域執行背景關系并且壓入執行堆疊中,每當遇到一個函式呼叫,就會為該函式創建一個新的執行背景關系并壓入堆疊頂,引擎會執行位于執行背景關系堆疊頂的函式,當函式執行完成之后,執行背景關系從堆疊中彈出,繼續執行下一個背景關系,當所有的代碼都執行完畢之后,從堆疊中彈出全域執行背景關系,
let a = 'Hello World!';
function first() {
console.log('Inside first function');
second();
console.log('Again inside first function');
}
function second() {
console.log('Inside second function');
}
first();
//執行順序
//先執行second(),在執行first()
3. 創建執行背景關系
創建執行背景關系有兩個階段:創建階段和執行階段
1)創建階段
(1)this系結
- 在全域執行背景關系中,this指向全域物件(window物件)
- 在函式執行背景關系中,this指向取決于函式如何呼叫,如果它被一個參考物件呼叫,那么 this 會被設定成那個物件,否則 this 的值被設定為全域物件或者 undefined
(2)創建詞法環境組件
- 詞法環境是一種有識別符號——變數映射的資料結構,識別符號是指變數/函式名,變數是對實際物件或原始資料的參考,
- 詞法環境的內部有兩個組件:加粗樣式:環境記錄器:用來儲存變數個函式宣告的實際位置外部環境的參考:可以訪問父級作用域
(3)創建變數環境組件
- 變數環境也是一個詞法環境,其環境記錄器持有變數宣告陳述句在執行背景關系中創建的系結關系,
2)執行階段
此階段會完成對變數的分配,最后執行完代碼,
簡單來說執行背景關系就是指:
在執行一點JS代碼之前,需要先決議代碼,決議的時候會先創建一個全域執行背景關系環境,先把代碼中即將執行的變數、函式宣告都拿出來,變數先賦值為undefined,函式先宣告好可使用,這一步執行完了,才開始正式的執行程式,
在一個函式執行之前,也會創建一個函式執行背景關系環境,跟全域執行背景關系類似,不過函式執行背景關系會多出this、arguments和函式的引數,
- 全域背景關系:變數定義,函式宣告
- 函式背景關系:變數定義,函式宣告,
this,arguments
如果對您有所幫助,歡迎您點個關注,我會定時更新技術檔案,大家一起討論學習,一起進步,

轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/467035.html
標籤:Html/Css
上一篇:說一說CSS中的變數
