JavaScript函數(二)

目錄

1.函數的聲明

2.函數的屬性和方法

3.函數的做用域

4.閉包知識點

5.小結


關於函數,能夠從如下3個方面去理解:
首先,數據類型上看:函數在JavaScript中是一種數據類型,是對象的一種;
其次,從功能上看:函數本質上是一段反覆調用的代碼塊;
最後,從地位上看:函數在JavaScript中和其餘基本數據類型同樣,能夠做爲參數和賦值,是「第一等公民」javascript


1.函數的聲明

聲明函數的方式有三種: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

2.函數的屬性和方法

函數做爲一個對象,一樣擁有屬性和方法,下面主要概括一下比較經常使用和重要的屬性和方法:
閉包

  • name屬性:返回該函數名的字符串;app

  • length屬性:返回形參的個數,即預期傳入參數的個數;函數

function fn (a,b){
    console.log(fn.name)
    console.log(fn.length)
}
fn()
//"fn"  
//2
  • arguments對象:是包含傳入函數實參的類數組對象,只有在函數執行階段而且存在參數纔會有值,未調用函數是爲nullthis

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方法

函數提供callapplybind3種方法能夠改變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

更多this的相關介紹,詳見【what's this???】

3.函數的做用域

做用域指的是變量存在的範圍,做用域可分爲全局做用域和局部做用域,變量在全局範圍可訪問到;局部做用域由函數所構造,變量只能在函數內部可訪問到;

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

4. 閉包知識點

關於閉包的知識點,將會單獨開一章節詳談,具體請看《JavaScript閉包(三)》


5.小結

經過《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當中經典的閉包得以實現提供可能性;此外,函數執行時所在的做用域是在定義時所在的做用域,而不是調用時所在的做用域;


參考資料

相關文章
相關標籤/搜索