【JavaScript高级】ES6中需要深入理解掌握的18个关键点总结笔记

1. let关键字
<body>

<button>测试1</button>
<br>
<button>测试2</button>
<br>
<button>测试3</button>
<br>
<!--
***let
1. 作用:
  * 与var类似, 用于声明一个变量
2. 特点:
  * 在块作用域内有效
  * 不能重复声明
  * 不会预处理, 不存在提升
3. 应用:
  * 循环遍历加监听
  * 使用let取代var是趋势
-->
<script type="text/javascript">
    //console.log(age);// age is not defined
    let age = 12;
    //let age = 13;不能重复声明
    console.log(age);
    let btns = document.getElementsByTagName('button');
    for(let i = 0;i<btns.length;i++){
            btns[i].onclick = function () {
                alert(i);
            }
    }

</script>
</body>

2. const关键字

<body>
<!--
1. 作用:
  * 定义一个常量
2. 特点:
  * 不能修改
  * 其它特点同let
3. 应用:
  * 保存不用改变的数据
-->
<script type="text/javascript">
  // 以前定义常量的方式
  var DATA_KEY = '12345678';

  // 使用const定义常量
  const PI = '3.141592654';
  PI = '11';
  console.log(PI);    // Uncaught TypeError: Assignment to constant variable.

</script>
</body>

3. 变量的解构赋值

<body>
  <!--
1. 理解:
  * 从对象或数组中提取数据, 并赋值给变量(多个)
2. 对象的解构赋值
  let {n, a} = {n:'tom', a:12}
3. 数组的解构赋值
  let [a,b] = [1, 'atguigu'];
4. 用途
  * 给多个形参赋值
-->
  <script type="text/javascript">
    let obj = { uname: 'zhangsan', age: 25 };
    // let username = obj.uname;

    // 使用ES6的解构赋值
    let { username, age } = obj;
    console.log(username, age);     // undefined 25

    // 等号的左边相当于是定义的全局变量,解构的名字必须是完全相同的(必须是对象本身已经存在的属性)
    let { uname } = obj;
    console.log(uname, age);        // zhangsan 25


    let arr = [1, 3, 5, 'aaa', true];
    // 按照变量的顺序去取出数据
    let [a, b, c, d, e, f] = arr;
    console.log(a, b, c, d, e, f);     // 1 3 5 "aaa" true undefined
    // 使用,来占位置
    let [, , g] = arr;


    // 使用对象的解构赋值来获取参数信息
    function foo({uname, age}){
      console.log(uname, age);      // zhangsan 25
    }
    foo(obj);
  </script>
</body>

4.  模板字符串

<body>
  <!--
1. 模板字符串 : 简化字符串的拼接
  * 模板字符串必须用 `` 包含
  * 变化的部分使用${xxx}定义
-->
  <script type="text/javascript">
    let obj = { username: 'zhangsan', age: 25 };
    let str = `${obj.username}是一个傻瓜,年龄${obj.age}`;
    console.log(str);
  </script>
</body>

5. 简化的对象写法

<body>
<!--
简化的对象写法
* 省略同名的属性值
* 省略方法的function
* 例如:
  let x = 1;
  let y = 2;
  let point = {
    x,
    y,
    setX (x) {this.x = x}
  };
-->
<script type="text/javascript">
  let username = 'zhangsan';
  let age = 18;
  //  同名的属性可以省略不写
  let obj = {
    username,
    age,
    getName : function(){
      return this.name;
    },
    // 可以省略函数的function
    getAge(){
      return this.age;
    }
  }
</script>
</body>

6. 深入理解箭头函数


<body>
    <button id="btn1">测试箭头函数this_1</button>
    <button id="btn2">测试箭头函数this_2</button>
    <button id="btn3">测试箭头函数this_3</button>



    <!--
* 作用: 定义匿名函数
* 基本语法:
  * 没有参数: () => console.log('xxxx')
  * 一个参数: i => i+2
  * 大于一个参数: (i,j) => i+j
  * 函数体不用大括号: 默认返回结果
  * 函数体如果有多个语句, 需要用{}包围,若有需要返回的内容,需要手动返回
* 使用场景: 多用来定义回调函数

* 箭头函数的特点:
    1、简洁
    2、箭头函数没有自己的this,箭头函数的this不是调用的时候决定的,而是在定义的时候处在的对象就是它的this
    3、扩展理解: 箭头函数的this看外层的是否有函数,
        如果有,外层函数的this就是内部箭头函数的this,
        如果没有,则this是window。
    4、箭头函数应用场景多用在回调函数中
-->
    <script type="text/javascript">
        // 1. 没有形参的时候
        let fun = () => console.log('aaa');

        // 2. 只有一个形参的时候, ()是可以省略的
        let fun2 = a => console.log(a);

        // 3. 两个及两个以上的形参的时候,()是不能省略的
        let fun3 = (x, y) => console.log(x, y);


        // 4. 函数体的情况
        // 4.1 函数体只有一条语句或者是表达式的时候, {}可以省略,省略之后会自动返回语句执行的结果或者是表达式的结果
        let func4 = (x, y) => x + y;

        // 4.2 有{} 的情况,需要自动手动去返回
        let fun5 = (x, y) => {
            return x + y;
        }

        // 4.3 函数体有多条语句的时候, {}不可以省略
        // 每一个函数都是有返回值的,如果没有手动指定返回值的话,就会默认返回undefined
        let fun6 = (x, y) => {
            console.log(x, y);
        }


        // 5. 箭头函数的this是他处于对象的内部
        let btn1 = document.getElementById('btn1');
        let btn2 = document.getElementById('btn2');
        btn1.onclick = function () {
            console.log(this);
        };




        // 6. 箭头函数this的深入理解.
        let obj = {
            name: '箭头函数',
            // 普通函数的this指向是外部水调用的我,那么我的this指向的就是哪个对象
            getName: function () {
                // 终极理解方案: 
                // 1. 去看一下这个箭头函数的外部有没有函数了 如果这个函数的外部还是有函数的话,那么this的指向就是和外部的这个函数的环境是一致的,
                // 否则this的指向就是指向的window对象
                // 2. 发现外部有一个getName函数,这个函数是一个普通函数,那么这个函数的this就是和箭头函数的this一致的,我们发现这个函数放入this指向的是哪个调用者,
                // 也就是只有obj对象调用我了之后,这个函数才可以向后执行,因此this就是执行obj对象,over。
                btn2.onclick = () => {
                    console.log(this);          // obj对象
                }
            }
        }
        obj.getName();





        // 7. 这是一个按钮
        let btn3 = document.getElementById('btn3');
        let obj3 = {
            name: '箭头函数',
            getName: () => {
                console.log('我是外部的this对象:', this);  // window
                btn3.onclick = () => {
                    // 分析思路:
                    // 1.先去看下这个函数是谁?这个函数是一个箭头函数,name去看下这个函数的外部有没有函数,如果有函数的话,那么里面的this就是和外边的this是一样的
                    // 如果外部没有函数的话,那么this就是window 
                    // 2. 发现外部有一个函数getName,但是仔细一看这个函数竟然还是一个箭头函数,那么这个this的指向就是还是看他外部有没有函数,一看之后,外部没有函数了,那么这个
                    // 函数的内部的this指向就是widnow对象
                    console.log('我是最里面的this对象:', this);          // this 是谁????, window对象
                }
            }
        }
        obj3.getName();




        // 8. 继续剖析
        let obj4 = {
            name: '箭头函数',
            getName: () => {
                btn2.onmouseenter = () => {
                    console.log(this);
                }
            }
        }
        obj4.getName();

        // 上面的代码等价于
        obj4.getName = () => {
            // ... window
        }





    </script>

</body>

7. 3点运算符

<body>
    <!--
* 用途
1. rest(可变)参数
    * 用来取代arguments 但比 arguments 灵活,只能是最后部分形参参数
    function fun(...values) {
        console.log(arguments);
        arguments.forEach(function (item, index) {
            console.log(item, index);
        });
        console.log(values);
        values.forEach(function (item, index) {
            console.log(item, index);
        })
    }
    fun(1,2,3);
2. 扩展运算符
  let arr1 = [1,3,5];
  let arr2 = [2,...arr1,6];
  arr2.push(...arr1);
-->
    <script type="text/javascript">

        // 使用arguments来获取参数信息
        function foo() {
            console.log(typeof arguments, arguments);           // Arguments


            // argument.callee() 本身也就是指向了函数本身, 应用于递归的场景,相当于是foo(), 再次调用了这个函数
            // arguments.callee();
        }




        // 使用...作为参数, 如果前面有参数作为占位符的话,那么后面的部分就是我要使用的部分
        // Rest parameter : 可变参数
        function foo2(a, ...value) {
            console.log(typeof value, value);     // Array
            // 由于此时value是一个数组类型,而不是一个伪数组,因此在这里就可以直接使用数组的方法了
            value.forEach((item, index) => {
                console.log(item, index);
            });
        }

        foo();
        foo2(10, 12);



        // 应用场景,展开运算符
        let arr = [1, 6];
        let arr1 = [2, 3, 4, 5];
        arr = [1, ...arr, 6]
        console.log(arr);

        // 数组元素的合并(灰常方便!!!)
        let arr3 = [...arr, ...arr1];
        console.log(arr3);

    </script>

</body>

8. 形参默认值

<body>
  <!--
    * 形参的默认值----当不传入参数的时候默认使用形参里的默认值
    function Point(x = 1,y = 2) {
    this.x = x;
    this.y = y;
    }
-->
  <script type="text/javascript">

    // 可以使用形参的默认值,来实现构造函数参数的初始化操作
    function Point(x = 0, y = 0) {
      this.x = x;
      this.y = y;
    }

    let point = new Point(23, 35);
    console.log(point);   // 23 35

    let point1 = new Point();
    console.log(point1);    // undefined undefined


  </script>

</body>

9.  Promise对象

<body>
  <!--
1. 理解:
  * Promise对象: 代表了未来某个将要发生的事件(通常是一个异步操作)
  * 有了promise对象, 可以将异步操作以同步的流程表达出来, 避免了层层嵌套的回调函数(俗称'回调地狱')
  * ES6的Promise是一个构造函数, 用来生成promise实例
2. 使用promise基本步骤(2步):
  * 创建promise对象
    let promise = new Promise((resolve, reject) => {
        //初始化promise状态为 pending
      //执行异步操作
      if(异步操作成功) {
        resolve(value);//修改promise的状态为fullfilled
      } else {
        reject(errMsg);//修改promise的状态为rejected
      }
    })
  * 调用promise的then()
    promise.then(function(
      result => console.log(result),
      errorMsg => alert(errorMsg)
    ))
3. promise对象的3个状态
  * pending: 初始化状态
  * fullfilled: 成功状态
  * rejected: 失败状态
4. 应用:
  * 使用promise实现超时处理

  * 使用promise封装处理ajax请求
    let request = new XMLHttpRequest();
    request.onreadystatechange = function () {
    }
    request.responseType = 'json';
    request.open("GET", url);
    request.send();
-->

  <script type="text/javascript">

    //  创建一个promise对象
    let promise = new Promise((resolve, reject) => {
      //   初始化promise状态,pending: 初始化
      // 这个函数是同步执行的, 因此函数的执行顺序是自上向下执行的
      console.log(222);


      // 异步操作,ajax请求
      setTimeout(() => {
        console.log(333);
        // 然后根据异步任务返回的结果去修改promise的状态
        // 异步任务执行成功
        reject('404 error');        // 修改promise的状态为成功状态:fullfilled
        resolve('success');
      }, 2000)
    });

    console.log(1111);



    promise.then((data) => {
      console.log('执行成功!', data);
    }, (error) => {
      console.log('执行失败!', error);
    });



    // 获取数据信息
    // Ajax 创建的三步骤:创建,open,send
    function getNews(url) {
      let promise = new Promise((resolve, reject) => {
        // 1. 创建请求对象
        let xmlHttp = new XMLHttpRequest();
        console.log('init state: ', xmlHttp.readyState);
        // 0 1 2 3 4 状态码,这个onreadystateChange这个函数会调用4次
        xmlHttp.onreadystatechange = () => {
          // 4 表示的是数据成功了, 200 表示成功或者失败的状态码
          if (xmlHttp.readyState === 4) {
            // 请求数据完成的时候
            if (xmlHttp.status === 200) {
              // success
              console.log(xmlHttp.responseText);
              resolve(xmlHttp.responseText);        // 数据获取成功, 返回获取从成功的数据信息
            } else {
              // error
              console.log(xmlHttp.responseText);
              resolve(xmlHttp.responseText);        // 数据获取成功, 返回获取从成功的数据信息
            }
          }

        }
        // 2. 创建open, 指定请求的方式为true或者false
        xmlHttp.open('GET', url);
        // 3.发送
        xmlHttp.send();

      });
      return promise;
    }



    // 获取新闻的内容信息, 只要前面的数据信息获取失败了的话,那么后面的函数就是不会执行的
    getNews('http://localhost:3000/news?id=2').then((data) => {
      console.log('get data success!', data);
      //  开始获取数据信息,发送第二次请求
      let url = 'http://localhost:3000' + JSON.parse(data).commentsUrl;

      // 为了实现代码的解耦,可以把当前的promise返回出去
      return getNews(url);
      console.log(url);
    }, (error) => {
      console.log(error);
    }).then((data) => {
      console.log('comments data load finished: ', data);
    }, (error) => {
      console.log('error', error);
    })




  </script>

</body>

10. Symbol的使用

<body>
<!--
  前言:ES5中对象的属性名都是字符串,容易造成重名,污染环境
  Symbol:
    概念:ES6中的添加了一种原始数据类型symbol(已有的原始数据类型:String, Number, boolean, null, undefined, 对象)
    特点:
      1、Symbol属性对应的值是唯一的,解决命名冲突问题
      2、Symbol值不能与其他数据进行计算,包括同字符串拼串
      3、for in, for of遍历时不会遍历symbol属性。
    使用:
      1、调用Symbol函数得到symbol值
        let symbol = Symbol();
        let obj = {};
        obj[symbol] = 'hello';
      2、传参标识
        let symbol = Symbol('one');
        let symbol2 = Symbol('two');
        console.log(symbol);// Symbol('one')
        console.log(symbol2);// Symbol('two')
      3、内置Symbol值
        * 除了定义自己使用的Symbol值以外,ES6还提供了11个内置的Symbol值,指向语言内部使用的方法。
        - Symbol.iterator
         * 对象的Symbol.iterator属性,指向该对象的默认遍历器方法(后边讲)

-->


<script type="text/javascript">
    // 创建一个symbol
    let symbol = Symbol();
    console.log(typeof symbol);
    let obj = {name: 'zhangsan', age: 30};

    // 给对象添加属性
    obj[symbol] = 'hahaha';
    console.log(obj);

    // symbol 的比较
    let symbol01 = Symbol('one');
    let symbol02 = Symbol('two');
    console.log(symbol01 === symbol02, symbol01, symbol02);

    // symbol定义常量
    const PERSON_KEY = Symbol('person_key');
    console.log(PERSON_KEY);

    // for in 遍历对象的属性,for of 都是无法遍历出来symbol的属性的
    for (let item in obj) {
        console.log(item, obj[item]);
    }

    let arr = [11, 2 , 3,3, 4, 4]
    // for of 可以用来遍历数据每一项
    for (let item of arr) {
        console.log(item);
    }

</script>

</body>

11.  Iterator遍历器

<body>
<!--
  概念: iterator是一种接口机制,为各种不同的数据结构提供统一的访问机制
  作用:
    1、为各种数据结构,提供一个统一的、简便的访问接口;
    2、使得数据结构的成员能够按某种次序排列
    3、ES6创造了一种新的遍历命令for...of循环,Iterator接口主要供for...of消费。
  工作原理:
    - 创建一个指针对象(遍历器对象),指向数据结构的起始位置。
    - 第一次调用next方法,指针自动指向数据结构的第一个成员
    - 接下来不断调用next方法,指针会一直往后移动,直到指向最后一个成员
    - 每调用next方法返回的是一个包含value和done的对象,{value: 当前成员的值,done: 布尔值}
      * value表示当前成员的值,done对应的布尔值表示当前的数据的结构是否遍历结束。
      * 当遍历结束的时候返回的value值是undefined,done值为false
  原生具备iterator接口的数据(可用for of遍历)
    1、Array
    2、arguments
    3、set容器
    4、map容器
    5、String
    。。。
-->

<script type="text/javascript">

    // 创建一个遍历器对象
    function myIterator(arr) {             // iterator 接口
        let nextIndex = 0;              // 记录当前指针的位置
        return {
            // 遍历器对象
            next() {
                // 1. 函数嵌套 2. 内部函数引用外部函数的变量 3. 内部方法外部调用
                return nextIndex < arr.length ? {value: arr[nextIndex++], done: false} : {value: undefined, done: true}
            }
        }
    }

    // 准备数据
    let arr = [1, 20, 3, 4];
    let iteratorObj = myIterator(arr);
    console.log(iteratorObj.next());
    console.log(iteratorObj.next());
    console.log(iteratorObj.next());
    console.log(iteratorObj.next());
    // 如果没有值的话,默认返回的是undefined
    console.log(iteratorObj.next());


    console.log('======================================================================');
    // 将iterator接口部署到指定的数据类型上,就可以直接使用for of去循环遍历
    // 可以遍历的对象: 数组,字符串,arguments,Set容器,map容器
    for (let item of arr) {
        console.log(item);
    }

    console.log('======================================================================');
    let str = 'aadsfsdfdfgfdg';
    for(let item of str) {
        console.log(item);
    }

    console.log('======================================================================');
    function func() {
        for (let item of arguments) {
            console.log(item);
        }
    }

    func(1, 2, 3, 'aa')



    // 普通对象是不能进行迭代遍历的,也就是不能使用 for of去遍历的
    console.log('======================================================================');
    let obj = {
        name : 'zhansdan',
        age : 18

    }
    for (let item of obj) {
        console.log(item);                  // Uncaught TypeError: obj is not iterable
    }

    console.log('symbol======================================================================');
    // 在指定的数据结构上面部署了一个iterator接口,当使用for of去遍历某一个数据结构的时候,首先去找Symbol.iterator
    // 如果找到了的话,就去遍历;没有找到的话就是不可以遍历的
    let targetData = {
        // Symbol 的迭代器变量
        [Symbol.iterator] : function(){
            let nextIndex = 0;
            return {
                next(){
                    // 遍历当前的对象
                    return nextIndex < this.length ? {value : this[nextIndex++], done : false} : {value : undefined, done : true}
                }
            }
        }
    }

    // 使用三点运算符,解构赋值,默认去调用iterator接口
    let arr = [1,2];
    let arr2  = [2, 4];
    console.log(...arr, ...arr2);

</script>
</body>

12.  Generator函数

<body>

<!--
 Generator函数
  概念:
    1、ES6提供的解决异步编程的方案之一
    2、Generator函数是一个状态机,内部封装了不同状态的数据,
    3、用来生成遍历器对象
    4、可暂停函数(惰性求值), yield可暂停,next方法可启动。每次返回的是yield后的表达式结果
  特点:
    1、function 与函数名之间有一个星号
    2、内部用yield表达式来定义不同的状态
    例如:
      function* generatorExample(){
        let result = yield 'hello';  // 状态值为hello
        yield 'generator'; // 状态值为generator
      }
    3、generator函数返回的是指针对象(接11章节里iterator),而不会执行函数内部逻辑
    4、调用next方法函数内部逻辑开始执行,遇到yield表达式停止,返回{value: yield后的表达式结果/undefined, done: false/true}
    5、再次调用next方法会从上一次停止时的yield处开始,直到最后
    6、yield语句返回结果通常为undefined, 当调用next方法时传参内容会作为启动时yield语句的返回值。
-->
<script type="text/javascript" src="./js/jquery-1.10.1.min.js"></script>
<script type="text/javascript">

    // 创建一个generator函数
    // Symbol(Symbol.toStringTag) : "Generator  这个实现内部原理实际上是这个对象的原型上面的一个标识,使用了Generator来进行标识这个对象的
    // 由于加上yield可以暂停,因此就是一个惰性求值函数;next函数可以用来再次执行函数
    function* myGenerator() {
        console.log('start');
        // 遇见了yield先暂停,然后返回出去一个对象{hello, false}
        let result = yield 'hello';
        console.log('=======================result now is : ', result);            // undefined,  传递过来的参数信息可以作为传递过来的那个参数的返回值
        // 没有返回值的调用函数aaa {undefined, false}
        yield console.log('aaa')
        console.log('pause and start');
        // {value: "generator", done: false}
        yield 'generator';
        console.log('finished');

        // return 的默认值就是undefined
        return '返回值'
    };


    // 直接调用是不能调用的,没有效果的
    let MG = myGenerator();       //  这是一个指针对象
    console.log(MG);
    console.log(MG.next('aaaaaaaa-----bbbbbbbb'));       // {value: "hello", done: false}
    console.log(MG.next('bbbbbbbbbb'));       // {value: "hello", done: false}
    console.log(MG.next());       // {value: "hello", done: false}
    console.log(MG.next());       // {value: undefined, done: true}
    console.log(MG.next());       // {value: undefined, done: true}


    // 对象的symbol.iterator属性  指向遍历器对象
    let obj = {uasename: 'zhangsan', age: 39};
    // 使用iterator中的迭代器对象,人为部署一个generator接口
    obj[Symbol.iterator] = function* myGeneratorFunc() {
        yield 1
        yield 2
        yield 3
    }
    /*for (let item of obj) {

        console.log(item);
    }*/
    let myFunc = obj[Symbol.iterator]();
    console.log(myFunc.next().value);
    console.log(myFunc.next().value);
    console.log(myFunc.next().value);
    console.log(myFunc.next().value);


    // 综上:通过给一个对象手动指定一个Symbol.iterator,可以实现for of对一个普通对象的遍历操作
    // 1. 内部的实现原理实际上是使用了generator函数的next方法完成了对象的遍历操作
    // 2. 遍历方式也就是obj[Symbol.iterator]的获取的是一个generator函数的引用,然后通过obj[Symbol.iterator]() 来调用函数
    // 3. 接着通过obj[Symbol.iterator]().next() 方式来实现数据的获取,得到的起始是yield位置执行完毕只有的返回值{value : aaa, done : false}的格式
    // 4. 最后通过obj[Symbol.iterator]().next().value 就可以实现普通对象的数据,通过iterator方式获取了


    // 案例练习
    /*
    * 需求:
    * 1、发送ajax请求获取新闻内容
    * 2、新闻内容获取成功后再次发送请求,获取对应的新闻评论内容
    * 3、新闻内容获取失败则不需要再次发送请求。
    * 
    * */


    // 使用Generator函数去异步获取数据
    function getNews(url) {
        $.get(url, function (data) {
            console.log(data);
            let url = 'http://localhost:3000' + data.commentsUrl;
            console.log(url);

            // 1. 在这里去向下移动指针 这里数据获取成功之后开始进行下一次数据的获取,这里把URl作为参数传递给下一个genrator函数
            // 2. 那么这里的参数url就是上一次执行完毕的一个返回值
            // 3. 如果之前的URL出错了,那么程序就不会向下执行了,回调函数就不会向下继续执行了
            generator.next(url);
        })
    }
    // getNews('http://localhost:3000/news?id=1')



    // 使用generator函数实现数据的获取
    function* sendReq() {
        // next()中传递的参数,可以当做下一次的启动的返回值的
        let url = yield getNews('http://localhost:3000/news?id=1');
        console.log('-------------current request url is ', url);               //  http://localhost:3000/comments?newsId=1
        yield getNews(url);
    }

    // 获取遍历器对象
    let generator = sendReq();
    console.log('-------------------', generator.next());


</script>


</body>

13.  async函数

<body>

<!--
  async函数(源自ES2017)
  概念: 真正意义上去解决异步回调的问题,同步流程表达异步操作
  本质: Generator的语法糖
  语法:
      async function foo(){
        await 异步操作;
        await 异步操作;
      }
  特点:
    1、不需要像Generator去调用next方法,遇到await等待,当前的异步操作完成就往下执行
    2、返回的总是Promise对象,可以用then方法进行下一步操作
    3、async取代Generator函数的星号*,await取代Generator的yield
    4、语意上更为明确,使用简单,经临床验证,暂时没有任何副作用

-->
<script type="text/javascript" src="./js/jquery-1.10.1.min.js"></script>
<script type="text/javascript">
    // async =======================================================================
    async function foo() {
        return new Promise(resolve => {
            // 普通写法
            /*setTimeout(()=>{
              resolve();
            }, 2000)*/

            // 上面的代码优化之后
            setTimeout(resolve, 2000);
        });
    }


    /**
     * 实现异步的执行
     * @return {Promise<void>}
     */
    async function test() {
        console.log('开始执行', new Date().toTimeString());
        await foo();
        console.log('开始执行', new Date().toTimeString());
    }
    // test();


    // 普通的函数 ==============================================================
    function test02() {
        return 'return value : ' + new Date().toTimeString();
    }
    async function asyncPrint() {
        let result = await test02();

        // 传递的参数就是我的返回值
        let result02 = await Promise.resolve('promise');
        // 失败的状态信息
        let result03 = await Promise.reject('失败的状态信息');

        console.log('result now is : ' + result, 'result02:', result02, 'result03:', result03);
    }
    // asyncPrint();



    // 获取新闻内容===============================================================
    async function getNews(url) {
        return new Promise((resolve, reject) => {
            $.ajax({
                method: 'GET',
                url,
                success: data => resolve(data),             // 调用resolve的时候,把参数传递进去即可,resolve中传递的参数就是await的返回值
                // error: error => reject(error)                   // 【注意点】:这里也是可以通过直接返回resolve的方式继续向下解析内容信息
                error: error => resolve(false)
            })
        })
    }
    async function sendMessage() {
        // 异步自动向下执行
        let result = await getNews('http://localhost:3000/news1?id=2');
        if (!result) {
            alert('数据拉取失败,……………………');
            return;
        }
        console.log('get news finished now : ', result);                            // result 中获取的实际上就是await的返回值
        result = await getNews('http://localhost:3000' + result.commentsUrl);
        console.log('get comments finished now : ', result);                        // result 中获取的实际上就是await的返回值
    }

    let res = sendMessage();
    console.log(res);
</script>

</body>

14.  class类的基本使用

<body>
</body>
<!--
1. 通过class定义类/实现类的继承
2. 在类中通过constructor定义构造方法
3. 通过new来创建类的实例
4. 通过extends来实现类的继承
5. 通过super调用父类的构造方法
6. 重写从父类中继承的一般方法
-->
<script type="text/javascript">

    /**
     * Person class
     * @param name
     * @param age
     * @constructor
     */
    function Person(name, age) {
        this.name = name;
        this.age = age;
    }

    console.log(new Person('zhangsan', 12));


    // 类的所有方法都会放在它的原型对象上面(实例对象上面的方法都是给实例本身使用, 所有的实例对象都是可以放在原型对象里面)
    class Animal {
        // 这是类的构造方法
        constructor(name, age) {
            this.name = name;
            this.age = age;
        }


        // 这个方法是放在Animal这个类的原型上面
        showName() {
            return this.name;
        }
    }


    class Dog extends Animal {
        constructor(name, age, sex) {
            // 1. super 的目的是去调用父类的构造方法
            // 2. 关键点:为了去调用父类的构造方法,这里需要手动在子类中把参数传递给父类去使用, 否则子类的就是一个{name : undefined, age  : undefined, sex : 'params'}
            super(name, age);               // 这句话调用的目的就是相当于:this.name = name;this.age = age;
            this.sex = sex;                 // 当依赖不上父类的时候,就需要自己去实现一个手动的属性
        }


        // 子类的方法重写父类的方法
        showName() {
            return this.name + ' ' + this.age + ' ' + this.sex;
        }
    }


    let dog = new Dog('redDog', 25, 'Male');            //  {name: "redDog", age: 25, sex: "Male"}
    console.log(dog, dog.showName());

</script>

15 . 字符串/数值/数组/对象的方法扩展

15.1 字符串扩展

<body>
<!--
1. includes(str) : 判断是否包含指定的字符串
2. startsWith(str) : 判断是否以指定字符串开头
3. endsWith(str) : 判断是否以指定字符串结尾
4. repeat(count) : 重复指定次数
-->
<script type="text/javascript">

  let str = 'afsfsfsdsdgsdggdwerewrsf';
  console.log(str.includes('t'));
  console.log(str.includes('a'));


  console.log(str.startsWith('a'))
  console.log(str.endsWith('b'))


  // str.repeat 用于将当前的字符串重复5次输出
  str = '123';
  console.log(str.repeat(5))


</script>
</body>

15.2 数值扩展

<body>
<!--
1. 二进制与八进制数值表示法: 二进制用0b, 八进制用0o
2. Number.isFinite(i) : 判断是否是有限大的数
3. Number.isNaN(i) : 判断是否是NaN
4. Number.isInteger(i) : 判断是否是整数
5. Number.parseInt(str) : 将字符串转换为对应的数值
6. Math.trunc(i) : 直接去除小数部分
-->
<script type="text/javascript">

  console.log(0b11);    // 二进制输出11
  console.log(0o56);    // 八进制输出56, 5*8^1 + 6 *8^0 = 46.

  let aaa = Infinity;
  console.log(Number.isFinite(1/0), aaa);   // true

  console.log(Number.isNaN('ss'));      // false

  console.log(Number.isInteger(10.0));    // true
  console.log(Number.isInteger(10.1));    // false



  // 【parseInt的几个注意点:】
  console.log(Number.parseInt('1212abc12'));  // 1212  (只要一个字符串的前面部分有数字的部分,就会直接把这个字符串转换为一个数字)
  console.log(Number.parseInt('abc12abc'));  // NAN  开头的部分如果不是数字的话,就会直接把这个转换为一个NAN

  console.log(Math.trunc(123.123));     // 123 (去除了小数点)

</script>
</body>

15.3 数组扩展

<body>
<button>测试1</button>
<br>
<button>测试2</button>
<br>
<button>测试3</button>
<br>

<!--
1. Array.from(v) : 将伪数组对象或可遍历对象转换为真数组
2. Array.of(v1, v2, v3) : 将一系列值转换成数组
3. find(function(value, index, arr){return true}) : 找出第一个满足条件返回true的元素
4. findIndex(function(value, index, arr){return true}) : 找出第一个满足条件返回true的元素下标
-->
<script type="text/javascript">

    // 获取到的是一个伪数组对象
    let btns = document.getElementsByTagName('button');


    // 使用原始的方法
    let arr = Array.prototype.slice.call(btns);
    console.log(arr, btns);
    console.log(arr.forEach((item) => item));


    // 使用ES6的方法
    Array.from(btns).forEach((item, index) => {
        console.log(item, index);
    })


    // 转换伪数组
    let arr01 = Array.of(1, 4, 'abc', 'def');
    console.log(arr01);


    // 查找数组中的元素, find 查找之后返回的是一个新的元素,而不是数组
    let arr02 = [1, 2, 2, 3, 3, 4, 4]
    let result = arr02.find((item, index) => {
        console.log(item, index);
        return item > 3;
    });
    console.log(result);

    // 找出第一个满足条件的数组下标
    console.log('---------------------------------------------------------')
    let index = arr02.findIndex((item, index) => {
        return item > 3;
    });
    console.log(index);                // 5
</script>
</body>

15.4 对象扩展

<body>
<!--
1. Object.is(v1, v2)
  * 判断2个数据是否完全相等
2. Object.assign(target, source1, source2..)
  * 将源对象的属性复制到目标对象上
3. 直接操作 __proto__ 属性
  let obj2 = {};
  obj2.__proto__ = obj1;
-->

<script type="text/javascript">
    // 对象测试
    console.log(0 == -0);         // true
    console.log(NaN == NaN);      // false, 注意NaN每次输出都是不同的值,因此不相同
    console.log(NaN);             // NaN


    // 对象输出测试,Object.is实际上是以【字符串】的方式来进行判断的
    console.log(Object.is(0, -0));        // false
    console.log(Object.is(NaN, NaN));     // true


    // 将源对象的属性复制到目标对象上面
    let obj = {};
    let obj1 = {username: 'zhangsan', age: 48};
    Object.assign(obj, obj1);
    console.log(obj);       // {username: "zhangsan", age: 48}
    let obj2 = {sex: 'MALE'};
    Object.assign(obj, obj2);
    console.log(obj);       // {username: "zhangsan", age: 48, sex: "MALE"}


    // 3. 直接操作__proto__, prototype 指向的是一个原型对象
    let obj3 = {};
    let obj4 = {money: 1500000};
    obj3.__proto__ = obj4;              // obj4 就是obj3的父类, 直接使用__proto__来实现
    console.log(obj3, obj3.money)

</script>
</body>

16. 深拷贝和浅拷贝的对比

<body>


<!--
  1、数据类型:
    * 数据分为基本的数据类型(String, Number, boolean, Null, Undefined)和对象数据类型
    - 基本数据类型:
      特点: 存储的是该对象的实际数据
    - 对象数据类型:
      特点: 存储的是该对象在栈中引用,真实的数据存放在堆内存里
  2、复制数据
    - 基本数据类型存放的就是实际的数据,可直接复制
      let number2 = 2;
      let number1 = number2;
    - 克隆数据:对象/数组
      1、区别: 浅拷贝/深度拷贝
         判断: 拷贝是否产生了新的数据还是拷贝的是数据的引用
         知识点:对象数据存放的是对象在栈内存的引用,直接复制的是对象的引用
         let obj = {username: 'kobe'}
         let obj1 = obj; // obj1 复制了obj在栈内存的引用
      2、常用的拷贝技术
        1). arr.concat(): 数组浅拷贝
        2). arr.slice(): 数组浅拷贝
        3). JSON.parse(JSON.stringify(arr/obj)): 数组或对象深拷贝, 但不能处理函数数据
        4). 浅拷贝包含函数数据的对象/数组
        5). 深拷贝包含函数数据的对象/数组
-->
<script type="text/javascript">

    // 修改str2的数据不会影响到原始数据
    let str = 'aaa';
    let str2 = str;
    console.log(str2);      // aaa
    str2 = '';
    console.log(str);       // aaa


    let bool1 = true;
    let bool2 = bool1;
    bool2 = false;
    console.log(bool1); // true


    // 使用引用
    let obj = {username: 'zhangsan', age: 35};
    let obj1 = obj;
    console.log(obj1);
    obj1.username = 'lisi';
    console.log(obj);


    // 数组传值
    let arr = [1, 4, {username: 'zhaoliu', age: 18}];
    let arr2 = arr;
    arr2[0] = 'abcdefg';      // 会把原来的数据也修改了
    console.log(arr, arr2);


    // 总结:
    /*
    * 基本数据类型:
    *   1. 拷贝之后会生成一份新的数据,修改拷贝之后的数据不会修改到原始数据
    *   2. 对象/数组拷贝之后不会生成新的数据,而会是一个对象的引用,修改拷贝之后的数据也会影响到原始的数据信息
    * 拷贝数据的基本方法:
    * 1. 直接复制给一个变量                       (浅拷贝)
    * 2. Object.assign()                        (浅拷贝)
    * 3. Array.prootype.comcat()                (浅拷贝)
    * 4. Array.prototype.slice()                 (浅拷贝)
    * 5. JSON.parse(JSON.stringfy())             (深拷贝:重要点,是不能处理函数的)
    *
    * 浅拷贝和深拷贝(只针对对象和数组):
    *   浅拷贝:拷贝的是对象的引用,修改拷贝之后的数据会影响到原始的数据
    *   深拷贝(深度克隆):拷贝的是数据,拷贝的时候会生成一份新的数据,修改拷贝之后的数据不会影响到原始的数据
    * */
    console.log('深拷贝和浅拷贝 =============================================================');
    // Object.assign实现拷贝数据
    let obj001 = {username: 'liuyu', age: 18};
    let obj002 = Object.assign(obj001);             // 浅拷贝
    console.log(obj002);

    obj002.username = 'zhangsanlisiwangwu';
    console.log(obj002);


    // 实现数组的拷贝
    console.log('数组的拷贝-------------------------------------------------');
    let arr001 = [1, 1, 2, 2, 323, 40, {
        username: 'zhaoliu',
        getName() {
            console.log("I am a function!");
            // ... 对象里面的一个函数, 在进行JSON.parse的时候是不能把这个函数也来拷贝一份的!!!
        }
    }, function () {
        console.log('function here!')
    }];
    let arr002 = arr001.concat();
    console.log(arr002);
    arr002[1] = 'hahaha';
    arr002[6].username = 'Object was updated!';
    console.log(arr001);


    // slice
    // let arr003 = arr001.slice(startIndex, endIndex)
    let arr003 = arr001.slice();            // 不传递参数的时候,实际上截取的是一个数组本身原封不动地复制了一份。
    console.log(arr003);
    arr003[6].username = 'I am a slice func!';
    console.log(arr001);


    // 深度拷贝
    console.log('数组的深度拷贝-------------------------------------------------');
    // 实现原理:实际上是中间的部分把一个对象转换为了一个字符串,然后相当于是数据类型
    let arr004 = JSON.stringify(arr001);
    console.log(arr004);
    // 开始实现深度拷贝一个对象
    // 【关键点:】JSON.stringfy()只可以把一个JS对象转换为JSON字符串,也就是只可以转换{}和Array([])这两种数据类型
    let parse = JSON.parse(JSON.stringify(arr001));
    parse[6].username = 'I am deep clone!'

    // 拷贝之后,发现原来arr001对象里面函数function不见了,去哪了???
    console.log(arr001, parse);                    // 深度拷贝的实现原理??


    // 深度拷贝高级知识【高级】======================================================================================================================
    console.log('深度拷贝=================================================================================================')
    /*
    * 如何实现深度拷贝:
    *  1. 拷贝的数据里面不能有对象/数组,即使有的话,也可以继续去遍历对象,或者数组,获取到对象的每一个值,然后再去复制,就是深度拷贝!
    *  2. 知识点储备:
    *    2.1 typeof返回是数据类型:String, Number, Boolean, Undefined, Object, Function【重点】
    *    2.2 Object.prototype.toString.call(obj)
    * */

    let result = 'abcd';
    console.log(Object.prototype.toString.call(result));        // [object String]
    result = null;
    console.log(Object.prototype.toString.call(result));        // [object Null]
    result = [];
    console.log(Object.prototype.toString.call(Array));         // [object Null]
    console.log(typeof Object.prototype.toString.call(Array));  // string

    let type = Object.prototype.toString.call(result);
    // slice(startIndex, endIndex), 注意: 这里是不包含endIndex 的位置的,因此可以直接从后往前数-1的位置就行
    console.log(type.slice(8, -1));

    let arr0001 = [12, 3, 34, 4, 4]
    for (let item in arr0001) {
        console.log(item);                  // 数组遍历的时候,遍历的是数组的下标index; 枚举对象的时候枚举的是对象的key
    }


    // 检测数据类型的功能函数
    /**
     * 数据类型通用检测
     * @param target
     * @return {string}
     */
    function checkType(target) {
        // 返回的就是一个类型字符串, 末尾的位置
        return Object.prototype.toString.call(target).slice(8, -1);
    }

    console.log(checkType([]));


    /**
     * 实现一个深度克隆函数
     * @param target
     */
    function clone(target) {
        // ... 只针对对象和数组这两种数据类型
        // result 是一个最终克隆出来的数据
        let result, targetType = checkType(target);
        if (targetType === 'Object') {
            result = {};

        } else if (targetType === 'Array') {
            result = [];
        } else {
            // 如果是字符串或者其他的普通基本数据类型的话
            return target;
        }

        // 遍历目标数据
        for (let item in target) {
            // 获取target中的每一项数据(不管你是对象或者数组都是可以的)
            // 对象的时候item是一个key;数组的时候是一个index数组下标
            let value = target[item];
            if (checkType(value) === 'Object' || checkType(value) === 'Array') {
                // ... 继续递归遍历获取到的value值
                result[item] = clone(value);

            } else {
                // 如果获取的到的数据是一个基本数据类型的话
                result[item] = value;
            }
        }

        return result;
    }


    // arr
    let arr00001 = [1, 2, 3, 4, {username: 'deep clone'}];
    let data00001 = clone(arr00001);
    console.log(data00001);
    data00001[4].username = 'hahahaha';
    console.log(arr00001, data00001);

    // obj
    let obj000001 = {username: 'liudehua', age: 28};
    console.log(obj000001);
    let data0002 = clone(obj000001);
    data0002.username = '11212';
    console.log(data0002);

</script>
</body>

17.  Set和Map数据结构

<body>
<!--
1. Set容器 : 无序不可重复的多个value的集合体
  * Set()
  * Set(array)
  * add(value)
  * delete(value)
  * has(value)
  * clear()
  * size
2. Map容器 : 无序的 key不重复的多个key-value的集合体(数组集合)
  * Map()
  * Map(array)
  * set(key, value)//添加
  * get(key)
  * delete(key)
  * has(key)
  * clear()
  * size
-->

<script type="text/javascript">
    // set 中存放的是不可重复的数据
    let set = new Set([1, 2, 3, 3, 4, 34, 234, 23]);
    console.log(set);

    set.add(10);
    console.log(set.size, set);
    set.delete(0);
    console.log(set.has(1));
    set.clear();


    // map 的使用(存放的是一个二维数组)
    let map = new Map([['name' , 'zhangsan'], ['age', '18']]);
    console.log(map);
    map.set('sex' , 'MALE');            // 添加元素
    console.log(map);
    map.delete('sex');
    console.log(map);


</script>
</body>

18.  for_of循环

<body>

<!--
for(let value of target){}循环遍历
  1. 遍历数组
  2. 遍历Set
  3. 遍历Map
  4. 遍历字符串
  5. 遍历伪数组
-->

<button>按钮1</button>
<button>按钮2</button>
<button>按钮3</button>

<script type="text/javascript">
    let set = new Set([13, 34324, 2342345, 235, 234543, 534, 5, 5]);
    for (let item of set) {
        console.log(item);
    }


    // set 实现一个数组去重
    let arr = [1, 3, 4545, 2, 3, 4];
    let arr1 = arr;
    arr = [];
    let set01 = new Set(arr1);
    // 开始去重
    for (let item of set01) {
        arr.push(item);
    }
    console.log(arr);

</script>
</body>

 

 

 

 

 

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值