1. 函数参数的默认值
1.1. 函数的默认参数
function show(a,b){
console.log(a,b);
}
show("welcome","wuwei"); // welcome wuwei
既然是函数封装,那函数封装是不是要具有通用性,
比如 我突然不想传第二个参数
function show(a,b){
console.log(a,b);
}
show("welcome",""); // welcome
show("welcome"); //welcome undefined
1.1.1. ES6之前设置默认参数
我们以前是怎么处理的,是不是用我们的短路算法
function show(a,b){
b = b || "china"
console.log(a,b);
}
show("welcome",""); // welcome china
show("welcome"); // welcome china
如果我第一个参数不传,我是不是也要一个默认值啊
function show(a,b){
a = a || "welcome";
b = b || "china";
console.log(a,b);
}
show("您好","wuwei"); // 您好 wuwei
show("","wuwei"); // welcome wuwei
1.1.2. ES6设置默认参数
ES6的默认参数,
function show(a = "欢迎",b = "无为"){
console.log(a,b);
}
show(); // 欢迎 无为
show("welcome"); //welcome 无为
1.1.3. 默认参数的位置
起一个不传不行,会挂掉的
show( , "wuwei"); // 就报错
通常情况下,定义了默认值的参数,应该是函数的尾参数。因为这样比较容易看出来,到底省略了哪些参数。如果非尾部的参数设置默认值,实际上这个参数是没法省略的。
// 例一
function f(x = 1, y) {
return [x, y];
}
f() // [1, undefined]
f(2) // [2, undefined])
f(, 1) // 报错
f(undefined, 1) // [1, 1]
// 例二
function f(x, y = 5, z) {
return [x, y, z];
}
f() // [undefined, 5, undefined]
f(1) // [1, 5, undefined]
f(1, ,2) // 报错
f(1, undefined, 2) // [1, 5, 2]
1. 2. 默认参数结合对象解构赋值
遇到我们无法上面的情况怎么办,还记得我们之前的对象解构吗
function show({a = "欢迎",b = "无为"}){
console.log(a,b);
}
show({a:"welcome",b:"wuwei"}); // welcome wuwei
show({b:"wuwei"}); //欢迎 wuwei
show({}); // 欢迎 无为
// 但是这样就报错
function show({a = "欢迎",b = "无为"}){
console.log(a,b);
}
show(); // 报错
// 所以我们对象也得给一个默认值
function show({a = "欢迎",b = "无为"} = {}){
console.log(a,b);
}
show(); // 欢迎 无为
1.3. 函数的参数默认已经定义了,你不能再用let,const声明了
function show(age = 18){
let age = 20;
console.log(age);
}
show(); // 程序报错,age已经被定义过了
// Uncaught SyntaxError: Identifier 'age' has already been declared
2. 默认参数对函数length的影响
指定了默认值以后,函数的length
属性,将返回没有指定默认值的参数个数。也就是说,指定了默认值后,length
属性将失真。
(function (a) {}).length // 1
(function (a = 5) {}).length // 0
(function (a, b, c = 5) {}).length // 2
- fn.length 返回形参个数
- arguments.length 返回实参个数
3. 默认值与作用域
一旦设置了参数的默认值,函数进行声明初始化时,参数会形成一个单独的作用域。等到初始化结束,这个作用域就会消失。这种语法行为,在不设置参数默认值时,是不会出现的。
var x = 1;
function f(x, y = x) {//AO{x:2,y:2}
console.log(y);
}
f(2) // 2
上面代码中,参数y
的默认值等于变量x
。调用函数f
时,参数形成一个单独的作用域。在这个作用域里面,默认值变量x
指向第一个参数x
,而不是全局变量x
,所以输出是2
。
let x = 1;
function f(y = x) { //AO{y:1,x:2}
let x = 2;
console.log(y);
}
f() // 1
上面代码中,函数f
调用时,参数y = x
形成一个单独的作用域。这个作用域里面,变量x
本身没有定义,所以指向外层的全局变量x
。函数调用时,函数体内部的局部变量x
影响不到默认值变量x
。
var x = 1;
function foo(x = x) { //AO{x:1}
// ...
}
foo() // ReferenceError: x is not defined
上面代码中,参数x = x
形成一个单独作用域。实际执行的是let x = x
,由于暂时性死区的原因,这行代码会报错”x 未定义“。
var x = 1;
function foo(x, y = function() { x = 2; }) {
var x = 3;
y();
console.log(x);
}
foo()//3
x//1
如果将var x = 3
的var
去除,函数foo
的内部变量x
就指向第一个参数x
,与匿名函数内部的x
是一致的,所以最后输出的就是2
,而外层的全局变量x
依然不受影响。
var x = 1;
function foo(x, y = function() { x = 2; }) {
x = 3;
y();
console.log(x);
}
foo() // 2
x // 1
4. rest 参数
ES6 引入 rest 参数(形式为...变量名
),用于获取函数的多余参数,这样就不需要使用arguments
对象了。reset 参数搭配的变量是一个数组,该变量将多余的参数放入数组中。
4.1. 扩展运算符,reset运算符
写法就是...
三个点
两个作用,扩展和剩余
4.1.1 扩展运算符
let arr = ["apple","banana","arange"];
console.log(arr); // ["apple", "banana", "arange"]
console.log(...arr); // apple banana arange
// ...arr 是将数组arr的内容每一项扩展出来
... 能展开数组,
4.1.2 重置运算符,配合函数使用
刚讲的扩展运算符,是将[1,2,3,4,5] ==> 1,2,3,4,5
function show(a){
console.log(a); // 1
}
show(1,2,3,4,5);
// 如果我想讲 1,2,3,4,5 == > [1,2,3,4,5]
function show(...a){
console.log(a); // [1, 2, 3, 4, 5]
}
show(1,2,3,4,5);
4.1.3 解决了arguments问题
arguments
对象不是数组,而是一个类似数组的对象。所以为了使用数组的方法,必须使用Array.prototype.slice.call
先将其转为数组。rest 参数就不存在这个问题,它就是一个真正的数组,数组特有的方法都可以使用。下面是一个利用 rest 参数改写数组push
方法的例子。
function show(){
console.log(arguments); // Arguments(5) [1, 2, 3, 4, 5, callee: ƒ, Symbol(Symbol.iterator): ƒ]
}
show(1,2,3,4,5);
// 如果想把arguments这个类数组变成数组
function show(){
let arr = Array.prototype.slice.call(arguments)
console.log(arr); // [1, 2, 3, 4, 5]
}
show(1,2,3,4,5);
// 如果想给这个数组排序
function show(){
let arr = Array.prototype.slice.call(arguments)
arr.sort(function(a,b){return a-b});
console.log(arr);
}
show(1,102,32,14,5); //[1, 5, 14, 32, 102]
但是如果用ES6排序
function show(...arr){
arr.sort(function(a,b){return a-b});
console.log(arr);
}
show(1,102,32,14,5); // [1, 5, 14, 32, 102]
4.1.4 传参时扩展
我们还可以在传参的时候扩展
// 解构方法
function show([a,b,c]){
console.log(a,b,c);
}
show([1,9,6]); // 1 9 6
// 扩展运算符,我在实参的时候就让你扩展
function show(a,b,c){
console.log(a,b,c);
}
show(...[1,9,6]); // 1 9 6
4.1.5 又叫剩余运算符
function show(a,b,c){
console.log(a,b,c);
}
show(1,2,3,4,5); // 1 2 3
这样我们abc拿到的就是1,2,3其他的是不是就没管啊
剩余运算符就是将剩余的参数放到一个数组中
function show(a,b,...c){
console.log(a,b,c);
}
show(1,2,3,4,5); // 1 2 [3,4,5]
// 这个时候发现c是出了a,b对应的那两项外剩余的所有参数组成的数组
如果当成剩余运算符来用必须放最后
注意哦!下面的写法会报错
function show(a,...b,c){
console.log(a,b,c);
}
show(1,2,3,4,5); // 报错
如果我们需要赋值一份数组,
var arr = [2,3,6,5,8];
var arr2 = [...arr];
// 以前Array身上还有一个from方法,拷贝数组
var arr = [2,3,6,5,8];
var arr2 = Array.from(arr);
// 这个方法还可以将字符串变成数组
var str = "abc";
var arr = Array.from(str);
console.log(arr); //["a", "b", "c"]
总结
展开数组
...[1,2,3] ==> 1,2,3
重置为数组
... 1,2,3 ==> [1,2,3]; 只能在传参的时候用
剩余参数,必须放到最后
function(a,b,...c){}
4.1.6 函数的length不包括rest参数
(function(a) {}).length // 1
(function(...a) {}).length // 0
(function(a, ...b) {}).length // 1
5. 箭头函数
5.1. 基本用法
ES6 允许使用“箭头”(=>
)定义函数。
箭头函数
() => {
}
5.1.1 箭头函数里面只有return语句
// 之前函数的写法
function show(){
return 1;
}
// 箭头函数的写法
let show = () => 1;
// 这里的1 就是return 1
function show(a,b){
return a + b;
}
// ES 6
let show = (a,b) => a + b;
注意如果我想return一个对象,这样写就会出问题
// 报错
let getTempItem = id => { id: id, name: "Temp" };
由于大括号被解释为代码块,所以如果箭头函数直接返回一个对象,必须在对象外面加上括号,否则会报错。
// 不报错
let getTempItem = id => ({ id: id, name: "Temp" });
5.1.2 常用的使用方法
如果想写语句,那么语句的写法是这样的
() => {
语句;
return;
}
这才是一个完整的箭头函数的写法
let show = (a,b) => {
console.log(a,b);
return a + b;
}
show();
5.2. 箭头函数注意点
箭头函数需要注意一下几点:
5.2.1 箭头函数的this指向
let obj = {
age: 18,
show: function(){
console.log(this.age);
}
}
obj.show(); // 毋庸置疑 打印18 this 指向obj
// 如果换成箭头函数呢
var age = 20;
let obj = {
age: 18,
show: () =>{
console.log(this.age);
}
}
obj.show();
// 关于this指向定义时作用域中的this
var a = 11;
function test(){
this.a = 22;
let b = () =>{
console.log(this.a)
}
b()
}
var obj = new test();
// 如果我里面添加定时器
let obj = {
age : 18,
show: function(){
setTimeout(function(){
alert(this.age); // 此时弹出undefined,此时this指向window
},2000)
}
}
obj.show();
// 如果使用箭头函数呢
let obj = {
age:18,
show: function(){
setTimeout(()=>{
alert(this.age); // 这里会弹出18 ,为什么呢
},2000)
}
}
obj.show();
此时就是this的问题,以前我们讲this的时候,说this指向运行时,也就是调用这个函数运行的对象,
但是箭头函数的this指向,函数定义时所在函数的this对象
因为箭头函数this
指向的固定化,并不是因为箭头函数内部有绑定this
的机制,实际原因是箭头函数根本没有自己的this
,导致内部的this
就是外层代码块的this
。正是因为它没有this
,所以也就不能用作构造函数。
5.2.2 箭头函数中的arguments
在箭头函数中没有arguments
// 普通函数
function show(){
console.log(arguments);
}
show(1,2,3,4,5); // Arguments(5) [1, 2, 3, 4, 5, callee: ƒ, Symbol(Symbol.iterator): ƒ]
// 箭头函数
let show = () => {
console.log(arguments);
}
show(1,2,3,4,5); // 报错
// Uncaught ReferenceError: arguments is not defined at show
那在箭头函数里面没有arguments,那用什么呢,可以用剩余参数
// 箭头函数
let show = (...args) => {
console.log(args);
}
show(1,2,3,4,5); // 打印 [1, 2, 3, 4, 5]
5.2.3 箭头函数不能到构造函数
因为箭头函数内部没有this,所有不能当成构造函数使用
// 普通的构造函数
function Show(){
this.name = "abc";
}
let show = new Show();
console.log(show.name); // abc
let Show = () => {
this.name = "abc"
}
let show = new Show();
console.log(show.name); // 报错 类型错误
// Uncaught TypeError: Show is not a constructor
5.2.4 总结一下
- 函数体内的
this
对象,就是定义时所在的对象,而不是使用时所在的对象。 - 不可以使用
arguments
对象,该对象在函数体内不存在。如果要用,可以用 rest 参数代替。 - 不可以当作构造函数,也就是说,不可以使用
new
命令,否则会抛出一个错误。
5.3. 函数参数的尾逗号
ES2017 允许函数的最后一个参数有尾逗号(trailing comma)。
这样的规定也使得,函数参数与数组和对象的尾逗号规则,保持一致了。
知道一点东西
function show(a,b,c,){
console.log(a,b,c)
}
show(1,2,3,)
// 以前这么写会报错,参数最后不能加逗号结束
// 但是ES2017 以后就不报错了