ES6
ES6
1、ES6是什么?
ECMAScript6,ECMAScript是语言的标准,6是版本号。
ECMAScript=由EMCA这个标准化组织制定的一种语言标准。
Javascript与ES6区别
Javascript=ES6(语法+API)+DOM+BOM
2、let和const
let和const是什么
声明变量和声明常量,let可以代替var声明变量,const(constant)声明常量。
什么是变量,什么是常量
var和let声明的变量后续可以对值进行修改,const声明的常量初始化后不能对值进行修改。
let和const的用法
const:
const一旦声明后就要立即初始化,不能留到后面进行赋值。
const允许在不重新赋值的情况下修改值:只用于引用类型
const person={name:'username'};
person.name='name';
3、let、const与var的区别
重复声明
已经存在的变量和常量再次声明会报错,除了var外,let和const再次声明会报错。
<!-- 重复声明 -->
<script>
// var允许重复声明
var a = 10;
var a = 20;
// let和const不允许重复声明
let b = 10;
let b = 20;
const c = 10;
const c = 20;
console.log(a);
console.log(b);
console.log(c);
</script>
变量提升
var会提升变量的声明,let和const不存在变量提升。
// 变量提升
console.log(d);
var d = 1;
console.log(e);
let e = 1; //输出报错
暂时性死区
ES6 明确规定,如果区块中存在let和const命令,这个区块对这些命令声明的变量或常量,从一开始就形成了封闭作用域。凡是在声明之前就使用这些变量或常量,就会报错。总之,在代码块内,使用let命令声明变量之前,该变量都是不可用的。这在语法上,称为“暂时性死区”。
只要作用域内存在let、const,let或者const所声明的变量或常量就自动绑定这个区域,不再受外部作用域的影响
<script>
let a = 2;
function func() {
console.log(a);
let a = 1;
}
fun();
</script>
这里let a = 1中的a会自动绑定function func() {}这个函数作用域,函数作用域内找不到不会向上查找全局作用域。
var不存在暂时性死区,let和const存在暂时性死区。
window对象的属性和方法
全局作用域中,var声明的变量,通过function()声明的函数,会自动变成window的对象和方法。
let和const不会
var:
<script>
var age = 18;
function ages() {}
console.log(ages() === window.ages()); //输出true
console.log(age === window.age);//输出false
</script>
let和const:
<script>
let age = 18;
const ages = function() {}
console.log(ages === window.ages); //输出false
console.log(age === window.age); //输出false
</script>
块级作用域
什么是块级作用域
JS中作用域有:全局作用域、函数作用域。没有块作用域的概念。ECMAScript 6(简称ES6)中新增了块级作用域。
块作用域由 { } 包括,if语句和for语句里面的{ }也属于块作用域。
<script>
{
var a = 1;
console.log(a); // 1
}
console.log(a); // 1
// 可见,通过var定义的变量可以跨块作用域访问到。
(function A() {
var b = 2;
console.log(b); // 2
})();
console.log(b); // 报错,
// 可见,通过var定义的变量不能跨函数作用域访问到
if(true) {
var c = 3;
}
console.log(c); // 3
for(var i = 0; i < 4; i++) {
var d = 5;
};
console.log(i); // 4 (循环结束i已经是4,所以此处i为4)
console.log(d); // 5
// if语句和for语句中用var定义的变量可以在外面访问到,
// 可见,if语句和for语句属于块作用域,不属于函数作用域。
{
var a = 1;
let b = 2;
const c = 3;
{
console.log(a); // 1 子作用域可以访问到父作用域的变量
console.log(b); // 2 子作用域可以访问到父作用域的变量
console.log(c); // 3 子作用域可以访问到父作用域的变量
var aa = 11;
let bb = 22;
const cc = 33;
}
console.log(aa); // 11 // 可以跨块访问到子 块作用域 的变量
// console.log(bb); // 报错 bb is not defined
// console.log(cc); // 报错 cc is not defined
}
</script>
var、let、const的区别
1、var定义的变量,没有块的概念,可以跨块访问, 不能跨函数访问。
2、let定义的变量,只能在块作用域里访问,不能跨块访问,也不能跨函数访问。
3、const用来定义常量,使用时必须初始化(即必须赋值),只能在块作用域里访问,而且不能修改。
4、同一个变量只能使用一种方式声明,不然会报错
<script type="text/javascript">
// 块作用域
{
var a = 1;
let b = 2;
const c = 3;
// c = 4; // 报错
// let a = 'a'; // 报错 注:是上面 var a = 1; 那行报错
// var b = 'b'; // 报错:本行报错
// const a = 'a1'; // 报错 注:是上面 var a = 1; 那行报错
// let c = 'c'; // 报错:本行报错
var aa;
let bb;
// const cc; // 报错
console.log(a); // 1
console.log(b); // 2
console.log(c); // 3
console.log(aa); // undefined
console.log(bb); // undefined
}
console.log(a); // 1
// console.log(b); // 报错
// console.log(c); // 报错
// 函数作用域
(function A() {
var d = 5;
let e = 6;
const f = 7;
console.log(d); // 5
console.log(e); // 6 (在同一个{ }中,也属于同一个块,可以正常访问到)
console.log(f); // 7 (在同一个{ }中,也属于同一个块,可以正常访问到)
})();
// console.log(d); // 报错
// console.log(e); // 报错
// console.log(f); // 报错
</script>
let和const的应用
1.var
2.闭包,使用立即执行的匿名函数,循环三次。
3.使用let,每次循环创建一个块级作用域
4、 模板字符串
1.模板字符串是什么
一般字符串是以‘’和“”定义,而模板字符串以反引号``来定义。
2.模板字符串与一般字符串的区别
<script>
// 2.模板字符串与一般字符串的区别
const person = {
username: 'Alex',
age: 18,
sex: 'male'
};
// 一般字符串
const info =
'我的名字是:' +
person.username +
', 性别:' +
person.sex +
', 今年' +
person.age +
'岁了';
console.log(info);
// 模板字符串
const info = `我的名字是:${person.username}, 性别:${person.sex}, 今年${person.age}岁了`;
console.log(info);
</script>
模板字符串的注意事项
1.输出多行字符串
<script>
// 模板字符串
const info = `第一行\n第二行`; //可以通过特殊转义符来进行换行
console.log(info);
// 另一种换行方式
var a = `第一行
第二行`; //模板字符串中所有空格和换行都会保留到输出中
console.log(a);
</script>
2.输出’和\特殊字符
使用反斜杠转义字符输出
<script>
// 2.输出 ` 和 \ 等特殊字符
const info = `'\`\\`;
console.log(info);
</script>
3.模板字符串的注入
<script>
// 3.模板字符串的注入
const username = 'alex';
const person = {
age: 18,
sex: 'male'
};
const getSex = function(sex) {
return sex === 'male' ? '男' : '女';
};
const id = `${username}, ${person.age + 2}, ${getSex(person.sex='male')}`;
console.log(id);
</script>
5.箭头函数
1.认识箭头函数
()=>{}; 去掉function
<script>
x => x * x
//箭头函数相当于
function (x) {
return x * x;
}
</script>
2.箭头函数的结构
箭头函数没办法命名名字,所以相当于匿名函数,所以需要复制给变量。
const/let/var 函数名= 参数=>函数体{}。
<script>
x => {
if (x > 0) {
return x * x;
}
else {
return - x * x;
}
}
</script>
3.箭头函数的注意事项
1.单个参数书写形式
<script>
//单个参数可以省略圆括号
const add=x => x + 1;
console.log(add(5));//输出6
//上面的箭头函数相当于:
function hs(x) {
return x + 1;
}
//-------第二种---------
const adds = x => {
return x + 1;
};
console.log(adds(1));//输出2
</script>
<script>
// 无参数或多个参数不能省略圆括号
const wc= () => {
return 1 + 1;
};
console.wc(add()); //输出2
//还有一种可以包含多条语句,这时候就不能省略{ ... }和return
const pd=x => {
if (x > 0) {
return x * x;
}
else {
return - x * x;
}
}
console.log(pd(4)); //输出16
// 两个参数:
const two=(x, y) => x * x + y * y;
console.log(two(6,6));//输出72
const sum = (x, y) => {
return x + y;
};
console.log(sum(1, 1));//输出2
</script>
2.单行函数体
<script>
// 单行函数体可以同时省略 {} 和 return
//const add = (x, y) => {
// return x + y;
// };
const addss = (x, y) => x + y;
console.log(addss(1, 1)); //输出2
//多行函数体不能再化简了
const addssss = (x, y) => {
const sum = x + y;
return sum;
};
console.log(addssss(6,6)); //输出12
</script>
3.单行对象
<script>
//如果要返回一个对象,就要注意,如果是单表达式,这么写的话会报错:
// SyntaxError:
x => { foo: x };
//因为和函数体的{ ... }有语法冲突,所以要改为:
// ok:
x => ({ foo: x });
const add = (x, y) => {
return {
value: x + y
};
};
const add = (x, y) => ({
value: x + y
}); // 如果箭头函数返回单行对象,可以在 {} 外面加上 (),让浏览器不再认为那是函数体的花括
</script>
非箭头函数的this指向
1.全局作用域的this指向
<script>
// 全局作用域的this指向
console.log(this);//指向window
</script>
2.一般函数(非箭头函数)的this指向
<script>
// 一般函数(非箭头函数)的this指向
function add() {
console.log(this)
}
// 'use strict' 严格模式
add(); //指向window,但没有window打点调用也是指向window,因为是非严格模式下。如果是严格模式下是指向undefined。
// -------------
const calc = {
add: add
};
calc.add(); // calc
const adder = calc.add;
adder(); // undefined->window(非严格模式下)
document.onclick = function() {
console.log(this);
}; //指向document
document.onclick();
function Person(username) {
this.username = username;
console.log(this);
}
const p = new Person('Alex'); //构造的this指向是构造函数被实例化后生成的对象。
// 只有在函数调用的时候 this 指向才确定,不调用的时候,不知道指向谁
// this 指向和函数在哪儿调用没关系,只和谁在调用有关
// 没有具体调用对象的话,this 指向 undefined,在非严格模式下,转向 window
</script>
严格模式
一、为什么使用严格模式
严格模式消除了JavaScript语法的一些不合理、不严谨之处,减少一些怪异行为。
消除代码运行一些不安全之处,保证代码运行的安全。
提高代码编译效率,增加运行速度。
为未来新版本的JavaScript做好铺垫。
二、如何开启严格模式
需要在所有语句之前放一个特定语句"use strict"
<script>
// 整个脚本都开启严格模式的语法
"use strict";
let v = "Hi! I'm a strict mode script!";
</script>
把"use strict"声明放在函数体的所有语句之前
function strict() {
// 函数级别严格模式语法
'use strict';
let sum = 1;
let result = 0
result += sum
}
三、严格模式与非严格模式(正常运行模式)的常见区别
(1)在非严格模式中,如果一个变量没有声明就赋值,默认是全局变量,不会出现报错。
num = 1;
(2)严格模式禁止这种用法,添加"use strict"开启严格模式后,会出现报错。
严格模式下变量必须添加关键字声明才可以
"use strict"
num = 1; //Uncaught ReferenceError: num is not defined
this指向undefined
(1)在非严格模式中,全局作用域中的函数内部this默认指向window
(2)在严格模式中,全局作用域中的函数内部this默认指向undefined
不允许变量重名。
箭头函数的this指向
1.箭头函数的this指向
箭头函数没有this
不适用箭头函数的场景
1.构造函数不能以箭头函数的形式来书写,构造函数最重要的就是this,而箭头函数没有this。
2.需要 this 指向调用对象的时候
3.需要使用 arguments 的时候
<script>
// 1.作为构造函数
// 箭头函数没有 this
const Person = () => {};
new Person();
// 2.需要 this 指向调用对象的时候
document.onclick = function() {
console.log(this);
};
document.addEventListener(
'click',
() => {
console.log(this); //window
},
false
);
// 3.需要使用 arguments 的时候
// 箭头函数中没有 arguments
function add() {
console.log(arguments);
}
add(1, 2, 3, 4, 5);
const add = () => console.log(arguments);
add();//报错
</script>
6.解构赋值
数组的解构赋值
1.认识结构赋值
<script>
// 普通定义一个数组,并将数组中每个数值赋值给变量输出,需要声明多个变量较麻烦
const arr = [1, 2, 3];
const a = arr[0],
b = arr[1],
c = arr[2];
console.log(a, b, c);
// 解构赋值的方式 解构赋值就是,解析某一数据的结构,将我们想要的东西提取出来赋值给变量或者常量。
const [d, e, f] = [4, 5, 6];
console.log(d, e, f);
</script>
2.什么是解构赋值呢?
解构赋值就是,解析某一数据的结构,将我们想要的东西提取出来赋值给变量或者常量。
数组解构赋值的原理
数组的解构赋值原理分为两步:模式(结构)匹配、索引值相同的完成赋值。
<script>
//1.模式(结构)匹配 要将数组进行解构赋值,那么将数组赋值的一方需要跟数组一样结构,类型相匹配。
//[]=[1,2,3] 左边需要跟右边赋值的结构相匹配。
//2.索引值相同的完成赋值
const [d, e, f] = [1, 2, 3];
//不取的可以直接逗号跳开
const [a, [, , b], c] = [1, [2, 4, 5], 3];
console.log(a, b, c);
</script>
数组解构赋值的默认值
1.默认值的基本用法
默认值设置,直接在左边的结构的索引值对他设置默认值
<script>
// 1.默认值的基本用法
const [a, b] = [];
const [a, b] = [undefined, undefined]; //默认为空的话输出undefined,相当于这条语句
//默认值设置,直接在左边的结构的索引值对他设置默认值
const[c=1,d=2]=[];
console.log(c,d);//输出1,2
</script>
2.默认值的生效条件
只有当一个数组成员严格等于(===)undefined 时,对应的默认值才会生效
<script>
// 2.默认值的生效条件
// 只有当一个数组成员严格等于(===)undefined 时,对应的默认值才会生效
const [a = 1, b = 2] = [3, 0]; //a=3 b=0
const [a = 1, b = 2] = [3, null]; //a=3 b=null
const [a = 1, b = 2] = [3]; //a=3,b=undefined,当一个数组成员等于undefined,对应的默认值会生效,那么也就是b=2。
console.log(a, b);
</script>
3.默认值表达式
如果默认值是表达式,默认值表达式是惰性求值的,用到了才会执行,用不到不会执行。惰性求值指的是只有用到时,才会去求值
<script>
// 3.默认值表达式
// 如果默认值是表达式,默认值表达式是惰性求值的,用到了才会执行,用不到不会执行
const func = () => {
console.log('我被执行了');
return 2;
};
// const [x = func()] = [1]; 输出1
const [x = func()] = []; //undefined,那么等于2
console.log(x);
</script>
数组解构赋值的应用
1.常见的类数组的解构赋值
<script>
//1.常见的类数组的解构赋值
function func() {
const [a, b] = arguments;
console.log(a, b);
}
func(1, 2);//输出1,2
//Nodelist
const [p1, p2, p3] = document.querySelectorAll('p');
console.log(p1, p2, p3);
</script>
2.函数参数的解构赋值
<script>
//2.函数参数的解构赋值
const array = [1, 2];
const add = ([x, y]) => x + y;
console.log(add(array));
</script>
3.交换变量的值
<script>
//3.交换变量的值
let x = 2;
let y = 1;
[x, y] = [y, x];
console.log(x, y);
</script>
对象解构赋值的原理
对象的解构赋值原理分为两步:模式(结构)匹配、属性名相同的完成赋值。
<script>
//1.模式(结构)匹配
//{} = {};
//2.属性名相同的完成赋值,不用按照顺序
const{age,username}={username:'王劲威',age:24};
console.log(age,username);
</script>
对象解构赋值的注意事项
1.默认值生效的条件
<script>
// 1.默认值的生效条件
//对象的属性值严格等于undefined时,对应的默认值才会生效。
const {
username = 'wjw', age = 0
} = {
username: 'haha'
};
console.log(username, age);
</script>
2.默认值表达式
//2.默认值表达式 惰性求值指的是只有用到时,才会去求值
<script>
const info = () => {
console.log('haha');
return 6;
}
const {
xm = 'haha', ages = info()
} = {
xm: 'dwad',
};
console.log(xm, ages);
</script>
3.将一个已经声明的变量用于解构赋值
如果将一个已经声明的变量用于对象的解构赋值,整个赋值需在**圆括号**中进行
<script>
// 3.将一个已经声明的变量用于解构赋值
// 如果将一个已经声明的变量用于对象的解构赋值,整个赋值需在圆括号中进行
let x = 2;
({
x
} = {
x: 6
});
console.log(x);
</script>
4.可以取到继承的属性
<script>
// 4.可以取到继承的属性 对象的原型是object 对象的属性是object原型的方法
const {
toString
} = {};
// console.log(toString);
// Object.prototype
// console.log(Object.prototype);
console.log({});
</script>
对象解构赋值的应用
1.函数参数的解构赋值
<script>
// 1.函数参数的解构赋值
const logPersonInfo = ({
age = 18,
username
}) => {
console.log(username, age);
}
logPersonInfo({
username: '哈哈',
age: 16
});
</script>
2.复杂的嵌套
//2.复杂的嵌套
<script>
const obj = {
x: 1,
y: [1, 2, 3, 4],
z: {
a: 5,
b: 6
}
}
const {
y,
y: [, yy],
z,
z: {
b
}
} = obj;
console.log(yy, y, z, b);
</script>
其它数据类型的解构赋值
1.字符串的解构赋值
字符串的结构赋值可以用数组的方式也可以用对象的方式来解构
2.数值和布尔值的解构赋值,先将等号右边的值转换为对象
3.undefined和null的解构赋值,undefined 和 null 无法转为对象,所以对它们进行解构赋值,都会报错
<script>
//1.字符串的解构赋值,可以用数组和对象的结构方式
//数组的方式解构赋值
const [a, b] = 'hello';
console.log(a, b);
//对象形式解构赋值
const {
0: c,
1: d
} = 'hello';
console.log(c, d);
//2.数值和布尔值的解构赋值,先将等号右边的值转换为对象
console.log(new Number(123));
const {
as = 1, toString
} = 123;
console.log(as, toString);
const {
bs = 2, toString
} = true;
console.log(bs, toString);
// 3.undefined 和 null 的解构赋值
// 由于 undefined 和 null 无法转为对象,所以对它们进行解构赋值,都会报错
const {
toString
} = undefined;
const {
toString
} = null;
</script>
7.对象字面量的增强
属性和方法的简洁表示法
1.对象字面量是什么
对象有两种写法:
(1)实例化构造函数生成对象
<script>
//1.对象字面量是什么
//对象有两种写法:
//(1)实例化构造函数生成对象
const ids = new Object();
ids.age = 18;
ids.speak = function() {}
</script>
(2)对象字面量
<script>
const person = {
sex = '男',
sleep: function() {
}
};
</script>
2.属性的简洁表示法,只要对象的键名跟变量或者常量的名称一样,可以只写一个。
<script>
//2.属性的简洁表示法,只要对象的键名跟变量或者常量的名称一样,可以只写一个
const nl = 18;
const info = {
nl
}
console.log(info);
</script>
3.方法的简洁表示法,方法可以省略冒号和function关键字
<script>
//3.方法的简洁表示法
const func = {
//talk:function(){}
talk() {}
};
console.log(func);
</script>
方括号语法
1.方括号语法的用法
<script>
//1.方括号语法的用法
const prop = 'age'; //如果要将age作为一个属性赋值给变量需要用到方括号语法。
const person = {};
person.prop = 18; //输出为 prop:18, 用打点的只是将prop作为字符串解析,相当于添加了在person里prop属性
person[prop] = 18; //方括号 加上方括号把prop对应的age添加给person
console.log(person);
</script>
方括号语法可以写在对象字面量中
2.方括号中可以放什么,[值或通过计算可以得到值的(表达式)]
<script>
// 2.方括号中可以放什么
// ${}
// [值或通过计算可以得到值的(表达式)]
const prop = 'age';
const func = () => 'age2';
const person = {
[prop]: 18,
[func()]: 18,
['sex']: 'male',
['s' + 'ex']: 'male'
};
console.log(person);
</script>
3.点语法和方括号语法的区别
使用规范:
点语法:点号要求后面的写法是合法的标识符,对于不合法的标识符不可以使用。
方括号语法:括号之间的值可以是任何表达式。
区别:
以访问对象属性为例:
1、属性名是合法标识符时,使用点语法和方括号语法都是可以的,属性名不是合法标识符时,只能使用方括号语法。
<script>
const obj = {
"age": 2,
"8i":"imooc"
}
// age是合法标识符,点语法和方括号语法都可以访问
console.log(obj.age) // 2
console.log(obj['age']) // 2
//8i不属于合法的标识符,使用点语法访问属性会报错
// console.log(obj.8i) // 报错 为了方便看到其他结果,所以将这句代码注释了,可以自己打开注释测试下代码效果
// 不符合语法标识符的属性,可以使用方括号语法访问
console.log(obj['8i'])// imooc
</script>
这段代码中属性名age是合法标识符,点语法和方括号语法都可以访问,8i不属于合法的标识符,使用点语法访问属性会报错,只能使用方括号语法。
2、使用变量或者常量保存属性名时,只能使用方括号语法,不能使用点语法。示例:
<script>
const obj = {
"age": 2,
"8i": "imooc"
}
// 定义一个常量property,值为age
const property = "age"
// 当属性为变量或常量时,必须通过方括号语法,即:obj[property],使用property保存的值age,所以等价于obj.age这种写法
console.log(obj[property]) // 2
// 当属性为变量或常量时,如果通过点语法,会将property看做字符串,表示访问obj对象下的property属性,而不是访问obj下的age属性,而obj对象中没有property属性,所以返回结果为undefined
console.log(obj.property) // undefined
</script>
8.函数参数的默认值
函数参数默认值的基本用法
<script>
//1.认识函数参数的默认值
// 调用函数的时候传参了,就用传递的参数;如果没传参,就用默认值
// 2.函数参数默认值的基本用法
const multiply = (x, y = 1) => x * y;
console.log(multiply(2));
</script>
函数参数默认值的注意事项
1.默认值的生效条件:不传参数,或者明确的传递 undefined 作为参数,只有这两种情况下,默认值才会生效
2.默认表达式:如果默认值是表达式,默认值表达式是惰性求值的
3.设置默认值的小技巧:最好从参数列表的右边开始设置
<script>
// 1.默认值的生效条件
// 不传参数,或者明确的传递 undefined 作为参数,只有这两种情况下,默认值才会生效
const multiply = (x, y = 1) => x * y;
console.log(multiply(2, 0)); //输出0
console.log(multiply(2, null)); //输出2
console.log(multiply(2, undefined)); //输出2
console.log(multiply(2)); //输出2
// 2.默认值表达式
// 如果默认值是表达式,默认值表达式是惰性求值的
const func = () => {
return 3;
}
const multiply = (x, y = func()) => x * y;
console.log(multiply(2)); //输出6
// 3.设置默认值的小技巧
// 函数参数的默认值,最好从参数列表的右边开始设置
const multiply = (x = 1, y) => x * y;
console.log(multiply(undefined, 2)); //最好是右边设置,如果设置左边,一开始(,y)会报错,只能传入undefined不会报错 输出2
const multiply = (x, y = func()) => x * y;
console.log(multiply(2)); //输出2
</script>
函数参数默认值的应用
<script>
// 1.接收很多参数的时候 比较麻烦,实际开发不用这种写法
const logUser = (username = 'ZhangSan', age = 0, sex = 'male') =>
console.log(username, age, sex);
logUser('Alex', 18, 'male');
logUser();
// 2.接收一个对象作为参数 需要打点比较麻烦
const logUser = options =>
console.log(options.username, options.age, options.sex);
const logUser = ({username = 'zhangsan',age = 0,sex = 'male'} = {}) =>console.log(username, age, sex); //使用该方法
logUser({
username: 'alex',
age: 18,
sex: 'male'
});
logUser({
username: 'alex'
});
//{ username = 'zhangsan', age = 0, sex = 'male'} = { username: 'alex'} 相当于对象的解构赋值
logUser({});
logUser();
// { username = 'zhangsan', age = 0, sex = 'male' } = {} 相当于对象的解构赋值
// { username = 'zhangsan', age = 0, sex = 'male' } = undefined 报错
</script>
9.剩余参数
1.认识剩余参数
参数中的…代表等等,这就是剩余参数的写法,剩余参数是数组类型。剩余参数永远是个数组,即使没有值,也是个空数组。
<script>
//1.认识剩余参数 参数中的...代表等等,这就是剩余参数的写法
const add = (x, y,z, ...args) => {};
</script>
2.剩余参数的注意事项
(1)箭头函数的剩余参数
<script>
//1.箭头函数的剩余参数
//箭头函数的参数部分只有一个剩余参数,也不能省略圆括号
const add=(...args)=>{};
</script>
(2)使用剩余参数替代arguments(类数组),获取实际参数
<script>
// 2.使用剩余参数替代 arguments(类数组) 获取实际参数
const add = function() {
console.log(arguments); //类数组接收参数
};
const add = (...args) => {
console.log(args); //剩余参数接收参数
};
add(1, 2);
</script>
(3)剩余参数的位置只能是最后一个,后面不能再有其他参数,不然会报错
剩余参数在实际开发中的应用
1.与数组结构赋值一起使用,剩余参数不一定要作为剩余参数使用
2.函数与数组解构赋值结合使用
3.剩余参数与对象解构赋值的使用
剩余参数跟数组和对象一起使用,不在是数组了,可能是剩余元素(可能是数组或对象)。
<script>
//剩余参数的应用
//1.与数组结构赋值一起使用,剩余参数不一定要作为剩余参数使用
//同时位置也必须是最后一个,不然会报错
const [num, ...args] = [1, 2, 3, 4];
console.log(num, args); //输出 1 (3) [2, 3, 4]
//函数与数组解构赋值结合使用
const func = ([num, ...args]) => {
console.log(num, args);
};
func([1, 2, 3, 4, 5, 6]); //输出 1 (5) [2, 3, 4, 5, 6]
//剩余参数与对象解构赋值的使用
const {
x,
y,
...z
} = {
a: 3,
x: 1,
y: 2,
b: 6
};
console.log(x, y, z); //输出 1 2 {a: 3, b: 6}
//剩余参数结合对象解构赋值和函数的使用
const sum = ({
a,
b,
...c
}) => {
console.log(a, b, c);
};
sum({
a: 6,
b: 6,
c: 7,
d: 8,
e: 9
}) //输出 6 6 {c: 7, d: 8, e: 9}
</script>
10.数组的展开运算符
数组展开运算符的基本用法
用法与剩余参数类似,但是不同只针对数组。
<script>
//数组的展开运算符基本用法
// [3, 1, 2]->3, 1, 2
console.log(Math.min(...[3, 1, 2]));
// 相当于
console.log(Math.min(3, 1, 2));
</script>
区分剩余参数和展开运算符
展开运算符
[3,1,2]->3,1,2 将数组转换为参数列表的形式
剩余参数
3,1,2->[3,1,2] 将参数列表转为数组的形式
<script>
// 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);
</script>
数组展开运算符的应用
<script>
//1.复制数组
const a = [1, 2];
const c = [...a];
a[0] = 3;
console.log(a); //输出3,2 后续改变a的值,c不会跟着变化,因为地址不同
console.log(c); //输出1,2
// 2.合并数组
const e = [1, 2];
const f = [3];
const g = [4, 5];
console.log([...e, ...f, ...g]); //输出12345
console.log([...g, ...f, ...e]); //顺序可以调换,没有要求 输出45312
console.log([1, ...e, 2, ...f, ...g, 3]); //合并数组的同时,还可以添加新的数组进去 输出1123453
//3.字符串转为数组
console.log([...
'wjw'
]); //输出[w,j,w]
// 4.常见的类数组转化为参数和数组
function add() {
console.log(...arguments); //输出123
console.log([...arguments]); //输出[1,2,3]
}
add(1, 2, 3);
</script>
11.对象的展开运算符
对象展开运算符的基本用法
1.展开对象:对象不能直接展开,必须在{}展开
对象的展开:把属性罗列出来,用逗号分隔,放到一个{}中,构成新对象
2.合并对象:新对象拥有全部属性,相同属性,后者覆盖前者
<script>
//对象展开运算符
//1.展开对象
//对象不能直接展开,必须在{}展开
const apple = {
color: '红色',
shape: '球形',
taste: '甜'
}
//对象的展开:把属性罗列出来,用逗号分隔,放到一个{}中,构成新对象
console.log({...apple
});
//那么将新的对象与之前的对象进行比较,地址是不相等的
console.log({
apple
} === apple); //输出false
//2.合并对象
const pen = {
color: '黑色',
shape: '圆柱形',
use: '写字'
}
console.log({...apple,
...pen
}); // 新对象拥有全部属性,相同属性,后者覆盖前者
// 相当于
// console.log({
// use: '写字',
// color: '红色',
// shape: '球形',
// taste: '甜'
// });
</script>
对象展开运算符的注意事项
1.空对象的展开:如果展开一个空对象,则没有任何效果
2.非对象的展开:如果展开的不是对象,则会自动将其转为对象,再将其属性罗列出来
3.对象中对象属性的展开:不会展开对象中的对象属性 如果属性合并 那么会将之前对象中的属性覆盖
<script>
//对象展开运算符的注意事项
// 1.空对象的展开
// 如果展开一个空对象,则没有任何效果
console.log({... {}
}); //输出{}
console.log({... {},
a: 1
}); //输出{a:1}
// 2.非对象的展开
// 如果展开的不是对象,则会自动将其转为对象,再将其属性罗列出来
console.log({...1
}); //输出{}
console.log({...undefined
}); //输出{}
console.log({...null
}); //输出{}
console.log({...true
}); //输出{}
// 如果展开运算符后面是字符串,它会自动转成一个类似数组的对象,因此返回的不是空对象
console.log({...
'alex'
}); //输出{0: "a", 1: "l", 2: "e", 3: "x"}
console.log([...
'alex'
]); //输出 (4) ["a", "l", "e", "x"]
console.log(...
'alex'); //输出 a l e 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: '写字'
});
</script>
对象展开运算符的应用
<script>
//对象展开运算符的应用
//1.复制对象
const a = {
a: 1,
b: 2
};
const b = {...a
};
console.log(b, b === a);
//2.用户参数和默认参数 两种方式
//第一种
/*const logUser = ({username = 'wjw',age = 0,sex = 'male'} = {}) => {
console.log(username, age, sex);
}***/
//第二种
const logUser = userParam => {
const defaultParam = {
username: 'wjw',
age: 0,
sex: 'male'
}
// const param = {...defaultParam,
// ...userParam
// }; 第一种赋值方式
const {
username,
age,
sex
} = {...defaultParam,
...userParam
}; //对象赋值解构方式
}
</script>
12.Set
Set是什么
1.Set是集合,跟数组类似。但不同的是,数组是一系列有序的集合,Set是一系列无序没有重复值的集合。
set中有add()方法,每次只能添加一个数值,如果添加相同的只会显示一个数值,因为set是无序没有重复值的集合。
set没有下标去标识每一个值,所以set是无序的,所以不能通过下标去访问set的成员。
<script>
//1.Set是集合。
//跟数组类似。但不同的是数组是一系列有序的集合,Set是一系列无序没有重复值的集合
//2.理解Set
const s = new Set();
//set中有add()方法,每次只能添加一个数值,如果添加相同的只会显示一个数值,因为set是无序没有重复值的集合。
s.add(1);
s.add(2);
s.add(1);
console.log(s); //输出12
//set没有下标去标识每一个值,所以set是无序的,所以不能通过下标去访问set的成员。
</script>
Set实例的方法和属性
1.方法:
add方法 给set中添加成员。add可以不用换行写,可以直接写在前面add的后面
has方法 判断set中是否有某个成员
delete方法 删除某个成员
clear方法 清理所有set中的成员
forEach方法,forEach中的key就是value,value就是set中的值,最后的set就是set本身,forEach遍历顺序按照添加集合的顺序遍历
2.属性
size 获取成员个数
<script>
//1.方法
//add方法 给set中添加成员。
const s = new Set();
//add可以不用换行写,可以直接写在前面add的后面
s.add(1).add(2).add(3).add(4).add(5).add(6).add(7).add(8).add(9);
//has方法 判断set中是否有某个成员
console.log(s.has(3)); //false
//delete方法 删除某个成员
s.delete(1);
console.log(s);
//clear方法 清理所有set中的成员
//s.clear();
//forEach方法
s.forEach(function(value, key, set) {
//forEach中的key就是value,value就是set中的值,最后的set就是set本身
console.log(value, key, set === s); //输出2 2 true 3 3 true 4 4 true 5 5 true 6 6 true 7 7 true 8 8 true 9 9 true
console.log(this); //如果用箭头函数,this指向window,跟function一样
//forEach遍历顺序按照添加集合的顺序遍历
}, document);
//2.属性
//size 获取成员个数
console.log(s.size);
</script>
Set构造函数的参数
1.数组
2.字符串、arguments、NodeList、Set等
<body>
<p>1</p>
<p>2</p>
<p>3</p>
<script>
//Set构造函数的参数
//1.数组
const s = new Set([1, 2, 1]);
console.log(s);
//2.字符串、arguments、NodeList、Set等
console.log(new Set('hi')); //输出 Set(2){'h','i'}
//arguments
function add() {
console.log(new Set(arguments));
}
add(1, 2, 3, 4, 5, 6, 7, 8, 9); //输出 Set(9) {1, 2, 3, 4, 5, …}
//NodeList
console.log(new Set(document.querySelectorAll('p'))); //输出 Set(3) {p, p, p}
//Set自身
const a = new Set(s);
console.log(a === s); //引用不同
</script>
</body>
Set的注意事项
1.判断重复的方式
Set 对重复值的判断基本遵循严格相等(===),但是对于 NaN 的判断与 === 不同,Set 中 NaN 等于 NaN,因为set中的nan相等,所以去掉重复值只有一个NaN。
2.什么时候使用 Set
① 数组或字符串去重时
② 不需要通过下标访问,只需要遍历时
③ 为了使用 Set 提供的方法和属性时(add delete clear has forEach size 等)
<script>
//Set的注意事项
//1.判断重复的方式
// Set 对重复值的判断基本遵循严格相等(===)
// 但是对于 NaN 的判断与 === 不同,Set 中 NaN 等于 NaN
const s = new Set([NaN, 2, NaN]) //因为set中的nan相等,所以去掉重复值只有一个nan
const a = new Set();
a.add({}).add({});
console.log({} === {}); //false 地址不同
console.log(a); //Set(2) {{…}, {…}}
// 2.什么时候使用 Set
// ① 数组或字符串去重时
// ② 不需要通过下标访问,只需要遍历时
// ③ 为了使用 Set 提供的方法和属性时(add delete clear has forEach size 等)
let d = new Set();
d.add([NaN]).add([NaN]);
console.log(d);
</script>
Set的应用
1.数组去重
2.字符串去重
3.存放DOM元素
<body>
<p>1</p>
<p>2</p>
<p>3</p>
<script>
//Set的应用
//1.数组去重
const s = new Set([1, 2, 1, 4, 6, 8, 6, 8, 9, 5, 9, 7, 3, 3, 2, 0]);
console.log([...s]); //第一种方式
console.log([...new Set([1, 2, 1, 4, 6, 8, 6, 8, 9, 5, 9, 7, 3, 3, 2, 0])]); //第二种方式
//2.字符串去重
const sa = new Set('abbacbd');
console.log([...sa].join('')); //第一种方式
console.log([...new Set('abbacbd')].join('')); //第二种方式
//3.存放DOM元素
const dom = new Set(document.querySelectorAll('p'));
dom.forEach(function(elem) {
elem.style.color = 'red';
elem.style.backgroundColor = 'yellow';
});
</script>
</body>
13.Map
Map是什么
1.认识Map,Map和对象一样都是键值对的集合
2.Map 和对象的区别,对象一般用字符串当作键,map是基本数据类型和引用数据类型都可以作为map的键。
<script>
//Map是什么
//1.认识Map,Map和对象一样都是键值对的集合
const m = new Map();
m.set('name', 'wjw');
m.set('sex', '男');
m.set('age', 18);
// 2.Map 和对象的区别
// 对象一般用字符串当作键
const obj = {
name: 'alex',
true: 'true',
[{}]: 'object' //输出是object
};
console.log(obj);
console.log({}.toString());
// 基本数据类型:数字、字符串、布尔值、undefined、null
// 引用数据类型:对象([]、{}、函数、Set、Map 等)
// 以上都可以作为 Map 的键
const c = new Map();
c.set('name', 'alex');
c.set(true, 'true');
c.set({}, 'object');
c.set(new Set([1, 2]), 'set');
c.set(undefined, 'undefined');
console.log(c);
</script>
Map实例的方法和属性
Map实例的方法和属性 Map是映射的意思
1.方法:
set方法 给Map中添加成员,set可以不用换行写,可以直接写在前面set的后面,键如果已经存在,后添加的键值对覆盖已有的。
get方法: 获取指定成员,通过键获取值
has方法 判断map中是否有某个成员
delete方法 删除某个成员
clear方法 清理所有map中的成员
forEach中的key就是键,,maps就是key中的值,最后的map就是m本身
<script>
//Map实例的方法和属性 Map是映射的意思
//1.方法
//set方法 给set中添加成员。
const m = new Map();
//set可以不用换行写,可以直接写在前面set的后面,键如果已经存在,后添加的键值对覆盖已有的
m.set('age', 18).set(true, 'true').set('age', 20).set('sex', 'male').set('name', 'william');
console.log(m);
//get 获取指定成员,通过键获取值
console.log(m.get('age'));
//has方法 判断map中是否有某个成员
console.log(m.has(true));
//delete方法 删除某个成员
m.delete(true);
console.log(m);
//clear方法 清理所有map中的成员
//s.clear();
//forEach
m.forEach((maps, key, map) => {
//forEach中的key就是键,,maps就是key中的值,最后的map就是m本身
console.log(maps, key, map === m);
}, document) //第二个参数,this指向
//2.属性
//size 获取成员个数
console.log(m.size);
</script>
Map构造函数的参数
1.数组,只能传二维数组,而且必须体现出键值对
<script>
//Map构造函数的参数
//1.数组
//只能传二维数组,而且必须体现出键值对
console.log(new Map([
['name', 'alex'],
['age', 18],
['sex', 'male']
])); //输出 Map(3) {"name" => "alex", "age" => 18, "sex" => "male"}
// 2.Set、Map 等
// Set
// Set 中也必须体现出键和值
const a = new Set([
['age', 18],
['sex', 'girl']
])
console.log(a); //输出 Set(2) {Array(2), Array(2)}
// Map
// 复制了一个新的 Map
const m1 = new Map([
['name', 'alex'],
['age', 18]
]);
console.log(m1); //输出 Map(2) {"name" => "alex", "age" => 18}
const m2 = new Map(m1);
console.log(m2, m2 === m1); // Map(2) {"name" => "alex", "age" => 18} false
</script>
Map的注意事项
<script>
//Map的注意事项
// 1.判断键名是否相同的方式
// 基本遵循严格相等(===)
// 例外就是 NaN,Map 中 NaN 也是等于 NaN
console.log(NaN === NaN); //false
const a = new Map();
a.set(NaN, 1).set(NaN, 2); //map和set的nan都是等于nan,那么前者也会被后者覆盖 输出Map(1) {NaN => 2}
console.log(a);
// 2.什么时候使用 Map
// 如果只是需要 key -> value 的结构,或者需要字符串以外的值做键,使用 Map 更合适
// forEach for in
// size
</script>
Map的应用
<script>
const [p1, p2, p3] = document.querySelectorAll('p');
// console.log(p1, p2, p3);
// const m = new Map();
// m.set(p1, 'red');
// m.set(p2, 'green');
// m.set(p3, 'blue');
const m = new Map([
[
p1, {
color: 'red',
backgroundColor: 'yellow',
fontSize: '40px'
}
],
[
p2, {
color: 'green',
backgroundColor: 'pink',
fontSize: '40px'
}
],
[
p3, {
color: 'blue',
backgroundColor: 'orange',
fontSize: '40px'
}
]
]);
m.forEach((propObj, elem) => {
for (const p in propObj) {
elem.style[p] = propObj[p]; //这里使用方括号语法,对象名[p(属性名)]获取属性
}
});
// m.forEach((color, elem) => {
// elem.style.color = color;
// });
console.log(m);
</script>
1)创建Map实例。(2)将数组中的对象替换成Map实例。(3)将forEach中的for…in遍历改成forEach遍历Map。
创建Map实例的代码是非常相似的,只有属性值不同,可以尝试封装函数,将属性值作为参数传入,在函数内部返回Map实例,调用时只需要传递不同的属性值就可以了
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<p>1</p>
<p>2</p>
<p>3</p>
<script>
// (1)创建Map实例。(2)将数组中的对象替换成Map实例。(3)将forEach中的for…in遍历改成forEach遍历Map。
/*创建Map实例的代码是非常相似的,只有属性值不同,可以尝试封装函数,将属性值作为参数传入,在函数内部返回Map实例,调用时只需要传递不同的属性值就可以了。*/
const [p1, p2, p3] = document.querySelectorAll('p')
function fun(color, backgroundColor, fontsize) {
return new Map([
['color', color],
['backgroundColor', backgroundColor],
['fontSize', fontsize]
]);
}
const m = new Map([
[p1, fun('red', 'pink', '40px')],
[p2, fun('green', 'yellow', '40px')],
[p3, fun('blue', 'orange', '40px')]
])
// 在此补充代码
m.forEach(function(func, ps) {
func.forEach(function(value, key) {
ps.style[key] = value;
})
})
</script>
</body>
</html>
14.遍历
lterator是什么
lterator是什么
1.lterator作用,iterator成为遍历器(迭代器)
2.寻找lterator
3.使用lterator
{value: 1, done: false} value遍历其中的值,done表示是否遍历完成,遍历到最后value是undefined,done显示true遍历才完成。
<script>
const it = [1, 2][Symbol.iterator]();
//{value: 1, done: false} value遍历其中的值,done表示是否遍历完成,遍历到最后value是undefined,done显示true遍历才完成。
console.log(it.next()); //{value: 1, done: false}
console.log(it.next()); //{value: 2, done: false}
console.log(it.next()); //{value: undefined, done: true}
</script>
4.什么是 Iterator
Symbol.iterator(可遍历对象的生成方法) -> it(可遍历对象) -> it.next() -> it.next() -> …(直到 done 为 true)
Symbol
1.什么是Symbol
Symbol 是 ES6 中引入的一种新的基本数据类型,用于表示一个独一无二的值。它是 JavaScript 中的第七种数据类型,与 undefined、null、Number(数值)、String(字符串)、Boolean(布尔值)、Object(对象)并列。
创建一个 Symbol 值的方式如下:
const a = Symbol();
console.log(a); //Symbol()
console.log(typeof a) // 类型是:Symbol
2.Symbol 的语法规范
创建Symbol:
let a = Symbol();
创建两个Symbol进行全等比较
let a = Symbol();
let b = Symbol();
console.log(a); //Symbol()
console.log(b); //Symbol()
console.log(a === b) // false
由 a === b 返回的结果为 false 可知 Symbol 值是唯一的,变量 a 和变量 b 并不是同一个值,但它们在控制台的输出却是一样的,如下图所示:
这样不利于我们区分两个变量,为此,我们可以在调用 Symbol 的时候传入一个字符串作为对当前 Symbol 变量的描述:
let a = Symbol("symbol1");
let b = Symbol("symbol2");
console.log(a); //Symbol("symbol1")
console.log(b); //Symbol("symbol2")
注意:Symbol 是基本数据类型,调用 Symbol 时不可以使用 new 关键字。如下写法是错误的:
//报错,Symbol is not a constructor
const a = new Symbol();
3.Symbol 属性的遍历
以 Symbol 类型的变量作为对象属性时,该属性不会出现在 for … in、for … of循环中。
let s1 = Symbol('a');
let s2 = Symbol('b');
// 由于 s1 和 s2 是一个变量,而不是字符串,因此需要使用中括号括起来(否则它会被当做字符串使用)
let a = {
name: "夕山雨",
[s1]: 24,
[s2]: function(){}
}
4.symbol-的作用
由于每一个 Symbol 值都是不相等的,这意味着 Symbol 值可以作为标识符,用于对象的属性名,就能保证不会出现同名的属性。这对于一个对象由多个模块构成的情况非常有用,能防止某一个键被不小心改写或覆盖。
let s1 = Symbol('s1');
let s2 = Symbol('s2')
const obj = {
age: 16,
age: 19,
[s1]: 'Hello!',
[s2]: 'world'
};
console.log(obj)
for…of的用法
1.认识for…of
2.与 break、continue 一起使用
3.在for…of中取得数组的索引
keys()得到的是索引的可遍历对象,可以遍历出索引值
values() 得到的是值的可遍历对象,可以遍历出值
entries() 得到的是索引+值组成的数组的可遍历对象
<script>
//for...of的用法
//1.认识for...of
const arr = [1, 2, 3];
const it = arr[Symbol.iterator]();
let next = it.next(); //将遍历的第一项赋值给next
console.log(next);
while (!next.done) {
console.log(next.value);
next = it.next();
console.log(next);
} //如果done一直为false就一直循环, while循环过于麻烦需要调用到iterator。
//使用for..of,(定义接收值 of 需要循环遍历的值) for...of循环封装了iterator的过程
// for...of 循环只会遍历出那些 done 为 false 时,对应的 value 值
for (const item of arr) {
console.log(item);
//输出123
}
//2.与 break、continue 一起使用
const arr2 = [1, 2, 3];
for (const s of arr2) {
if (s === 2) {
// break;
continue;
}
console.log(s);
//输出13
}
//3.在for...of中取得数组的索引
const arr3 = [1, 2, 3];
//keys()得到的是索引的可遍历对象,可以遍历出索引值
console.log(arr3.keys());
for (const key of arr3.keys()) {
console.log(key);
//输出012
}
// values() 得到的是值的可遍历对象,可以遍历出值
for (const value of arr3.values()) {
console.log(value);
//输出123
}
// entries() 得到的是索引+值组成的数组的可遍历对象
for (const entries of arr.entries()) {
console.log(entries);
/* (2) [0, 1]
(2) [1, 2]
(2) [2, 3]
*/
}
//可以使用数组解构赋值的方式
for (const [index, value] of arr3.entries()) {
console.log([index, value])
/* (2) [0, 1]
(2) [1, 2]
(2) [2, 3]
*/
}
</script>
JS中的forEach,for in,for of和for的遍历优缺点及区别
forEach:(可以三个参数,第一个是value,第二个是index,第三个是数组体)
缺点:不能同时遍历多个集合,在遍历的时候无法修改和删除集合数据,方法不能使用break,continue语句跳出循环,或者使用return从函数体返回,对于空数组不会执行回调函数优点:便利的时候更加简洁,效率和for循环相同,不用关心集合下标的问题,减少了出错的效率
for in:(它大部分用于遍历对象 json)
for of:(可遍历map,object,array,set string等)用来遍历数据可以使用break,continue和return,不仅支持数组的遍历,还可以遍历类似数组的对象,支持字符串的遍历最简洁,最直接的遍历数组的语法,支持map和Set对象遍历
原生可遍历与非原生可遍历
1.什么是可遍历
原生可遍历:只要有Symbol.iterator方法,并且这个方法可以生成可遍历对象,就是可遍历的
只要可遍历,就可以使用 for…of 循环来统一遍历。
原生可遍历的有哪些,原生可遍历(自身包含Symbol.iterator),都可以统一用for…of遍历
<script>
//原生可遍历与非原生可遍历
//1.什么是可遍历
//只要有Symbol.iterator方法,并且这个方法可以生成可遍历对象,就是可遍历的
// 只要可遍历,就可以使用 for...of 循环来统一遍历
// 2.原生可遍历的有哪些,原生可遍历(自身包含Symbol.iterator),都可以统一用for...of遍历
// 数组
for (const arr of[1, 2, 3]) {
console.log(arr);
}
// 字符串
for (const str of 'hello') {
console.log(str);
}
// Set
for (const s of new Set([4, 5, 6])) {
console.log(s);
}
// Map
for (const m of new Map([
['name', 'william'],
['age', '20'],
['sex', 'male']
])) {
console.log(m);
}
// arguments
function add() {
for (const sum of arguments) {
console.log(sum);
}
}
add(7, 8, 9);
// NodeList
for (const p of document.querySelectorAll('p')) {
p.style.color = 'red';
console.log(p);
}
</script>
2.非原生可遍历
<script>
// {next()} {value,done}
person[Symbol.iterator] = () => {
let index = 0;
return {
next() {
index++;
if (index === 1) {
return {
value: person.age,
done: false
};
} else if (index === 2) {
return {
value: person.sex,
done: false
};
} else {
return {
done: true
};
}
}
};
};
for (const item of person) {
console.log(item);
}
// 有 length 和索引属性的对象
const obj = {
'0': 'alex',
'1': 'male',
length: 2
};
obj[Symbol.iterator] = Array.prototype[Symbol.iterator];
obj[Symbol.iterator] = () => {
let index = 0;
return {
next() {
let value, done;
if (index < obj.length) {
value = obj[index];
done = false;
} else {
value = undefined;
done = true;
}
index++;
return {
value,
done
};
}
};
};
for (const item of obj) {
console.log(item);
}
</script>
使用iterator的场合
只要是原生可遍历,具备Symbol.iterator。都可以使用iterator。
1.数组展开运算符,因为数组是可遍历的,同时展开运算符底层也使用了iterator过程,所以原生可遍历都可以使用展开运算符。
2.数组解构赋值。
3.Set 和 Map 的构造函数。
<script>
//使用iterator的场合
// 原生可遍历的 原生的具备Symbol.iterator
// Array 数组
// String 字符串
// Set
// Map
// 函数的 arguments 对象
// NodeList 对象
// for...of
//1.数组展开运算符
console.log(...[1, 2, 3]); //因为数组是可遍历的,同时展开运算符底层也使用了iterator过程,所以原生可遍历都可以使用展开运算符。
console.log(...
'hello');
console.log(...new Set([4, 5, 6]));
console.log(...new Map([
['age', 18],
['name', 'hhaa'],
['sex', 'male']
]));
// 2.数组的解构赋值
// const [a, b] = [1, 2];
// const [a, b] = [...[1, 2]];
// const [a, b] = 'hi';
// const [a, b] = [...
// 'hi'
// ];
const [a, b] = [...new Set([3, 4])];
// console.log(a, b);
// 3.Set 和 Map 的构造函数
// new Set(iterator);
// new Map(iterator);
</script>
15.字符串新增方法
includes
includes判断字符串是否包含某个字符,第一个参数是查找字符串中是否包含的字符,第二个参数是搜索的位置,默认是0
<script>
//字符串新增方法-includes
//判断字符串是否包含某个字符
//1.基本用法
console.log('abc'.includes('a')); //true
console.log('abc'.includes('ab')); //true
console.log('abc'.includes('ac')); //false
//2.includes的第二个参数
//第二个参数表示开始搜索的位置,默认是0
//3.实际开发的应用
let chinese = '哈啊哈哈哈哈哈哈哈嘻嘻嘻啦啦啦啦';
const addchinese = (chinese, name, value) => {
chinese += chinese.includes('芜湖') ? '起飞' : '芜湖';
chinese += `${name}+${value}`;
return chinese;
};
chinese = addchinese(chinese, '芜湖', '笑尿');
console.log(chinese);
chinese = addchinese(chinese, '你爹', '淦');
console.log(chinese);
</script>
padStart()和padEnd()方法
1.补全字符串的长度 padstart是从字符串前端开始补全,padend是从末尾处开始补全
2.原字符串的长度,等于或大于最大长度,不会消减原字符串,字符串补全不生效,返回原字符串
3.如果省略第二个参数,默认使用空格补全长度
<script>
//补全字符串的长度 padstart是从字符串前端开始补全,padend是从末尾处开始补全
//1.基本用法
console.log('x'.padStart(5, 'ab')); //输出ababx
console.log('x'.padStart(3, 'ab')); //输出abx;
console.log('x'.padEnd('5', 'ab')); //输出xababa
//2.注意事项
// 原字符串的长度,等于或大于最大长度,不会消减原字符串,字符串补全不生效,返回原字符串
console.log('xxx'.padStart(2, 'ab'));
console.log('xxx'.padEnd(2, 'ab'));
// 用来补全的字符串与原字符串长度之和超过了最大长度,截去超出位数的补全字符串,原字符串不动
console.log('abc'.padStart(10, '0123456789')); //输出0123456abc
console.log('abc'.padEnd(10, '0123456789')); //输出abc0123456
// 如果省略第二个参数,默认使用空格补全长度
console.log('x'.padStart(4));
console.log('x'.padEnd(4));
//3.实际开发应用
</script>
trimStart()和trimEnd()方法
<script>
//trimStart()和trimEnd()方法
// 清除字符串的首或尾空格,中间的空格不会清除 trimStart也叫trimleft,trimEnd也叫trimright
const s = ' a b c ';
console.log(s);
console.log(s.trimStart());
//console.log(s.trimLeft());
console.log(s.trimEnd());
//console.log(s.trimRight());
//清除左右空格
console.log(s.trim());
// 2.应用
const usernameInput = document.getElementById('username');
const btn = document.getElementById('btn');
btn.addEventListener('click', () => {
console.log(usernameInput.value);
// 验证
console.log(usernameInput.value.trim());
if (usernameInput.value.trim() !== '') {
// 可以提交
console.log('可以提交');
} else {
// 不能提交
console.log('不能提交');
}
// 手动提交
}, false);
</script>
replaceAll()方法
searchValue:表示搜索模式,可以是一个字符串,也可以是一个全局的正则表达式(带有 g 修饰符)。
replacement: 表示替换的文本,是一个字符串。
16.数组的新增方法
includes()
判断数组中是否含有某个成员,第二个参数表示搜索的起始位置,默认值是 0
<script>
// 1.基本用法
// 判断数组中是否含有某个成员
console.log([1, 2, 3].includes('2')); // false
console.log([1, 2, 3].includes(2));
// 第二个参数表示搜索的起始位置,默认值是 0
console.log([1, 2, 3].includes(2, 2));
// 基本遵循严格相等(===),但是对于 NaN 的判断与 === 不同,includes 认为 NaN === NaN
console.log(NaN === NaN);
console.log([1, 2, NaN].includes(NaN));
//2.应用
// 去重
const arr = [];
for (const item of[1, 2, 1]) {
if (!arr.includes(item)) {
arr.push(item);
}
}
console.log(arr);
</script>
Array.form()方法
Array.form 将其他数据类型转换为数组
<script>
//Array.form 将其他数据类型转换为数组
console.log(Array.from('str')); //输出[s,t,r]
//什么可以通过Array.form转为数组
//数组、字符串、nodelist、set、map、arguments
//2.拥有 length 属性的任意对象 将对象转为数组的前提,对象的键要是字符串型的数字,或者数值型的数字。
const obj = {
'0': 'a',
'1': 'b',
name: 'Alex',
length: 3
};
console.log(Array.from(obj));
// 3.第二个参数
// 作用类似于数组的 map 方法,用来对每个元素进行处理,将处理后的值放入返回的数组
console.log([1, 2].map(
value => {
return value * 2;
}
))
console.log(Array.from([1, 2], value => value * 2));
// 4.第三个参数,this指向,箭头函数没有this 指向window
console.log(Array.from([1, 2], value => {
console.log(this);
}, document))
console.log(Array.from([1, 2], function() {
console.log(this);
}, document))
</script>
find()和findindex()方法
find() 找到满足条件的一个立即返回
findindex()找到满足条件的一个立即返回该索引值
<script>
//find() 找到满足条件的一个立即返回
//findindex()找到满足条件的一个立即返回该索引值
//1.基本用法 第三个参数this指向
console.log([1, 5, 10, 15].find((value, index) => {
return value > 9; //输出10
}));
console.log([1, 5, 10, 15].findIndex((value, index) => {
return value > 9; //输出2
}));
//2.实际开发的应用
const students = [{
name: '张三',
sex: '男',
age: 16
}, {
name: '李四',
sex: '女',
age: 22
}, {
name: '王二麻子',
sex: '男',
age: 32
}];
console.log(students.find(value => value.sex === '女'));
console.log(students.findIndex(value => value.sex === '女'));
</script>
17.对象的新增方法
Object.assign()方法
object.assign() 用来合并对象 但属性相同,后面的会覆盖前面的属性
assign参数:Object.assign(目标对象, 源对象1,源对象2,…): 目标对象
合并但返回的不是新对象,不像结构赋值是返回一个新对象,他是合并到前一个对象中,返回的是前一个对象
<script>
//1.基本用法
//object.assign() 用来合并对象 但属性相同,后面的会覆盖前面的属性
// Object.assign(目标对象, 源对象1,源对象2,...): 目标对象
const apples = {
color: '红色',
shape: '圆形',
taste: '甜'
};
const pens = {
color: '黑色',
shape: '圆柱形',
use: '写字'
};
/*但返回的不是新对象,不像结构赋值是返回一个新对象,他是合并到前一个对象中,返回的是前一个对象*/
// Object.assign 直接合并到了第一个参数中,返回的就是合并后的对象,前一个对象
console.log(Object.assign(apples, pens));
console.log(apples); //输出的结果是合并的结果
console.log(apples === Object.assign(apples, pens)); //输出true
//可以合并多个对象
console.log(Object.assign({}, apples, pens));
//2.注意事项
//基本数据类型作为源对象,assign有多个参数(第一个是目标参数,后面的是源对象)
// 与对象的展开类似,先转换成对象,再合并
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'));
//3.同名属性的替换
// 后面的直接覆盖前面的
const apple = {
color: ['红色', '黄色'],
shape: '圆形',
taste: '甜'
};
const pen = {
color: ['黑色', '银色'],
shape: '圆柱形',
use: '写字'
};
console.log(Object.assign({}, apple, pen));
// 4.应用
// 合并默认参数和用户参数
const logUser = userOptions => {
const Default = {
username: 'ZhangSan',
age: 0,
sex: 'male'
}
const option = Object.assign({}, Default, userOptions);
console.log(option);
};
logUser({
username: 'William',
age: '18'
});
</script>
Object.keys()、Object.values()、Object.entries()
<script>
//1.基本用法
const person = {
name: 'alex',
age: 18
};
console.log(Object.keys(person));
console.log(Object.values(person));
console.log(Object.entries(person));
// 2.与数组类似方法的区别
// 数组的 keys()、values()、entries() 等方法是实例方法,返回的都是 Iterator
// 对象的 Object.keys()、Object.values()、Object.entries() 等方法是构造函数方法,返回的是数组
console.log([1, 2].keys()); //实例方法
console.log([1, 2].values());
console.log([1, 2].entries());
console.log(person.keys); //构造函数方法
// 3.使用 for...of 循环遍历对象
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);
}
</script>
18.ES6之Promise与class类
初始Promise
Promise是什么
了解promise前先认识异步和同步
1.同步和异步
(1)js代码,正常情况下都是同步的;同步就是按照顺序,从上往下依次执行,如下:
同步就好比一次只能做一件事,事件A做完才能继续往下做事件B。
同步有个缺点,就是会阻塞程序往下运行;假如事件A需要花费很多时间,那么事件B就会处于等待状态(就会被阻塞),直到事件A做完,才能做事件B;而异步就能处理这个问题。
异步简单理解就是一次能做多件事,即可以同时做事件A和事件B。js中常见的异步就是定时器,如下代码中,打印“事件B”的代码是异步的(包裹在定时器中),所以代码准备打印“事件B”的过程中,不会阻塞,程序会继续往下执行打印“事件C”:
2.回调函数
(1)我们把作为参数传入到另一个函数中的函数称为回调函数。如下代码中,函数A是函数B的参数,并且在函数B中执行,则A就是一个回调函数:
如果想实现先执行B,过1s,再执行A,则可以利用回调函数实现,如下
Promise 一般用来解决层层嵌套的回调函数(回调地狱 callback hell)的问题
Promise的基本用法
1.实例化构造函数生成实例对象
const p = new Promise(() => {});
一个Promise的当前状态必须为以下三种状态中的一种:等待态(Pending)、执行态(Fulfilled)和拒绝态(Rejected)
promise有三种状态,一开始是pending(未完成),执行resolve,变成fulfilled(resolved)以成功
执行reject,变成rejected,已失败
状态的改变只能是单向的,且变化后不可在改变。 意思就是如果resolve和reject一起执行,状态是成功态
then方法 ,有两个回调函数,一个回调是状态从pedind到fulfilled执行,第二个是pending到rejected执行
<script>
//Promise基本用法
//1.实例化构造函数生成实例对象 (promise是一个构造函数)
//promise解决的不是回调函数,而是回调地狱
const p = new Promise(() => {});
//2.promise的状态
//一个Promise的当前状态必须为以下三种状态中的一种:等待态(Pending)、执行态(Fulfilled)和拒绝态(Rejected)
const s = new Promise((resolve, reject) => {
//promise有三种状态,一开始是pending(未完成),执行resolve,变成fulfilled(resolved)以成功
//执行reject,变成rejected,已失败
//状态的改变只能是单向的,且变化后不可在改变。 意思就是如果resolve和reject一起执行,状态是成功态
resolve(); //输出pending->filfilled
//reject主要传入的是error错误对象
reject(); //输出pending->rejected
});
console.log(s);
//3.then方法 ,有两个回调函数,一个回调是状态从pedind到fulfilled执行,第二个是pending到rejected执行
s.then(() => {
console.log('success');
}, () => {
console.log('error');
});
//4.resolve和reject函数的参数
const a = new Promise((resolve, reject) => {
// resolve({
// username: 'alex'
// });
reject(new Error('reason'));
})
a.then((data) => {
console.log('完成', data);
}, (err) => {
console.log('失败', err);
})
</script>
promise是什么?
Promise是用来解决异步操作的问题。回调函数层层嵌套的问题。链式调用
Promise有三种状态:
pending(等待状态)
fulfilled(执行状态)
rejected(失败状态)
promise状态一经改变就不会变化了,promise状态是两种情况,pending-fulfilled,pending-rejected
then()方法
1.then()方法什么时候执行
pending-fulfilled执行第一个回调函数
pending-rejected执行第二个回调函数
2.执行后的返回值、
then方法执行后返回一个新的promise对象,promise状态为等待态pending
3.then方法返回的promise对象的状态改变
在then的回调函数中,return后面的东西会用promise包装一下,等价于return new Promise((resolve, reject)=>{resolve()}
<script>
//3.then方法返回的promise对象的状态改变
const s = new Promise((resolve, reject) => {
resolve(); //执行resolve,pending->fulfilled,then方法执行第一个回调函数
});
s.then(
() => {
console.log('success'); //输出这个success
//return 123;
/*这里返回一个promise对象,promise有个规定,在then的回调函数中,
return后面的东西会用promise包装一下,等价于return new Promise((resolve, reject)=>{resolve(123)}*/
return new Promise((resolve, reject) => {
//默认执行resolve
resolve(123); //相当于返回的undefined用promise包装
//reject();
})
//可以直接省略上方的,直接写return xxx。
//默认返回的永远是成功的promise对象
},
() => {
console.log('err');
}).then(
(data) => {
//接着输出success2,undefined。因为前面的then调用了它,前面的then返回的是一个promise对象,然后看前面的then的那个回调函数起作用了,前面的resolve起作用了
console.log('success2', data);
return 456;
},
() => {
console.log('err2');
}
).then(
//输出success3,因为前面的then调用了他,并且前的then返回了新的promise对象,前面的then的回调函数是resolve,前面return456,用Promise包装,默认执行resolve
(data) => {
console.log('success3', data)
},
(err) => {
console.log('err', err);
}
)
</script>
Catch()
<script>
//1.catch
//catch有什么用?catch专门用来处理rejected状态
//2.基本用法
new Promise((resolve, reject) => {
//resolve(123);
reject('reason');
}).then(() => {
console.log();
}).catch((data) => {
console.log('error', data);
})
</script>
finally()
<script>
//finally()
//1.什么时候执行呢?当Promise状态发生变化是,无论如何变化都会执行,不变化就不执行
new Promise((resolve, reject) => {
resolve(123)
}).then((data) => {
console.log('one', data);
}).finally(data => {
console.log(data);
}).catch((err) => {
})
</script>
Promise.resolve()和Promise.reject()
1.Promise.resolve()
是成功状态promise的一种简写方式
(1)Promise.resolve()方法的参数
一般参数
//参数
//一般参数
Promise.resolve('foo').then(data => {
console.log('1', data);
})
promise对象自身作为参数。
当Promise.resolve()接收的是promise对象时,直接返回这个promise对象,什么都不做
当 resolve 函数接收的是 Promise 对象时,后面的 then 会根据传递的 Promise 对象的状态变化决定执行哪一个回调
//可以把promise对象,当做一个参数
const p1 = new Promise(resolve => {
// setTimeout(() => {
// resolve('我执行了');
// },1000);
setTimeout(resolve, 1000, '我执行了');
})
//当Promise.resolve()接收的是promise对象时,直接返回这个promise对象,什么都不做
Promise.resolve(p1).then(data => { //这里的data是p1里面的resolve,返回给promise对象,相当于p1.then(),p1中的resolve执行了,then在执行p1的data
// 当 resolve 函数接收的是 Promise 对象时,后面的 then 会根据传递的 Promise 对象的状态变化决定执行哪一个回调
console.log(data);
})
//等价于
p1.then(data => {
console.log(data);
})
具有then方法的对象作为参数
// 具有 then 方法的对象
function func(obj) {
obj.then(1, 2);
}
func({
then(resolve, reject) {
console.log(a, b);
}
});
const thenable = {
then(resolve, reject) {
console.log('then');
resolve('data');
// reject('reason');
}
};
Promise.resolve(thenable).then(
/*thenable传入promise.resolve会立即执行thenable中的then方法,
执行then方法后会返回一个新的promise对象,但对象为等待态pending,
如果需要改变p1的状态则要在thenable的then方法中标明形参resolve和reject并在其中执行。*/
data => console.log(data),
err => console.log(err)
);
console.log(Promise.resolve(thenable));
2.Promise.reject()
失败状态Promise的一种简写形式
new Promise((resolve, reject) => {
console.log('reason');
})
//等价于
Promise.reject('reason');
参数
不管什么参数,都会原封不动得向后传递,作为后续方法的参数
const p2 = new Promise((resolve, reject) => {
resolve('123');
}).then(data => {
return Promise.reject('reason');
})
.catch(data => {
console.log(data);
})
Promise.all()
promise.all()关注多个promise对象的状态的变化
可以在promise.all()中传入多个promise实例,包装成一个新的promise实例返回
promise.all要最后一个参数完成了才会开始回调
promise.all()的状态变化与所有转入的promise实例对象状态有关,所有实例对象的状态是resolved(成功态),最终的状态才会变成resolved,才会调用p.then的第一个回调
只要有一个promise实例对象的状态是rejected,那么最终的状态是rejected,调用第二个回调
//2.基本用法
const delay = ms => {
return new Promise(resolve => {
setTimeout(resolve, ms);
})
};
const p1 = delay(1000).then(() => {
console.log('p1完成了');
return 'p1';
});
const p2 = delay(1000).then(() => {
console.log('p2完成了');
return 'p2';
});
//promise.all要最后一个参数完成了才会开始回调
//promise.all()的状态变化与所有转入的promise实例对象状态有关,所有实例对象的状态是resolved(成功态),最终的状态才会变成resolved,才会调用p.then的第一个回调
//只要有一个promise实例对象的状态是rejected,那么最终的状态是rejected,调用第二个回调
const p = Promise.all([p1, p2]);
p.then((data) => {
console.log(data)
}, (err) => {
console.log(err)
});
Promise.race()和Promise.allSettled()方法
promise.race() 与promise.all()相似,同样是关注多个promise实例对象的状态的变化
Promise.race()的状态取决于第一个完成的promise实例对象,如果第一个完成了,那最终就是成功了执行then的第一个回调,如果第一个失败了,那最终就是失败了执行then的第二个回调
//promise.race()和promise.allSettled()
//1.promise.race() 与promise.all()相似,同样是关注多个promise实例对象的状态的变化
const delay = (ms) => {
return new Promise(resolve => {
setTimeout(resolve, ms);
});
};
const p1 = delay(1000).then(() => {
console.log('p1完成了');
return 'p1';
})
const p2 = delay(1000).then(() => {
console.log('p2完成了');
return 'p2';
})
//Promise.race()的状态取决于第一个完成的promise实例对象,如果第一个完成了,那最终就是成功了执行then的第一个回调,如果第一个失败了,那最终就是失败了执行then的第二个回调
const racePromise = Promise.race([p1, p2]);
racePromise.then(data => {
console.log(data) //输出结果是p1完成了 p1 p2完成了 不会输出p2
}, err => {
console.log(err); //第一个失败就执行第二个回调 p1完成了 reason p2完成了
});
promise.allsettled()
promise.allsettled()的状态与传入的promise实例对象的状态没有任何关系,他永远都是fulfilled(成功的),它只是会记录每个promise的表现
//2.promise.allsettled()
//promise.allsettled()的状态与传入的promise实例对象的状态没有任何关系,他永远都是fulfilled(成功的),它只是会记录每个promise的表现
const allSettledPromise = Promise.allSettled([p1, p2]);
allSettledPromise.then(data => {
console.log(data);
})
Promise.any()
Promise.any(iterable);
iterable:表示一个可迭代的对象,例如:数组
传入的参数是一组Promise实例,那么所有Promise实例都变成rejected状态,返回的Promise才会变成rejected状态,参数中只要有一个Promise改变为成功状态,则返回的Promise状态就是成功的状态。
(1)参数中只有一个成功状态的Promise实例
// 失败
const p1 = new Promise((resolve, reject) => {
reject()
});
// 失败
const p2 = new Promise((resolve, reject) => {
reject()
});
// 成功
const p3 = new Promise(resolve => {
resolve()
});
const res = Promise.any([p1, p3, p2])
console.log(res) // 返回成功状态的Promise
传入的一组Promise实例参数中,虽然p1、p2这两个是失败状态,但其中的p3是成功状态,所以Promise.any()最终返回结果是成功状态的Promise,如下图所示:
(2)参数中全部是失败状态的Promise实例
// 失败
const p1 = new Promise((resolve, reject) => {
reject()
});
// 失败
const p2 = new Promise((resolve, reject) => {
reject()
});
// 失败
const p3 = new Promise((resolve, reject) => {
reject()
});
const res = Promise.any([p1, p3, p2])
console.log(res) // 返回失败状态的Promise
由于参数中的p1、p2、p3这个三个Promise实例都是失败状态的,所以Promise.any()返回一个失败状态的Promise实例,如下图所示:
(1)Promise.any()不会因为某个Promise实例变成失败状态而结束,这个方法用于返回第一个成功的 promise。只要有一个 Promise成功此方法就会终止,它不会等待其他的 Promise全部完成
const p1 = new Promise((resolve, reject) => {
reject("失败");
});
const p2 = new Promise((resolve, reject) => {
setTimeout(resolve, 500, "最后完成");
});
const p3 = new Promise((resolve, reject) => {
setTimeout(resolve, 100, "第一个完成");
});
const res = Promise.any([p1, p2, p3])
res.then((value) => {
console.log(value);
})
上面这段代码中,p1是失败状态,但Promise.any()方法并没有结束,而是返回第一个成功的Promise, 即p3,当有一个Promise成功后,就会终止,所以最终输出结果只有“第一个完整”,并没有“最后完成”内容。如下图所示:
Promise注意事项
1.resolve和reject函数执行之后的代码:
推荐在调用 resolve 或 reject 函数的时候加上 return,不再执行它们后面的代码
new Promise((resolve, reject) => {
//return resolve('123');
//return reject('456');
})
2.Promise.all/race/allSettled 的参数问题
这三个方法可以传入多个promise对象,传入是以[]数组的方式传入,如果传入的不是promise数组,那么会将不是promise数组的参数转变为promise对象
Promise.all([1, 2, 3]).then(datas => {
console.log(datas); //输出[1, 2, 3]
})
//等价于
Promise.all([
Promise.resolve(1),
Promise.resolve(2),
Promise.resolve(3),
]).then(datas => {
console.log(datas);
})
不只是数组,任何可遍历的都可以作为参数
数组、字符串、Set、Map、NodeList、arguments、字符串
Promise.race('hello').then(datas => {
console.log(datas);
})
//Map
Promise.all(new Map([
['username', 'wjw'],
['age', 18]
])).then(data => {
console.log(data);
})
3.Promise.all/race/allSettled 的错误处理
3.Promise.all/race/allSettled 的错误处理
const dealy = ms => {
return new Promise(resolve => {
setTimeout(resolve, 1000);
});
};
const p1 = dealy(1000).then(() => {
console.log('p1完成了');
// return 'p1';
return Promise.reject('reason');
});
const p2 = dealy(2000).then(() => {
console.log('p2完成了');
return 'p2';
}).catch(err => {
console.log(err);
});
const all = Promise.all([p1, p2]).then(data => {
console.log(data);
}).catch(err => {
console.log(err);
})
// 错误既可以单独处理,也可以统一处理
// 一旦被处理,就不会在其他地方再处理一遍
异步加载图片
const urlImgPromise = (url) => {
return new Promise((resolve, reject) => {
const img = new Image();
//当图片加载完成
img.onload = () => {
setTimeout(() => {
document.body.appendChild(img);
resolve();
}, 1000);
}
img.onerror = () => {
reject(new Error(`Could not load image at${url}`));
}
img.src = url;
});
};
urlImgPromise('http://climg.mukewang.com/5b16558d00011ed506000338.jpg').then(() => {
return new Promise((resolve, reject) => {
resolve(urlImgPromise('http://climg.mukewang.com/5b165603000146ca06000338.jpg'));
})
}).catch(err => {
console.log(err);
}).then(() => {
return urlImgPromise('http://climg.mukewang.com/5b1656140001c89906000338.jpg');
}).catch(err => {
console.log(err);
})
初始Class
Class是什么
初始class
class是类的统称,类可以看做是对象的模板,用一个类可以创建许多不同的对象
class基本用法
类名一般是首字母大写
class Person {
//实例化执行构造方法,所以必须有构造方法,但可以不写出来
constructor(name, age) {
this.name = name;
this.age = age;
console.log('实例化时构造方法执行了');
//一般在构造方法中定义属性,方法不再构造方法中定义
}
//各实例共享的方法
speak() {
console.log('speak');
}
}
const zs = new Person('zs', 18);
console.log(zs.name, zs.age);
zs.speak();
console.log(typeof Person); //是function类型
console.log(Person.prototype.speak); //与构造函数类似,同样可以再原型链上找到speak,speak只是person类中添加的,但实际是在person类的原型上添加的
class的两种定义形式
//class两种定义形式
//1.声明形式
class Person {
constructor() {}
speak() {}
}
//2.表达式形式
const Persons = class {
constructor() {
console.log('haha');
}
speak() {}
};
new Persons();
//立即执行的类
new(class {
constructor() {
console.log('object');
}
})();
实例属性、静态方法和静态属性
<script>
//实例属性
//实例方法就是值为函数的特殊属性
class Person {
//可以在外面设置默认值,但没有this
age = 18;
sex = 'male';
getSex = function() {
return this.sex;
}
constructor(name, sex) {
this.name = name; //this上添加的属性就是实例属性
}
}
const p = new Person('alex', '男');
console.log(p.name);
console.log(p.sex);
</script>
//静态方法
//静态方法就是类的方法,之前的是实例方法,是通过实例对象调用的。静态方法不需要实例化类就能调用的方法
class Staticmethod {
constructor(name, sex) {
this.name = name;
this.sex = sex;
}
talk() {
console.log('talk');
}
static talk() {
console.log('we need to talk');
}
}
const s = new Staticmethod();
Staticmethod.talk();
s.talk();
console.log(s.talk() === Staticmethod.talk());
// 静态属性
// 类的属性
class Person {
constructor(name) {
this.name = name;
}
// 不要这么写,目前只是提案,有兼容性问题
// static version = '1.0';
static getVersion() {
return '1.0';
}
}
// Person.version = '1.0';
const p = new Person('Alex');
console.log(p.name);
// console.log(Person.version);
console.log(Person.getVersion());
私有属性和方法
私有属性和方法
1.为什么需要私有属性和方法
一般情况下,类的属性和方法都是公开的
公有的属性和方法可以被外界修改,造成意想不到的错误
2.模拟私有属性和方法
2.1._ 开头表示私有
class Person {
constructor(name) {
this._name = name;
}
speak() {
console.log('speak');
}
getName() {
return this._name;
}
}
const p = new Person('alex');
console.log(p.getName());
3.将私有属性和方法移出类
(function() {
let name = ''; //name是当前函数作用域的变量
class Person {
constructor(username) {
console.log('username:' + username);
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
子类继承父类
class Person {
constructor(name, sex) {
this.name = name;
this.sex = sex;
this.say = () => {
console.log('say');
}
}
speak() {
console.log('speak');
}
static speak() {
console.log('speak chinese');
}
}
Person.version = '1.0';
class Programmer extends Person {
//因为继承了父类的构造方法,所以子类的构造方法要调用父类的构造方法
constructor(name, sex, feature) {
super(name, sex, );
//this操作不能放在super前面
this.feature = feature;
}
//子类如果有跟父类同名的方法,那么以子类为准
speak() {
console.log('hhhahh');
}
}
const p = new Programmer('zs', 'male', '秃头使你变强');
console.log(p.name);
console.log(p.sex);
console.log(p.feature);
p.say();
p.speak();
Programmer.speak();
console.log(Programmer.version);
super
1.作为函数调用
代表父类的构造方法,只能在子类的构造方法中,用在其他地方会报错
super虽然代表了父类的构造方法,但是内部的this指向子类的实例
class Persons {
constructor(name) {
this.name = name
}
}
class Programmers extends Persons {
constructor(name, sex) {
super(name, sex);
this.sex = sex;
}
}
2.作为对象使用
在构造方法中使用或一般方法
super代表父类的原型对象Person.prototype。那么子类要用父类的方法可以通过super去调用。不会因为子类使用跟父类同名的方法而覆盖父类的方法
通过super调用父类的方法,方法内部的this指向子类的实例
super代表父类的原型对象Person.prototype。那么子类要用父类的方法可以通过super去调用。
在静态方法中使用
通过super调用父类的方法时,方法内部的this指向是子类,而不是子类的实例
class Person {
constructor(name) {
this.name = name;
console.log(this);
}
speak() {
console.log('speak');
// console.log(this);
}
static speak() {
console.log('静态父类');
console.log(this);
}
}
class Programmer extends Person {
constructor(name, sex) {
super(name, sex);
console.log(super.name); //输出 undefined,定义父类实例上的方法或属性无法通过super调用,sup只能调用原型对象
// super.speak();
}
speak() {
super.speak(); //输出子类的实例
console.log('Programmer speak');
}
//在静态方法中使用
//通过super调用父类的方法时,方法内部的this指向是子类,而不是子类的实例
static speak() {
super.speak();
console.log('静态子类');
}
}
new Programmer();
const p = new Programmer('wjw', 'male');
p.speak();
Programmer.speak();
// 3.注意事项
// 使用 super 的时候,必须显式指定是作为函数还是作为对象使用,否则会报错。
//console.log(super); //报错
console.log(super());
console.log(super.speak);
子类继承父类,使用super,那么子类相当于把父类的constructor全继承了,可以使用父类的构造方法
ES6之Module模块与Babel编译
Module是什么
Module是模块系统
1.什么是模块,模块是一个一个局部作用域的代码块
2.什么是模块系统
模块系统需要解决的主要问题
1.模块化的问题
2.消除全局变量()
3.管理加载顺序(将js文件中的方法区分开来放入新的文件中,那么js的文件是要按照顺序加载的)
Module基本用法
2.使用 script 标签加载模块
<script src="./index.js" type="module"></script>
一个文件就是一个模块
只要你会用到 import 或 export,在使用 script 标签加载的时候,就要加上 type=“module”
3.分析 Module 解决的问题
① 模块化的问题
② 消除全局变量
③ 管理加载顺序
export default和对应 import
1.认识导出和导入
导出(export)的东西可以被导入(mport),并访问到
一个模块没有导出,也可以将其导入 (js文件中没有写导出),其他页面也可以将其导入
被导入的代码都会执行一遍,也仅会执行一遍
2.基本用法
导出:export default (跟着需要导入的模块)
导入:import (可以任意名称)from './xxx.js’
import age from './module.js';
注意:一个模块只能有一个 export default,不能有多个export default,如果要多个导出需要用export。
export和对应的import
1.基本用法
导出
export(声明或语句),不同于export default一样,后面只需要名称,expoet需要整个声明或语句
expoet const age=18; export {age}; 两种导出方式
导入,导入不能随意命名,如果要命名需要后面加as
import {age}from'./module.js';
2.多个导入
import{func}from './module.js';
import{ClassName}from './module.js';
多个导入的另一种方式
import{sexx,name,add,fun,num1,num2}from'./module.js';
3.导出导入起别名 需要加as关键字,那么导入时要写别名而不是原先的名称
import{sexx,name as username,add,fun,num1,num2}from'./module.js';
export{sex as sexx,name,add,fun,num1,num2};
4.整体导入 会导入所有输出,包括export default
import * as obj from'./module.js';
5.同时导入 注意export default必须写在前面
import year,{sexx}from './module.js';
Module的注意事项
Babel
Babel是什么
1.认识 Babel
官网:https://babeljs.io/
在线编译:https://babeljs.io/repl
Babel 是 JavaScript 的编译器,用来将 ES6 的代码,转换成 ES6 之前的代码
2.解释编译结果
Babel 本身可以编译 ES6 的大部分语法,比如 let、const、箭头函数、类
但是对于 ES6 新增的 API,比如 Set、Map、Promise 等全局对象,以及一些定义在全局对象上的方法(比如 Object.assign/Array.from)都不能直接编译,需要借助其它的模块
Babel 一般需要配合 Webpack 来编译模块语法
使用Babel编译ES6代码
1.安装Node.js
https://nodejs.org/en/
2.初始化
在当前项目窗口输入cmd打开命令行工具
然后输入npm init,这是初始化命令
初始化完毕后,文件中有一个package.json文件
3.安装Babel需要的包
还是在项目窗口中打开cmd,输入npm install --save-dev @babel/core @babel/cli 回车
4.添加script
"scripts": {
"build": "babel src -d dist"
},
5.安装babel配置文件
打开cmd,输入npm install @babel/preset-env --save-dev
安装后再src创建.babelrc的文件,在文件中配置
{
"presets": ["@babel/preset-env"]
}
6.编译
cmd输入 npm run build。生成编译后的文件
Webpack入门
Webpack是什么
Webpack初体验
1.初始化项目 npm init
2.安装webpack需要的包
npm install --save-dev webpack-cli@3.3.12 webpack@4.44.1
修改package.json中的scripts
3.配置webpack
根目录创建webpack.config.js配置文件
其中的mode是开发者模式,默认没写mode是上传模式,代码都经过压缩和混淆了。
4.编译并测试
npm run webpack
entry和output
1.entry,entry指定的是入口文件
分为单路口和多路口
2.出口分为多个和单个
单路口和多路口
module.exports = {
mode: 'development',
//单路口
entry: './src/index.js', //将src中的index文件转换到dist目录下名为main.js的文件中
//多路口
entry: {
main: './src/index.js',
search: './src/search.js'
},
//单个出口
output: {
//dirname指代当前所在的目录,dist是目录下的文件名,path.resolve是node提供的方法,将在当前目录下创建名为dist的文件夹
//dirname返回的是绝对路径与dist进行拼接
//单出口
path: path.resolve(__dirname, 'dist'),
// //dist文件中的文件名称为main.js
filename: 'main.js',
},
//多出口
output: {
path: path.resolve(__dirname, 'dist'),
//多个出口,使用[]里面写上name,当打包一个name=main,第二个name=search
filename: '[name].js'
}
};
loader
1.什么是loader
首先webpack是静态模块打包器,当本质是处理js的,但也可以处理css和图片
loader就是让webpack能够去处理那些非js文件的模块
2.babel
babel-loader 先让babel编译成兼容性代码 然后在用webpack进行打包,使用babel-loader编译成es5代码
安装babel-loader
npm install --save-dev babel-loader@8.1.0
3.安装 Babel
npm install --save-dev @babel/core@7.11.0 @babel/preset-env@7.11.0
创建 .babelrc配置文件 { “presets”: ["@babel/preset-env"] }
4.配置 babel-loader
https://www.webpackjs.com/loaders/
在webpack.config.js中配置loader
5.引入 core-js
编译新增 API 因为es6新增了set map promise等不能直接编译,所以需要引入core-js进行编译
npm install --save-dev core-js@3.6.5
import “core-js/stable”;
6.打包并测试
npm run webpack
plugins
1.什么是plugins
插件
loader 被用于帮助 webpack 处理各种模块,而插件则可以用于执行范围更广的任务
插件官方文档 https://www.webpackjs.com/plugins/
2.html-webpack-plugin
安装 html-webpack-plugin
npm install --save-dev html-webpack-plugin@4.3.0
3.配置 html-webpack-plugin 插件
处理CSS文件
js中导入css
import './index.css'
安装css-loader": “^4.1.1”,
安装html-webpack-plugin": “^4.3.0”,
安装mini-css-extract-plugin": “^0.9.0”,
使用 file-loader 处理 CSS 中的图片
如果是外部的资源,是不需要考虑 webpack 的,只有本地的图片才需要被 webpack 处理
安装file-loader
npm install --save-dev file-loader@6.0.0
安装的插件
webpack.config.js配置
module: {
rules: [{
test: /\.css$/,
// 需要多个loader不需要写参数配置,用[]进行包裹,如果需要写参数配置用{}包裹
// 注意:多个loader,那么会从右到左开始使用
// 统一设置公共路径,保证图片和css能够正常使用
use: [{
loader: MiniCssExtractPlugin.loader,
options: { publicPath: '../' }
},
'css-loader'
]
},
{
test: /\.(jpg|png|gif)$/,
//将图片放入文件夹中
use: {
loader: 'file-loader',
//ext是后缀名
options: {
name: 'img/[name].[ext]'
}
}
}
]
},
使用 html-withimg-loader 处理 HTML 中的图片
安装html-withimg-loader
npm install --save-dev html-withimg-loader@0.1.16
webpack.config.js配置
{
test: /\.(html|htm)$/,
loader: 'html-withimg-loader'
}
如果同时使用file-loader,那么html页面可能出现以对象的形式设置图片,这是因为file-loader默认是以es6模块导出。要修改esModule为false
使用 file-loader 处理 JS中的图片
将图片导入js文件中
配置webpack.config.js
使用 url-loader 处理 JS中的图片
安装url-loader的同时也要安装file-loader,因为底层用到file-loader
安装url-loader
npm install --save-dev url-loader@4.1.0
配置webpack.config.js
使用webpack-dev-server搭建开发环境
1.安装webpack-dev-server
npm install --save-dev webpack-dev-server@3.11.0
2.package.json配置搭建环境,这样以后不用每次都npm run了。配置了只要保存就会自动打包。而且不会在硬盘生成dist目录了,而是在内存中生成dist目录。–open Chrome 自动打开浏览器
3.运行
npm run dev
那么只会每次在代码中修改,保存后浏览器会实时刷新,注意运行npm run dev后不能关闭窗口,不然不能实时刷新,同时页面也会无法访问。