JavaScript函數(shù)


layout: post
title: JavaScript函數(shù)
tags: [JavaScript, 函數(shù)]
author: 楊比軒


函數(shù)定義和調(diào)用

簡單的函數(shù)定義

//求絕對值
function abs(x){
    if(x < 0)
      return -x;
    else
    return x;
}

和java語法類似:

  • function 函數(shù)關(guān)鍵字申明
  • abs 函數(shù)名
  • (x) 參數(shù)
  • {....}函數(shù)體

JS如果沒有return語句,函數(shù)執(zhí)行完畢會返回undefined

本著萬物皆對象的原則,function也是對象,所以:

var abs = function(x){
  ....
};

此時函數(shù)是一個匿名函數(shù),變量abs指向匿名函數(shù),本質(zhì)上和上面那種方式?jīng)]有區(qū)別,但是這里有一點要注意,此處其實是一個賦值語句,所以末尾要加上;

函數(shù)的調(diào)用

正常調(diào)用不做贅述,說說比較特殊的地方。

JS函數(shù)允許傳遞多個參數(shù)或者不傳遞參數(shù),所以:

abs(1,2,“123”,4,5);//后面的參數(shù)不會生效,結(jié)果返回 1
abs(); //結(jié)果返回 NaN

返回NaN解釋:函數(shù)傳遞的x變成undefined,計算結(jié)果變成NaN

所以,為了避免這種情況的產(chǎn)生,可以做健壯性處理:

function abs(x){
  if(x !== 'number'){
    throw 'not a number';
  }
  
  if(x > 0)
    return -x;
  else
    return x;
}

arguments

JS的函數(shù)默認會有一個arguments參數(shù),此參數(shù)指向函數(shù)的參數(shù)集,有點類似于array,但是不是array。

可以使用arguments的length屬性,來判斷參數(shù)的個數(shù)和對參數(shù)進行訪問

demo:

function getSum(x,y,z){
    var num = 0;
    console.log("arguments length:" + arguments.callee);
    for(var i = 0 ; i < arguments.length ; i++){
        console.log(arguments[i]);
        num = num + arguments[i];
    }
    return num;
}
console.log(getSum(1,2,3,4,5,6,7,8,9,10));
//輸出:1,2,3,4,5,6,7,8,9,10,55

rest

ES6引入的新標準,用于處理多余傳入函數(shù)內(nèi)的參數(shù),用法:

function restDemo(x, y, ...rest){
    console.log("x="+x);
    console.log("y="+y);
    console.log("rest=" + rest);
}
restDemo(1,2,3,4,5,6);
//輸出:
//x=1
//y=2
//rest=3,4,5,6

關(guān)于return

JS有自動在行末加;的機制,所以要小心return語句

demo:

//return了一個匿名對象,這種是常規(guī)情況,可以正常運行
function returnDemo(){
    return {name:'luke'};
}

//return的代碼過長時,需要換行,此時js會自動加`;`在行末,所以返回undefined
function returnDemo(){
    return
    { name:'luke'};
}

//這種的也正常,{ 表示此行未結(jié)束,會阻斷添加 ; 
function returnDemo(){ 
    return {
      name:'luke'
    };
}

變量的作用域

JS的函數(shù)可以嵌套,所有當兩個嵌套的函數(shù)的變量重名后,內(nèi)部可以調(diào)用外部變量,外部則不能訪問內(nèi)部。

'use strict';

function foo() {
    var x = 1;
    function bar() {
        var y = x + 1; // bar可以訪問foo的變量x!
    }
    var z = y + 1; // ReferenceError! foo不可以訪問bar的變量y!

此時如果兩個參數(shù)名字相同,則內(nèi)部參數(shù)相當于重寫了外部參數(shù),會達到屏蔽的效果。

'use strict';

function foo() {
    var x = 1;
    function bar() {
        var x = 'A';
        console.log('x in bar() = ' + x); // 'A'
    }
    console.log('x in foo() = ' + x); // 1
    bar();
}

變量的自動提升

在JS中可以引用稍后申明的變量而不會引發(fā)異常就是因為有自動變量提升(variable hoisting),但是提升也會帶來一些問題。

console.log(x === undefined);//logs true
var x = 3;

//上述代碼相當于
var x;
console.log(x === undefined);
x = 3;

分析:

var x = 3;相當于一個申明變量語句和一個賦值語句,但是自動提升只會提升申明語句var x;僅此而已。所以log輸出就是true

所以,一段代碼或者函數(shù)中的var語句應(yīng)該盡可能的放在接近代碼段的頂部位置,一避免產(chǎn)生麻煩。

全局變量

全局變量相當于全局對象的屬性,在網(wǎng)頁中全局對象是window,所以可是使用window.variable來訪問和設(shè)置全局變量。
所以在函數(shù)體之用var或者const申明的變量既是全局變量,也是window對象的屬性,。
無論是function還是var聲明的變量,在js中都是變量,也就是window的屬性,當前前提是function和var都全局的。

局部變量
for或者if中聲明的變量其實在{}也可以引用,因為js的變量作用域其實是函數(shù)內(nèi)部,而for等不是函數(shù)。
ES6引入了新標準,let關(guān)鍵字可以替代var可以申明塊級的作用域的變量。

{
    let abc = "qweqweqweqweqweqweq";
}
console.log(abc);//logs:Uncaught ReferenceError: abc is not defined(…)

常量
ES6添加的新特性,const關(guān)鍵字申明一個固定值的常量。

命名空間
javascript實際上只有一個全局作用域。任何變量如果在當前函數(shù)作用域中找到,就會繼續(xù)往上找(上指的是父類),最后再全局作用中也沒有找到,就匯報ReferenceError錯誤。

全局變量會綁定到window上,不同的js文件如果使用了相同的全局變量,就會造成明明沖突,并且難以發(fā)現(xiàn)。

避免這個問題的方法就是把所有的變量和函數(shù)全部綁定到一個全局變量中,如:

//全局變量
var MMP = {};

//其它變量
MMP.a='a';
MMP.b = function(){
    console.log("this is function");
}

這樣就可以將需要的變量申明在MMP下。

對象方法

在對象內(nèi)部定義的函數(shù)也叫方法,和普通的函數(shù)其實沒啥區(qū)別,但是此種方法可以使用this關(guān)鍵字來指向當前調(diào)用方法的對象。

var person = {
    name:"bixuan",
    age:23
    //此方法就是返回對象的age屬性的value
    getAge:function(){

        return this.age;
    }
}

如果此時getAge()方法寫在person對象之外時,當person.getAge()種調(diào)用時,可以獲得和上面一樣的結(jié)果,但是要是單獨的調(diào)用getAge()

var person = {
    name:"bixuan",
    age:23,
    getage:getAge
}
function getAge(){
    return this.age;
}

person.getage();// 結(jié)果就是23
getAge();//結(jié)果是 undefined

person.getage指向的就是函數(shù)getAge(),函數(shù)中的this指向的就是person對象,所以返回的就是23,而直接運行getAge()this指向的是window對象,因為沒有age屬性,所以結(jié)果是undefined。

還有一種情況就是函數(shù)的多層嵌套,也會導(dǎo)致this的調(diào)用出現(xiàn)問題。

var person = {
    name:"bixuan",
    age:23,
    
    getage:function(){

        function getAge(){
            return this.age;
        }
    }
}

person.getage();//結(jié)果是 undefined

原因就是this又重新指向了undefined了,當然如果處于非strict模式下,thi又會指向window。為了避免這種情況的發(fā)生,可以在外層加上一句:

...
var that = this; //用that來存儲this變量,供內(nèi)層調(diào)用
function getAge(){
    return that.age;
}
...

apply()

函數(shù)對象都會有一個apply()方法,此方法接收兩個參數(shù),一個是需要綁定的this對象,第二個參數(shù)是一個Array,存儲的是函數(shù)本來的參數(shù)。

function sum(x, y){
    console.log(this.age);
    console.log( x + y);
}

sum.apply({age:23},[1,2]);
//輸出 23,3

call()和apply類似,不同的是call第二個參數(shù)不是array,而是按順序?qū)⒃瘮?shù)所有的參數(shù)順序傳入。`call({},參數(shù)1,參數(shù)2,...)

利用apply的這個特性,可以用來實現(xiàn)裝飾器。

裝飾器:對已有的函數(shù)方法進行裝飾,在實現(xiàn)原有功能的基礎(chǔ)上,添加額外的功能,或者增強原有功能。

function pri(){
    console.log("this is a demo");
}

var count = 0;
(function newPro(){
    count++;

    pri.apply(null,[]);
    
})();

上述例子就實現(xiàn)了對函數(shù)pri的裝飾,在執(zhí)行原有代碼的同時,對其運行的次數(shù)進行了統(tǒng)計。

高階函數(shù)

普通函數(shù)接受的參數(shù)都是數(shù)據(jù)類型,高階函數(shù)就是把函數(shù)對象當作參數(shù)進行傳遞。

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

function useSum(x, y, sum){

    return sum(x, y);
}
  1. map/reduce

map()方法相當于對arr中的數(shù)據(jù)按照傳入的參數(shù)函數(shù)進行一次迭代。

function pow(x){
    return x*x;
}

var arr = [1, 2, 3, 4, 5];

arr.map(pow);// [1,2,9,16,25]

reduce()其實也是對arr中的數(shù)據(jù)進行迭代,但是和map不同的是,reduce()相當于嵌套迭代。

需要注意的是:reduce()必須接收兩個參數(shù)的函數(shù)

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

var arr = [1, 2, 3, 4, 5];

arr.reduce(sum);//15

**2. filter **

不知道官方的叫法是什么,應(yīng)該翻譯做過濾器吧。使用arr調(diào)用,并傳入返回true/false來對arr中的元素進行判定從而進行過濾。

var arr = [1,2,3,4,5];
var newArr = arr.filter(function(x){
    if(x%2 === 0){
        return true;
    }else{
        return false;
    }
})

console.log(arr);//[1,2,3,4,5]
console.log(newArr);//[2,4]

filter()不會更改調(diào)用數(shù)組的值,而是返回一個新的arr對象。

回調(diào)函數(shù)

filter接受回調(diào)函數(shù)。

  • 當回調(diào)函數(shù)一個參數(shù)時,代表數(shù)組的元素
  • 當對調(diào)函數(shù)三個參數(shù)時,分別為:數(shù)組的元素,元素索引,數(shù)組本身
arr.filter(function(a,b,c){
    console.log(a);
    console.log(b);
    console.log(c);
})

所以可以利用filter來實現(xiàn)對arr的去重復(fù),去空元素等等操作。

//去掉arr中的重復(fù)元素
//這里利用的就是indexOf返回的是數(shù)組中該元素第一次出現(xiàn)的索引
//來實現(xiàn)對arr的去重讀
var strs = ['a','b','c','d','e','b','c','e','g','a'];

var newStrs = strs.filter(function(element, index, arr){
    if(arr.indexOf(element) == index){
        return true;
    }
    else{
        return false;
    }
})

console.log(newStrs);

3.sort

sort應(yīng)該可以翻譯為排序器吧,類似java中的comparable,只需要傳入一個排序的方法,就能對調(diào)用的數(shù)組進行排序。

sort默認是將數(shù)組的元素轉(zhuǎn)換為String對象后進行排序,排序的依據(jù)是ASCII值的大小。

//按從小到大的循序排序
var nums = [12312,345345,24654356,234234,2342,4564,2342,6786,34,85,24558,62,48,4,512,5,95623];

console.log(nums.sort(function(x,y){
    if(x > y){
        return 1;
    }else{
        return -1;
    }
    return 0;
}))

通過傳入的比較方法的不同,可實現(xiàn)不同的實現(xiàn)效果,所以重點在于傳入的比較函數(shù)。

閉包

一般的方法和函數(shù)都是返回計算的結(jié)果,也就是基本數(shù)據(jù)類型和對象。當函數(shù)返回的是包含參數(shù)的函數(shù)對象時,這種的就稱為閉包。

var nums = [12312,345345,24654356,234234,2342,4564,2342,6786,34,85,24558,62,48,4,512,5,95623];
function lazy_sum(arr){
    var sum = function(){
        return arr.reduce(function(x,y){
            return x + y;
        })
    }
    return sum;
}

var result = lazy_sum(nums);

console.log(result);//[Function: sum]
console.log(result());//25383212

打印結(jié)果說明,result此時是一個函數(shù),調(diào)用此函數(shù),輸出結(jié)果為sum()函數(shù)執(zhí)行的結(jié)果。
若此時繼續(xù)執(zhí)行代碼:

nums.push(123123);
console.log(result());//25506335

但需要注意的是,此時因為傳入的引用數(shù)據(jù)類型,所以當此數(shù)據(jù)的值發(fā)生變化時,最終的結(jié)果也會受到影響。

箭頭函數(shù)

箭頭函數(shù)有點類似于java 8中的新特性。


var f = x => x+1;
//和下面的寫法實現(xiàn)了相同的功能

var f = function(x){
    return x+1;`
}

此處由于函數(shù)體比較簡單,所以省略了{...} return,并且當參數(shù)不止一個時,也需要使用(...)括起來。所以,箭頭函數(shù)也有以下幾種寫法:

(x,y) => x + y; //多個參數(shù)需要用括號括起來
(x,y) => {  //實現(xiàn)復(fù)復(fù)雜邏輯體時,需要使用{}括起來
    if(x > y)
        return true;
    else
        return false;
}

/*空參也需要括號。像這種直接返回對象的時候,需要使用()把括號
把對象括起來,否則引起函數(shù)體和對象體的語法沖突而報錯
*/
() => ({name:"bixuan"}) 。

箭頭函數(shù)同時也修復(fù)了this的歷史遺留問題,不是在多層調(diào)用時指向window或者undefined了,而是指向詞法作用域,也就是外層調(diào)用這的OBJ。

//以前的寫法會出現(xiàn)undefined或者指向的了window
var obj = {
    birth: 1990,
    getAge: function () {
        var b = this.birth; // 1990
        var fn = function () {
            return new Date().getFullYear() - this.birth; // this指向window或undefined
        };
        return fn();
    }
};

//而箭頭函數(shù)則不會有這個問題出現(xiàn)
var obj = {
    birth: 1990,
    getAge: function () {
        var b = this.birth; // 1990
        var fn = () => new Date().getFullYear() - this.birth; // this指向obj對象
        return fn();
    }
};
obj.getAge(); // 25

但是有一點也需要注意,就是箭頭函數(shù)因為綁定了this,所以再調(diào)用調(diào)用call()或者apply()的時候,傳入的第一個參數(shù)無法對this進行綁定,還是會和上面一樣,綁定在外層調(diào)用者上,所以call({...},X,Y)的第一個對象參數(shù)會被忽略掉。

var obj = {
    birth: 1990,
    getAge: function(year){
        var b = this.birth;//1990
        var fn = (y) => y - this.birth;//此時,this.birth還是1990

        return fn.call({birth:3000},year);
    }
};
var obj2 = {
    birth: 1990,
    getAge: function(year){
        var b = this.birth;//1990
        // var fn = (y) => y - this.birth;//此時,this.birth還是1990
        var fn  = function(y){
            return y - this.birth;
        };

        return fn.call({birth:3000},year);
    }
};


console.log(obj.getAge(2016));//結(jié)果是26
console.log(obj2.getAge(2016));//-984
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

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

  • 函數(shù)函數(shù)定義與調(diào)用變量作用域全局變量方法高階函數(shù)閉包箭頭函數(shù)$generator$ 函數(shù) 函數(shù)定義與調(diào)用 定義函數(shù)...
    染微言閱讀 688評論 0 5
  • 本文是大神廖雪峰的JavaScript教程學(xué)習筆記。并不是教程,如有需要,請前往廖雪峰大神大博客. 一、函數(shù)定義和...
    0o凍僵的企鵝o0閱讀 570評論 1 3
  • 函數(shù)就是最基本的一種代碼抽象的方式。 定義函數(shù)function abs(x) {if (x >=0){return...
    _我和你一樣閱讀 510評論 0 0
  • 本文只是關(guān)于JavaScript函數(shù)的一些入門介紹,寫的有些寬泛和簡單,目的是對JavaScript的函數(shù)有一個大...
    小松_路飛閱讀 562評論 0 2
  • 在JavaScript中,函數(shù)即對象,程序可以隨意操控它們。比如,JavaScript可以把函數(shù)賦值給變量,或者作...
    kissLife閱讀 1,051評論 0 0

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