函数
1. 函数基础
基础结构:
function test(a, b) {
// 执行语句
}
// 例子
if(a > 3) {
for(var i = 0; i< 10; i++) {
console.log(i)
}
}
if(a > 2) {
for(var i = 0; i< 10; i++) {
console.log(i)
}
}
if(a > 1) {
for(var i = 0; i< 10; i++) {
console.log(i)
}
}
上面代码重复代码过多,耦合高
我们编程的主要思想就是高内聚 低耦合
高内聚: 开发的一个功能模块,相关性联系性强,独立性强
低耦合:我们希望把重复的代码抽象出来,组成一个独立的功能体去完成独立的功能
解耦合:—》函数
2. 函数的组成部分和声明
函数的最基本的写法:函数声明
function test(arguments || c参数) {
// 函数的执行语句
}
test()// 函数的调用
3. 函数的参数与形实参的映射关系
例如:
function test() {
var a = 1,
b = 2;
console.log(a + b)
}
test();
// 此时 a, b均为定值 我们创造一个函数 不希望值是固定的
// 这时候我们可以需要参数
// 占位 形式上占位
function test(a, b) {
console.log(a + b)
}
// 实参
test(1, 2)
// a, b 可以理解为函数内部声明的变量(类似于), 用户可以传入需要的值
// 区别在于参数可以在函数调用的时候赋值, 而函数内部变量无法在函数执行的时候赋值
// a=>1, b=>2 参数一一对应
// test(NaN, false) 参数没有特定的类型
function test(a, b, c) {
console.log(a, b, c)
}
test(1, 2)//会不会报错?1, 2 undefined
test(1, 2, 3, 4)// 1, 2, 3
// 当实参和形参数量可以不等,
// 问题 是否能够知道有哪些实参
function test(a, b) {
consoe.log(test.length);// 形参个数
console.log(arguments.length);// 实参个数
console.log(arguments[1]) // 2 可以取到第几位是什么
}
test(1, 2);
// [1, 2]
// 小栗子
// 一个函数被执行的时候, 累加他的实参
function test() {
var a = 0;
for(var i = 0; i < arguments.length; i++) {
a += arguments[i];
}
console.log(a)
}
test();
// 问题
function test(a, b) {
a = 3
console.log(arguments[0]);// 3
}
test(1, 2);
// 改变实参 可以在函数内部更改实参的值
function test(a, b) {
b = 3
console.log(arguments[1]);// undefined
}
test(1);
// 实参中没有传递b的值 函数内部会不会打印出来b?
// 不会 实参中没有传递值 b就是undefined, 给未被定义的形参赋值
// 实参打印不出来
// 总结: 如果实参内有值可以在函数内部重新赋值, 如果实参内没有,函数内部再去更改是没有用的
思考:
// 以下函数, 形参a = 3 , arguments[0] = 3; 两者变量是不是同一个东西?
// 映射关系 arguments数组中有形参的变量,会形成映射关系一一对应
function test(a, b) {
a = 3// 栈内存
console.log(arguments[0]);// 堆内存存储数组,栈内存存储地址
}
test(1, 2);
4. 函数参数默认值
// 初始化参数 默认值:如果没有设置就是undefined
function test(a, b) {
console.log(a, b)// 1, undefined
}
test(1)
// 如果不传实参还想要他有默认值
function test(a = 1, b = 2) {
console.log(a, b)// 1, 2
}
test()undefined
function test(a = 1) {
console.log(a, b)// 1, undefined
}
test()
// 问题 a为默认值, b怎么传递?
function test(a = 1) {
console.log(a, b)// 1=》2, undefined
}
test(2)// 此时修改的时a的默认值
function test(a = 1) {
console.log(a, b)// 1, 2
}
test(undefined, 2)
// 如果形参传值, 实参为undefined, 默认找形参的值
// 实参非undefined 形参undefined 默认找实参的值
// 形参内赋值 低版本浏览器不兼容 ES6写法
// 曲线救国===>等同于形参内赋值
function test(a, b) {
var a = arguments[0] || 1;
var b = arguments[1] || 1;
}
test();
5. 函数的返回值
// 终止函数执行
function test() {
console.log('我正在执行');
console.log('我执行完了这个函数');
// return 隐式添加 终止函数执行
}
test();
// return 之后语句不会执行
// 返回值
function test(name) {
if(!name) {
return '1';
}else {
return name;
}
}
test('zs');
6. 全局变量与函数
a = 1;
function test() {
var b = 1;
console.log(a);//1
}
test();
console.log(b);// a is not defined
a = 1;
function test1() {
var b = 2;
console.log(a);// 1
function test2() {
var c = 3;
console.log(b);// 2
}
test2();
console.log(c)// c is not defined
}
test1();
// 里面变量可以访问外面变量, 外面无法访问里面的
// b, c局部变量[[scope]]
function test1() {
var a = 1;
console.log(b)
}
function test2() {
var b = 2;
console.log(a)
}
test1();
test2();
// 变量访问的范围
一个固定的功能或者程序, 被封装的过程, 实现一个固定的功能或者程序,在这个封装体需要一个入口和一个出口, 入口就是参数, 出口就是返回。
7. 函数递归
// n的阶乘不能用for
// n! = n * (n - 1)
function fact(n) {
if(n === 1) {
return 1;
}
return n*fact(n-1);
}
// 函数自己调用自己 性能差 慎用
// 黄金分割数列
// 1, n3 = n2 + n1;
// 2. n <= 2
function fb(n) {
if(n <= 0) {
return 0;
}
if(n <=2 ) {
return 1
}
return fb( n - 1 ) + fb(n - 2)
}
8. 立即执行函数
当一个函数在一个页面开始就调用 而且只使用这么一次的话,一般函数还继续存在GO中
- 自动执行
- 执行之后立即释放
IIFE:immediately-invoked function expression
初始化函数: 最好用立即执行函数
(function() {}());// w3c建议
(function() {})();// 常用
(function() {
var a = 1,
b = 2;
console.log(a + b) // 3
})()
(function test() {
var a = 1,
b = 2;
console.log(a + b) // 3
})()
// test is not defined
// 函数执行完立即销毁
(function(a, b) {
console.log(a + b) // 3
})(1, 2)
// 可以传参
var add = (function(a, b) {
return a + b
})(1, 2)
console.log(add);// 3
// 注意括号包起来的都是表达式
// 思考一下函数会不会执行?
function test() {
}()
// 不会 语法错误
var test = function() {
}()
// 可以执行
// 什么时候加括号会执行不会报错
// 一定是表达式才能被括号执行
var test = function() {
console.log(1);
}
console.log(test);// function
var test = function() {
console.log(2);
}()
console.log(test);// 2 undefined
// 立即执行 执行完后销毁
// 函数声明变成表达式的方法 + - ! || &&
// 接括号就会执行 该函数函数名自动会忽略
function test(a, b) {
console.log(2)
}(1)// 不会执行 优先解析括号内表达式
// 逗号运算符
(1, 2)// 返回逗号后面的值 ===》2
9. callee与caller
function test(a, b, c) {
console.log(arguments.callee.length)//3当前arguments所指向的函数test 在哪函数里指向哪个函数
console.log(test.length)// 3
}
test(1, 2, 3)// 3
// 找不到函数名情况下 递归
var sum = (function() {
if(n<=1) {
return 1;
}
return n + arguments.callee(n - 1);
})(10)
//caller
// 严格模式报错 caller callee
function test1() {
test2()
}
function test2() {
console.log(test.caller)// 谁调用了test2 就返回哪个函数
}