JS-函数基础

208 阅读3分钟

编程的时候最好达到高内聚(模块代码相关性强 -> 独立性强),低耦合(重复少 -> 函数)的要求。 体现在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

作用:

  1. 终止函数执行
  2. 返回值
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

总结:

函数就是一个固定的功能或者是程序段被封装的过程。在这个封装体中需要一个入口(参数)和出口(返回值)。