02函數-01-函數的定義和調用


1、定義函數

function abs(x) { 
    if (x >= 0) {
        return x;
    } else {
        return -x;
    }
}
如果沒有return,函數執行完畢也會返回結果,不過結果是undefined。

由於在JS的世界中,函數也是一個對象,所以上述定義的 函數實際上也是一個對象,而 函數名則可視為指向該函數的變量

所以上面的函數定義等價於:
var abs = function (x) {
    if (x >= 0) {
        return x;
    } else {
        return -x;
    }
};

2、調用函數

JS的函數允許任意個,也不影響調用,接以上的abs函數:
//比定義的參數多
abs(10, 'blablabla'); // 返回10
abs(-9, 'haha', 'hehe', null); // 返回9

//比定義的參數少
abs(); // 返回NaN --> 此時abs(x)函數的參數x將收到undefined,計算結果為NaN

JS中有個關鍵字 arguments,只在函數內部起作用,永遠指向當前函數的調用者傳入的所有參數,類似Array但不是Array:

實際上arguments最常用於判斷傳入參數的個數。你可能會看到這樣的寫法:
// foo(a[, b], c)
// 接收2~3個參數,b是可選參數,如果只傳2個參數,b默認為null:
function foo(a, b, c) {
    if (arguments.length === 2) {
        // 實際拿到的參數是a和b,c為undefined
        c = b; // 把b賦給c
        b = null; // b變為默認值
    }
    // ...
    //要把中間的參數b變為“可選”參數,就只能通過arguments判斷,然后重新調整參數並賦值
}

為了獲得多余的參數,我們也就是利用arguments,但是稍微有點不方便:
function foo(a, b) {
    var i, rest = [];
    if (arguments.length > 2) {
        for (i = 2; i<arguments.length; i++) {
            rest.push(arguments[i]);
        }
    }
    console.log('a = ' + a);
    console.log('b = ' + b);
    console.log(rest);
}
ES6標准引入了rest參數,上面的函數可以改寫為:
function foo(a, b, ...rest) {
    console.log('a = ' + a);
    console.log('b = ' + b);
    console.log(rest);
}

foo(1, 2, 3, 4, 5);
// 結果:
// a = 1
// b = 2
// Array [ 3, 4, 5 ]

foo(1);
// 結果:
// a = 1
// b = undefined
// Array []
//如果傳入的參數連正常定義的參數都沒填滿,也不要緊,rest參數會接收一個空數組(注意不是undefined)

拓展思維:
//用rest參數編寫一個sum()函數,接收任意個參數並返回它們的和

'use strict';

function sum(...rest) {
  var result = 0;
  for(var value of rest){
    result += value;
  };
  return result;
}

// 測試:
var i, args = [];
for (i=1; i<=100; i++) {
    args.push(i);
}
if (sum() !== 0) {
    alert('測試失敗: sum() = ' + sum());
} else if (sum(1) !== 1) {
    alert('測試失敗: sum(1) = ' + sum(1));
} else if (sum(2, 3) !== 5) {
    alert('測試失敗: sum(2, 3) = ' + sum(2, 3));
} else if (sum.apply(null, args) !== 5050) {
    alert('測試失敗: sum(1, 2, 3, ..., 100) = ' + sum.apply(null, args));
} else {
    alert('測試通過!');
}

//定義一個計算圓面積的函數area_of_circle(),它有兩個參數:r: 表示圓的半徑;pi: 表示π的值,如果不傳,則默認3.14

'use strict';
function area_of_circle(r, pi) {
  var temp = 3.14;
  if(arguments.length > 1){
      temp = pi;
  }
  return temp*r*r;
}

// 測試:
if (area_of_circle(2) === 12.56 && area_of_circle(2, 3.1416) === 12.5664) {
    alert('測試通過');
} else {
    alert('測試失敗');
}


//網友答約:pi=pi?pi:3.14; return pi*r*r;

3、變量作用域

在JavaScript中,我們說如果一個變量不用var申明,那么默認作為全局變量。而我們使用了var申明的變量,實際上也是有作用域的。

  • 如果函數內申明,則函數體外不可引用
  • 函數可以嵌套,內部函數的變量可以訪問外部函數的變量,反之不可
  • 內部與外部變量重名的話,查找變量從內往外,則屏蔽外部函數變量

JS中的變量申明會“提升”到函數頂部,賦值不提升,這句話是什么意思呢?看下面的代碼:
'use strict';

function foo() {
    var x = 'Hello, ' + y;
    alert(x);
    var y = 'Bob';
}

foo();

y雖然申明在x之后,但是 var x = 'Hello,' + y 並不會報錯(這就是變量申明的提升),但是alert出來的是 Hello, undefined,即y的值還是undefined(這就是賦值不提升)。

實際上JavaScript引擎看到的代碼相當於:
function foo() {
    var y; // 提升變量y的申明
    var x = 'Hello, ' + y;
    alert(x);
    y = 'Bob';
}

這個特性其實並沒有什么好處,反而容易讓人犯錯,所以按像Java語言中一樣的變量申明習慣就好了,需要用的變量先申明,再使用。

3.1 全局作用域

JavaScript默認有一個全局對象window, 全局作用域的變量實際上被綁定到window的一個屬性,因此,假如你定義了一個全局作用域的變量 test,那么是 直接訪問test和訪問window.test是一樣的

由於函數定義也有兩種方式,其中 以變量方式定義的函數實際上也是一個全局變量,被綁定在window對象,你可以通過 window.test() 類似的方法調用,事實上,alert()方法也是window的一個變量!

全局變量會綁定在window上,那么當你 引入不同的文件,文件之間如果用到了相同的全局變量或同名函數,就會造成命名沖突,並且很難被發現。

減少沖突的一個方法就是把自己的所有的全局變量和函數,全部綁定到一個全局變量中:
// 唯一的全局變量MYAPP:
var MYAPP = {};

// 其他變量:
MYAPP.name = 'myapp';
MYAPP.version = 1.0;

// 其他函數:
MYAPP.foo = function () {
    return 'foo';
};

//把自己的代碼全部放入唯一的名字空間MYAPP中,會大大減少全局變量沖突的可能

3.2 局部作用域

在之前我的理解,比如在函數內定義的變量,因為作用域在函數內,所以就算是局部作用域了,而下面的代碼:
'use strict';

function foo() {
    for (var i=0; i<100; i++) {
        //
    }
    i += 100; // 仍然可以引用變量i
}

即是說,實際上 這種默認方式的作用域並不完全是局部的,至少在for循環語句塊中的作用域,在for循環之外還可以引用(媽呀JavaScript不愧是10天寫出來的函數,確實很厲害但是有些地方真的很討厭),為了解決這個問題,ES6引入了新的關鍵字 let,這個可以替代var 申明一個塊級作用域的變量
'use strict';

function foo() {
    var sum = 0;
    for (let i=0; i<100; i++) {
        sum += i;
    }
    i += 1; // SyntaxError
}

3.3 常量

在ES6標准之前,我們表示常量通常都使用全部大寫的方式來“告訴“使用者,不要修改它的值,但實際上這個值是可以被修改了,是存在風險的。在ES6標准中引入了新的 關鍵字const,可以申明不可改變的常量,且同let一樣具有塊級作用域



免責聲明!

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



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