02.ECMAScript 6 新特性

177 阅读10分钟

一.let关键字

1.1 var的弊端

使用var关键字声明变量的弊端:

1、var声明的变量有预解析,造成 逻辑混乱,可以先使用,后声明

2、var可以重复定义同一个变量,逻辑错误,第二次应该是修改变量,而不是定义

3、var用在for循环条件中,造成for 循环的污染的问题

4、var 声明的变量没有块级作用域(ES5中的作用域:全局和局部)

<script>
    /* 
   Var的弊端
      1.var会有预解析,在变量声明之前可以使用
  */
    //1.var会有预解析,在变量声明之前可以使用
    console.log(str1); //undefined
    var str1 = "hello";

    //2.var可以重复定义
    var num1 = 10;
    var num1 = 30;
    console.log(num1);

    //3.块级作用域的问题:ES5没有块级作用域,只有全局作用域和函数作用域
    {
      var str2 = 'wrold';
    }
    console.log(str2);

    //4.for循环中变量污染
    var i;
    for (i = 0; i < 4; i++) {
      console.log(i); //0 1 2 3
    }
    console.log(i); //4,变量污染
  </script>

1.2 ES6变量声明:let

let 关键字用来声明变量,使用 let 声明的变量有几个特点:

  1. ==不允许重复声明==
  2. ==块儿级作用域==
  3. ==不存在变量提升==
  4. ==不影响作用域链== ==应用场景:以后声明变量使用 let==
 /* 
   let关键字的使用:
     1.let取消预解析,变量声明不会提升,必须先声明然后再使用
     2.let不可以重复定义
     3.let有块级作用域
     4.解决for循环里的变量污染
     5.let不影响作用域链
   */
    //1. let取消预解析, 变量声明不会提升, 必须先声明然后再使用
    //console.log(str); //Uncaught ReferenceError: Cannot access 'str' before initialization
    let str = "hello";

    // 2. let不可以重复定义
    let str2 = "world";
    //let str2 = "jason";
    //console.log(str2); //Uncaught SyntaxError: Identifier 'str2' has already been declared

    //3.let有块级作用域
    {
      let num = 10;
    }
    //console.log(num); //Uncaught ReferenceError: num is not defined

    //4.解决for循环里的变量污染
    for (let i = 0; i < 4; i++) {
      console.log(i); //0 1 2 3
    }
    //console.log(i); //Uncaught ReferenceError: i is not defined

    //5.let不影响作用域链
    let school = "atguigu"; {
      let school = "尚硅谷";

      function fn() {
        console.log(school); //"尚硅谷"
      }
      fn();
    }
    console.log(school); //"atguigu"

二.块级作用域

块指的是代码块,一对 {} 之间就是一个代码块。

变量的有效范围只在这对 大括号 之间,就是块级作用域

块级作用域非常有必要:

var btns = document.querySelectorAll('button')
for(var i = 0; i < btns.length; i++){
  btns[i].onclick = function(){
    console.log(i)
  }
}

此时我们点击按钮,输出的i不会是每个按钮对应的按钮,而是按钮的个数。就是因为for里面的变量是var声明的,没有形成块级作用域,每次访问i都是访问的全局的i。如果我要在事件里面得到按钮的索引,就需要别的方法来实现。

如果我们换成let

let btns = document.querySelectorAll('button')
for(let i = 0; i < btns.length; i++){
  btns[i].onclick = function(){
    console.log(i)
  }
}

此时我们点击按钮,就能输出按钮对应的索引了。这是因为我们使用了let关键字,let会在{}之间形成一个块级作用域,for循环执行多少次,我们就会形成多少个块级作用域,每个i对应1个,当我们在事件里面获取i的时候,i会到事件函数的上级作用域找,刚好就是每个i对应的块级作用域。我们就简单地得到了每个按钮对应的索引。

所以,let的特点:

1、let声明的变量没有预解析,不会有变量提升

2、let不可以重复定义同一个变量

3、let用在for循环条件中,不会造成for 循环的污染的问题

4、let声明的变量有块级作用域(ES6中的作用域:全局和局部还有块级作用域)

三.const关键字

==const 关键字用来声明常量==,const 声明有以下特点

  1. const 一旦声明就必须赋值,不赋值就会报错
  2. 标识符一般为大写(潜规则,因为是常量,常量一般是大写)
  3. const不允许重复声明
  4. const拥有块级作用域
  5. 一旦赋值就不能修改(基本数据类型)
  6. const修改引用数据类型
    1. 直接修改引用数据类型的引用地址:不可以,会报错:Uncaught TypeError: Assignment to constant variable.
    2. const修改数组里的数据:可以 应用场景:声明对象类型使用 const ,非对象类型声明选择 let
 /* 
    const关键字
      1.const 一旦声明就必须赋值,不赋值就会报错
      2.const一旦赋值就不能修改
      3.const拥有块级作用域
      4.const修改引用数据类型(对象)
        4.1 const直接修改引用类型的地址:不允许
        4.2 const修改引用类型的里面的数据:可以
      5.const修改引用数据类型(数组)
        5.1 const直接修改数组:不允许
        5.2 const修改数组里的数据:可以  
    */
    //1.const 一旦声明就必须赋值,不赋值就会报错
    //const str; //Uncaught SyntaxError: Missing initializer in const declaration

    //2.const一旦赋值就不能修改
    // const str = "hello";
    // str = "hello world";
    // console.log(str); //Uncaught TypeError: Assignment to constant variable.

    // 3.const拥有块级作用域
    // {
    //   const num = 10;
    // }
    // console.log(num); //Uncaught ReferenceError: num is not defined

    //4.const修改引用数据类型(对象)
    //4.1 const直接修改引用类型的地址:不允许
    const obj = {
      name: 'jason',
      age: 18
    }
    // obj = {
    //   genger: 'male'
    // }
    //console.log(obj); //04. const关键字.html: 75 Uncaught TypeError: Assignment to constant variable.

    //4.2 const修改引用类型的里面的数据:可以
    obj.name = "grace";
    console.log(obj); //{name:'grace',age:18}

    //5.const修改引用数据类型(数组)
    //5.1 const直接修改数组:不允许
    const arr = [1, 2, 3, 4, 5];
    // arr = [1, 1, 1];
    // console.log(arr); //Uncaught TypeError: Assignment to constant variable.

    //5.2 const修改数组里的数据:可以
    arr[0] = 10;
    console.log(arr); //[10,2,3,4,5]

四.模板字符串

在es5中,当我们需要把多个字符串和多个变量拼接到一起的时候,写法还是比较复杂的

let name = '狗蛋',age = 12,gender = '男'
let str = "大家好,我叫" + name + ",今年" + age + "岁了,我是一个" + gender + "孩子"

所以在es6中为了解决这个问题,提供了一种模板字符串

固定用法:

`固定字符${变量或者表达式}`
  • 在模板字符串中,可以解析 ${} 之间的变量或者表达式
  • 在整个字符串中允许换行

所以当我们在es6中拼接字符串

let name = '狗蛋',age = 12,gender = '男'
let str = `大家好,我叫${name},今年${age}岁了,我是一个${gender}孩子`

五.变量的解构赋值

ES6 允许按照一定模式,从数组或对象中提取值,对变量进行赋值,这被称为解构。

目的:简化代码。

ES6 允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构赋值

5.1 对象的解构赋值

 /* 
     对象的解构赋值
    */
    var obj = {
      name: "jason",
      age: 18,
      doWhat: function () {
        console.log("program");
      }
    }
    //1.在ES5里
    /* let name = obj.name;
    let age = obj.age;
    let doWhat = obj.doWhat; */

    //2.全部解构:等号左边的变量要和属性名相同
    /* let {
      name,
      age,
      doWhat
    } = obj;

    console.log(name);
    console.log(age);
    console.log(doWhat); */

    //3.部分解构

    /*  let {
       name
     } = obj;
     console.log(name); */

    //4.解构之后重新命名
    let {
      name: itsName
    } = obj
    console.log(itsName);

    //5.将现有的对象的方法赋值到某个变量
    let {
      random
    } = Math;
    let num = random();
    console.log(num);

5.2 数组解构

/* 
    数组解构
    */
    var arr = [1, 2, 3, 4, 5];

    //1.完全解构
    /*  let [
       a,
       b,
       c,
       d,
       e,
       f
     ] = arr;
     console.log(a); //1
     console.log(b); //2
     console.log(c); //3
     console.log(d); //4
     console.log(e); //5
     console.log(f); //undefined */

    //2.部分解构
    let [a, , , , e] = arr;
    console.log(a); //1
    console.log(e); //5

    // 3.复合解构
    let arr2 = [1, 2, [10, 20, 30]];
    let [j, k, [x, y, z]] = arr2;
    console.log(j); //1
    console.log(k); //2
    console.log(x); //10
    console.log(y); //20
    console.log(z); //30

5.3 字符串的解构

  • 应用:==不需要变量直接利用ES6语法交换变量==
 /* 
    字符串的解构
    */
    let str = "liu";
    let [x, y, z] = str;
    console.log(x, y, z); //l i u

    str[1] = 's'; //无法修改
    console.log(str); //liu
    console.log(str[1]); //l

    //应用:交换字符串的位置
    let str1 = "hello";
    let str2 = "world";

    [str2, str1] = [str1, str2]; //不需要变量直接利用ES6语法交换变量
    console.log(str1, str2); //world hello

注意:频繁使用对象方法、数组元素,就可以使用解构赋值形式

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>04.ES6里面的结构赋值</title>
</head>

<body>
  <script>
    /* 
ES6里面允许按照一定的模式从数组和对象中提取值,对变量进行赋值,这被称为结构赋值
*/
    //1.数组的解构
    let F4 = ["小沈阳", "刘能", "赵四", "宋小宝"];
    let [xiao, liu, zhao, song] = F4;
    console.log(xiao);
    console.log(liu);
    console.log(zhao);
    console.log(song);

    //2.对象的解构
    let obj = {
      name: "赵本山",
      age: 70,
      xiaopin: function () {
        console.log("昨天,今天,明天");
      }
    }
    let {
      name,
      age,
      xiaopin
    } = obj;
    console.log(name);
    console.log(age);
    console.log(xiaopin);
    //调用
    xiaopin();
  </script>
</body>

</html>

六.简化对象写法

ES6 允许在大括号里面,直接写入变量和函数,作为对象的属性和方法。这样的书写更加简洁。

当我们在以前定义对象字面量的时候,如果有如下代码

let name = '狗蛋',age = 12,gender = '男'
let obj = {
  name : name,
  age : age,
  gender : gender
}

此时我们发现obj的属性名和变量是同样的,可以在es6中简化为:

let obj = {name,age,gender}

也就是说,如果一个对象的属性名和外面的一个变量名同名,可以直接交变量名作为属性名,并会自动地把变量的值作为属性的值

方法上面也可以进行简化:

/* 
    1.对象的简化写法:
       如果一个对象的属性名和外面的一个变量名同名,可以直接交变量名作为属性名,并会自动地把变量的值作为属性的值
    2.方法的简化:
    3.this的指向:
      3.1 直接调用this:指向window
      3.2 函数内部调用,this指向window
      3.3 对象调用:this指向当前调用的对象
          
    */
    let name = "张涛";
    let age = 18;
    let work = "华为";

    /* let obj = {
      name,
      age,
      work
    };
    console.log(name, age, work); */

    let func = function () {
      console.log(this);
    }
    //func(); //1.直接调用this:指向window  2.函数内部调用,this指向window


    let obj = {
      name,
      age,
      work,
      func
    };
    obj.func(); //3.对象调用:this指向当前调用的对象

image-20201113134721027

注意:对象简写形式简化了代码,所以以后用简写就对了

七.函数参数默认值和参数解构

7.1 函数形参默认值

es5里面如果我们想要实现参数可以省略,我们有一个办法是这样做

function add(a,b,c,d){
  a = a || 0;
  b = b || 0;
  c = c || 0;
  d = d || 0;
  return a + b + c + d;
}

此时我们可以把给了默认值的参数省略

add(10,20) // 30
add(10,20,30) // 60

但是这样的做法比较麻烦,es6中提供了一种更加方便的方式,专门实现参数默认值。参数有了默认值之后就可以在调用的时候省略。

funciton 函数名(参数=默认值){ // 注意当 参数 为 undefined 时 参数 赋值为 默认值
  
}

上面的例子就可以必成:

function add(a=0,b=0,c=0,d=0){
  return a + b + c + d;
}

7.2 函数参数的解构赋值

函数参数解构

 // 参数是一组有次序的值
function f([x, y, z]) { 
    console.log(x, y, z);
}
f([1, 2, 3]);
// 参数是一组无次序的值
function fn({x, y, z}) { // {x, y, z} = obj 解构 
    console.log(x, y, z);
}
fn({z: 4, x: 5, y: 6});

7.3 解构赋值指定参数的默认值

function func2({name, age} = {}){   //防止不传实参时候的报错
    console.log(name, age);
}
func2();   //undefined undefined
// func2(); //相当于传了一个null   {name, age}=null 就会报错
// func2({});  //不会报错,输出:undefined undefined
function func2({name="luowowo", age=11} = {}){    //指定默认值
    console.log(name, age);
}
func2();  //luowowo 11

八.模板字符串

模板字符串(template string)是增强版的字符串,用反引号(`)标识,特点:

  1. 字符串中可以出现换行符
  2. 可以使用 ${xxx} 形式输出变量
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>05.ES6中的模板字符串</title>
</head>

<body>
  <h1>静夜思</h1>
  <p>
    <h3>李白</h3>
  </p>
  <h2>床前明月光</h2>
  <h2>疑是地上霜</h2>
  <h2>举头望明月</h2>
  <h2>低头思故乡</h2>
  <script>
    /* 
    ES6中的模板字符串:双引号,单引号,新增反引号
    */
    //1.可以定义字符串
    let str = `我是一个字符串`;
    console.log(str);
    //2.反引号可以换行
    let JYS = `
    <h1>静夜思</h1>
  <p>
    <h3>李白</h3>
  </p>
  <h2>床前明月光</h2>
  <h2>疑是地上霜</h2>
  <h2>举头望明月</h2>
  <h2>低头思故乡</h2>
   `
    console.log(JYS);
    //3.变量拼接
    let name = "grace";
    let loveWho = `${name}是我最喜欢的女人`;
    console.log(loveWho);
  </script>
</body>

</html>

注意:当遇到字符串与变量拼接的情况使用模板字符串

九.==箭头函数==

ES6 允许使用 “箭头”(=>)简化函数的定义。

箭头函数本质也是函数,它出现的目的是为了让我们在使用回调函数的时候更简单

9.1 箭头函数的语法

固定语法:

(参数) => { 函数体 }

ES6 允许使用「箭头」(=>)定义函数。

let fn = (arg1, arg2, arg3) => {
     return arg1 + arg2 + arg3;
}

9.1.1 箭头函数的注意点:

  1. 如果形参只有一个,则小括号可以省略
  2. 函数体如果只有一条语句,则花括号可以省略,函数的返回值为该条语句的执行结果
  3. 如果函数体里面只有一个语句,可以省略大括号不写, 并且他会默认返回 => 符号后面的数据。
  4. 如果函数体有多个语句,则不能省略大括号。
  5. 箭头函数 this 指向声明时所在作用域下 this 的值
  6. 箭头函数不能作为构造函数实例化
  7. 不能使用 arguments,可以用rest参数替代
  8. 如果函数体只有一个语句,且返回一个对象,建议是,不要写简写的方式。

注意:箭头函数不会更改 this 指向,用来指定回调函数会非常合适

代码示例:

// 无参数无返回
let func11 = () => console.log('func11');
func11();

// 无参数有返回

let func22 = () => 'func22';
console.log(func22());

// 有参数无返回
let func33 = x => console.log('func33', x);
func33(2);

// 有参数有返回
let func44 = (x, y) => {
    let sum = x + y; 
    return sum + 'func44';
};
console.log(func44(1, 2));

注意:

// 如果return的是单一个对象,则需要加上大括号和return,例如:

// let func55 = (x, y) => {a:,x b:y};    //报错
let func66 = (x, y) => {
    return { a: x, b: y };
};
console.log(func66(5, 8));
// 箭头函数不可以使用 arguments 获取参数列表,可以使用 rest 参数代替。
let func=(a,b)=>{
    console.log(a,b);
    console.log(arguments);
}
func(1,2)// 这种方式获取不到实参列表
let fun=(...value)=>console.log(value);
fun(1,2)//[ 1, 2 ]

9.1.2 使用场景(回调函数):

// 定时器中的回调函数
setInterval(() => {
    console.log("我用了箭头函数");
}, 1000);

// forEach中的回调函数
var arr = [22, 32, 11, 3, 5, 7, 88];
arr.forEach(item => console.log(item));
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>07.箭头函数的使用</title>
</head>

<body>
  <script>
    /* 
箭头函数的使用:ES6允许箭头函数的使用
*/
    /* let add = (a, b) => {
      return a + b;
    }
    let result = add(1, 2);
    console.log(result); */

    //1.this是静态的,this始终指向函数声明时所在作用域的this的值
    function getName() {
      console.log(this.name);
    }
    let getName2 = () => {
      console.log(this.name);
    }
    //设置window的name属性
    window.name = "尚硅谷";
    const school = {
      name: "atguigu",
    }
    //1.1直接调用
    // getName();//尚硅谷
    // getName2();//尚硅谷

    //12 call方法调用
    getName.call(school); //atguigu
    getName2.call(school); //尚硅谷

    //2.不能作为构造函数实例化对象
    let person = (name, age) => {
      this.name = name;
      this.age = age;
    }
    //let me = new person("xiao", 30);
    //console.log(me); //07.ES6中箭头函数.html:46 Uncaught TypeError: person is not a constructor

    //3.不能使用arguments变量
    let fn = () => {
      //console.log(arguments); //07.ES6中箭头函数.html:51 Uncaught ReferenceError: arguments is not defined
    }
    fn(1, 2, 3);

    //4.箭头函数的简写
    //4.1 省略小括号,当形参有且只有一个时
    let add = a => {
      return a + a;
    }
    let sum = add(2);
    console.log(sum);

    //4.2 省略大括号(并且可以省略return关键字),当代码体只有一条语句的时候
    let pow = a => a * a;
    let mi = pow(3);
    console.log(mi);
  </script>
</body>

</html>

9.2 箭头函数中的this

9.2.1 this指向总结:

  1. 全局使用(函数全局调用)指向window
  2. 对象调用指向该对象(事件中的事件源)
  3. 箭头函数没有自己的作用域,即箭头函数 this 指向其外层作用域
var name = "windowName";
var obj = {
    name: "奶茶",
    fn: function () {
        console.log(this.name);
    },
};
obj.fn();

var obj2 = {
    name: "绿茶",
    fn2: () => {
        console.log(this.name);
    },
};
obj2.fn2();

//所以创建字面量对象,不适合书写箭头函数

9.2.2 在dom操作中使用箭头函数

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
        .box{
            width: 200px;
            height: 200px;
            background-color: pink;
        }
    </style>
</head>
<body>
    <div id="odiv" class="box"></div>
    <script>
      var obj = document.getElementById("odiv");
      obj.onclick = function () {
        // setTimeout(function () {
        //   console.log(this);
        //   this.style.width = "300px"; //修改不了
        // }, 1000);

        setTimeout(() => {
          console.log(this);
          this.style.width = "300px"; //修改成功
        }, 1000);
      };
    </script>
</body>
</html>

6.1 箭头函数的实践

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>08.箭头函数的实践</title>
</head>
<style>
  .div {
    width: 200px;
    height: 200px;
    border: 1px solid #ccc;
    background: #ccc;
  }
</style>

<body>
  <div class="div"></div>
  <script>
    /* 
    需求一:点击两秒后盒子变成紫色
    需求二:从数组中返回偶数的元素
    */
    //需求一:点击两秒后盒子变成紫色
    //1.1 普通写法
    let div = document.querySelector(".div");
    /* div.onclick = function () {
      setTimeout(function () {
        div.style.backgroundColor = "purple";
      }, 100)
    } */
    //1.2 this写法
    /* div.onclick = function () {
      setTimeout(function () {
        console.log(this); //这里的this指向window
        this.style.backgroundColor = "purple";
      }, 100)
    } */

    //1.2 解决办法
    div.onclick = function () {
      let _this = this;
      console.log(_this);
      setTimeout(function () {
        console.log(_this); //这里的this指向盒子本身
        _this.style.backgroundColor = "purple";
      }, 100)
    }


    //1.3 ES6箭头函数写法
    /* div.addEventListener("click", function () {
      setTimeout(() => {
        //这里的this是静态的,会找到外层的函数div
        this.style.backgroundColor = "purple";
      }, 200);
    }); */


    //需求二:从数组中返回偶数的元素
    /* 
    let arr = [1, 2, 5, 6, 8, 9, 10, 7, 2, 20, 30, 15];
    let newArr = [];
    arr.forEach(function (item, index) {
      if (item % 2 == 0) {
        newArr.push(item);
      }
    }) 
    console.log(newArr);
    */

    /* const arr = [1, 2, 5, 6, 8, 9, 10];
    const result = arr.filter(function (item, index) {
      if (item % 2 === 0) {
        return true;
      } else {
        return false;
      }
    })
    console.log(result); */

    //简便一
    /* const result = arr.filter(item => {
      if (item % 2 === 0) {
        return true;
      } else {
        return false;
      }
    })
 */
    //简便二
    /* const result = arr.filter(item => item % 2 === 0)
    console.log(result); */


    /* 
    总结:箭头函数适合的场景
    1.箭头函数适合与this无关的回调:定时器,数组的方法回调
    2.箭头函数不适合于this有关的回调:事件回调,对象的方法
    */
  </script>

</body>

</html>

6.2 箭头函数的默认值

   /* 
函数参数的默认值:
1.形参初始值具有默认值的参数,一般位置要靠后(潜规则)
*/
    //1.形参初始值具有默认值的参数,一般位置要靠后(潜规则)
    // function add(a, b, c = 10) {
    //   return a + b + c;
    // }
    // let result = add(1, 2);
    // console.log(result);

6.3 箭头函数与结构赋值的使用

//2.与结构赋值联合使用
    function connect({
      host,
      username,
      password,
      port
    }) {
      console.log(host);
      console.log(username);
      console.log(password);
      console.log(port);
    }
    connect({
      host: "localhost",
      username: "root",
      password: "root",
      port: 3306,
    });

十.rest 参数和拓展运算符

10.1 rest参数

arguments 对象:

function fn(){
    console.log(arguments);// 伪数组
}

fn(10, 20, 30, 50, 60);

ES6提供了新的方法:使用rest 参数搭配的变量是一个数组,该变量将多余的参数放入数组中。

注意,rest 参数之后不能再有其他参数(即只能是最后一个参数),否则会报错。

function func( a, b ,...rest){  // 把剩余的参数都交给rest
    console.log(rest);
}

func(10, 20, 30, 50, 60);

function func2(...rest){   //rest 接收所有参数作为一个数组
    rest.forEach(function (item) {
        console.log(item);
    });
}
func2(60, 70, 80, 90);

// 报错
function f(a, ...b, c) {
  // ...
}

ES6 引入 rest 参数,用于获取函数的实参,用来代替 arguments

image-20201113151928959

注意:rest 参数非常适合不定个数参数函数的场景

 //1.ES5的方式:arguments
    function getArr() {
      console.log(arguments); //类数组
    }
    getArr(1, 2, 3, 4, 5)
    //2.ES6:rest方法:
    //2.1 rest必须写在最后面, 且rest是一个真正的数组

    function add(x, y, ...rest) {
      //console.log(x, y, ...rest);
      console.log(x, y); //1 2
      rest.forEach((item) => {
        console.log(item); //3 4 5
      })
    }
    add(1, 2, 3, 4, 5)
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>10.rest参数</title>
</head>

<body>
  <script>
    /* 
ES6中引入rest参数,用来代替arguments参数
*/
    //1.ES5获取实参的方式
    let func = function () {
      console.log(arguments); //是一个对象
    }
    func("小沈阳", "宋小宝", "刘能", "尼古拉斯舞王");


    //2.ES6中rest方式
    //2.1 rest参数必须放到参数最后
    function date(a, b, ...args) {
      console.log(a);
      console.log(b);
      console.log(args); //是一个数组
    }
    date("小沈阳", "宋小宝", "刘能", "尼古拉斯舞王");
  </script>
</body>

</html>

10.2、拓展运算符

ES6中的扩展运算符是一个非常有用的运算符号,可以用来快速展开数组,对象...

它的作用就是可以将数组或者对象展开,拆开成为一个一个单独的数据。

// 快速将一个数组拆开成一个一个的元素
let arr = [1, 2, 3, 4]
console.log(...arr)
// 快速将一个对象里面的数据复制一份到一个新的对象里面
let obj = { name: '狗蛋', age: 12, gender: '男' }
console.log({id:1,birthday:'2020-02-02', ...obj})
// 将一个字符串拆开成为多个单独的字符
let str = 'abc'
console.log(...str)

使用场景:

// 1、数组中的值作为函数参数使用
let arr1 = [10, 20, 30];

function func(a, b, c){
    console.log(a,b,c)
}

func(...arr1);  //等效于:func(10,20,30);     输出结果10 20 30

// 2、合并数组
let arr2 = [40, 50, 60];
let newArr = [...arr1,...arr2];  // 等效于 [ 10, 20, 30, 40, 50, 60 ]
console.log(newArr);    //[ 10, 20, 30, 40, 50, 60 ]

// 3、合并对象
let obj1 = {
    name:"luowowo",
    age:"11",
};
let obj2 = {
    email:"luowowo@163.com",
};
let newObj = {...obj1,...obj2}; // 等效于{ name: 'luowowo', age: '11', email: 'luowowo@163.com' }
console.log(newObj);    //{ name: 'luowowo', age: '11', email: 'luowowo@163.com' }

// 4、es6中另一个合并对象的方法
let newObj2 = Object.assign({},obj1,obj2);  // 把第二个及第二个以上的参数都合并到第1个上面去。
console.log(newObj2);   //{ name: 'luowowo', age: '11', email: 'luowowo@163.com' }

10.2.1 扩展运算符的应用

  • 数组的合并

    • //1.数组的合并
          let xyj = ["孙悟空", "猪八戒", "沙和尚", "唐僧", "白龙马"];
          let hl = ["贾宝玉", "林黛玉", "薛宝钗"];
          let con=[...xyj,...hl];
          console.log(con); 
      
  • 数组的克隆

    • //2.数组的克隆
          let hl = ["贾宝玉", "林黛玉", "薛宝钗"];
          let newHl = [...hl];
          console.log(newHl);
      
  • 伪数组转换为真数组

    • //3.将伪数组转换为真数组
          let divs = document.querySelectorAll(".div");
          let arrDiv = [...divs];
          console.log(arrDiv);
      

10.3、...在解构赋值中的使用

  • 针对数组解构
let [a, b, c, ...arr] = [1, 2, 3, 4, 5, 6, 7];
console.log(a, b, c, arr); // a = 1, b = 2, c = 3, arr = [4,5,6,7]

按照顺序把数组里面的元素解构到等号左边的变量里面,当左边的变量不够,会把剩下的数据放到arr这个数据里面,此时arr是就一个新的数组。

  • 解构字符串
let str = "abcde";
let [a, b, c, ...strArr] = str;
console.log(a, b, c, strArr); // a = 'a',b='b',c='c',strArr=['d','e']

十一.练习

11.1 练习一:指定日期返回今年的第几天?

  • 解法一:常规方法累加
 /* 
    指定日期返回今年的第几天?
    */
    function getSomeDay(date) {
      let year = date.getFullYear()
      let month = date.getMonth() + 1;
      let someDate = date.getDate();
      let monthDate = 0; //记录每月有多少天
      let sumDate = 0; //记录是今年的第多少天
      for (let i = 1; i < month; i++) {
        if (i == 1 || i == 3 || i == 5 || i == 7 || i == 8 || i == 10 || i == 12) {
          monthDate = 31;
        } else if (i == 4 || i == 6 || i == 9 || i == 11) {
          monthDate = 30;
        } else {
          //二月
          year % 4 === 0 ? monthDate = 29 : monthDate = 28
        }
        sumDate += monthDate;
      }
      sumDate += someDate;
      return sumDate;
    }
    let date = new Date("2020-06-02");
    console.log(date.toString() + "是今年的第", getSomeDay(date) + "天"); //31+29+31+30+31+2=154
  • 解法二:时间戳相减
//解法二:时间戳相减
    function getCurrentDay(date) {
      let year = date.getFullYear();
      let newDate = new Date(year.toString()).getTime() //获得新年第一天八点的日期
      let unixDate = date.getTime();
      let unixCha = unixDate - newDate; //时间差

      let day = Math.ceil(unixCha / 1000 / 60 / 60 / 24) + 1;
      return day
    }
    let date = new Date("2020-06-02");
    console.log(date.toString() + "是今年的第" + getCurrentDay(date) + "天");//154
  • 解法三:
 function getSomeDay(date) {
      let year = date.getFullYear()
      let month = date.getMonth() + 1;
      let someDate = date.getDate();
      let monthDate = 0; //记录每月有多少天
      let sumDate = 0; //记录是今年的第多少天
      for (let i = 1; i < month; i++) {
        switch (i) {
          case 1:
          case 3:
          case 5:
          case 7:
          case 8:
          case 10:
          case 12:
            monthDate = 31;
            break;
          case 2:
            year % 4 === 0 ? monthDate = 29 : monthDate = 28;
            break;
          case 4:
          case 6:
          case 9:
          case 11:
            monthDate = 30;
            break;
        }
        sumDate += monthDate;
      }
      sumDate += someDate;
      return sumDate;
    }
    let date = new Date("2020-06-02");
    console.log(date.toString() + "是今年的第", getSomeDay(date) + "天"); //31+29+31+30+31+2=154 

11.2 练习二:使用Math对象,制作一个十六进制随机颜色

 /* 
    练习二:十六进制随机颜色
    题目描述:使用Math对象,制作一个十六进制随机颜色
    十六进制:123456789abcdef
    */
    //1.写出随机数的函数
    function getRandom(min, max) {
      return Math.floor(Math.random() * (max - min + 1)) + min;
    }

    //2.封装十六进制方法,并设置body背景颜色
    function getRandomColor() {
      let arr = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 'a', 'b', 'c', 'd', 'e', 'f'];
      let str = "";
      for (let i = 0; i < 6; i++) {
        let randomNum = getRandom(0, 15);
        str += arr[randomNum];
      }
      str = "#" + str.toString();
      let body = document.querySelector("body");
      body.style.backgroundColor = str;
      console.log(str);
    }

    //3.定时器驱动方法
    setInterval(() => getRandomColor(), 1000);

11.3 练习三:练习三:判断字符串"dsejscnesjcnssdoisc"中哪个字符出现次数最多并计算次数

let str = "dsejscnesjcnssdoisc";

    let obj = {};
    for (let i = 0; i < str.length; i++) {
      let char = str.charAt(i);
      if (obj[char]) {
        obj[char]++; //把字符都放进对象里
      } else {
        obj[char] = 1;
      }
    }
    console.log(obj);
    let c = '';
    let count = 0; //记录次数
    for (item in obj) {
      if (obj[item] > count) {
        count = obj[item];
        c = item;
      }
    }
    console.log("dsejscnesjcnssdoisc" + "出现字符次数最多的是" + c + "出现次数为" + count);
let str = "dsejscnesjcnssdoisc";
    let arr = [...str];
    let obj = {};
    arr.forEach((item) => {
      if (obj[item]) {
        obj[item]++;
      } else {
        obj[item] = 1
      }
    });
    console.log(obj);
    let count = 0;
    let c = '';

    for (k in obj) {
      if (obj[k] > count) {
        count = obj[k];
        c = k
      }
    }
    console.log("dsejscnesjcnssdoisc" + "出现字符次数最多的是" + c + "出现次数为" + count);