在线观看不卡亚洲电影_亚洲妓女99综合网_91青青青亚洲娱乐在线观看_日韩无码高清综合久久

鍍金池/ 教程/ HTML/ 函數(shù)
面向?qū)ο蟮?Javascript
客戶(hù)端的 JavaScript
概述
核心概念深入
函數(shù)式的 Javascript
對(duì)象與 JSON
前端 JavaScript 框架
基本概念
數(shù)組
閉包
正則表達(dá)式
函數(shù)

函數(shù)

函數(shù),在 C 語(yǔ)言之類(lèi)的過(guò)程式語(yǔ)言中,是頂級(jí)的實(shí)體,而在 Java/C++ 之類(lèi)的面向?qū)ο蟮恼Z(yǔ)言中,則被對(duì)象包裝起來(lái),一般稱(chēng)為對(duì)象的方法。而在 JavaScript 中,函數(shù)本身與其他任何的內(nèi)置對(duì)象在低位上是沒(méi)有任何區(qū)別的,也就是說(shuō),函數(shù)本身也是對(duì)象。

總的來(lái)說(shuō),函數(shù)在 JavaScript 中可以:

  • 被賦值給一個(gè)變量
  • 被賦值為對(duì)象的屬性
  • 作為參數(shù)被傳入別的函數(shù)
  • 作為函數(shù)的結(jié)果被返回
  • 用字面量來(lái)創(chuàng)建

函數(shù)對(duì)象

創(chuàng)建函數(shù)

創(chuàng)建 JavaScript 函數(shù)的一種不長(zhǎng)用的方式(幾乎沒(méi)有人用)是通過(guò) new 操作符來(lái)作用于 Function“構(gòu)造器”:

var funcName = new Function( [argname1, [... argnameN,]] body ); 

參數(shù)列表中可以有任意多的參數(shù),然后緊跟著是函數(shù)體,比如:

var add = new Function("x", "y", "return(x+y)");  
print(add(2, 4));

將會(huì)打印結(jié)果:

6

但是,誰(shuí)會(huì)用如此難用的方式來(lái)創(chuàng)建一個(gè)函數(shù)呢?如果函數(shù)體比較復(fù)雜,那拼接這個(gè) String 要花費(fèi)很大的力氣,所以 JavaScript 提供了一種語(yǔ)法糖,即通過(guò)字面量來(lái)創(chuàng)建函數(shù):

function add(x, y){  
    return x + y;  
}  

或:

var add = function(x, y){  
    return x + y;  
} 

事實(shí)上,這樣的語(yǔ)法糖更容易使傳統(tǒng)領(lǐng)域的程序員產(chǎn)生誤解,function 關(guān)鍵字會(huì)調(diào)用 Function 來(lái) new 一個(gè)對(duì)象,并將參數(shù)表和函數(shù)體準(zhǔn)確的傳遞給 Function 的構(gòu)造器。 通常來(lái)說(shuō),在全局作用域(作用域?qū)⒃谙乱还?jié)詳細(xì)介紹)內(nèi)聲明一個(gè)對(duì)象,只不過(guò)是對(duì)一個(gè)屬性賦值而已,比如上例中的add函數(shù),事實(shí)上只是為全局對(duì)象添加了一個(gè)屬性,屬性名為 add,而屬性的值是一個(gè)對(duì)象,即 function(x, y){return x+y;},理解這一點(diǎn)很重要,這條語(yǔ)句在語(yǔ)法上跟:

var str = "This is a string"; 

并無(wú)二致。都是給全局對(duì)象動(dòng)態(tài)的增加一個(gè)新的屬性,如此而已。

為了說(shuō)明函數(shù)跟其他的對(duì)象一樣,都是作為一個(gè)獨(dú)立的對(duì)象而存在于 JavaScript 的運(yùn)行系統(tǒng),我們不妨看這樣一個(gè)例子:

function p(){  
    print("invoke p by ()");  
}  

p.id = "func";  
p.type = "function";  

print(p);  
print(p.id+":"+p.type);  
print(p()); 

沒(méi)有錯(cuò),p 雖然引用了一個(gè)匿名函數(shù)(對(duì)象),但是同時(shí)又可以擁有屬性,完全跟其他對(duì)象一樣,運(yùn)行結(jié)果如下:

function (){
print("invoke p by ()");
}
func:function
invoke p by ()

函數(shù)的參數(shù)

在 JavaScript 中,函數(shù)的參數(shù)是比較有意思的,比如,你可以將任意多的參數(shù)傳遞給一個(gè)函數(shù),即使這個(gè)函數(shù)聲明時(shí)并未制定形式參數(shù),比如:

function adPrint(str, len, option){  
    var s = str || "default";  
    var l = len || s.length;  
    var o = option || "i";  

    s = s.substring(0, l);  
    switch(o){  
       case "u":  
           s = s.toUpperCase();  
           break;  
       case "l":  
           s = s.toLowerCase();  
           break;  
       default:  
           break;  
    }  

    print(s);  
}  

adPrint("Hello, world");  
adPrint("Hello, world", 5);  
adPrint("Hello, world", 5, "l");//lower case  
adPrint("Hello, world", 5, "u");//upper case 

函數(shù) adPrint 在聲明時(shí)接受三個(gè)形式參數(shù):要打印的串,要打印的長(zhǎng)度,是否轉(zhuǎn)換為大小寫(xiě)的標(biāo)記。但是在調(diào)用的時(shí)候,我們可以按順序傳遞給adPrint一個(gè)參數(shù),兩個(gè)參數(shù),或者三個(gè)參數(shù)(甚至可以傳遞給它多于 3 個(gè),沒(méi)有關(guān)系),運(yùn)行結(jié)果如下:

Hello, world
Hello
hello
HELLO

事實(shí)上,JavaScript 在處理函數(shù)的參數(shù)時(shí),與其他編譯型的語(yǔ)言不一樣,解釋器傳遞給函數(shù)的是一個(gè)類(lèi)似于數(shù)組的內(nèi)部值,叫 arguments,這個(gè)在函數(shù)對(duì)象生成的時(shí)候就被初始化了。比如我們傳遞給 adPrint 一個(gè)參數(shù)的情況下,其他兩個(gè)參數(shù)分別為undefined.這樣,我們可以才 adPrint 函數(shù)內(nèi)部處理那些 undefined 參數(shù),從而可以向外部公開(kāi):我們可以處理任意參數(shù)。

我們通過(guò)另一個(gè)例子來(lái)討論這個(gè)神奇的 arguments:

function sum(){  
    var result = 0;  
    for(var i = 0, len = arguments.length; i < len; i++){  
       var current = arguments[i];  
       if(isNaN(current)){  
           throw new Error("not a number exception");  
       }else{  
           result += current;  
       }  
    }  

    return result;  
}  

print(sum(10, 20, 30, 40, 50));  
print(sum(4, 8, 15, 16, 23, 42));//《迷失》上那串神奇的數(shù)字  
print(sum("new")); 

函數(shù) sum 沒(méi)有顯式的形參,而我們又可以動(dòng)態(tài)的傳遞給其任意多的參數(shù),那么,如何在 sum 函數(shù)中如何引用這些參數(shù)呢?這里就需要用到 arguments 這個(gè)偽數(shù)組了,運(yùn)行結(jié)果如下:

150
108
Error: not a number exception

函數(shù)作用域

作用域的概念在幾乎所有的主流語(yǔ)言中都有體現(xiàn),在 JavaScript 中,則有其特殊性:JavaScript 中的變量作用域?yàn)楹瘮?shù)體內(nèi)有效,而無(wú)塊作用域,我們?cè)?Java 語(yǔ)言中,可以這樣定義 for 循環(huán)塊中的下標(biāo)變量:

public void method(){  
    for(int i = 0; i < obj1.length; i++){  
       //do something here;  
    }  
    //此時(shí)的i為未定義  
    for(int i = 0; i < obj2.length; i++){  
       //do something else;  
    }    
}  

而在 JavaScript 中:

function func(){  
    for(var i = 0; i < array.length; i++){  
       //do something here.  
    }  
    //此時(shí)i仍然有值,及I == array.length  
    print(i);//i == array.length;  
}  

JavaScript 的函數(shù)是在局部作用域內(nèi)運(yùn)行的,在局部作用域內(nèi)運(yùn)行的函數(shù)體可以訪問(wèn)其外層的(可能是全局作用域)的變量和函數(shù)。JavaScript 的作用域?yàn)樵~法作用域,所謂詞法作用域是說(shuō),其作用域?yàn)樵诙x時(shí)(詞法分析時(shí))就確定下來(lái)的,而并非在執(zhí)行時(shí)確定,如下例:

var str = "global";  
function scopeTest(){  
    print(str);  
    var str = "local";  
    print(str);  
}  

scopeTest(); 

運(yùn)行結(jié)果是什么呢?初學(xué)者很可能得出這樣的答案:

global
local

而正確的結(jié)果應(yīng)該是:

undefined
local

因?yàn)樵诤瘮?shù) scopeTest 的定義中,預(yù)先訪問(wèn)了未聲明的變量 str,然后才對(duì) str 變量進(jìn)行初始化,所以第一個(gè) print(str)會(huì)返回 undifined 錯(cuò)誤。那為什么函數(shù)這個(gè)時(shí)候不去訪問(wèn)外部的 str 變量呢?這是因?yàn)椋谠~法分析結(jié)束后,構(gòu)造作用域鏈的時(shí)候,會(huì)將函數(shù)內(nèi)定義的 var 變量放入該鏈,因此 str 在整個(gè)函數(shù) scopeTest 內(nèi)都是可見(jiàn)的(從函數(shù)體的第一行到最后一行),由于 str 變量本身是未定義的,程序順序執(zhí)行,到第一行就會(huì)返回未定義,第二行為 str 賦值,所以第三行的 print(str)將返回”local”。

函數(shù)上下文

在 Java 或者 C/C++等語(yǔ)言中,方法(函數(shù))只能依附于對(duì)象而存在,不是獨(dú)立的。而在 JavaScript 中,函數(shù)也是一種對(duì)象,并非其他任何對(duì)象的一部分,理解這一點(diǎn)尤為重要,特別是對(duì)理解函數(shù)式的 JavaScript 非常有用,在函數(shù)式編程語(yǔ)言中,函數(shù)被認(rèn)為是一等的。

函數(shù)的上下文是可以變化的,因此,函數(shù)內(nèi)的 this 也是可以變化的,函數(shù)可以作為一個(gè)對(duì)象的方法,也可以同時(shí)作為另一個(gè)對(duì)象的方法,總之,函數(shù)本身是獨(dú)立的??梢酝ㄟ^(guò) Function 對(duì)象上的 call 或者 apply 函數(shù)來(lái)修改函數(shù)的上下文:

call 和 apply

call 和 apply 通常用來(lái)修改函數(shù)的上下文,函數(shù)中的 this 指針將被替換為 call 或者 apply 的第一個(gè)參數(shù)

//定義一個(gè)人,名字為jack  
var jack = {  
    name : "jack",  
    age : 26  
}  

//定義另一個(gè)人,名字為abruzzi  
var abruzzi = {  
    name : "abruzzi",  
    age : 26  
}  

//定義一個(gè)全局的函數(shù)對(duì)象  
function printName(){  
    return this.name;  
}  

//設(shè)置printName的上下文為jack, 此時(shí)的this為jack  
print(printName.call(jack));  
//設(shè)置printName的上下文為abruzzi,此時(shí)的this為abruzzi  
print(printName.call(abruzzi));  

print(printName.apply(jack));  
print(printName.apply(abruzzi));  

只有一個(gè)參數(shù)的時(shí)候call和apply的使用方式是一樣的,如果有多個(gè)參數(shù):

Js代碼  收藏代碼
setName.apply(jack, ["Jack Sept."]);  
print(printName.apply(jack));  

setName.call(abruzzi, "John Abruzzi");  
print(printName.call(abruzzi));  

得到的結(jié)果為:

Js代碼  收藏代碼
Jack Sept.  
John Abruzzi  

apply 的第二個(gè)參數(shù)為一個(gè)函數(shù)需要的參數(shù)組成的一個(gè)數(shù)組,而 call 則需要跟若干個(gè)參數(shù),參數(shù)之間以逗號(hào)(,)隔開(kāi)即可。

使用函數(shù)

前面已經(jīng)提到,在 JavaScript 中,函數(shù)可以

  • 被賦值給一個(gè)變量
  • 被賦值為對(duì)象的屬性
  • 作為參數(shù)被傳入別的函數(shù)
  • 作為函數(shù)的結(jié)果被返回

我們就分別來(lái)看看這些場(chǎng)景:

賦值給一個(gè)變量:

//聲明一個(gè)函數(shù),接受兩個(gè)參數(shù),返回其和  
function add(x, y){  
    return x + y;  
}  

var a = 0;  
a = add;//將函數(shù)賦值給一個(gè)變量  
var b = a(2, 3);//調(diào)用這個(gè)新的函數(shù)a  
print(b);  

這段代碼會(huì)打印”5”,因?yàn)橘x值之后,變量 a 引用函數(shù) add,也就是說(shuō),a 的值是一個(gè)函數(shù)對(duì)象(一個(gè)可執(zhí)行代碼塊),因此可以使用 a(2, 3)這樣的語(yǔ)句來(lái)進(jìn)行求和操作。

賦值為對(duì)象的屬性:

var obj = {  
    id : "obj1"  
}  

obj.func = add;//賦值為obj對(duì)象的屬性  
obj.func(2, 3);//返回5 

事實(shí)上,這個(gè)例子與上個(gè)例子的本質(zhì)上是一樣的,第一個(gè)例子中的 a 變量,事實(shí)上是全局對(duì)象(如果在客戶(hù)端環(huán)境中,表示為 window 對(duì)象)的一個(gè)屬性。而第二個(gè)例子則為 obj 對(duì)象,由于我們很少直接的引用全局對(duì)象,就分開(kāi)來(lái)描述。

作為參數(shù)傳遞:

//高級(jí)打印函數(shù)的第二個(gè)版本  
function adPrint2(str, handler){  
    print(handler(str));  
}  

//將字符串轉(zhuǎn)換為大寫(xiě)形式,并返回  
function up(str){  
    return str.toUpperCase();  
}  

//將字符串轉(zhuǎn)換為小寫(xiě)形式,并返回  
function low(str){  
    return str.toLowerCase();  
}  

adPrint2("Hello, world", up);  
adPrint2("Hello, world", low);  

運(yùn)行此片段,可以得到這樣的結(jié)果:

HELLO, WORLD
hello, world

應(yīng)該注意到,函數(shù) adPrint2 的第二個(gè)參數(shù),事實(shí)上是一個(gè)函數(shù),將這個(gè)處理函數(shù)作為參數(shù)傳入,在 adPrint2 的內(nèi)部,仍然可以調(diào)用這個(gè)函數(shù),這個(gè)特點(diǎn)在很多地方都是有用的,特別是,當(dāng)我們想要處理一些對(duì)象,但是又不確定以何種形式來(lái)處理,則完全可以將“處理方式”作為一個(gè)抽象的粒度來(lái)進(jìn)行包裝(即函數(shù))。

作為函數(shù)的返回值:

先來(lái)看一個(gè)最簡(jiǎn)單的例子:

function currying(){  
    return function(){  
       print("curring");  
    }  
}

函數(shù) currying 返回一個(gè)匿名函數(shù),這個(gè)匿名函數(shù)會(huì)打印”curring”,簡(jiǎn)單的調(diào)用 currying()會(huì)得到下面的結(jié)果:

function (){  
    print("curring");  
} 

如果要調(diào)用 currying 返回的這個(gè)匿名函數(shù),需要這樣:

currying()();  

第一個(gè)括號(hào)操作,表示調(diào)用 currying 本身,此時(shí)返回值為函數(shù),第二個(gè)括號(hào)操作符調(diào)用這個(gè)返回值,則會(huì)得到這樣的結(jié)果:

currying