JavaScript 中函數的應用十分的廣泛,如果你仔細研究過JQuery等庫的話,你會發現
基本JQuery所提供的庫,都是可以接收一個函數作為傳入參數,達到更靈活的目的。
0x00 函數的主要用途
在JS中,函數的用途無外乎以下集中:
- 作為函數調用
- 作為方法調用
- 作為構造器調用
- 指定上下文調用
作為函數和方法調用的,是比較熟悉的。如果直接定義的情況下,我們認為是做為函數調用,而將一個函數具體賦給了一個對象后,我們稱之為做為方法調用。
但是,這二者之間的區別是可以被模糊掉的。例如,我們在全局環境下聲明一個函數,其實是可以看作是在window
對象上面定義了一個方法。因為從上下文的角度來講,函數的的上下文
是不可或缺的。因此,一個函數調用,其實就是一個賦予它上下文執行的過程。
作為構造器調用的過程,其實就是用一個函數聲稱一個對象。這個函數的額作用就是返回一個對象。如下面的一個構造器:
function Person() {
this.age = 22;
this.say = function() {
return "hello";
};
}
var p1 = new Person();
這里特別的就是 new 關鍵字,這關鍵字其實就是創建一個空對象,將這個空對象作為函數的上下文傳遞給函數,如果沒有顯示的返回值,則將此對象返回。這點,有過其他面向對
象編程經驗的,自然不言自明。
指定上下文的調用的過程,就是用到了函數的兩個重要方法,apply()
和call()
這兩個方法的作用是一樣的,都是給函數指定一個上下文,然后調用它。這種機制,在很多支持事件回
調的函數中,都可以看到。
0x01 匿名函數
匿名函數 顧名思義,就是沒有名字的函數。我們來看兩種函數的聲明定義:
function foo1() {
return "123";
}
var foo2 = function() {
return "456";
};
foo1();
foo2();
這兩種看起來沒有什么太大的差異,但是如果你直接通過函數名調用,你會發現不一樣的結果:
>foo1
< function foo1(){...};
>foo2
< function(){...};
第二個函數的用法,就稱之為匿名函數。他沒有直接給于一個名字,就是沒有直接給定上下文。而是通過賦值給某個變量,對象的屬性,來指定一個匿名函數上下文。而有名函數的聲明過
程,就是在上下文中綁定了一個這個函數名的屬性。上例子中,我們可以通
過window
調用也是一樣的:
window.foo1
0x02 即時函數
有經驗的老司機同志,一定見過這樣的函數調用方式:
(function(){})();
而這樣的一行代碼,就可以將一個函數執行。這里特別注意的是兩個括號,可以精剪為:
(...)();
第一個括號的,接收的是一個函數,可以是一個匿名函數,也可以是一個已經聲明的函數的名字或者調用。第二個括號里面標識的是注入參數,即我們在第一個括號中的函數的參數,可以
通過第二個括號來注入。如:
(function(a,b) {
return a+b;
})(1,2);
// out 3
這段代碼的作用其實就是如下的四個步驟:
- 創建一個函數實例
- 執行該函數
- 銷毀該函數(應為沒有引用這個函數的引用了)
題,完成之后,便可以銷毀,避免了全局環境被污染的問題。
看下面這段代碼:
(function(app) {
app.AppComponent =
ng.core.Component({
selector: 'my-app',
template: '<h1>My First Angular 2 App</h1>'
})
.Class({
constructor: function() {}
});
})(window.app || (window.app = {}));
靈活的控制全局環境的生成。
0x03 遞歸函數
遞歸是需要結果很多重復操作的選擇,性能方面的我們暫且不說,但是從另一個角度來說
,至少代碼
夠簡練。但是,如果沒遇到一種情況:
obj.foo = function(){};
我們要在這個函數的內部,進行遞歸調用,誠然我們直接通過在方法的內部調用:
obj.foo = function(){
obj.foo();
};
這樣的方式,在這個函數的上下文沒有被改動的情況下,是可行的。當然我們進一步,采
用this
:
obj.foo = function(){
this.foo();
};
這樣的情況,要求每個調用的這個函數的方法屬性都叫做foo
,萬一名字變了,又是個
坑了。那么,
其實JavaScript支持一種內連函數的聲明方式,如下:
obj.foo = function a(){
a();
};
給了函數一個名字,就不再是匿名函數了,即時是上下文和調用名字變了,依然可以安心的遞歸下去。
注意:值得注意的是,函數的
arguments
對象中,有一個叫callee
的屬性,這個
屬性就是用來指代函數本身的,不過這個函數是有名還是匿名。但是這個屬性有被取消的
趨勢,并且在嚴格模式下已經是不可用的了。
0x04 函數即對象?
JavaScript中,函數是作為第一型對象存在的。這既是一種函數式編程的方式,又是一
種面向對象的方式,所以很多原教旨主義者,對此十分反感。但是我認為,真是結合了
這兩者形式,才是功能強大的原因。
JavaScript中,既有代表對象的原型對象Object
又有代表函數的Function
原型對象。函數既是對象,
或者對象既是函數,這只不過是你從不同的角度來看待這件事情罷了。
0x05 總結
JavaScript一直被處在一種被忽視的地位,因為各種庫的存在,很多人執著于庫,是用JQuery
還是TypeScript
,
是用AngularJS
還是React
?其實,我覺得這些都可以,一個庫的存在,之所以有很多人用,你說有很致命的bug,
不能說不可能,只是可能性很小。而很多人卻忽視了JavaScript本身的問題。包括,google也是,angularJS2.0的版本,
更加偏向于TypeScript去了。當然,JavaScript有許多被人詬病的問題。但是,它畢竟是現在最重要的前端語言,(額...
node派請不要出來打臉),了解其被背后的機制,是作為一個程序員所必要的,如果你要使用到它的話。
JavaScript我認為最重要的幾個特性,就是:
- 函數
- 對象
- 正則表達式
- 定時器
這四者之間,相互交叉那是當然的,而我認為,這四者之中,最基礎的是函數。正則雖然有些不同,當在JavaScript,中正則
是屬于特殊對象的范疇。在應用過程中,使用到函數的那是必不可少的。所以...
函數為基。
我的blog原址: