一、函数的拓展
1.1、默认参数
在ES5中设置默认参数:
javascript
`function func(words, name) {
name = name || "闷墩儿";
console.log(words, name);
}
func("大家好!我是");
func("大家好!我是", "憨憨");
func("大家好!我是", "");`
在控制台可以看见如下:
在以上代码中:
- func函数一共有两个形式参数,在func('大家好!我是') 我们指定第一个形式参数words的值,所以name 使用的默认值【闷墩儿】
- 在 func('大家好!我是','憨憨')中我们指定了两个形式参数的值,所以name没有使用默认值。
- 在 func('大家好!我是','')中我们指定了第二个形式参数为空字符串,被认为是没有赋值,所以也使用的是默认值。
注:如果我们想给形式参数设置默值需要在函数中单独定义。
在ES6中我们可以直接在函数的形参里设置默认值。
举个例子:
新建一个 index.html 文件,在文件中写入以下内容:
javascript
`function func(words,name="草莓"){
console.log(words,name);
}
func("请给我一个");
func("请给我一个","梨");
func("请给我一个","");`
在上面的代码中:
- func("请给我一个") 只传入第一个参数,第二个参数没传入任何值,故第二个参数使用了默认值。
- func("请给我一个","梨") 传入了二个参数,所以第二个参数没有使用默认值。
- func("请给我一个","") 第二个参数,虽然传入的是空字符串,空字符串也算是一个参数值,故同样不会使用默认值。
可以看到在函数中设置默认值,如果传入的第二个参数值为空字符串,会被当作值传入,而不会使用默认值。
参数变量是默认声明的,我们不能用let或者const 再次声明
举个例子:
javascript
`function func(words, name= "草莓"){
let words ="我需要一个";
console.log(words,name);
}
func("请给我一个","梨");`
可以看到控制台出现了报错:
在设置默认值的时候也要注意设置的位置:
设置默认值的参数,一般放在其他参数的后面,也就是尾部。
如果给第一个形参设置了默认值,调用函数传参时,无法省略该参数,将会直接代替。
举个例子:
javascript
``function func(words ="你好",name){
console.log(`${words}${name}`);
}
func("小蓝");``
在控制台可以看见,传入函数的值"小蓝",被当作第一个参数的值,而第二个参数没有传入值,所以输出的是 undefined.
使用自定义函数作为形式参数的默认值:
举个例子:
javascript
`function part(){
return "❤";
}
function func(words,name =part()){
console.log(words,name);
}
func("请给我一颗");
func("请给我一颗","💗");`
在控制台可以看见:
在上面的代码中:
- 我们定义了一个名为 parameter 的函数,在函数中返回了一颗黑色的爱心。
- 给 func 函数中的第二个形式参数设置默认参数为 parameter 函数中的返回值。
- 使用 func('请给我一颗小'),只设置了第一个形参的值,所以第二个参数使用的是默认值「小黑心」。
- 使用 func('请给我一颗小','💗'),设置了两个形参的值,所以第二个参数没有使用默认值。
解构参数
解构可以用在函数参数传递的过程中。
举个例子:
javascript
``function func(name,value,mount,{a,b,c,d= "苹果"}){
console.log(`${name}用${value}元钱买了${mount}
个${d}。`);
console.log(`${name}用${value}元钱买了${mount}
个${d}。`);
}
func("小蓝",5,3,{
a:"小蓝",
b:"菠萝",
c:"桃子",
});``
在控制台可以看见如下输出:
在上面的代码中:
- func 函数包含了4个参数,其中4个参数是解构参数,解构参数里面包含4个参数变量 a,b,c,d。
- 使用func('小蓝',5,3,{a:'西瓜',b:'菠萝',c:'桃子'}) 调用该函数,其中传入name 参数的值为"小蓝",value 参数的值为 5,mount 参数的值为 3;解构参数只传入三个值,a 的值为"西瓜",b 的值为"菠萝",c 的值为"桃子",d 使用的是默认值。
在上面代码中可以看出,解构参数是以键值对的形式传入的,其参数名与键值名保持一致,即为成功。
rest参数
rest 参数又称剩余参数,用于获取函数的多余参数。rest参数搭配的变量是一个数组,改变量将多余的参数放入数组中。
rest 参数和扩展运算符在写法上样,都是三点(...),但两者的使用方法上是截然不同的。
扩展运算符就像是rest参数的逆运算,主要用于以下几个方面:
- 改变函数的调用。
- 数组构造。
- 数组解构。
rest 参数语法格式为:
javascript
`myfunction(parameters,...rest);
//剩余参数必须是函数的最后一个参数`
举个例子:
javascript
`function func(a,b,...rest){
console.log(rest);
}
func(1,2,3,4,5,6,7,8);`
在控制台可以到如下输出:
在上面代码中,给了func函数传入了10个参数,形式参数a和b 各取一个值,多余的8个参数都被rest参数获取。
箭头函数
箭头函数,顾名思义,就是箭头(=>)来表示的函数。箭头函数和普通函数都是用来定义函数的,但两者在语法构成上有所不同。
新建一个文件,使用es5的方式定义一个函数。
javascript
`let sum = function (a,b){
return a+b;
}
console.log(sum(1,2));`
上面定义了一个求和函数
修改代码使用箭头函数:
javascript
`let sum=(a,b)=>a+b;
console.log(sum(1,2));`
两个程序都会显示输出为 3
可以看出使用箭头函数代码变得简洁了。
箭头函数的基本使用方法:
javascript
`(param1,param2,...,paramN)=>{expression}`
箭头函数前面()放着函数的参数,箭头函数后面{}放着函数内部执行的表达式。
箭头函数除了代码简洁之外,还解决了匿名函数的this 指向问题。
this 指向
this 是指想调用包含自身函数对应的对象。
一个函数能够实现在指定时间周期内一直计数。用es5语法实现:
javascript
`function Number(){
var that= this;
that.num=1;
setInterval(function count(){
that.num++;
},1000);
}`
在上面例子中,定义了一个名 Number() 的函数,该函数里包含一个 num 参数。在 Number() 函数里有一个 count() 的函数,在其内部让 num 自增。count 函数是作为 setInterval() 函数的参数而存在的,setInterval() 函数的作用是在某个周期内自动调用函数。
可以看到什么两个函数中,每个函数都会定义自己的this ,this 只会处在于你所使用的作用域范围内。
而在箭头函数中的this 对象,就是定义改函数所在的作用域所指向的对象,而不是使用所在作用域指向的对象。
javascript
`function Number(){
this.num=1;
setInterval(()=>{
this.num++;
},100);
} `
总结一下区别:
- 箭头函数的this 指向是其上下文的this,没有方法可以改变其指向。
- 普通函数的this 指向调用它的那个对象。
二.类的扩展
四.异步编程
1.1 Promise 对象基础应用
在开发中,会遇到这样的需求:通过接口1的返回值,去获取接口2的数据,然后通过接口2的返回值,获取接口3的数据。即每次请求接口数据时,都需要使用上一次的返回值。为了实现这个需求,通常回事用回调函数来完成,即把函数作为参数进行层层嵌套。
代码如下:
javascript
`<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<script>
var outnum = function(n, callback){
setTimeout(function(){
console.log(n);
callback();
},100);
};
outnum("1",function(){
outnum("2",function(){
outnum("3",function(){
console.log("0")'
};
};
};
</script>
</body>
</html>`
在上述代码中,我们可以发现可以通过回调函数层层嵌套的形式达到最终数据请求的目的,但代码结构不明朗,可读性差。
定义Promise
为了解决这种地狱式回调,可以使用Promise对象 ,且代码更优雅,由于Promise对象是一个构造函数,必须通过实例化来生成,定义格式代码如下:
javascript
`let p= new Promise(function(resolve,reject){
//异步事件
}`
在定义格式的代码中,需要说明的几个问题:
- 在实例化中,参数为函数,函数中又有两个用于回调的函数
- 两个回调函数中, resolve 为异步成功执行时的回调,其参数可以传递执行的结构。
- reject为异步执行失败的回调,其参数可以传递失败的结果信息。
使用 resolve和reject方法传递出去的参数,需要用到then方法接收。
Promise对象的then方法
Promise对象实例化之后,可以调用 then 方法获取两个回调函数中的传参值,该方法接收两个回调函数作为参数,第一个参数是必选参数,表示异步成功执行后的resolve 回调函数,第二个参数是可选参数,表示异步失败后执行的reject 回调函数。
格式如下:
javascript
`p.then(
function(v){},
function(e){}
);`
其中参数v的值表示resolve回调函数的参数值,e值表示reject回调函数中的参数值。
如下列代码所示:
javascript
`let n=6;
let p2 =new Promise(function(resolve,reject){
setTimeout(function(){
if(n>5){
resolve(n);
}else{
reject("必须大于5")
}
});
});
p2.then(
function(v){
console.log(v);
},
fucntion(e){
console.log(e);
}
);
//结果是 6`
此外,一个then方法执行后,如果仍然返回一个Promise对象,则可以继续在执行then方法,形成链式结构
解决地狱式回调
使用Promise来实现开头提到的需求,从而解决由此引起的回调地狱问题。
代码如下:
javascript
`<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<script>
var outnum= function(order){
return new Promise(function(resolve,reject){
setTimeout(function(){
console.log(order);
resolve(); //完成异步回调后执行函数
},1000);
});
};
outnum("1")
.then(function(){
return outnum("2");
})
.then(function(){
return outnum("3");
})
.then(function(){
return outnum("0");
});
</script>
</body>
</html>`
执行上述代码之后的页面效果与使用地狱式回调方式是完全一样的,但 Promise 对象实现的代码可读性更强,还可以很方便地取到异步执行后传递的参数值,因此,这种代码的实现方式,更适合在异步编程中使用。
Promise 对象中的方法
1.1 Promise.all 方法
Promise.all()
方法用于将多个 Promise 实例,包装成一个新的 Promise 实例。
是不是感觉上面的解释太简便了,以至于对它的用法有点模糊。那我们就详细地对Promise.all进行解释。
其实我们可以从参数和返回值两个方面对Promise.all()
进行剖析。
首先是参数方面:
Promise.all()
的参数是一个包含多个Promise实例的数组(当然数组内部Promise实例数量可以是一个,但那就没必要使用all方法,但会在你不清楚Promise实例数量时使用)。
然后看返回值方面。
返回值要分为两种情况:
- 第一种情况就是数组内部的Promise实例全部成功,就是状态变为resolve,注意是全部,全部,全部(all)。这样它的返回值也是一个数组,但是这个数组里面的元素变为对应的原Promise实例的resolve状态的值。后面可以调用then方法使用。
- 第二种情况就是数组内部的Promise实例没有全部成功,这样它的状态就会变为reject拒绝状态。
这样的话,我们就可以做一个简单地总结:
Promise.all()
方法就是检测参数数组内部的所有Promise实例是否成功,若成功,则调用then方法进行处理,否则变为reject状态。
重点还是all,all,all。