第二十四节: ES6 函数扩展

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 = 3var去除,函数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. 展开数组

    ...[1,2,3] ==> 1,2,3

  2. 重置为数组

    ... 1,2,3 ==> [1,2,3]; 只能在传参的时候用

  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 总结一下
  1. 函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象。
  2. 不可以使用arguments对象,该对象在函数体内不存在。如果要用,可以用 rest 参数代替。
  3. 不可以当作构造函数,也就是说,不可以使用new命令,否则会抛出一个错误。


5.3. 函数参数的尾逗号

ES2017 允许函数的最后一个参数有尾逗号(trailing comma)。

这样的规定也使得,函数参数与数组和对象的尾逗号规则,保持一致了。

知道一点东西

function show(a,b,c,){
    console.log(a,b,c)
}
show(1,2,3,)
// 以前这么写会报错,参数最后不能加逗号结束
// 但是ES2017 以后就不报错了

你可能感兴趣的:(第二十四节: ES6 函数扩展)