JavaScript中的函數(shù)

@(javascript)[js函數(shù)]

[toc]

JavaScript中的函數(shù)

函數(shù)的分類與定義函數(shù)的方式

JavaScript中的函數(shù)可以分為兩類:有名函數(shù)匿名函數(shù)徐伐。而定義函數(shù)的方式有兩種:函數(shù)聲明函數(shù)表達式嗜傅。

目標:定義一個函數(shù) fn ==> 有名函數(shù)

// 使用函數(shù)聲明
function fn(){
    // 函數(shù)執(zhí)行體
}
// 使用函數(shù)表達式
var fn = function(){
    // 函數(shù)執(zhí)行體
}

使用函數(shù)聲明的重要特征就是函數(shù)聲明提升损同,即在讀取代碼前會先讀取函數(shù)聲明菩混。函數(shù)名()表示執(zhí)行函數(shù)
看看下面的代碼沒有任何問題。

// 定義一個有名函數(shù) fn1 使用函數(shù)聲明
function fn(){
    console.log("fn1")
}
// 調(diào)用函數(shù) fn1
fn1();  // fn1

// 定義一個有名函數(shù) fn2 使用函數(shù)表達式
var fn2 = function(){
    console.log("fn2")
}
// 調(diào)用函數(shù) fn2
fn2(); // fn2

但是如果是把調(diào)用放在定義函數(shù)前面汰扭,使用函數(shù)表達式的就會報錯(Uncaught ReferenceError: fn1 is not defined)

// 調(diào)用函數(shù) fn1
fn1(); // fn1
// 定義一個有名函數(shù) fn1 使用函數(shù)聲明
function fn(){
    console.log("fn1")
}

// 調(diào)用函數(shù) fn2
fn2(); // Uncaught ReferenceError: fn1 is not defined
// 定義一個有名函數(shù) fn2 使用函數(shù)表達式
var fn2 = function(){
    console.log("fn2")
}

這就是使用兩種的區(qū)別德崭。

函數(shù)的返回值

每一個函數(shù)在調(diào)用的時候都會默認返回一個undefined

function fn(){
    console.log(1)
}
fn(); // 1
console.log(fn); // console出一個函數(shù) 即 fn
console.log(fn()); // undefined

這里需要注意的地方就是關(guān)于函數(shù)執(zhí)行過程函數(shù)執(zhí)行結(jié)果弟胀。

fn()表示調(diào)用函數(shù)楷力。那就會執(zhí)行函數(shù)體。并默認返回一個undefined孵户。只不過這個值undefined沒有變量接收或者說是我們沒有用這個值萧朝。

console.log(fn)就只是console出一個變量fn的值。只不過這個值是一個函數(shù)夏哭。

console.log(fn())與第一個的區(qū)別就是函數(shù)執(zhí)行了并返回了一個結(jié)果检柬。這個結(jié)果呢與上面不同的就是現(xiàn)在這個結(jié)果我們用上了(放在了console)里面。再由于值是undefined,所以console了一個undefined何址。

既然函數(shù)是可以有返回值的里逆,并且這個值默認是一個undefined。那我們可以可以修改呢用爪?答案是可以的原押。
我們使用return語句可以讓函數(shù)返回一個值

function fn(){
    console.log(1)
    return "哈哈"
}
fn(); // 1
console.log(fn); // 函數(shù) fn
console.log(fn()); // 哈哈

可以看一下第一個與第二個的結(jié)果與之前的是相同的。但是第三個的結(jié)果就不同了偎血,他是字符串哈哈诸衔。因為我們修改了函數(shù)的默認返回值。

所以颇玷,可以把默認函數(shù)理解成這樣的

function fn(){
    return undefined;
}

而我們修改返回值就是吧這個undefined給變成其他的值了笨农。
注意:函數(shù)的返回值可以是任意的數(shù)據(jù)類型。

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

函數(shù)是可以接收參數(shù)的帖渠,在定義函數(shù)的時候放的參數(shù)叫形式參數(shù)谒亦,簡稱形參。在調(diào)用函數(shù)的時候傳遞的參數(shù)叫實際參數(shù)空郊,簡稱實參诊霹。一個函數(shù)可以擁有任意個參數(shù)

function fn(a,b){
    console.log(a)
    console.log(b)
    console.log(a+b)
}
// 調(diào)用函數(shù)并傳遞參數(shù)
fn(3,5);  // 3,5,8

fn(3); // 3,undefined,NaN

fn(3,5,10) // 3,5,8

可以看看上面的例子。定義函數(shù)的時候有兩個形參渣淳。調(diào)用的時候分為了三種情況。

第一種伴箩,傳遞兩個參數(shù)入愧,在console時候a=3,b=5,a+b=8。老鐵嗤谚,沒問題棺蛛。

第二種,傳遞一個參數(shù)巩步,在console的時候a=3,b=undefined,a+b=NaN旁赊。哈哈,你不行椅野。

第三種终畅,傳遞3個。在console的時候a=3,b=5,a+b=8竟闪。握草离福,你牛逼。對第三個參數(shù)視而不見了炼蛤。

以上就是三種情況妖爷。一句話:參數(shù)一一對應(yīng),實參少了理朋,那么沒有對應(yīng)的就是undefined絮识,實參多了绿聘,多出來的就是沒有用的

arguments

在不確定參數(shù)(或者定義函數(shù)的時候沒有形參)的時候,調(diào)用函數(shù)你傳遞參數(shù)了次舌,但是你沒有使用新參去接收熄攘,就無法使用。把此時就有一個arguments對象可以獲取到實參的個數(shù)以及具體的值垃它。

function fn(){
    console.log(arguments)
}
fn(1,2,3,4,5,6,7) // Arguments(7) [1, 2, 3, 4, 5, 6, 7, callee: ?, Symbol(Symbol.iterator): ?]

arguments在嚴格模式下無法使用鲜屏。

函數(shù)遞歸

遞歸:就是函數(shù)自己調(diào)用自己。比如下面經(jīng)典的階層遞歸函數(shù)

function stratum(n){
    if (n <= 1){
        return 1;
    } else {
        return n * stratum(n - 1);
    }
}
stratum(5) // 120 = 5 * (4 * (3 * (2 * 1) ) )

可以看出實現(xiàn)的階層的功能国拇。
不過需要注意一下每一個的執(zhí)行順序洛史。不是5 * 4 * 3 * 2 * 1。而是5 * (4 * (3 * (2 * 1) ) )的順序酱吝。為了證明這一點也殖。可以將*換為-

function fn(n){
    if (n <= 1){
        return 1;
    } else {
        return n - fn(n - 1);
    }
}
fn(5) // 3

如果是按照不帶括號的5-4-3-2-1 = -5务热。但是結(jié)果卻是3忆嗜。那3是怎么來的呢?5 - (4 - (3 - (2 - 1) ) ) = 5 - (4 - (3 - 1)) = 5 - (4 - 2) = 5 - 2 = 3

還可以使用arguments.callee方法調(diào)用自身崎岂。這個方法就指向當前運行的函數(shù)

function stratum(n){
    if (n <= 1){
        return 1;
    } else {
        return n * arguments.callee(n - 1);
    }
}
stratum(5) // 120

遞歸雖然可以讓代碼更簡潔捆毫,但是能不使用遞歸的時候就不要使用,遞歸會影響性能(因為過多的調(diào)用自己會一直保存每一次的返回值與變量冲甘,導(dǎo)致內(nèi)存占用過多甚至內(nèi)存泄露)绩卤。

console.time(1);
function stratum(n){
    if (n <= 1){
        return 1;
    } else {
        return n * arguments.callee(n - 1);
    }
}
console.log(stratum(5))
console.timeEnd(1) // 1: 4.470947265625ms

console.time(2)
var a = 1;
for (var i = 1; i <= 5; i++) {
    a *= i;
}
console.log(a);
console.timeEnd(2)  // 2: 0.2373046875ms

兩個階層,一看江醇。for循環(huán)快太多了濒憋。具體的性能問題可以看看<a href="http://www.reibang.com/p/6bdc8e3637f2" target=“_blank”>愛情小傻蛋</a>關(guān)于遞歸的算法改進。

函數(shù)閉包

閉包是指有權(quán)訪問另一個函數(shù)作用域中的變量的函數(shù)陶夜。
兩個條件:

  1. 函數(shù)嵌套函數(shù)
  2. 內(nèi)部函數(shù)使用包含函數(shù)的變量或者是參數(shù)
function fn(){
    var a = 1;
    return function(){
        console.log(a);
        a++;
    }
}
fn()(); // 1
fn()(); // 1

var a = fn();
a(); // 1
a(); // 2

上面的例子中的函數(shù)就是一個閉包凛驮。注意上面的直接調(diào)用返回值與先保存返回值在調(diào)用的區(qū)別。

閉包只能取得包含函數(shù)中任何變量的最后一個值条辟。this是無法在閉包函數(shù)中調(diào)用的黔夭。因為每一個函數(shù)都有一個this

閉包函數(shù)中使用的變量是不會進行銷毀的羽嫡,像上面的var a = fn()纠修,這個函數(shù)a中使用了函數(shù)fn中的變量,且a是一直存在的厂僧,所以函數(shù)fn里面的變量a是不會銷毀的扣草。如果是直接調(diào)用函數(shù)fn()()只是相當于調(diào)用一次fn函數(shù)的返回值。調(diào)用完函數(shù)返回值就銷毀了。所以變量a不會一直保存辰妙。

因為閉包函數(shù)的變量會一直保存不會

call鹰祸,apply與bind

三個方法都是改變this指向

call apply

function fn(a,b){
    console.log(a)
    console.log(b)
    console.log(this.name)
}

var name = "嘻嘻"
var obj = {
    "name": "哈哈"
}
// 執(zhí)行函數(shù)fn
fn(1,2) // 1,2,嘻嘻

直接調(diào)用函數(shù)fn(1,2)this.name的值是嘻嘻

如果使用call:

fn.call(obj,1,2) // 1,2,哈哈

call方法的第一個參數(shù)是改變this指向的東西密浑,可以是任何的數(shù)據(jù)類型蛙婴。只不過如果是null或者是undefined就會指向window。<span style="color: red;font-weight: 900;">其他的參數(shù)</span>依次對應(yīng)函數(shù)的每一個形參尔破。

如果使用apply

fn.apply(obj,[1,2])

apply的使用與call的使用的唯一的區(qū)別就是它對應(yīng)函數(shù)每一項形參<span style="color: red;font-weight: 900;">是一個數(shù)組</span>而不是單獨的每一個街图。

call與applu都是在函數(shù)調(diào)用的時候去使用

bind則是在函數(shù)定義的時候使用

function fn(a,b){
    console.log(a)
    console.log(b)
    console.log(this.name)
}

var name = "嘻嘻"
var obj = {
    "name": "哈哈"
}
// 執(zhí)行函數(shù)fn
fn(1,2) // 1,2,嘻嘻

如果使用bind可以是一下幾種方式

// 使用函數(shù)表達式 + 匿名函數(shù)
var fn = function(a,b){
    console.log(a)
    console.log(b)
    console.log(this.name)
}.bind(obj)
fn(1,2)

// 使用有名函數(shù)
function fn(a,b){
    console.log(a)
    console.log(b)
    console.log(this.name)
}
fn.bind(obj)(1,2)

// 函數(shù)在自執(zhí)行
(function fn(a,b){
    console.log(a)
    console.log(b)
    console.log(this.name)  
}.bind(obj)(1,2))

(function fn(){
    console.log(a)
    console.log(b)
    console.log(this.name)
}.bind(obj))(1,2);

(function fn(){
    console.log(a)
    console.log(b)
    console.log(this.name)
}).bind(obj)(1,2);

使用bind的時候也是可以傳遞參數(shù)的,但是不要這樣使用懒构,因為使用bind后你不調(diào)用函數(shù)那么參數(shù)還是沒有作用餐济。既然還是要調(diào)用函數(shù),我們一般就把函數(shù)的實參傳遞到調(diào)用的括號里面胆剧。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末絮姆,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子秩霍,更是在濱河造成了極大的恐慌篙悯,老刑警劉巖,帶你破解...
    沈念sama閱讀 207,113評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件铃绒,死亡現(xiàn)場離奇詭異鸽照,居然都是意外死亡,警方通過查閱死者的電腦和手機颠悬,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評論 2 381
  • 文/潘曉璐 我一進店門移宅,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人椿疗,你說我怎么就攤上這事】返浚” “怎么了届榄?”我有些...
    開封第一講書人閱讀 153,340評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長倔喂。 經(jīng)常有香客問我铝条,道長,這世上最難降的妖魔是什么席噩? 我笑而不...
    開封第一講書人閱讀 55,449評論 1 279
  • 正文 為了忘掉前任班缰,我火速辦了婚禮,結(jié)果婚禮上悼枢,老公的妹妹穿的比我還像新娘埠忘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 64,445評論 5 374
  • 文/花漫 我一把揭開白布莹妒。 她就那樣靜靜地躺著名船,像睡著了一般。 火紅的嫁衣襯著肌膚如雪旨怠。 梳的紋絲不亂的頭發(fā)上渠驼,一...
    開封第一講書人閱讀 49,166評論 1 284
  • 那天,我揣著相機與錄音鉴腻,去河邊找鬼迷扇。 笑死,一個胖子當著我的面吹牛爽哎,可吹牛的內(nèi)容都是我干的蜓席。 我是一名探鬼主播,決...
    沈念sama閱讀 38,442評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼倦青,長吁一口氣:“原來是場噩夢啊……” “哼瓮床!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起产镐,我...
    開封第一講書人閱讀 37,105評論 0 261
  • 序言:老撾萬榮一對情侶失蹤隘庄,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后癣亚,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體丑掺,經(jīng)...
    沈念sama閱讀 43,601評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,066評論 2 325
  • 正文 我和宋清朗相戀三年述雾,在試婚紗的時候發(fā)現(xiàn)自己被綠了街州。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,161評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡玻孟,死狀恐怖唆缴,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情黍翎,我是刑警寧澤面徽,帶...
    沈念sama閱讀 33,792評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站匣掸,受9級特大地震影響趟紊,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜碰酝,卻給世界環(huán)境...
    茶點故事閱讀 39,351評論 3 307
  • 文/蒙蒙 一霎匈、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧送爸,春花似錦铛嘱、人聲如沸暖释。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽饭入。三九已至,卻和暖如春肛真,著一層夾襖步出監(jiān)牢的瞬間谐丢,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評論 1 261
  • 我被黑心中介騙來泰國打工蚓让, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留乾忱,地道東北人。 一個月前我還...
    沈念sama閱讀 45,618評論 2 355
  • 正文 我出身青樓历极,卻偏偏與公主長得像窄瘟,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子趟卸,可洞房花燭夜當晚...
    茶點故事閱讀 42,916評論 2 344

推薦閱讀更多精彩內(nèi)容

  • 函數(shù)和對象 1蹄葱、函數(shù) 1.1 函數(shù)概述 函數(shù)對于任何一門語言來說都是核心的概念。通過函數(shù)可以封裝任意多條語句锄列,而且...
    道無虛閱讀 4,527評論 0 5
  • 第2章 基本語法 2.1 概述 基本句法和變量 語句 JavaScript程序的執(zhí)行單位為行(line)图云,也就是一...
    悟名先生閱讀 4,118評論 0 13
  • 前言:本文將詳細的介紹JS中函數(shù)的相關(guān)概念(包括函數(shù)的call stack 、this 邻邮、作用域竣况、閉包、柯里化筒严、高...
    EnochQin閱讀 640評論 2 2
  • --- 學(xué)習目標: - 掌握編程的基本思維 - 掌握編程的基本語法 typora-copy-images-to: ...
    YFBigHeart閱讀 1,047評論 0 2
  • 和好朋友聊了一上午的天丹泉,覺得幸福感蹭蹭的往出冒。想起原來兩人吃串串火鍋鸭蛙,包里還要偷偷背上一斤半勁酒的日子摹恨,忍俊不禁...
    魚在盤子里想家閱讀 332評論 0 0