前端基礎知識2
阿新 • • 發佈:2018-11-26
作用域
- 理解
- 就是一塊"地盤", 一個程式碼段所在的區域
- 它是靜態的(相對於上下文物件), 在編寫程式碼時就確定了
- 分類
- 全域性作用域
- 函式作用域
- 沒有塊作用域(ES6有了)
- 作用
- 隔離變數,不同作用域下同名變數不會有衝突
<script type="text/javascript"> /* //沒塊作用域 if(true) { var c = 3 } console.log(c)*/ var a = 10, b = 20 function fn(x) { var a = 100, c = 300; console.log('fn()', a, b, c, x) function bar(x) { var a = 1000, d = 400 console.log('bar()', a, b, c, d, x) } bar(100) bar(200) } fn(10) </script>
作用域與執行上下文
- 區別1
- 全域性作用域之外,每個函式都會建立自己的作用域,作用域在函式定義時就已經確定了。而不是在函式呼叫時
- 全域性執行上下文環境是在全域性作用域確定之後, js程式碼馬上執行之前建立
- 函式執行上下文是在呼叫函式時, 函式體程式碼執行之前建立
- 區別2
- 作用域是靜態的, 只要函式定義好了就一直存在, 且不會再變化
- 執行上下文是動態的, 呼叫函式時建立, 函式呼叫結束時就會自動釋放
- 聯絡
- 執行上下文(物件)是從屬於所在的作用域
- 全域性上下文環境==>全域性作用域
- 函式上下文環境==>對應的函式使用域
<script type="text/javascript"> var a = 10, b = 20 function fn(x) { var a = 100, c = 300; console.log('fn()', a, b, c, x) function bar(x) { var a = 1000, d = 400 console.log('bar()', a, b, c, d, x) } bar(100) bar(200) } fn(10) </script>
作用域鏈
- 理解
- 多個上下級關係的作用域形成的鏈, 它的方向是從下向上的(從內到外)
- 查詢變數時就是沿著作用域鏈來查詢的
- 查詢一個變數的查詢規則
- 在當前作用域下的執行上下文中查詢對應的屬性, 如果有直接返回, 否則進入2
- 在上一級作用域的執行上下文中查詢對應的屬性, 如果有直接返回, 否則進入3
- 再次執行2的相同操作, 直到全域性作用域, 如果還找不到就丟擲找不到的異常
<script type="text/javascript">
var a = 1
function fn1() {
var b = 2
function fn2() {
var c = 3
console.log(c)
console.log(b)
console.log(a)
console.log(d)
}
fn2()
}
fn1()
</script>
閉包
- 如何產生閉包?
- 當一個巢狀的內部(子)函式引用了巢狀的外部(父)函式的變數(函式)時, 就產生了閉包
- 閉包到底是什麼?
- 使用chrome除錯檢視
- 理解一: 閉包是巢狀的內部函式(絕大部分人)
- 理解二: 包含被引用變數(函式)的物件(極少數人)
- 注意: 閉包存在於巢狀的內部函式中
- 產生閉包的條件?
- 函式巢狀
- 內部函式引用了外部函式的資料(變數/函式)
<script type="text/javascript">
function fn1 () {
var a = 2
var b = 'abc'
function fn2 () { //執行函式定義就會產生閉包(不用呼叫內部函式)
console.log(a)
}
// fn2()
}
fn1()
function fun1() {
var a = 3
var fun2 = function () {
console.log(a)
}
}
fun1()
</script>
常見的閉包
- 將函式作為另一個函式的返回值
- 將函式作為實參傳遞給另一個函式呼叫
<script type="text/javascript">
// 1. 將函式作為另一個函式的返回值
function fn1() {
var a = 2
function fn2() {
a++
console.log(a)
}
return fn2
}
var f = fn1()
f() // 3
f() // 4
// 2. 將函式作為實參傳遞給另一個函式呼叫
function showDelay(msg, time) {
setTimeout(function () {
alert(msg)
}, time)
}
showDelay('atguigu', 2000)
</script>
閉包的作用
- 使用函式內部的變數在函式執行完後, 仍然存活在記憶體中(延長了區域性變數的生命週期)
- 讓函式外部可以操作(讀寫)到函式內部的資料(變數/函式)
問題:
- 函式執行完後, 函式內部宣告的區域性變數是否還存在? 一般是不存在, 存在於閉中的變數才可能存在
- 在函式外部能直接訪問函式內部的區域性變數嗎? 不能, 但我們可以通過閉包讓外部操作它
<script type="text/javascript">
function fn1() {
var a = 2
function fn2() {
a++
console.log(a)
// return a
}
function fn3() {
a--
console.log(a)
}
return fn3
}
var f = fn1()
f() // 1
f() // 0
</script>
閉包的生命週期
- 產生: 在巢狀內部函式定義執行完時就產生了(不是在呼叫)
- 死亡: 在巢狀的內部函式成為垃圾物件時
<script type="text/javascript">
function fn1() {
//此時閉包就已經產生了(函式提升, 內部函式物件已經建立了)
var a = 2
function fn2 () {
a++
console.log(a)
}
return fn2
}
var f = fn1()
f() // 3
f() // 4
f = null //閉包死亡(包含閉包的函式物件成為垃圾物件)
</script>
閉包的應用 (自定義JS模組)
閉包的應用2 : 定義JS模組
- 具有特定功能的js檔案
- 將所有的資料和功能都封裝在一個函式內部(私有的)
- 只向外暴露一個包信n個方法的物件或函式
- 模組的使用者, 只需要通過模組暴露的物件呼叫方法來實現對應的功能
<script type="text/javascript" src="myModule.js"></script>
<script type="text/javascript">
var module = myModule()
module.doSomething()
module.doOtherthing()
</script>
function myModule() {
//私有資料
var msg = 'My atguigu'
//操作資料的函式
function doSomething() {
console.log('doSomething() '+msg.toUpperCase())
}
function doOtherthing () {
console.log('doOtherthing() '+msg.toLowerCase())
}
//向外暴露物件(給外部使用的方法)
return {
doSomething: doSomething,
doOtherthing: doOtherthing
}
}
閉包的應用2 : 定義JS模組
- 具有特定功能的js檔案
- 將所有的資料和功能都封裝在一個函式內部(私有的)
- 只向外暴露一個包信n個方法的物件或函式
- 模組的使用者, 只需要通過模組暴露的物件呼叫方法來實現對應的功能
<script type="text/javascript" src="myModule2.js"></script>
<script type="text/javascript">
myModule2.doSomething()
myModule2.doOtherthing()
</script>
(function () {
//私有資料
var msg = 'My atguigu'
//操作資料的函式
function doSomething() {
console.log('doSomething() '+msg.toUpperCase())
}
function doOtherthing () {
console.log('doOtherthing() '+msg.toLowerCase())
}
//向外暴露物件(給外部使用的方法)
window.myModule2 = {
doSomething: doSomething,
doOtherthing: doOtherthing
}
})()
閉包的缺點及解決
- 缺點
- 函式執行完後, 函式內的區域性變數沒有釋放, 佔用記憶體時間會變長
- 容易造成記憶體洩露
- 解決
- 能不用閉包就不用
- 及時釋放
<script type="text/javascript">
function fn1() {
var arr = new Array[100000]
function fn2() {
console.log(arr.length)
}
return fn2
}
var f = fn1()
f()
f = null //讓內部函式成為垃圾物件-->回收閉包
</script>