在JavaScript中,函數是一等公民。JavaScript是一門面向對象的編程語言,可是同時也有不少函數式編程的特性,如Lambda表達式,閉包,高階函數等,函數式編程時一種編程範式。前端
function dada() { var a = 1; var b = function() { console.log(a); } return b // b 就是一個閉包函數,由於它能訪問dada函數的做用域 }
JavaScript的函數也是對象,能夠有屬性,能夠賦值給一個變量,能夠放在數組裏做爲元素,能夠做爲其餘對象的屬性,什麼均可以作,別的對象能作的它也能作,別的對象不能作的它也能作。ajax
函數和其餘普通對象來講,是同樣的,有屬性有方法,普通對象能作的,函數也能作。學習JavaScript中的閉包和高級函數是基礎篇哦!算法
那麼什麼是閉包?閉包,就是有權訪問其外部做用域中的變量和參數的函數。編程
var func = (function() { var item = 0; return { add: function(num) { item += typeof num === 'number' ? num : 1; }, value: function() { return item; } } })();
閉包函數能夠訪問它建立時所處的上下文環境中的變量以及參數,this以及arguments除外。segmentfault
閉包:設計模式
函數做爲返回值,高階函數除了能夠接受函數做爲參數外,還能夠把函數做爲結果值返回。閉包的造成與變量的做用於和變量的生命週期密切相關。數組
變量做用域:緩存
var func = function() { var a = 1; console.log(a); // 1 } func(); console.log(a); // Uncaught ReferenceError: a is not defined
閉包是一個能夠訪問到其餘函數內部變量的函數,閉包的重點在於,變量的做用域,和,變量的生命週期。網絡
變量的做用域數據結構
// 變量的做用域 var my = function() { var a = 1; console.log('my', a); // 1 } my(); console.log(a) // ReferenceError: a is not defined
變量的聲明週期
// 變量的生命週期 let fu = function() { let a = 0; a++; console.log('dada', a); } fu(); // 1 fu(); // 1 fu(); // 1 let func = function() { let a = 0; return function() { a++; console.log('a', a); } } let fun = func() fun(); // 1 fun(); // 2 fun(); // 3
閉包中的變量沒有被銷燬,這個涉及到垃圾回收機制,即標記清楚和引用計數
function num() { for(var i=0; i< 10; i++) { setTimeout(function() { console.log('da', i) // 10 },0) } } num(); function num1() { for(var i=0; i< 10; i++) { (function (i) { setTimeout(function() { console.log('da', i) },0) })(i) // 1,2,3,...,10 } } num1()
什麼是閉包的例子:
function da() { var a = 1; function dada() { console.log(a); } return dada } var d = da(); d(); // 1
閉包若是不是那麼必要,請不要去建立它,因閉包在處理速度和內存消耗方面對性能具備負面影響。
閉包的形式與變量的做用域以及變量的生存週期有着密切的相關性。
變量的做用域:
變量的做用域指的是變量的有效範圍,當一個函數中聲明的一個變量不帶上關鍵字var的時候,這個變量就成爲了全局變量,當這個變量用var聲明的時候,這個變量就變成了局部變量,只有在函數內部才能訪問到這個變量,在函數外面是訪問不到的。舉例:
var func = function() { var a = 1; alert(a); // 1 }; func(); alert(a); // Uncaught ReferenceError: a is not defined
嵌套例子:
var a = 1; var fun = function() { var b = 2; var func1 = function() { var c = 3; alert(b); // 2 alert(a); // 1 } func1(); alert(c); // c is not defined }; fun();
變量的生命週期:
閉包的又一重要概念,變量的生命週期,對於全局變量的生命週期來講是永久的,對於函數內部的局部變量來講,是短暫的,它們都會隨着調用的結束而被銷燬。
var func = function() { var a = 1; }; func();
var func = function() { var a = 1; return function() { a++; alert(a); } }; var da = func(); da(); // 2 da(); // 3 da(); // 4
閉包
函數做爲返回值
function num(arr) { return arr.reduce(function (x,y) { return x + y; }); } num([1,2,3]); // 6
變成函數
function func(arr) { var sum = function() { return arr.reduce(function(x,y) { return x + y; }); } return sum; } // 調用 函數 var da = func([1,2,3]); // 調用函數 // 運行 da(); // 6
var da1 = func([1,2]); var da2 = func([1,2]); da1 == da2 // false
每次調用返回的都是一個新的函數
利用閉包進行緩存:
function add(a) { return a + 1; }
利用閉包進行緩存:
閉包的做用
封裝變量,閉包能夠封裝造成私有變量:
var da = function() { var a = 1; for (var i=0; i < arguments.length; i++){ a = a * arguments[i]; } return a; } alert(a(1,2,3));
在JavaScript中是沒有塊級做用域的概念的:
function add() { var arr = [ 1,2,3 ]; for (var i=0; i < arr.length; i++) { alert( arr[i]); } var i; // 從新聲明變量 alert(i); }
塊級做用域效果,閉包:
(function() { // 塊級做用域 })()
延續局部變量的生命週期:
var myImg = function( src ) { var img = new Image(0; img.src = src; }; myImg('http:///...');
解決請求丟失問題:
var da = (function() { var imgs = []; return function(src) { var img = new Image(); imgs.push(img); img.src = src; } })();
閉包是指有權訪問另外一個函數做用域中變量的函數。
閉包:函數對象能夠經過做用域關聯起來,函數體內的變量均可以保存在函數做用域內。
詞法做用域:做用域是在編寫代碼的時候肯定的
動態做用域:做用域是在代碼運行的時候肯定的
<script> function add(num){ var sum = 5; return sum + num; } var sum = add(4); </script>
Execution Contexts = { variable object:變量對象; this value: this指針; scope chain:做用域鏈; }
全局變量
function myFunction() { var a = 4; return a * a; }
var a = 4; function myFunction() { return a * a; }
var counter = 0; function add() { return counter += 1; } add(); add(); add(); // 計數器如今爲 3
function add() { var counter = 0; return counter += 1; } add(); add(); add(); // 本意是想輸出 3, 但事與願違,輸出的都是 1 !
function outter(){ var sky="blue"; function inner(){ console.log(sky); } return inner; } var result=outter(); result(); //"blue"
函數與對其狀態即爲詞法環境的引用共同構成閉包,也就是,閉包可讓你從內部函數訪問外部函數做用域。
詞法做用域:
function init() { var name = "dada"; // name 是一個被 init 建立的局部變量 function displayName() { // displayName() 是內部函數,一個閉包 alert(name); // 使用了父函數中聲明的變量 } displayName(); } init();
閉包:
function mFunc() { var name = "dada"; function displayName() { alert(name); } return displayName; } var myFunc = mFunc(); myFunc();
高階函數
什麼是高階函數,JavaScript中的函數都指向某個變量,既然變量能夠指向函數,函數的參數能接收變量,那麼一個函數就能夠接收另外一個函數做爲參數,就叫高階函數。
高級函數:
function add(x, y, f) { return f(x) + f(y); }
'use strict'; function pow(x) { return x * x; } var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9]; var results = arr.map(pow); // [1, 4, 9, 16, 25, 36, 49, 64, 81] console.log(results);
var f = function (x) { return x * x; }; var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9]; var result = []; for (var i=0; i<arr.length; i++) { result.push(f(arr[i])); }
var arr = [1, 3, 5, 7, 9]; arr.reduce(function (x, y) { return x + y; }); // 25
var arr = [1, 3, 5, 7, 9]; arr.reduce(function (x, y) { return x * 10 + y; }); // 13579
在一個數組中,刪除偶數,保留奇數:
var arr = [1, 2, 4, 5, 6, 9, 10, 15]; var r = arr.filter(function (x) { return x % 2 !== 0; }); r; // [1, 5, 9, 15]
回調函數
var arr = ['A', 'B', 'C']; var r = arr.filter(function (element, index, self) { console.log(element); // 依次打印'A', 'B', 'C' console.log(index); // 依次打印0, 1, 2 console.log(self); // self就是變量arr return true; });
'use strict'; var arr = [10, 20, 1, 2];
arr.sort(function (x, y) { if (x < y) { return -1; } if (x > y) { return 1; } return 0; }); console.log(arr); // [1, 2, 10, 20]
var a1 = ['B', 'A', 'C']; var a2 = a1.sort(); a1; // ['A', 'B', 'C'] a2; // ['A', 'B', 'C'] a1 === a2; // true, a1和a2是同一對象
every()方法能夠判斷數組的全部元素是否知足測試條件
find()方法用於查找符合條件的第一個元素
findIndex()方法返回這個元素的索引
高階函數即爲輸入參數裏有函數,或是輸出是函數的函數
function add() { var num = 0 return function(a) { return num = num + a } } var adder = add() adder(1) // 輸出: 1 adder(2) // 輸出: 3
高階函數知足條件:函數做爲參數被傳遞,函數做爲返回值輸出
回調函數:
var getUserInfo = function( userId, callback ){ $.ajax( 'http://xxx.com/getUserInfo?' + userId, function( data ){ if ( typeof callback === 'function' ){ callback( data ); } }); } getUserInfo( 522624714, function( data ){ alert ( data.userName ); });
//從小到大排列 [ 1, 5, 3 ].sort( function( a, b ){ return a - b; }); // 輸出: [ 1, 3, 5 ] //從大到小排列 [ 1, 5, 3 ].sort( function( a, b ){ return b - a; }); // 輸出: [ 5, 3, 1 ]
什麼是函數式編程,函數式編程時一種編程形式,讓你能將函數做爲參數傳遞給其餘函數而且可以將函數做爲值返回。
在JavaScript中,函數是一類特殊的對象:
function hello() { console.log('hello'); } hello();
hello.name='da'; console.log(hello.name); // da
const num = function(x) { return x*x; } num(8); // 64
高階函數是一個函數,它是接收函數做爲參數或者是將函數做爲輸出的值進行返回。
高階函數實戰:
const arr1 = [1,2,3]; const arr2 = arr1.map(function(x) { return x * 2; }); console.log(arr2);
const arr1 = [1,2,3]; const arr2 = arr1.map(x => x*2);
結語
簡而言之,高階函數是一個函數,它是能夠接受函數做爲參數,還能夠做爲返回一個值返回,返回一個函數。閉包可讓你從內部函數訪問外部函數做用域。閉包便是一個函數,可以訪問另外一個函數做用域的變量的函數。
關於目前文章內容即涉及前端,PHP知識點,若是有興趣便可關注,很榮幸,能被您發現,真是慧眼識英!也感謝您的關注,在將來的日子裏,但願可以一直默默的支持我,我也會努力寫出更多優秀的做品。咱們一塊兒成長,從零基礎學編程,將 Web前端領域、數據結構與算法、網絡原理等通俗易懂的呈現給小夥伴。分享 Web 前端相關的技術文章、工具資源、精選課程、熱點資訊。
推薦閱讀
一、你知道多少this,new,bind,call,apply?那我告訴你
二、爲何學習JavaScript設計模式,由於它是核心
意見反饋:
若本號內容有作得不到位的地方(好比:涉及版權或其餘問題),請及時聯繫咱們進行整改便可,會在第一時間進行處理。
感謝閱讀,原創不易,喜歡就點個贊吧,這是我寫做最大的動力。
這是一個有質量,有態度的博客