# 函数 定义方式 - 函数声明 ``` function a(){...} ``` - 函数表达式 ``` let a = function(){...} ``` - 箭头函数 ``` let a = ()={...} ``` - Function构造函数 ``` let a = new Function('','','') ``` ## 1. 箭头函数 - 适合嵌入函数的场景 - 只有一个参数可以不用括号 - 可以不用大括号,箭头后接一行代码或一个表达式、赋值操作,隐式返回这行代码的值 - 不能用arguments super 和 new.target,已不能用作构造函数。没有prototype属性 ## 2. 函数名 - 就是函数的指针,一个函数可以有多个名字 - 使用函数名可会访问函数指针而不执行函数 - 所有函数都会暴露一个只读的 name 属性 ## 3. 理解参数 - 参数表现为一个数组 - 定义不传,使用命名访问值为 undefined - 使用function关键字定义函数时,可以在函数体内访问 arguments 对象 - arguments 是一个类数组对象 - es不存在验证命名参数的机制 - arguments 可以和命名参数一起使用 - arguments 始终与对应的命名函数同步,但是在内存中是分开的 - arguments 对象的长度是根据传入的参数个数,而非定义函数时给出的命名参数个数确定的 - 箭头函数不能使用 arguments 关键字 - es中的所有参数都按 值传递。不可能按引用传递参数。对象传递对象的引用的值。 - 严格模式下,arguments 始终等于 传递过来的值,修改命名参数不影响 arguments。 ## 4. 没有重载 - 没有函数签名所以没有重载 - 覆盖定义 ## 5. 默认参数值 - 在函数定义的参数后面使用 = 为参数赋默认值 - 在使用默认参数是,arguments对象的值不反映参数的默认值,只反映给函数的参数 - 默认参数可以是值、对象,也可以是 一个执行的函数。执行的函数在被调用且无传值时调用。 - 默认参数会按照它们的顺序依次被初始化。(暂时性死区) ## 6. 参数的扩展与收集 ### 6.1 扩展参数 ```js let values = [1, 2, 3, 4] function getSum(){ let sum = 0; for(let i = 0; i < arguments.length; ++i){ sum += arguments[i] } return sum } ``` 方法1. ```js getSum.apply(null,values); ``` 方法2. ```javascript getSum(...values) ``` ### 6.1 收集参数 ```javascript function getSum(...values){ return values.reduce((x,y)=>x+y, 0) } getSum(1,2,3) ``` 收集参数只能作为最后一个参数 箭头函数不支持arguments函数,但支持收集参数的定义方式。 ## 7. 函数声明与函数表达式 在任何代码执行前,函数声明会被读取,并生成函数定义。而函数表达式必须等到代码执行到它那一行才会执行。**函数声明提升**。 ## 8. 函数作为值 函数名就是变量,所以函数可以用在任何可以使用变量的地方。 ## 9. 函数内部 ### 9.1 arguments 类数组对象,包含调用函数时传入的所有参数。 箭头函数没有这个属性。 有一个 callee 属性,指向 argument 对象所在函数的指针。 用于递归调用时解耦。 ### 9.2 this 在标准函数内,this 引用的是把函数当作方法调用的上下文对象。 this 在被调用时被确定。 在箭头函数内,this引用的时定义箭头函数的上下文。定义时 确定。 ### 9.3 caller 引用的是调用当前函数的函数。 ### 9.4 new.target 函数可以作为构造函数实例化一个新对象。也可以正常使用。 检测函数是否使用 new 关键字调用的 new.target 属性。如果正常调用,则为undefined ```javascript function King(){ if(!new.target){ throw 'King must bu instantiated using "new"' } console.log('Lint instantiated using "new"') } new King() King() ``` ## 10. 函数属性与方法 **length**:函数定义的命名参数个数 **prototype** **apply() 和 call()** apply接受两个参数,函数内this的值和一个参数数组(Array对象或arguments对象)。 call函数第一个参数一样,但是参数得一个个地列出来。**第一个参数用来 控制 函数体 this 值** **bind** 创建一个新的函数实例,其this值会被绑定到 bind函数的参数对象。 **toLocaleStrign ** 函数代码 **toString ** 函数带啊吗 **valueOf ** 函数本身 ## 11. 函数表达式 ## 12. 递归 ## 13. 尾调用优化 条件: 1. 代码在严格模式下执行 2. 外部函数的返回值是对尾调用函数的调用 3. 尾调用函数返回后不需要执行额外逻辑 4. 尾调用函数不是引用外部函数作用域中自由变量的闭包 未优化代码: ```javascript function fib(n){ if(n < 2){ return n } return fib(n-1) + fib(n-2) } ``` 尾调用优化 ```javascript // 基础框架 function fib(n){ return fibImpl(0,1,n) } // 执行递归 function fibImpl(a,b,n){ if(n === 0){ return a } return fibImpl(b, a + b, n-1) } ``` ## 14. 闭包 引用了**另一个函数作用域变量**的函数。 ## 15. 立即调用的函数表达式 1. 创建块级定义域 2. 锁定参数值 ## 总结 - 函数表达式与函数声明是不一样的。函数声明要求写出函数名称,而函数表达式并不需要。没 有名称的函数表达式也被称为匿名函数。 - ES6新增了类似于函数表达式的箭头函数语法,但两者也有一些重要区别。 - javAscript中函数定义与调用时的参数极其灵活 arguments对象,以及ES6新增的扩展操作符, 可以实现函数定义和调用的完全动态化。 - 函数内部也暴露了很多对象和引用,涵盖了函数被谁调用、使用什么调用,以及调用时传入了 什么参数等信息。 - javAscript引擎可以优化符合尾调用条件的函数,以节省栈空间。 - 闭包的作用域链中包含自己的一个变量对象,然后是包含函数的变量对象,直到全局上下文的 变量对象。 - 通常,函数作用域及其中的所有变量在函数执行完毕后都会被销毁。 - 闭包在被函数返回之后,其作用域会一直保存在内存中,直到闭包被销毁。 - 函数可以在创建之后立即调用,执行其中代码之后却不留下对函数的引用。 - 立即调用的函数表达式如果不在包含作用域中将返回值赋给一个变量,则其包含的所有变量都 会被销毁。 - 虽然 Javascript没有私有对象属性的概念,但可以使用闭包实现公共方法,访问位于包含作用域 中定义的变量。 - 可以访问私有变量的公共方法叫作特权方法。 - 特权方法可以使用构造函数或原型模式通过自定义类型中实现,也可以使用模块模式或模块增 强模式在单例对象上实现。 --- 转载至CSDN博主:[[李唐敏民](https://blog.csdn.net/qq_39041210 "李唐敏民")] --- 最后修改:2021 年 02 月 18 日 © 允许规范转载 打赏 赞赏作者 支付宝微信 赞 如果觉得我的文章对你有用,请随意赞赏
1 条评论
以终为始的思考方式为行业指明方向。