JavaScript中的函數(shù)

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

[toc]

JavaScript中的函數(shù)

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

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

目標(biāo):定義一個(gè)函數(shù) fn ==> 有名函數(shù)

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

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

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

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

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

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

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

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

函數(shù)的返回值

每一個(gè)函數(shù)在調(diào)用的時(shí)候都會(huì)默認(rèn)返回一個(gè)undefined。

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

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

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

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

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

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

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

可以看一下第一個(gè)與第二個(gè)的結(jié)果與之前的是相同的。但是第三個(gè)的結(jié)果就不同了,他是字符串哈哈。因?yàn)槲覀冃薷牧撕瘮?shù)的默認(rèn)返回值。

所以,可以把默認(rèn)函數(shù)理解成這樣的

function fn(){
    return undefined;
}

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

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

函數(shù)是可以接收參數(shù)的,在定義函數(shù)的時(shí)候放的參數(shù)叫形式參數(shù),簡(jiǎn)稱形參。在調(diào)用函數(shù)的時(shí)候傳遞的參數(shù)叫實(shí)際參數(shù),簡(jiǎn)稱實(shí)參。一個(gè)函數(shù)可以擁有任意個(gè)參數(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ù)的時(shí)候有兩個(gè)形參。調(diào)用的時(shí)候分為了三種情況。

第一種,傳遞兩個(gè)參數(shù),在console時(shí)候a=3,b=5,a+b=8。老鐵,沒問題。

第二種,傳遞一個(gè)參數(shù),在console的時(shí)候a=3,b=undefined,a+b=NaN。哈哈,你不行。

第三種,傳遞3個(gè)。在console的時(shí)候a=3,b=5,a+b=8。握草,你牛逼。對(duì)第三個(gè)參數(shù)視而不見了。

以上就是三種情況。一句話:參數(shù)一一對(duì)應(yīng),實(shí)參少了,那么沒有對(duì)應(yīng)的就是undefined,實(shí)參多了,多出來的就是沒有用的

arguments

在不確定參數(shù)(或者定義函數(shù)的時(shí)候沒有形參)的時(shí)候,調(diào)用函數(shù)你傳遞參數(shù)了,但是你沒有使用新參去接收,就無法使用。把此時(shí)就有一個(gè)arguments對(duì)象可以獲取到實(shí)參的個(gè)數(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在嚴(yán)格模式下無法使用。

函數(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) ) )

可以看出實(shí)現(xiàn)的階層的功能。
不過需要注意一下每一個(gè)的執(zhí)行順序。不是5 * 4 * 3 * 2 * 1。而是5 * (4 * (3 * (2 * 1) ) )的順序。為了證明這一點(diǎn)??梢詫?code>*換為-

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

如果是按照不帶括號(hào)的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)用自身。這個(gè)方法就指向當(dāng)前運(yùn)行的函數(shù)

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

遞歸雖然可以讓代碼更簡(jiǎn)潔,但是能不使用遞歸的時(shí)候就不要使用,遞歸會(huì)影響性能(因?yàn)檫^多的調(diào)用自己會(huì)一直保存每一次的返回值與變量,導(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

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

函數(shù)閉包

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

  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ù)就是一個(gè)閉包。注意上面的直接調(diào)用返回值與先保存返回值在調(diào)用的區(qū)別。

閉包只能取得包含函數(shù)中任何變量的最后一個(gè)值。this是無法在閉包函數(shù)中調(diào)用的。因?yàn)槊恳粋€(gè)函數(shù)都有一個(gè)this。

閉包函數(shù)中使用的變量是不會(huì)進(jìn)行銷毀的,像上面的var a = fn(),這個(gè)函數(shù)a中使用了函數(shù)fn中的變量,且a是一直存在的,所以函數(shù)fn里面的變量a是不會(huì)銷毀的。如果是直接調(diào)用函數(shù)fn()()只是相當(dāng)于調(diào)用一次fn函數(shù)的返回值。調(diào)用完函數(shù)返回值就銷毀了。所以變量a不會(huì)一直保存。

因?yàn)殚]包函數(shù)的變量會(huì)一直保存不會(huì)

call,apply與bind

三個(gè)方法都是改變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方法的第一個(gè)參數(shù)是改變this指向的東西,可以是任何的數(shù)據(jù)類型。只不過如果是null或者是undefined就會(huì)指向window。<span style="color: red;font-weight: 900;">其他的參數(shù)</span>依次對(duì)應(yīng)函數(shù)的每一個(gè)形參。

如果使用apply

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

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

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

bind則是在函數(shù)定義的時(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ù)表達(dá)式 + 匿名函數(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í)候也是可以傳遞參數(shù)的,但是不要這樣使用,因?yàn)槭褂?code>bind后你不調(diào)用函數(shù)那么參數(shù)還是沒有作用。既然還是要調(diào)用函數(shù),我們一般就把函數(shù)的實(shí)參傳遞到調(diào)用的括號(hào)里面。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

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

友情鏈接更多精彩內(nèi)容