【ES6】ES6超详讲解(@_@;)

文章目录

一、初识ES6

ES与JavaScript的关系

JavaScript(浏览器端) = ECMAScript(语法 + API) + DOM + BOM

对于不支持的浏览器,可以使用Bable转码

let和const

let代替var声明变量

const用来声明常量

注意

  1. const声明的常量,允许在不重新赋值的情况下修改它的值
const person = {username : 'aaa'};
person = {};    // 报错
person.username = 'niubi';   // 可以成功修改
console.log(person);
  1. 在一个具体的业务场景中,如果不清楚用const还是let,就先写成const

和var的区别

  1. 重复声明:var一个变量可以重复声明,let和const则不能

  2. 变量提升:let和const不存在变量提升

  3. 暂时性死区

    只要作用域内存在let、const,他们所声明的变量或常量就会自动 “绑定” 这个区域,不再受外部作用域的影响

    let a = 2;
    function func () {
        console.log(a);
        let a = 1;
    }
    func();  // 报错
    
    let b = 1;
    function func() {
        console.log(b);
    }
    func();  // 成功打印
    
  4. window对象的属性和方法

    全局作用域中,var声明的变量、通过function声明的函数,会自动变为window对象的属性或方法,但let、const不会

  5. 块级作用域

    var没有块级作用域,let、const有块级作用域

    作用域:块级作用域,函数作用域,全局作用域

二、模板字符串与箭头函数

模板字符串
const person = {
    username: 'name',
    age: 20,
    sex: 'male'
};
const info1 = '我叫' + person.username + ',性别: ' + person.sex + ',今年' + person.age + '岁了';
console.log(info1);

const info2 = `我叫${person.username},性别: ${person.sex},今年${person.age}岁了`;
console.log(info);

注意

  1. 模板字符串中,所有空格、换行或缩进都会被保留在输出中
  2. 只要最终可以得出一个值的就可以通过${}注入到模板字符串中
箭头函数

省略了function关键字

(x, y) => {
    return x + y;
};    // 此时是一个匿名函数

const add = (x, y) => {
    return x + y;
}

注意

  1. 单个参数:可以将参数的括号省略掉
const add = x => {
    return x + 1;
};
  1. 单行函数体:可以同时省略{}return
const add = (x, y) => {
    return x + y;
};
const add = (x, y) => x + y;
  1. 单行对象:如果箭头函数返回单行对象,可以在{}外面加上(),让浏览器不再认为那是函数体的花括号
const add = (x, y) => {
    return {
        value: x + y
    };
};

const add = (x, y) => ({
    value: x + y
});
this指向

非箭头函数的this指向

  1. 全局作用域中的this指向
console.log(this);    // window
  1. 一般函数中的this指向

this指向和函数在哪调用无关,只和谁在调用有关

// 1. 
function add() {
    console.log(this);
}
add();
// 严格模式下,指向undefined
// 非严格模式下,undefined -> window

// 2.
document.onclick = function () {
    console.log(this);
};
document.onclick();  // 指向document

// 3. 
function Person(username) {
    this.username = username;
    console.log(this);
}
const p = new Person('lisi');

箭头函数的this指向

箭头函数没有自己的this(通过作用域链查找)

不适合箭头函数的场景
  1. 作为构造函数
  2. 需要this指向调用对象的时候
  3. 需要使用arguments的时候

三、解构赋值

什么是解构赋值

解析某一数据的结构,将我们想要的东西提取出来,赋值给变量或常量

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. 模式(解构)匹配
  2. 索引值相同的完成赋值
const [a, [, , b], c] = [1, [2, 4, 5], 3];
console.log(a, b, c);    // 1, 5, 3
数组解构赋值的默认值
const [a, b] = [];  // 等价于 const [a, b] = [undefined, undefined];
console.log(a, b);    // undefined undefined

// 给取到undefined元素一个默认值
const [a = 1, b = 2] = [];
console.log(a, b);    // 1 2

默认值的生效条件

只有当一个数组成员严格等于 === undefined 时,对应的默认值才会生效

默认值表达式

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

// 如果用不到表达式,那么表达式就不会执行
const func = () => {
    console.log('执行了表达式');
};
const [a = func()] = [1];
console.log(a);   // 1
类数组的解构赋值
function func() {
    const [a, b] = arguments;
    console.log(a, b);    // 1 2
}
func(1, 2);
<p>123</p>
<p>456</p>
<script>
    const [p1, p2] = document.querySelectAll('p');
    console.log(p1, p2);
</script>
函数参数的解构赋值
const array = [1, 1];
const add = arr => arr[0] + arr[1];
// 用解构赋值之后
const add = ([a, b]) => a + b;
console.log(add(array));  // 2
交换变量的值
[x, y] = [y, x];
对象的解构赋值
  1. 模式匹配
  2. 属性名相同的完成赋值(和书写顺序无关)
const {age, username} = {username: 'lisi', age: 18};

// 完整写法
const {age: age, username: username} = {username: 'lisi', age: 18};

// 起别名
const {age: nage, username: nname} = {username: 'lisi', age: 18};
console.log(nage, nname); // 18 'lisi'
console.log(age, username); // 报错
对象解构赋值的默认值

对象的属性值严格等于undefined时,对应的默认值才会生效

const {username = 'niubi', age = 0} = {username: 'lisi'};
  1. 默认值表达式

如果默认值是表达式,那么默认值表达式是惰性求值的

  1. 将一个已经声明的变量用于解构赋值
let x = 2;
({x} = {x: 1});     // 加上圆括号,防止{}被浏览器认为是代码块
  1. 可以取到继承的属性
const {a = 1} = {};
console.log(a);   // 1

const {toString} = {};
console.log(toString);  // 不是undefined
// Object.prototype   可以取到继承的属性
console.log(Object.prototype);
其他数据类型的解构赋值
字符串的解构赋值
  1. 数组形式的解构赋值
const [a, b, , , c] = 'hello';
console.log(a,b,c);
  1. 对象形式的解构赋值
const {0: a, 1: b, length} = 'hello';    // 属性名就是索引值
数值和布尔值的解构赋值

将等号右边的值转为对象,只能获取到继承的东西

const {aa, toString} = 123;
console.log(aa, toString);
undefined 和 null 的解构赋值

由于undefined和null无法转为对象,所以对它们进行解构赋值都会报错

四、对象字面量的增强与函数参数的默认值

对象字面量的增强
对象字面量
// 对象字面量
const person = {
    age: 18,
    speak: function () {};
};
属性简洁表示

当键名和变量或常量名一样的时候,可以只写一个

const age = 18;
const person2 = {
    age: age
};

const person3 = {
    age
};
方法的简洁表示
const person = {
    // speak: function () {}
    speak(){};
};
方括号语法

给对象添加属性

const prop = 'age';
const person = {};
person[prop] = 18;

// 此时person对象如下
{age: 18}
// 方括号语法可以写在对象字面量中
const person = {
    [prop]: 18
};

方括号中可以放什么?

[值或通过计算可以得到值的(表达式)]

方括号语法和点语法的区别

点语法是方括号语法的特殊形式,当属性名或方法名是合法标识符时,可以使用点语法,其他情况使用方括号语法

函数参数的默认值

和解构赋值时设置默认值的方式类似

const multiply = (x, y) => {
    if (typeof y === 'undefined') {
        y = 1;
    }
    return x * y;
};

// 设置参数默认值
const multiply2 = (x, y = 1) => {
    return x * y;
};

注意:

  1. 默认值生效条件:不传参,或明确传undefined为参数的情况下,默认值才会生效
  2. 默认值表达式:惰性求值
  3. 函数参数的默认值最好从参数列表的右边开始设置

经典用法

const loguser = ({username = 'zhangsan', age = 0, sex = 'male'} = {}) => console.log(username, age, sex);
logUser();

五、剩余参数与展开运算符

剩余参数

当不确定要传几个参数的时候就用剩余参数

[1, 2, 3] --->  1, 2, 3
const add = (x, y, z, ...args) => {
    console.log(x, y, z, args);
};

实参与形参中的固定参数进行对应,剩余的就归剩余参数管(放入数组当中)

剩余参数永远是个数组,若没有取到值,那就是空数组

注意

  1. 箭头函数的剩余参数

    箭头函数的参数部分当只有一个剩余参数时,不能省略圆括号

    const add = (...args) => {
        
    }
    
  2. 使用剩余参数代替arguments获取实际参数

    由于箭头函数中没有arguments,所以要想获取参数数组,就可以用剩余参数

    const add = (...args) => {
        console.log(args);
    };
    add(1, 2);
    
  3. 剩余参数的位置

    剩余参数只能是最后一个参数,之后不能再有其他参数,否则会报错

数组的展开运算符

将数组的形式展开为参数列表的形式

[1, 2, 3] --->  1, 2, 3

eg. 求数组中的最小值

let min = Math.min(...[2, 1, 3]);
// 相当于  Math.min(2, 1, 3);

展开运算符应用

  1. 复制数组

    let arr = [1, 2, 3, 4];
    let arr2 = [...arr];
    
  2. 合并数组

    let arr1 = [1, 2];
    let arr2 = [3, 4];
    let arr3 = [5];
    let arr = [...arr1, ...arr2, ...arr3];
    
  3. 字符串转为数组

    [...'hello'];
    
  4. 类数组对象转换为数组

    [...arguments];
    
    [...document.querySelectorAll('p')];
    
对象的展开运算符

对象不能直接展开,必须要在{}

const student = {
    name: 'xiaohong',
    sex: 'female'
};
const student1 = {...student};

也可以将对象的属性进行合并

注意

  1. 空对象的展开

    如果展开一个空对象,则没有任何效果

  2. 非对象的展开

    如果展开的不是对象,则会自动将其转为对象,再将其属性罗列出来

    如果展开字符串,它会自动转成一个类似数组的对象,因此返回的不是空对象

    console.log({...'hello'});   // {0: 'h', 1: 'e', 2: 'l', 3: 'l', 4: 'o'}
    
  3. 不会展开对象中的对象属性

应用

  1. 复制对象

  2. 用户参数和默认参数

    const logUser = userParam => {
        const defaultParam = {
            username: 'ZhangSan',
            age: 0,
            sex: 'male'
        };
        
        const param = {...defaultParam, ...userParam};   // 将用户参数和默认参数进行合并
        console.log(param.username);
    };
    

六、Set 和 Map 数据结构

Set

Set 是一系列无序、没有重复值的数据集合

const s = new Set();
s.add(1);
s.add(2);

注意

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

    s.add(1).add(2);   // 支持连续调用
    
  2. has 方法

    判断Set中是否含有某个成员

    console.log(s.has(1));   // true
    
  3. delete 方法

    删除成员(删除不存在的成员不会报错)

  4. clear

    清空 Set

  5. forEach

    遍历set中的元素(按照成员添加进集合的顺序遍历)

    s.forEach(function (value, key, set) {   // set是当前set的引用
        console.log(value, key, set === s)  
    }, document);  // 第二个参数指定函数中的this指向
    
    // set 中value == key
    
  6. size

Set 构造函数的参数

Set 构造函数可以传入数组、字符串、arguments、NodeList、Set等

注意
  1. Set判断重复的方式

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

  2. 什么时候使用Set

    • 数组或字符串去重
    • 不需要通过下标访问,只需要遍历时
    • 为了使用Set提供的方法和属性时
Map

映射

Map 和对象都是键值对的集合

const m = new Map();
m.set('age', 18);   // 添加元素
Map 和对象的区别

对象一般用字符串当作键,而 Map 的键可以是基本数据类型,引用数据类型

Map实例的方法和属性
  1. set

    const m = new Map();
    m.set('age', 18).set(true, 'true').set('age', 20);
    console.log(m);
    // 使用set添加新成员,键如果已经存在,后添加的键值会覆盖已有的
    
  2. get

    通过键来获取值

    console.log(m.get('age'));
    
  3. has

  4. delete

  5. clear

  6. forEach

  7. size

和Set的方法用法类似

构造函数的参数
  1. 数组

    只能传二维数组,并且必须体现出键和值

    console.log(new Map([['name', 'lisi'], ['age', 18]]));
    
  2. Set、Map

    Set中也必须体现出键和值

    const s = new Set([['name', 'lisi'], ['age', 18]]);
    console.log(new Map(s));
    
    const m1 = new Map([['name', 'lisi'], ['age', 18]]);
    const m2 = new Map(m1);
    
注意
  1. 判断键名的方式

    基本遵循严格相等===,是对于NaN的判断与 === 不同,Map 中NaN 等于 NaN

  2. 什么时候使用Map

    如果只是需要 key -> value 的结构,或者需要字符串以外的值做键,使用Map更合适

七、遍历器与for…of循环

Iterator

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

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

Iterator遍历器是一个统一的遍历方式,但使用过程较繁琐,一般情况下不会直接使用Iterator遍历

ES6提供了for…of使遍历更方便

for…of
Iterator自动化
const arr = [1, 2, 3];
const it = arr[Symbol.iterator]();
let next = it.next();
console.log(next);
while (!next.done) {
    console.log(next.value);
    next = it.next();
    console.log(next);
}
使用for…of
const arr = [1, 2, 3];
for(const item of arr) {
    console.log(item);
}   // for…of循环只会遍历出那些done为false时,对应的value值
在for…of中得到数组的索引
const arr = [1, 2, 3];
for (const keys of arr.keys()) {
    console.log(keys);
}
entries()得到的是索引 + 值组成的数组的可遍历对象
for (const entries of arr.entries()) {
    console.log(entries);
}
原生可遍历与非原生可遍历

只要有 Symbol.iterator 方法,并且这个方法可以生成可遍历对象,就是可遍历的

只要可遍历,就可以使用for…of来统一遍历

原生可遍历

可以直接使用for…of遍历

  • 数组
  • 字符串
  • Set
  • Map
  • arguments
  • NodeList
非原生可遍历

默认情况下不能直接使用for…of来遍历

  • 一般对象
const person = {sex: 'male', age: 18};
person[Symbol.iterator] = () => {
    let index = 0;
    return {
        next(){
            index++;
            if (index === 1) {
                return {
                    value: person.sex,
                    done: false
                };
            } else if (index === 2) {
                return {
                    value: person.age,
                    done: false
                };
            } else {
                return {
                    done: true
                };
            }
        }
    }
}

for (const item of person) {
    console.log(item);
}
使用了iterator的地方
  • 数组的展开运算符(只要可遍历,就可以使用数组的展开运算符)

  • 数组的解构赋值(只要可遍历,就可按数组方式进行解构赋值)

    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]);
    const [a, b] = [...new Set([3, 4])];
    
  • Set和Map的构造函数

    构造函数的参数必须是可遍历的

八、ES6新增方法

1. 字符串新增方法
includes()

判断字符串中是否含有某些字符

console.log('abc'.includes('a'));   // true
console.log('abc'.includes('ab'));  // true
console.log('abc'.includes('ac'));  // false

穿第二个参数表示开始搜索的位置(默认是0)

console.log('abc'.includes('a', 0));   // true
console.log('abc'.includes('a', 1));   // false
padStart() 和 padEnd()

补全字符串长度

'm'.padStart(6,'ab');   // 'ababam'
'm'.padEnd(6, 'ab');    // 'mababa'

注意:

  • 原字符串的长度等于或大于最大长度,不会消减原字符串,字符串不全不生效,返回原字符串

  • 用来补全的字符串与原字符串长度之和超过了最大长度,截去超出位数的补全字符串,原字符串不动

    console.log('abc',padStart(10, '0123456789'));
    
  • 如果省略第二个参数,默认使用空格补全

trimStart() 和 trimEnd()

清除字符串的首或尾空格

对应和trimLeft() 和 trimRight() 一样

trim()首尾空格全部清除

replace() 和replace()

replace()用来替换第一个匹配的内容

replaceAll() 用来替换所有匹配的内容

'aabbcc'.replace('b', 'x');    // aaxbcc
'aabbcc'.replaceAll('b', 'x');    // aaxxcc
2. 数组新增方法
includes()

基本遵循严格相等,但认为NaN 等于 NaN

Array.from()

将其他数据类型转换为数组

可以转化为数组的有:

  • 数组、字符串、Set、Map、nodeList、arguments均可转换为数组

  • 拥有length属性的任意对象

第二个参数类似于map方法,对每个元素处理之后放入数组中

let arr = [1, 2, 3, 4];
const arr2 = arr.map((value) => {
    return value * 2;
});
console.log(arr2);    // [2, 4, 6, 8]   map方法会返回一个新数组
const arr = [1, 2, 3, 4];
const arr2 = Array.from(arr, value => value * 2);
console.log(arr);
console.log(arr2);

第三个参数用来修改内部的第二个参数内部的this指向

find() 和 findIndex()

find() 找到满足条件的一个立即返回

findIndex() 找到满足条件的一个,立即返回其索引

const arr = [1, 2, 3, 4, 9, 3, 4];
const num = arr.find((value) => {
    return value > 5;
});
console.log(num);    // 9

第二个参数修改第一个参数中的this指向

3. 对象新增方法
Object.assign()

用来合并对象

const apple = {
    color: '红色',
    shape: '圆形',
    taste: '甜'
};
const pen = {
    color: '黑色',
    shape: '圆柱形',
    use: '写'
};
// 合并对象
console.log(Object.assign(apple, pen));    // {color: '黑色', shape: '圆柱形', taste: '甜', use: '写'}
console.log(apple);    // {color: '黑色', shape: '圆柱形', taste: '甜', use: '写'}
console.log(pen);      // {color: '黑色', shape: '圆柱形', use: '写'}
// 该方法直接合并到了第一个参数中,返回的就是合并后的对象

如果要返回一个新对象,可以用下面的方法

Object.assign({}, apple, pen);

注意

基本数据类型作为源对象

与对象的展开类似,先转换成对象再合并

console.log(Object.assign({}, undefined));
Object.keys()、Object.values()、Object.entries()
const person = {
    name: 'lisi',
    age: 18
};

console.log(Object.keys(person));        // ['name', 'age']
console.log(Object.values(person));      // ['lisi', 18]
console,log(Object.entries(person));     // 二维数组

返回的都是数组

使用for…of遍历对象

const person = {
    name: 'lisi',
    age: 18
};        
for (const person1 of Object.entries(person)) {
    console.log(person1);
}    // 不能保证顺序

九、Promise

Promise 是异步操作的一种解决方案

一般用来解决层层嵌套的回调函数的问题

解决的不是回调函数,而是回调地狱

const p = new Promise(() => {});    // pending 未完成
1. Promise 的状态
const p = new Promise((resolve, reject) => {
    resolve();   // pending -> fulfilled 已成功
    // reject();    // pending -> rejected  已失败 
});

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

2. then 方法
p.then(() => {
    console.log('success');
}, () => {
    console.log('fail');
});   // 已成功 执行第一个函数,已失败,执行第二个函数
执行后的返回值

then 方法执行后返回一个新的Promise 对象

const p = new Promise((resolve, reject) => {
    resolve('resolve');
});
p.then(data => {
    console.log('succ1', data);    // succ1 resolve
}, data2 => {
    console.log('err1', data2);   
    // return undefined;   // 默认返回undefined
    // 相当于
    return new Promise(resolve => {
        resolve(undefined);
    });
}).then(data3 => {
    console.log('succ2', data3);    // succ2 undefined
}, (data4) => {
    console.log('err2', data4);
});
3. catch() 方法

catch() 本质上是then() 的特例,专门用来处理rejected 状态

const p = new Promise((resolve, reject) => {
   reject('fail');
});
p.then(null, data => {
    console.log(data);
});

个人理解:then() 和 chtch() 都是用来捕获Promise的执行状态的,then() 既可以捕获resolve,也可以捕获reject,而catch() 只能捕获reject,两者都是只要捕获成功就返回一个fulfilled状态的Promise,否则catch() 没反应,then() 会报错

catch() 可以捕获它前面的错误

一般建议:Promise 对象后面要跟catch 方法,这样可以处理Promise内部的错误

4. finally() 方法

也是then() 方法的特里,返回传过来的值(不分状态)

6. 构造函数方法
Promise.resolve()

是成功状态Promise的一种简写形式

new Promise(resolve => resolve('succ'));
// 相当于
Promise.resolve('succ');
  • 可以传的参数
  1. 一般参数

  2. Promise对象

    当参数为Promise对象时,直接返回这个Promise对象,什么都不做

    const p = new Promise(resolve => resolve('succ'));
    Promise.resolve(p).then(data => {
        console.log(data);
    });
    

    当resolve函数接收的时Promise对象时,后面的then会根据传递的Promise对象的状态变化决定执行哪一个回调

    const p = new Promise(resolve => resolve('succ'));
    const p2 = new Promise(resolve => resolve(p)).then(data => {
        console.log(data);
    });   // 个人理解,这其实相当于是Promise对象的复制
    
  3. 具有then方法的对象

    const obj = {
        then() {
            console.log('obj--then');
        }
    };
    Promise.resolve(obj);    // 传入之后会执行obj中的then方法,并返回一个Promise对象,状态为pending
    // obj的then方法可以有resolve和reject两个参数,来决定状态
    

    // 参数是其他值时,相当于时通过resolve传参

Promise.reject()

是失败状态Primise的一种简写形式

const p = new Promise((resolve, reject) => {
    reject('fail');
});
Promise.reject('fail');
  • 参数

不管什么参数,都会原封不动的向后传递,作为后续方法的参数

Promise.all()

关注多个Promise对象的状态变化

传入多个Promise实例,包装成一个新的Promise实例返回

Promise.all() 的状态变化与所传入的Promise实例对象状态有关

所有状态都变成resolved,最终状态才会变成resolved

只要有一个变成rejected,最终的状态就会变成rejected

Promise.race()

它的状态取决于第一个完成的 Promise实例对象,如果第一个完成的成功了,那最终的就成功;如果第一个完成的失败了,那最终的就失败

Promise.allSettled()

Promise.allSettled() 的状态与传入的Promise状态无关,永远都是成功的

它只会忠实的记录下各个Promise的表现,分别以对象的形式表现

7. 注意
resolve或reject函数执行后的代码

后面的代码还可以继续执行

建议在调用这两个函数的时候加上return,不再执行它们后面的代码

const p = new Promise((resolve, reject) => {
    return resolve('resolve');
});
Promise.all / race / allSettled的参数问题
  1. 参数如果不是Promise数组,会将不是Promise的数组元素转变为Promise对象(成功状态)

  2. 任何可遍历的都可以作为参数

  3. Promise.all / race / allSettled的错误处理

八、Class类

1. 初始Class
class Person {
    constructor(name, age) {
        this.name = name;
        this.age = age;
    }
    sayHello() {
        console.log(this.name, this.age);
    }
}
const p = new Person('姓名', 18);
console.log(p);
p.sayHello();

// class 和 构造函数其实底层是一样的,只是在语法层面是class,但作用效果和构造函数一样
两种定义形式
// 声明形式
class Person {}

// 表达式形式
const Person2 = class {}

// 立即执行的类
new (class {
    constructor() {
        console.log('constructor');
    }
})();
2. 属性与方法
实例属性和实例方法
class Person {
    age = 18;    // 实例属性
    sex = 'male';
    static version = '1.0';     // 静态属性(目前只是提案,有兼容性问题),可以考虑将它写成方法的形式
    static getVersion() {
        return '1.0';
    }
    getAge = function () {    // 实例方法
        return this.age;
    }
    constructor(age, sex) {    // 构造方法
        this.age = age;
        this.sex = sex;
    }
    static getAge() {         // 静态方法
        console.log('666');
    }
}
私有属性和方法

一般情况下,类的属性和方法都是公开的,公有属性和方法可以被外界修改,造成意向不到的后果

  • 模拟私有属性和方法
  1. _开头表示私有

  2. 将私有属性和方法移除类

(function () {
    let name = '';
    class Person {
        constructor(username) {
            name = username;
        }
        getName() {
            return name;
        }
    }
    window.Person = Person;
})();
(function () {
    const p = new Person('lisi');
    console.log(p.name);    // undefined
    console.log(p.getName());     // lisi
})();
3. 继承
entends
super
  1. 在构造方法中使用

    super 代表父类的原型对象Person.prototype

    所以定义在父类实例上的方法或属性,无法通过super调用

    通过super调用父类的方法时,方法内部的this指向当前子类的实例

  2. 在静态方法中使用

    super 指向父类,而不是父类的原型对象

    通过super调用父类的方法时,方法内部的this指向当前的子类,而不是子类的实例

注意:使用super的时候,必须显式指定是作为函数还是作为对象使用

九、Module模块

模块:一个一个的局部作用域的代码块

模块内部默认是严格模式

模块系统需要解决的问题:

  1. 模块化的问题
  2. 消除全局变量
  3. 管理加载顺序
1. Module的两种导出和导入方式
export default和import

导出方式

const age = 18;
export default age;
// export default 18;
// 也可导出对象等

一个模块没有导出,也可以将其导入

import './index.js';

被导入的代码都会执行一遍,也仅会执行一遍

一个模块只能有一个export default

导入

import age from './index.js';
// import './index.js';
export 和 import

导出方式

// export 声明或语句
export const age = 555;

// const age = 5;
//export {age};

导入

import {age} from 'index.js';

多个导入导出,起别名

export {fn as func, className, age};
import {func, className as person, age} from './index.js';

整体导入

会导入所有导出,包括 export default 导出的

import * as obj from './index.js';

同时导入

// 一定是export default的在前
import age, {func, age, className} from './index.js';
2. 注意
模块顶层的this

模块顶层的this指向undefined

可以利用这个做提示信息

// 在要作为模块的的js文件中,如果未被按照模块的方式加载,进行提示
if (typeof this !== "undefined") {
    throw new Error('请使用模块化加载');
}
import 和 import()

import 命令具有提升效果,会提升到整个模块的头部,率先执行

import 和 export 命令只能在模块的顶层,不能在代码块中执行

import() 可以按条件导入

if (PC) {
    import('pc.js').then().catch();
} else if (Mobile) {
    import('mobile.js').then().catch();
}
导入导出复合写法
export {age} from './module.js';   // 复合写法export的东西无法在当前模块中使用
// 等价于
import {age} from './module.js';
export {age} from './module.js';

相当于中转站(不推荐写)

十、Babel与Webpack

1. Babel

Babel是js的一个编译器,将新版本的代码转换成浏览器都兼容支持的ES5或ES3版本的代码,兼容性问题交给Babel来处理

Babel本身可以编译ES6的大部分语法,如let,const,箭头函数,类

但是对于ES6新增的API,如Set,Map,Promise等全局对象,以及一些定义在全局对象上的定义,如Object.assign / Array.from 都不能直接编译,需要借助其他模块

Babel一般需要配合Webpack来编译模块语法

准备

node.js:是一个平台或者工具,对应浏览器

npm:node包管理工具

npm init

package.json:记录安装的包

  1. 安装node.js

  2. 初始化项目

    npm init -> package.json
    
  3. 安装Babel需要的包

    // 切换安装源,把源从国外切换到国内
    npm config set registry https://registry.npm.taobao.org
    
    npm install --save-dev @babel/core @babel/cli
    

使用Babel编译ES6代码

  1. 执行编译命令,在package.json文件中添加执行babel的命令
"scripts": {
  "build": "babel src -d dist"   // 使用babel命令将src目录中的文件编译并输出到dist目录中
    // babel src --out-dir dist
},
  1. babel的配置文件
npm install @babel/preset-env --save-dev
  1. 创建.babelrc文件
{
  "presets": ["@babel/preset-env"]
}
  1. npm run build
2. Webpack

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

模块:webpack可以处理js / css / 图片 / 图标字体等单位

静态:开发过程中存在于本地的 js/css 图片/ 图标字体等文件,就是静态的(从远程服务器获取到的就是动态的,webpack无法处理)

使用
  1. 初始化项目

    npm init
    
  2. 安装Webpack需要的包

    npm install --save-dev webpack-cli webpack
    
  3. 配置webpack

    创建webpack.config.js文件

    const path = require('path');    // path是node自己提供的一个模块(导入)
    
    module.exports = {         // 导出
        mode: 'development',
        entry: './src/index.js',
        output: {
            path: path.resolve(__dirname, 'dist'),
            filename: 'bundle.js'
        }
    };
    
    "scripts": {
      "webpack": "webpack --config webpack.config.js"
    },
    
  4. npm run webpack
    
核心概念
  1. entry

    单入口

    entry: './src/index.js',
    

    多入口

    entry: {
        main: './src/index.js',
        search: './src/search.js'
    },
    
  2. output

    对应单入口

    output: {
        path: path.resolve(__dirname, 'dist'),
        filename: 'bundle.js'
    }
    

    对应多入口

    output: {
        path: path.resolve(__dirname, 'dist'),
        filename: '[name].js'
    }
    
  3. loader

loader让webpack能够去处理那些非js文件的模块(有的loader也可以处理js文件)

帮助webpack处理各种模块

babel-loader将babel和webpack连接了起来,先完成代码编译再进行模块打包

使用

  1. npm install --save-dev babel-loader@8.1.0 @babel/core @babel/preset-env
    
  2. .babelrc配置文件

    {
      "presets": ["@babel/preset-env"]
    }
    
  3. webpack.config.js文件中添加

    module: {
        rules: [
            {
                test: /\.js$/,      // 想在哪些文件上面使用babel-loader
                exclude: /node_modules/,
                loader: 'babel-loader'    // 使用 babel-loader
            }      // 不同的loader放在不同的对象中
        ]
    }
    
  4. npm run webpack
    

    这样的操作相当于把之前在命令行中的操作放到了webpack中,并不会编译新增API

    加入@babel/polyfill

  5. npm install --save-dev core-js@3.6.5
    
  6. 引入core-js,在代码开头加

    import 'core-js/stable';
    
  7. npm run webpack
    

配置loader

3. plugins

插件用于执行范围更广的任务

html-webpack-plugin
  1. 安装插件

    npm install --save-dev html-webpack-plugin
    
  2. 在配置文件webpack.config.js中将其引入

    // 导入插件
    const HtmlWebpackPlugin = require('html-webpack-plugin');
    // 在module.exports中加入
    plugins: [
        new HtmlWebpackPlugin({   // 几个入口就创建几个实例
            template: './3.html',    // 指定一个html作为模板,最终打包完成后的js文件放在模板中
            filename: 'index.html',
            chunks: ['index'],     // 指明要引入哪一个js文件(entry那里的名字)
            minify: {      // html-webpack-plugin的其他功能
                // 删除index.html 中的注释
                removeComments: true,
                // 删除index.html 中的空格
                collapseWhitespace: true,
                // 删除各种html标签属性值的双引号
                removeAttributeQuotes: true
            }
        })
    ]
    
  3. 运行webpack打包

    npm run webpack
    
处理css文件
  1. 需要安装css-loader

    // 帮助webpack去识别css文件
    npm install --save-dev css-loader    // 识别css
    npm install --save-dev style-loader    // 处理css   将样式添加到了style标签中
    
    module: {
        rules: [
            {
                test: /\.css$/,
                use: ['style-loader' ,'css-loader']    // 从右向左执行
            }
        ],
    },
    

要想使css通过link来引入,就用mini-css-extract-plugin

new MiniCssExtractPlugin({
	filename: 'css/[name].css'
})
module: {
    rules: [
        {
            test: /\.css$/,
            use: [MiniCssExtractPlugin.loader, 'css-loader']
        }
    ]
}
使用file-loader处理css中的本地图片
{
    test: /\.(png|jpg|gif)/,
    use: {
        loader: "file-loader",
        options: {
            name: 'img/[name].[ext]'     // 图片复制到dist的img目录下并保持原名
        }
    }
},

使用html-withimg-loader来处理html中的图片

解析HTML文件,和file-loader配合使用

使用url-loader可以对js中的图片进行处理(将图片作为模块引入)

将一下比较小的图片进行base64编码

配合file-loader一起使用

{
    test: /\.(jpg|png|gif)$/,
    use: {
        loader: 'url-loader',
            options: {
                name: 'img/[name].[ext]',
                esModule: false,
                limit: 3000    // 当图片小于3K的时候进行base64编码
            }
    }
}

各个包的版本号

"css-loader": "^4.1.1",
"file-loader": "^6.0.0",
"html-webpack-plugin": "^4.3.0",
"mini-css-extract-plugin": "^0.9.0",
"style-loader": "^1.2.1",
"webpack": "^4.44.1",
"webpack-cli": "^3.3.12",
"html-withimg-loader": "^0.1.16",
"url-loader@4.1.0"
4. 使用webpack-dev-serve搭建开发环境

webpack-dev-server@3.11.0

在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

_错错错

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值