Bootstrap

【前端开发----ES6++】前端开发之ES6(ECMAscript6)语法以及ES6++快速学习入门(前端必备了解知识)

ES6++(ES6---- ES9)

下面链接时笔记的获取方式,包括Md和PDF文档
ES6++超详细的学习笔记,点我获取
在这里插入图片描述

什么是ECMA

// 中文名称是欧洲计算机制造商协会,这个组织的目的是评估,开发和认可电信和计算机标准。1994年后该组织改名为ECMA国际

什么是ECMAscript

// ECMAscript是由ECMA 国际通过ECMA-262标准化的脚本程序设计语言

1,ES6

1.1 let变量声明以及声明特性

//变量不能重复声明
let star = 'zll';
let star = 'wxl';//此时会报错

//块级作用域有效 全局、函数、ecal
{ //if for else while
    let str='zzl';
}
console.log(str);//此时会报错,因为拿不到

// 不存在变量提升
console.log(name);//会报错,不能使用name这个变量
let name='zzl';

//不影响作用域链
{
    let name='zzl';//虽然是块级作用域,但是不影响作用域链的效果
    function fn(){
        console.log(name);
    }
    fn();//可以拿到
}
1.1.1 let const和var的区别?
// var   存在变量提升(在声明语句代码之前可以使用,不会报错)  let  const 存在暂时性死区(声明之后在声明语句后面代码才可以用)

console.log(a)             console..log(a)   报错        console.log(a) 报错
var a = 10                   let  a = 10                     const a = 10

// vat 声明的变量可以重复声明   let const声明的变量不能重复声明
var  a  = 10                let a = 10                  const  a = 10
var  a  = 20			   let a = 20 报错             const  a = 14 报错
// var 没有块级作用域     let const 有块级作用域(例如:在while  for 循环中)
for(var i = 0;i<10;i++){}          for(let i = 0;i<10;i++){}
i泄漏为全局变量,存在于全局变量中       i有块级作用域

// var let 是声明变量,变量值可以改变     cosnt 声明常量,值不能改(准确来说,内存地址不能变),而且声明时必须赋值

1.2 const 声明常量以及特点

1const声明常量
2,一定要赋初始值
3,常量的值不能再修改
4,也是块级作用域,
5,声明常量

const CL='zzl';
// 对于数组和对象的元素修改,不算2中的常量修改,不会报错
const SZ=['zzl','wxl'];
SZ.push('ll');//此时不会报错,因为只是修改数据,并没有修改地址

1.3 变量的解构赋值

//数组解构赋值
    const nameS=['zzl','wxl','ll'];
    let [name1,name2,name3]=nameS;
    console.log(name1);//输出zzl

//对象的结构赋值
    const data={
        name:'zzl',
        age:'22',
        active:function(){
            console.log('我会飞');
        }
    }
    let {name,age,active}=data;
    console.log(name); // 输出zzl
    active(); // 调用方法,输出我会飞

let {active} = data;
active();

1.4 模板字符串

let str=`字符串`

// 它可以支持换行
// 变量拼接

let name='zzl';
let data=`${name}是个人`   //  'zzl是个人'

1.5 对象简化写法

// es6允许在大括号里面直接写入变量名和函数名,作为对象的属性和方法

let name='zzl';
let fun=function(){
    console.log('我是人');
}
const data={
    name, //可以直接写变量名,不用再写值了
    fun
}
console.log(data);    // 结果输出  name:'zzl',fun()


const data={
    // 旧的方法定义
    // act:function(){
    //     console.log('我会动');
    // }
    //新的方法定义
    act(){
        console.log('我会动');
    }
}

1.6 箭头函数以及声明特点

// 声明一个函数
let fn=function(a,b){
}
// 箭头函数声明一个函数
let fn=(a,b)=>{
}
fn(1,2);
1.6.1 特点
// 箭头函数的额this是静态的,this始终指向函数声明时所在作用域下的this的值
function getname(){
    //name是zzl,直接调用的话this值指向window
}
    console.log(this.name);//zzl
let getname2=()=>{
    //而这个箭头函数是在全局作用域下声明的,所以this也是指向window
    console.log(this.name);//zzl
}

window.name='zzl';
const data={
    name:'wxl'
}

//直接调用
getname();//name是zzl,直接调用的话this值指向window
getname2();//而箭头函数是在全局作用域下声明的,所以this也是指向window

// call方法调用
getname.call(data);//此时普通函数this的值已经变为data了。
getname2.call(data);
//输出wxl,因为它的this依然指向函数getname2声明时所在作用域下的this的值window.name='zzl';



// 不能作为构造实例化对象
let Person=(name,age)=>{
    this.name=name;
    this.age=age;
}
let me=new Person('zzl','22');//此时会报错,箭头函数不能作为实例化对象。



//不能使用arguments变量
// arguments:用来保存实参的
let fn=()=>{
    console.log(arguments);
}
fn(1,2,3);//会报错,不用用来保存实参
1.6.2 简写箭头函数
// 省略小括号
let add=(n)=>{
    return n+n;
}
console.log(add(9));

//简写为下面----------

//当形参有且只有一个的时候,可以省略小括号
let add=n=>{
    return n+n;
}
console.log(add(9));



//省略花括号
//当代码体只有一条语句的时候

let sum=(n)=>{
    return n*n;
};
console.log(sum(9));

//简写为下面----------

//当代码体只有一条语句的时候,此时return必须省略,而且语句的执行结果就是函数的返回值
let sum=(n)=>n*n;
console.log(sum(9));

//小括号和花括号同时省略----------
let sum=n=>n*n;
console.log(sum(9));
1.6.3 箭头函数注意事项
// 箭头函数适合与this无关的回调,比如定时器,数组的方法回调。
// 箭头函数不适合与this有关的回调,比如DOM元素的事件回调、对象的方法。

btn.addEventListener("click",function(){
    //此时普通函数的this指向事件缘
    //如果使用箭头函数,事件源将变成外部作用域的this值,即这个函数所在的作用域
})


1.6.4 箭头函数中的this
// 箭头函数不会创建自己的this,它只会从自己的作用域链的上一层继承this

function Person() {
  this.age = 0;

  setInterval(function growUp() {
    this.age++;   // 定时器中使用普通函数,this永远指向window,因为是window调用定时器  所以this.age 值得是Nan 
  }, 1000);
}

var p = new Person();
// '------------------------------------'
function Person() {
  this.age = 0
  setInterval(()=> {   // 箭头函数没有自己this,只能往上一层去找
    console.log(this)  // Person对象
    this.age++;
    console.log(this.age)
  }, 1000);
}
var p = new Person();


1.6.5 笔试题
var myObject = {
  foo: "bar",
  func: function () {
    var self = this;  // self = myObject
    console.log(this.foo);  // 'bar'
    console.log(self.foo);  // 'bar'
    (function () {
      console.log(this.foo); // undefined  // 立即执行函数里面this永远指向window
      console.log(self.foo); // 'bar'
    }());
  }
};
myObject.func(); 

// ----------------------------------------------------------------------------

function plugin () {
    this.a = 1
    this.b = {
        x: 1,
        confirm: function(title, callback) {
            this.title = title
            console.log(this, '2')  
            callback()
        }
    }
    console.log(this, '1')
}
var obj = new plugin()
obj.b.confirm('弹窗', () => {
    console.log(this, '3')
})
1.6.6 箭头函数与普通函数的区别?
1,this 的指向问题
	// 箭头函数没有自己的作用域,所以没有自己的this,箭头函数中的this指向箭头函数当前函数声明时的作用域链的上一层
	// 普通函数有自己的作用域
2,call() 和apply()
	// 箭头函数里this的指向不受call,apply的指向,只跟定义位置作用域链的上一层有关,而普通函数可以
3,箭头函数不能当构造函数,而且没有Prototype属性,而普通函数可以
4,箭头函数没有arguments对象,普通函数有arguments对象,箭头函数中的参数可以用...参数名称来获取剩余参数
5,箭头函数不能作为generator函数,普通函数可以

1.6.7 箭头函数的this的指向?
// 箭头函数里的this指向沿着其作用域链往上找
代码的执行结果是什么?

1.7 函数参数初始值

function add(a,b,c=10){
}

//函数参数初始值与结构赋值结合使用
function data({name,age='22'}){
    console.log(name);
    console.log(age);
}

data({
    name='zzl',
    // age='23'
})
1.7.1 rest参数
// 用于获取函数的实参,可以代替arguments。

function data(){
    console.log(arguments); // 输出的是一个对象
}
data('zzl','wxl');

// rest参数
function data(...args){
    console.log(args); //输出的是一个数组,可以使用filter some...
}
data('zzl','wxl');


// --------------------------------------------------------------

// rest参数必须放在参数最后。

function data(a,b,...args){
    console.log(a);//1
    console.log(b);//2
    console.log(args);//   [3,4,5]
}
data(1,2,3,4,5)

// --------------------------------------------------------------------------------------

const obj1={
    q:'zzl'
}
const obj2={
    s:'wxl'
}
const data={...obj1,...obj2}; //相当于合并了两个对象
console.log(data);  //  {q:'zzl',s:'wxl'}

1.8 扩展运算符

// 它能将数组转换为逗号分隔的参数序列

const names=['zzl','wxl','ll'];
function data(){
    console.log(arguments);
}

//不用扩展运算符
data(names); // 只输出一个结果,是一个数组
// 用扩展运算符
data(...names); // 输出3个结果,等价于:data('zzl','wxl','ll'),即参数序列

// --------------------------------------------------------
    // 如果数组里面有引用类型的话,扩展呢运算符也只是浅拷贝。
    // 可以用来数组的合并
    // 数组的克隆
    // 伪数组转为真正的数组
    // 数组的合并

const a=['zzl'];
const b=['wxl'];
const c=[...a,...b];  //  c=['zzl','wxl']

//将伪数组转为真正的数组
const divs=document.querySelectorAll('div');  // 得到伪数组
const divdata=[...divs];  //  转为真正的数组

1.8 symbol

// 它一种新的数据类型,表示独一无二的值,类似于字符串的数据类型。

// 它的值是唯一的,用来解决命名冲突的问题。
// 它的值不能于其他数据进行运算。
// 它定义的对象属性不能使用for…in 循环遍历,但是可以使用Reflect.ownKeys来获取对象的所有键名。
 
// 创建symbol
let s = Symbol();

// 下面的两个symbol对象时不一样的
let s2 = Symbol('aini')
let s3 = Symbol('sini')

// symbol.for创建
let s4 = Symbol.for('aini')
let s5 = Symbol.for('aini')


let game={
    //假如有很多代码很多变量名
}

//声明一个对象
let data={
    //Symbol保证了up和down的属性名是独一无二的,
    // 所以添加进去也不怕也不怕有属性名冲突
    
    up:Symbol(),  
    //up属性的数据类型为Symbol
    
    down:Symbol()
};

//第一种添加方式
//把这个Symbol添加到game方法中
game[data.up]=function(){
    console.log('我会飞'); //安全的向这个对象中添加了两个方法
}
game[data.down]=function(){
    console.log('我会爬');
}
console.log(game);

//

//第二种添加方式
    let play={
        name='run',
        [Symbol('say')]:function(){
            console.log('我会说话');
        },
        [Symbol('sleep')]:function(){
            console.log('我会睡觉');
        }
    }
    console.log(paly);

//补充代码
    const title1 = Symbol("title")
    const title2 = Symbol("title")
    const info={
        [title1]:'zzl',
        [title2]:'wxl'
    }
Symbol有很多内置方法
1.8.1 Symbol的数据类型及特性?
// 数据类型的声明
let sym = Symbol()
// 作用:保证每一个对象的名字都是独一无二的

// 可以接受字符串作为参数,作位对Symbol实例的描述
let sym = Symbol('aini')
// symbol 的参数是一个对象,可以调用该对象的toString 方法将其转换为字符串,然后生成一个symbol值
// symbol函数的参数只是对当前Symbol 值的描述,因此相同参数的Symbol函数返回的值是不相等的
let s1 = SYmbol('s1')  s2 = Symbol('s1')  cosnole.log(s1 === s2)  // false

// symbol值不能与其他类型的值进行运算,否则会报错

//symbol 值可以显示转换为字符串和Boolean 但是不能转换为Number

// Object.getOwnPropertySymbol 方法可以获取指定对象的所有Symbol属性名

// symbol声明的属性是没办法通过Object.key() 拿到,所以可以实现私有属性
1.8.2 Symbol.iterator的作用?
// Symbol.iterator 为每一个对象定义了默认的迭代器。该迭代器可以被for ... of 循环使用

1.9 迭代器

任何数据结构只要部署了Iterator接口,就可以使用for…of 来遍历.

1.9.1 具备iterator接口的数据类型
// Array
// Argunments
// Set
// Map
// String
// TypedArray
// NodeList

// 这个接口就是对象里面的一个属性,属性的名字叫Symbol.iterator,也可以自己对结构进行布置iterator接口。

// -------------------------------------------------------------------------------------------

for( let i in data){
	i是键名
}
for( let i of data){
	i是键值
}
const arr=['zzl','wxl'];
console.log(arr);
//arr里面就有Symbol.iterator这个属性。
1.9.2 工作原理
// 先创建一个指针对象,指向当前数据结构的起始位置
// 第一次调用对象的next方法,指针自动指向数据结构的第一个成员
// 接下来不断调用next方法,指针一直往后移动,直到指向最后一个成员
// 每次调用next方法就会返回一个包含value和done属性(是否完成)的对象

let xiyou = ['唐曾','孙悟空','猪八戒','沙僧']

let iterator = xiyou[Symbol.iterator]();

// 调用对象的next方法
console.log(iterator.next())  // {value:'唐曾',done: false}
console.log(iterator.next()) // {value:'孙悟空',done: false}
console.log(iterator.next()) // {value:'猪八戒',done: false}
console.log(iterator.next()) // {value:'沙僧',done: false}
console.log(iterator.next()) // {value:undefined,done: true}
1.9.3 手写迭代器
//声明一个对象
const data={
    name:'zzl',
    lis:[
        'wxl',
        'll',
        'hll'
    ],
    
    //自己给某些结构加上iterator接口
    [Symbol.iterator](){
        //索引变量
        let index=0;
        let _this=this;
        return {
            //返回一个指针对象,即创建一个指针对象
            next:function(){
                //创建对象的next方法
                // 返回一个包含value和done属性(是否完成)的对象
                if(index < _this.lis.length){
                    const result =  { value:_this.lis[index], done:false};
                    index++;
                    return result;
                }else{                   
                    return {value: undefined, done:true};
                }
            }
        };
    }
}

//自定义遍历这个对象
for(let v of data){
    console.log(v);
}
console.log('--------------------')
console.log(data);
1.9.4 iterator接口的作用?
// Iterator 是一个接口,用于处理所有不同的数据结构遍历器。只要数据结构定义了Iterator接口时可以用for of 来循环遍历

// for of 循环的执行过程
    var arr = ['blue','green','yellow','balck','orange']
    let it = iterator(arr)
    console.log(it.next())
    console.log(it.next())
    console.log(it.next())
    console.log(it.next())

    function iterator(arr){
      let nextIndex = 0
      return {
        next: function() {
          return nextIndex < arr.length ? {
            value: arr[nextIndex ++] ,done: true
          } : {
            value : undefined,done : false
          }
        }
      }
    }

// Iterator 接口部署在数据结构的Symbol.iteratro属性
// 一个数据结构只要具有Symbol.Iterator属性,就可以认为是'可遍历的'
// Symbol.Iterator 属性是一个函数,每次运行会返回一个生成器对象包含俩属性value和done

// 原生具备Iterator接口的数据结构
// Array   Map   Set   String   函数的arguments对象

// 伪数组转换为真正数组的方法
1Array.prototype.slice.call(arguments)
2, Array.from(arguments)

// 

1.10 数组的常用方法

1.10.1 forEach()
// 第一个参数:数组元素;
// 第二个参数:数组索引;
// 第三个元素:数组对象本身

var numbers = [1,2,3,4,5,4,3,2,1];
numbers.forEach(function(item,index,array){
    console.log(item,index,array[index]);
});

// -----------------------------------------------------------
// 为每个元素加 1
var data = [1,2,3,4];
data.forEach(function(v,i,a){
	a[i] = v + 1; 
    //会改变原数组元素
});
console.log(data);

// 如果只关心数组元素的值,可以只使用一个参数,额外的参数将被忽略

// ------------------------------------------------------------------
// 求和
var data = [1,2,3,4];
var sum = 0;
data.forEach(function(value){ //第一个参数数组元素,相当于遍历多有元素;
    sum += value; 
    //不会改变原数组元素;
});
console.log(sum);

// -------------------------------------------------------------------

// (1)forEach()方法无法在所有元素都被传递给调用的函数之前终止遍历,即没有像 for 循环中使用的 break 语句;
// (2)如果要提前终止,必须把 forEach()方法放在 try 块中,并能抛出一个异常:
function foreach(a,f,t){
    try{
        a.forEach(f, t);
    }
    catch(e){
        if(e === foreach.break) return;
        else throw e;
    }
}
foreach.break = new Error("StopIteration");
1.10.2 every() ------------ some()
//(1)最相似的是 every()和 some(),它们都是数组的逻辑判定:用于判定数组元素是否满足某个条件;
//(2)对 every(),传入的函数必须对每一项都返回 true,这个方法才返回 true;
//(3)而 some()是只要传入的函数对数组中的某一项返回 true,就会返回 true,否则返回 false;

var numbers = [1,2,3,4,5,4,3,2,1];
var everyResult = numbers.every(function(item,index,array){
    return (item > 2);
});
console.log(everyResult); // false

var someResult = numbers.some(function(item,index,array){
	return (item > 2);
});
console.log(someResult); // true

// (4)一旦 every()和 some()确定该返回什么值时,它们就会停止遍历数组元素;
// (5)(some()在判定函数第一次返回 true 后就返回 true,但如果判定函数一直返回 false,它将会遍历整个数组;every()恰好相反,它在判定函数第一次返回 false 后就返回 false,但如果判定函数一直返回 true,它将会遍历整个数组)。
// (6)根据数学上的惯例,在空数组上调用时,every()返回 true,some()返回 false;
1.10.3 filter()
//(1)返回的是数组元素是调用的数组的一个子集;
//(2)回调函数是用来逻辑判定的,该函数返回 true 或 false;如果返回的是 true 或真值,则该函数处理的数组元素就被添加到返回的子集数组中;
//(3)filter()方法会跳过稀疏数组中缺少的元素,它的返回数组总是密集的

var numbers = [1,2,3,4,5,4,3,2,1];
var filterResult = numbers.filter(function(item,index,array){
	return item>2;
});
console.log(filterResult);

var evenResult = numbers.filter(function(value,index){
	return index % 2 == 0; // [1, 3, 5, 3, 1] index 是索引
});
console.log(evenResult);

// 压缩稀疏数组
var sparse = [1,,,4];
var dense = sparse.filter(function(){
	return true; //过滤稀疏元素
});
console.log(dense);

// 压缩并删除 undefined 和 null 元素
var sparse = [1,null,3,undefined,,6];
var dense = sparse.filter(function(v){
	return v !== undefined && v != null;
});
console.log(dense);

//(注:该方法返回的是符合条件的数组元素;)
1.10.4 map()
//(1)将调用的数组的每个元素传递给回调函数,并将调用的结果组成一个新数组返回;
//(2)其不会修改原始数组,如果是稀疏数组,返回的也是相同方式的稀疏数组,即具有相同的长度、相同的缺失元素;

var numbers = [1,2,3,4,5,4,3,2,1];
var mapResult = numbers.map(function(item,index,array){
	return item*2;
});
console.log(mapResult);

//-------------------------------------------------
var a = [1,null,3,undefined,5];
var b = a.map(function(v){
	return v * v;
});
console.log(b);
// --------------------------------------------

//不做任何处理
var spare = [1,,4,null,undefined,NaN,6];
var dense = spare.map(function(v,i,a){
});
console.log(dense);

// ----------------------------------------------------
// 返回所有数组元素
var spare = [1,,4,null,undefined,NaN,6];
var dense = spare.map(function(v,i,a){
	return v;
});
console.log(dense);

// -----------------------------------------------------------
//能处理的元素进项处理,空元素返回空元素;
var spare = [1,,4,null,undefined,NaN,6];
var dense = spare.map(function(v,i,a){
	return v*v; //进行数据类型转换,由于 null 和 undefined 不能进行数据类型转换});
console.log(dense) //,所以都返回 NaN
1.10.5 reduce() --------------------- reduceRight()
//(1)reduce()和 reduceRight();这两个方法都会迭代数组的所有项,然后构建一个最终返回的值;
//(2)其中,reduce()方法从数组的第一项开始,逐个遍历到最后;而 reduceRight 则从数组的最后一项开始,向前遍历到第一项;
//(3)这两个方法都接收两个参数:调用的函数 callbackfn 和作为归并基础的初始值initialValue(可选的);
//(4)这个函数接收 4 个参数:前一个值、当前值、项的索引和数组对象;其返回的任何值都会作为第一个参数自动传给下一项;第一次迭代发生在数组的第二项上,因此第一个参数是数组的第一项,第二个参数就是数组的第二项


// 求和
var values = [1,2,3,4];
var sum = values.reduce(function(prev,cur,index,array){
	return prev + cur;
});
console.log(sum); // 10
    // 第一次遍历:prev=1 cur=2;
    // 第二次遍历:prev=3 cur=3;
    // 第三次遍历:prev=6 cur=4;
    // 第四次遍历:prev=10
/*
    说明:第一次执行回调函数,prev 与 cur 分别为数组的第 1 和第 2 项,即 prev 是 1,cur 是 2;第二次,
    prev 是第一次执行的结果,即 3(1+2 的结果),cur 是数组的第三项,即是 3,以此类推;
    reduce()参数函数的参数也可以只使用两个参数,如:
*/

//-------------------------------------------------------------------------------
// 求和
var values = [1,2,3,4];
	var sum = values.reduce(function(prev,cur){
return prev + cur;
});
console.log(sum); // 10

// ----------------------------------------------------------------------------------
// 求积
var product = values.reduce(function(prev,cur){
	return prev * cur;
});
console.log(product); // 24

//------------------------------------------------------------------------------
// 求最大值
var max = values.reduce(function(prev,cur){
	return prev > cur ? prev : cur;
});
console.log(max); // 4
// reduce()方法的第二个参数 initialValue 是回调函数的第一个参数 previousValue 的初始值;如:
// 把以上所有示例添加第二个参数,如:
var values = [1,2,3,4];
var sum = values.reduce(function(prev,cur){
return prev + cur;
},2); // 12

/*
意思就是 pre 不是第一项,而是给 pre 赋一个值,cur 从第一项开始迭代说明,第一次调用时,prev 为 initialValue 值,即为 2,cur 为数组第一项,即为 1;第二次调用,prev 为 3,cur 为 2,以此类推;reduceRight()的作用类似,只不过方向相反,即按照数组索引从高到低(从右到左)处理数组;如:
*/
var values = [1,2,3,4,5];
var sum = values.reduceRight(function(prev,cur,index,array){
	return prev + cur; //21
},6); 
//pre 的初始值为 6;
console.log(sum); // 15


// ---------------------------------------------------------------------
// 求 2^(3^4),乘方操作的优先顺序是从右到左
var a = [2,3,4];
var big = a.reduceRight(function(accu, value){
	return Math.pow(value, accu);
});
console.log(big);

//在空数组上,不带初始值参数的 reduce()将导致类型异常;如果数组只有一个元素并且没有指定初始值,或者一个空数组并且指定了一个初始值,该方法只是简单的返回那个值而不会调用回调函数;

var a = [];
// var b1 = a.reduce(function(x,y){return x + y}); // no initial value 会报错
// var b2 = a.reduce(function(x,y){return x + x}); / /也会报错
var b3 = a.reduce(function(x,y){return x + y},3); //只返回初始值
console.log(b3); // 3

// (1)使用 reduce()还是 reduceRight(),主要取决于要从哪头开始遍历数组;除此之外,它们完全相同。
//(2)reduce()和 reduceRight()是典型的函数式编程;有些地方,会把其回调函数称为化简函数,这个化简函数的作用就是用某种方法把两个值组合或化简为一个值,并返回化简后的值;如以上的示例,化简函数通过各种方法,组合了两个值

1.11 生成器

// 生成器就是一个特殊的函数,是异步编程新的解决方案。分别通过以下代码可知运行逻辑。

function * fun(){
    console.log('zzl');
}
let a=fun();
// console.log(a);//输出一个迭代器对象(有next方法)
a.next(); // 输出zzl

// --------------------------------------------
function * fun(){
    console.log('zzl');
    console.log('wxl');
}
let a=fun();
console.log(a);
//输出一个迭代器对象(有next方法)
a.next();
//输出zzl wxl 即全部输出

// -------------------------------------------------------------
function * fun(){
    console.log('zzl');
    yield '我要暂停'
    console.log('wxl');
}
let a=fun();
console.log(a);
//输出一个迭代器对象(有next方法)
a.next();
//输出zzl 我要暂停

// -----------------------------------------------------------------------------
function * fun(){
    console.log('zzl');
    yield '我要暂停'
    console.log('wxl');
}
let a=fun();
// console.log(a);//输出一个迭代器对象(有next方法)
a.next();//输出zzl
a.next();//输出wxl
function * fun(){
    // console.log('zzl');
    yield '我要暂停'
    // console.log('wxl');
}
let a=fun();
console.log(a);
//输出一个迭代器对象(有next方法)
for(i of fun()){
    console.log(i);//我要暂停
}

// -----------------------------------------------------
function * fun(){
    // console.log('zzl');
    yield '我要暂停'
    // console.log('wxl');
}
let a=fun();
console.log(a.next()); //输出 {value: '我要暂停', done: false}
1.11.1 生成器函数的参数传递
function * fun(arg){
    console.log(arg); //输出aaa
    let one = yield 111;
    console.log(one); //输出bbb
    let two = yield 222;
    console.log(two); //输出ccc
    let three = yield 333;
    console.log(three); //输出ddd
}
let a=fun('aaa');
console.log(a.next());//第一次调用next
//next方法可以传入实参
//第二次调用next的实参将作为第一个yield的整体返回结果
console.log(a.next('bbb')) // 输出{value: 222, done: false}
console.log(a.next('ccc')) // 输出{value: 333, done: false}
console.log(a.next('ddd')) // 输出{value: undefined, done: true}
1.11.2 生成器函数实例
// 实例1:在控制台每隔一秒分别打印111,222,333
// 回调地狱

    setTimeout(() => {
        console.log(111);
        setTimeout(() => {
           console.log(222);
            setTimeout(() => {
               console.log(333);
           }, 3000);
       }, 2000);
     }, 1000);

// 解决方法
    function one(){
        setTimeout(()=>{
            console.log(111);
            a.next(); //定时器运行完调用下一个,实现了异步编程
        },1000)
    }
    function two(){
        setTimeout(()=>{
            console.log(222);
            a.next();
        },2000)
    }
    function three(){
        setTimeout(()=>{
            console.log(333);
            a.next();
        },3000)
    }

    function *fun(){
        yield one();
        yield two();
        yield three();
    }

//调用生成器函数
let a=fun();
a.next();
结果演示

// -------------------------------------------------------------------------------------------------
// 实例2 模拟获取用户数据,订单数据,商品数据
    function one(){
        setTimeout(()=>{
            let data='用户数据';
            a.next(data);//第二次调用next的实参将作为第一个yield的整体返回结果
        },1000)
    }
    function two(){
        setTimeout(()=>{
            let data='订单数据';
            a.next(data);
        },1000)
    }
    function three(){
        setTimeout(()=>{
            let data='商品数据';
            a.next(data);
        },1000)
    }
    function *fun(){
        let users= yield one(); 
        console.log(users); //用户数据 作为第一个yield的整体返回结果
        let orders= yield two();
        console.log(orders); // 订单数据
        let goods= yield three();
        console.log(goods); // 商品数据
    }
    //调用生成器函数
    let a=fun();
    a.next();

1.12 Promise

// 它是es6中异步编程的新的解决方案。相当于一个构造函数。

function fun(){
    return new Promise((resolve,reject)=>{
        //如果成功就调用resolve
        let data='数据库中的数据';
        resolve(data); 
        //promise状态变为成功
        
        //如果失败就调用reject
        let err='数据库读取失败';
        reject(err);//promise状态变为失败
    })
}

var promise = fun()
promise.then(
    //调用then方法
    function(success){
        // promise状态变为成功后then会调用第一个回调函数
        console.log(success);
    },function(err){
        // promise状态变为失败后then会调用第二个回调函数
        console.log(err);
    }
)
1.12.1 promise封装读取文件
cosnt fs = require('fs')

fs.readFile('./aini.txt',(err,data) => {
    // 如果失败,则抛出异常
    if(err) throw err;
    // 如果没哟出错,则输出内容
    console.log(data.toString())
})

// 使用Promise封装
cosnt p = new Promise(function(resolve,reject){
    fs.readFile('./aini.txt',(err,data) => {
        // 如果失败,则抛出异常
        if(err) reject(err)
        // 如果没哟出错,则输出内容
        resolve(data)
	})
})

p.then(function(value){
    console.log(value.toString())
    
},function(resson){
    console.log('读取失败!!!')
    
})
1.12.2 Promise 封装Ajax请求
const p = new Promise(function(resolve,reject){
    
    cosnt xhr = new XMLHttpRequest()

    xhr.open('GET','https://api.apiopen.top/getJoke')

    xhr.send()

    xhr.onreadystatechange = function(){
        if (xhr.randyState == 4){
            // 判断状态码
            if (xhr.status >= 200 && xhr.status <300){
                // 表示成功
                resolve(xhr.response)
            }else{
                reject(xhr.status)
            }
        }
    }
})

p.then(function(value){
    console.log(value.toString())
    
},function(resson){
    console.log(reason)  
})

1.12.3 Promise.property.then()
const p = new Promise((resolve,reject){
   setTimeout(() => {
    resolve('用户数据')
	}1000)                   
})

// 调用,then方法的结果也是promise对象,结果由回调函数的结果决定
// 1,如果回调函数返回的结果是非promise类型的数据,状态为成功,返回值为对象的成功值,如果不写return,默认返回undefined,也是成功的promise
    const p1 = p.then(value => {
       console.log(value) 
       return '11232'  // 成功的Promise
    }.err => {
       console.warn(reason)   
    })

// 2,如果返回的是promise对象,则这个promise对象的返回结果决定,then方法返回的结果
    const p1 = p.then(value => {
       console.log(value) 
       return new Promise(funtion(resolve,reject){
            resolve('ok')  // 成功,则then方法返回结果是成功
            reject('失败了')  // 失败,则then方法返回的结果是失败的
        )
    }.err => {
       console.warn(reason)   
    })

// 如果抛出错误,then 也是个失败的结果
    const p1 = p.then(value => {
       console.log(value) 
       throw new Error('出错啦') // then的状态为rejected
    }.err => {
       console.warn(reason)   
    })
    
    
// 链式调用
p.then(value=> {
    return new Promise(function(resoleve,reject){
        resolve()
    })
},reason => {
   
}).then(value=> {
         return new Promise(function(resoleve,reject){
        resolve()
    })
},reason => {
    
}).then(value=> {
         return new Promise(function(resoleve,reject){
        resolve()
    })
},reason => {
    
})
1.12.4 Promsie 多个文件内容读取
// 回调地狱
cosnt fs = require('fs')

fs.readFile('./aini.txt',(err,data1) => {
    fs.readFile('./norah.txt',(err,data2) => {
    	 fs.readFile('./dilnur.txt',(err,data3) => {
    		let reault = data1 + data2 + data3
		})
	})
})

// 使用promise实现
const p = new Promise(function(resolve,reject){
    fs.readFile('./aini/txt',(err,data) => {
        resolve(data)
    })
})

p.then(value => {
    return new Promise((resolve,reject) => {
       fs.readFile('./norah.txt',(err,data) => {
    		resolve([value,data])
		})  
    }) 
}).then(value => {
      return new Promise((resolve,reject) => {
       fs.readFile('./dilnur.txt',(err,data) => {
           value.push(data)
    	   resolve(data)
		})  
    }) 
}).then(value => {
    console.log(value.join('\r\n'))
})
1.12.5 Promise对象的catch()方法
const p = new Promise((reslove,reject)=>{
    setTimeout(() => {
      reject('出错了')  
    },1000)
})


// 第一种
    p.then(function(value){
       // 成功的结果 
    },function(reason){
        // 失败的结果
    })

 // 第二种
    p.then(function(value){
        // 成功的结果
    }).catch(function(reason){
        //失败的结果
    })

// 第三种
    p.then(function(value){
        // 成功的结果
    })

    p.catch(function(reason){
        //失败的结果
    })

1.13 Set

Set的数据类型及特性?
// set 是ES6新增的复杂数据类型,类似于数组,但是值没有重复分
// 新建
	const s = new Set()
// 可以接受数组或字符串当参数
    const s = new Set([1,2,3,4,2,3])
    const s1 = new Set('abcdeftg')
// set 可以转化成数组,通过...扩展运算符或者Array.from()方法
    let arr = [...s]     // 等同于  Array.from(s)
    let arr1 = [...s1]   // 等同于  Array.from(s1)
// set 用于数组去重
    
// set 里面的判断机制是 === 运算符 (NaN 例外)
let set = new Set()
set.add(5) 
set.add('5')  // 5 '5' 是两个元素

// set 实例的属性和方法
1,size() 返回长度
2, has() 判断是否包含某个元素
2,forEach  方法
    let set = new Set([1,3,2,3,54,3,2])
    console.log(set)
    set.forEach((item,index) => console.log(item + '-----------'  + index))
	// item 和 key 值一样
// 并集
let arr = [1,2,3,4]
let arr1 = [2,3,4,5,6]
let a = new Set(arr)
let b = new Set(arr1)
let set = new Set([...a,...b])

// 交集
let arr = [1,2,3,4]
let arr1 = [2,3,4,5,6]
let a = new Set(arr)
let b = new Set(arr1)

let intersect = new Set([..a].filter(x => b.has(a)))

// 差集
let arr = [1,2,3,4]
let arr1 = [2,3,4,5,6]
let a = new Set(arr)
let b = new Set(arr1)

let chaji = new Set([..a].filter(x => !b.has(a)))

1.14 Map

Map的数据类型及特性?
// Map 是ES6新增的复杂数据类型,类似于对象,也是键值对集合,但是键的范围不限于字符串,各种类型的值都可以当做键
// Map 是值与值的对应,是更完美的键值对结构的提现

// 新建Map数据的方法 
1const m = new Map()

// Map 的方法
1, set()  m.set({x: 1}, 22)
2, get    m.get({x: 1})
3, has() 判断某个键是否存在  m.has({x:1})
4, delete() 删除某个键值对

// 判断是不是Map类型的数据结构
let map = new Map() 
1, map instanceof Map     //  true 通过原型链查找
2, Object.prototype.toString.call(map) // [object, Map]

// 为什么 Object.prototype.toString.call() 用的是原型链顶端的toString方法呢?
// 因为数组和对象对toString方法都进行了改写

// 可以接受数组作为参数
// 注意的是:数组成员是一个个表示键值对的数组
	let map = new Map(
      [
        ['name','aini'],
        ['age',22],
        [{name:'norah'},'这是一个对象']
      ]
    )
      // 一个坑
    map.get({name:'norah'})   // undefined  是因为 {} 与 {} 不是同一个对象

// 解决办法:
let obj = {name:'norah'}
let map = new Map(
      [
        ['name','aini'],
        ['age',22],
        [obj,'这是一个对象']
      ]
    )
map.get(obj)  // 这样才能获取正确的key值


// map参数的扩展
// 任何具有 Iterator 接口,且每个成员都是一个双元素的数组的数据结构,都可以当做map函数的参数

1.15 Class类

// 以手机类为例
function Phone(brand,price){
    this.brand = brand
    this.price = price
}
// 静态成员
//相当于 python里的staticmethod
Phone.name = '手机'
Phone.change = function(){
    console.log('我可以改变世界')
}

// 相当于Python里的classmethod
Phone.ptototype.size = "5.5英寸"
Phone.prototy.call = function() {
    console.log('我可以打电话')
}

ler Huawei = new Phone('华为',5999)

// ES6语法
class Phone{
    // 构造函数方法
    constructor(brand,price){
        this.brand = brand
        this.call = call
    }
    
    // 语法必须使用这个方法,不能用ES5老语法
    call() {
        console.log('我可以打电话')
    }
}

ler Huawei = new Phone('华为',5999)
1.15.1 构造函数继承
function Phone(brand,price){
    this.brand = brand
    this.price = price
}

Phone.prototype.call = function(){
    console.log('我可以打电话')
}

//智能手机
function SmartPhone(brand,price,color,size){
    Phone.call(this,brand,price){
        this.color = color
        this.size = size
    }
}

// 设置子级构造函数的原型
SmartPhone.prototype = new Phone
SmartPhone.prototype.constructor = SmartPhone

// 声明子类的方法
SmartPhone.prototype.photo = function(){
    console.log('我可以拍照')
}

SmartPhone.prototype.playGame = function() {
    console.log('我可以打游戏')
}

const chuzi = new SmartPhone("锤子",2499,'黑色','505')
1.15.2 ES6里类的继承
class Phone{
    constructor(brand,price){
        this.brand = brand
        this.price = price
    }
    
    call(){
        console.log('我可以打电话')
    }
}

class SmartPhone extends Phone{
    constructor(brand,price,color,size){
        super(brand,price)
        this.color = color
        this.size = size
    }
    photo() {
        console.log('我可以拍照')
    }
    playGame() {
        console.log('我可以打游戏')
    }
}

const xioami = new SmartPhone('xiaomi',2499,'黑色','505')
1.15.3 子类对父类方法的重写
class Phone{
    constructor(brand,price){
        this.brand = brand
        this.price = price
    }
    
    call(){
        console.log('我可以打电话')
    }
}

class SmartPhone extends Phone{
    constructor(brand,price,color,size){
        super(brand,price)
        this.color = color
        this.size = size
    }
    photo() {
        console.log('我可以拍照')
    }
    playGame() {
        console.log('我可以打游戏')
    }
    
    // 可以对父类方法重写
    call(){
        console.log('我可以进行视频通话')
    }
}

const xioami = new SmartPhone('xiaomi',2499,'黑色','505')
1.15.4 class中的getter和setter
class Phone{
    get price() {
        console.log('我被读取了')
        return 'i love you '
    }
    
    set price(val){
        console.log('价格修改了')
        this.val = val
    }
}

let s = new Phone()
console.log(s.price) 
// '我被读取了'
// 'i love you ' -------------------> 这才是s.price的返回值

s.price = 'free' // '价格修改了'

1.16 数值扩展

// Number.EPSILON  -------------------------- 是Javascript表示的最下精度
// Number.isFinite -------------------------- 检测一个数是否为有限数
// Number.isNaN -----------------------------检测一个数值是否为 NaN
// Number.parseInt  Number.parseFloat-------- 字符串转整数
// Number.isInteger ---------------------------判断一个数是否为整数
// Math.trunc ----------------------------------将数字的小数部分抹掉
// Math.sign -------------------------------------判断一个数到底为正数,负数还是0 -----------> 1  -1  0


// 进制数的表示方法
let b = 0b1010
let o = 0o777
let d = 100
let x = 0xfff

1.17 对象方法扩展

// Object.is --------------------------------> 判断两个值是否完全相等
	console.log(Object.is(NaN,NaN)) // true
	console.log(NaN === NaN)  // false
// Object.assign ----------------------------> 对象的合并
// Object.setPropertyOf ----------------------> 设置Object原型对象
// Object.getPropertyOf-----------------------> 获取对象的属性

1.18 模块化

// 模块化是指将一个大的文件,拆分成许多小的文件,然后将小文件组合起来,一个个小文件就是一个模块。

    // 防止命名冲突
    // 代码复用
    // 高维护性
1.18.1 ES Module
//第一种暴露方法:分别暴露
export let a='zzl'; //向外暴露
export function fun(){
    console.log('我是谁');//向外暴露
}

//第二种暴露方法:统一暴露[ 注意{}导出的并不是对象 ]
let a='zzl';
export{a,fun};

//第三种:默认暴露
export default{
    a:'zzl',
    fun:function(){
        console.log('我是谁');
    }
}
1.18.2 html代码引入js模块
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <script type="module">
        //第一种引入方式:引入mi.js暴露的模块
        import * as m from './模块化/m1.js';
        console.log(m.a); //zzl

        //第二种引入方式解构赋值形式:引入mi.js暴露的模块
        import {a as a1,fun} from './模块化/m1.js';
        console.log(a1);//可以给a一个别名a1 输出zzl
        //默认暴露的引入比较特殊,引入语法是

        import * as m from './模块化/m1.js';
        m.default.fun();
        //默认暴露的解构赋值

        import{default as mm} from './模块化/m1.js';
        console.log(mm);
        //简便形式 针对默认暴露

        import m from './模块化/m1.js';
        //动态调用模块,即使用时再导入

        import('./模块化/m1.js').then(module =>{
             module.fun();
    })
    </script>
</body>
</html>
1.18.3 module.exports 与 exports的使用与区别
// 二者都是专门负责导出的东西,都属于commonJS规范,是同步加载的.

// exports
    a.js
        const a=10

        //导出 (实际上exports就是一个对象)
        exports._a = a

    b.js

        // 引入a.js导出的对象,实际就是exports对象
        const ba =require('./a.js') // === exports对象
        console.log('输出',ba._a) //输出 10

// exports
	a.js
		const a=10
        //导出 (实际上exports就是一个对象)
        // exports._a=a
        /**
         * 实际上module也是一个对象
         * 下边三行代码具体逻辑如下:
         * 1、module.exports = exports 即4行的exports,
         *      此时是引用赋值,并且可以继续使用原来b.js的导入方式
         * 2、重新给module.exports一个新对象地址{_a:a}
         * 3、b.js导入方式不变,因为只是对象地址,该对象module.exports依然是导出对象
         */
        module.exports = { 
            _a:a
        }
	b.js
		// 引入a.js导出的对象,实际就是module.exports对象
        const ba=require('./a.js') // === module.exports对象

        console.log('输出',ba._a) //输出 10
1.18.4 require()与import()
if (true){
   import * from './a.js'
}

/*
以上代码是错误的,因为imprt关键字不支持在这类代码里,但是require支持,因为require本质是函数,但是,require不支持在浏览器里运行,并且它是同步执行的,所以ESModule里可以用import函数来引入外部模块,并且它是异步执行的.
*/

if(true){
    import('./a.js').then(res=>{
        console.log(res._a)
    }).catch(err=>{
        console.log(err)
    })
}
// <script src type=”module”> 也是异步加载执行的,相当于加了async属性

1.19 Babel

// 它能将js的新语法转化为旧语法,以便更好的兼容。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
    <body>
        // 安装工具 babel-cli   babel-preset-env  browserify(打包工具)
        	// 初始化  npm init -yes
        	// 安装  npm i babel-cli babel-preset-env browserify -D
        	// 因为局部安装,用 npx 执行命令
        		// npx babel aini.js(源文件目录) -d dist/js(打包的目标目录) --presers=babel-preset-env
        		// 打包 npx browserify dist/js/app.js -o dist/bundle.js
    </body>
</html>

2,ES7

2.1 新特性

Array.prototype.includes // 检测数组中是否包含某个元素
    const name = ['aini','norah','dilnur']
    console.log(name.includes('aini')) // true
** // 幂次方
     console.log(2 ** 10) // 1024   Math.pow(2,10) 一样的效果

3,ES8

3.1 async函数

// async 函数的返回值为promise对象
// promise对象的结果由 async函数执行的返回值决定

    async function fn() {
      return "aini"
      
      // 如果跑错误,则返回的是失败的promise对象
      // 如果返回的是一个promise对象,结果取决于返回的promise对象的状态
    }

    const result = fn();
    console.log(result)   // promise对象

3-2 await 表达式

// await 必须写在async函数中
// await 右侧的表达式一般为promise对象
// await 返回的是promise成功的值
// await 的promise失败了,就会抛出异常,需要通过try...catch捕获处理

    const p = new Promise((resolve,reject) => {
      resolve('用户数据')
    })

    async function fn() {
      const result = await p
      console.log(result)  // '用户数据'
    }

    fn()

// --------------------------------------------------------------------------------------

async function fn() {
    try{
      	const result = await p
      	console.log(result)  // '用户数据'
    }catch(e){
        console.log(e)
    	}
 		
    }
fn()

3-3 async和await结合

const fs = require("fs");

function readWeiXue() {
  return new Promise((resolve,reject) => {
    fs.readFile('/为学.md',(err,data)=>{
        if(err){
          reject(err)
        }else{
          resolve(data)
        }
      })
  })  
}

function readChaYangShi() {
  return new Promise((resolve,reject) => {
    fs.readFile('/插秧诗.md',(err,data)=>{
        if(err){
          reject(err)
        }else{
          resolve(data)
        }
      })
  })  
}

function readGuanShuYouGan() {
  return new Promise((resolve,reject) => {
    fs.readFile('/观书有感.md',(err,data)=>{
        if(err){
          reject(err)
        }else{
          resolve(data)
        }
      })
  })  
}

async function main() {
  let weixue = await readWeiXue()
  let chayang = await readChaYangShi() 
  let guanshu = await readGuanShuYouGan() 
  console.log(weixue.toString())
  console.log(chayang.toString())
  console.log(guanxue.toString())
}

3-4 对象方法的扩展

Object.values()  // 返回一个给定对象所有可枚举属性值的数组
Object.entries() // 返回一个给定对象自身可遍历属性的数组
Object.getOwnPropertyDescriptors() // 返回指定对象自身属性的描述对象

4,ES9

4.1 扩展运算符和rest参数

function connect({host, port, ...user}){
  console.log(host)  // 127.0.0.1
  console.log(port)  // 3306 
  console.log(user)  // {username: 'aini', password: 'aini'}
}

connect({
  host:'127.0.0.1',
  port:3306,
  username:'aini',
  password:'aini'
})

const a = {
  q: 'aini'
}
const b = {
  w: 'norah'
}
const c = {
  e: 'dilnur'
}
const d = {
  r: 'zhuozi'
}

// 对象的合并
const name = {...a, ...b, ...c, ...d}
console.log(name) //  {q: 'aini', w: 'norah', e: 'dilnur', r: 'zhuozi'}
       
;