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一样具有块级作用域。