前言:
這是一篇關於閉包函數的總結和筆記javascript
但願對你們有點幫助 寫的很差的地方,也請你們多多指教
一: js中的命名函數,匿名函數,自調用函數和回調函數
1.命名函數: 函數若是有名字,就是命名函數java
function f1( ) { console.log("我就是命名函數"); }
2.匿名函數: 函數若是沒有名字,就是匿名函數面試
(function () { console.log("我就是匿名函數"); })
3.自調用函數: 本身調用本身緩存
var f2 = function () { console.log("你好"); }; f2();
fn裏面存儲的是函數體代碼,經過fn()的方式調用. 匿名函數自調用: 在匿名函數後面加括號,裏面外面均可以; 也能夠在函數前用 ! + -;
緣由:js只能識別表達式(由運算符組成的式子) 與 語句( 程序流程控制 );讓匿名函數能夠調用原理:將匿名函數變成表達式閉包
4.回調函數: 回調函數就是一個參數將這個函數做爲參數傳到另外一個函數裏面當那個函數執行完以後,再執行傳進去的這個函數。模塊化
function f3(fn) { fn(); //函數調用 --- 說明fn這個變量中存儲的是一個函數 }; function f4() { console.log("函數能夠做爲參數使用") } f3(f4); // 調用f3,將f4做爲參數使用
二: 函數的語法
1.函數聲明函數
function f5() { console.log("我是函數聲明"); }; f5()
2.表達式聲明this
var f6 = function () { console.log("我是表達式聲明"); }; f6();
三: 匿名函數的四種常見場景
1.註冊事件code
document.querySelector("#btn").onclick = function () { console.log("我是匿名函數調用的一種場景"); };
2.定時器對象
setInterval(function () { console.log("我是定時器中的匿名函數"); },1000);
3.變量存儲
var f7 = function () { console.log("我是變量存儲中的函數"); };
4.對象方法
var obj = { name: "拉克絲", say: function () { console.log(this.name); } }; obj.say();
四: 閉包函數
閉包有三個特性: 函數嵌套函數; 函數內部能夠引用外部的參數和變量; 參數和變量不會被垃圾回收機制回收
閉包的好處是:1.但願一個變量長期駐紮在內存中 2.避免全局變量的污染 3.私有成員的存在
01 全局變量的累加
var a = 1; function f7( ){ a++; alert(a); } f7(); // 2 f7(); // 3 f7(); // 4
02 局部變量
function f8( ){ var a = 1; a++; alert(a); } f8(); // 2 f8(); // 2 f8(); // 2
03 局部變量的累加
function outer( ){ var x = 10; return function ( ) { //函數嵌套函數 x++; alert(x); } } //外部函數只調用一次,獲得的同一個變量 var y = outer(); //外部函數賦值給變量y 調用一次外部函數,獲得一個閉包函數 y(); // 11 //調用閉包函數,返回內部變量 y(); // 12 y(); // 13 outer()(); // 11 outer()(); // 11 outer()(); // 11
04 經典例子
function fn() { var num = 3; return function () { var n = 0; console.log(++n); console.log(++num); } } var fn1 = fn(); fn1() // 1 4 fn1() // 1 5 fn1() // 1 6 fn()() // 1 4 fn()() // 1 4 fn()() // 1 4
05 模塊化代碼,減小全局變量的污染
var abc = (function ( ) { //abc 爲外部匿名函數的返回值 var a = 1; return function ( ) { a++; alert(a); } }()); abc(); // 2 // 調用一次abc函數,其實就是調用裏面內部函數的返回值 abc(); // 3 abc() // 4
06 私有成員的存在
var aaa = (function ( ) { var a = 1; function bbb( ) { a++; alert(a); } function ccc( ) { a++; alert(a); } return { b: bbb, c: ccc } })(); aaa.b(); // 2 aaa.c(); // 3 aaa.b(); // 4 aaa.c(); // 5
07 使用匿名函數實現累加
使用匿名函數實現局部變量駐留在內存中,從而實現累加
function aaa( ) { var a = 10; return function ( ) { //匿名函數 a++; return a; }; } var y = aaa(); alert(y()); // 11 alert(y()); // 12 alert(y()); // 13
08 閉包做爲參數傳遞
var num = 15; var fn1 = function (x) { if(x > num){ console.log( x ); } } void function (fn2) { var num = 100; fn2(30); }(fn1); // 30 // void 至關於匿名函數的自調用 // fn2是當作參數傳進來的 不是在void裏面聲明的 是在全局做用域裏聲明的 因此拿的是num=15 因此打印出來30
對上題有疑惑的,能夠看下面這個
var num = 15; var fn1 = function (x) { if(x > num){ console.log( x ); // 30 } } void function (fn2) { var num = 100; fn2(30); function fn3(x) { console.log( num ); // 100 if(x < num){ console.log( x ); //30 } } fn3(30) }(fn1);
閉包的應用場景
一.點擊li標籤,出現對應的索引
<ul id="ul1"> <li>你好我是循環閉包中的第0個</li> <li>你好我是循環閉包中的第1個</li> <li>你好我是循環閉包中的第2個</li> <li>你好我是循環閉包中的第3個</li> <li>你好我是循環閉包中的第4個</li> </ul>
1.js中添加索引的作法
var ul1 = document.getElementById("ul1"); for(var i = 0; i < ul1.children.length; i++){ ul1.children[i].setAttribute("index",i); ul1.children[i].onclick = function () { console.log(this.getAttribute("index")) } };
2.jQuery中index作法
$(function () { var index = null; $("li").on("click",function () { index = $(this).index(); console.log(index); }); })
3.閉包的處理
var ul1 = document.getElementById("ul1"); //錯誤的作法 function showLiNum(ul1) { for(var i = 0; i < ul1.children.length; i++){ ul1.children[i].onclick = function () { console.log(i); } } } showLiNum(ul1) //當點擊以前,循環已結束;因此i的值爲別.children.length;
正解:利用一個閉包,創建一個匿名函數;將每一個i存在內存中.onclick函數用的時候提取出外部匿名函數的值
function showLiNum(ul1) { for(var i = 0; i < ul1.children.length; i++){ (function (i) { ul1.children[i].onclick = function () { console.log(i); } }(i)); } } showLiNum(ul1)
也能夠這樣
function showLiNum(ul1) { for(var i = 0; i < ul1.children.length; i++){ ul1.children[i].onclick = (function (i) { return function () { console.log(i); } }(i)); } } showLiNum(ul1);
二.定時器
開啓定時器,分別打印1,2,3,4,5
錯誤的寫法:
for(var i = 1; i <= 5; i++){ setTimeout(function () { console.log(i); },1000) }
正確寫法
for(var i = 1; i <= 5; i++){ (function (i) { setTimeout(function () { console.log(i); },i*1000); }(i)) } //至關於同時啓動3個定時器,i*1000是爲5個定時器分別設置了不一樣的時間,同時啓動, //可是執行時間不一樣,每一個定時器間隔都是1000毫秒,實現了每隔1000毫秒就執行一次打印的效果。
經典面試題
最後上一道閉包中的經典面試題,若是你能弄懂這個,那說明閉包弄懂的差很少啦
var name = "The Window"; var obj = { name : "The object", getNameFunc : function(){ return function(){ return this.name; } } } alert( obj. getNameFunc()() ) var name = "The Window"; var obj = { name : "The object", getNameFunc : function(){ var that = this; return function(){ return that.name; } } } alert( obj. getNameFunc()() )
javascript是動態(或者動態類型)語言,this關鍵字在執行的時候才能肯定是誰。因此this永遠指向調用者,即對'調用對象'的引用; "誰調用,指向誰"。第一部分執行代碼object.getNameFunc()以後,返回了一個新的匿名函數,此時在全局做用域調用匿名函數,它不在是object的屬性或者方法,此時調用者是window,所以輸出是 The Window。第二部分,當執行函數object.getNameFunc()後返回的是:function( ){return that.name;}此時的that=this。而this指向object,因此that指向object。他是對object的引用,因此輸出The Object。總結:關於js中的this,記住誰調用,this就指向誰;要訪問閉包的this,要定義個變量緩存下來。通常var that(_this) = this。