關於函數,能夠從如下3個方面去理解:
首先,數據類型上看:函數在JavaScript中是一種數據類型,是對象的一種;
其次,從功能上看:函數本質上是一段反覆調用的代碼塊;
最後,從地位上看:函數在JavaScript中和其餘基本數據類型同樣,能夠做爲參數和賦值,是「第一等公民」javascript
聲明函數的方式有三種:java
1.聲明式 function fn(){ console.log(1) } fn()//1
2.表達式 var fn = function(args){ console.log(args) } fn(a)//a
3.構造函數式 var fn = new Function("arg1","arg2","return arg1+arg2") fn(1,2)//3
函數本質上是對象的一種,因此函數名保存的其實是指向函數對象的指針,第3種構造函數的方法對於理解「函數是對象,函數名是指針」的概念更加直觀,可是第3種方法解析效率較低並且書寫不簡潔,因此通常不用構造函數方法去聲明函數;
當聲明式和表達式同時聲明同一個函數時,表達式會覆蓋聲明式,緣由是函數做爲變量在js解析階段進行變量提高,聲明式和表達式都會提高至當前做用域頭部,而後表達式會從新爲fn賦值,從而覆蓋聲明式定義的函數;數組
function fn(){ console.log(1) } var fn = function(){ console.log(2) } fn()//2
關於return語句的理解,若是聲明的函數沒有return語句,則默認return undefined,不然返回定義的值;瀏覽器
var a = function(){ console.log(1) } var b = function(){ return 2; } a() === undefined//true
函數做爲一個對象,一樣擁有屬性和方法,下面主要概括一下比較經常使用和重要的屬性和方法:閉包
name屬性:返回該函數名的字符串;app
length屬性:返回形參的個數,即預期傳入參數的個數;函數
function fn (a,b){ console.log(fn.name) console.log(fn.length) } fn() //"fn" //2
arguments對象:是包含傳入函數實參的類數組對象,只有在函數執行階段而且存在參數纔會有值,未調用函數是爲null
;this
function fn (a,b){ console.log(arguments) } fn(1,2) //[1,2]
arguments對象的length屬性表明實參的個數,注意和函數的length屬性的區別,函數的length表明形參的個數,arguments的length屬性表明實參個數;
arguments對象有一個callee屬性,返回arguments對象所在的函數指針;
能夠利用callee實現函數的遞歸,例如累加或階乘操做:spa
function increment(arg){ if(arg === 1){ return 1 } return arg+arguments.callee(arg-1) } function increMultipler(arg){ if(arg === 1){ return 1 } return arg*arguments.callee(arg-1) }
這裏另外提一個函數的caller屬性,該屬性保存調用當前函數的函數的引用,注意的是若是在全局做用域下讀取該屬性,值爲null,由於頂層對象在瀏覽器中爲window不是函數;prototype
function outer(){ inner(); } function inner(){ console.log(arguments.callee.caller) } outer();
this對象:表明函數執行時的環境對象,簡單的說就是誰調用了該函數,this的指向是動態的,只有在函數調用時this對象才能肯定;
//在瀏覽器全局環境下,即window對象下 var print = function(){ console.log(this) } print()//this指向Window,由於這是Window對象調用了print方法 //在特定對象的環境下 var o = { print: function(){ console.log(this) } } o.print()//this指向o,由於這是o對象調用print方法
函數提供call
、apply
和bind
3種方法能夠改變this對象;
1.call方法 function fn(){ return this } var o = {} fn() === this//true,this指向window對象 fn.call(o) === o//true,this指向o對象 //call方法還能夠傳入參數; function add(x,y){ return x+y } add.call(null,1,2)
2.apply方法,與call不一樣的是apply傳入的參數爲數組 var arr = [1,2] function add(x,y){ return x+y } add.apply(null,arr)
實際上,apply和call的區別只在於傳遞參數的不一樣,它們真正強大的地方在於可以擴充函數賴以運行的做用域,好比slice函數本來只存在於數組當中,當中經過使用call方法,能夠實現不一樣做用域下調用該方法;
function fn(a,b){ console.log(Array.prototype.slice.call(arguments)) } fn(1,2)//[1,2]
bind方法會建立一個函數實例,並將該函數的this對象綁定到傳入該方法的參數;
function fn(){ return this } var o = {} var newFn =fn.bind(o) newFn() === o//true
做用域指的是變量存在的範圍,做用域可分爲全局做用域和局部做用域,變量在全局範圍可訪問到;局部做用域由函數所構造,變量只能在函數內部可訪問到;
var a =1//a處於全局做用域 function fn(){ var a = 2//a處於局部做用域,外部沒法訪問; return a; } a//1 fn()//2
值的注意的是,函數執行時所在的做用域是定義時所在的做用域,而不是調用時所在的做用域;
var a = 1; function fn(){ console.log(a) } function fn2(){ var a = 2; fn() } fn2()//1
關於閉包的知識點,將會單獨開一章節詳談,具體請看《JavaScript閉包(三)》
經過《JavaScript函數(二)》,咱們大體瞭解關於函數的知識點以下:
函數本質上是一段反覆調用的代碼塊,是對象的一種,在js中做爲「第一等公民」,能夠賦值和傳參;
函數聲明的方法有3種:聲明式、表達式和構造函數式;其中構造函數是可以直觀理解函數的「函數是對象,函數名是指針」的概念;當表達式和聲明式同時聲明同名函數時,表達式會覆蓋聲明式,緣由是變量提高的做用;
函數做爲對象,一樣具備屬性和方法;name
返回該函數名的字符串,length返回形參的個數;
arguments對象是包含傳入函數的實參的類數組對象,只有在執行階段該對象纔有值,未調用函數時爲null
,arguments.length表示實參的個數,arguments.callee返回arguments對象所在的函數的指針;
函數的caller返回調用當前函數的函數的指針,在全局做用域下讀取該屬性爲null;
this對象表明當前函數執行時的環境對象,this對象只有在函數執行階段才能肯定;
可使用call、apply和bind改變this的指向;call和apply的區別在於兩者傳遞的參數不一樣,call爲零散的數據,apply爲數組,兩者最大的用處是擴展函數的做用域;bind方法能夠返回一個函數的實例,並綁定this對象至傳入bind的參數;
函數能夠開闢一個獨立的做用域,這使得js當中經典的閉包得以實現提供可能性;此外,函數執行時所在的做用域是在定義時所在的做用域,而不是調用時所在的做用域;
《JavaScript高級程序設計(第3版)》