1.函数的命名
函数是行为。所以它们的名字通常是动词。它应该简短且尽可能准确地描述函数的作用。这样读代码的人就能得到正确的线索。
一种普遍的做法是用动词前缀来开始一个函数,这个前缀模糊地描述了这个动作。团队内部必须就前缀的含义达成一致。
例如,以 “show” 开头的函数通常会显示某些内容。
“get…” —— 返回值,
“calc…” —— 计算
“create…” —— 创建,
“check…” —— 检查并返回 boolean 值,等。
下面列举几个例子
showMessage(..) // 显示信息
getAge(..) // 返回 age (gets it somehow)
calcSum(..) // 计算求和并返回结果
createForm(..) // 创建表格 (通常会返回它)
checkPermission(..) // 检查权限并返回 true/false
2.函数创建的方式(函数声明、函数表达式、箭头函数)
方式一(函数声明)
function abs(x) {
if (x >= 0) {
return x;
} else {
return -x;
}
}
方式二(函数表达式)
var abs = function (x) {
if (x >= 0) {
return x;
} else {
return -x;
}
};
在这种方式下,function (x) { … }是一个匿名函数,它没有函数名。但是,这个匿名函数赋值给了变量abs,所以,通过变量abs就可以调用该函数。
上述两种定义完全等价,注意第二种方式按照完整语法需要在函数体末尾加一个;,表示赋值语句结束。
方法三(箭头函数)
一些比较简单的方法,比如要创建一个sum()函数,a和b为参数,sum(a,b)可返回a+b的值。
用函数表达式的话大致是这样的代码
var sum = function(a, b) {
return a + b;
};
而使用箭头函数的话,代码如下所示
var sum = (a, b) => a + b;
如果我们只有一个参数,那么括号可以省略,甚至更短:
var double = n => n * 2;
有时我们需要一些更复杂的东西,比如多个表达式或语句。这也是可能的,但我们应该把它们装在花括号里。然后在他们内部使用正常的 return。
var sum = (a, b) => { // 花括号打开多线功能
var result = a + b;
return result; // 如果我们使用花括号,使用返回来获得结果
};
3.调用函数
3.1 参数过多
rest变量
调用函数时,按顺序传入参数即可:
abs(10); // 返回10
abs(-9); // 返回9
由于JavaScript允许传入任意个参数而不影响调用,因此传入的参数比定义的参数多也没有问题,虽然函数内部并不需要这些参数:
abs(10, 'blablabla'); // 返回10
abs(-9, 'haha', 'hehe', null); // 返回9
若想要接收多出的参数应该怎么做呢,在ES6前有arguments(不过箭头函数中没有arguments),这里要用ES6后提供的rest 参数,它的操作符表示为3个点 …,且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 []
“arguments” 变量
函数的上下文会提供一个非常特殊的类数组对象 arguments,所有的参数被按序放置。
例如:
function showName() {
alert( arguments.length );
alert( arguments[0] );
alert( arguments[1] );
// 它是可遍历的
// for(let arg of arguments) alert(arg);
}
// 依次弹出提示:2,Julius,Caesar
showName("Julius", "Caesar");
// 依次弹出提示:1,Ilya,undefined(不存在第二个参数)
showName("Ilya");
在 JavaScript 引入 Rest 参数之前,无论入参数是多是少,想获取所有的入参只能使用 arguments。
时至今日,这仍是一个可用的方法。
即使 arguments 是一个类数组且可遍历的变量,但它终究不是数组。它没有数组原型链上的函数,我们没法直接调用诸如 arguments.map(…) 等这样的函数。
同样的,因为它总是包含所有的参数,我们并不能像使用 Rest 参数一样,期望它只截取入参的一部分。
因此如果你不想受困于以上“缺点”,那么赶紧使用 Rest 参数吧。
3.2 回调函数
使用函数表达式传递函数。
我们写一个包含三个参数的函数 ask(question, yes, no):
question 是文本
yes 是当回答 “Yes” 时候运行的脚本
no 是当回答 “No” 时候运行的脚本
函数需要提出 question(问题),依赖用户的回答, 调用 yes() 或 no():
function ask(question, yes, no) {
if (confirm(question)) yes()
else no();
}
function showOk() {
alert( "You agreed." );
}
function showCancel() {
alert( "You canceled the execution." );
}
ask("Do you agree?", showOk, showCancel);
点击确定则调用showOK,点击取消则调用showCancel
例子中,showOk 对应回答 “yes” 的回调,showCancel 对应回答“否”。
当然此处我们可以使用函数表达式进行简写,效果与上面的代码相同
function ask(question, yes, no) {
if (confirm(question)) yes()
else no();
}
ask(
"Do you agree?",
function() { alert("You agreed."); },
function() { alert("You canceled the execution."); }
);
3.3 将对象属性用于做实参
将对象属性用作实参, 从而不必记住参数的顺序.
function arraycopy(from, from_start, to, to_start, length) {
for (var i = from_start, j = to_start; i < from_start + length; ++i, ++j) {
to[j] = from[i];
}
}
function easycopy(args) {
arraycopy(args.from,
args.from_start || 0, //default param
args.to,
args.to_start || 0,
args.length);
}
var a = [1, 2, 3, 4],
b = [];
easycopy({
from: a,
from_start:1,
to: b,
length: 3
});
console.log(b); // [ 2, 3, 4 ]
当使用对象属性作为参数时,IDE会提示属性名。
以对象属性名作为参数的做法,虽然实现效率较低,但是不必再去记住实参的顺序。
4. 函数中的return语句(坑)
要小心了,由于JavaScript引擎在行末自动添加分号的机制,上面的代码实际上变成了:
function foo() {
return; // 自动添加了分号,相当于return undefined;
{ name: 'foo' }; // 这行语句已经没法执行到了
}
所以正确的多行写法是:
function foo() {
return { // 这里不会自动加分号,因为{表示语句尚未结束
name: 'foo'
};
}
5. JavaScript中的变量声明提前(坑)
如果按照我的思维理解,以下的输出应该是1 1 2,而实际上,会输出1 undefined 2
"use strict";
var x = 1;
console.log(x);//输出 1
var A = function() {
console.log(x);//看起来像会输出全局变量 x,即1,实际上输出undefined
var x = 2;
console.log(x);//输出 2
}
A();
这个是为什么呢?因为JavaScript编译器在编译的时候,会将A body内的声明变量提到最前,所以实际上的执行代码是这样的
"use strict";
var x = 1;
console.log(x);//输出 1
var A = function() {
var x;
console.log(x);//看起来像会输出全局变量 x,即1,实际上输出undefined
x = 2;
console.log(x);//输出 2
}
A();
声明了x,但是没有对齐进行赋值,故输出undefined。