- ==1.函數(shù)的用法==
- 復(fù)用代碼(略)
- 把函數(shù)作為數(shù)據(jù)/值使用
- 把函數(shù)作為命名空間使用
- ==2. 作用域|變量提升 ==
- 變量作用域
- 變量聲明提升
- 做題思路
- ==3. 閉包==
- 閉包
函數(shù)的用法
- 用法1:復(fù)用代碼〔醣粒可以定義函數(shù)、調(diào)用函數(shù),復(fù)用函數(shù)号涯,這是函數(shù)的本職功能茅逮,也是函數(shù)的語法特性冯事。
- 用法2:把函數(shù)作為數(shù)據(jù)/值使用
- 用法3:把函數(shù)作為命名空間使用
把函數(shù)作為數(shù)據(jù)/值使用
函數(shù)也是數(shù)據(jù)/值。這意味著我們可以把函數(shù)賦值給變量德绿、賦值給對象屬性、賦值給數(shù)組元素退渗、賦值給形參等
示例: 函數(shù)名本質(zhì)上就是一個(gè)變量
function sum(a,b){return a + b}
把函數(shù)賦值給另一個(gè)變量
let s = sum
s(2,3)//5
把函數(shù)賦值給對象的屬性
let obj = {name:'張三',age:19}
obj.s = sum
obj.s(3,3)
把函數(shù)賦值給數(shù)組
let arr = [1,2,sum]
console.log(arr[2](arr[0], arr[1]));
把函數(shù)作為命名空間使用
當(dāng)把函數(shù)作為命名空間使用時(shí)移稳,可以確保函數(shù)體內(nèi)的變量不會污染全局命名空間。
有名函數(shù)
let maxValue = 100
function max(a,...aaa) {//只定義了全局變量max
let maxValue = -Infinity
for (n of aaa) {
if (n > maxValue) {
maxValue = n
}
}
return maxValue
}
console.log(window.max);//我不想添加到window上会油,可以使用自執(zhí)行函數(shù)
自執(zhí)行函數(shù)(推薦)
;(function(a,...aaa){
let maxValue = -Infinity
for (n of aaa) {
if (n > maxValue) {
maxValue = n
}
}
console.log(maxValue);
return maxValue
}(33,4,555))
// console.log(window.max)
====2. 作用域與變量提升 ====
變量作用域
復(fù)習(xí)
let
定義變量的關(guān)鍵字个粱,它是臨時(shí)性賦值。
const
定義常量的關(guān)鍵字翻翩,它是永久性賦值都许。聲明常量約定全部使用大寫字母命名標(biāo)識符。
var
ES6之前定義變量的關(guān)鍵字嫂冻,它是臨時(shí)性賦值胶征。
變量作用域指定義變量的區(qū)域。 變量在哪個(gè)區(qū)域定義,就在哪個(gè)區(qū)域發(fā)生作用桨仿。變量作用域決定了變量的可訪問性睛低。在當(dāng)前定義區(qū)域內(nèi),變量可以被訪問到服傍,在這個(gè)區(qū)域以外钱雷,變量不能被訪問到。
示例:全局作用域 VS 局部作用域
<script>
var a = 10 //變量a擁有全局作用域
function fn(){
console.log(a) //輸出10,變量a在全局任何位置都可以訪問
var b = 20
console.log(b) //輸出20吹零,變量b定義在函數(shù)fn內(nèi)罩抗,故擁有局部作用域,在函數(shù)fn內(nèi)可以訪問
}
console.log(b) // 報(bào)錯(cuò)瘪校。變量b是局部變量澄暮,無法從函數(shù)fn外面訪問。
</script>
有兩種類型的作用域:
- 全局作用域:局部作用域之外的區(qū)域就是全局作用域阱扬。
- 局部作用域:局部作用域指函數(shù)作用域和塊級作用域泣懊。這兩種局部作用域使用不同的關(guān)鍵詞定義:
- 函數(shù)作用域:指在
函數(shù)體
內(nèi),使用關(guān)鍵詞var
定義的變量麻惶,會創(chuàng)建變量的函數(shù)作用域馍刮,也就是說變量只能在當(dāng)前函數(shù)體內(nèi)才可以訪問。注:變量定義在if或for創(chuàng)建的代碼塊內(nèi)窃蹋,是不會創(chuàng)建函數(shù)作用域的卡啰。 - 塊級作用域: 指在
大括號
包裹的區(qū)域內(nèi)静稻,使用關(guān)鍵詞let
或const
定義的變量,會創(chuàng)建塊級作用域匈辱,只能在大括號內(nèi)才可以訪問振湾。
- 函數(shù)作用域:指在
示例:函數(shù)作用域
<script>
var a = 10
function fn(){
var a = 20
console.log(a) //20 這個(gè)a定義在函數(shù)內(nèi),是局部變量
}
console.log(a) //10 這個(gè)a定義在函數(shù)外亡脸,是全局變量
</script>
示例:塊級作用域
<script>
let a = 10
if(a === 10){
let a = 20//let在這里創(chuàng)建了局部作用域押搪,故變量b是局部變量。
var b = 30 //var只能創(chuàng)建函數(shù)作用域浅碾,無法創(chuàng)建塊級作用域大州。故在這里創(chuàng)建的變量b是全局變量
console.log(a) //20 這個(gè)a定義在函數(shù)內(nèi),是局部變量
}
console.log(b) //10 因?yàn)閎定義在全局垂谢,故b可以讀取到10
console.log(a) //10 這個(gè)a定義在函數(shù)外厦画,是全局變量
</script>
變量聲明提升
JavaScript 提升是指解釋器似乎在執(zhí)行代碼之前將函數(shù)、變量或類的聲明移動(dòng)到其作用域頂部的過程滥朱。
示例:var
console.log(x === undefined); // true
var x = 3;
(function() {
console.log(x); // undefined
var x = 'local value';
})();
等價(jià)于
var x;
console.log(x === undefined); // true
x = 3;
(function() {
var x;
console.log(x); // undefined
x = 'local value';
})();
示例:let 與 const的臨時(shí)性死區(qū)
從塊開始到聲明被處理根暑,變量都處于“時(shí)間死區(qū)”
console.log(x); // ReferenceError
const x = 3;
console.log(y); // ReferenceError
let y = 3;
如果 const x = 2 聲明根本沒有提升,那么 console.log(x) 語句應(yīng)該能夠從上層作用域讀取 x 值焚虱。然而购裙,因?yàn)?const 聲明仍然“污染”了它定義的整個(gè)范圍,console.log(x) 語句改為從 const x = 2 聲明中讀取 x鹃栽,它尚未初始化躏率,并拋出 ReferenceError。
盡管如此民鼓,將詞法聲明描述為非提升可能更有用薇芝,因?yàn)閺墓髁x的角度來看,這些聲明的提升不會帶來任何有意義的特征丰嘉。
變量聲明提升是關(guān)于函數(shù)作用域的一個(gè)重要問題夯到。先看一個(gè)的例子:
//請問console.log()第一次輸出的是什么?
var n = 123
function fn(){
console.log(n) //?
var n = 456
console.log(n)
}
fn()
您可能會想當(dāng)然第認(rèn)為第一次console.log()輸出的是123饮亏,也就是全局變量n的值耍贾。但事實(shí)并非如此,第一個(gè)console.log()實(shí)際上輸出的是undefined
路幸。因?yàn)樵诤瘮?shù)體內(nèi):變量n也有定義荐开,故局部變量n會覆蓋全局變量n。盡管在函數(shù)體內(nèi)简肴,第一次調(diào)用局部變量n的時(shí)候晃听,n還沒有定義安岂,但n實(shí)際上已經(jīng)存在于本地空間(函數(shù)體內(nèi))了楔绞,這種特殊現(xiàn)象叫做提升(hoisting) 脂信。
提升(hoisting) :指當(dāng)JS執(zhí)行過程進(jìn)入新函數(shù)時(shí)齿坷,新函數(shù)內(nèi)的的所有變量聲明(如var n)都會被提升到當(dāng)前作用域的頂部。
示例:模擬var變量聲明提升
<script>
/**********************
var n //變量聲明被提升至頂部
*********************/
console.log(n) // undefined(在這里可以讀取到n的原因就在于變量聲明提升)
var n = 123
function fn(){
/*****變量聲明被提升至頂部******
var n
*************************/
console.log(n) //undefined(在這里可以讀取到n的原因就在于變量聲明提升)
var n = 456
console.log(n) //456
}
fn()
</script>
示例: 模擬函數(shù)提升
/********整個(gè)函數(shù)被提升到頂部**********
function fn(){
console.log('hello world')
}
*******************************/
fn() // 'hello world' (在這里可以讀取到fn()的原因就在于函數(shù)提升)
function fn(){
console.log('hello world')
}
函數(shù)聲明也會創(chuàng)建變量初斑,所以函數(shù)聲明也會發(fā)生變量提升辛润,與變量聲明提升不同的是,整個(gè)函數(shù)见秤,包括函數(shù)聲明與函數(shù)體都會被提升至當(dāng)前作用域頂部频蛔。
做題思路
- 要知道只有一個(gè)全局作用域。
- 你只需確定局部作用域有幾個(gè)秦叛;(局部作用域有兩種:var聲明會創(chuàng)建函數(shù)作用域,let和const聲明會塊級作用域瀑粥。)
- 預(yù)解析:在每個(gè)作用域內(nèi)做預(yù)解析挣跋;(
var a
和function(){}
一定會提升,let
和const
提升會報(bào)錯(cuò)狞换,所以你就理解為let和const不提升) - 逐行解析: 逐行讀取代碼
- 優(yōu)先級:①如果出現(xiàn)變量名和函數(shù)名相同的情況避咆,那么函數(shù)a的優(yōu)先級高于變量a的優(yōu)先級;②如果局部變量和全局變量同名修噪,那么局部變量覆蓋全局變量查库。
示例
示例