原文: http://pij.robinqu.me/JavaScript_Core/JavaScript_Basics/Function.htmljavascript
本文存在批註,但該網站的Markdown編輯器不支持,因此沒法正常展現,請到原文參考。java
Javascript中,要記住函數是first-class citizen。node
函數聲明語句git
function plus(x ,y) { }
函數定義表達式github
var plus = function (x, y) { }
做爲函數調用數組
function a(){}; a();
做爲方法調用瀏覽器
a={}; a.x = function(){}; a.x();
經過call和apply間接調用函數(改變this)閉包
call 和 apply帶有多個參數,call和apply把當前函數的this指向第一個參數給定的函數或對象中,並傳遞其他全部的參數做爲當前函數的參數。app
var O = function () { this.foo = 'hello'; this.hello = function () { return 'world'; } }; var fn = function () { console.log('call', this); }; var o = new O(); fn.call(o);//此時fn的this指向o
call和apply的不一樣之處,在於call傳遞的參數是做爲arguments依次傳入的,例如
fn.call(o, 1, 2, 3);
而apply傳遞的參數是以一個數組的方式傳入的,例如fn.apply(o, [1, 2, 3]);
當傳入參數少於函數聲明的參數時,留空的參數的值是undefined
。
Javascript容許傳入參數的個數大於聲明時制定的參數個數。能夠用arguments
來訪問這些參數
function f(){ var i; for( i = 0; i < arguments.length ; i++) { console.log(arguments[i]); } } f(1,2,3,4,5,6);
函數經過取得arguments的長度獲得傳入參數的個數,使用一個循環獲取每個參數。
arguments還有兩個屬性,callee
和caller
callee
表示正在執行的function對象,caller
表示調用當前function的function
例如
function f(){ console.log(arguments.callee);//[Function: f] console.log(arguments.callee.caller);[Function: g] var i; for( i = 0; i < arguments.length ; i++) { console.log(arguments[i]); } } function g(){ f(1,2,3,4,5,6); } g();
callee
的重要用法之一是在匿名函數中實現遞歸
var result = function (x) { if (x <= 1) return 1; return x * arguments.callee(x - 1); }(3); console.log(result);
上例使用了一個匿名函數和callee實現了一個階乘。
javascript中的函數能夠做爲值來傳遞
function square(x) { return x * x; } var s = square; s(4);
(function() { }());
Javascript函數對象的內部狀態不只包含着函數的代碼邏輯,還引用當前的做用域鏈。函數對象經過做用域鏈相互關聯起來,函數體內部變量包含在函數做用域內,這就叫閉包。
例如
var scope = 'global scope'; function checkscope() { var scope = 'local scope'; function f() { return scope; } return f; } checkscope()();
這段checkscope聲明瞭一個局部變量,定義了一個函數f,函數f返回了這個局部變量的值,最後返回了這個函數f。在定義函數f的做用域外調用f,獲得的返回仍然是函數f建立時所在的做用域的局部變量scope。
又例如
var counter = (function() { var count = 0; return function () { return count++ ; } }());
代碼定義了一個當即執行函數並返回給counter,這個函數定義了一個局部變量count,返回了一個子函數,該子函數每次調用,都會吧count加一併返回。
閉包的注意事項
觀察下面的示例:
var add_the_handlers = function (nodes) { var i; for (i = 0; i < nodes.length; i += 1) { nodes[i].onclick = function (e) { alert(i); }; } };
這個函數指望的結果,是在運行的時候爲每一個node在onclick的時候alert出各自的序號,可是實際運行的結果卻不一樣:全部的node在單擊的時候alert出來的數字老是同一個。
這是由於alert所在的匿名函數的閉包中存放的i是第一行的i,而不是在循環中得到的i的當前值。
因此若是但願達到預期結果,應該在循環中建立多個閉包,在閉包中存放當前循環的i的值:
var add_the_handlers = function (nodes) { var i; for (i = 0; i < nodes.length; i += 1) { nodes[i].onclick = function (i) { return function(e){ alert(e); }; }(i); } };
這裏使用一個當即執行函數並傳遞當前的i的值,返回一個新生成的函數。在這個新生成的函數的閉包中就保存了當前的i的值。
在一個對象中的this始終引用當前對象,可是在函數中,特別是在閉包中,this有一些特殊的行爲。
函數中的this對象始終綁定在函數運行時的上下文環境上。因此在普通模式下調用一個全局函數,this始終指向window(客戶端),在嚴格模式下調用一個全局函數,this始終是undefined
示例
var name = "The Window"; var object = { name: "My Object", getNameFunc: function () { return function () { return this.name; }; }, getName : function () { return this.name; } }; console.log(object.getNameFunc()()); console.log(object.getName());
getNameFunction()返回了一個匿名函數,這個匿名函數在調用的時候,上下文是window(瀏覽器中),因此在瀏覽器中輸出的是the Window
而getName()調用的時候上下文是object,因此成功輸出object的name
其實以上代碼中
object.getNameFunc()()
等效於
var fnc = object.getNameFunc();//這時候的fnc已經脫離了object對象
fnc();
因此若是想要getNameFunction()正確返回Object的Name,須要在返回的匿名函數的閉包中保存在函數聲明時的this,
getNameFunc: function () { var that = this; return function () { return that.name; }; },
這樣就能夠了。。
函數柯里化是指,把接受多個參數的函數轉換成接受一個單一參數的函數,而且返回接受餘下的參數並且返回結果的新函數的技術。
示例
var add1 = add.curry(1); console.log(add1(2));
其中,add是接受兩個參數的函數,add調用了curry返回一個只接受一個參數的新函數,以後調用add1便等效於調用add(1, 2);
javascript並不原生支持curry,能夠用prototype來模擬
Function.prototype.curry = function () { var slice = Array.prototype.slice, args = slice.apply(arguments), that = this; return function () { return that.apply(null, args.concat(slice.apply(arguments))); }; }; function add(n1, n2) { return n1 + n2; } var add1 = add.curry(1); console.log(add1(2));
curry建立了一個新函數,在新函數的閉包中保存了原先傳遞的參數。
length
函數的length表示函數實參的數量,是隻讀的prototype
指向一個該函數的原型對象的引用toString
返回一個字符串