js中的閉包實例展示
前言
準確來說,閉包是基于正常的垃圾回收處理機制下的。也就是說,一般情況一個函數(shù)(函數(shù)作用域)執(zhí)行完畢,里面聲明的變量會全部釋放,被垃圾回收器回收。但閉包利用一個技巧,讓作用域里面的變量,在函數(shù)執(zhí)行完之后依舊保存沒有被垃圾回收處理掉。
閉包
定義
MDN定義
javascriptkit
詞法作用域
閉包的三大特點為:
1、函數(shù)嵌套函數(shù)
2、內部函數(shù)可以訪問外部函數(shù)的變量
3、參數(shù)和變量不會被回收。
作用域鏈
函數(shù)在執(zhí)行的過程中,先從自己內部找變量如果找不到,再從創(chuàng)建當前函數(shù)所在的作用域(詞法作用域)去找, 以 此往上注意找的是變量的當前的狀態(tài)
作用域鏈的博客
函數(shù)連同它作用域鏈上的要找的這個變量,共同構成閉包
一般情況下使用閉包主要是為了
- 封裝數(shù)據
- 暫存數(shù)據
一個典型的閉包案例
function car(){
var speed = 0
function fn(){
speed++
console.log(speed)
}
return fn
}
var speedUp = car()
speedUp() //1
speedUp() //2
當函數(shù)內部沒有執(zhí)行以下的代碼時
function fn(){
speed++
console.log(speed)
}
return fn
在代碼執(zhí)行完成后,函數(shù)內部的局部變量speed就會被銷毀,由于全局標量speedUp一直存在(除非關閉當前頁面,否則全局變量一直存在),那么函數(shù)內部的作用域就沒有辦法被銷毀,里面有東西一直被使用,這點與瀏覽器的垃圾回收機制相仿,當我們執(zhí)行speedUp(),他會在函數(shù)的詞法作用域下去尋找,函數(shù)里面又返回了一個fn,因而形成閉包,簡單的理解為
var speed = 0
function fn(){
speed++
console.log(speed)
}
這一段代碼形成一個閉包,如果不return fn,那函數(shù)內部的局部變量就會被銷毀。
我們可以看看上述代碼利用立即執(zhí)行語句和立即執(zhí)行函數(shù)可以怎么演變:
function car(){
var speed = 0
function fn(){
speed++
console.log(speed)
}
return fn
}
var speedUp = car()
//1
function car(){
var speed = 0
return function (){
speed++
console.log(speed)
}
}
var speedUp = car()
//2
function car(speed){
return function (){
speed++
console.log(speed)
}
}
var speedUp = car(3)
//3
function car(){
var speed = arguments[0]
return function (){
speed++
console.log(speed)
}
}
var speedUp = car()
//4
function car(){
var speed = 0
return function (){
speed++
console.log(speed)
}
}
//5 car可以不寫,則為匿名函數(shù)
var speedUp = (function car(speed){
return function (){
speed++
console.log(speed)
}
}
)(3)
閉包的相關案例
如下代碼輸出多少?如果想輸出3,那如何改造代碼?
var fnArr = [];
for (var i = 0; i < 10; i ++) {
fnArr[i] = function(){
return i
};
}
console.log( fnArr[3]() ) // 10
同等演變
假設只有兩層循環(huán):
var fnArr = []
for (var i = 0; i < 2; i ++) {
fnArr[i] = (function(j){
return function(){
return j
}
})(i)
}
fnArr[3]()
//1
var fnArr = []
fnArr[0] = (function(j){
return function(){
return j
}
})(0)
}
fnArr[1] = (function(j){
return function(){
return j
}
})(1)
}
fnArr[3]()
//2
var a = (function(j){
return function(){
return j
}
})(0)
}
var b = (function(j){
return function(){
return j
}
})(1)
}
b()
//3
var a = (function(j){
return function(){
return j
}
})(0)
}
function fn2(j){
return function(){
return j
}
}
var b = fn2(1)
//4
var a = (function(j){
return function(){
return j
}
})(0)
}
function fn2(j){
return function(){
return j
}
return f
}
var b = fn2(1)
//5
var a = (function(j){
return function(){
return j
}
})(0)
}
function fn2(j){
var j = arguments[0]
function f(){
return j
}
return f
}
var b = fn2(1)
改造后(立即執(zhí)行語句,演變過程)
var fnArr = []
for (var i = 0; i < 10; i ++) {
fnArr[i] = (function(j){
return function(){
return j
}
})(i)
}
console.log( fnArr[3]() ) // 3
var fnArr = []
for (var i = 0; i < 10; i ++) {
(function(i){
fnArr[i] = function(){
return i
}
})(i)
}
console.log( fnArr[3]() ) // 3
var fnArr = []
for (let i = 0; i < 10; i ++) {
fnArr[i] = function(){
return i
}
}
console.log( fnArr[3]() ) // 3
封裝一個 Car 對象
var Car = (function(){
var speed = 0;
function set(s){
speed = s
}
function get(){
return speed
}
function speedUp(){
speed++
}
function speedDown(){
speed--
}
return {
setSpeed: setSpeed,
get: get,
speedUp: speedUp,
speedDown: speedDown
}
})()
Car.set(30)
Car.get() //30
Car.speedUp()
Car.get() //31
Car.speedDown()
Car.get() //3
如下代碼輸出多少?如何連續(xù)輸出 0,1,2,3,4
for(var i=0; i<5; i++){
setTimeout(function(){
console.log('delayer:' + i )
}, 0)
}
輸出結果為:delayer:5(連續(xù)輸出5個),執(zhí)行setTimeout時,代碼會掛到任務隊列中區(qū),待i遍歷完成之后執(zhí)行,而此時i = 5,所以輸出delayer:5(連續(xù)輸出5個)
修改后
for(var i=0; i<5; i++){
(function(j){
setTimeout(function(){
console.log('delayer:' + j )
}, 0)//1000-1000*j
})(i)
}
或者
for(var i=0; i<5; i++){
setTimeout((function(j){
return function(){
console.log('delayer:' + j )
}
}(i)), 0)
}
如下代碼輸出多少?
function makeCounter() {
var count = 0
return function() {
return count++
};
}
var counter = makeCounter()
var counter2 = makeCounter();
console.log( counter() ) // 0
console.log( counter() ) // 1
console.log( counter2() ) // 0
console.log( counter2() ) // 1
補全代碼,實現(xiàn)數(shù)組按姓名、年紀、任意字段排序
var users = [
{ name: "John", age: 20, company: "Baidu" },
{ name: "Pete", age: 18, company: "Alibaba" },
{ name: "Ann", age: 19, company: "Tecent" }
]
users.sort(byName)
users.sort(byAge)
users.sort(byField('company'))
解答
function byName(user1, user2){
return user1.name > user2.name
}
function byAge (user1, user2){
return user1.age > user2.age
}
function byFeild(field){
return function(user1, user2){
return user1[field] > user2[field]
}
}
users.sort(byField('company'))
寫一個 sum 函數(shù),實現(xiàn)如下調用方式
console.log( sum(1)(2) ) // 3 console.log( sum(5)(-1) ) // 4
總結
以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或者工作具有一定的參考學習價值,如果有疑問大家可以留言交流,謝謝大家對腳本之家的支持。
相關文章
JavaScript實現(xiàn)事件總線(Event?Bus)的方法詳解
Event?Bus?事件總線,通常作為多個模塊間的通信機制,相當于一個事件管理中心。本文將介紹如何在JavaScript中實現(xiàn)事件總線,需要的可以參考一下2022-05-05
JavaScript實現(xiàn)正則去除a標簽并保留內容的方法【測試可用】
這篇文章主要介紹了JavaScript實現(xiàn)正則去除a標簽并保留內容的方法,結合實例形式詳細分析了javascript針對a標簽及span標簽的正則匹配相關操作技巧,需要的朋友可以參考下2018-07-07
值得學習的bootstrap fileinput文件上傳工具
這篇文章主要分享了一款值得大家學習的bootstrap fileinput文件上傳工具,具有一定的參考價值,感興趣的小伙伴們可以參考一下2016-11-11

