1. 程式人生 > 其它 >Java 定時任務

Java 定時任務

1. 函式的定義和呼叫

函式的命名規範與變數類似,不再贅述。

1.1. 函式定義

JavaScript 的函式可以巢狀,也就是函式內部可以再定義函式。

// 引數列表不是必須的,可空
function fun(a, b, c, str) {
    // do something
    return str; //沒有return,函式執行完也會返回結果,為undefined。
}
// 第二種函式定義,匿名函式,配合事件使用
var num = function(x) {
    // do something
    return x;
};
// 在這種方式下,函式定義成匿名函式,無函式名。但函式賦值給了變數 abs,
// 所以通過變數 abs 就可以呼叫該函式。
// 以上兩種定義完全等價。

1.2. 函式呼叫

// 1.直接呼叫
function fun(a, b) {
    return a+b;
}
var result = fun(3, 5);

// 2.事件呼叫
<button onclick='fun(3, 5)'>點我呼叫方法</button>s

1.3. 自執行函式

自執行函式就是指無需手動呼叫,在宣告函式時自動呼叫的函式型別。

// 宣告自執行函式的語法結構有三種
// 1.!function(形參列表){}(實參列表);
!function(num1, num2) {
    var sum = num1 + num2;
}(1, 2);

// 2.(function(形參列表){}(實參列表));
// 這種寫法使用圓括號將匿名函式及之後的圓括號包裹成為一個整體。
(function(num1, num2) {
    var sum = num1 + num2;
}(1, 2));

// 3.(function(形參列表){})(實參列表);
(fuction(num1, num2) {
    var sum = num1 + num2;
})(1, 2);

1.4. arguments 引數

arguments 關鍵字只在函式內部起作用,並且永遠指向當前函式的呼叫者傳入的所有引數。arguments 類似 Array 但它不是一個 Array

function foo(x) {
    console.log('x = ' + x); // 10
    for (var i=0; i<arguments.length; i++) {
        console.log('arg ' + i + ' = ' + arguments[i]); // 10, 20, 30
    }
}
foo(10, 20, 30); //x = 10 arg 0 = 10 arg 1 = 20 arg 2 = 30

利用 arguments,你可以獲得呼叫者傳入的所有引數。也就是說,即使函式不定義任何引數,還是可以拿到引數的值:

function abs() {
    if (arguments.length === 0) {
        return 0;
    }
    var x = arguments[0];
    return x >= 0 ? x : -x;
}

abs();     // 0
abs(10);   // 10
abs(-9);   // 9

實際上 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變為預設值
    }
    // ...
}

arguments 物件除了可以儲存實參列表外,還有一個重要的屬性 callee,用於返回 arguments 所在函式的引用。arguments.callee() 可以呼叫自身函式執行,也被稱為遞迴,因此 arguments.callee() 是遞迴呼叫。

// 在 strict 模式下 callee 不可用
var num = 1;
function func() {
    console.log(num);
    num++;
    if(num <= 5) { // 當 num<=5 時,函式遞迴呼叫本身
        arguments.callee(); // 表示呼叫函式本身,效果與 func() 相同
    }
}

要把中間的引數 b 變為“可選”引數,就只能通過 arguments 判斷,然後重新調整引數並賦值。

1.5. rest 引數

由於 JavaScript 函式允許接收任意個引數,於是我們就不得不用 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);
}

為了獲取除了已定義引數 a、b 之外的引數,我們不得不用 arguments,並且迴圈要從索引2開始以便排除前兩個引數,這種寫法很彆扭,只是為了獲得額外的 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 引數只能寫在最後,前面用 ... 標識,從執行結果可知,傳入的引數先繫結 a、b,多餘的引數以陣列形式交給變數 rest,所以,不再需要 arguments 我們就獲取了全部引數。如果傳入的引數連正常定義的引數都沒填滿,也不要緊,rest 引數會接收一個空陣列(注意不是 undefined)。

2. 變數作用域

在 JavaScript 中,變數只有函式作用域,也即函式內宣告的變數只有函式內部能用。

  • 在函式外,無論是否使用 var 宣告變數,都是全域性變數,在整個 JavaScript 檔案可用;
  • 在函式內,使用 var 宣告的變數為區域性變數,只在當前函式可用;
  • 在函式內,不用 var 宣告的變數依然為全域性變數,在整個 JavaScript 檔案可用;
  • 在 JavaScript 中,函式內部能夠使用全域性變數,而函式外部不能使用區域性變數。

JavaScript 預設有一個全域性物件 window,全域性作用域的變數實際上被繫結到 window 的一個屬性:

'use strict';

var course = 'Learn JavaScript';
alert(course);         // 'Learn JavaScript'
alert(window.course);  // 'Learn JavaScript'
// 因此,直接訪問全域性變數 course 和訪問 window.course 是完全一樣的。

全域性變數會繫結到 window 上,不同的 JavaScript 檔案如果使用了相同的全域性變數,或者定義了相同名字的頂層函式,都會造成命名衝突,並且很難被發現。減少衝突的一個方法是把自己的所有變數和函式全部繫結到一個全域性變數中。例如:

// 唯一的全域性變數MYAPP:
var MYAPP = {};

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

// 其他函式:
MYAPP.foo = function () {
    return 'foo';
};
// 把自己的程式碼全部放入唯一的名字空間 MYAPP 中,會大大減少全域性變數衝突的可能。

由於 JavaScript 的變數作用域實際上是函式內部,我們在 for 迴圈等語句塊中是無法定義具有區域性作用域的變數的:

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

為了解決塊級作用域,ES6 引入了新的關鍵字 let,用 let 替代 var 可以申明一個塊級作用域的變數:

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

ES6 標準引入了新的關鍵字 const 來定義常量,constlet 都具有塊級作用域:

'use strict';
const PI = 3.14;
PI = 3; // 某些瀏覽器不報錯,但是無效果!
PI;     // 3.14

解構賦值
從 ES6 開始,JavaScript 引入瞭解構賦值,可以同時對一組變數進行賦值。

// 1.傳統做法:把一個數組的元素分別賦值給幾個變數
var array = ['hello', 'JavaScript', 'ES6'];
var x = array[0];
var y = array[1];
var z = array[2];

// 2.在 ES6 中,可以使用解構賦值,直接對多個變數同時賦值
'use strict';
var [x, y, z] = ['hello', 'JavaScript', 'ES6'];
// x, y, z分別被賦值為陣列對應元素:
console.log('x = ' + x + ', y = ' + y + ', z = ' + z);

對陣列元素進行解構賦值時,多個變數要用 [...] 括起來。

如果陣列本身還有巢狀,也可以通過下面的形式進行解構賦值,注意巢狀層次和位置要保持一致:

let [x, [y, z]] = ['hello', ['JavaScript', 'ES6']];
x; // 'hello'
y; // 'JavaScript'
z; // 'ES6'

解構賦值還可以忽略某些元素:

let [, , z] = ['hello', 'JavaScript', 'ES6']; // 忽略前兩個元素,只對z賦值第三個元素
z; // 'ES6'

如果需要從一個物件中取出若干屬性,也可以使用解構賦值,便於快速獲取物件的指定屬性:

var person = {
    name: '小明',
    age: 20,
    gender: 'male',
    passport: 'G-12345678',
    school: 'No.4 middle school'
};
var {name, age, passport} = person;
// name, age, passport分別被賦值為對應屬性:

對一個物件進行解構賦值時,同樣可以直接對巢狀的物件屬性進行賦值,只要保證對應的層次是一致的:

var person = {
    name: '小明',
    age: 20,
    gender: 'male',
    passport: 'G-12345678',
    school: 'No.4 middle school',
    address: {
        city: 'Beijing',
        street: 'No.1 Road',
        zipcode: '100001'
    }
};
var {name, address: {city, zip}} = person;
name;     // '小明'
city;     // 'Beijing'
zip;      // undefined, 因為屬性名是zipcode而不是zip
// 注意: address不是變數,而是為了讓city和zip獲得巢狀的address物件的屬性:
address;  // Uncaught ReferenceError: address is not defined

使用解構賦值對物件屬性進行賦值時,如果對應的屬性不存在,變數將被賦值為 undefined,這和引用一個不存在的屬性獲得 undefined 是一致的。如果要使用的變數名和屬性名不一致,可以用下面的語法獲取:

var person = {
    name: '小明',
    age: 20,
    gender: 'male',
    passport: 'G-12345678',
    school: 'No.4 middle school'
};

// 把passport屬性賦值給變數id:
let {name, passport:id} = person;
name;     // '小明'
id;       // 'G-12345678'
// 注意: passport不是變數,而是為了讓變數id獲得passport屬性:
passport; // Uncaught ReferenceError: passport is not defined

解構賦值還可以使用預設值,這樣就避免了不存在的屬性返回 undefined 的問題:

var person = {
    name: '小明',
    age: 20,
    gender: 'male',
    passport: 'G-12345678'
};
// 如果person物件沒有single屬性,預設賦值為true:
var {name, single=true} = person;
name; // '小明'
single; // true

有些時候,如果變數已經被聲明瞭,再次賦值的時候,正確的寫法也會報語法錯誤:

// 宣告變數:
var x, y;
// 解構賦值:
{x, y} = { name: '小明', x: 100, y: 200};
// 語法錯誤: Uncaught SyntaxError: Unexpected token =
// 這是因為 JavaScript 引擎把 { 開頭的語句當作了塊處理,
// 於是 = 不再合法。解決方法是用小括號括起來:
({x, y} = { name: '小明', x: 100, y: 200});

解構賦值在很多時候可以大大簡化程式碼。例如,交換兩個變數 xy 的值,可以這麼寫,不再需要臨時變數:

var x=1, y=2;
[x, y] = [y, x];

// 快速獲取當前頁面的域名和路徑:
var {hostname:domain, pathname:path} = location;

如果一個函式接收一個物件作為引數,那麼,可以使用解構直接把物件的屬性繫結到變數中。例如,下面的函式可以快速建立一個 Date 物件:

function buildDate({year, month, day, hour=0, minute=0, second=0}) {
    return new Date(year + '-' + month + '-' + day + ' ' + hour + ':' + minute + ':' + second);
}
// 它的方便之處在於傳入的物件只需要 year、month 和 day 這三個屬性:
buildDate({ year: 2017, month: 1, day: 1 });
// Sun Jan 01 2017 00:00:00 GMT+0800 (CST)
// 也可以傳入 hour、minute 和 second 屬性:
buildDate({ year: 2017, month: 1, day: 1, hour: 20, minute: 15 });
// Sun Jan 01 2017 20:15:00 GMT+0800 (CST)

3. this 關鍵字

this 關鍵字指向當前函式呼叫語句所在的作用域。簡記:誰呼叫函式,this 指向誰。

function func() {
    // 直接在 window 物件中使用 func() 呼叫函式,this 指向 window 物件
    console.log(this);
}
func();

4. 方法

在一個物件中繫結函式,稱為這個物件的方法。

5. Map/Reduce

比如我們有一個函式 \(f(x) = x^2\) ,要把這個函式作用在一個數組[1, 2, 3, 4, 5, 6, 7, 8, 9]上,就可以用 map 實現如下:

'use strict';

function pow(x) {
    return x * x;
}
var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9];
var results = arr.map(pow); // [1, 4, 9, 16, 25, 36, 49, 64, 81]
document.write(results);

6. 箭頭函式

ES6 標準新增了一種新的函式:Arrow Function(箭頭函式)。為什麼叫Arrow Function?因為它的定義用的就是一個箭頭:

x => x * x
// 上面的箭頭函式相當於:
function (x) {
    return x * x;
}

箭頭函式相當於匿名函式,並且簡化了函式定義。箭頭函式有兩種格式,一種像上面的,只包含一個表示式,連 { ... }return 都省略掉了。還有一種可以包含多條語句,這時候就不能省略 { ... }return

x => {
    if (x > 0) {
        return x * x;
    }
    else {
        return - x * x;
    }
}

如果引數不是一個,就需要用括號 () 括起來:

/ 兩個引數:
(x, y) => x * x + y * y

// 無引數:
() => 3.14

// 可變引數:
(x, y, ...rest) => {
    var i, sum = x + y;
    for (i=0; i<rest.length; i++) {
        sum += rest[i];
    }
    return sum;
}

如果要返回一個物件,就要注意,如果是單表示式,這麼寫的話會報錯:

x => { foo: x }   // SyntaxError:
// 因為和函式體的{ ... }有語法衝突,所以要改為:
x => ({ foo: x }) // ok:

7. 包裝物件

  • 不要使用new Number()new Boolean()new String() 建立包裝物件;
  • parseInt()parseFloat() 來轉換任意型別到 number
  • String() 來轉換任意型別到 string,或者直接呼叫某個物件的 toString() 方法;
  • 通常不必把任意型別轉換為 boolean 再判斷,因為可以直接寫 if (myVar) {...}
  • typeof 操作符可以判斷出 numberbooleanstringfunctionundefined
  • 判斷 Array 要使用 Array.isArray(arr)
  • 判斷 null 請使用 myVar === null
  • 判斷某個全域性變數是否存在用 typeof window.myVar === 'undefined';
  • 函式內部判斷某個變數是否存在用 typeof myVar === 'undefined'