企业🤖AI智能体构建引擎,智能编排和调试,一键部署,支持私有化部署方案 广告
[toc] ### 参数默认值 #### 使用方法 在书写参数时,直接给形参赋值,赋的值即为该参数的默认值。当调用函数时,如果没有给对应的参数赋值(给的值是undefined),则会自动使用默认值。 ```js function sum (n1, n2 = 10, n3 = 5){ return n1+n2+n3; } sum(10, 20, 30); //三个参数都给的话按正常参数计算,返回60 sum(20); //只给第一个参数,后两个参数取默认值,返回35 sum(10, undefined, 10); //第一个和第三个参数有值,第二个没赋值取默认值,返回30 sum(); //一个参数都没给,因为n1没有默认值,相当于undefined+10+5,返回NaN ``` >参数默认值可以为各类型的值和表达式,例如 ```js function getEle (name, container = document.getElementById('mydiv'), content){ //执行代码块 } ``` #### [扩展\]对arguments的影响 在正常模式下,arguments和形参是同步的,即在函数体中,改变形参的值,arguments对应的值也会改变。 在严格模式下(use strict),arguments和形参是脱离的,即在函数体中,改变形参的值,arguments对应的值是不变的。 无论在哪种模式下,只要给参数加了默认值,那么该函数就会自动按照严格模式的规则来执行,即arguments与形参脱离关系。 #### [扩展\]留意暂时性死区 形参和ES6中的let和const声明一样,具有作用域,并且根据参数的声明顺序,存在暂时性死区。 ```js function test (a, b = a){ console.log(a, b); } test(1); //执行该代码可以正常输出1 1,因为b取默认值的时候a已经有值了。 function test (a = b , b){ console.log(a, b); } test(undefined, 3); //执行该代码会报错,因为a的默认值为b,此时b还没有被赋值,还在暂时性死区内,所以会报错。 ``` >在ES6模式下,函数体内不能再声明与参数同名的变量,这样也会报错。其原因都是因为参数作用域的关系。 ### 剩余参数 在日常编写函数的过程中,调用函数时往往形参个数不固定,无论调用者设置几个参数都可以执行。此时通常使用arguments来获取用户输入的所有参数。 但arguments存在一些缺陷: 1. 如果跟形参配合使用,根据是否为严格模式及参数值被改变的关系,容易造成混乱。 2. 从语义上来说,使用arguments获取参数,由于形参缺失,无法从函数定义上理解函数的真实意图。 为解决上面的问题,ES6制定了剩余参数的概念。专门用于收集末尾的所有参数,并将其放到一个形参数组中。 剩余参数的用法是在参数名前面加上'...',用法如下: ```js function test(...value){ //value即是定义的剩余参数 console.log(value) } test(1); //输出结果[1] test(1,3); //输出结果[1,3] test(1,3,4,5); //输出结果[1, 3, 4, 5] ``` >使用剩余参数,无论输入多少个参数,都会将其整合为一个数组。是解决参数不固定,替代arguments的一个完美方案。 剩余参数的使用注意事项: 1. 一个函数,在形参中只能定义一个剩余参数。 2. 剩余参数必须是该函数最后一个参数。 ### 展开运算符 在日常开发中,由于参数的数量不确定,我们会使用剩余参数来解决这个问题。但如果给的参数本身就是一个数组或对象,剩余参数就会出现数组套数组或数组套对象的情况。这种情况在函数体中是很难操作的。我们想要的是数组或对象中的每一项分别做为参数给这个函数。从ES6开始为我们提供了展开运算符的方法来解决这个问题 使用方法,在要展开的参数名前面加'...',例如: ```js let arr= [1,2,3,4,5,6]; test(...arr); //此时将arr数组中的每一个值分别做为参数。相当于test(1,2,3,4,5,6) ``` >加了展开运算符的参数,可以在函数参数列表的任何位置出现。 #### 针对数组展开(ES6中提供的方法) 展开运算符可以实现只有一层的数组深拷贝功能。 ```js let arr1= [1,2,3,4]; let arr2= [...arr1]; //一行代码就完成了arr2深度克隆arr1,两个数组值一样,但互相不关联。 let arr3= [1,3,...arr1,4,6]; //也可以任何位置插入其它的值。 ``` #### 针对对象展开(ES7中提供的方法) ```js let obj1 = {name:'wang', age:30}; let obj2 = {...obj1}; //同数组一样,也可以深拷贝只有一层的对像,两个对像值一样,但互相不影响。 let obj3 = {...obj1, age:28}; //利用该方法可以实现复制原有对像的值再将需要改变的值重新赋值。 ``` **注意:** >展开运算符实际来讲只能算是浅拷贝,只能支持只有一层数据的数组或对象深拷贝。如果对象中还包含一个对象,那么被包含的对象是无法被深拷贝的。 ### 函数的双重用途 当创建一个函数后,调用该函数有两种方式,一种是使用new来创建一个新的函数,另一种是直接调用该函数。 在ES6之前,函数体内部是无法判断该函数是否采用new的方式来调用的。有时就会因为this的指向问题导致报错。 在ES6中为我们提供了一个特殊的API(new.target),可以使用该API在函数内部,判断该函数是否使用了new来调用。 new.target使用该表达式返回的结果是:如果没有使用new来调用函数,则返回undefined。如果使用new来调用,则返回的是new后面的函数体。 利用该API,就可以在函数体内判定该函数是否采用new的方式来调用的: ```js //定义一个构造函数Test function Test(...args){ if (new.target === undefined){ //返回报错信息并退出函数 } //正常执行的函数体 } //使用该方法就可以限制该函数只能使用new来调用。 ``` ### 箭头函数 #### 回顾this指向问题 ES6中箭头函数的出现,可以解决一部份this指向的问题。 首先,回顾一下this指向的几种情况: 1. 通过对象调用函数,this指向对象 2. 直接调用函数,this指向全局对象 3. 如果通过new调用函数,this指向新创建的对象 4. 如果通过apply、call、bind调用的函数,this指向该方法指向的数据 5. 如果是DOM事件函数,this指向事件源 #### 箭头函数的使用语法 ES6中箭头函数的出现,可以非常好的解决this指向的问题。 1. 箭头函数是一个函数表达式,理论上,任何使用函数表达式的场景都可以使用箭头函数。 ```js //什么是函数表达式? const test = function(a, b){ //函数体内容 }; //上面的例子中,等号后面的所有内容叫做函数表达式。 function abc(){ //函数体内容 } //函数abc叫做函数声明,不是函数表达式 ``` 2. 完整的箭头函数语法 ```js     (参数1, 参数2, ...args) => { //函数体内容     } ``` 3. 如果声明的箭头函数参数只有一个,可以省略小括号 ```js 参数 => { //函数体 } ``` >该写法只适用于只有一个参数的情况,如果有多个参数和没有参数均不能用这种写法。 4. 如果箭头函数的函数体,只有一条返回语句,可以省略大括号和return关键字。 ```js 参数 => 返回结果的表达式; ``` 5. 如果箭头函数只有一条返回语句,但返回的是对象,需在返回对象的大括号外面加一个()包起来。否则系统会认为对象的{}是函数体,会报语法错误。 ```js let sum = (a, b) => ({         a:a,         b:b,         sum:a+b     }) //用()包裹起来,就代表{}中是一个表达式,而不是函数体。 ``` #### 箭头函数的注意细节 1. 箭头函数函数体中的this,取决于箭头函数定义的位置的this指向,而与如何调用无关。 2. 箭头函数中,不存在this、arguments、new.target。如果使用了,则使用的是函数外层对应的this、arguments、new.target。 3. 箭头函数没有原型,因此箭头函数不能作为构造函数使用。 #### 箭头函数的适用场景 1. 临时性使用的函数,并不会刻意调用它,例如:     1. 事件处理函数     2. 异步处理函数     3. 其它临时性函数 2. 为了绑定外层this的函数 3. 在不影响其它代码的情况下,保持代码的整清。例如常使用的:数组方法中的回调函数