Snipaste_20200510_172501.jpg
先了解變量提升與函數(shù)提升
- 變量聲明提升
通過var聲明的變量,在定義語句之前就能訪問到
值:undefined - 函數(shù)提升聲明
通過function聲明的函數(shù),在函數(shù)之前就能直接調用
值:函數(shù)對象
var a=3
function fn(){
console.log(a)
var a=4
}
fn() //a輸出多少撞反? undefined
console.log(b) //undefined 變量提升
fn2() //可調用 函數(shù)提升
fn3() //不可調用 變量提升
var b=3
function fn2(){
console.log("fn2()")
}
var fn3=function(){
console.log("fn3()")
}
再了解執(zhí)行上下文
- 代碼分類(位置)
全局代碼
函數(shù)(局部)代碼 - 全局執(zhí)行上下文
在執(zhí)行全局代碼前將window確定為全局執(zhí)行上下文
對全局數(shù)據(jù)進行預處理
*var定義的全局變量==>undefined,添加為window的屬性
*function聲明的全局變量==>賦值(fun),添加為window的方法
*this ==>賦值(window)
開始執(zhí)行全局代碼 - 函數(shù)執(zhí)行上下文
在調用函數(shù)磨确,準備執(zhí)行函數(shù)體之前,創(chuàng)建對應的函數(shù)執(zhí)行上下文對象
對局部數(shù)據(jù)進行預處理
*形參變量==>賦值(實參)==>添加為執(zhí)行上下文屬性
*arguments==>賦值(實參列表)沥潭,添加為執(zhí)行上下文屬性
*var定義的局部變量==>undefined,添加為執(zhí)行上下文的屬性
*function聲明的函數(shù)==>賦值(fun),添加為執(zhí)行上下文的方法
*this==>賦值(調用函數(shù)的對象)
開始執(zhí)行函數(shù)體代碼
//放到html 的script中運行
//全局執(zhí)行上下文
console.log(a1,window.a1)
a2()
console.log(this)
var a1 = 3
function a2(){
console.log("a2()")
}
console.log(a1)
//函數(shù)執(zhí)行上下文
function fn(a1){
console.log(a1) //2
console.log(a2) //undefined
a3() //a3()
console.log(this) //window
console.log(arguments) //(2,3)
var a2 = 3
function a3(){
console.log("a3()")
}
}
fn(2,3)
再了解下作用與作用于
var x= 10;
function fn(){
console.log(x);
}
function show(f){
var x=20
fn();
}
show(fn)
閉包
- 當一個嵌套的內部(子)函數(shù)引用了嵌套的外部(父)函數(shù)的變量(函數(shù))時政敢,就產(chǎn)生了閉包
- 閉包包含被引用變量(函數(shù))的對象
最簡單的閉包
function fn1(){
var a=2
function fn2(){//執(zhí)行函數(shù)定義就會產(chǎn)生閉包(不用調用內部函數(shù))
console.log(a)
}
// fn2()
}
fn1()
常見的閉包
將函數(shù)作為另一個函數(shù)的返回值
function fn1(){
//此時閉包已經(jīng)產(chǎn)生(函數(shù)提升其徙,內部函數(shù)對象已創(chuàng)建)
var a=2
function fn2(){
a++
console.log(a)
}
return fn2()
}
var f=fn1()
f() //3
f() //4
f=null //閉包死亡 (包含閉包的函數(shù)對象成為垃圾對象)
*將函數(shù)實參傳給另一個函數(shù)調用
function showDelay(msg,time){
setTimeout(function(){
alert(msg) //假如沒有alert(msg) 就不會產(chǎn)生閉包
},time)
}
showDelay("111",1000)
作用:
- 使用函數(shù)內部的變量在函數(shù)執(zhí)行完后,仍然存活在內存中(延長了局部變量的生命周期)
- 讓函數(shù)外部可以操作(讀寫)到函數(shù)內部的數(shù)據(jù)(變量/函數(shù))
生命周期:
- 產(chǎn)生:嵌套內部函數(shù)定義就會產(chǎn)生閉包(不用調用內部函數(shù))
- 死亡:嵌套內部函數(shù)成為垃圾對象
缺點:
- 延長了局部變量的生命周期喷户,占用時間變長唾那,容易造成內存泄漏 記得釋放f=null
鞏固:
var name = "window"
var object = {
name: "obj",
getNameFunc: function () {
return function () {
return this.name;
}
}
}
console.log(object.getNameFunc()())
var name2 = "window"
var object2 = {
name: "obj",
getNameFunc: function () {
var that = this
return function () {
return this.name;
}
}
}
console.log(object2.getNameFunc()())
function fun(n, o) {
console.log(o)
return {
fun: function (m) {
return fun(m, n)
}
}
}
var a = fun(0); a.fun(1); a.fun(2); a.fun(3) //undefined 0 0 0
var b = fun(0).fun(1).fun(2).fun(3)//undefined 0 1 2
var c = fun(0).fun(1); c.fun(2); c.fun(3) //undefined 0 1 1
這個東西當年做對的,現(xiàn)在回頭反而不會了褪尝。闹获。。河哑。
關鍵點:
a.fun(1)并沒有一個新的變量接受避诽,所以閉包是產(chǎn)生了但是立即消失了
var a1=fun(0); var a2=a1.fun(1);var a3=a2.fun(2) ;var a4=a3.fun(2) 可以參照這條理解。
運用:
節(jié)流與防抖
//<body>
//<button id="btn1">節(jié)流函數(shù)</button>
//<button id="btn2">防抖函數(shù)</button>
//<script type="text/javascript">
function debounce(fn, delay) {
let timeoutId = null
console.log("debounce已被運行")
return function () {
//console.log("this:",this,'arguments:',arguments)
const that = this
const args = arguments
if (timeoutId) {
//console.log('刪除定時器')
clearTimeout(timeoutId)
}
timeoutId = setTimeout(() => { console.log('運行需要運行的函數(shù)'); fn.apply(that, args) }, delay)
}
}
btn1 = document.getElementById('btn1')
btn1.addEventListener('click', throttle(onclick1, 1000), false);
btn2 = document.getElementById('btn2')
btn2.addEventListener('click', debounce(onclick1, 1000), false);
//</script>
//</body>