JS函數及作用域及異常的處理

在這裏插入圖片描述

JS語法

函數

function 函數名(參數列表) {
    函數體;
    return 返回值; 
}

function add(x,y){
    return x+y; 
}
console.log(add(3,5));

函數表達式

使用表達式來定義函數,表達式中的函數名可以省略,如果這個函數名不省略,也只能用在此函數內部。

// 匿名函數
const add = function(x, y){
    return x + y;
};
console.log(add(4, 6));

// 有名字的函數表達式
const sub = function fn(x, y){
    return x - y;
};
console.log(sub(5, 3));
//console.log(fn(3, 2)); // fn只能用在函數內部

// 有名字的函數表達式
const sum = function _sum(n) {
    if (n===1) return n;
    return n + _sum(--n) // _sum只能內部使用
}
console.log(sum(4));

函數、匿名函數、函數表達式的差異

  1. 函數和匿名函數,本質上都是一樣的,都是函數對象,只不過函數有自己的標識符——函數名,匿名函數需要藉助其它的標識符而已。
  2. 區別在於,函數會聲明提升,函數表達式不會。
console.log(add(4, 6));
// 匿名函數
function add (x, y){ // 聲明提升
    return x + y;
};

//console.log(sub(5, 3)); //sub未定義
// 有名字的函數表達式
const sub = function (x, y){
    return x - y;
};
console.log(sub(5, 3));

高階函數

高階函數:函數作爲參數或返回一個函數

完成一個計數器counter

const counter = function (){
    let c = 0;
    return function(){
        return ++c;
    };
};

const c = counter()
console.log(c())
console.log(c())
console.log(c())

完成一個map函數:可以對某一個數組的元素進行某種處理

const map = function(arr,fn) {
    let newarr = [];
    for (let i in arr){
        newarr[i] = fn(arr[i]);
    }
    return newarr
}

console.log(map([1,2,3,4], function(x){return ++x}));

另附counter的生成器版本,僅供參考

const counter = (function * () {
    let count = 1;
    while (true)
        yield count++;
})();

console.log(counter.next())
console.log(counter.next())

箭頭函數

箭頭函數就是匿名函數,它是一種更加精簡的格式。
將上例中的你們函數更改爲箭頭函數

// 以下三行等價
console.log(map([1,2,3,4], (x) => {return x*2}));
console.log(map([1,2,3,4], x => {return x*2}));
console.log(map([1,2,3,4], x => x*2));

箭頭函數參數

  1. 如果一個函數沒有參數,使用()
  2. 如果只有一個參數,參數列表可以省略小括號()
  3. 多個參數不能省略小括號,且使用逗號間隔

箭頭函數返回值

  1. 如果函數體部分有多行,就需要使用{},如果有返回值使用return。
  2. 如果只有一行語句,可以同時省略大括號和return。
  3. 只要有return語句,就不能省略大括號。 console.log(map([1,2,3,4], x => {return ++x})) ,有return必須有大括號。
  4. 如果只有一條非return語句,加上大括號,函數就成了無返回值了,例如 console.log(map([1,2,3,4], x => {x2})); 加上了大括號,它不等價於 x =>{return x2} 。因此,記住 x => x*2 這種正確的形式就行了。

函數參數

普通參數

一個參數佔一個位置,支持默認參數

const add = (x,y) => x+y;
console.log(add(4,5));

// 缺省值
const add1 = (x,y=5) => x+y;
console.log(add1(4,6));
console.log(add1(4));

那如果有這樣一個函數

const add2 = (x=6,y) => x+y;

這可以嗎?嘗試使用一下

console.log(add2());
console.log(add2(1));

console.log(add2(y=2,z=3)); // 可以嗎?

上面add2的調用結果分別爲
NaN、NaN、5
爲什麼?

  1. JS中並沒有Python中的關鍵字傳參
  2. JS只是做參數位置的對應
  3. JS並不限制默認參數的位置
    add2()相當於add(6, undefined)
    add2(1)相當於add(1, undefined)
    add2(y=2,z=3)相當於add2(2,3),因爲JS沒有關鍵字傳參,但是它的賦值表達式有值,y=2就是2,z=3就是3
    建議,默認參數寫到後面,這是一個好的習慣。

可變參數(rest parameters剩餘參數)

JS使用…表示可變參數(Python用*收集多個參數)

const sum = function (...args){
    let result = 0;
    for (let x in args){
        result += args[x];
    }
    return result;
};

console.log(sum(3,6,9))

arguments對象

函數的所有參數會被保存在一個arguments的鍵值對字典對象中

(function (p1, ...args) {
    console.log(p1);
    console.log(args);
    console.log('----------------');
    console.log(arguments); // 對象
    for (let x of arguments)
        console.log(x);
})('abc', 1,3,5)

ES6之前,arguments是唯一可變參數的實現
ES6開始,不推薦,建議使用可變參數。爲了兼容而保留
注意,使用箭頭函數,取到的arguments不是我們想要的,如下

((x,...args) => {
    console.log(args); // 數組
    console.log(x);
    console.log(arguments); // 不是傳入的值
})(...[1,2,3,4]);

參數解構

和Python類似,Js提供了參數解構,依然使用了…符號來解構

const add = (x, y) => {console.log(x,y);return x + y};
console.log(add(...[100,200]))
console.log(add(...[100,200,300,3,5,3]))
console.log(add(...[100]))

Js支持參數解構,不需要解構後的值個數和參數個數對應

函數返回值

python 中可以使用 return 1,2 返回多值,本質上也是一個值,就是一個元組。Js中呢?

const add = (x, y) => {return x,y};
console.log(add(4,100)); // 返回什麼?

表達式的值:
類C的語言,都有一個概念——表達式的值
賦值表達式的值:等號右邊的值
逗號表達式的值:類C語言,都支持逗號表達式,逗號表達式的值,就是最後一個表達式的值

a = (x = 5, y = 6, true);
console.log(a); // true

b = (123, true, z = 'test')
console.log(b)

function c() {
    return x = 5, y = 6, true, 'ok'; 
}

console.log(c()); // ok

所以,JS的函數返回值依然是單值

作用域

// 函數中變量的作用域
function test(){
    a = 100;
    var b = 200;
    let c = 300;
}
// 先要運行test函數
test()

console.log(a);
console.log(b); // 不可見
console.log(c); // 不可見

// 塊作用域中變量
if (1){
    a = 100;
    var b = 200;
    let c = 300; 
}

console.log(a);
console.log(b);
console.log(c); // 不可見

function是函數的定義,是一個獨立的作用域,其中定義的變量在函數外不可見
var a = 100 可以提升聲明,也可以突破非函數的塊作用域
a = 100 隱式聲明不能提升聲明,在“嚴格模式”下會出錯,但是可以把變量隱式聲明爲全局變量。建議少用
let a = 100 不能提升聲明,而且不能突破任何的塊作用域。推薦使用

function show(i, arg) {
    console.log(i, arg) 
}

// 作用域測試
x = 500;
var j = 'jjjj';
var k = 'kkkk';

function fn(){
    let z = 400;
    {
        var o = 100; // var 作用域當前上下文
        show(1, x);
        t = 'free'; // 此語句執行後,t作用域就是全局的,不推薦
        let p = 200;
    }
    var y = 300;
    show(2,z);
    show(3,x);
    show(4,o);
    show(5,t);
    //show(6,p); // 異常,let出不來上一個語句塊
    {
        show(7,y);
        show(8,o);
        show(9,t);
        {
            show(10,o);
            show(11,t);
            show(12,z);
        }
    }

    j = 'aaaa';
    var k = 'bbbb';
    show(20, j);
    show(21, k);
}

// 先執行函數
fn()

show(22, j);
show(23, k);

//show(13,y); // 異常,y只能存在於定義的上下文中,出不了函數
show(14,t); // 全局,但是嚴格模式會拋異常

//show(15,o) // 看不到o,異常原因同y

show(16,z); // 變量聲明提升,var聲明瞭z,但是此時還沒有賦值
var z = 10;

const m = 1
//m = 2 // 常量不可以重新賦值

嚴格模式:使用"use strict";,這條語句放到函數的首行,或者js腳本首行

異常

拋出異常

Js的異常語法和Java相同,使用throw關鍵字拋出
使用throw關鍵字可以拋出任意對象的異常

throw new Error('new error');
throw new ReferenceError('Ref Error');
throw 1;
throw 'not ok';
throw [1,2,3];
throw {'a':1};
throw () => {}; // 函數

捕獲異常

try…catch 語句捕獲異常
try…catch…finally 語句捕獲異常,finally保證最終一定執行
注意這裏的catch不支持類型,也就是說至多一個catch語句。可以在catch的語句塊內,自行處理異常

try {
    //throw new Error('new error');
    //throw new ReferenceError('Ref Error');
    //throw 1;
    //throw new Number(100);
    // throw 'not ok';
    // throw [1,2,3];
    // throw {'a':1};
    throw () => {}; // 函數
} catch (error) {
    console.log(error);
    console.log(typeof(error));
    console.log(error.constructor.name);
} finally {
    console.log('===end===') 
}

Symbols類型

ES6提供Symbol類型,內建原生類型

let sym1 = Symbol()
let sym2 = Symbol('key1')
let sym3 = Symbol('key1')
console.log(sym2 == sym3) // false,symbol值是唯一的
  1. 作爲對象的屬性key
let s = Symbol()
let t = 'abc'
let a = {
    [s]:'xyz', // symbol做key,注意要使用中括號,這個key一定唯一t:'ttt', 
    [t]:'ooo'
}

console.log(a)
console.log(a[s])
a[s] = 2000
console.log(a[s])
  1. 構建常量
// 以前用法
var COLOR_RED = 'RED';
var COLOR_ORANGE = 'ORANGE';
var COLOR_YELLOW = 'YELLOW';
var COLOR_GREEN = 'GREEN';
var COLOR_BLUE = 'BLUE';
var COLOR_VIOLET = 'VIOLET';

// 現在
const COLOR_RED = Symbol();
const COLOR_ORANGE = Symbol();
const COLOR_YELLOW = Symbol();
const COLOR_GREEN = Symbol();
const COLOR_BLUE = Symbol();
const COLOR_VIOLET = Symbol();
  1. 補充
let z = 'abc'
let obj = {z:z}
console.log(obj)
Info: Start process (下午3:55:21)
{ z: 'abc' }
Info: End process (下午3:55:21)


let z = 'abc'
let obj = {[z]:z} // [z]取z的值
console.log(obj)
Info: Start process (下午3:54:00)
{ abc: 'abc' }
Info: End process (下午3:54:01)
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章