💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、星火、月之暗面及文生图 广告
[TOC] * * * * * > 定义: 函数是 JavaScript 中的基本组件之一。 一个函数是 JavaScript 过程 :一组执行任务的语句。 ### 1. 定义函数 ~~~ 定义一个函数并不会自动的执行它。 定义了函数仅仅是赋予函数以名称并明确函数被调用时该做些什么。 调用函数才会以给定的参数真正执行这些动作。 function f1(arg1,arg2) {} var a1=function f1(arg1,arg2) {} var a2=function (arg1,arg2) {} f1.name=>'f1'; a1.name=>'f1'; a2.name=>'a2'; ~~~ * * * * * ### 2. 参数 >[danger]原始值参数(比如一个具体的数字)被作为值传递给函数;值被传递给函数,如果被调用函数改变了这个参数的值,这样的改变不会影响到全局或调用函数。 >[danger]如果你传递一个对象(即一个非原始值,例如Array或用户自定义的对象)作为参数,而函数改变了这个对象的属性,这样的改变对函数外部是可见的。 ~~~ 在函数体内,使用arguments对象获取类数组对象的参数列表, arguments[0] 就表示第一个传进来的参数,如果在函数体内修改arguments[0],同步的表示第一个参数的变量值也会变,并不是说他们引用相同的内存,而是值会同步; arguments变量只是类数组对象,并不是一个数组。 称其为类数组对象是说它有一个索引编号和 length (得到参数的个数)属性。 尽管如此,它并不拥有全部的Array对象的操作方法。 ~~~ ~~~ 函数的参数,如果没传(由于可传可不传),或者不是需要的类型, 为了程序的健壮 在ES5中针对这种情况就要做判断,专门给参数赋默认值: function multiply(a, b) { b = (typeof b !== 'undefined') ? b : 1; return a*b; } multiply(5); // 5 在ES6中对这种操作做了规范,更容易: function multiply(a, b = 1) { return a*b;} multiply(5); // 5 ~~~ * * * * * ### 3. 函数作用域 一个函数可以访问在其所在定义域内的所有变量和函数; 如果一个函数定义在全局定义域内,那么它就可以访问全局作用域内的所有变量和函数; 如果一个函数定义在另外一个函数内,那么它就可以访问父函数内的所有变量和函数; 函数中的 `this` 引用的是函数据以执行的环境对象 * * * * * ### 4. 返回值 函数总是有返回值得 ![](https://box.kancloud.cn/5978c04f2ccc706c92b020139a7c1577_429x60.png) ![](https://box.kancloud.cn/2e919fec4bd7d2e50cfbd2b931fe0fc7_409x221.png) * * * * * ### 5. 递归调用 ~~~ 一个函数可以指向并调用自身。有三种方法可以达到这个目的: 1. 函数名 2. arguments.callee 3. 作用域下的一个指向该函数的变量名 比如: var a=function f3(x) { /* 那么在函数体内,a(y), f3(y),arguments.callee(y) 是相等的, 都是以y为参数,执行函数 */ } ~~~ 当然,在函数体内调用自身的函数就是递归,为了避免无限循环,我们就需要设定执行条件。 分析下一个递归调用 ![](https://box.kancloud.cn/f902bb0f95dc5c4a38b497eea9e2ab7c_429x453.png) ~~~ 将程序的执行过程写出来,就像顺序执行代码: if(3<0) return; console.log('begin:', 3); { if(2<0) return; console.log('begin:', 2); { if(1<0) return; console.log('begin:', 1); { if(0<0) return; console.log('begin:', 0); { if(-1<0) return; //这一条件符合,跳出递归 } console.log('end:', 0); } console.log('end:', 1); } console.log('end:', 2); } console.log('end:', 3); ~~~ * * * * * ### 6. 函数的闭包 可以在一个函数里面嵌套另外一个函数。嵌套(内部)函数对其容器(外部)函数是私有的。它自身也形成了一个闭包。一个闭包是一个可以自己拥有独立的环境与变量的的表达式(通常是函数)。 既然嵌套函数是一个闭包,就意味着一个嵌套函数可以”继承“容器函数的参数和变量。换句话说,内部函数包含外部函数的作用域。 ~~~ 总结: 1. 内部函数只可以在外部函数中访问。 2.内部函数形成了一个闭包:它可以访问外部函数的参数和变量,但是外部函数却不能使用它的参数和变量。 ~~~ * * * * * ### 7. 箭头函数: 箭头函数表达式(也称胖箭头函数)相比函数表达式具有较短的语法并以词法的方式绑定 this。箭头函数总是匿名的。 优点: 有两个因素会影响引入箭头函数:更简洁的函数和 this。 ~~~ setTimeout/setInterval(function() { //这里的this自动绑定到顶层作用域 }); function f1() { setTimeout(function() { console.log(this.name); }); } function f2() { setTimeout(()=>{ console.log(this.age); }); } window.name='global'; window.age=25; var o={ name: 'local', age: 26, showName: f1, showAge: f2 }; o.showName() 'global' o.showAge() 26 ~~~ * * * * * ### 8. 参数和函数内部定义的函数名相同的情况 ![](https://box.kancloud.cn/a0082e923cd7f40a8b51028a63356089_351x213.png) ### 9. 严格模式 ~~~ 当函数的执行作用域为全局作用域: 1. 非严格模式下 , this 默认指向window; 2. 严格模式下,this 为 undefined; ~~~ ![](https://box.kancloud.cn/eff278c704a26892bdfa4e06069f2803_427x479.png) * * * * * ### 10. 高阶函数 > `Higher-order function` > 两个主要特征: > 1.函数可以作为参数被传递 > 2.函数可以作为返回值输出 >[info] 题 1:设计一个函数,输入索引就可以得到值 |index|0|1|2|3|4|5| | --- | --- | --- | --- | --- | --- | --- | | value |1|1|2|3|5|8| ~~~ 使用递归方法 function f1(n) { if(n<2) return 1; else { return f1(n-2)+f1(n-1) //这里为了减少耦合可用 arguments.callee来代替f1 } } 但是这样存在性能问题,所以我们需要缓存计算值 function f2(f,n) { var result={}; if(result[n]===undefined) { result[n]=f(n); } return result[n]; } 这里就把把函数当做参数传递了进去 ~~~ >[info]题 2:构造一个简单的闭包(返回值为函数) ~~~ function f3(arg1) { var o={ name: arg1 }; return function() { return o.name; } } var a=f3('wdd'); //此时a是函数引用 a(); //输出 'wdd' ~~~