ES6基础入门
初识ES6
- ES与Javascript的关系 JavaScript(浏览器端)= ECMAScript(语法+API) + DOM+BOM
let和const是什么
- let和const是什么
- let代替var声明变量
- const声明常量
-
let和const的用法
let age = 18
const sex = 'male' -
什么是变量,什么是常量
- var、let声明的就是变量,变量一旦初始化之后,还可以重新赋值
- const 声明的就是常量,常量一旦初始化,就不能重新赋值了,否则就会报错
const
-
为什么需要const 为了那些一旦初始化就不希望重新赋值的情况设计的
-
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'}
- 什么时候用 const,什么时候用 let
for (let i = 0; i < 3; i++) {}
const username = 'Alex'; //在不知道是否会改变值的时候,先用const,之后值改变就会出现报错
username = 'ZhangSan'; //报错
let const 与var的区别
- 重复声明( 已经存在的变量或常量,又声明了一遍) var 允许重复声明,let、const 不允许
function fun(a){
let a=1
}
fun()
//此时会报错,因为a已经作为函数参数声明过一遍了,函数内部又声明了一遍
- 变量提升
- var 会提升变量的声明到当前作用域的顶部
console.log(a);
var a = 1;
相当于
var a;
console.log(a);
a = 1;
- let、const 不存在变量提升
(养成良好的编程习惯,对于所有的变量或常量,做到先声明,后使用)
- 暂时性死区 只要作用域内存在 let、const,它们所声明的变量或常量就自动“绑定”这个区域,不再受到外部作用域的影响
let a = 2;
function func() {
console.log(a); //报错
let a = 1;
}
func();
- 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);
作用域链:内层作用域->外层作用域->...->全局作用域
- 有哪些块级作用域
- {}
- for(){}
- while(){}
- do{}while()
- if(){}
- switch(){}
- function(){}
- const person = { getAge: function () {} };
模板字符串与箭头函数
模板字符串
- 认识模板字符串
const username2 = `alex`;
- 模板字符串与一般字符串的区别
const person = {
username: 'Alex',
age: 18,
sex: 'male'
};
const info = `我的名字是:${person.username}, 性别:${person.sex}, 今年${person.age}岁了`;
console.log(info);
和其他东西一起使用的时候,使用模板字符串,方便注入
其他情况下使用模板字符串或一般字符串都行
- 模板字符串的注意事项
- 输出多行字符串
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()
- 箭头函数中的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没有定义
解构赋值
数组的解构赋值
- 解构赋值是什么
- 认识解构赋值
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, 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
- 数组解构赋值的默认值
- 默认值的基本用法
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
- 数组解构赋值的应用
- 常见的类数组的解构赋值
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);
对象的解构赋值
- 对象解构赋值的原理
- 模式(结构)匹配 {}={}
- 属性名相同的完成赋值
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
- 对象解构赋值的注意事项
- 默认值的生效条件
对象的属性值严格等于 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中
其他数据类型的解构赋值
- 字符串的解构赋值
例: ''='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
字符串既可以按数组形式来解构赋值,也可以按对象形式来解构赋值
- 数值和布尔值的解构赋值 先将等号右边的值转为对象
console.log(new Number(123)); //Number{123}
const { a = 1, toString } = 123;
console.log(a, toString); //1 function toString()
- undefined 和 null 的解构赋值
由于 undefined 和 null 无法转为对象,所以对它们进行解构赋值,都会报错
const { toString } = undefined;
const { toString } = null;
对象字面量的增强
属性和方法的简洁表示法
- 对象字面量是什么
实例化构造函数生成对象
// 对象字面量
const person = {
age: 18,
speak: function () {}
};
- 属性的简洁表示法 键名和变量或常量名一样的时候,可以只写一个
const age = 18;
const person = {
age
};
console.log(person);
- 方法的简洁表示法 方法可以省略冒号和 function 关键字
const person = {
// speak: function () {}
speak() {}
};
console.log(person);
方括号语法
- 方括号语法的用法
// 方括号语法可以写在对象字面量中
const person = {
[prop]: 18
};
console.log(person); //18
- 方括号中可以放什么
// ${}
// [值或通过计算可以得到值的(表达式)]
const prop = 'age';
const func = () => 'age2';
const person = {
['s' + 'ex']: 'male'
};
console.log(person);
- 方括号语法和点语法的区别
点语法是方括号语法的特殊形式
person.age 等价于 person['age']
属性名由数字、字母、下划线以及$构成,并且数字还不能打头的时候可以使用点语法
合法标识符可以用来作为变量或常量名
当你的属性或方法名是合法标识符时,可以使用点语法,其他情况下请使用方括号语法
函数参数默认值
-
认识函数参数的默认值 调用函数的时候传参了,就用传递的参数;如果没传参,就用默认值1
multiply(2, 1); 等同于multiply(2); -
函数参数默认值的基本用法
const multiply = (x, y = 1) => x * y;
console.log(multiply(2));
默认值的注意事项
- 默认值的生效条件
- 不传参数,或者明确的传递 undefined 作为参数,只有这两种情况下,默认值才会生效
-
默认值表达式 如果默认值是表达式,默认值表达式是惰性求值的
-
设置默认值的小技巧 函数参数的默认值,最好从参数列表的右边开始设置
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语法扩展
剩余参数
是什么
- 认识剩余参数
const add = (x, y, z, ...args) => {}; - 剩余参数的本质
const add = (x, y, ...args) => {
console.log(x, y, args); //1 2 [3,4,5]
};
add(1, 2, 3, 4, 5);
剩余参数永远是个数组,即使没有值,也是空数组
注意事项
-
箭头函数的剩余参数 箭头函数的参数部分即使只有一个剩余参数,也不能省略圆括号
const add = (...args) => {}; -
使用剩余参数替代 arguments 获取实际参数
const add = function () {
console.log(arguments);
}; //用下面的来替换
const add = (...args) => {
console.log(args);
};
add(1, 2);
- 剩余参数的位置 剩余参数只能是最后一个参数,之后不能再有其他参数,否则会报错
应用
- 完成 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));
- 与解构赋值结合使用 剩余参数不一定非要作为函数参数使用
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 });
展开运算符
数组展开运算符
- 认识展开运算符
[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
- 数组展开运算符的基本用法
// console.log(Math.min(...[3, 1, 2]));
// 相当于
console.log(Math.min(3, 1, 2));
- 区分剩余参数和展开运算符
- 根本区别
展开运算符
[3,1,2]->3,1,2剩余参数3,1,2->[3,1,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]);//展开运算符将二维数组转化为一维运算符
- 应用
- 复制数组
const a = [1, 2];;
const c = [...a];
a[0] = 3;
console.log(a);
console.log(c); //[1,2]
- 合并数组
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]
- 字符串转为数组 字符串可以按照数组的形式展开
console.log(...'alex');
console.log('a', 'l', 'e', 'x');
console.log([...'alex']); //['a','l','e','x']
console.log('alex'.split('')); //ES6之前的方法
- 常见的类数组转化为数组 arguments
function func() {
console.log([...arguments]);
}
func(1, 2);
NodeList
console.log(document.querySelectorAll('p'));
console.log([...document.querySelectorAll('p')].push);
对象的展开运算符
- 基本用法
- 展开对象 对象不能直接展开,必须在 {} 中展开
const apple = {
color: '红色',
shape: '球形',
taste: '甜'
};
对象的展开:把属性罗列出来,用逗号分隔,放到一个 {} 中,构成新对象
console.log({ ...apple });
console.log({ ...apple } === apple); //false 得到了一个新的对象
- 合并对象
const apple = {
color: '红色',
shape: '球形',
taste: '甜'
};
const pen = {
color: '黑色',
shape: '圆柱形',
use: '写字'
};
console.log({ ...apple, ...pen });
// 后面的属性会覆盖前面的
相当于
console.log({
use: '写字',
color: '红色',
shape: '球形',
taste: '甜'
});
- 注意事项
-
空对象的展开 如果展开一个空对象,则没有任何效果
-
非对象的展开
如果展开的不是对象,则会自动将其转为对象,再将其属性罗列出来
如果展开运算符后面是字符串,它会自动转成一个类似数组的对象,因此返回的不是空对象
console.log({ ...'alex' });//{0:a,1:l,2:e,3:x}
console.log({ ...[1, 2, 3] });//{0:1,1:2,2:3} -
对象中对象属性的展开 不会展开对象中的对象属性
const apple = {
feature: {
taste: '甜'
}
};
const pen = {
feature: {
color: '黑色',
shape: '圆柱形'
},
use: '写字'
};
// console.log({ ...apple });
// console.log({ ...apple, ...pen });
// 相当于
console.log({
feature: {
color: '黑色',
shape: '圆柱形'
},
use: '写字'
});
- 应用
- 复制对象
const a = { x: 1, y: 2 };
// const b = a;
const c = { ...a };
console.log(c, c === a);
- 用户参数和默认参数
const logUser = userParam => {
const defaultParam = {
username: 'ZhangSan',
age: 0,
sex: 'male'
};
const param = { ...defaultParam, ...userParam };
console.log(param.username); //zahngsan
};
logUser();
Set
Set是什么
-
什么是 Set 集合
数组是一系列有序的数据集合
Set 是一系列无序、没有重复值的数据集合 -
理解 Set Set 中不能有重复的成员
Set 没有下标去标示每一个值,所以 Set 是无序的,也不能像数组那样通过下标去访问 Set 的成员
set实例的方法和属性
- 方法
- 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)
- 属性 size
Set构造函数的参数
- 数组
const s = new Set([1,2,1]);
console.log(s); //Set(1,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); //false
Set的注意事项
- 判断重复的方式
Set 对重复值的判断基本遵循严格相等(===)
但是对于 NaN 的判断与 === 不同,Set 中 NaN 等于 NaN
//对于对象而言
const s = new Set();
s.add({}).add({});
console.log({} === {}); //false
console.log(s); //{}{}
- 什么时候使用 Set
- 数组或字符串去重时
- 不需要通过下标访问,只需要遍历时
- 为了使用 Set 提供的方法和属性时(add delete clear has forEach size 等)
Set的应用
-
数组去重
console.log([...new Set([1, 2, 1])]); -
字符串去重
- 'abbacbd';
const s = new Set('abbacbd');
console.log([...s].join('')); //join:数组转化为字符串
console.log(s);
//即
console.log([...new Set('abbacbd')].join(''));
- 存放 DOM 元素
const s = new Set(document.querySelectorAll('p'));
s.forEach(function(elem){
elem.style.color = 'red';
elem.style.vackground = 'yellow';
});
Map
是什么
- 认识 Map
映射
Map 和对象都是键值对的集合
键->值,key->value
const m = new Map();
m.set('name', 'alex');
m.set('age', 18);
console.log(m); //{name->'Alex',age->18}
- 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);
属性和方法
- 方法
- 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);
- 属性 size 对象没有size属性
参数
- 数组 只能传二维数组,而且必须体现出键和值
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
注意事项
- 判断键名是否相同的方式 基本遵循严格相等(===) 例外就是 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}
- 什么时候使用 Map
如果只是需要 key -> value 的结构,或者需要字符串以外的值做键,使用 Map 更合适
只有模拟现实世界的实体时,才使用对象
遍历器与for..of循环
Iterator(遍历器)
-
Iterator 的作用 Iterator:遍历器(迭代器)
-
寻找 Iterator
console.log([1, 2][Symbol.iterator]());//Array Iterator{ } -
使用 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:可遍历对象的生成方法
-
什么是 Iterator Symbol.iterator(可遍历对象的生成方法) -> it(可遍历对象) -> it.next() -> it.next() -> ...(直到 done 为 true)
-
为什么需要 Iterator 遍历器
-
遍历数组:for 循环和 forEach 方法
-
遍历对象:for in 循环 Iterator 遍历器是一个统一的遍历方式
// console.log([][Symbol.iterator]()); // console.log({}[Symbol.iterator]);
- 如何更方便的使用 Iterator
Symbol.iterator->it->next()
我们一般不会直接使用 Iterator 去遍历 而是使用for..of
for...of的用法
- 认识 for...of
const arr = [1, 2, 3];
for (const item of arr) {
console.log(item);
}
for...of 循环只会遍历出那些 done 为 false 时,对应的 value 值
- 与 break、continue 一起使用
const arr = [1, 2, 3];
for (const item of arr) {
if (item === 2) {
// break;
continue;
}
console.log(item); //1 3
}
- 在 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
}
原生可遍历和非原生可遍历
-
什么是可遍历 只要有 Symbol.iterator 方法,并且这个方法可以生成可遍历对象,就是可遍历的
只要可遍历,就可以使用 for...of 循环来统一遍历 -
原生可遍历的有哪些
- 数组
- 字符串
- 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';
}
- 非原生可遍历的有哪些
- 一般的对象
- 有 length 和索引属性的对象
使用了Iterator的场合
-
for...of 针对的是可遍历的
-
数组的展开运算符
console.log(...[1, 2, 3]);
console.log(1, 2, 3);
console.log(...'str');
console.log(...new Set([1, 2, 3]));
console.log(...{}); ×
- 数组的解构赋值
const [a, b] = [1, 2];
const [a, b] = [...'hi'];
const [a, b] = [...new Set([3, 4])];
console.log(a, b);
- Set 和 Map 的构造函数
new Set(iterator)
new Map(iterator)
ES6的新增方法
字符串的新增方法
- includes() 判断字符串中是否含有某些连续字符
-
基本用法
console.log('abc'.includes('ac')); // false -
第二个参数 表示开始搜索的位置,默认是 0
console.log('abc'.includes('a', 0)); -
应用 例:把 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() 补全字符串长度
- 基本用法
console.log('x'.padStart(5, 'ab')); //ababx
console.log('x'.padEnd(5, 'ab')); //xabab
console.log('x'.padEnd(4, 'ab')); //xaba
-
注意事项 原字符串的长度,等于或大于最大长度,不会消减原字符串,字符串补全不生效,返回原字符串
console.log('xxx'.padStart(2, 'ab'));//xxx
用来补全的字符串与原字符串长度之和超过了最大长度,截去超出位数的补全字符串,原字符串不动
如果省略第二个参数,默认使用空格补全长度
console.log('x'.padStart(4));//四个空格 + x -
应用 显示日期格式
console.log('10'.padStart(2, 0)); //10
console.log('1'.padStart(2, 0)); //01
- trimStart()和trimEnd() 清除字符串的首或尾空格,中间的空格不会清除
- 应用
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()
-
基本用法 判断数组中是否含有某个成员
console.log([1,2,3].includes(2);
第二个参数表示搜索的起始位置,默认值是 0
console.log([1,2,3].includes(2,2));
基本遵循严格相等(===),但是对于 NaN 的判断与 === 不同,includes 认为 NaN === NaN -
应用 去重 [1, 2, 1];
const arr = [];
for(const item of [1,2,1]) {
if(!arr.includes(item)) {
arr.push(item);
}
}
console.log(arr);
- Array.from() 将其他数据类型转换成数组
-
基本用法
console.log(Array.from('str'));// ['s','t','r'] -
哪些可以通过 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));
- 第二个参数 作用类似于数组的 map 方法,用来对每个元素进行处理,将处理后的值放入返回的数组
console.log(Array.from('12', value => value * 2)); //[2,4]
console.log(Array.from('12').map(value => value * 2)); //[2,4]
- 第三个参数
Array.from(
'12',
value => {
console.log(this); //window
},
document
);
Array.from(
'12',
function () {
console.log(this); //document
},
document
);
- find()和findIndex()
find():找到满足条件的一个立即返回
findIndex():找到满足条件的一个,立即返回其索引
- 基本用法
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
- 应用
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() 用来合并对象
-
基本用法
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
- 注意事项
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. 同名属性的替换
后面的直接覆盖前面的
- 应用
合并默认参数和用户参数
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()
- 基本用法
const person = {
name: 'Alex',
age: 18
};
console.log(Object.keys(person));
console.log(Object.values(person));
console.log(Object.entries(person));
- 与数组类似方法的区别
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() 等方法是构造函数方法,返回的是数组
- 使用 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
-
认识 Promise
Promise 是异步操作的一种解决方案 -
什么时候使用 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的基本用法
-
实例化构造函数生成实例对象 Promise 解决的不是回调函数,而是回调地狱
-
Promise 的状态
const p = new Promise((resolve, reject) => {reject()});
Promise 有 3 种状态,
一开始是 pending(未完成),执行 resolve,变成 fulfilled(resolved),已成功
执行 reject,变成 rejected,已失败
Promise 的状态一旦变化,就不会再改变了 -
then 方法
p.then(
() => {
console.log('success');
},
() => {
console.log('error');
}
);
- 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()
- 什么时候执行
pending->fulfilled 时,执行 then 的第一个回调函数
pending->rejected 时,执行 then 的第二个回调函数 - 执行后的返回值 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
- 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 对象
- 使用 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()
-
有什么用 catch 专门用来处理 rejected 状态
catch 本质上是 then 的特例
then(null, err => {}); -
基本用法
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()
- 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));
- 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()
-
有什么用 Promise.all() 关注多个 Promise 对象的状态变化
传入多个 Promise 实例,包装成一个新的 Promise 实例返回 -
基本用法
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');
});
- Promise.race()
Promise.race() 的状态取决于第一个完成的 Promise 实例对象,
如果第一个完成的成功了,那最终的就成功;如果第一个完成的失败了,那最终的就失败
const racePromise = Promise.race([p1, p2]);
racePromise.then(
data => {
console.log(data);
},
err => {
console.log(err);
}
);
- Promise.allSettled()
Promise.allSettled() 的状态与传入的Promise 状态无关
永远都是成功的
它只会忠实的记录下各个 Promise 的表现
const allSettledPromise = Promise.allSettled([p1, p2]);
allSettledPromise.then(data => {
console.log('succ', data);
});
注意事项
- resolve 或 reject 函数执行后的代码
推荐在调用 resolve 或 reject 函数的时候加上 return,不再执行它们后面的代码
new Promise((resolve, reject) => {
// return resolve(123);
return reject('reason');
console.log('hi');
});
- 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);
});
- 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是什么
-
认识 Class 类可以看做是对象的模板,用一个类可以创建出许多不同的对象
-
Class 的基本用法 类名一般大写
class Person {}√
class Person {
// 实例化时执行构造方法,所以必须有构造方法,但可以不写出来
constructor(name, age) {
// console.log('实例化时执行构造方法');
// this 代表实例对象,上面定义的是实例属性/方法
this.name = name;
this.age = age;
// 一般在构造方法中定义属性,方法不在构造方法中定义
// this.speak = () => {};
}
// 各实例共享的方法
speak() {
console.log('speak');
}
}
- 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的两种定义形式
- 声明形式
class Person {
constructor() {}
speak() {}
}
- 表达式形式 (了解)
const Person = class {
constructor() {
console.log('constructor');
}
speak() {}
};
new Person();
立即执行的匿名类
new (class {
constructor() {
console.log('constructor');
}
})();
实例属性、静态方法和静态属性
- 实例属性 方法就是值为函数的特殊属性
class Person {
age = 0;
sex = 'male';
getSex = function () {
return this.sex;
};
constructor(){}
}
const p = new Person('Alex');
console.log(p.age);
- 静态方法 就是类的方法
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();
- 静态属性 就是类的属性
class Person {
constructor(name) {
this.name = name;
}
static getVersion() {
return '1.0';
}
}
console.log(Person.getVersion());
私有属性和方法
-
为什么需要私有属性和方法 一般情况下,类的属性和方法都是公开的
公有的属性和方法可以被外界修改,造成意想不到的错误 -
模拟私有属性和方法 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是什么
-
什么是模块 模块:一个一个的局部作用域的代码块
-
什么是模块系统 模块系统需要解决的主要问题
① 模块化的问题 ② 消除全局变量 ③ 管理加载顺序
ES Module
Module的基本用法
babel
babel是什么
-
认识 Babel
官网:babeljs.io/
在线编译:babeljs.io/repl
Babel 是 JavaScript 的编译器,用来将 ES6 的代码,转换成 ES6 之前的代码 -
使用 Babel (见代码1.html)
-
解释编译结果
- Babel 本身可以编译 ES6 的大部分语法,比如 let、const、箭头函数、类
- 但是对于 ES6 新增的 API,比如 Set、Map、Promise 等全局对象,以及一些定义在全局对象上的方法(比如 Object.assign/Array.from)都不能直接编译,需要借助其它的模块
- Babel 一般需要配合 Webpack 来编译模块语法
babeljs.io/setup
使用babel前的准备工作
- 什么是 Node.js 和 npm
Node.js 是个平台或者工具,对应浏览器
后端的 JavaScript = ECMAScript + IO + File + ...等服务器端的操作
npm:node 包管理工具
-
安装 node -v / npm -v
-
初始化项目 npm init -> package.json
-
安装 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代码
- 执行编译的命令 在 package.json 文件中添加执行 babel 的命令
"scripts": {
"build": "babel src -d dist"
//"build": "babel src --out dir dist"
},
npm run build
- Babel 的配置文件
.babelrc
npm install @babel/preset-env@7.11.0 --save-dev
即可创建配置出文件 .babelrc,并配置出
{
"presets": ["@babel/preset-env"]
}
webpack
webpack基础
webpack是什么
-
认识 Webpack webpack 是静态模块打包器,当 webpack 处理应用程序时,会将所有这些模块打包成一个或多个文件
-
什么是 Webpack 模块:webpack 可以处理 js/css/图片、图标字体等单位
静态
开发过程中存在于本地的 js/css/图片/图标字体等文件,就是静态的
动态的内容,webpack没办法处理,只能处理静态的
使用webpack
-
初始化项目
npm init -
安装 webpack 需要的包
npm install --save-dev webpack-cli@3.3.12 webpack@4.44.1 -
配置 webpack webpack.config.js
"webpack": "webpack --config webpack.config.js" -
编译并测试
npm run webpack
webpack的核心概念
entry和output
- entry:指定入口文件
- output:出口文件