系列
JavaScript教程
函数
函数是执行特定任务的单元,比如前面介绍过的parseInt()
、parseFloat()
都是函数。
function关键字
定义一个函数通常很简单,可以使用function关键字定义函数:
function add(value1, value2) { // 定义函数add
return value1 + value2;
}
const result = add(1, 2); // 调用函数add
console.log(result);
上面代码的各个部分如下图:
在JavaScript中定义函数,有几个部分:
- 函数名,位于
function
之后,和变量名的命名规则一样 - 参数,在括号
()
里,可以没有参数,也可以多个参数 - 函数体里面可以为空,也可以有多个语句
为了让代码易于理解,应该使用有意义的函数名。这样,以后在代码中用到这个函数的时候,就知道该函数的功能。
函数表达式
虽然前面的函数声明在语法上是一个语句,但函数也可以由函数表达式创建。这样的函数可以是匿名 的:它不必有一个名称。例如,函数add也可这样来定义:
const add = function (value1, value2) { // 创建一个匿名函数对象,并赋给变量add
return value1 + value2;
};
const result = add(1, 2); // 调用函数add
console.log(result);
因此,我们也可以给对象动态添加函数属性,称之为方法 。
const calculator = {};
calculator.add = function (value1, value2) {
return value1 + value2;
};
const result = calculator.add(1, 2); // 调用对象calculator的add方法
console.log(result);
箭头函数
在ES6里面,新加入了箭头函数。箭头函数表达式(也称胖箭头函数)相比函数表达式具有较短的语法并以词法的方式绑定 this。箭头函数总是匿名 的。
胖箭头长的是这样子的:=>
,不要误以为是等于大于运算符,看一个例子:
const add = (value1, value2) => { // 创建一个箭头函数对象,并赋给变量add
return value1 + value2;
};
const result = add(1, 2); // 调用函数add
console.log(result);
至于什么是绑定this,后面变量的作用域部分会讲解。
函数参数
Python的函数参数分为以下4种:
- 必要参数(Required arguments)
- 默认参数(Default arguments)
- 关键字参数(Keyword arguments)
- 变长参数(Variable-length arguments)
必要的参数(Required arguments)
一般情况下,一个函数定义的时候声明了几个参数,调用的时候就要传几个参数,并且要保证参数的顺序是和定义的要求一致。
比如:
def add(x, y):
return x + y;
add(2)
第四行,我们调add
函数只传了一个参数,因此程序就报错了。
默认参数(Default arguments)
我们在定义函数的时候,可以给某些参数赋一个默认值,这样调用的时候不传这个参数
def add(x, y=1):
return x + y;
result = add(2)
print(result)
但是如果这么写,会是什么结果呢?
def add(x=1, y):
return x + y;
add(2)
报错了!原因是有默认值得参数只能写到没有默认值参数的后面。
关键字参数(Default arguments)
如果只想传某些参数的值,而不用考虑实参的顺序,怎么办?那就用关键字参数。
def add(x, y, z=3):
print('x: {0}'.format(x))
return x + y + z;
result = add(y=2, x=1)
print('result: {0}'.format(result))
变长参数(Variable-length arguments)
有些时候,函数的参数数量不确定,那就可以用变长参数,变量名前面加一个星号*
,实际传入的是一个tuple
。
def add(x, *args):
print('x: {0}'.format(x))
print('args: {0}'.format(args))
result = x
for arg in args:
result += arg
return result;
result = add(1, 2, 3, 4, 5, 6, 7)
print('result: {0}'.format(result))
当变量名前加两个星号**
,则实际传入的是一个dict
:
def print_user(name, **args):
print('x: {0}'.format(name))
print('args: {0}'.format(args))
print_user('Tom', age=10, gender='male')
当用两个星号的时候,变长的参数部分一定要用关键字参数来作为字典的key。
编码风格探讨——过多的参数
有时候定义一个函数,可能会发现这个函数依赖五、六个甚至更多的参数,定义出来就是这样子:
function printRecord(firstName, lastName, dateOfBirth, streetAddress, postCode, city, country, email, website) { // 函数定义
const result = {
name: lastName + ', ' + firstName,
DOB: dateOfBirth,
address: streetAddress + '\n' + postCode + ' ' + city + '\n' + country,
email: email,
url: website
};
console.log(JSON.stringify(result));
}
printRecord("雷", "李", "2000-08-10", "某街某小区", "000000", "xx市", "中国", "email@duolaima.com", "unknown"); // 函数调用
这样的函数定义,有一些潜在的问题:
- 调用这个printRecord函数的时候,很难记住要传哪些参数,以及参数的顺序,需要不断地查这个函数的接口定义才行,很麻烦
- 将来要增加或删除一个参数的时候,调用的地方因为不太容易读,修改起来不太容易。
如何优化呢?那就是传一个对象,包含所有必须的参数:
function printRecord(record) {
const result = {
name: record.lastName + ", " + record.firstName,
DOB: record.dateOfBirth,
address: record.streetAddress + "\n"
+ record.postCode + " " + record.city + "\n"
+ record.country,
email: record.email,
url: record.website
};
console.log(JSON.stringify(result));
}
const record = {
firstName: "雷",
lastName: "李",
dateOfBirth: "2000-08-10",
streetAddress: "某街某小区",
postCode: "000000",
city: "xx市",
country: "中国",
email: "email@duolaima.com",
website: "unknown"
};
printRecord(record);
虽然调用的时候,需要保证传入的是个对象,但这时候完全不用担心参数的顺序,甚至删除函数某个参数的时候,都不需要改调用的代码。
那么多少个参数算是过多呢?3个,5个还是7个?这个没有明确的规则,具体还要看代码的具体实现。不过,当你感觉传对象比挨个传参数会让你目前的代码更容易理解的时候,肯定是参数多了需要重构了。