目錄
全局作用域
局部作用域
函數作用域
塊作用域
作用域鏈
閉包
垃圾回收機制
作用域(scope)規定了變量能夠被訪問的“范圍”,離開了這個“范圍”變量便不能被訪問,作用域分為全局作用域和局部作用域。
全局作用域
<script>
標簽和 .js
文件的【最外層】就是所謂的全局作用域,在此聲明的變量在函數內部也可以被訪問
<script>// 此處是全局function sayHi() {// 此處為局部}// 此處為全局
</script>
全局作用域中聲明的變量,任何其它作用域都可以被訪問,如下代碼所示 ?
<script>// 全局變量 nameconst name = '小明'// 函數作用域中訪問全局function sayHi() {// 此處為局部console.log('你好' + name)}// 全局變量 flag 和 xconst flag = truelet x = 10// 塊作用域中訪問全局if(flag) {let y = 5console.log(x + y) // x 是全局的}
</script>
總結:
-
為
window
對象動態添加的屬性默認也是全局的,不推薦! -
函數中未使用任何關鍵字聲明的變量為全局變量,不推薦!!!
-
盡可能少的聲明全局變量,防止全局變量被污染
JavaScript 中的作用域是程序被執行時的底層機制,了解這一機制有助于規范代碼書寫習慣,避免因作用域導致的語法錯誤。
?
?
局部作用域
局部作用域分為函數作用域和塊作用域。
函數作用域
在函數內部聲明的變量只能在函數內部被訪問,外部無法直接訪問。
<script>// 聲明 counter 函數function counter(x, y) {// 函數內部聲明的變量const s = x + yconsole.log(s) // 18}// 設用 counter 函數counter(10, 8)// 訪問變量 sconsole.log(s)// 報錯
</script>
總結:?
-
函數內部聲明的變量,在函數外部無法被訪問
-
函數的參數也是函數內部的局部變量
-
不同函數內部聲明的變量無法互相訪問
-
函數執行完畢后,函數內部的變量實際被清空了
塊作用域
在 JavaScript 中使用 {}
包裹的代碼稱為代碼塊,代碼塊內部聲明的變量外部將【有可能】無法被訪問。
<script>{// age 只能在該代碼塊中被訪問let age = 18;console.log(age); // 正常}// 超出了 age 的作用域console.log(age) // 報錯let flag = true;if(flag) {// str 只能在該代碼塊中被訪問let str = 'hello world!'console.log(str); // 正常}// 超出了 age 的作用域console.log(str); // 報錯for(let t = 1; t <= 6; t++) {// t 只能在該代碼塊中被訪問console.log(t); // 正常}// 超出了 t 的作用域console.log(t); // 報錯
</script>
JavaScript 中除了變量外還有常量,常量與變量本質的區別是【常量必須要有值且不允許被重新賦值】,常量值為對象時其屬性和方法允許重新賦值。
<script>// 必須要有值const version = '1.0.0';// 不能重新賦值// version = '1.0.1';// 常量值為對象類型const user = {name: '小明',age: 18}// 不能重新賦值user = {};// 屬性和方法允許被修改user.name = '小明明';user.gender = '男';
</script>
總結:
-
let
聲明的變量會產生塊作用域,var
不會產生塊作用域 -
const
聲明的常量也會產生塊作用域 -
不同代碼塊之間的變量無法互相訪問
-
推薦使用
let
或const
作用域鏈
作用域鏈本質上是底層的變量查找機制,在函數被執行時,會優先查找當前函數作用域中查找變量,如果當前作用域查找不到則會依次逐級查找父級作用域直到全局作用域
<script>// 全局作用域let a = 1let b = 2// 局部作用域function f() {let c// let a = 10;console.log(a) // 1 或 10console.log(d) // 報錯// 局部作用域function g() {let d = 'yo'// let b = 20;console.log(b) // 2 或 20}// 調用 g 函數g()}console.log(c) // 報錯console.log(d) // 報錯f();
</script>
?
-
嵌套關系的作用域串聯起來形成了作用域鏈
-
相同作用域鏈中按著從小到大的規則查找變量
-
子作用域能夠訪問父作用域,父級作用域無法訪問子級作用域
閉包
閉包概念? :? 內部函數及其對外部變量的引用捆綁在一起
? ? ? ? ? ? ? ? ? ? ? ?或? 內部函數訪問外部函數的變量?
形成條件:閉包 ? ? 內部函數+外部函數 ?(提供一個變量)
?? ?作用:閉包 ? ?延伸變量的作用域,形成獨立的作用域
?? ?原理:作用域鏈
?? ?缺點:容易造成內存泄漏(內存因某種原因無法釋放)
<body><script>// 1. 閉包 : 內層函數 + 外層函數變量// function outer() {// const a = 1// function f() {// console.log(a)// }// f()// }// outer()// 2. 閉包的應用: 實現數據的私有。統計函數的調用次數// let count = 1// function fn() {// count++// console.log(`函數被調用${count}次`)// }// 3. 閉包的寫法 統計函數的調用次數function outer() {let count = 1function fn() {count++console.log(`函數被調用${count}次`)}return fn}const re = outer()// const re = function fn() {// count++// console.log(`函數被調用${count}次`)// }re()re()// const fn = function() { } 函數表達式// 4. 閉包存在的問題: 可能會造成內存泄漏</script>
</body>
垃圾回收機制
?? ?JS為我們聲明的變量、函數等分配內存,當這些變量、函數、對象的內存不再使用->垃圾
?? ?垃圾回收(GC)機制
?? ?引用計數------ie瀏覽器
?? ?
?? ?簡單,容易造成內存泄漏
?? ?標記清除
?? ?標記通過全局標記所有從全局出發能夠訪問到的對象,不能夠訪問到的對象未被標記
// 垃圾回收(GC)機制// let cat = { name: '貓', age: 10 }// console.log(cat.name)// cat = null// 引用計數-ie瀏覽器// 簡單。容易造成內存泄漏// let cat = { name: '貓', age: 10 }// let cat2 = cat// cat = nullfunction f() {let o = {a: 1,}let o2 = {b: 2,}o.b = o2o2.a = o}f()// 標記清除// 標記 通過全局 標記所有從全局出發能夠訪問到的對象,不能夠訪問到的對象未被標記function fn() {c = {}let o = {}let o2 = {}o.a = o2o2.b = o}fn()console.log(c)