JavaScript函數學習總結(一)---函數定義


博客原文地址:Claiyre的個人博客
如需轉載,請在文章開頭注明原文地址

在許多傳統的OO語言中,對象可以包含數據,還可擁有方法,也就是屬於該對象的函數。但在JavaScript中,函數也被認為是一個對象,一個Function對象,因此函數被稱為JavaScript的一級公民( first-class objects)!

普通函數定義與初始化

1.通過構造器Function
我們知道,JavaScript中Array,Date等基本類型的聲明是通過其對應的構造器(關鍵字)來聲明的。如

var arr1 = new Array();   #聲明一個空數組
var date1 = new Date();   #聲明一個時間對象,獲取當前時間

與定義其他基本對象類型如Array,Date一樣,JavaScript的函數是通過關鍵字Function來定義並初始化的

 new Function([arg1],[arg2](,[arg3,...]),functionBody);

舉個栗子

var f1 = new Function('name','age','console.log("hello," + name);console.log("You are " + age)');
f1("Claiyre",20);

當然,除了這種‘吃力不討好’的定義方法外,你還有其他更簡潔的方法來定義一個函數!

2.通過函數聲明(function statement)
語法:

function name([arg1,[,arg2,[...argn]]]) {
   functionBody;
 }

name是指定的函數名,arg是函數的參數,可以為任意多個(當然,不能超過可以表示的范圍),functionBody為函數體。
栗子如下

function sayHi (name,age){
       console.log("hello," + name);
       console.log("you are " + age);
 }

3.通過函數表達式(function expression)
語法

   function [name]([arg1,[,arg2,[...argn]]]) {
       console.log("hello," + name);
       console.log("you are " + age);
   }

4.總結

  • 方式2和方式3擁有幾乎相同的語法,極其相似。但聰明的你一定發現了它們的不同之處了對不對O(∩_∩)O——函數表達式定義一個函數時可以省略函數名,從而創建一個匿名函數或一個立即執行函數(IIFE)。
  • 相比於方式2和方式3,方式1用Function構造器聲明函數的方式更為低效,因為后者生成的對象是在函數被創建時解析的,前者是和其他代碼一起解析的。
  • 通過函數調用的方式調用Function 的構造函數(不用new關鍵字)跟方式1是一樣的
  • 使用Function構造器,並不會在創建它的上下文(Context)中創建閉包,所以它們只能訪問自身局部變量和全局變量,相當於它們總是在全局作用域中被創建的,即便實際並不是!
myName = 'outer';
var person = {
                myName: 'inner',
                sayHi: new Function('like','console.log("I am "+ myName + "!I like " + like)')
            };
 person.sayHi("banana");

結果如下:

generator(生成器)函數的定義

generator是ES6標准引入的新的數據類型,旨在利用生成器函數返回一個對象,該對象遵守迭代器協議,而generator的環境(context)在每次執行后都會被保存,以便迭代器對象再次使用,每次迭代就相當於生成一個新的函數,這也是命名為generator(函數生成器)的緣故吧。迭代器協議是ES6標准引入的新協議之一,具體參見 the iterable protocol

與普通函數類似,generator函數也有三種定義方式,但與普通函數稍有不同,具體如下

  1. 通過GeneratorFunction構造器
    new GeneratorFunction ([arg1[, arg2[, ...argN]],] functionBody)
  1. 通過function* 聲明(function* statement)
    function* name([param[, param[, ... param]]]) {
         statements
     }
  1. 通過function表達式(function expression)
    function* [name]([param1[, param2[, ..., paramN]]]) {
       statements
    }

調用一個生成器函數並不會立即執行它的主體,而是返回一個與該函數對應的迭代器對象,當調用這個迭代器對象的next()方法時,生成器函數的主體才會被執行,直到遇見第一個 yield表達式

紙上談兵終覺淺,我們來看一個例子

function* idMaker(){
  var index = 0;
  while(index < 3)
    yield index++;
}

var gen = idMaker();   // 此時並沒有執行idmaker的主體

console.log(gen.next().value); // 0
console.log(gen.next().value); // 1
console.log(gen.next().value); // 2
console.log(gen.next().value); // undefined

即可用yield指定要返回的值,也可用 yield* 委派另一個generator函數對象

如下

function* generatorOne(index){                                                 
   yield index;
   yield* generatorTwo(index);
   yield index+10;
}

 function* generatorTwo(index){
   yield index+1;
   yield index+2;
   yield index+3;
}

var ge = generatorOne(10);                                                                
console.log(ge.next());   
console.log(ge.next());
console.log(ge.next());
console.log(ge.next());
console.log(ge.next());
console.log(ge.next());

運行結果如下:

我們可以看到ge.next()返回了一個對象,該對象有兩個屬性,value是其返回的值,done代表迭代器是否迭代到了盡頭

generator就像是一個不用占用任何全局變量就可以記住執行狀態的函數,它的好處當然不只是上面例子那樣簡單,關於優點和用處,個人認為廖雪峰老師寫的已相當不錯,參見generator

箭頭函數的定義

ES6標准有一個深受廣大程序猿喜愛的新屬性,那就是箭頭函數。它的定義就是用一個箭頭。其具體語法如下

let add = x => x+1 ;
add(10);   //11

是不是很簡便呢?

當然如果有多個參數的話,需要給所有參數加上括號

let add = (x,y) => x+y ;
add(5,6);

以此類推,如果函數體有多條語句的話,也應該給函數體加上{}
此外,箭頭函數的另一個特殊之處是它的this總是指向詞法作用域,不能被改變,比如:

var myName = "outer";
var person = {
    myName:"inner",
    age:20
};
var sayHi= ()=>console.log(this.myName);
sayHi();                                  //outer
sayHi.call(person);                       //outer

參考資料:
MDN JavaScript


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM