ES6相关知识

225 阅读26分钟

ES6基础入门

初识ES6

  • ES与Javascript的关系 JavaScript(浏览器端)= ECMAScript(语法+API) + DOM+BOM

let和const是什么

  1. let和const是什么
  • let代替var声明变量
  • const声明常量
  1. let和const的用法 let age = 18
    const sex = 'male'

  2. 什么是变量,什么是常量

  • var、let声明的就是变量,变量一旦初始化之后,还可以重新赋值
  • const 声明的就是常量,常量一旦初始化,就不能重新赋值了,否则就会报错

const

  1. 为什么需要const 为了那些一旦初始化就不希望重新赋值的情况设计的

  2. const的注意事项

  • 使用const声明常量,一旦声明,就必须立即初始化,不能留到以后赋值 const sex; sex='male';//错误的
    const sex = 'male'; //正确的
  • const 声明的常量,允许在不重新赋值的情况下修改它的值 (引用数据类型) 基本数据类型: const sex = 'male'; sex = 'female' //此时会报错 引用数据类型
       const person = { username: 'Alex' };
       // person = {};  //此时会报错
       person.username = 'ZhangSan';  //此时不会报错
       console.log(person);  //{username:'ZhangSan'}
  1. 什么时候用 const,什么时候用 let
    for (let i = 0; i < 3; i++) {}
    const username = 'Alex';  //在不知道是否会改变值的时候,先用const,之后值改变就会出现报错
    username = 'ZhangSan';  //报错

let const 与var的区别

  1. 重复声明( 已经存在的变量或常量,又声明了一遍) var 允许重复声明,let、const 不允许
 function fun(a){
   let a=1
 }
 fun()
 //此时会报错,因为a已经作为函数参数声明过一遍了,函数内部又声明了一遍
  1. 变量提升
  • var 会提升变量的声明到当前作用域的顶部
   console.log(a);
   var a = 1;

相当于

    var a;
    console.log(a);
    a = 1;
  • let、const 不存在变量提升
    (养成良好的编程习惯,对于所有的变量或常量,做到先声明,后使用)
  1. 暂时性死区 只要作用域内存在 let、const,它们所声明的变量或常量就自动“绑定”这个区域,不再受到外部作用域的影响
      let a = 2;
      function func() {
        console.log(a); //报错
        let a = 1;
      }
      func();
  1. window 对象的属性和方法 全局作用域中,var 声明的变量,通过 function 声明的函数,会自动变成 window 对象的属性或方法,而let、const 不会
    var+function
       var age = 18;
       function add() {}
       console.log(window.age);  //18
       console.log(window.add === add);//true

let/const

      let age = 18;
      const add = function () {};
      console.log(window.age);  //undefined
      console.log(window.add === add); //false

5. 块级作用域

  • 什么是块级作用域
    var 没有块级作用域
      for (var i = 0; i < 3; i++) {
       console.log(i);  //0 1 2
     }
     console.log(i);  //3

let/const 有块级作用域

  for (let i = 0; i < 3; i++) {
        // i = i+1
        // console.log(i);
      }
console.log(i);  //报错
  • 作用域链
 function func() {
        for (let i = 0; i < 3; i++) {
          // console.log(i);
        }
      }
      func();
      console.log(i);

作用域链:内层作用域->外层作用域->...->全局作用域

image.png

  • 有哪些块级作用域
    1. {}
    2. for(){}
    3. while(){}
    4. do{}while()
    5. if(){}
    6. switch(){}
    7. function(){}
    8. const person = { getAge: function () {} };

模板字符串与箭头函数

模板字符串

  1. 认识模板字符串
      const username2 = `alex`;
  1. 模板字符串与一般字符串的区别
      const person = {
        username: 'Alex',
        age: 18,
        sex: 'male'
      };
      const info = `我的名字是:${person.username}, 性别:${person.sex}, 今年${person.age}岁了`;
      console.log(info);

和其他东西一起使用的时候,使用模板字符串,方便注入
其他情况下使用模板字符串或一般字符串都行

  1. 模板字符串的注意事项
  • 输出多行字符串
  const info = `第1行\n第2行`;
  const info = `第1行
第2行`;
  console.log(info);

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

  • 输出 ` 和 \ 等特殊字符
     const info = `'\`\\`;
     console.log(info);  //输出'`\,在模板字符中\起到转义作用
  • 模板字符串的注入 ${}
     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); //alex,20,女

只要最终可以得出一个值的就可以通过 ${} 注入到模板字符串中

箭头函数

1. 箭头函数是什么

      const add = (x, y) => {
         return x + y;
      };
      console.log(add(1, 1));
  • 箭头函数的结构 const/let 函数名 = 参数 => 函数体
  • 如何将一般函数改写成箭头函数
// 声明形式
// function add() {}

// 声明形式->函数表达式形式
// const add = function () {};

// 函数表达式形式->箭头函数
const add = () => {};

2. 箭头函数的注意事项

  • 单个参数

单个参数可以省略圆括号

      const add = x => {
         return x + 1;
      };
      console.log(add(1));

无参数或多个参数不能省略圆括号

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

多行函数体不能再化简了

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

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

  • 全局作用域中的 this 指向 console.log(this); // window
  • 一般函数(非箭头函数)中的 this 指向 'use strict';
    严格模式就指向 undefined
    只有在函数调用的时候 this 指向才确定,不调用的时候,不知道指向谁
    this 指向和函数在哪儿调用没关系,只和谁在调用有关
    没有具体调用对象的话,this 指向 undefined,在非严格模式下,转向 window
 const obj= {
            fn1:function(){
                console.log(this);  //window
            },
            fn2:test
        }
        function test(){
            console.log(this)  //object
            setTimeout(function(){
                console.log(this)  //window
            })
        }
        const res=obj.fn1;
        res();
        obj.fn2()
  1. 箭头函数中的this指向 箭头函数没有自己的 this
     const calc = {
       add: () => {
          console.log(this);
        }
     };
     calc.add(); // window
  • 练习
     const calc = {
       add: function () {
         // this
         const adder = () => {
           console.log(this);
         };
         adder();
       }
     };
     //第一种情况
    calc.add(); // calc
    //第二种情况
    const addFn = calc.add;
    addFn(); // undefined->window
        function foo(){
            return() =>{
                return() =>{
                    return() =>{
                        console.log('id',this.id);//id:1
                    }
                }
            }
        }
        var f = foo.call({id:1});
        var t1 = f.call({id:2})()();
        var t2 = f().call({id:3})();
        var t3 = f()().call({id:4});

3. 不适用箭头函数的情景

  • 作为构造函数 箭头函数没有 this
    const Person = () => {}; new Person();// 会报错
  • 需要 this 指向调用对象的时候
      document.onclick = function () {
         console.log(this);
       };
       
       document.addEventListener(
         'click',
         () => {
           console.log(this); //window
         },
         false
      );
  • 需要使用 arguments 的时候 箭头函数中没有 arguments
       function add() {
         console.log(arguments);
       }
       add(1, 2,3,4,5);

       const add = () => console.log(arguments);
       add();  //报错:arguments没有定义

解构赋值

数组的解构赋值

  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);
  • 什么是解构赋值 解析某一数据的结构,将我们想要的东西提取出来,赋值给变量或常量
  1. 数组解构赋值的原理
  • 模式(结构)匹配 [] = [1, 2, 3];
  • 索引值相同的完成赋值 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,5,3
  1. 数组解构赋值的默认值
  • 默认值的基本用法
      const [a, b] = [];
      const [a, b] = [undefined, undefined];

      const [a = 1, b = 2] = [];
      console.log(a, b);  //1 2
  • 默认值的生效条件 只有当一个数组成员严格等于(===)undefined 时,对应的默认值才会生效
      const [a = 1, b = 2] = [3, 0];  //3 0
      const [a = 1, b = 2] = [3, null];  //3 null
      const [a = 1, b = 2] = [3];   //1 2
  • 默认值表达式 如果默认值是表达式,默认值表达式是惰性求值的
      const func = () => {
        console.log('我被执行了');
        return 2;
      };
      const [x = func()] = [];
      console.log(x);  //2

练习

        const arr = [1,2,3,4];
        let [a,b='b',c,d]=arr;
        console.log(b);//2
  1. 数组解构赋值的应用
  • 常见的类数组的解构赋值
    arguments
      function func() {
        const [a, b] = arguments;
        console.log(a, b);  //1 2 
      }
      func(1, 2);

NodeList

      console.log(document.querySelectorAll('p'));
      const [p1, p2, p3] = document.querySelectorAll('p');
      console.log(p1, p2, p3);
  • 函数参数的解构赋值
      const array = [1, 1];
      const add = ([x = 0, y = 0]) => x + y;
      console.log(add([])); //0

  • 交换变量的值
      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. 对象解构赋值的原理
  • 模式(结构)匹配 {}={}
  • 属性名相同的完成赋值
    const { age, username } = { username: 'Alex', age: 18 };
    console.log(age, username);
  • 取别名
    const { age: age, username: uname } = { username: 'Alex', age: 18 };
    console.log(age, uname); //Alex 0
  1. 对象解构赋值的注意事项
  • 默认值的生效条件 对象的属性值严格等于 undefined 时,对应的默认值才会生效
    const { username = 'ZhangSan', age = 0 } = { username: 'alex' };
    console.log(username, age); //Alex 0
  • 默认值表达式 如果默认值是表达式,默认值表达式是惰性求值的
  • 将一个已经声明的变量用于解构赋值 如果将一个已经声明的变量用于对象的解构赋值,整个赋值需在圆括号中进行
      let x = 2;
      // ({ x } = { x: 1 });
      [x] = [1];
      console.log(x); //1
  • 可以取到继承的属性
      const { toString } = {};
      console.log(toString); //toString
      // 因为toString在Object.prototype中

其他数据类型的解构赋值

  1. 字符串的解构赋值
    例: ''='hello' ×
  • 数组形式的解构赋值`
      const [a, b, , , c] = 'hello';
      console.log(a, b, c);
  • 对象形式的解构赋值
      const { 0: a, 1: b, length } = 'hello';
      console.log(a, b, length); // h e 5

字符串既可以按数组形式来解构赋值,也可以按对象形式来解构赋值

  1. 数值和布尔值的解构赋值 先将等号右边的值转为对象
      console.log(new Number(123));  //Number{123}
      const { a = 1, toString } = 123;
      console.log(a, toString);  //1 function toString()
  1. undefined 和 null 的解构赋值
    由于 undefined 和 null 无法转为对象,所以对它们进行解构赋值,都会报错
      const { toString } = undefined;
      const { toString } = null;

对象字面量的增强

属性和方法的简洁表示法

  1. 对象字面量是什么
    实例化构造函数生成对象
      // 对象字面量
      const person = {
        age: 18,
        speak: function () {}
      };
  1. 属性的简洁表示法 键名和变量或常量名一样的时候,可以只写一个
       const age = 18;
       const person = {
         age
       };
       console.log(person);
  1. 方法的简洁表示法 方法可以省略冒号和 function 关键字
      const person = {
        // speak: function () {}
        speak() {}
      }; 
     console.log(person);

方括号语法

  1. 方括号语法的用法
      // 方括号语法可以写在对象字面量中
      const person = {
         [prop]: 18
      };
      console.log(person);  //18
  1. 方括号中可以放什么
      // ${}
      // [值或通过计算可以得到值的(表达式)]
       const prop = 'age';
       const func = () => 'age2';
       const person = {
         ['s' + 'ex']: 'male'
       };
       console.log(person);
  1. 方括号语法和点语法的区别 点语法是方括号语法的特殊形式
    person.age 等价于 person['age']
    属性名由数字、字母、下划线以及$构成,并且数字还不能打头的时候可以使用点语法
    合法标识符可以用来作为变量或常量名
    当你的属性或方法名是合法标识符时,可以使用点语法,其他情况下请使用方括号语法

函数参数默认值

  1. 认识函数参数的默认值 调用函数的时候传参了,就用传递的参数;如果没传参,就用默认值1
    multiply(2, 1); 等同于multiply(2);

  2. 函数参数默认值的基本用法

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

默认值的注意事项

  1. 默认值的生效条件
  • 不传参数,或者明确的传递 undefined 作为参数,只有这两种情况下,默认值才会生效
  1. 默认值表达式 如果默认值是表达式,默认值表达式是惰性求值的

  2. 设置默认值的小技巧 函数参数的默认值,最好从参数列表的右边开始设置

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

函数参数默认值的应用

  • 接收一个对象作为参数
      const logUser = ({ username = 'zhangsan', age = 0, sex = 'male' } = {}) =>
        console.log(username, age, sex);
     
       logUser({ username: 'alex' });
       { username = 'zhangsan', age = 0, sex = 'male' } = { username: 'alex' }

       logUser({});  //默认值
       logUser();  //报错(相当于对undefined解构赋值)
       { username = 'zhangsan', age = 0, sex = 'male' } = {}

       { username = 'zhangsan', age = 0, sex = 'male' } = undefined

ES6语法扩展

剩余参数

是什么

  1. 认识剩余参数 const add = (x, y, z, ...args) => {};
  2. 剩余参数的本质
      const add = (x, y, ...args) => {
        console.log(x, y, args);  //1 2 [3,4,5]
      };
      add(1, 2, 3, 4, 5);

剩余参数永远是个数组,即使没有值,也是空数组

注意事项

  1. 箭头函数的剩余参数 箭头函数的参数部分即使只有一个剩余参数,也不能省略圆括号
    const add = (...args) => {};

  2. 使用剩余参数替代 arguments 获取实际参数

  const add = function () {
     console.log(arguments);
  };  //用下面的来替换
  const add = (...args) => {
   console.log(args);
  };
 add(1, 2);
  1. 剩余参数的位置 剩余参数只能是最后一个参数,之后不能再有其他参数,否则会报错

应用

  1. 完成 add 函数
      const add = (...args) => {
        let sum = 0;

        for (let i = 0; i < args.length; i++) {
          sum += args[i];
        }

        return sum;
      };
      console.log(add(1, 2, 3));
  1. 与解构赋值结合使用 剩余参数不一定非要作为函数参数使用
      const func = ([num, ...args]) => {};
      func([1, 2, 3]);

      const { x, y, ...z } = { a: 3, x: 1, y: 2, b: 4 };
      // 必须是最后一个
      console.log(x, y, z);   //1 2 {a:3,b:4}

      const func = ({ x, y, ...z }) => {};
      func({ a: 3, x: 1, y: 2, b: 4 });

展开运算符

数组展开运算符

  1. 认识展开运算符
      [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
  1. 数组展开运算符的基本用法
      // 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. 复制数组
      const a = [1, 2];;

      const c = [...a];
      a[0] = 3;
      console.log(a);
      console.log(c);  //[1,2]
  1. 合并数组
      const a = [1, 2];
      const b = [3];
      const c = [4, 5];

      console.log([...a, ...b, ...c]);  //[1,2,3,4,5]
      console.log([1, ...b, 2, ...a, ...c, 3]);//[1,3,2,1,2,4,5]
  1. 字符串转为数组 字符串可以按照数组的形式展开
    console.log(...'alex');
    console.log('a', 'l', 'e', 'x');
    console.log([...'alex']);  //['a','l','e','x']      
    console.log('alex'.split(''));  //ES6之前的方法
  1. 常见的类数组转化为数组 arguments
      function func() {
        console.log([...arguments]);
      }
      func(1, 2);

NodeList

      console.log(document.querySelectorAll('p'));
      console.log([...document.querySelectorAll('p')].push);

对象的展开运算符

  • 基本用法
  1. 展开对象 对象不能直接展开,必须在 {} 中展开
      const apple = {
        color: '红色',
        shape: '球形',
        taste: '甜'
      };

      对象的展开:把属性罗列出来,用逗号分隔,放到一个 {} 中,构成新对象
      console.log({ ...apple });
      console.log({ ...apple } === apple);  //false 得到了一个新的对象
  1. 合并对象
      const apple = {
        color: '红色',
        shape: '球形',
        taste: '甜'
      };
      const pen = {
        color: '黑色',
        shape: '圆柱形',
        use: '写字'
      };
      console.log({ ...apple, ...pen });
      // 后面的属性会覆盖前面的
      相当于
      console.log({
        use: '写字',
        color: '红色',
        shape: '球形',
        taste: '甜'
      });
  • 注意事项
  1. 空对象的展开 如果展开一个空对象,则没有任何效果

  2. 非对象的展开
    如果展开的不是对象,则会自动将其转为对象,再将其属性罗列出来
    如果展开运算符后面是字符串,它会自动转成一个类似数组的对象,因此返回的不是空对象
    console.log({ ...'alex' }); //{0:a,1:l,2:e,3:x}
    console.log({ ...[1, 2, 3] });//{0:1,1:2,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);
  1. 用户参数和默认参数
      const logUser = userParam => {
        const defaultParam = {
          username: 'ZhangSan',
          age: 0,
          sex: 'male'
        };

        const param = { ...defaultParam, ...userParam };
        console.log(param.username);  //zahngsan
      };
      logUser();

Set

Set是什么

  1. 什么是 Set 集合
    数组是一系列有序的数据集合
    Set 是一系列无序、没有重复值的数据集合

  2. 理解 Set Set 中不能有重复的成员
    Set 没有下标去标示每一个值,所以 Set 是无序的,也不能像数组那样通过下标去访问 Set 的成员

set实例的方法和属性

  1. 方法
  • add
const s = new Set();
s.add(1).add(2).add(2);
console.log(s);   //Set [1,2]
  • has
console.log(s.has(1));  //true
console.log(s.has(3));  //false
  • delete s.delete(1);
    使用 delete 删除不存在的成员,什么都不会发生,也不会报错
  • clear s.clear(); consoel.log(s);//Set[ ]
  • forEach
forEach(function(value,key,set){
   //set中value=key
   console.log(value,key,set===s);  //1 1 true;2 2 true;
   },document);

按照成员添加进集合的顺序遍历

  • 练习
let s = new Set();
        s.add(1).add('b').add();
        const obj = {};
        s.forEach((value,key,set)=>{
            console.log(this)  //window
        },obj)
  1. 属性 size

Set构造函数的参数

  1. 数组
const s = new Set([1,2,1]);
console.log(s);  //Set(1,2)
  1. 字符串、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);  //false

Set的注意事项

  1. 判断重复的方式

Set 对重复值的判断基本遵循严格相等(===)
但是对于 NaN 的判断与 === 不同,Set 中 NaN 等于 NaN

//对于对象而言
      const s = new Set();
      s.add({}).add({});
      console.log({} === {});  //false
      console.log(s);  //{}{}
  1. 什么时候使用 Set
    • 数组或字符串去重时
    • 不需要通过下标访问,只需要遍历时
    • 为了使用 Set 提供的方法和属性时(add delete clear has forEach size 等)

Set的应用

  1. 数组去重 console.log([...new Set([1, 2, 1])]);

  2. 字符串去重

  • 'abbacbd';
      const s = new Set('abbacbd');
      console.log([...s].join(''));  //join:数组转化为字符串
      console.log(s);
//即
      console.log([...new Set('abbacbd')].join(''));
  1. 存放 DOM 元素
const s = new Set(document.querySelectorAll('p'));
s.forEach(function(elem){
  elem.style.color = 'red';
  elem.style.vackground = 'yellow';
});

Map

是什么

  1. 认识 Map 映射
    Map 和对象都是键值对的集合
    键->值,key->value
      const m = new Map();
      m.set('name', 'alex');
      m.set('age', 18);
      console.log(m);  //{name->'Alex',age->18}
  1. 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);

属性和方法

  1. 方法
  • set
    使用 set 添加的新成员,键如果已经存在,后添加的键值对覆盖已有的
    m.set('age',18).set(true.'true')
  • get console.log(m.get('age')); //18
  • has console.log(m.has('age')); //true
  • delete m.delete('age'); 使用 delete 删除不存在的成员,什么都不会发生,也不会报错
  • clear m.clear();
  • forEach
      m.forEach(function (value, key, map) {
        // console.log(value, key, map === m);
        console.log(this);  //HTMLDocument
      }, document);
  1. 属性 size 对象没有size属性

参数

  1. 数组 只能传二维数组,而且必须体现出键和值
console.log(
  new Map([
    ['name':'alex'],
    ['age':18]
  ])
);
//{name->'Alex',age->18}
  • 复制了一个新的 Map
      const m1 = new Map([
        ['name', 'alex'],
        ['age', 18]
      ]);
      console.log(m1);
      const m2 = new Map(m1);
      console.log(m2, m2 === m1);  //false

注意事项

  1. 判断键名是否相同的方式 基本遵循严格相等(===) 例外就是 NaN,Map 中 NaN 也是等于 NaN
      console.log(NaN === NaN);  //true
      const m = new Map();
      m.set(NaN, 1).set(NaN, 2);
      console.log(m);  //Map {NaN -> 2}
  1. 什么时候使用 Map 如果只是需要 key -> value 的结构,或者需要字符串以外的值做键,使用 Map 更合适
    只有模拟现实世界的实体时,才使用对象

遍历器与for..of循环

Iterator(遍历器)

  1. Iterator 的作用 Iterator:遍历器(迭代器)

  2. 寻找 Iterator console.log([1, 2][Symbol.iterator]()); //Array Iterator{ }

  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:可遍历对象的生成方法

  1. 什么是 Iterator Symbol.iterator(可遍历对象的生成方法) -> it(可遍历对象) -> it.next() -> it.next() -> ...(直到 done 为 true)

  2. 为什么需要 Iterator 遍历器

  • 遍历数组:for 循环和 forEach 方法

  • 遍历对象:for in 循环 Iterator 遍历器是一个统一的遍历方式

    // console.log([][Symbol.iterator]());
    // console.log({}[Symbol.iterator]);
    
  1. 如何更方便的使用 Iterator Symbol.iterator->it->next()
    我们一般不会直接使用 Iterator 去遍历 而是使用for..of

for...of的用法

  1. 认识 for...of
      const arr = [1, 2, 3];
      for (const item of arr) {
        console.log(item);
      }
      

for...of 循环只会遍历出那些 done 为 false 时,对应的 value 值

  1. 与 break、continue 一起使用
      const arr = [1, 2, 3];
      for (const item of arr) {
        if (item === 2) {
          // break;
          continue;
        }
        console.log(item);  //1 3
      }
  1. 在 for...of 中取得数组的索引
      const arr = [1, 2, 3];

      // keys() 得到的是索引的可遍历对象,可以遍历出索引值     
      // values() 得到的是值的可遍历对象,可以遍历出值
      // entries() 得到的是索引+值组成的数组的可遍历对象
      for (const [index, value] of arr.entries()) {
        console.log(index, value);  //0 1 \n 1 2 \n 2 3 \n
      }

原生可遍历和非原生可遍历

  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';
      }
  1. 非原生可遍历的有哪些
  • 一般的对象
  • 有 length 和索引属性的对象

使用了Iterator的场合

  1. for...of 针对的是可遍历的

  2. 数组的展开运算符

      console.log(...[1, 2, 3]);
      console.log(1, 2, 3);
      console.log(...'str');
      console.log(...new Set([1, 2, 3]));
      console.log(...{}); ×
  1. 数组的解构赋值
      const [a, b] = [1, 2];
      const [a, b] = [...'hi'];
      const [a, b] = [...new Set([3, 4])];

      console.log(a, b);
  1. Set 和 Map 的构造函数
   new Set(iterator)
   new Map(iterator)

ES6的新增方法

字符串的新增方法

  • includes() 判断字符串中是否含有某些连续字符
  1. 基本用法 console.log('abc'.includes('ac')); // false

  2. 第二个参数 表示开始搜索的位置,默认是 0
    console.log('abc'.includes('a', 0));

  3. 应用 例:把 www.imooc.com/course/list
    变成www.imooc.com/course/list…

      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');
      url = addURLParam(url, 'sort', 'pop');
      console.log(url);
  • padStart()和padEnd() 补全字符串长度
  1. 基本用法
      console.log('x'.padStart(5, 'ab'));  //ababx
      console.log('x'.padEnd(5, 'ab'));    //xabab
      console.log('x'.padEnd(4, 'ab'));    //xaba
  1. 注意事项 原字符串的长度,等于或大于最大长度,不会消减原字符串,字符串补全不生效,返回原字符串
    console.log('xxx'.padStart(2, 'ab')); //xxx
    用来补全的字符串与原字符串长度之和超过了最大长度,截去超出位数的补全字符串,原字符串不动
    如果省略第二个参数,默认使用空格补全长度
    console.log('x'.padStart(4)); //四个空格 + x

  2. 应用 显示日期格式

      console.log('10'.padStart(2, 0));  //10
      console.log('1'.padStart(2, 0));  //01
  • trimStart()和trimEnd() 清除字符串的首或尾空格,中间的空格不会清除
  1. 应用
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
      );

数组的新增方法

  • includes()
  1. 基本用法 判断数组中是否含有某个成员
    console.log([1,2,3].includes(2);
    第二个参数表示搜索的起始位置,默认值是 0
    console.log([1,2,3].includes(2,2));
    基本遵循严格相等(===),但是对于 NaN 的判断与 === 不同,includes 认为 NaN === 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')); // ['s','t','r']

  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));
  1. 第二个参数 作用类似于数组的 map 方法,用来对每个元素进行处理,将处理后的值放入返回的数组
      console.log(Array.from('12', value => value * 2));  //[2,4]
      console.log(Array.from('12').map(value => value * 2));  //[2,4]
  1. 第三个参数
      Array.from(
        '12',
        value => {
          console.log(this);  //window
        },
        document
      );
      Array.from(
        '12',
        function () {
          console.log(this);  //document
        },
        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)
    );  //10
    console.log(
      [1, 5, 10, 15].findIndex((value, index, arr) => {
        // console.log(value, index, arr);
        return value > 9;
      }, document)
    );  //2
  1. 应用
    const students = [
      {
        name: '张三',
        sex: '男',
        age: 16
      },
      {
        name: '李四',
        sex: '女',
        age: 22
      },
      {
        name: '王二麻子',
        sex: '男',
        age: 32
      }
    ];
    console.log(students.find(value => value.sex === '女'));  {'李四','女',22}
    console.log(students.findIndex(value => value.sex === '女'));  //1

对象的新增方法

  • Object.assign() 用来合并对象
  1. 基本用法

    Object.assign(目标对象, 源对象1,源对象2,...): 目标对象

      const apple = {
        color: '红色',
        shape: '圆形',
        taste: '甜'
      };
      const pen = {
        color: '黑色',
        shape: '圆柱形',
        use: '写字'
      };
      console.log(Object.assign(apple, pen));
      //Object { color: "黑色", shape: "圆柱形", taste: "甜", use: "写字" }

      Object.assign 直接合并到了第一个参数中,返回的就是合并后的对象
      console.log(apple);
      console.log(Object.assign(apple, pen) === apple);  //true
  1. 注意事项 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'));  //{0:'s',1:'t',2:'r'}

2.2. 同名属性的替换
后面的直接覆盖前面的

  1. 应用
    合并默认参数和用户参数
      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();
  • 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));
  1. 与数组类似方法的区别
      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() 等方法是构造函数方法,返回的是数组

  1. 使用 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

认识promise

  1. 认识 Promise
    Promise 是异步操作的一种解决方案

  2. 什么时候使用 Promise Promise 一般用来解决层层嵌套的回调函数(回调地狱 callback hell)的问题
    层层嵌套的回调函数:

      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. 实例化构造函数生成实例对象 Promise 解决的不是回调函数,而是回调地狱

  2. Promise 的状态
    const p = new Promise((resolve, reject) => {reject()});
    Promise 有 3 种状态,
    一开始是 pending(未完成),执行 resolve,变成 fulfilled(resolved),已成功
    执行 reject,变成 rejected,已失败
    Promise 的状态一旦变化,就不会再改变了

  3. then 方法

      p.then(
        () => {
          console.log('success');
        },
        () => {
          console.log('error');
        }
      );
  1. resolve 和 reject 函数的参数
    const p = new Promise((resolve, reject) => {     
        reject(new Error('reason'));
      });
      p.then(
        data => {
          console.log('success', data);
        },
        err => {
          console.log('error', err);
        }
      );
      console.log(p);

Promise的实例方法

then()

  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);  // promise promise false
  1. then 方法返回的 Promise 对象的状态改变
      const p = new Promise((resolve, reject) => {
        // resolve();
        reject();
      });
      p.then(
        () => {
          // console.log('success');
        },
        () => {
          console.log('err');
        }
        //err

在 then 的回调函数中,return 后面的东西,会用 Promise 包装一下
return undefined;等价于

          return new Promise(resolve => {
            resolve(undefined);
          });

默认返回的永远都是成功状态的 Promise 对象

  1. 使用 Promise 解决回调地狱 运动
//核心代码
      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
      );

catch()

  1. 有什么用 catch 专门用来处理 rejected 状态
    catch 本质上是 then 的特例
    then(null, err => {});

  2. 基本用法

       new Promise((resolve, reject) => {
        reject('reason');
      }).catch(err => {
          console.log(err);  //reason

          // return undefined
        })
        .then(data => {
          console.log(data);  //undefined
        })

catch() 可以捕获它前面的错误
一般总是建议,Promise 对象后面要跟 catch 方法,这样可以处理 Promise 内部发生的错误

finally()(了解)

当 Promise 状态发生变化时,不论如何变化都会执行,不变化不执行

Promise的构造函数方法

Promise.resolve()和Promise.reject()

  1. Promise.resolve() 成功状态 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);

      new Promise(resolve => resolve(p1)).then(data => {
        console.log(data);
      });

具有 then 方法的对象
后面的 then 会根据传递的 Promise 对象的状态变化决定执行哪一个回调

      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));
  1. Promise.reject() 失败状态 Promise 的一种简写形式
    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);
         }
       );
  1. Promise.allSettled() Promise.allSettled() 的状态与传入的Promise 状态无关
    永远都是成功的
    它只会忠实的记录下各个 Promise 的表现
      const allSettledPromise = Promise.allSettled([p1, p2]);
      allSettledPromise.then(data => {
        console.log('succ', data);
      });

注意事项

  1. resolve 或 reject 函数执行后的代码
    推荐在调用 resolve 或 reject 函数的时候加上 return,不再执行它们后面的代码
      new Promise((resolve, reject) => {
        // return resolve(123);
        return reject('reason');

        console.log('hi');
      });
  1. 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);
      });
  1. 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));

错误既可以单独处理,也可以统一处理 0 一旦被处理,就不会在其他地方再处理一遍

Class类

初识class

class是什么

  1. 认识 Class 类可以看做是对象的模板,用一个类可以创建出许多不同的对象

  2. Class 的基本用法 类名一般大写 class Person {}

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

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

        // 各实例共享的方法
        speak() {
          console.log('speak');
        }
      }
  1. Class 与构造函数
   class Person {
        constructor(name, age) {
          this.name = name;
          this.age = age;

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

        speak() {
          console.log('speak');
        }
      }
       Person.prototype.run = function () {};

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

class的两种定义形式

  1. 声明形式
      class Person {
        constructor() {}
        speak() {}
      }
  1. 表达式形式 (了解)
      const Person = class {
        constructor() {
          console.log('constructor');
        }

        speak() {}
      };
      new Person();

立即执行的匿名类

      new (class {
        constructor() {
          console.log('constructor');
        }
      })();

实例属性、静态方法和静态属性

  1. 实例属性 方法就是值为函数的特殊属性
     class Person {
       age = 0;
       sex = 'male';
       getSex = function () {
         return this.sex;
       };
       constructor(){}
     }

     const p = new Person('Alex');
     console.log(p.age);
  1. 静态方法 就是类的方法
      class Person {
        constructor(name) {
          this.name = name;
        }

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

         static speak() {
           console.log('人类可以说话');

           // this 指向类
           console.log(this);
         }
      }

      const p = new Person('Alex');
      p.speak();

      Person.speak();
  1. 静态属性 就是类的属性
      class Person {
        constructor(name) {
          this.name = name;
        }

        static getVersion() {
          return '1.0';
        }
      }

      console.log(Person.getVersion());

私有属性和方法

  1. 为什么需要私有属性和方法 一般情况下,类的属性和方法都是公开的
    公有的属性和方法可以被外界修改,造成意想不到的错误

  2. 模拟私有属性和方法 2.1. _ 开头表示私有

      class Person {
        constructor(name) {
          this._name = name;
        }

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

        getName() {
          return this._name;
        }
      }

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());
      })();

继承

extends

super

calss的应用

module模块

Module是什么

  1. 什么是模块 模块:一个一个的局部作用域的代码块

  2. 什么是模块系统 模块系统需要解决的主要问题
    ① 模块化的问题 ② 消除全局变量 ③ 管理加载顺序
    ES Module

Module的基本用法

babel

babel是什么

  1. 认识 Babel
    官网:babeljs.io/
    在线编译:babeljs.io/repl
    Babel 是 JavaScript 的编译器,用来将 ES6 的代码,转换成 ES6 之前的代码

  2. 使用 Babel (见代码1.html)

  3. 解释编译结果

  • Babel 本身可以编译 ES6 的大部分语法,比如 let、const、箭头函数、类
  • 但是对于 ES6 新增的 API,比如 Set、Map、Promise 等全局对象,以及一些定义在全局对象上的方法(比如 Object.assign/Array.from)都不能直接编译,需要借助其它的模块
  • Babel 一般需要配合 Webpack 来编译模块语法
    babeljs.io/setup

使用babel前的准备工作

  1. 什么是 Node.js 和 npm Node.js 是个平台或者工具,对应浏览器
    后端的 JavaScript = ECMAScript + IO + File + ...等服务器端的操作

npm:node 包管理工具

  1. 安装 node -v / npm -v

  2. 初始化项目 npm init -> package.json

  3. 安装 Babel 需要的包

npm install --save-dev @babel/core @babel/cli
npm install --save-dev @babel/core@7.11.0 @babel/cli@7.10.5

使用babel编译ES6代码

  1. 执行编译的命令 在 package.json 文件中添加执行 babel 的命令
"scripts": {
   "build": "babel src -d dist"
   //"build": "babel src --out dir dist"
 }, 

npm run build

  1. Babel 的配置文件 .babelrc npm install @babel/preset-env@7.11.0 --save-dev
    即可创建配置出文件 .babelrc,并配置出
      {
        "presets": ["@babel/preset-env"]
      }

webpack

webpack基础

webpack是什么

  1. 认识 Webpack webpack 是静态模块打包器,当 webpack 处理应用程序时,会将所有这些模块打包成一个或多个文件

  2. 什么是 Webpack 模块:webpack 可以处理 js/css/图片、图标字体等单位

静态
开发过程中存在于本地的 js/css/图片/图标字体等文件,就是静态的
动态的内容,webpack没办法处理,只能处理静态的

使用webpack

  1. 初始化项目 npm init

  2. 安装 webpack 需要的包 npm install --save-dev webpack-cli@3.3.12 webpack@4.44.1

  3. 配置 webpack webpack.config.js
    "webpack": "webpack --config webpack.config.js"

  4. 编译并测试 npm run webpack

webpack的核心概念

entry和output

  1. entry:指定入口文件
  2. output:出口文件