我在上一篇【javascript基礎】基本概念中介紹了javascript的一些基本概念,多謝你們的閱讀和意見,本身寫的東西能夠被你們閱讀,真心高興,剛開始發佈的時候我一直盯着閱讀人數,雖然知道你們可能就是點開一下而已,可是仍是給我一些繼續寫下去的信心。那今天寫一些關於javascript函數的一些知識,幫助你們熟悉或者複習一些函數的基本知識。javascript
PS:最近jQuery源碼交流羣( 239147101)加了很多熱新人,但願你們仍是以學習爲主,儘可能少灌水,給你們一個好的提高本身的環境。html
函數在任何一種編程語言中都是一個很重要的結構或者組成部分,編程中的複雜結構和功能都會有函數的參與。javascript中的函數是一個對象,函數對象時Function類型的實例,因爲Function類型是一個引用類型,那麼函數能夠擁有本身的方法和屬性,同時也由於函數是一個對象,那麼函數名是一個指向函數對象的指針,能夠被賦值。下面詳細介紹函數的各個部分。java
函數的建立有三種方式,分別爲使用Function的構造函數、函數聲明、函數表達式,下面分別介紹這三種方法。面試
這種方式是直接new出來一個Function 實例,經過使用Function的構造函數進行建立函數。Function構造函數能夠接收任意多個參數,可是最後一個參數會被認爲是函數體,前面的因此參數被當作被建立出來的函數的參數。編程
var test = new Function("a","b","return a + b");//參數a和b,函數體return a + b console.log(test(1,2));//3
咱們能夠看出比較的麻煩,而且《javascript高級程序設計》也不推薦咱們使用這種方式,主要是由於瀏覽器要解析常規的javascript代碼以外,還要解析傳入的參數字符串,這個相似eval()的解釋,影響性能。數組
這種方式是建立的常見方式之一,具體請看瀏覽器
var test = function(a,b){ return a + b; }
console.log(test(1,2));
上面的代碼就是建立一個函數,使用test()進行調用。其實,上面的代碼是先建立了一個匿名的函數,以後把這個匿名的函數賦值給test變量。每一個函數有一個name屬性,這個屬性不是ECMA標準的一部分,可是許多地方可使用它。咱們能夠給上面的函數起一個名字,具體下面代碼app
//函數的名字newName var test = function newName(a,b){ return a + b; } console.log(test.name);//newName //匿名函數 var nTest = function (a,b){ return a + b; } console.log(nTest.name);//""
這個屬性在後面詳細解釋吧。編程語言
這種方式和C語言中的很相似,這種是最多見的一種建立函數的方法。是經過關鍵字function直接聲明,請看函數
function test(a,b){ return a + b; } console.log(test(1,2));//3 console.log(test.name);//test
以上介紹了三個建立函數的方式,如今介紹三種的區別,確切的說是後兩種的區別,由於Function不推薦使用,性能是一大緣由。區別就是使用函數聲明這種方式會使函數的聲明提早,相似前面咱們提到的變量申明的提早。也就是說,使用函數申明方式,咱們能夠將函數的聲明放在調用函數代碼的後面,由於解析器會在代碼執行以前將函數的聲明提高,提到源代碼樹的頂部,而函數表達式方式則會報錯,具體請看
//調用函數 console.log(test(1,2));//3 //建立函數(函數申明方式) function test(a,b){ return a + b; } //上面的函數相等於 //建立函數(函數申明方式) //function test(a,b){ // return a + b; //} //console.log(test(1,2));//3 //調用函數 console.log(ntest(1,2));//TypeError: undefined is not a function //建立函數(函數表達式方式) var ntest = function (a,b){ return a + b; }
javascript語言不像java那些語言有函數重載這一律念,其實函數名就是一個指針,指向一個Function實例的地址,固然只能指向一個函數,固然沒有重載的概念了,只有覆蓋,後面定義的函數覆蓋前面定義的函數,具體請看
function test(a,b){ return a + b; } //下面的函數覆蓋上面的 function test(a,b){ return a + b + 100; } console.log(test(0,0));//100
也就是說若是一個同名的函數表達式和函數申明的函數在一塊兒,不管位置是怎麼樣的,最後的函數就會是用函數表達式建立的函數,由於函數申明會提高到頂部嘛,看看下面的代碼
var test = function (a,b){ return a + b -100; } function test(a,b){//會被下面的函數覆蓋 return a + b; } function test(a,b){//會被函數表達式覆蓋 return a + b + 100; } console.log(test(0,0));//-100
函數的內部有兩個重要的對象:arguments和this。
arguments是一個相似組對象,包含因此傳入函數的全部參數, 寫程序或者面試中常問的就是如何將arguments轉化完成一個數組,請看
Array.prototype.slice.call(arguments); Array.prototype.slice.call(arguments,0); Array.prototype.slice.call(arguments,0,arguments.length); Array.apply(this, arguments); //沒用過 Array.apply(null, arguments); //沒用過
arguments有一個length屬性,表示函數傳入參數的個數,還有一個callee屬性,這是一個指針,指向擁有這個arguments的函數,這個主要是在函數內部調用本身時使用,也就是遞歸時使用。看個例子就明白了
function test(count){ console.log("參數:"+arguments[0]+"個數:"+arguments.length); if(count <= 0){ console.log("遞歸"+count+" 結束了"); }else{ console.log("遞歸"+count); arguments.callee(--count);//調用本身 } } test(3); /* 參數:3個數:1 遞歸3 參數:2個數:1 遞歸2 參數:1個數:1 遞歸1 參數:0個數:1 遞歸0 結束了 */
javascript中的this和java中的this差很少,this引用的是函數的執行環境,就是this在不一樣的執行環境中引用的是不一樣的對象,執行環境這裏尚未說到,之後會詳細介紹,這裏的this也是簡單的介紹一下,我之後會整理一些面試題,幫助你們理解。看例子吧,
//全局變量 var color = "red";//至關於window.color = "red" //定義一個對象 var obj = {color : "blue"}; function pop(color){ alert(this.color); } pop();//至關於window.pop();輸入"red" //obj對象增長一個方法,將pop賦值給它 obj.pop = pop; obj.pop(); //輸出"blue"
解釋一下,this這個對象是在函數執行時才綁定的,能夠說是一個動態的。pop函數是定義在window下的一個函數,也就是全局做用域的一個函數,當直接執行pop函數時,就是在全局做用域下調用pop時。this引用的是window,this.color就是window.color,輸出red。當咱們把pop這個函數賦值給obj這個對象而且調用pop的時候,this引用的就是obj這個對象,this.color 就是obj.color,輸出blue。
這裏說一下函數的屬性和方法,包括length,name,prototype,apply,call這幾個。
這個屬性比較簡單,就是表示定義函數時定義的參數的個數,要和arguments.length區分開,arguments.length表示實際輸入的參數個數,看例子
function test(a,b){ console.log("輸入的參數個數:"+arguments.length); console.log("定義的參數個數:"+test.length); } test();//0,2 test(1);//1,2 test(1,2)//2,2 test(1,2,3)//3,2 //函數的內部咱們能夠經過arguments[i],取得輸入的參數,假如定義一個參數,輸入兩個參數,那怎麼取得第二個參數呢 function testA(c){ console.log("輸入的參數個數:"+arguments.length); console.log("定義的參數個數:"+test.length); console.log("第二個參數:"+arguments[1]); } testA(1,100);2,2,100 //這裏能夠遍歷取得全部的參數,不講了
這個屬性在前面提到了一點,這個就是函數的名字,咱們在建立函數的時候說了這個屬性,這個屬性不是標準屬性,可是不少地方就使用這個屬性,主要也是在遞歸調用上使用。name屬性是隻讀屬性,不能修改它的值。直接看例子
//修改name屬性 function test(){ console.log(test.name); //修改name屬性 test.name = "newName"; console.log(test.name); } test();//test,test //函數內部使用name屬性,遞歸調用 function testD(count){ console.log("參數:"+arguments[0]+"個數:"+arguments.length); if(count <= 0){ console.log("遞歸"+count+" 結束了"); }else{ console.log("遞歸"+count); testD(--count);//調用本身 } } testD(3); /* 參數:3個數:1 遞歸3 參數:2個數:1 遞歸2 參數:1個數:1 遞歸1 參數:0個數:1 遞歸0 結束了 */
name屬性的使用和arguments.callee()的效果是同樣的,只不過arguments.callee()更方便些,當函數名字更改時程序不用更改。
函數的prototype屬性是一個很重要的屬性,特別是在自定義引用類型和實現繼承時。咱們如今這簡單的介紹一下它,由於這個屬性足以單獨寫一篇文章。咱們能夠認爲prototype是一個模板,在new 一個對象時候會參照這個模板,將模板裏的屬性和方法複製給對象,固然你不定義這個模板,這個模板不存在方法和屬性。簡單例子
function People(name){ this.name = name; } //prototype中的屬性 People.prototype.home = "jilin"; var hainan = new People("hainan"); console.log(hainan.home);//jilin
先簡單介紹到這,後面單獨詳細說。
這兩個方法做用是同樣的,就是改變this做用域的值,在特定的做用域中調用本身,也就是設置this的指向,不一樣點在於參數接收方式不一樣。apply方法須要兩個參數,第一個是指定的做用域,就是要把this指向的對象,第二個是參數數組,函數調用須要的參數,這個參數數組也能夠是arguments這個僞數組。call的第一個參數也是給this綁定的值,其餘的參數個數不定,其餘的參數就是函數調用須要的參數,和apply不一樣,你要一個一個的都列舉出來。看例子
function sum(a,b){ return a + b; } //arguments參數 function callSum1(a,b){ return sum.apply(this,arguments); } //數組參數 function callSum2(a,b){ return sum.apply(this,[a,b]); } //列舉全部參數 function callSum3(a,b){ return sum.call(this,a,b); } console.log(callSum1(1,2)); console.log(callSum2(1,2)); console.log(callSum3(1,2));
上面是傳遞參數的例子,再看看改變this指向的例子
//全局變量 var color = "red";//至關於window.color = "red" //定義一個對象 var obj = {color : "blue"}; function pop(color){ alert(this.color); } pop();//至關於window.pop();輸入"red" pop.call(this);//red pop.call(obj);//blue
解釋一下,pop.call(this)這句代碼改變了this的指向,由於是在全局中調用的函數,this指向window,輸出window.color。pop.call(obj)這句代碼將this指向了obj,因此輸出obj.color。
把函數這部分的基礎和你們說了一下,本身講代碼敲了一遍實驗了一下,有些東西看着容易懂,寫起來仍是挺困難的,但願你們也要多寫寫吧,我一直覺得做文就很差,這是難爲你們了。要放假了,有點想家了,想吃家裏的酸菜了。