ES6

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后不能关闭窗口,不然不能实时刷新,同时页面也会无法访问。

总结

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值