一篇文章把你帶入到JavaScript中的閉包與高級函數

file

在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;
}

利用閉包進行緩存:

file
file

閉包的做用

封裝變量,閉包能夠封裝造成私有變量:

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);
}

file

'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設計模式,由於它是核心


意見反饋:
若本號內容有作得不到位的地方(好比:涉及版權或其餘問題),請及時聯繫咱們進行整改便可,會在第一時間進行處理。


感謝閱讀,原創不易,喜歡就點個贊吧,這是我寫做最大的動力。

歡迎關注達達的簡書!

這是一個有質量,有態度的博客

博客

相關文章
相關標籤/搜索