JavaScript的閉包及函數重載

JavaScript的閉包及函數重載

閉包的概念

什麼是閉包

說到JavaScript的閉包,須要先說一說JavaScript的做用域。javascript

JavaScript在ECMA6以前,做用域是隻有全局做用域跟函數做用域的。(這裏先不涉及let和const)html

函數內部能夠讀取器外部做用域定義的變量,而外部做用域不能直接讀取到函數中定義的變量。java

eg:閉包

var n = 0;
function f1() {
	console.log(n); // 0
}

f1();

function f2() {
	var a = 1;
}
console.lot(a); // throw error .... undefind
複製代碼

上述例子中,函數f1在運行時能夠訪問到全局做用域定義的變量n。而全局做用域中沒法訪問到函數f2中定義的變量a。app

那麼還有一個例子:函數

var n = 0;
function f1() {
	console.log(n); // 0

	var a = 1;
	function f2() {
		console.log(n); // 0
		console.log(a); // 1
	}

	f2();
}

f1();
複製代碼

上述例子中的,函數f1中定義了函數f2,f2可以訪問到f1中定義的變量a以及全局做用域中定義的變量n。那麼,這能夠說明,更裏層的函數可以訪問到更外層的做用域中定義的變量。而更外層的代碼,沒法訪問到更裏層的函數做用域定義的變量。這是JavaScript鏈式做用域的做用。oop

那麼,若是咱們有須要在全局做用域中訪問到函數做用域中定義的變量,那應該怎麼作呢?學習

咱們發現函數f2是可以訪問到f1中定義的變量的。那麼將他返回到全局做用域:ui

function f1() {
	var a = 1;
	
	return function f2() {
		console.log(a);
	}
}

var function2 = f1();
function2(); // 1
複製代碼

那麼,上述的函數f2,就是一個閉包。this

閉包是用於讀取其餘函數內部變量的函數(取自@阮一峯老師的博客)

閉包的特性

1.閉包能夠讀取其餘函數內部變量。

這個特性這裏再也不贅述

2.閉包會將函數內部的局部變量存儲在內存中

如上一節最後一個例子,函數f2將變量a保存在內存中,而後在被調用時,就可以訪問到了。

那麼,利用這個特性,咱們能夠作一個相似於計數器的東西:

eg:

function counter() {
	var a = 1;
	
	return function f1() {
		return a++;
	}
}

var counter1 = f1();
counter1(); // 1
counter1(); // 2
counter1(); // 3
複製代碼

這個特性也會致使一個問題,就是若是濫用閉包的話,內存消耗會比較多。

利用閉包來定義私有變量

利用閉包的特性,咱們能夠利用閉包作私有變量:

var ClassA = function () {
	var a = 0;
	this.getA = function() {
		return a;
	}
	this.setA = function(val) {
		a = val;
	}
}

var objectA = new ClassA();
console.log(objectA.getA()); // 0
objectA.setA(111);
console.log(objectA.getA()); // 111
objectA.a; // error
複製代碼

這裏的a變量只能被ClassA以及getA和setA訪問到。

利用閉包作函數重載

最近學習到了一個在JavaScript中實現函數重載的方法,感受比較有趣。

衆所周知JavaScript的函數的參數是不固定的,因此沒辦法作函數重載。若是要實現相似於函數重載的方法,比較日常的作法是使用if-elseswitch來判斷參數,而後根據傳入的參數不一樣,輸出不一樣的結果。

而最近學習了一個,利用閉包處理的比較乾淨一些的作法。

1.準備階段先擼一下須要存放重載函數的類/對象

var object = {};
複製代碼

2.定義須要重載的函數。

function sum0(val0) {
	return val0;
}

function sum1(val0, val1) {
	return val0 + val1;
}

function sum2(val0, val1, val2) {
	return val0 + val1 + val2;
}
複製代碼

3.而後定義裝載重載函數的函數(核心):

function addMethod(object, name, f) {
	// 1. 從object中獲取名字爲name的函數做爲old備用
	var old = object[name];
    // 2. 將object[name]定義爲一個新的函數
    object[name] = function() {
        // 3. f.length爲函數定義時的參數個數
        // arguments.length爲函數調用時的參數個數
        // 若是調用的函數的形參和實參一致,則直接調用。不然,調用old函數
        if (f.length === arguments.length) {  
            return f.apply(this, arguments);    
        } else if (typeof old === "function") {
            return old.apply(this, arguments);    
        }  
    };
}
複製代碼

4.裝載函數:

addMethod(object, "sum", sum0);
addMethod(object, "sum", sum1);
addMethod(object, "sum", sum2);

console.log(object.sum(1)); // 1
console.log(object.sum(1, 2)); // 3
console.log(object.sum(1, 2, 3)); // 5
複製代碼

上述代碼使用old存儲上一次調用addMethod方法加入的函數。而後經過閉包存儲在內存中。使得每次調用add函數時,出現參數不等的狀況時可以調用到old,以查找到上一個add進來的函數。最終達到函數重載的目的。


參考:阮一峯老師的JavaScript教程

相關文章
相關標籤/搜索