1、定義函數
function abs(x) {
if (x >= 0) {
return x;
} else {
return -x;
}
}
7
1
function abs(x) {
2
if (x >= 0) {
3
return x;
4
} else {
5
return -x;
6
}
7
}
如果沒有return,函數執行完畢也會返回結果,不過結果是undefined。
由於在JS的世界中,函數也是一個對象,所以上述定義的
函數實際上也是一個對象,而
函數名則可視為指向該函數的變量。
所以上面的函數定義等價於:
var abs = function (x) {
if (x >= 0) {
return x;
} else {
return -x;
}
};
7
1
var abs = function (x) {
2
if (x >= 0) {
3
return x;
4
} else {
5
return -x;
6
}
7
};
2、調用函數
JS的函數允許任意個,也不影響調用,接以上的abs函數:
//比定義的參數多
abs(10, 'blablabla'); // 返回10
abs(-9, 'haha', 'hehe', null); // 返回9
//比定義的參數少
abs(); // 返回NaN --> 此時abs(x)函數的參數x將收到undefined,計算結果為NaN
6
1
//比定義的參數多
2
abs(10, 'blablabla'); // 返回10
3
abs(-9, 'haha', 'hehe', null); // 返回9
4
5
//比定義的參數少
6
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判斷,然后重新調整參數並賦值
}
11
1
// foo(a[, b], c)
2
// 接收2~3個參數,b是可選參數,如果只傳2個參數,b默認為null:
3
function foo(a, b, c) {
4
if (arguments.length === 2) {
5
// 實際拿到的參數是a和b,c為undefined
6
c = b; // 把b賦給c
7
b = null; // b變為默認值
8
}
9
// ...
10
//要把中間的參數b變為“可選”參數,就只能通過arguments判斷,然后重新調整參數並賦值
11
}
為了獲得多余的參數,我們也就是利用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);
}
11
1
function foo(a, b) {
2
var i, rest = [];
3
if (arguments.length > 2) {
4
for (i = 2; i<arguments.length; i++) {
5
rest.push(arguments[i]);
6
}
7
}
8
console.log('a = ' + a);
9
console.log('b = ' + b);
10
console.log(rest);
11
}
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)
18
1
function foo(a, b, rest) {
2
console.log('a = ' + a);
3
console.log('b = ' + b);
4
console.log(rest);
5
}
6
7
foo(1, 2, 3, 4, 5);
8
// 結果:
9
// a = 1
10
// b = 2
11
// Array [ 3, 4, 5 ]
12
13
foo(1);
14
// 結果:
15
// a = 1
16
// b = undefined
17
// Array []
18
//如果傳入的參數連正常定義的參數都沒填滿,也不要緊,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('測試通過!');
}
28
1
//用rest參數編寫一個sum()函數,接收任意個參數並返回它們的和
2
3
'use strict';
4
5
function sum(rest) {
6
var result = 0;
7
for(var value of rest){
8
result += value;
9
};
10
return result;
11
}
12
13
// 測試:
14
var i, args = [];
15
for (i=1; i<=100; i++) {
16
args.push(i);
17
}
18
if (sum() !== 0) {
19
alert('測試失敗: sum() = ' + sum());
20
} else if (sum(1) !== 1) {
21
alert('測試失敗: sum(1) = ' + sum(1));
22
} else if (sum(2, 3) !== 5) {
23
alert('測試失敗: sum(2, 3) = ' + sum(2, 3));
24
} else if (sum.apply(null, args) !== 5050) {
25
alert('測試失敗: sum(1, 2, 3, ..., 100) = ' + sum.apply(null, args));
26
} else {
27
alert('測試通過!');
28
}
//定義一個計算圓面積的函數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;
20
1
//定義一個計算圓面積的函數area_of_circle(),它有兩個參數:r: 表示圓的半徑;pi: 表示π的值,如果不傳,則默認3.14
2
3
'use strict';
4
function area_of_circle(r, pi) {
5
var temp = 3.14;
6
if(arguments.length > 1){
7
temp = pi;
8
}
9
return temp*r*r;
10
}
11
12
// 測試:
13
if (area_of_circle(2) === 12.56 && area_of_circle(2, 3.1416) === 12.5664) {
14
alert('測試通過');
15
} else {
16
alert('測試失敗');
17
}
18
19
20
//網友答約: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();
9
1
'use strict';
2
3
function foo() {
4
var x = 'Hello, ' + y;
5
alert(x);
6
var y = 'Bob';
7
}
8
9
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';
}
6
1
function foo() {
2
var y; // 提升變量y的申明
3
var x = 'Hello, ' + y;
4
alert(x);
5
y = 'Bob';
6
}
這個特性其實並沒有什么好處,反而容易讓人犯錯,所以按像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中,會大大減少全局變量沖突的可能
13
1
// 唯一的全局變量MYAPP:
2
var MYAPP = {};
3
4
// 其他變量:
5
MYAPP.name = 'myapp';
6
MYAPP.version = 1.0;
7
8
// 其他函數:
9
MYAPP.foo = function () {
10
return 'foo';
11
};
12
13
//把自己的代碼全部放入唯一的名字空間MYAPP中,會大大減少全局變量沖突的可能
3.2 局部作用域
在之前我的理解,比如在函數內定義的變量,因為作用域在函數內,所以就算是局部作用域了,而下面的代碼:
'use strict';
function foo() {
for (var i=0; i<100; i++) {
//
}
i += 100; // 仍然可以引用變量i
}
8
1
'use strict';
2
3
function foo() {
4
for (var i=0; i<100; i++) {
5
//
6
}
7
i += 100; // 仍然可以引用變量i
8
}
即是說,實際上
這種默認方式的作用域並不完全是局部的,至少在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
}
x
1
'use strict';
2
3
function foo() {
4
var sum = 0;
5
for (let i=0; i<100; i++) {
6
sum += i;
7
}
8
i += 1; // SyntaxError
9
}
3.3 常量
在ES6標准之前,我們表示常量通常都使用全部大寫的方式來“告訴“使用者,不要修改它的值,但實際上這個值是可以被修改了,是存在風險的。在ES6標准中引入了新的
關鍵字const,可以申明不可改變的常量,且同let一樣具有塊級作用域。