JavaScript作用域和閉包
在javascript中,如果對作用域和閉包弄不清楚,寫代碼就會出很多問題,今天對作用域和閉包做一個總結,
作用域
作用域主要分為全域作用域和區域作用域,其中區域作用域分為函式作用域和塊級作用域,
全域作用域
如果你在大括號({})或者函式的外面定義了一個變數,那么它就是一個全域的變數,它的作用域就是全域作用域,
let a = 1
function fun1 () {
console.log(a) // 結果:1
function fun2 () {
console.log(a) // 結果:1
}
fun2()
}
fun1()
console.log(a) // 結果:1
變數a就是定義在最外層,它就能在全域任意地方被使用,
值得注意的是,在同一級作用域中,使用let或const宣告變數的時候第二個同名會報錯,而使用var宣告變數的時候,會覆寫前面的變數;
區域作用域
如果你在函式或者大括號({})內定義的變數,就是區域作用域的變數,它能夠在該級作用域級任意下級作用域中使用,
function fun1() {
let a = 100
console.log(a) // 結果: 100
function fun2 () {
console.log(a) // 結果:100
}
fun2()
}
fun1()
console.log(a) // 結果: a is not defined
a只能在fun1函式內部包括內部函式中使用,一旦出了fun1的范圍就無法使用該變數了,
自由變數的查找
一個變數在當前作用域沒有定義卻被使用了,就是自由變數,它的執行規則是怎樣的呢?
自由變數的查找是向上級作用域,一層一層以此尋找,直至找到為止,如果全域作用域都沒有找到,則報錯xx is not defined,
let a = 100
function fun1 () {
let a1 = 2
function fun2 () {
let a2 = 3
let a = 0
function fun3 () {
let a3 = 4
a++
console.log(a + a1 + a2 + a3) // 結果: 10
}
fun3()
}
fun2()
}
fun1()
console.log(a) // 結果: 100
正如上述代碼所示,在fun3函式內,a和a1、 a2都沒有定義,但被使用了,在執行的時候,它會往上級作用域中查找,從而找到它們的值,值得注意的一點的是,在全域作用域和fun2的函式中我們都定義了a,但是在fun3中使用的fun2中定義的值,外面的使用的全域作用域的值,也就是說,它往上級查找的時候,只要查找到就會停止查找,會就近使用,作用域間也不會互相干擾,(它們里面存在的變數提升和函式提升可以查看我的另一篇博客有專門的總結)
閉包
閉包是作用域應用的特殊情況,主要有兩種表現:(1)函式作為引數被傳遞,(2)函式作為回傳值被回傳,
/**
* 函式作為回傳值
*/
function create () {
const a1 = 100
return function () {
console.log(a1)
}
}
const fn = create()
const a1 = 200
fn() // 結果: 100
/**
* 函式作為引數
*/
function print (fn) {
const a2 = 300
fn()
}
const a2 = 400
function fn1 () {
console.log(a2)
}
print(fn1) // 結果: 400
上面代碼演示了函式的兩種表現,值得注意的是:在閉包中,自由變數的查找,是在函式定義的地方,向上級作用域查找,不是在執行的地方!
閉包的實際應用場景
(1)隱藏資料, 如做一個簡單的cache工具
(2)函式防抖與節流
(補充)this在不同場景中的取值
首先我們要知道this是在執行程序中確定它的值的,那么在作用域和閉包不同的場景下如何取值呢?
(1) 當作普通函式被呼叫時,this是當前的window物件
function fn1 () {
console.log(this)
}
fn1() // 結果: window
(2) 在使用call、apply或者 bind的情況下,this是指定的物件
function fn1 () {
console.log(this)
}
fn1.call({x: 100}) // 結果: {x: 100}
(3) 作為物件方法被呼叫時,this是當前物件
class People {
constructor (name) {
this.name = name
}
sayHi() {
console.log(this) // 結果:當前物件
}
}
(4) 在class的方法中被呼叫時,this是當前物件(第3點和第4點其實一樣)
class People {
constructor (name) {
this.name = name
}
sayHi() {
console.log(this)
}
}
// 實體化并使用
const p = new People('一個人')
p.sayHi() // 結果: p 物件
(5) 在箭頭函式中使用,this指向的是上層物件,箭頭函式本身并不會指定this
class People {
constructor (name) {
this.name = name
}
test1() {
setTimeout(function() {
console.log(this) // 結果:window; 原因:這個是由setTimeout觸發的執行,所以this就是window
})
}
test2() {
setTimeout(() => {
console.log(this) // 結果:window; 原因:這個是雖然是由setTimeout觸發的執行,但是箭頭函式的this指向的是上級作用域的this,也就是當前物件
})
}
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/qianduan/254414.html
標籤:其他
下一篇:jsonp解決跨域問題
