ES6新增方法和属性以及ajax笔记

<!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>
 //认识箭头函数
      //  const add =(x,y)=>{
          //  return x+y;
      //  }
      //  console.log((add(1,1)))
   //箭头函数的结构
     //const/let 函数名=参数=>函数体
  // 3,如何将一般函数改写成箭头函数
     // 声明形式
     // function add(){}
  // 声明形式
  // const add=function (){}
  //函数表达式形式=>箭头函数

//箭头函数 的注意事项
    // 1.单个参数
      // 单个参数可以省略圆括号
      // const add = x => {
      //   return x + 1;
      // };
      // console.log(add(1));

      // 无参数或多个参数不能省略圆括号
      // const add = () => {
      //   return 1 + 1;
      // };
      // const add = (x, y) => {
      //   return x + y;
      // };
      // console.log(add(1, 1));

      // 2.单行函数体
      // 单行函数体可以同时省略 {}return
      // const add = (x, y) => {
      //   return x + y;
      // };
      // const add = (x, y) => x + y;
      // console.log(add(1, 1));

      // 多行函数体不能再化简了
      // const add = (x, y) => {
      //   const sum = x + y;
      //   return sum;
      // };

      // 3.单行对象
      // const add = (x, y) => {
      //   return {
      //     value: x + y
      //   };
      // };
      // const add = (x, y) => ({
      //   value: x + y
      // });

      // 如果箭头函数返回单行对象,可以在 {} 外面加上 (),让浏览器不再认为那是函数体的花括号

      // const add = (x, y) => [x, y];

      // console.log(add(1, 1));
  
//非箭头函数中的 this 指向
       // 1.全局作用域中的 this 指向
       // console.log(this); // window
       // 2.一般函数(非箭头函数)中的 this 指向
       // 'use strict';
       // function add() {
       //   console.log(this);
       // }
       // 严格模式就指向 undefined
       // add(); // undefined->window(非严格模式下)
       // window.add();
       // const calc = {
       //   add: add
       // };
       // // calc.add(); // calc
       // const adder = calc.add;
       // adder(); // undefined->window(非严格模式下)
       // document.onclick = function () {
       //   console.log(this);
       // };
       // document.onclick();
       // function Person(username) {
       //   this.username = username;
       //   console.log(this);
       // }
       // const p = new Person('Alex');
       // 只有在函数调用的时候 this 指向才确定,不调用的时候,不知道指向谁
       // this 指向和函数在哪儿调用没关系,只和谁在调用有关
       // 没有具体调用对象的话,this 指向 undefined,在非严格模式下,转向 window
// 箭头函数中的 this 指向
         // 1.箭头函数中的 this 指向
         // 箭头函数没有自己的 this
         // const calc = {
         //   add: () => {
         //     console.log(this);
         //   }
         // };
         // calc.add(); // window
         // 2.练习
         // 'use strict';
        //  const calc = {
        //    add: function () {
            //  this
            //  const adder = () => {
            //    console.log(this);
            //  };
            //  adder();
        //    }
        //  };
         // calc.add(); // calc
        //  const addFn = calc.add;
        //  addFn(); // undefined->window
// 不适用箭头函数的场景 
      // 1.作为构造函数
     // 箭头函数没有 this
     // const Person = () => {};
     // new Person();
     // 2.需要 this 指向调用对象的时候
     // document.onclick = function () {
     //   console.log(this);
     // };
     // document.addEventListener(
     //   'click',
     //   () => {
     //     console.log(this); //window
     //   },
     //   false
     // );
     // 3.需要使用 arguments 的时候
     // 箭头函数中没有 arguments
     // function add() {
     //   console.log(arguments);
     // }
     // add(1, 2,3,4,5);
     // const add = () => console.log(arguments);
     // add();
     // 剩余参数

// 模板字符串
 // 1.认识模板字符串
      // const username1 = 'alex';
      // // "alex"

      // const username2 = `alex`;
      // console.log(username1, username2, username1 === username2);

      // 2.模板字符串与一般字符串的区别
    //   const person = {
        // username: 'Alex',
        // age: 18,
        // sex: 'male'
    //   };

      // const info =
      //   '我的名字是:' +
      //   person.username +
      //   ', 性别:' +
      //   person.sex +
      //   ', 今年' +
      //   person.age +
      //   '岁了';
      // console.log(info);

      // const info = `我的名字是:${person.username}, 性别:${person.sex}, 今年${person.age}岁了`;
      // console.log(info);

      // 和其他东西一起使用的时候,使用模板字符串,方便注入
      // 其他情况下使用模板字符串或一般字符串都行  
// 模板字符串的注意事项
      // 1.输出多行字符串
      // 一般字符串
      // const info = '第1行\n第2行';
      // console.log(info);

      // 模板字符串
      // const info = `第1行\n第2行`;
      //       const info = `第1行
      // 第2行`;
      //       console.log(info);

      // 模板字符串中,所有的空格、换行或缩进都会被保留在输出之中

      // 2.输出 `\ 等特殊字符
      // const info = `'\`\\`;
      // console.log(info);

      // 3.模板字符串的注入
      // ${}
      // const username = 'alex';
      // const person = { age: 18, sex: 'male' };
      // const getSex = function (sex) {
      //   return sex === 'male' ? '男' : '女';
      // };

      // const info = `${username}, ${person.age + 2}, ${getSex(person.sex)}`;
      // console.log(info);

      // 只要最终可以得出一个值的就可以通过 ${} 注入到模板字符串中
// 对象解构赋值的原理
  // 1.模式(结构)匹配
      // {}={}

      // 2.属性名相同的完成赋值
      // const { age, username } = { username: 'Alex', age: 18 };
      // const { age: age, username: username } = { username: 'Alex', age: 18 };
      // console.log(age, username);

      // 取别名
      //   const { age: age, username: uname } = { username: 'Alex', age: 18 };
      //   console.log(age, uname);
// 对象解构赋值的注意事项
                  // 1.默认值的生效条件
              // 对象的属性值严格等于 undefined 时,对应的默认值才会生效
              // const { username = 'ZhangSan', age = 0 } = { username: 'alex' };
              // console.log(username, age);
              // 2.默认值表达式
              // 如果默认值是表达式,默认值表达式是惰性求值的
              // 3.将一个已经声明的变量用于解构赋值
              // 如果将一个已经声明的变量用于对象的解构赋值,整个赋值需在圆括号中进行
              // let { x } = { x: 1 };
              // console.log(x);
              // let x = 2;
              // // ({ x } = { x: 1 });
              // [x] = [1];
              // console.log(x);
              // 4.可以取到继承的属性
              // const { a = 1} = {};
            //  const { toString } = {};
              // console.log(toString);
              // Object.prototype
              // console.log(Object.prototype);
            //   console.log({});
 // 1.函数参数的解构赋值
          // const logPersonInfo = user => console.log(user.username, user.age);
          // const logPersonInfo = ({ age = 0, username = 'ZhangSan' }) =>
          //   console.log(username, age);
          // // logPersonInfo({ username: 'alex', age: 18 });
          // logPersonInfo({});
          // // { age, username:username }={ username: 'alex', age: 18 }
          // 2.复杂的嵌套
        //   const obj = {
            // x: 1,
            // y: [2, 3, 4],
            // z: {
            //   a: 5,
            //   b: 6
            // }
        //   };
          // const { x, y, z } = obj;
          // console.log(x, y, z);
        //   const {
            // y,
            // y: [, yy],
            // z,
            // z: { b }
        //   } = obj;
        //   console.log(yy, y, z, b);
          // [, yy] = [2, 3, 4]
 //其他类型的解构赋值
       // 1.字符串的解构赋值
        // ''='hello' ×
        // 数组形式的解构赋值
        // const [a, b, , , c] = 'hello';
        // console.log(a, b, c);
        // 对象形式的解构赋值
        // const { 0: a, 1: b, length } = 'hello';
        // console.log(a, b, length);
        // console.log('hello'.length);
        // 字符串既可以按数组形式来解构赋值,也可以按对象形式来解构赋值
        // 2.数值和布尔值的解构赋值
        // 先将等号右边的值转为对象
        // console.log(new Number(123));
        // const { a = 1, toString } = 123;
        // console.log(a, toString);
        // const { b = 2, toString } = true;
        // console.log(b, toString);
        // 3.undefined 和 null 的解构赋值
        // 由于 undefined 和 null 无法转为对象,所以对它们进行解构赋值,都会报错
        // const { toString } = undefined;
        // const { toString } = null;
//数组的解构赋值
          // 1.认识解构赋值
          // const arr = [1, 2, 3];
          // const a = arr[0];
          // const b = arr[1];
          // const c = arr[2];
          // console.log(a, b, c);
          // const [a, b, c] = [1, 2, 3];
          // console.log(a, b, c);
          // 2.什么是解构赋值
          // 解析某一数据的结构,将我们想要的东西提取出来,赋值给变量或常量
//数组解构赋值的原理
       // 1.模式(结构)匹配
      // [] = [1, 2, 3];

      // 2.索引值相同的完成赋值
      // const [a, b, c] = [1, 2, 3];
      // console.log(a, b, c);

      // 不取的,可以直接用逗号跳过
      //   const [a, [, , b], c] = [1, [2, 4, 5], 3];
      //   console.log(a, b, c);
//数组解构赋值的默认值 
         // 1.默认值的基本用法
      // const [a, b] = [];
      // const [a, b] = [undefined, undefined];
      // const [a = 1, b = 2] = [];
      // console.log(a, b);
      // 2.默认值的生效条件
      // 只有当一个数组成员严格等于(===)undefined 时,对应的默认值才会生效
      // const [a = 1, b = 2] = [3, 0];
      // const [a = 1, b = 2] = [3, null];
      // const [a = 1, b = 2] = [3];
      // console.log(a, b);
      // 3.默认值表达式
      // 如果默认值是表达式,默认值表达式是惰性求值的
      // const func = () => {
      //   console.log('我被执行了');
      //   return 2;
      // };
      // // const [x = func()] = [1];
      // const [x = func()] = [];
      // console.log(x);
// 数组解构赋值的应用
     // 1.常见的类数组的解构赋值
      // arguments
      // function func() {
      //   // console.log(arguments);
      //   // console.log(arguments.push);
      //   const [a, b] = arguments;
      //   console.log(a, b);
      // }
      // // func();
      // func(1, 2);

      // NodeList
      // console.log(document.querySelectorAll('p'));
      // const [p1, p2, p3] = document.querySelectorAll('p');
      // console.log(p1, p2, p3);

      // 2.函数参数的解构赋值
      // const array = [1, 1];
      // // const add = arr => arr[0] + arr[1];
      // const add = ([x = 0, y = 0]) => x + y;
      // // console.log(add(array));
      // console.log(add([]));

      // [x,y]=[1,1]

      // 3.交换变量的值
       //   let x = 1;
       //   let y = 2;

         // let tmp = x;
         // x = y;
         // y = tmp;
         // console.log(x, y);

         // [x, y] = [y, x];
       //   [x, y] = [2, 1];
       //   console.log(x, y);
// 函数参数
  // 1.认识函数参数的默认值
      // 调用函数的时候传参了,就用传递的参数;如果没传参,就用默认值
      // multiply(2, 1);
      // multiply(2);

      // 2.函数参数默认值的基本用法
      // const multiply = (x, y) => {
      //   if (typeof y === 'undefined') {
      //     y = 1;
      //   }

      //   return x * y;
      // };

    //   const multiply = (x, y = 1) => x * y;
    //   console.log(multiply(2));
// 函数参数默认值的注意事项
     // 1.默认值的生效条件
      // 不传参数,或者明确的传递 undefined 作为参数,只有这两种情况下,默认值才会生效
      // const multiply = (x, y = 1) => x * y;
      // // console.log(multiply(2, 0));
      // // console.log(multiply(2, null));
      // console.log(multiply(2, undefined));
      // console.log(multiply(2));

      // 2.默认值表达式
      // 如果默认值是表达式,默认值表达式是惰性求值的

      // 3.设置默认值的小技巧
      // 函数参数的默认值,最好从参数列表的右边开始设置
      // const multiply = (x = 1, y) => x * y;
      // console.log(multiply(undefined, 2));

    //   const multiply = (x, y = 1) => x * y;
    //   console.log(multiply(2));

// 函数参数默认值的应用
     // 1.接收很多参数的时候
     // const logUser = (username = 'ZhangSan', age = 0, sex = 'male') =>
     //   console.log(username, age, sex);
     // logUser('Alex', 18, 'male');
     // logUser();
     // 2.接收一个对象作为参数
     // const logUser = options =>
     // console.log(options.username, options.age, options.sex);
    //  const logUser = ({ username = 'zhangsan', age = 0, sex = 'male' } = {}) =>
    //    console.log(username, age, sex);
     // logUser({
     //   username: 'alex',
     //   age: 18,
     //   sex: 'male'
     // });
     // logUser({ username: 'alex' });
     // { username = 'zhangsan', age = 0, sex = 'male' } = { username: 'alex' }
     // logUser({});
    //  logUser();
     // { username = 'zhangsan', age = 0, sex = 'male' } = {}
     // { username = 'zhangsan', age = 0, sex = 'male' } = undefined
// 属性和方法的简洁表示
      // 1.对象字面量是什么
      // 实例化构造函数生成对象
      // const person = new Object();
      // person.age = 18;
      // person.speak = function () {};
      // 对象字面量
      // const person = {
      //   age: 18,
      //   speak: function () {}
      // };
      // 2.属性的简洁表示法
      // 键名和变量或常量名一样的时候,可以只写一个
      // const age = 18;
      // const person = {
      //   // age: age
      //   age
      // };
      // console.log(person);
      // 3.方法的简洁表示法
         // 方法可以省略冒号和 function 关键字
       //    const person = {
         // speak: function () {}
           //    speak() {}
           //  };
       //    console.log(person);
// 方括号语法的用法
        // 1.方括号语法的用法
        // const prop = 'age';
        // const person = {};
        // // person.prop = 18;
        // person[prop] = 18;
        // 方括号语法可以写在对象字面量中
        // const person = {
        //   [prop]: 18
        // };
        // console.log(person);
        // 2.方括号中可以放什么
        // ${}
        // [值或通过计算可以得到值的(表达式)]
        // const prop = 'age';
        // const func = () => 'age2';
        // const person = {
        //   // [prop]: 18
        //   // [func()]: 18
        //   // ['sex']: 'male'
        //   ['s' + 'ex']: 'male'
        // };
        // console.log(person);
        // 3.方括号语法和点语法的区别
        // 点语法是方括号语法的特殊形式
        // const person = {};
        // person.age 等价于 person['age']
        // 属性名由数字、字母、下划线以及 $ 构成,并且数字还不能打头的时候可以使用点语法
        // age18_$ √
        // 18age ×
        // 合法标识符可以用来作为变量或常量名
        // 当你的属性或方法名是合法标识符时,可以使用点语法,其他情况下请使用方括号语法
// 展开运算符
     // 数组认识展开运算符
     // [3, 1, 2];
     // Math.min
     // console.log(Math.min([3, 1, 2]));
     // console.log(Math.min(3, 1, 2));
     // [3, 1, 2]->3, 1, 2
     // 2.数组展开运算符的基本用法
     // console.log(Math.min(...[3, 1, 2]));
     // 相当于
    //  console.log(Math.min(3, 1, 2));
    //  区分剩余参数和展开运算符
// 区分剩余参数和展开运算符
      // 1.根本区别
      // 展开运算符
      // [3,1,2]->3,1,2

      // 剩余参数
      // 3,1,2->[3,1,2]

      // 2.区分剩余参数和展开运算符

      // 剩余参数
      // const add = (...args) => {
      //   // console.log(args);

      //   // 展开运算符
      //   // console.log(...args);
      //   // console.log(...[1, 2, 3]);
      //   console.log(1, 2, 3);
      // };
      // add(1, 2, 3);

      //   console.log([...[1, 2, 3], 4]);
      // [1, 2, 3]->1,2,3
// 数组展开运算符的应用
    // 1.复制数组
    // const a = [1, 2];
    // // const b = a;
    // // a[0] = 3;
    // // console.log(b);
    // const c = [...a];
    // // const c = [1, 2];
    // a[0] = 3;
    // console.log(a);
    // console.log(c);
    // 2.合并数组
    // const a = [1, 2];
    // const b = [3];
    // const c = [4, 5];
    // // console.log([...a, ...b, ...c]);
    // // console.log([...b, ...a, ...c]);
    // console.log([1, ...b, 2, ...a, ...c, 3]);
    // 3.字符串转为数组
    // 字符串可以按照数组的形式展开
    // console.log(...'alex');
    // console.log('a', 'l', 'e', 'x');
    // console.log([...'alex']);
    // console.log('alex'.split(''));
    // reverse
    // 4.常见的类数组转化为数组
    // arguments
    // function func() {
    //   // console.log(arguments.push);
    //   console.log([...arguments]);
    // }
     // func(1, 2);
    // NodeList
    // console.log(document.querySelectorAll('p'));
    // console.log([...document.querySelectorAll('p')].push);
// 剩余参数
    //  剩余参数是什么
      // 1.认识剩余参数
     // const add = (x, y, z, ...args) => {};
     // 2.剩余参数的本质
    //  const add = (x, y, ...args) => {
    //    console.log(x, y, args);
    //  };
    //  add();
    //  add(1);
    //  add(1, 2);
    //  add(1, 2, 3, 4, 5);
     // 剩余参数永远是个数组,即使没有值,也是空数组
     // 3, 4, 5->[3, 4, 5]
// 剩余参数的注意事项
      // 1.箭头函数的剩余参数
      // 箭头函数的参数部分即使只有一个剩余参数,也不能省略圆括号
      // const add = (...args) => {};
      // 2.使用剩余参数替代 arguments 获取实际参数
      // const add = function () {
      //   console.log(arguments);
      // };
      // const add = (...args) => {
      //   console.log(args);
      // };
      // add(1, 2);
      // 3.剩余参数的位置
      // 剩余参数只能是最后一个参数,之后不能再有其他参数,否则会报错
      //   const add = (x, y, ...args) => {
          // console.log(args);
      //   };
// 剩余参数的应用
       // 1.完成 add 函数
       // const add = (...args) => {
       //   let sum = 0;
       //   for (let i = 0; i < args.length; i++) {
       //     sum += args[i];
       //   }
       //   // reduce
       //   return sum;
       // };
       // // console.log(add());
       // // console.log(add(1, 1));
       // console.log(add(1, 2, 3));
       // 2.与解构赋值结合使用
       // 剩余参数不一定非要作为函数参数使用
       // const [num, ...args] = [1, 2, 3, 4];
       // 必须是最后一个
       // const [...args,num] = [1, 2, 3, 4];
       // console.log(num, args);
       // const func = ([num, ...args]) => {};
       // func([1, 2, 3]);
       // const { x, y, ...z } = { a: 3, x: 1, y: 2, b: 4 };
       // // 必须是最后一个
       // // const { x, ...z, y } = { a: 3, x: 1, y: 2, b: 4 };
       // console.log(x, y, z);
       // const func = ({ x, y, ...z }) => {};
       // func({ a: 3, x: 1, y: 2, b: 4 });
// 对象展开运算符的基本用法
       // 1.展开对象
      // 对象不能直接展开,必须在 {} 中展开
      // const apple = {
      //   color: '红色',
      //   shape: '球形',
      //   taste: '甜'
      // };
      // console.log(...apple);
      // console.log([...apple]);
      // 对象的展开:把属性罗列出来,用逗号分隔,放到一个 {} 中,构成新对象
      // console.log({ ...apple });
      // console.log({ ...apple } === apple);
      // 2.合并对象
    //   const apple = {
        // color: '红色',
        // shape: '球形',
        // taste: '甜'
    //   };
    //   const pen = {
        // color: '黑色',
        // shape: '圆柱形',
        // use: '写字'
    //   };
      // console.log({ ...pen });
      // console.log({ ...apple, ...pen });
      // 新对象拥有全部属性,相同属性,后者覆盖前者
      // console.log({ ...pen, ...apple });
      // 相当于
      // console.log({
      //   use: '写字',
      //   color: '红色',
      //   shape: '球形',
      //   taste: '甜'
      // });
      // console.log({ pen, apple });
    //   console.log({ ...pen, apple });
// 对象展开运算符的注意事项
    // 1.空对象的展开
    // 如果展开一个空对象,则没有任何效果
    // console.log({ ...{} });
    // console.log({ ...{}, a: 1 });
    // 2.非对象的展开
    // 如果展开的不是对象,则会自动将其转为对象,再将其属性罗列出来
    // console.log({ ...1 });
    // console.log(new Object(1));
    // console.log({ ...undefined });
    // console.log({ ...null });
    // console.log({ ...true });
    // 如果展开运算符后面是字符串,它会自动转成一个类似数组的对象,因此返回的不是空对象
    // console.log({ ...'alex' });
    // console.log([...'alex']);
    // console.log(...'alex');
    // console.log({ ...[1, 2, 3] });
    // 3.对象中对象属性的展开
    // 不会展开对象中的对象属性
    //    const apple = {
    //  feature: {
    //    taste: '甜'
    //  }
    //    };
    //    const pen = {
        //  feature: {
        //    color: '黑色',
        //    shape: '圆柱形'
        //  },
        //  use: '写字'
    //    };
    //    console.log({ ...apple });
    //    console.log({ ...apple, ...pen });
    //    相当于
    //    console.log({
        //  feature: {
        //    color: '黑色',
        //    shape: '圆柱形'
        //  },
        //  use: '写字'
    //    });
// 对象展开运算符的应用
             // 1.复制对象
         // const a = { x: 1, y: 2 };
         // // const b = a;
         // const c = { ...a };
         // console.log(c, c === a);
         // 2.用户参数和默认参数
         // add(1, 2);
         // const logUser = ({
         //   username = 'ZhangSan',
         //   age = 0,
         //   sex = 'male'
         // } = {}) => {
         //   console.log(username, age, sex);
         // };
        //  const logUser = userParam => {
        //    const defaultParam = {
            //  username: 'ZhangSan',
            //  age: 0,
            //  sex: 'male'
        //    };
        //    const param = { ...defaultParam, ...userParam };
        //    const param = { ...defaultParam, ...undefined };
        //    console.log(param.username);
        //    const { username, age, sex } = { ...defaultParam, ...userParam }
        //    console.log(username, age, sex);
        //  };
        //  logUser();
// set和Map
          // 1.什么是 Set
          // 集合
          // [1, 2];
          // 数组是一系列有序的数据集合
          // Set 是一系列无序、没有重复值的数据集合
          // 2.理解 Set
          // console.log([1, 2, 1]);
          // console.log(new Array(1, 2, 1));
          //   const s = new Set();
          //   s.add(1);
          //   s.add(2);
            // Set 中不能有重复的成员
          //   s.add(1);
          //   console.log(s);
          // Set 没有下标去标示每一个值,所以 Set 是无序的,也不能像数组那样通过下标去访问 Set 的成员
// Set 实例的方法和属性
           // 1.方法
           // add
        //  const s = new Set();
        //  s.add(1).add(2).add(2);
           // console.log(s);
           // has
           // console.log(s.has(1));
           // console.log(s.has(3));
           // delete
           // s.delete(1);
           // // 使用 delete 删除不存在的成员,什么都不会发生,也不会报错
           // s.delete(3);
           // console.log(s);
           // clear
           // s.clear();
           // console.log(s);
           // forEach
           // s.forEach(function (value, key, set) {
           //   // Set 中 value = key
           //   console.log(value, key, set === s);
           //   // console.log(this);
           // }, document);
           // console.log(s);
           // 按照成员添加进集合的顺序遍历
           // 2.属性
              // size
           //    console.log(s.size);
           //    console.log(s);
//  Set 构造函数的参数
             // 1.数组
             // const s = new Set([1, 2, 1]);
             // console.log(s);
             // 2.字符串、arguments、NodeList、Set 等
             // console.log(new Set('hi'));
             // function func() {
             //   console.log(new Set(arguments));
             // }
             // func(1, 2, 1);
            //  console.log(new Set(document.querySelectorAll('p')));
            //  const s = new Set([1, 2, 1]);
            //  console.log(new Set(s) === s);
            //  console.log(s);
// Set 的注意事项
            // 1.判断重复的方式
            // const s = new Set([1, 2, 1]);
            // const s = new Set([NaN, 2, NaN]);
            // console.log(1 === 1);
            // console.log(NaN === NaN);
            // Set 对重复值的判断基本遵循严格相等(===)
            // 但是对于 NaN 的判断与 === 不同,Set 中 NaN 等于 NaN
            // console.log(s);
            // const s = new Set();
            // s.add({}).add({});
            // console.log({} === {});
            // console.log(s);
            // 2.什么时候使用 Set
            // ① 数组或字符串去重时
            // ② 不需要通过下标访问,只需要遍历时
            // ③ 为了使用 Set 提供的方法和属性时(add delete clear has forEach size 等)
// Set 的应用 
          // 1.数组去重
          // [1, 2, 1];
          // const s = new Set([1, 2, 1]);
          // console.log(s);
          // s.forEach
          // console.log(...s);
          // console.log([...s]);
          // console.log([...new Set([1, 2, 1])]);
          // 2.字符串去重
          // 'abbacbd';
          // const s = new Set('abbacbd');
          // console.log([...s].join(''));
          // console.log(s);
          // console.log([...new Set('abbacbd')].join(''));
          // 3.存放 DOM 元素
          // console.log(document.querySelectorAll('p'));
          // for()
          //   const s = new Set(document.querySelectorAll('p'));
          //   console.log(s);
          //   s.forEach(function (elem) {
            // console.log(elem);
            // elem.style.color = 'red';
            // elem.style.backgroundColor = 'yellow';
        //   });
// Map 是什么
               // 1.认识 Map
               // 映射
               // Map 和对象都是键值对的集合
               // 键->值,key->value
               // const person = {
               //   name: 'alex',
               //   age: 18
               // };
               // const m = new Map();
               // m.set('name', 'alex');
               // m.set('age', 18);
               // console.log(m);
               // 2.Map 和对象的区别
               // 对象一般用字符串当作键
               // const obj = {
               //   name: 'alex',
               //   true: 'true',
               //   [{}]: 'object'
               // };
               // console.log(obj);
               // console.log({}.toString());
               // 基本数据类型:数字、字符串、布尔值、undefined、null
               // 引用数据类型:对象([]、{}、函数、Set、Map 等)
               // 以上都可以作为 Map 的键
            //    const m = new Map();
            //    m.set('name', 'alex');
            //    m.set(true, 'true');
            //    m.set({}, 'object');
            //    m.set(new Set([1, 2]), 'set');
            //    m.set(undefined, 'undefined');
            //    console.log(m);
// Map 实例的属性和方法
       // 1.方法
       // set
      //    const m = new Map();
       // 使用 set 添加的新成员,键如果已经存在,后添加的键值对覆盖已有
       //    m.set('age', 18).set(true, 'true').set('age', 20);
       // console.log(m);
       // set get
       // add
       // get
       //    console.log(m);
       // console.log(m.get('age'));
       // // get 获取不存在的成员,返回 undefined
       // console.log(m.get('true'));
       // console.log(m.get(true));
       // has
       // console.log(m.has('age'));
       // console.log(m.has('true'));
       // delete
       // m.delete('age');
       // m.delete('name');
       // // 使用 delete 删除不存在的成员,什么都不会发生,也不会报错
       // console.log(m);
       // clear
       // m.clear();
       // console.log(m);
       // forEach
       // m.forEach(function (value, key, map) {
       //   // console.log(value, key, map === m);
       //   console.log(this);
       // }, document);
       // 2.属性
       // size
       // 对象没有类似的属性
// Map 构造函数的参数
         // 1.数组
         // console.log(new Map(['name', 'alex', 'age', 18]));
         // 只能传二维数组,而且必须体现出键和值
         // console.log(
         //   new Map([
         //     ['name', 'alex'],
         //     ['age', 18]
         //   ])
         // );
         // 2.Set、Map 等
         // Set
         // Set 中也必须体现出键和值
         // const s = new Set([
         //   ['name', 'alex'],
         //   ['age', 18]
         // ]);
         // console.log(new Map(s));
         // console.log(s);
         // Map
         // 复制了一个新的 Map
        //  const m1 = new Map([
        //    ['name', 'alex'],
            //    ['age', 18]
        //  ]);
        //  console.log(m1);
        //  const m2 = new Map(m1);
        //  console.log(m2, m2 === m1);
//  Map 的注意事项
            // 1.判断键名是否相同的方式
            // 基本遵循严格相等(===)
            // 例外就是 NaN,Map 中 NaN 也是等于 NaN
            // console.log(NaN === NaN);
            // const m = new Map();
            // m.set(NaN, 1).set(NaN, 2);
            // console.log(m);
            // 2.什么时候使用 Map
            // 如果只是需要 key -> value 的结构,或者需要字符串以外的值做键,使用 Map 更合适
            // forEach for in
            // size
            // 只有模拟现实世界的实体时,才使用对象
            // const person = {};
// 1.Iterator 的作用
             // Iterator:遍历器(迭代器)
             // for()
             // [1,2].forEach
             // new Set().forEach
             // Iterator 也是用来遍历的
             // 2.寻找 Iterator
             // console.log(Iterator);
             // console.log([1, 2][Symbol.iterator]());
             // const it = [1, 2][Symbol.iterator]();
             // console.log(it);
             // 3.使用 Iterator
             // const it = [1, 2][Symbol.iterator]();
             // console.log(it.next()); // {value: 1, done: false}
             // console.log(it.next()); // {value: 2, done: false}
             // console.log(it.next()); // {value: undefined, done: true}
             // console.log(it.next()); // {value: undefined, done: true}
             // it:可遍历对象(可迭代对象)
             // Symbol.iterator:可遍历对象的生成方法
             // 4.什么是 Iterator
             // Symbol.iterator(可遍历对象的生成方法) -> it(可遍历对象) -> it.next() -> it.next() -> ...(直到 done 为 true)
 // 1.为什么需要 Iterator 遍历器
        // 遍历数组:for 循环和 forEach 方法
        // 遍历对象:for in 循环
     
         // Iterator 遍历器是一个统一的遍历方式
         // console.log([][Symbol.iterator]());
         // console.log({}[Symbol.iterator]);
         // 2.如何更方便的使用 Iterator
         // Symbol.iterator->it->next()
         // 我们一般不会直接使用 Iterator 去遍历
         // for..of      
// for...of 的用法
        // 1.认识 for...of
        // const arr = [1, 2, 3];
        // // const it = arr[Symbol.iterator]();
        // // // console.log(it.next());
        // // // console.log(it.next());
        // // // console.log(it.next());
        // // // console.log(it.next());
        // // let next = it.next();
        // // console.log(next);
        // // while (!next.done) {
        // //   console.log(next.value);
        // //   next = it.next();
        // //   console.log(next);
        // // }
        // for (const item of arr) {
        //   console.log(item);
        // }
        // // for...of 循环只会遍历出那些 done 为 false 时,对应的 value 值
        // 2.与 break、continue 一起使用
        // const arr = [1, 2, 3];
        // for (const item of arr) {
        //   if (item === 2) {
        //     // break;
        //     continue;
        //   }
        //   console.log(item);
        // }
         // arr.forEach()
         // 3.在 for...of 中取得数组的索引
        //  const arr = [1, 2, 3];
         // keys() 得到的是索引的可遍历对象,可以遍历出索引值
        //  console.log(arr.keys());
         // for (const key of arr.keys()) {
         //   // console.log(key);
         // }
         // // values() 得到的是值的可遍历对象,可以遍历出值
         // for (const value of arr.values()) {
         //   console.log(value);
         // }
         // for (const value of arr) {
         //   console.log(value);
         // }
         // entries() 得到的是索引+值组成的数组的可遍历对象
         // for (const entries of arr.entries()) {
         //   console.log(entries);
         // }
        //  for (const [index, value] of arr.entries()) {
        //    console.log(index, value);
        //  }
// 原生可遍历和非原生可遍历  
     // 1.什么是可遍历
      // 只要有 Symbol.iterator 方法,并且这个方法可以生成可遍历对象,
      // 只要可遍历,就可以使用 for...of 循环来统一遍历
      // 2.原生可遍历的有哪些
      // 数组
      // 字符串
      // Set
      // Map
      // arguments
      // NodeList
      // for (const item of [1, 2, 3]) {
      //   console.log(item);
      // }
      // for (const item of 'hi') {
      //   console.log(item);
      // }
      // for (const item of new Set([1, 2])) {
      //   console.log(item);
    // }
    // for (const elem of document.querySelectorAll('p')) {
    //   console.log(elem);
    //   elem.style.color = 'red';
    // }
    // 3.非原生可遍历的有哪些
    // 一般的对象
    // const person = { sex: 'male', age: 18 };
    // console.log(person[Symbol.iterator]());
    // {next()} {value,done}
    // person[Symbol.iterator] = () => {
    //   let index = 0;
    //   return {
    //     next() {
    //       index++;
    //       if (index === 1) {
    //         return {
    //           value: person.age,
    //           done: false
    //         };
    //       } else if (index === 2) {
    //         return {
   //           value: person.sex,
   //           done: false
   //         };
   //       } else {
   //         return {
   //           done: true
   //         };
   //       }
   //     }
   //   };
   // };
   // for (const item of person) {
   //   console.log(item);
   // }
   // for in
   // 有 length 和索引属性的对象
    //  const obj = {
    //    '0': 'alex',
    //    '1': 'male',
    //    length: 2
    //  };
    //  obj[Symbol.iterator] = Array.prototype[Symbol.iterator];
     // obj[Symbol.iterator] = () => {
   //   let index = 0;
   //   return {
   //     next() {
   //       let value, done;
   //       if (index < obj.length) {
   //         value = obj[index];
   //         done = false;
   //       } else {
   //         value = undefined;
   //         done = true;
   //       }
   //       index++;
   //       return {
   //         value,
   //         done
   //       };
   //     }
   //   };
      // };
   //    for (const item of obj) {
       //    console.log(item);
   //    }

//使用了 Iterator 的场合
         // 原生可遍历的
         // Array 数组
         // String 字符串
         // Set
         // Map
         // 函数的 arguments 对象
         // NodeList 对象
         // for...of
         // 1.数组的展开运算符
         // console.log(...[1, 2, 3]);
         // console.log(1, 2, 3);
         // console.log(...'str');
         // console.log(...new Set([1, 2, 3]));
         // console.log(...{}); ×
         // 2.数组的解构赋值
         // const [a, b] = [1, 2];
         // const [a, b] = [...[1, 2]];
         // const [a, b] = 'hi';
         // const [a, b] = [...'hi'];
         // const [a, b] = [...new Set([3, 4])];
         // console.log(a, b);
         // 3.Set 和 Map 的构造函数
         // new Set(iterator)
         // new Map(iterator)
// 字符串新增的方法
         // 判断字符串中是否含有某些字符
         // 1.基本用法
         // console.log('abc'.includes('a'));
         // console.log('abc'.includes('ab'));
         // console.log('abc'.includes('bc'));
         // console.log('abc'.includes('ac')); // false
         // 2.第二个参数
         // 表示开始搜索的位置,默认是 0
         // console.log('abc'.includes('a'));
         // console.log('abc'.includes('a', 0));
         // console.log('abc'.includes('a', 1)); // false
         // 3.应用
         // https://www.imooc.com/course/list
         // https://www.imooc.com/course/list?c=fe&sort=pop&name=value
        //  let url = 'https://www.imooc.com/course/list?';
        //  const addURLParam = (url, name, value) => {
        //    url += url.includes('?') ? '&' : '?';
        //    url += `${name}=${value}`;
        //    return url;
        //  };
        //  url = addURLParam(url, 'c', 'fe');
        //  console.log(url);
        //  url = addURLParam(url, 'sort', 'pop');
        //  console.log(url);
// padStart() 和 padEnd()
            // 补全字符串长度
            // 1.基本用法
            // console.log('x'.padStart(5, 'ab'));
            // console.log('x'.padEnd(5, 'ab'));
            // console.log('x'.padEnd(4, 'ab'));
            // 2.注意事项
            // 原字符串的长度,等于或大于最大长度,不会消减原字符串,字符串补全不生效,返回原字符串
            // console.log('xxx'.padStart(2, 'ab'));
            // console.log('xxx'.padEnd(2, 'ab'));
            // 用来补全的字符串与原字符串长度之和超过了最大长度,截去超出位数的补全字符串,原字符串不动
            // console.log('abc'.padStart(10, '0123456789'));
            // console.log('abc'.padEnd(10, '0123456789'));
            // 如果省略第二个参数,默认使用空格补全长度
            // console.log('x'.padStart(4));
            // console.log('x'.padEnd(4));
            // 3.应用
            // 显示日期格式
            // 2020
            // 10
            // 10
            // 2020-10-10
            // 2020-01-01
            // console.log('10'.padStart(2, 0));
            // console.log('1'.padStart(2, 0));
// trimStart() 和 trimEnd()
          // 清除字符串的首或尾空格,中间的空格不会清除
          // 1.基本用法
          // const s = '  a b c  ';
          // console.log(s);
          // // console.log(s.trimStart());
          // // console.log(s.trimLeft());
          // // console.log(s.trimEnd());
          // // console.log(s.trimRight());
          // console.log(s.trim());
          // 2.应用
          // const usernameInput = document.getElementById('username');
          // const btn = document.getElementById('btn');
          // btn.addEventListener(
          //   'click',
          //   () => {
              // console.log(usernameInput.value);
              // 验证
              // console.log(usernameInput.value.trim());
              // if (usernameInput.value.trim() !== '') {
              //   可以提交
              //   console.log('可以提交');
              // } else {
              //   不能提交
              //   console.log('不能提交');
              // }
              // 手动提交
          //   },
          //   false
          // );
//数组的方法 
           // 1.基本用法
         // 判断数组中是否含有某个成员
         // console.log([1, 2, 3].includes('2')); // false
         // console.log([1, 2, 3].includes(2));
         // 第二个参数表示搜索的起始位置,默认值是 0
         // console.log([1, 2, 3].includes(2, 2));
         // 基本遵循严格相等(===),但是对于 NaN 的判断与 === 不同,includes 认为 NaN === NaN
         // console.log(NaN === NaN); 
         // console.log([1, 2, NaN].includes(NaN));
         // 2.应用
         // 去重
         // [1, 2, 1];
        //  const arr = [];
        //  for (const item of [1, 2, 1]) {
        //    if (!arr.includes(item)) {
            //  arr.push(item);
        //    }
        //  }
        //  console.log(arr);
// Array.from()
      // 将其他数据类型转换成数组
      // 1.基本用法
      // console.log(Array.from('str'));
      // 2.哪些可以通过 Array.from() 转换成数组
      // 2.1.所有可遍历的
      // 数组、字符串、Set、Map、NodeList、arguments
      // console.log(Array.from(new Set([1, 2, 1])));
      // console.log([...new Set([1, 2, 1])]);
      // 2.2.拥有 length 属性的任意对象
      // const obj = {
      //   '0': 'a',
      //   '1': 'b',
      //   name: 'Alex',
      //   length: 3
      // };
      // console.log(Array.from(obj));
      // console.log([...obj]); // ×
      // 3.第二个参数
      // 作用类似于数组的 map 方法,用来对每个元素进行处理,将处理后的值放入返回的数组
      // console.log(
      //   [1, 2].map(value => {
      //     return value * 2;
         //   })
         // );
         // console.log(Array.from('12', value => value * 2));
         // console.log(Array.from('12').map(value => value * 2));
         // 4.第三个参数
       //   Array.from(
           // '12',
           // value => {
           //   console.log(this);
           // },
           // document
       //   );
           // Array.from(
       //  '12',
       //  function () {
       //    console.log(this);
       //  },
       //  document
       // );
// find() 和 findIndex()
     // find():找到满足条件的一个立即返回
     // findIndex():找到满足条件的一个,立即返回其索引
     // 1.基本用法
     // console.log(
     //   [1, 5, 10, 15].find((value, index, arr) => {
     //     // console.log(value, index, arr);
     //     console.log(this);
     //     return value > 9;
     //   }, document)
     // );
     // [1, 5, 10, 15].find(function (value, index, arr) {
     //   // console.log(value, index, arr);
     //   console.log(this);
     //   return value > 9;
     // }, document);
     // console.log(
     //   [1, 5, 10, 15].findIndex((value, index, arr) => {
     //     // console.log(value, index, arr);
     //     return value > 9;
     //   }, document)
     // );
     // 2.应用
    // const students = [
    //   {
    //     name: '张三',
    //     sex: '男',
    //     age: 16
    //   },
    //   {
    //     name: '李四',
    //     sex: '女',
    //     age: 22
    //   },
    //   {
    //     name: '王二麻子',
    //     sex: '男',
    //     age: 32
    //   }
    // ];
    // console.log(students.find(value => value.sex === '女'));
    // console.log(students.findIndex(value => value.sex === '女'));
// 对象的新增方法
// Object.assign()  
         // 用来合并对象
         // 1.基本用法
         // Object.assign(目标对象, 源对象1,源对象2,...): 目标对象
         // const apple = {
         //   color: '红色',
         //   shape: '圆形',
         //   taste: '甜'
         // };
         // const pen = {
         //   color: '黑色',
         //   shape: '圆柱形',
         //   use: '写字'
         // };
         // console.log(Object.assign(apple, pen));
         // Object.assign 直接合并到了第一个参数中,返回的就是合并后的对象
         // console.log(apple);
         // console.log(Object.assign(apple, pen) === apple);
         // 可以合并多个对象
         // console.log(Object.assign({}, apple, pen));
         // console.log(apple);
         // console.log({ ...apple, ...pen });
         // 2.注意事项
         // 2.1.基本数据类型作为源对象
        // 与对象的展开类似,先转换成对象,再合并
        // console.log(Object.assign({}, undefined));
        // console.log(Object.assign({}, null));
        // console.log(Object.assign({}, 1));
        // console.log(Object.assign({}, true));
        // console.log(Object.assign({}, 'str'));
        // 2.2.同名属性的替换
        // 后面的直接覆盖前面的
        // const apple = {
        //   color: ['红色', '黄色'],
        //   shape: '圆形',
        //   taste: '甜'
        // };
        // const pen = {
        //   color: ['黑色', '银色'],
        //   shape: '圆柱形',
        //   use: '写字'
        // };
        // console.log(Object.assign({}, apple, pen));
        // 3.应用
        // 合并默认参数和用户参数
        // const logUser = userOptions => {
        //   const DEFAULTS = {
            // username: 'ZhangSan',
            // age: 0,
            // sex: 'male'
                    // };
                    // const options = Object.assign({}, DEFAULTS, userOptions);
            // const options = Object.assign({}, DEFAULTS, undefined);
            // console.log(options);
        //   };
        //   logUser();
          // logUser({});
          // logUser({ username: 'Alex' });
//  Object.keys()、Object.values() 和 Object.entries()
     // 1.基本用法
      // const person = {
      //   name: 'Alex',
      //   age: 18
      // };
      // console.log(Object.keys(person));
      // console.log(Object.values(person));
      // console.log(Object.entries(person));
      // 2.与数组类似方法的区别
      // console.log([1, 2].keys());
      // console.log([1, 2].values());
      // console.log([1, 2].entries());
      // console.log(person.keys);
      // 数组的 keys()、values()、entries() 等方法是实例方法,返回的都是 Iterator
      // 对象的 Object.keys()、Object.values()、Object.entries() 等方法是构造函数方法,返回的是数组
      // 3.使用 for...of 循环遍历对象
    //   const person = {
        // name: 'Alex',
        // age: 18
    //   };
      // for (const key of Object.keys(person)) {
      //   console.log(key);
      // }
      // for (const value of Object.values(person)) {
      //   console.log(value);
      // }
             // for (const entries of Object.entries(person)) {
             //   console.log(entries);
             // }
        // for (const [key, value] of Object.entries(person)) {
        //   console.log(key, value);
        // }
        // Object.keys()/values()/entires() 并不能保证顺序一定是你看到的样子,这一点和 for in 是一样的
// Promise        
      // 1.认识 Promise
       // Promise 是异步操作的一种解决方案
       // 回调函数
       // document.addEventListener(
       //   'click',
       //   () => {
       //     console.log('这里是异步的');
       //   },
       //   false
       // );
       // console.log('这里是同步的');
       // 2.什么时候使用 Promise
       // Promise 一般用来解决层层嵌套的回调函数(回调地狱 callback hell)的问题
       // 运动
      //    const move = (el, { x = 0, y = 0 } = {}, end = () => {}) => {
          //  el.style.transform = `translate3d(${x}px, ${y}px, 0)`;
          //  el.addEventListener(
          //    'transitionend',
          //    () => {
              //  console.log('end');
              //  end();
          //    },
          //    false
          //  );
      //    };
      //    const boxEl = document.getElementById('box');
      //    document.addEventListener(
          //  'click',
          //  () => {
          //    move(boxEl, { x: 150 }, () => {
              //  move(boxEl, { x: 150, y: 150 }, () => {
              //    move(boxEl, { y: 150 }, () => {
                  //  console.log('object');
                  //  move(boxEl, { x: 0, y: 0 });
              //    });
              //  });
          //    });
             //  },
         //    false
// Promise 的基本用法
      // 1.实例化构造函数生成实例对象
      // console.log(Promise);

      // Promise 解决的不是回调函数,而是回调地狱
      // const p = new Promise(() => {});

      // 2.Promise 的状态
      // const p = new Promise((resolve, reject) => {
      //   // Promise 有 3 种状态,一开始是 pending(未完成),执行 resolve,变成 fulfilled(resolved),已成功
      //   // 执行 reject,变成 rejected,已失败

      //   // Promise 的状态一旦变化,就不会再改变了

      //   // pending->fulfilled
      //   // resolve();

      //   // pending->rejected
      //   reject();
      // });

      // 3.then 方法
      // p.then(
      //   () => {
      //     console.log('success');
      //   },
      //   () => {
      //     console.log('error');
      //   }
      // );

      // 4.resolve 和 reject 函数的参数
    //   const p = new Promise((resolve, reject) => {
        // Promise 有 3 种状态,一开始是 pending(未完成),执行 resolve,变成 fulfilled(resolved),已成功
        // 执行 reject,变成 rejected,已失败

        // Promise 的状态一旦变化,就不会再改变了

        // pending->fulfilled
        // resolve('succ');
        // resolve({ username: 'alex' });

        // pending->rejected
        // reject('reason');
        // reject(new Error('reason'));
    //   });
    //   p.then(
        // data => {
        //   console.log('success', data);
        // },
        // err => {
        //   console.log('error', err);
        // }
    //   );

    //   console.log(p);
// Promise 的注意事项
         // 1.resolve 或 reject 函数执行后的代码
         // 推荐在调用 resolve 或 reject 函数的时候加上 return,不再执行它们后面的代码
         // new Promise((resolve, reject) => {
         //   // return resolve(123);
         //   return reject('reason');
         //   console.log('hi');
         // });
         // 2.Promise.all/race/allSettled 的参数问题
         // 参数如果不是 Promise 数组,会将不是 Promise 的数组元素转变成 Promise 对象
         // Promise.all([1, 2, 3]).then(datas => {
         //   console.log(datas);
         // });
         // 等价于
         // Promise.all([
         //   Promise.resolve(1),
         //   Promise.resolve(2),
         //   Promise.resolve(3)
                 // ]).then(datas => {
         //   console.log(datas);
         // });
         // 不只是数组,任何可遍历的都可以作为参数
         // 数组、字符串、Set、Map、NodeList、arguments
         // Promise.all(new Set([1, 2, 3])).then(datas => {
         //   console.log(datas);
         // });
         // 3.Promise.all/race/allSettled 的错误处理
        //  const delay = ms => {
        //    return new Promise(resolve => {
            //  setTimeout(resolve, ms);
        //    });
        //  };
        //  const p1 = delay(1000).then(() => {
        //    console.log('p1 完成了');
        //    return 'p1';
        //    return Promise.reject('reason');
        //  });
     // .catch(err => {
     //   console.log('p1', err);
     // });
    //  const p2 = delay(2000).then(() => {
    //    console.log('p2 完成了');
    //    return 'p2';
       // return Promise.reject('reason');
    //  });
     // // .catch(err => {
     // //   console.log('p2', err);
     // });
    //  const allPromise = Promise.all([p1, p2]);
    //  allPromise
    //    .then(datas => {
        //  console.log(datas);
    //    })
    //    .catch(err => console.log(err));
     // 错误既可以单独处理,也可以统一处理
     // 一旦被处理,就不会在其他地方再处理一遍

// Promise的应用
    //   异步加载图片
    //   const loadImgAsync = url => {
        // return new Promise((resolve, reject) => {
        //   const img = new Image();
        //   img.onload = () => {
            // resolve(img);
        //   };
        //   img.onerror = () => {
            // reject(new Error(`Could not load image at ${url}`));
        //   };
        //   img.src = url;
        // });
    //   };
    //   const imgDOM = document.getElementById('img');
    //   loadImgAsync('https://2img.mukewang.com/5f057a6a0001f4f918720764.jpg')
        // .then(img => {
        //   console.log(img.src);
        //   setTimeout(() => {
            // imgDOM.src = img.src;
        //   }, 1000);
        // })
        // .catch(err => {
        //   console.log(err);
        // });
// 1.什么时候执行
  // pending->fulfilled 时,执行 then 的第一个回调函数
  // pending->rejected 时,执行 then 的第二个回调函数
  // 2.执行后的返回值
  // then 方法执行后返回一个新的 Promise 对象
  // const p = new Promise((resolve, reject) => {
  //   resolve();
  //   // reject();
  // });
  // const p2 = p
  //   .then(
  //     () => {},
  //     () => {}
  //   )
  //   .then()
  //   .then();
  // console.log(p, p2, p === p2);
  // 3.then 方法返回的 Promise 对象的状态改变
  // const p = new Promise((resolve, reject) => {
  //   // resolve();
  //   reject();
  // });
  // p.then(
  //   () => {
  //     // console.log('success');
  //   },
  //   () => {
  //     console.log('err');
  //     // 在 then 的回调函数中,return 后面的东西,会用 Promise
  //     // return undefined;
  //     // 等价于
  //     // return new Promise(resolve => {
  //     //   resolve(undefined);
  //     // });
  //     return 123;
  //     // return new Promise(resolve => {
  //     //   resolve(123);
  //     // });
  //     // 默认返回的永远都是成功状态的 Promise 对象
  //     // return new Promise((resolve, reject) => {
  //     //   reject('reason');
  //     // });
  //   }
  // )
  //   .then(
  //     data => {
  //       console.log('success2', data);
  //       // return undefined;
  //       return new Promise(resolve => {
  //         resolve(undefined);
  //       });
  //     },
  //     err => {
  //       console.log('err2', err);
  //     }
  //   )
    //   .then(
  //     data => {
  //       console.log('success3', data);
  //     },
  //     err => {
  //       console.log('err3', err);
  //     }
  //   );
  // 4.使用 Promise 解决回调地狱
  // 运动
    //  const move = (el, { x = 0, y = 0 } = {}, end = () => {}) => {
    //    el.style.transform = `translate3d(${x}px, ${y}px, 0)`;
    //    el.addEventListener(
        //  'transitionend',
        //  () => {
        //    end();
        //  },
        //  false
    //  );
//    };
//    const boxEl = document.getElementById('box');
//    const movePromise = (el, point) => {
    //  return new Promise(resolve => {
    //    move(el, point, () => {
        //  resolve();
    //    });
    //  });
//    };
//    document.addEventListener(
    //  'click',
    //  () => {
    //    movePromise(boxEl, { x: 150 })
        //  .then(() => {
        //    return movePromise(boxEl, { x: 0, y: 0 });
    //   })
    //   .then(() => {
        // return movePromise(boxEl, { x: 150, y: 150 });
    //   })
    //   .then(() => {
        // return movePromise(boxEl, { y: 150 });
            //  });
        //  },
        //  false
    //    );
    // document.addEventListener(
    //   'click',
    //   () => {
    //     move(boxEl, { x: 150 }, () => {
    //       move(boxEl, { x: 150, y: 150 }, () => {
    //         move(boxEl, { y: 150 }, () => {
    //           move(boxEl, { x: 0, y: 0 });
    //         });
      //       });
      //     });
      //   },
      //   false
      // );   
// catch()
     // 1.有什么用
     // then(
     //   data => {},
     //   err => {}
     // );
     // then(data => {});
     // catch 专门用来处理 rejected 状态
     // catch 本质上是 then 的特例
     // then(null, err => {});
     // 2.基本用法
    //  new Promise((resolve, reject) => {
       // resolve(123);
    //    reject('reason');
    //  })
    //    .then(data => {
        //  console.log(data);
    //    })
       // .then(null, err => {
       //   console.log(err);
       // });
    //    .catch(err => {
        //  console.log(err);
        //  return undefined;
        //  throw new Error('reason');
    //    })
    //    .then(data => {
        //  console.log(data);
            // })
            // .catch(err => {
        //  console.log(err);
    //    });
     // catch() 可以捕获它前面的错误
     // 一般总是建议,Promise 对象后面要跟 catch 方法,这样可以处理 Promise 内部发生的错误
 
// finally()   
     // 1.什么时候执行
     // 当 Promise 状态发生变化时,不论如何变化都会执行,不变化不执行
     // new Promise((resolve, reject) => {
     //   // resolve(123);
     //   reject('reason');
     // })
     //   .finally(data => {
     //     console.log(data);
     //   })
     //   .catch(err => {});
     // 2.本质
     // finally() 本质上是 then() 的特例
     // new Promise((resolve, reject) => {
     //   // resolve(123);
     //   reject('reason');
     // })
     //   .finally(data => {
     //     console.log(data);
     //   })
     //   .catch(err => {});
    //    等同于
    //    new Promise((resolve, reject) => {
        //  resolve(123);
        //  reject('reason');
    //    })
        //  .then(
        //    result => {
            //  return result;
        //    },
    //    err => {
        //  return new Promise((resolve, reject) => {
        //    reject(err);
        //  });
    //    }
    //  )
    //  .then(data => {
    //    console.log(data);
    //  })
    //  .catch(err => {
    //    console.log(err);
    //  });
// Promise.resolve() 和 Promise.reject() 
      // 1.Promise.resolve()
       // 是成功状态 Promise 的一种简写形式
       // new Promise(resolve => resolve('foo'));
       // // 简写
       // Promise.resolve('foo');
       // 参数
       // 一般参数
       // Promise.resolve('foo').then(data => {
       //   console.log(data);
       // });
       // Promise
       // 当 Promise.resolve() 接收的是 Promise 对象时,直接返回这个 Promise 对象,什么都不做
       // const p1 = new Promise(resolve => {
       //   setTimeout(resolve, 1000, '我执行了');
       //   // setTimeout(() => {
             //   //   resolve('我执行了');
       //   // }, 1000);
       // });
       // Promise.resolve(p1).then(data => {
       //   console.log(data);
       // });
       // 等价于
       // p1.then(data => {
       //   console.log(data);
       // });
       // console.log(Promise.resolve(p1) === p1);
       // 当 resolve 函数接收的是 Promise 对象时,后面的 then 会根据传递的 Promise 对象的状态变化决定执行
       // new Promise(resolve => resolve(p1)).then(data => {
       //   console.log(data);
       // });
       // 具有 then 方法的对象
       // function func(obj) {
       //   obj.then(1, 2);
       // }
       // func({
       //   then(resolve, reject) {
       //     console.log(a, b);
       //   }
       // });
             // const thenable = {
       //   then(resolve, reject) {
       //     console.log('then');
       //     resolve('data');
       //     // reject('reason');
       //   }
       // };
       // Promise.resolve(thenable).then(
       //   data => console.log(data),
       //   err => console.log(err)
       // );
       // console.log(Promise.resolve(thenable));
       // 2.Promise.reject()
       // 失败状态 Promise 的一种简写形式
       // new Promise((resolve, reject) => {
       //   reject('reason');
       // });
       // 等价于
       // Promise.reject('reason');
       // 参数
       // 不管什么参数,都会原封不动地向后传递,作为后续方法的参数
       // const p1 = new Promise(resolve => {
       //   setTimeout(resolve, 1000, '我执行了');
       // });
       // Promise.reject(p1).catch(err => console.log(err));
    //    new Promise((resolve, rejcet) => {
        //  resolve(123);
    //    })
        //  .then(data => {
           // return data;
           // return Promise.resolve(data);
        //    return Promise.reject('reason');
        //  })
        //  .then(data => {
        //    console.log(data);
        //  })
    //    .catch(err => console.log(err));
// Promise.all()
    // 1.有什么用
    // Promise.all() 关注多个 Promise 对象的状态变化
    // 传入多个 Promise 实例,包装成一个新的 Promise 实例返回
    // 2.基本用法
    // const delay = ms => {
    //   return new Promise(resolve => {
        // setTimeout(resolve, ms);
    //   });
    // };
    // const p1 = delay(1000).then(() => {
    //   console.log('p1 完成了');
    //   return 'p1';
    //   return Promise.reject('reason');
    // });
    // const p2 = delay(2000).then(() => {
    //   console.log('p2 完成了');
    //   return 'p2';
    //   return Promise.reject('reason');
    // });
    // Promise.all() 的状态变化与所有传入的 Promise 实例对象状态有关
    // 所有状态都变成 resolved,最终的状态才会变成 resolved
    // 只要有一个变成 rejected,最终的状态就变成 rejected
    // const p = Promise.all([p1, p2]);
    // p.then(
    //   data => {
        // console.log(data);
    //   },
    //   err => {
        // console.log(err);
    //   }
    // );
// Promise.race() 和 Promise.allSettled()
    //    const delay = ms => {
        //    return new Promise(resolve => {
            //  setTimeout(resolve, ms);
        //    });
        //  };
        //  const p1 = delay(1000).then(() => {
        //    console.log('p1 完成了');
        //    return 'p1';
        //    return Promise.reject('reason');
        //  });
        //  const p2 = delay(2000).then(() => {
        //    console.log('p2 完成了');
        //    return 'p2';
        //    return Promise.reject('reason');
        //  });
      // 1.Promise.race()
      // Promise.race() 的状态取决于第一个完成的 Promise 实例对象,如果第一个完成的成功了,那
      // const racePromise = Promise.race([p1, p2]);
      // racePromise.then(
      //   data => {
      //     console.log(data);
      //   },
      //   err => {
      //     console.log(err);
         //   }
         // );
              // 2.Promise.allSettled()
          // Promise.allSettled() 的状态与传入的Promise 状态无关
          // 永远都是成功的
          // 它只会忠实的记录下各个 Promise 的表现
        //   const allSettledPromise = Promise.allSettled([p1, p2]);
        //   allSettledPromise.then(data => {
            // console.log('succ', data);
        //   });
// class类继承
       // 1.认识 Class
      // 人类:类
      // 具体的人:实例、对象

      // 类可以看做是对象的模板,用一个类可以创建出许多不同的对象

      // 2.Class 的基本用法
      // 类名一般大写
      // class Person {} √
      // class Person() {} ×
      // class Person {}; ×
      // function func() {}

      // class Person {
      //   // 实例化时执行构造方法,所以必须有构造方法,但可以不写出来
      //   constructor(name, age) {
      //     // console.log('实例化时执行构造方法');
      // this 代表实例对象,上面定义的是实例属性/方法
      //     this.name = name;
      //     this.age = age;

      //     // 一般在构造方法中定义属性,方法不在构造方法中定义
      //     // this.speak = () => {};
      //   }

      //   // speak:function(){}

      //   // 各实例共享的方法
      //   speak() {
      //     console.log('speak');
      //   }
      // }

      // // Person();
      // const zs = new Person('ZS', 18);
      // const ls = new Person('LS', 28);
      // // console.log(zs.name);
      // // console.log(zs.age);
      // // console.log(zs.speak);
      // zs.speak();
      // // console.log(ls.name);
      // // console.log(ls.age);
      // // console.log(ls.speak);
      // console.log(zs.speak === ls.speak);

      // 3.Class 与构造函数
    //   class Person {
        // constructor(name, age) {
        //   this.name = name;
        //   this.age = age;

          // this.speak = () => {};
        // }

        // speak() {
        //   console.log('speak');
        // }

        // run(){}
      //   }
      //   Person.prototype.run = function () {};

      // console.log(typeof Person);
      // console.log(Person.prototype.speak);

      // function Person(name, age) {
      //   this.name = name;
      //   this.age = age;

      //   // this.speak = () => {};
      // }
      // Person.prototype.speak = function () {};
//  实例属性、静态方法和静态属性 
    // 1.实例属性
    // 方法就是值为函数的特殊属性
    // class Person {
    //   age = 0;
    //   sex = 'male';
    //   getSex = function () {
    //     return this.sex;
    //   };
    //   constructor(name, sex) {
    //     this.name = name;
    //     // this.age = 18;
    //     this.sex = sex;
    //   }
    //   // speak() {
    //   //   this.age = 18;
    //   // }
    // }
    // const p = new Person('Alex');
    // console.log(p.name);
    // console.log(p.age);
    // 2.静态方法
    // 类的方法
    // class Person {
    //   constructor(name) {
    //     this.name = name;
    //   }
    //   speak() {
    //     console.log('speak');
    //     console.log(this);
    //   }
    //   // static speak() {
    //   //   console.log('人类可以说话');
    //   //   // this 指向类
    //   //   console.log(this);
    //   // }
    // }
    // // Person.speak = function () {
    // //   console.log('人类可以说话');
     // //   console.log(this);
     // // };
     // const p = new Person('Alex');
     // p.speak();
     // Person.speak();
     // 3.静态属性
     // 类的属性
       // class Person {
       //   constructor(name) {
           // this.name = name;
       //   }
         //  不要这么写,目前只是提案,有兼容性问题
         // static version = '1.0';
       //   static getVersion() {
           // return '1.0';
       //   }
       // }
     // Person.version = '1.0';
     //    const p = new Person('Alex');
     //    console.log(p.name);
        // console.log(Person.version);
     //    console.log(Person.getVersion());
// 私有属性和方法
    // 1.为什么需要私有属性和方法
    // 一般情况下,类的属性和方法都是公开的
    // 公有的属性和方法可以被外界修改,造成意想不到的错误
    // class Person {
    //   constructor(name) {
    //     this.name = name;
    //   }
    //   speak() {
    //     console.log('speak');
    //   }
    //   getName() {
    //     return this.name;
    //   }
    // }
    // const p = new Person('Alex');
    // console.log(p.name);
    // p.speak();
    // // ....
    // p.name = 'zs';
    // console.log(p.name);
    // 2.模拟私有属性和方法
    // 2.1._ 开头表示私有
    // class Person {
    //   constructor(name) {
    //     this._name = name;
    //   }
    //   speak() {
    //     console.log('speak');
    //   }
    //   getName() {
    //     return this._name;
    //   }
    // }
    // const p = new Person('Alex');
    // // console.log(p.name);
    // p.name = 'zd';
    // console.log(p.getName());
    // 2.2.将私有属性和方法移出类
    // (function () {
    //   let name = '';
    //   class Person {
        // constructor(username) {
        //   this.name = name;
        //   name = username;
        // }
        // speak() {
        //   console.log('speak');
        // }
            // getName() {
            //   return name;
            // }
        //   }
        //   window.Person = Person;
    // })();
    // (function () {
    //   const p = new Person('Alex');
    //   console.log(p.name);
    //   console.log(p.getName());
    // })();
// class类继承   
               // 1.子类继承父类
       // class Person {
       //   constructor(name, sex) {
           // this.name = name;
           // this.sex = sex;
           // this.say = function () {
           //   console.log('say');
           // };
       //   }
       //   speak() {
           // console.log('speak');
       //   }
       //   static speak() {
           // console.log('static speak');
       //   }
       // }
       // Person.version = '1.0';
       // class Programmer extends Person {
       //   constructor(name, sex) {
       //     super(name, sex);
       //   }
       // }
       // const zs = new Programmer('zs', '男');
       // console.log(zs.name);
       // console.log(zs.sex);
       // zs.say();
       // zs.speak();
       // Programmer.speak();
      // console.log(Programmer.version);
      // 2.改写继承的属性或方法
    //   class Programmer extends Person {
        // constructor(name, sex, feature) {
          // this.feature = feature; ×
          // this 操作不能放在 super 前面
        //   super(name, sex);
          // this.feature = feature;
        // }
        // hi() {
        //   console.log('hi');
        // }
        // 同名覆盖
        // speak() {
        //   console.log('Programmer speak');
        // }
        // static speak() {
        //   console.log('Programmer static speak');
        // }
    //   }
    //   Programmer.version = '2.0';
    //   const zs = new Programmer('zs', '男', '秃头');
    //   console.log(zs.name);
    //   console.log(zs.sex);
    //   console.log(zs.feature);
    //   zs.say();
    //   zs.speak();
    //   zs.hi();
    //   Programmer.speak();
    //   console.log(Programmer.version);
// super
          // 1.作为函数调用
    // 代表父类的构造方法,只能用在子类的构造方法中,用在其他地方就会报错
    // super 虽然代表了父类的构造方法,但是内部的 this 指向子类的实例
    // class Person {
    //   constructor(name) {
    //     this.name = name;
    //     console.log(this);
    //   }
    // }
    // class Programmer extends Person {
    //   constructor(name, sex) {
    //     super(name, sex);
    //   }
    //   // hi() {
    //   //   super(); // ×
    //   // }
    // }
    // // new Person();
    // new Programmer();
       // 2.作为对象使用
       // 2.1.在构造方法中使用或一般方法中使用
   // super 代表父类的原型对象 Person.prototype
   // 所以定义在父类实例上的方法或属性,是无法通过 super 调用的
   // 通过 super 调用父类的方法时,方法内部的 this 指向当前的子类实例
   // class Person {
   //   constructor(name) {
   //     this.name = name;
   //     console.log(this);
   //   }
   //   speak() {
   //     console.log('speak');
   //     // console.log(this);
   //   }
   //   static speak() {
   //     console.log('Person speak');
   //     console.log(this);
   //   }
      // }
   // class Programmer extends Person {
   //   constructor(name, sex) {
   //     super(name, sex);
   //     // console.log(super.name);
   //     // super.speak();
   //   }
   //   // hi() {
   //   //   super(); // ×
   //   // }
   //   speak() {
   //     super.speak();
   //     console.log('Programmer speak');
   //   }
   //   // 2.2.在静态方法中使用
   //   // 指向父类,而不是父类的原型对象
   //   // 通过 super 调用父类的方法时,方法内部的 this 指向当前的子类,而不是子类的实例
   //   static speak() {
   //     super.speak();
   //     console.log('Programmer speak');
   //   }
   // }
   // // new Person();
   // // new Programmer();
   // Programmer.speak();
   // 3.注意事项
      // 使用 super 的时候,必须显式指定是作为函数还是作为对象使用,否则会报错
    //   class Person {
        // constructor(name) {
        //   this.name = name;
        // }
        // speak() {
    //    console.log('speak');
       //  }
   //    }
   //    class Programmer extends Person {
       //  constructor(name, sex) {
       //    super(name, sex);
          // console.log(super);
          // console.log(super());
          // console.log(super.speak);
       //  }
   //    }


 // 1.前后端通信是什么
      // 前端和后端数据交互的过程
      // 浏览器和服务器之间数据交互的过程
      // 2.后端向前端发送数据
      // 访问页面
      // 3.前端向后端发送数据
      // 用户注册
 // 1.前后端通信的过程
         // 前后端的通信是在‘请求-响应’中完成的
         // 2.概念解释
         // 前端:浏览器端
         // 客户端:只要能和服务器通信的就叫客户端
         // 命令行工具
         // curl https:www.imooc.com
         // 后端:服务器端
 // 1.使用浏览器访问网页
         // 在浏览器地址栏输入网址,按下回车
         // 2.HTML 的标签
         // 浏览器在解析 HTML 标签的时候,遇到一些特殊的标签,会再次向服务器发送请求
         // link/img/script/iframe
         // 还有一些标签,浏览器解析的时候,不会向服务器发送请求,但是用户可以使用他们向服务器发
         // a/form
         // 3.Ajax 和 Fetch
// localStorage
      // 1.localStorage 是什么
      // localStorage 也是一种浏览器存储数据的方式(本地存储),它只是存储在本地,不会发送到服务器端
      // 单个域名下的 localStorage 总大小有限制
      // 2.在浏览器中操作 localStorage
      // 3.localStorage 的基本用法
      // console.log(localStorage);
      // // setItem()
      // localStorage.setItem('username', 'alex');
      // localStorage.setItem('username', 'zs');
      // localStorage.setItem('age', 18);
      // localStorage.setItem('sex', 'male');
      // // length
      // // console.log(localStorage.length);
      // // getItem()
      // // console.log(localStorage.getItem('username'));
      // // console.log(localStorage.getItem('age'));
      // // // 获取不存在的返回 null
      // // console.log(localStorage.getItem('name'));
      // // removeItem()
      // // localStorage.removeItem('username');
      // // localStorage.removeItem('age');
      // // // 删除不存在的 key,不报错
      // // localStorage.removeItem('name');
      // // clear()
      // localStorage.clear();
      // console.log(localStorage);
      // 4.使用 localStorage 实现自动填充
    //   const loginForm = document.getElementById('login');
    //   const btn = document.getElementById('btn');
    //   const username = localStorage.getItem('username');
    //   if (username) {
        // loginForm.username.value = username;
    //   }
    //   btn.addEventListener(
        // 'click',
        // e => {
        //   e.preventDefault();
          // 数据验证
        //   console.log(loginForm.username.value);
        //   localStorage.setItem('username', loginForm.username.value);
        //   loginForm.submit();
          // },
          // false
      //   );
// localStorage 的注意事项
       // 1.localStorage 的存储期限
       // localStorage 是持久化的本地存储,除非手动清除(比如通过 js 删除,或者清除浏览器缓存),否则数据是永远不会过期的
       // sessionStorage
       // 当会话结束(比如关闭浏览器)的时候,sessionStorage 中的数据会被清空
       // sessionStorage.setItem('username', 'alex');
       // sessionStorage.getItem('username');
       // sessionStorage.removeItem('username');
       // sessionStorage.clear();
       // 2.localStorage 键和值的类型
       // localStorage 存储的键和值只能是字符串类型
       // 不是字符串类型,也会先转化成字符串类型再存进去
       // localStorage.setItem({}, 18);
       // // localStorage.setItem('students', [{},{}]);
       // console.log(
       //   typeof localStorage.getItem('[object Object]'),
       //   localStorage.getItem('[object Object]')
       // );
       // console.log({}.toString());
       // 3.不同域名下能否共用 localStorage
       // 不同的域名是不能共用 localStorage 的
       // 4.localStorage 的兼容性
       // IE7及以下版本不支持 localStorage,IE8 开始支持
       // caniuse.com
 // 1.Cookie 是什么
         // Cookie 全称 HTTP Cookie,简称 Cookie
         // 是浏览器存储数据的一种方式
         // 因为存储在用户本地,而不是存储在服务器上,是本地存储
         // 一般会自动随着浏览器每次请求发送到服务器端
         // 2.Cookie 有什么用
         // 利用 Cookie 跟踪统计用户访问该网站的习惯,比如什么时间访问,访问了哪些页面,在每个网页的停留时间等
         // 3.在浏览器中操作 Cookie
         // 不要在 Cookie 中保存密码等敏感信息
// Cookie 的基本用法
     // 1.写入 Cookie
     // document.cookie = 'username=zs';
     // document.cookie = 'age=18';
     // 不能一起设置,只能一个一个设置
     // document.cookie = 'username=zs; age=18';
     // 2.读取 Cookie
    //  console.log(document.cookie);
     // 读取的是一个由名值对构成的字符串,每个名值对之间由“; ”(一个分号和一个空格)隔开
     // username=zs; age=18
//Cookie 的属性
          // 1.Cookie 的名称(Name)和值(Value)
          // 最重要的两个属性,创建 Cookie 时必须填写,其它属性可以使用默认值
          // Cookie 的名称或值如果包含非英文字母,则写入时需要使用 encodeURIComponent() 编码,读取时使用 decodeURIComponent() 解
          // document.cookie = 'username=alex';
          // document.cookie = `username=${encodeURIComponent('张三')}`;
          // document.cookie = `${encodeURIComponent('用户名')}=${encodeURIComponent(
          //   '张三'
          // )}`;
          // 一般名称使用英文字母,不要用中文,值可以用中文,但是要编码
          // 2.失效(到期)时间
          // 对于失效的 Cookie,会被浏览器清除
          // 如果没有设置失效(到期)时间,这样的 Cookie 称为会话 Cookie
          // 它存在内存中,当会话结束,也就是浏览器关闭时,Cookie 消失
          // document.cookie = 'username=alex';
          // 想长时间存在,设置 Expires 或 Max-Age
          // expires
          // 值为 Date 类型
          // document.cookie = `username=alex; expires=${new Date(
          //   '2100-1-01 00:00:00'
          // )}`;
          // max-age
          // 值为数字,表示当前时间 + 多少秒后过期,单位是秒
          // document.cookie = 'username=alex; max-age=5';
          // document.cookie = `username=alex; max-age=${24 * 3600 * 30}`;
          // 如果 max-age 的值是 0 或负数,则 Cookie 会被删除
          // document.cookie = 'username=alex';
          // document.cookie = 'username=alex; max-age=0';
          // document.cookie = 'username=alex; max-age=-1';
          // 3.Domain 域
          // Domain 限定了访问 Cookie 的范围(不同域名)
          // 使用 JS 只能读写当前域或父域的 Cookie,无法读写其他域的 Cookie
          // document.cookie = 'username=alex; domain=www.imooc.com';
          // www.imooc.com m.imooc.com 当前域
          // 父域:.imooc.com
          // 4.Path 路径
          // Path 限定了访问 Cookie 的范围(同一个域名下)
          // 使用 JS 只能读写当前路径和上级路径的 Cookie,无法读写下级路径的 Cookie
          // document.cookie = 'username=alex; path=/course/list';
          // document.cookie = 'username=alex; path=/1.Cookie/';
          // 当 Name、Domain、Path 这 3 个字段都相同的时候,才是同一个 Cookie
          // 5.HttpOnly
          // 设置了 HttpOnly 属性的 Cookie 不能通过 JS 去访问
          // 6.Secure 安全标志
          // Secure 限定了只有在使用了 https 而不是 http 的情况下才可以发送给服务端
          // Domain、Path、Secure 都要满足条件,还不能过期的 Cookie 才能随着请求发送到服务器端
// Cookie 的注意事项  
            // 1.前后端都可以创建 Cookie
            // 2.Cookie 有数量限制
            // 每个域名下的 Cookie 数量有限
            // 当超过单个域名限制之后,再设置 Cookie,浏览器就会清除以前设置的 Cookie
            // 3.Cookie 有大小限制
            // 每个 Cookie 的存储容量很小,最多只有 4KB 左右
// 初识跨域  
              // 1.跨域是什么
              // 同域,不是跨域
              // const url = './index.html';
              // http://127.0.0.1:5500
              // 不同域,跨域,被浏览器阻止
              // const url = 'https://www.imooc.com';
              // const xhr = new XMLHttpRequest();
              // Access to XMLHttpRequest at 'https://www.imooc.com/' from origin 'http://127.0.0.1:5
              // 向一个域发送请求,如果要请求的域和当前域是不同域,就叫跨域
              // 不同域之间的请求,就是跨域请求
              // xhr.onreadystatechange = () => {
              //   if (xhr.readyState != 4) return;
              //   if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) {
              //     console.log(xhr.responseText);
              //   }
              // };
              // xhr.open('GET', url, true);
              // xhr.send(null);
              // 2.什么是不同域,什么是同域
              // https(协议)://www.imooc.com(域名):443(端口号)/course/list(路径)
              // 协议、域名、端口号,任何一个不一样,就是不同域
              // 与路径无关,路径一不一样无所谓
              // 不同域
              // https://www.imooc.com:443/course/list
              // http://www.imooc.com:80/course/list
              // http://www.imooc.com:80/course/list
              // http://m.imooc.com:80/course/list
              // http://imooc.com:80/course/list
              // 同域
              // http://imooc.com:80
              // http://imooc.com:80/course/list
              // 3.跨域请求为什么会被阻止
              // 阻止跨域请求,其实是浏览器本身的一种安全策略--同源策略
              // 其他客户端或者服务器都不存在跨域被阻止的问题
              // 4.跨域解决方案
              // ① CORS 跨域资源共享
              // ② JSONP
              // 优先使用 CORS 跨域资源共享,如果浏览器不支持 CORS 的话,再使用 JSONP
//  CORS 跨域资源共享 
           // 1.CORS 是什么
           // const url = 'https://www.imooc.com';
           // const url = 'https://www.imooc.com/api/http/search/suggest?words=js';
           // const xhr = new XMLHttpRequest();
           // xhr.onreadystatechange = () => {
           //   if (xhr.readyState != 4) return;
           //   if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) {
           //     console.log(xhr.responseText);
           //   }
           // };
           // xhr.open('GET', url, true);
           // xhr.send(null);
           // Access-Control-Allow-Origin: *
           // 表明允许所有的域名来跨域请求它,* 是通配符,没有任何限制
           // 只允许指定域名的跨域请求
           // Access-Control-Allow-Origin: http://127.0.0.1:5500
           // 2.使用 CORS 跨域的过程
           // ① 浏览器发送请求
           // ② 后端在响应头中添加 Access-Control-Allow-Origin 头信息
           // ③ 浏览器接收到响应
           // ④ 如果是同域下的请求,浏览器不会额外做什么,这次前后端通信就圆满完成了
           // ⑤ 如果是跨域请求,浏览器会从响应头中查找是否允许跨域访问
           // ⑥ 如果允许跨域,通信圆满完成
           // ⑦ 如果没找到或不包含想要跨域的域名,就丢弃响应结果
           // 3.CORS 的兼容性
           // IE10 及以上版本的浏览器可以正常使用 CORS
           // https://caniuse.com/
               // JSONP
      // 1.JSONP 的原理
      // script 标签跨域不会被浏览器阻止
      // JSONP 主要就是利用 script 标签,加载跨域文件
      // 2.使用 JSONP 实现跨域
      // 服务器端准备好 JSONP 接口
      // https://www.imooc.com/api/http/jsonp?callback=handleResponse
      // 手动加载 JSONP 接口或动态加载 JSONP 接口
    //   const script = document.createElement('script');
    //   script.src =
        // 'https://www.imooc.com/api/http/jsonp?callback=handleResponse';
    //   document.body.appendChild(script);
    //   声明函数
    //   const handleResponse = data => {
        // console.log(data);
    //   };
      // handleResponse({
      //   code: 200,
      //   data: [
      //     {
      //       word: 'jsp'
      //     },
      //     {
      //       word: 'js'
      //     },
      //     {
      //       word: 'json'
      //     },
      //     {
      //       word: 'js 入门'
      //     },
      //     {
      //       word: 'jstl'
      //     }
      //   ]
      // });
       // 优先使用 CORS,如果浏览器不支持 CORS 的话,再使用 JSONP
     // </script>
     <!-- <script src="https://www.imooc.com/api/http/jsonp?callback=handleResponse"></script> -
     <!-- 相当于 -->
     <!-- <script>
       handleResponse({
         code: 200,
         data: [
           {
             word: 'jsp'
           },
           {
             word: 'js'
           },
           {
             word: 'json'
           },
           {
             word: 'js 入门'
           },
           {
             word: 'jstl'
           }
         ]
       });
     </script> -->
<!-- 初识 Ajax -->
      // 1.Ajax 是什么
      // Ajax 是 Asynchronous JavaScript and XML(异步 JavaScript 和 XML)的简写
      // Ajax 中的异步:可以异步地向服务器发送请求,在等待响应的过程中,不会阻塞当前页面,浏览器可以做自己
      // XML(可扩展标记语言)是前后端数据通信时传输数据的一种格式
      // XML 现在已经不怎么用了,现在比较常用的是 JSON
      // Ajax 其实就是浏览器与服务器之间的一种异步通信方式
      // 使用 Ajax 可以在不重新加载整个页面的情况下,对页面的某部分进行更新
      // ① 慕课网注册检测
      // ② 慕课网搜索提示
      // 2.搭建 Ajax 开发环境
      // Ajax 需要服务器环境,非服务器环境下,很多浏览器无法正常使用 Ajax
      // Live Server
      // windows phpStudy
      // Mac MAMP
 <!-- Ajax 的基本用法 -->
         <!-- get请求 -->
       <!-- <form action="https://www.imooc.com/api/http/search/suggest" method="get">
         <input type="text" name="username" />
         <input type="text" name="words" />
         <input type="password" name="password" />
         <input type="submit" value="提交" />
       </form> -->
       <script>
          // 1.携带数据
          // GET 请求不能通过请求体携带数据,但可以通过请求头携带
          // const url =
          //   'https://www.imooc.com/api/http/search/suggest?words=js&username=alex&age=18';
          // const xhr = new XMLHttpRequest();
          // xhr.onreadystatechange = () => {
          //   if (xhr.readyState != 4) return;
          //   if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) {
          //     console.log(xhr.responseText);
          //   }
          // };
          // xhr.open('GET', url, true);
          // xhr.send(null);
          // 不会报错,但不会发送数据
          // xhr.send('sex=male');
          // 2.数据编码
          // 如果携带的数据是非英文字母的话,比如说汉字,就需要编码之后再发送给后端,不然会造成乱码问题
          // 可以使用 encodeURIComponent() 编码
        //   const url = `https://www.imooc.com/api/http/search/suggest?words=${encodeURIComponent(
            // '前端'
        //   )}`;
        //   const xhr = new XMLHttpRequest();
        //   xhr.onreadystatechange = () => {
            // if (xhr.readyState != 4) return;
            // if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) {
            //   console.log(xhr.responseText);
            // }
        //   };
        //   xhr.open('GET', url, true);
        //   xhr.send(null);
// post请求
       // 1.携带数据
     // POST 请求主要通过请求体携带数据,同时也可以通过请求头携带
     // const url = 'https://www.imooc.com/api/http/search/suggest?words=js';
     // const xhr = new XMLHttpRequest();
     // xhr.onreadystatechange = () => {
     //   if (xhr.readyState != 4) return;
     //   if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) {
     //     console.log(xhr.responseText);
     //   }
     // };
     // xhr.open('POST', url, true);
     // 如果想发送数据,直接写在 send() 的参数位置,一般是字符串
     // xhr.send('username=alex&age=18');
     // 不能直接传递对象,需要先将对象转换成字符串的形式
     // xhr.send({
     //   username: 'alex',
     //   age: 18
     // });
     // [object Object]
     // 2.数据编码
     // xhr.send(`username=${encodeURIComponent('张三')}&age=18`);
// axios
        // <!-- <script src="https://unpkg.com/axios/dist/axios.min.js"></script> -->
        <!-- <script src="https://unpkg.com/axios@0.19.2/dist/axios.min.js"></script> -->
        <script>
          // 1.axios 是什么
          // axios 是一个基于 Promise 的 HTTP 库,可以用在浏览器和 node.js 中
          // 第三方 Ajax 库
          // http://www.axios-js.com/zh-cn/docs/
          // 2.axios 的基本用法
          // 引入 axios
          // console.log(axios);
          const url = 'https://www.imooc.com/api/http/search/suggest?words=js';
          // axios(url, {
          //   method: 'post',
          //   // 请求时的头信息
          //   headers: {
          //     'Content-Type': 'application/x-www-form-urlencoded'
          //     // 'Content-Type': 'application/json'
          //   },
          //   // 通过请求头携带的数据
          //   params: {
          //     username: 'alex'
          //   },
          //   // 通过请求体携带的数据
          //   // application/json
          //   // data: {
          //   //   age: 18,
          //   //   sex: 'male'
          //   // }
          //   // application/x-www-form-urlencoded
          //   data: 'age=18&sex=male'
          //   // timeout: 10
          //   // withCredentials: true
          // })
          //   .then(response => {
          //     console.log(response);
          //     console.log(response.data.data);
          //   })
          //   .catch(err => {
          //     console.log(err);
          //   });
          // axios
          //   .get(url, {
          //     params: {
          //       username: 'alex'
          //     }
          //   })
          //   .then(response => {
          //     console.log(response);
          //   });
          // axios
          //   .post(url, 'username=alex&age=18')
          //   .then(response => {
          //     console.log(response);
          //   })
          //   .catch(err => {
          //     console.log(err);
          //   });
        //   axios
            // .post('https://www.imooc.com/api/http/json/search/suggest?words=js', {
            //   username: 'alex'
            // })
            // .then(response => {
            //   console.log(response);
            // })
            // .catch(err => {
            //   console.log(err);
            // });
          // axios.put()
          // axios.delete()
// Fetch
                  // 1.Fetch 是什么
        // Fetch 也是前后端通信的一种方式
        // Fetch 是 Ajax(XMLHttpRequest)的一种替代方案,它是基于 Promise 的
        // Ajax 的兼容性比 Fetch 好
        // abort timeout
        // 2.Fetch 的基本用法
        // console.log(fetch);
        // console.log(ajax);
        // fetch() 调用后返回 Promise 对象
        // const url = 'https://www.imooc.com/api/http/search/suggest?words=js';
        // body: (...)
        // bodyUsed: false
        // ok: true
        // status: 200
        // statusText: "OK"
        // type: "cors"
        // url: "https://www.im
        // 第二个参数是对象,用来配置 fetch
        // const fd = new FormData();
        // fd.append('username', 'alex');
        // fetch(url, {
        //   method: 'post',
          // body: null
          // body: 'username=alex&age=18',
          // body: JSON.stringify({ username: 'alex' })
        //   body: fd,
          // headers: {
          //   // 'Content-Type': 'application/x-www-form-urlencoded'
          //   'Content-Type': 'application/json'
          // }
        //   mode: 'cors'
          // credentials:'include'
        // })
        //   .then(response => {
            // console.log(response);
            // body/bodyUsed
            // body 只能读一次,读过之后就不让再读了
            // ok
            // 如果为 true,表示可以读取数据,不用再去判断 HTTP 状态码了
            // if (response.ok) {
              // console.log(response.json());
            //   return response.json();
            //   return response.text();
            // } else {
            //   throw new Error(`HTTP CODE 异常 ${response.status}`);
            // }
        //   })
        //   .then(data => {
            // console.log(data);
        //   })
        //   .catch(err => {
            // console.log(err);
        //   });
// JSON
                // 1.JSON 是什么
      // Ajax 发送和接收数据的一种格式
      // XML
      // username=alex&age=18
      // JSON
    //   const url = 'https://www.imooc.com/api/http/search/suggest?words=js';
    //   const xhr = new XMLHttpRequest();
    //   xhr.onreadystatechange = () => {
        // if (xhr.readyState != 4) return;
        // if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) {
        //   console.log(xhr.responseText);
        // }
    //   };
    //   xhr.open('GET', url, true);
    //   xhr.send(null);
      // {"code":200,"data":[{"word":"jsp"},{"word":"js"},{"word":"json"},{"word":"js \u5165\u95e8"}
      // HTML/XML
      // JSON 全称是 JavaScript Object Notation
      // 2.为什么需要 JSON
      //  JSON 有 3 种形式,每种形式的写法都和 JS 中的数据类型很像,可以很轻松的和 JS 中的数据类型互
      // JS->JSON->PHP/Java
      // PHP/Java->JSON->JS
// JSON 的 3 种形式
                // 1.简单值形式
       // .json
       // JSON 的简单值形式就对应着 JS 中的基础数据类型
       // 数字、字符串、布尔值、null
       // 注意事项
       // ① JSON 中没有 undefined 值
       // ② JSON 中的字符串必须使用双引号
       // ③ JSON 中是不能注释的
       // 2.对象形式
       // JSON 的对象形式就对应着 JS 中的对象
       // 注意事项
       // JSON 中对象的属性名必须用双引号,属性值如果是字符串也必须用双引号
       // JSON 中只要涉及到字符串,就必须使用双引号
       // 不支持 undefined
       // 3.数组形式
       // JSON 的数组形式就对应着 JS 中的数组
       // [1, "hi", null]
       // 注意事项
       // 数组中的字符串必须用双引号
       // JSON 中只要涉及到字符串,就必须使用双引号
       // 不支持 undefined
       //    const xhr = new XMLHttpRequest();
       //    xhr.onreadystatechange = () => {
           //  if (xhr.readyState != 4) return;
           //  if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) {
           //    console.log(xhr.responseText);
           //    console.log(typeof xhr.responseText);
           //  }
       //    };
       // xhr.open('GET', './plain.json', true);
         // xhr.open('GET', './obj.json', true);
      //    xhr.open('GET', './arr.json', true);
      //    xhr.send(null);
// JSON 的常用方法
            // 1.JSON.parse()
      // JSON.parse() 可以将 JSON 格式的字符串解析成 JS 中的对应值
      // 一定要是合法的 JSON 字符串,否则会报错
      // const xhr = new XMLHttpRequest();
      // xhr.onreadystatechange = () => {
      //   if (xhr.readyState != 4) return;
      //   if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) {
      //     console.log(xhr.responseText);
      //     console.log(typeof xhr.responseText);
      //     console.log(JSON.parse(xhr.responseText));
      //     // console.log(JSON.parse(xhr.responseText).data);
      //   }
      // };
      // // xhr.open('GET', './plain.json', true);
      // // xhr.open('GET', './obj.json', true);
      // xhr.open('GET', './arr.json', true);
      // // xhr.open(
      // //   'GET',
      // //   'https://www.imooc.com/api/http/search/suggest?words=js',
      // //   true
      // // );
      // xhr.send(null);
      // 2.JSON.stringify()
      // JSON.stringify() 可以将 JS 的基本数据类型、对象或者数组转换成 JSON 格式的字符串
      // console.log(
      //   JSON.stringify({
      //     username: 'alex',
      //     age: 18
      //   })
      // );
      // const xhr = new XMLHttpRequest();
      // xhr.open(
      //   'POST',
      //   'https://www.imooc.com/api/http/search/suggest?words=js',
      //   true
      // );
      // xhr.send(
      //   JSON.stringify({
      //     username: 'alex',
      //     age: 18
      //   })
      // );
      // 3.使用 JSON.parse() 和 JSON.stringify() 封装 localStorage
    //   import { get, set, remove, clear } from './storage.js';
    //   set('username', 'alex');
    //   console.log(get('username'));
    //   set('zs', {
        // name: '张三',
        // age: 18
    //   });
    //   console.log(get('zs'));
    //   remove('username');
    //   clear();
// XHR 的属性
              // 1.responseType 和 response 属性
       // const url = 'https://www.imooc.com/api/http/search/suggest?words=js';
       // const xhr = new XMLHttpRequest();
       // xhr.onreadystatechange = () => {
       //   if (xhr.readyState != 4) return;
       //   if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) {
       //     // 文本形式的响应内容
       //     // responseText 只能在没有设置 responseType 或者 responseType = '' 或 'te
       //     // console.log('responseText:', xhr.responseText);
       //     // 可以用来替代 responseText
       //     console.log('response:', xhr.response);
       //     // console.log(JSON.parse(xhr.responseText));
       //   }
       // };
       // xhr.open('GET', url, true);
       // // xhr.responseType = '';
       // // xhr.responseType = 'text';
       // xhr.responseType = 'json';
       // xhr.send(null);
       // IE6~9 不支持,IE10 开始支持
       // 2.timeout 属性
       // 设置请求的超时时间(单位 ms)
       // const url = 'https://www.imooc.com/api/http/search/suggest?words=js';
       // const xhr = new XMLHttpRequest();
       // xhr.onreadystatechange = () => {
       //   if (xhr.readyState != 4) return;
       //   if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) {
       //     console.log(xhr.response);
       //   }
       // };
       // xhr.open('GET', url, true);
       // xhr.timeout = 10000;
       // xhr.send(null);
       // IE6~7 不支持,IE8 开始支持
       // 3.withCredentials 属性
       // 指定使用 Ajax 发送请求时是否携带 Cookie
       // 使用 Ajax 发送请求,默认情况下,同域时,会携带 Cookie;跨域时,不会
       // xhr.withCredentials = true;
       // 最终能否成功跨域携带 Cookie,还要看服务器同不同意
       // const url = 'https://www.imooc.com/api/http/search/suggest?words=js';
       // // const url = './index.html';
       // const xhr = new XMLHttpRequest();
       // xhr.onreadystatechange = () => {
       //   if (xhr.readyState != 4) return;
       //   if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) {
       //     console.log(xhr.response);
       //   }
       // };
       // xhr.open('GET', url, true);
       // xhr.withCredentials = true;
       // xhr.send(null);
           // IE6~9 不支持,IE10 开始支持
// XHR 的方法
        //    <!-- <form
    //   action="https://www.imooc.com/api/http/search/suggest?words=js"
    //   method="post"
    // >
    //   <input type="text" name="username" />
    //   <input type="password" name="password" />
    //   <input type="submit" value="提交" />
    // </form> -->
    // <script>
      // 1.abort()
      // 终止当前请求
      // 一般配合 abort 事件一起使用
      // const url = 'https://www.imooc.com/api/http/search/suggest?words=js';
      // const xhr = new XMLHttpRequest();
      // xhr.onreadystatechange = () => {
      //   if (xhr.readyState != 4) return;
      //   if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) {
      //     console.log(xhr.response);
      //   }
      // };
      // xhr.open('GET', url, true);
      // xhr.send(null);
      // xhr.abort();
      // 2.setRequestHeader()
      // 可以设置请求头信息
      // xhr.setRequestHeader(头部字段的名称, 头部字段的值);
      // const url = 'https://www.imooc.com/api/http/search/suggest?words=js';
    //   const url = 'https://www.imooc.com/api/http/json/search/suggest?words=js';
    //   const xhr = new XMLHttpRequest();
    //   xhr.onreadystatechange = () => {
        // if (xhr.readyState != 4) return;
        // if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) {
        //   console.log(xhr.response);
        // }
        //   };
        //   xhr.open('POST', url, true);
          // 请求头中的 Content-Type 字段用来告诉服务器,浏览器发送的数据是什么格式的
          // xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
        //   xhr.setRequestHeader('Content-Type', 'application/json');
          // xhr.send(null);
          // xhr.send('username=alex&age=18');
        //   xhr.send(
            // JSON.stringify({
            //   username: 'alex'
            // })
        //   );
            //  
//  XHR 的事件 
               // 1.load 事件
           // 响应数据可用时触发
           // const url = 'https://www.imooc.com/api/http/search/suggest?words=js';
           // const xhr = new XMLHttpRequest();
           // // xhr.onload = () => {
           // //   if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) {
           // //     console.log(xhr.response);
           // //   }
           // // };
           // xhr.addEventListener(
           //   'load',
           //   () => {
           //     if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) {
           //       console.log(xhr.response);
           //     }
           //   },
           //   false
           // );
           // xhr.open('GET', url, true);
           // xhr.send(null);
           // IE6~8 不支持 load 事件
           // 2.error 事件
           // 请求发生错误时触发
           // const url = 'https://www.imooc.com/api/http/search/suggest?words=js';
           // const url = 'https://www.iimooc.com/api/http/search/suggest?words=js';
           // const xhr = new XMLHttpRequest();
           // xhr.addEventListener(
           //   'load',
           //   () => {
           //     if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) {
           //       console.log(xhr.response);
           //     }
           //   },
           //   false
           // );
           // xhr.addEventListener(
           //   'error',
           //   () => {
           //     console.log('error');
           //   },
           //   false
           // );
           // xhr.open('GET', url, true);
           // xhr.send(null);
           // IE10 开始支持
           // 3.abort 事件
           // 调用 abort() 终止请求时触发
           // const url = 'https://www.imooc.com/api/http/search/suggest?words=js';
           // const xhr = new XMLHttpRequest();
           // xhr.addEventListener(
           //   'load',
           //   () => {
           //     if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) {
           //       console.log(xhr.response);
           //     }
           //   },
           //   false
           // );
           // xhr.addEventListener(
           //   'abort',
           //   () => {
           //     console.log('abort');
           //   },
           //   false
           // );
           // xhr.open('GET', url, true);
           // xhr.send(null);
           // xhr.abort();
           // IE10 开始支持
           // 4.timeout 事件
           // 请求超时后触发
        //    const url = 'https://www.imooc.com/api/http/search/suggest?words=js';
        //    const xhr = new XMLHttpRequest();
        //    xhr.addEventListener(
            //  'load',
            //  () => {
            //    if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) {
                //  console.log(xhr.response);
            //    }
            //  },
            //  false
        //    );
        //    xhr.addEventListener(
            //  'timeout',
            //  () => {
            //    console.log('timeout');
            //  },
            //  false
        //    );
        //    xhr.open('GET', url, true);
        //    xhr.timeout = 10;
        //    xhr.send(null);
           // IE8 开始支持
</script>
</body>
</html>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值