【javascript基礎】二、函數

前言

我在上一篇【javascript基礎】基本概念中介紹了javascript的一些基本概念,多謝你們的閱讀和意見,本身寫的東西能夠被你們閱讀,真心高興,剛開始發佈的時候我一直盯着閱讀人數,雖然知道你們可能就是點開一下而已,可是仍是給我一些繼續寫下去的信心。那今天寫一些關於javascript函數的一些知識,幫助你們熟悉或者複習一些函數的基本知識。javascript

PS:最近jQuery源碼交流羣( 239147101)加了很多熱新人,但願你們仍是以學習爲主,儘可能少灌水,給你們一個好的提高本身的環境。html

函數

函數在任何一種編程語言中都是一個很重要的結構或者組成部分,編程中的複雜結構和功能都會有函數的參與。javascript中的函數是一個對象,函數對象時Function類型的實例,因爲Function類型是一個引用類型,那麼函數能夠擁有本身的方法和屬性,同時也由於函數是一個對象,那麼函數名是一個指向函數對象的指針,能夠被賦值。下面詳細介紹函數的各個部分。java

建立函數

函數的建立有三種方式,分別爲使用Function的構造函數、函數聲明、函數表達式,下面分別介紹這三種方法。面試

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;  
}

not重載

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是一個相似組對象,包含因此傳入函數的全部參數, 寫程序或者面試中常問的就是如何將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 結束了
*/

this

 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這幾個。

length

這個屬性比較簡單,就是表示定義函數時定義的參數的個數,要和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屬性是隻讀屬性,不能修改它的值。直接看例子

//修改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屬性是一個很重要的屬性,特別是在自定義引用類型和實現繼承時。咱們如今這簡單的介紹一下它,由於這個屬性足以單獨寫一篇文章。咱們能夠認爲prototype是一個模板,在new 一個對象時候會參照這個模板,將模板裏的屬性和方法複製給對象,固然你不定義這個模板,這個模板不存在方法和屬性。簡單例子

function People(name){
  this.name =  name;
}
//prototype中的屬性
People.prototype.home = "jilin";
var hainan = new People("hainan");
console.log(hainan.home);//jilin

先簡單介紹到這,後面單獨詳細說。

call和apply

這兩個方法做用是同樣的,就是改變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。

小結

把函數這部分的基礎和你們說了一下,本身講代碼敲了一遍實驗了一下,有些東西看着容易懂,寫起來仍是挺困難的,但願你們也要多寫寫吧,我一直覺得做文就很差,這是難爲你們了。要放假了,有點想家了,想吃家裏的酸菜了。

相關文章
相關標籤/搜索