编程的时候最好达到高内聚(模块代码相关性强 -> 独立性强),低耦合(重复少 -> 函数)的要求。 体现在js上,就是模块的单一责任制(只需要完成一个功能,尽量避免模块之间的依赖)
函数声明
用function声明一个函数
function 函数名(参数){
函数执行语句
}
函数必须要被调用才会执行
函数名();
要注意全局变量和局部变量的使用
function test(){
var a = b = 1; //这样写b是全局变量
}
函数表达式/字面量
var test = function test1(){
var a = 1,
b = 1;
console.log(a, b);
}
console.log(test.name); //test1
//test1(); //报错
test(); //正常执行 1 1
通过表达式赋值会忽略函数test1的名字,所以test1在外部不可见,无法执行,但是在函数内部是可以调用的(递归)。
这种把函数赋值给一个变量的方式被称为:匿名函数表达式 / 函数字面量
函数的组成部分:function、函数名、参数(可选)、返回值、return(没有也会默认return undefined)
函数参数
function test(a, b){
console.log(a + b);
}
test(1, 2); //3
参数a,b类似于在函数内部声明局部变量a,b,区别就是,参数可以在被调用的时候赋值
参数被定义的时候是没有值的,只是形式上占位用的占位符 -> 形参
调用()的时候传入实参,给形参赋值
函数内部也是知道实参(arguments)和形参(test)的
function test(a, b){
console.log(arguments.length); //实参3
console.log(test.length); //形参2
}
test(1, 2, 3);
形参和实参一一对应,但是数量可以不等,多出的形参就是undefined,多出来实参也不会报错。
例:一个函数被调用时,累加他的实参值
function sum(){
var num = 0;
for(var i = 0; i < arguments.length; i++){
num += arguments[i];
}
console.log(num); //6
}
sum(1, 2, 3)
在函数内部也是可以更改传入的实参的值的
function test(a, b){
a = 3;
console.log(arguments[0]); //3
}
test(1, 2);
既然a和arguments[0]都是3,那a和arguments[0]是同一个东西吗?
实参和形参存放在不同的地方,a存放在栈内存里,arguments存放于堆内存里,所以不是同一东西;但是实参和形参存在相互映射的关系,一一对应,实参变了,形参跟着变
前提是有形参有对应的实参值,如果没有对应的值,映射关系是不存在的
function test(a, b){
b = 3;
console.log(arguments[1]); //undefined
}
test(1);
实参没有传入b的值,b就是未被定义(undefined)
实参都没有这个值,谈何更改
例:
var a = 1,
b = 2;
function test(a, b) {
a = 3;
console.log(a); //3
arguments[1] = 5;
console.log(arguments[1]); //5
}
test(a, 2);
console.log(a); //1
console.log(b); //2
实参传入的a和形参的a不是一个a,只是存在映射关系,形参的a改变了,对实参没有影响,虽然在函数内部a被改成3了,但是对于实参a来说,还是1
映射关系只会存在于函数的作用域里面,在作用域外面,映射关系对于外部的变量是不会有任何的影响的。
初始化参数(默认值:undefined)
法1:
function test(a = 1, b){
console.log(a, b); //1,undefined
}
test();
让b传入实参,a用默认值
function test(a = 1, b){
console.log(a, b); //1,2
}
test(undefined, 2);
function test1(a = undefined, b){
console.log(a, b); //1,2
}
test1(1, 2);
形参和实参之间会选择有值的那个
这种设置默认值的方法是ES6的方法,低版本浏览器不兼容
法2:
function test(a, b){
var a = arguments[0] || 1;
var b = arguments[1] || 2;
console.log(a, b); //1,2
}
test();
法3:
function test(a, b){
var a = typeof(arguments[0]) !== 'undefined' ? arguments[0] : 1;
var b = typeof(arguments[1]) !== 'undefined' ? arguments[1] : 2;
console.log(a, b); //1,2
}
test();
return
作用:
- 终止函数执行
- 返回值
function test(name){
if(!name){
return '未填写姓名';
}
return name;
}
console.log(test('Jackson'));
用或运算写
function test(name){
return name || '未填写姓名'; //最后一个无论是真或假都会返回,但是'未填写姓名'是真的
}
console.log(test('Jackson'));
如果不写return的话,函数默认也会隐式添加一个return
递归
性能没有for循环好,慎用
递归就是在找到出口之后,一步一步向上计算,所以一定要找到规律和结束出口。
例:阶乘
function factorial(n){
if (n === 1) {
return 1;
}
return n * factorial(n - 1);
}
console.log(factorial(4)); //24
factorial(4) = 4 * factorial(3); //4 * 6
factorial(3) = 3 * factorial(2); //3 * 2
factorial(2) = 2 * factorial(1); //2 * 1
return 1只是把factorial(1)的结果return出去了,还有一堆队列要处理
例:斐波那契数列
function series(n){
if (n <= 2) {
return 1;
}
return series(n - 1) + series(n - 2);
}
console.log(series(5)); //5
总结:
函数就是一个固定的功能或者是程序段被封装的过程。在这个封装体中需要一个入口(参数)和出口(返回值)。