ES6学习

ECMAScript 6(以下简称ES6)是JavaScript语言的下一代标准。

因为当前版本的ES6是在2015年发布的,所以又称ECMAScript 2015(简称ES2015)。虽然浏览器在不断更新,但并不是所有用户的电脑浏览器都支持ES6,所以在使用的过程中建议还是转成es5,保证代码的可执行性。至于转换的方式大家可以用Babel或者Traceur转码器。

一、let和const

在ES6以前,Javascript并没有块级作用域的概念,有的是全局作用域和函数作用域,而let的出现就是为了打破局面,let是块级作用域。const是代表常量,必须在定义的时候初始化,不可改变。

  {
      var a=5;
      let b=10;
  }
  console.log(a);   // 5
  console.log(b);   // b is not defined

上面代码,var声明的变量由于不存在块级作用域所以可以在全局环境中调用,而let声明的变量由于存在块级作用域所以不能在全局环境中调用。

再来看一个经典例子(闭包):

var a=[];
//执行for循环
for(var i=0;i<10;i++){
      a[i]=function(){ //因为这个是定义,并没有调用方法,不会执行
           console.log(i);
      };
}
//for循环之后,此时 i = 10;再次执行a[6]();因为 i 一直被引用,所以不会回收,进入到 a[i] 的方法里面, 打印的是 i ,也就是10
a[6](); //输出10

下面用let修改上面代码:

var a=[];
  for(let i=0;i<10;i++){
        a[i]=function(){
            console.log(i);
        };
    }
    a[6]();    //打印6

a[6]函数(闭包)这个执行环境中,它会首先寻找该执行环境中是否存在 i,没有找到,因为 i 是块级作用域,就沿着作用域链继续向上到了其所在的代码块执行环境,找到了i=6,于是输出了6,即a6;的结果为6。这时,闭包被调用,所以整个代码块中的变量i和函数a6被销毁。
const 是定义常量:const a = 14; 此后变量 a 的值无法更改覆盖。

二、解构赋值

ES6 允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构。
以前,为变量赋值,只能直接指定值。

var a = 1;
var b = 2;
var c = 3;

ES6允许写成下面形式,面代码表示,可以从数组中提取值,按照对应位置,对变量赋值。

var [a,b,c] = [1,2,3];
console.log(a,b,c);  // 1 2 3

如果解构不成功,变量的值就等于undefined。

var [a,b] = [1];
console.log(a,b);   // 1  undefined

解构赋值还有嵌套比较复杂的写法,如下:

let [foo,[[bar],[baz]]] = [111,[[222],[333]]];
console.log(foo,bar,baz); // 111 222 333
 
let [head,...foot] = [1,2,3,4];
console.log(head,foot); // 1 [2,3,4]

另一种情况是不完全解构,即等号左边的模式,只匹配一部分的等号右边的数组。这种情况下,解构依然可以成功

let [x,y] = [10000,20000,30000];
console.log(x,y); // 10000 20000

默认值可以引用解构赋值的其他变量,但该变量必须已经声明

let [a=1,b=a] = [2,3];
console.log(a,b); // 2 3

对象的解构也可以指定默认值

var {x,y=5} = {x:1};
console.log(x,y); // 1 5

对象的解构赋值解构不仅可以用于数组,还可以用于对象(json)
对象的解构与数组有一个重要的不同。数组的元素是按次序排列的,变量的取值由它的位置决定;
而对象的属性没有次序,变量必须与属性同名,才能取到正确的值

var {a,b} = {a:'apple',b:'banana'};
console.log(a,b); // apple banana
var {b,a} = {a:'apple',b:'banana'};
console.log(a,b); // apple banana

如果变量名与属性名不一致,必须写成下面这样

let obj = {first:'hello',last:'world'};
let {first:f,last} = obj;  // first ---> f,那么此时f就是first,而不是undefined了,有点类似别名的概念
console.log(f,last); // hello world

对象的解构赋值的内部机制,是先找到同名属性,然后再赋给对应的变量。 真正被赋值的是后者,而不是前者
v是匹配的模式,n才是变量。真正被赋值的是变量n,而不是模式v。
注意,采用这种写法时,变量的声明和赋值是一体的

var {v:n} = {v:'vue',r:'react'};    // v ---> n,那么此时n就是vue,而不是undefined了
console.log(n); // vue
console.log(v); // Uncaught ReferenceError: v is not defined
console.log(r); // Uncaught ReferenceError: r is not defined

解构赋值总结:
1、左右两边结构必须一样
2、右边必须是个东西
3、声明和赋值不能分开【必须在一句话里完成】

三、函数

1、函数参数扩展:收集参数 …args

function show(a,b,...args){    // ...args必须是最后一个参数
   console.log(a,b);   // 1,2
   console.log(args);   // [3,4,5]
}
show(1,2,3,4,5)

上面代码实参传入5个,形参a,b对应的是1,2;剩下多余的实参3,4,5会被…args收集在一个数组里。

2、箭头函数
原先书写函数格式可以写成如下方式:

function fn(){
    console.log("我是ES5的函数书写格式")
}
fn();  // 我是ES5的函数书写格式

上面的写法等价于下面的写法
在ES6中可以简写成下面格式:箭头函数的写法 function(){ } 变成 ()=>{ }

var fn = ()=>{
    console.log(“我是es6的箭头函数书写方式”)
}
fn();   // 我是es6的箭头函数书写方式

箭头函数表达式的语法比函数表达式更短,并且不绑定自己的this,arguments,super或 new.target。这些函数表达式最适合用于非方法函数,并且它们不能用作构造函数,不能使用new。

如果函数只有一条语句可以写成下面这样子:

var fun = ()=>Math.random()*10
console.log(fun());

如果参数只有一个可以把()省略掉,但是没有参数或者有两个以上参数必须要加小括号:

// 一个参数的情况
var fun = a => console.log(a)
fun(1);  // 1 只有一个参数a可以把括号省略,并且只有一个返回值可以把return和{}省略

// 多个参数的情况
var fun = (a,b) => console.log(a+b);
fun(1,2);   // 3

箭头函数内部arguments对象,取而代之的是使用rest参数…来代替:

// 原先函数
funtion fun(a){
    console.log(arguments);   // 1  Arguments(4) [1, 2, 3, 4, callee: ƒ, Symbol(Symbol.iterator): ƒ]
}
a(1,2,3,4)

// 使用箭头函数
var fun = (a)=>{
   console.log(arguments);   // 报错  arguments is not defined
}
fun(1,2,3,4)

// 箭头函数的arguments对象使用 ...扩展来代替
var fun = (...a)=>{
    console.log(c);   //  [1, 2, 3, 4]
}
fun(1,2,3,4)

箭头函数不能使用new()来调用

var fun = (a)=>{
    console.log(a)
}
var fns = new fun(1);
console.log(fns());  // fun is not a constructor

普通函数的this是谁调用就指向谁,可以改变this指向;
箭头函数中的this指向创建时指向谁就是谁,不能改变this的指向。

var obj = {
  a: 10,
  b: function(){
    console.log(this.a); //输出10
  },
  c: function() {
     return ()=>{
           console.log(this.a); //输出10,捕获了上面obj的this作为自己的this
     }
  }
}
obj.b(); 
obj.c()();

所谓箭头函数的 this 捕获的是所在的上下文,比如下面这个例子:b是一个箭头函数,然后它的 this是指向window,这是为什么呢,因为箭头函数捕获的是obj{}这个对象的环境,然后这个环境的this指向的是window,就相当于上一条的例子:在c方法里面return的那个箭头函数捕获的是c:function(){}这个环境的this,而这个环境的this是obj

var obj = {
  a: 10,
  b: () => {
    console.log(this.a); //undefined
    console.log(this); //window
  },
  c: function() {
    console.log(this.a); //10
    console.log(this); //obj{...}
  }
}
obj.b(); 
obj.c();

对于函数的this指向问题:
箭头函数的this永远指向其上下文的 this,任何方法都改变不了其指向,如call(), bind(), apply();
普通函数的this指向调用它的那个对象。

四、ES6数组

1、数组展开 …arr扩展运算符
var arr = [1,2,3] === …arr展开参数;

var arr = [1,2,3];
console.log(arr);   // [1,2,3]
console.log(...arr);  // 展开数组   1,2,3

2、map():方法 ‘映射’,一个对一个,有return返回值

var arr = [1,2,3];
var result = arr.map((item,index,itself) =>{   // 参数:item数组每一项,index数组下标,itself数组本身
     return item * 2;    // 数组的每一项 * 2
})
console.log(result);   // 2,4,6

3、reduce:汇总 一堆出来一个 应用:购物车总价

// 例子1:求和
var arr = [1,2,3];
let result = arr.reduce((tmp,item,index) => {   // 参数:最终结果,数组每一页,索引(从1开始)
    return tmp + item
})
console.log(result);   // 6

// 例子2:求平均数
let arr = [1,2,3];
let b = arr.reduce((tmp, item, index) => {
        if (index != arr.length - 1) {
            return tmp + item;
        } else {
            return (tmp + item) / arr.length
        }
    })
    console.log(b)

4、filter:过滤器 留一部分过滤一部分
示例:找出可以能被3整除的数子

let arr = [12,34,23,45,32,122,99];
let result = arr.filter(item => item%3 == 0 ? true : false )
console.log(result);  // [12, 45, 99]

5、forEach():循环 没有return 返回值

6、Array.of创建数组新方法:用于将一组值,转换为数组。

Array.of(3, 11, 8) // [3,11,8]
Array.of(3) // [3]
Array.of(3).length // 1

这个方法的主要目的,是弥补数组构造函数Array()的不足。因为参数个数的不同,会导致Array()的行为有差异。

Array() // []
Array(3) // [, , ,]
Array(3, 11, 8) // [3, 11, 8]

上面代码中,Array方法没有参数、一个参数、三个参数时,返回结果都不一样。只有当参数个数不少于 2 个时,Array()才会返回由参数组成的新数组。参数个数只有一个时,实际上是指定数组的长度。

Array.of基本上可以用来替代Array()或new Array(),并且不存在由于参数不同而导致的重载。它的行为非常统一。

Array.of() // []
Array.of(undefined) // [undefined]
Array.of(1) // [1]
Array.of(1, 2) // [1, 2]

Array.of总是返回参数值组成的数组。如果没有参数,就返回一个空数组。

Array.of方法可以用下面的代码模拟实现。

function ArrayOf(){
  return [].slice.call(arguments);
}

7、isArray 判断是不是数组

8、Array.from(): 将类数组转换成数组

var arrayLike = {
    '0': 'a',
    '1': 'b',
    '2': 'c',
    length: 3
}
// ES5方法:
var arr1 = [].slice.call(arrayLike);
console.log(arr1);    // ["a", "b", "c"]

// ES6方法:
var arr2 = Array.from(arrayLike);
console.log(arr2);   // ["a", "b", "c"]

9、find()和findIndex()
find():
数组实例的find方法,用于找出第一个符合条件的数组成员。它的参数是一个回调函数,所有数组成员依次执行该回调函数,直到找出第一个返回值为true的成员,然后返回该成员。如果没有符合条件的成员,则返回undefined。

var arr = [1, 4, -5, 10]
arr.find((n) => n < 0)  // -5

 [1, 5, 10, 15].find(function(value, index, arr) {   // 参数:当前的值、当前的位置和原数组。
     return value > 9;
}) // 10

findIndex():
数组实例的findIndex()方法返回第一个符合条件的数组成员的位置,如果所有成员都不符合条件,返回undefined

var arr = [1, 5, 10, 15];
arr.findIndex((value, index, arr) => {
   return value > 9;
}) // 2   第一个大于9的数字在数组中是10,它的下标在数组中的位置是 2

这两个参数都可以接收第二个参数,用来绑定回调函数的this对象

var function f(v){
   return v > this.age
}
let person = {name:"Blue",age:"20"};
let arr = [10,12,26,15];
arr.find(f,person);   // 26

另外,这两个方法都可以发现NaN,弥补了数组的indexOf方法的不足。

[NaN].indexOf(NaN)  // -1
[NaN].findIndex(y => Object.is(NaN, y))// 0

上面代码中,indexOf方法无法识别数组的NaN成员,但是findIndex方法可以借助Object.is方法做到。

数组的浅拷贝,引用之间的拷贝,没有实现数组的真正复制

var arr1 = [1, 2, 3];
var arr2 = arr1;
arr2.push(4);
console.log(arr1, arr2); //[1, 2, 3, 4] [1, 2, 3, 4]

数组深拷贝,传统做法
var arr1 = [1,2,3];
var arr2 = [];

//通过for循环遍历之后将arr1数组的每一项赋值给arr2数组的每一项, 就实现了数组的深拷贝,这时候我再去操作arr2的数组的时候,arr1就不会受影响了
for(var i=0;i

ES6实现的数组的深拷贝方法1

var arr1 = [1,2,3];
var arr2 = Array.from(arr1);
// 数组尾部添加
arr2.push(100);
console.log(arr1,arr2); // [1, 2, 3] [1, 2, 3, 100]

ES6实现的数组的深拷贝方法2

var arr1 = [1,2,3];
// 超引用拷贝数组
var arr2 = [...arr1];
// 数组尾部添加
arr2.push(1000);
console.log(arr1,arr2); // [1, 2, 3] [1, 2, 3, 1000]

    function show(...args){
	// 此时这个形式参数就是一个数组,我们可以直接push东西进来,如下
	args.push(5);
	console.log(args);
	}
	// 调用
	show(1,2,3,4); // [1, 2, 3, 4, 5]

10、ES6数组中一些小技巧

// ES5去重方法1:
// 最简单数组去重法
/*
* 新建一新数组,遍历传入数组,值不在新数组就push进该新数组中
* IE8以下不支持数组的indexOf方法
* */
function uniq(array){
    var temp = []; //一个新的临时数组
    for(var i = 0; i < array.length; i++){
        if(temp.indexOf(array[i]) == -1){
            temp.push(array[i]);
        }
    }
    return temp;
}
var aa = [1,2,2,4,9,6,7,5,2,3,5,6,5];
console.log(uniq(aa));

// ES5去重方法2:
// 思路:获取没重复的最右一值放入新数组
/*
* 推荐的方法
*
* 方法的实现代码相当酷炫,
* 实现思路:获取没重复的最右一值放入新数组。
* (检测到有重复值时终止当前循环同时进入顶层循环的下一轮判断)*/
function uniq(array){
    var temp = [];
    var index = [];
    var l = array.length;
    for(var i = 0; i < l; i++) {
        for(var j = i + 1; j < l; j++){
            if (array[i] === array[j]){
                i++;
                j = i;
            }
        }
        temp.push(array[i]);
        index.push(i);
    }
    console.log(index);
    return temp;
}
var aa = [1,2,2,3,5,3,6,5];
console.log(uniq(aa));


**************************************************************************

// ES6数组去重
var arr = [1,23,2,3,43,2];
// 数组去重1:通过ES6新增的数据结构  Set()去重,结果是Set数据结构
var c = new Set(arr);
conosle.log(c);   // 结果是set结构  Set(5) {1, 23, 2, 3, 43}

// 数组去重2:使用ES6扩展运算符和Set结构去重,结果是数组格式结构
var c = [...new Set(arr)];   // 结果是数组结构  [1, 23, 2, 3, 43]

// 最大值
var c = Math.max.apply(Math,arr);   // 43  ES5查找最大值方法
var c = Math.max(...arr);   // 43  ES6查找 最大值方法

五、字符串

1、多了三个新方法
includes(),starsWith(),endsWith(); 这三个方法都支持第二个参数,表示开始搜索的位置。
使用第二个参数n时,endsWith的行为与其他两个方法有所不同。它针对前n个字符,而其他两个方法针对从第n个位置直到字符串结束。

            console.log(str.startsWith("J"));  返回布尔值,表示参数字符是否在原字符串头部
            console.log(str.endsWith("t"));    返回布尔值,表示参数字符是否在原字符串尾部
            console.log(str.includes('S'));    返回布尔值,表示是否找到了参数字符串

2、字符串模版 ``

3、repeat()几次 字符串重复

六、for…of遍历对象

var obj = {
    a:1,
    b:2,
}
console.log(Object.keys(obj));  // ["a","b"]
console.log(Object.values(obj));// [1,2]
console.log(Object.entries(obj));  // [Array(2), Array(2), Array(2)]
for(var [key,val] of Object.entries(obj)){   // 解构遍历
    console.log(key,val);   // a 1   b 2
}

//for of一个arr对象
var arr = ['红楼梦','西游记','三国演义','水浒传','火影'];
//只循环key  0 1 2 3 4 输出key值,也就是下标索引
for(var key of arr.keys()){
console.log(key);
}
//只循环value,注意数组是没有.values() 直接 var value of arr ,输出 红楼梦,西游记,三国演义,水浒传,火影
for(var value of arr){
console.log(value);
}
//循环key,value
for(var [key,value] of arr.entries()){
console.log(key,value);
}

//for in循环与for of循环的区别
var arr = ['apple','banana','orange','pear'];
for(var i in arr){
// i打印出来的就是arr数组对应的索引
// 0 1 2 3
console.log(i);
}
for(var i of arr){
// i值打印出来的就是我们想要的数组具体的值
// apple banana orange pear
console.log(i);
}

//for of不能循环json
var json = {'a':'apple','b':'banana','c':'orange','d':'pear'};
for(var name in json){
// a b c d
console.log(name);
// apple
console.log(json.a);
// pear
console.log(json['d']);
}
// 注意for..of可以循环arr,但是不可以循环json,会报错,特别注意下
for(var name of json){
Uncaught TypeError: undefined is not a function
console.log(json);
}

一个数据结构只要部署了Symbol.iterator属性,就被视为具有 iterator 接口,就可以用for…of循环遍历它的成员。也就是说,for…of循环内部调用的是数据结构的Symbol.iterator方法。

for…of循环可以使用的范围包括数组、Set 和 Map 结构、某些类似数组的对象(比如arguments对象、DOM NodeList 对象)、后文的 Generator 对象,以及字符串。

七、JSON对象

1、JSON对象
JSON.stringify:将一个JSON对象转换成字符串
JSON.parse :将一个 JSON 字符串转换为对象。

  //json
   let json = {a:1,b:2};
   let str = "www.baidu.com/path/user?data="+encodeURIComponent(JSON.stringify(json))
   console.log(str);

2、简写
名字跟值(key和value)一样的时候, 留一个就行
方法
show:function(){…}
show(){…}’

八、面向对象

1、对象的简洁语法

// 传统对象_单体模式写法 key-value模式:
let obj = {
   name:"blue",
   age:29,
   fn:function(){
      console.log(this.name)
  }
}

// ES6_单体模式写法  不需要写key,
var name = "blue";
var age = 29;
let obj = {
   name,    // key和val一样是可以省略了key
   age,
   fn(){   // 函数可以简写成这样
        console.log(this.name)
   }
}
// 调用
console.log(obj.age);  // 19
console.log(obj.fn()); // blue

2、对象里可以使用变量

let attrName = "width";
let obj = {
   [attrName]:100,   // 变量要放在"[]"中,这里不支持对象作为key
}

3、Object.is()判断两个 值或者对象是否相等

4、类和继承
(1)、传统面向对象的写法:

function Person(name,age){ // 类、构造函数
    this.name = name;
    this.age = age;
}
Person.prototype.showName = function(){
    return this.name;
};
Person.prototype.showAge = function(){
    return this.age;
};
var p1 = new Person('allen',28);
var p2 = new Person('xiaoxiaoyou',101);
console.log(p1.showName()); // allen
console.log(p2.showAge()); // 101
console.log(p1.showName == p2.showName); //true 注意不是调用方法,没有括号,所以才true
console.log(p1.constructor == Person); // true 构造方法相等

(2)、ES6面向对象写法:

class Person{
    // 构造器
    constructor(name,age){
        this.name = name;
        this.age = age;
    }
    showName(){
        return this.name;
    }
    showAge(){
        return this.age;
    }
}
var p1 = new Person('aaa',18);
var p2 = new Person('bbb',20);
console.log(p1.name); // aaa
console.log(p1.showName()); // aaa
console.log(p2.showAge()); // 20
console.log(p1.showAge == p2.showAge); // true
console.log(p1.constructor == Person); // true

(3)、面向对象给class赋值默认值:

class Person{
    // 构造器
    constructor(name='default',age=0){
        this.name = name;
        this.age = age;
    }
    showName(){
        return this.name;
    }
    showAge(){
        return this.age;
    }
}

var p1 = new Person();
console.log(p1.name); // 构造器里面给的默认值 default
console.log(p1.age); // 构造器里面给的默认值 0

(4)、传统写法原型继承extends

//传统写法原型继承
function Person(name,age){ // 类、构造函数
    this.name = name;
    this.age = age;
}
Person.prototype.showName = function(){
    return this.name;
};
Person.prototype.showAge = function(){
    return this.age;
};
// 工人类
function Worker(name,age){
    // 属性继承过来
    Person.apply(this,arguments);
}
// 原型继承
Worker.prototype = new Person();
var p1 = new Person('allen',28);
var w1 = new Person('worker',1000);
console.log(w1.showName()); // 确实继承过来了 result:worker

(5)、 ES6中面向对象实现类继承
class关键字、构造器和类分开了
class里面直接追加方法;

class User{
constructor(name,age){
    this.name = name;
    this.age = age;
}
showName(){
    console.log(this.name)
}
}

class VipUser extends User{
constructor(name,age,salary){
    super(name,age);
    this.salary = salary;
}
showSalary(){
    console.log(this.salary)
}
}

let p1 = new VipUser("red",20,"20万")
console.log(p1.name);
console.log(p1.age);
console.log(p1.salary)
p1.showSalary()
p1.showName()

九、promise – 承诺的意思

在promise之前代码过多的回调或者嵌套,可读性差、耦合度高、扩展性低。通过Promise机制,扁平化的代码机构,大大提高了代码可读性;用同步编程的方式来编写异步代码,保存线性的代码逻辑,极大的降低了代码耦合性而提高了程序的可扩展性。

在来回顾下同步和异步的区别个人理解:
异步请求:操作之间没有关系,可以同时进行多个操作,代码复杂
同步请求:同一时间只能干一个事情,代码简单
promise—消除异步操作,可以简单的理解为用同步一样的方式,来书写异步代码。

function createPromise(url){
    return new Promise((resolve,reject) =>{ // new一个Promise对象,里面接受一个函数参数,参数函数里面接受两个回到函数作为参数 ,一个是成功回调函数,一个是失败回调函数
       $.ajax({
           url,
           dataType:json,
           success(arr){
               resolve(arr);   // 成功调用resolve参数函数
           },
           error(err){
              reject(err);   // 失败调用reject参数函数  
           }
       })
   })
}

// Promise异步请求多个接口时,使用Promis.call();方法
Promise.call([  
    createPromise('data/arr.txt'),
     createPromise('data/json.txt'),
]).then((arr)=>{     // then接收两个参数,一个对应参数resolve,一个对应参数reject; then里面才是异步的过程,then比setTimeout执行的早
    let [res1, res2] = arr
    console.log("全部成功了");
},()=>{
   console.log("至少有一个失败了")
})

Promise的方法:
1、Promise.all():都成功或者都是失败

//  有了Promise之后的异步,的语法
Promise.all([$.ajax(),$.ajax()]).then(results => {
    // 成功的
},err =>{
    // 失败的
})

2、Promise.race():

Promise.race([   // 用的少,用法都差不多,race谁成功执行谁 ])

3、

var p = new Promise((res,rej) => {
   res()
})
p.then(()=>{console.log(1)})
.then(() => {
    console.log(2)
    a
})
.then(() => {
    console.log(3)
})
.catch((err)=>{
    console.log(err)
})

4、Promise小demo

// promise修改动画例子:
window.function(){
  function movePromise(obj,attr,target,duration,callback){
      return new Promise((res,rej) => {
          var b = parseInt(getComputedStyle(obj)[attr]);
          var c = target - b;
          var d = duration;
          var temp = new Date().getTime();
          var timer = setInterval(() => {
              var t = new Date().getTime() - temp;
              if (t >= d) {
                  clearInterval(timer);
                  t = d;
              }
              var v = c / d * t + b;
              obj.style[attr] = v + 'px';
              if (t === d) {
              //  typeof callback === "function" && callback();
              res()
              }
          }, 20)
      })
  }
  var oDiv = document.getElementById("box");
  movePromise(oDiv,"width",200,500)
  .then(()=>movePromise(oDiv,"height",200,500))
  .then(() => movePromise(oDiv, "left", 200, 500))
  .then(() => movePromise(oDiv, "top", 200, 500))
  .then(() => {console.log("哈哈哈")})
}

下面是网上大牛的博客的理解可以参考下:

//Promise对象 ---> 用来传递异步操作过来的数据的
//Pending(等待、处理中) ---> Resolve(完成,fullFilled)   ---> Reject(拒绝,失败)
//这里只是定义,还没开始执行
var p1 = new Promise(function(resolve,reject){
    resolve(1); // 成功了,返回一个promise对象1
    // reject(2); // 失败了
});

// 接收成功和失败的数据,通过then来传递
// then也是返回一个promise对象,会继续往下传递数据,传递给下一个then
p1.then(function(value){
    // resolve
    console.log(value); //执行打印1
    return value + 1; // 1
    alert(`成功了:${value}`);
},function(value){
    // reject
    alert(`失败了:${value}`);
}).then(function(value){
    console.log(value); // 2
});

//catch捕获异常错误
var p1 = new Promise(function(resolve,reject){
    resolve('成功了'); //返回一个promise对象“成功了”
});
//then也是返回一个promise对象,会继续往下传递数据
p1.then(function(value){
    console.log(value); //打印“成功了”
    // throw是用来抛错误的
    throw '发生了点小意外';
}).catch(function(e){
    // catch用来捕获这个错误的 ---> 追踪
    console.log(e);
});

//all ---> 全部,用于将多个promise对象,组合,包装成
//Promise.all([p1,p2,p3,...]); 所有的promise对象,都正确,才走成功
//否则,只要有一个错误,就走失败
var p1 = Promise.resolve(1);
var p2 = Promise.reject(0);
Promise.all([true,p1,p2]).then(function(obj){
    console.log(`成功了:${obj}`);
},function(obj){
    console.log(`失败了:${obj}`);
});

// race ---> 返回的也是一个promise对象
//最先执行的的promise结果,哪个最快我用哪个,所以下面打印的是one
var p1 = new Promise(function(resolve,reject){
    setTimeout(resolve,50,'one');
});
var p2 = new Promise(function(resolve,reject){
    setTimeout(resolve,100,'two');
});
Promise.race([p1,p2]).then(function(val){
    console.log(val);
});

//resolve ---> 生成一个成功的promise对象
//语法规则:Promise.resolve(val); // 普通值
// Promise.resolve(arr); // 数组之类
//Promise.resolve(promise); // 传递另一个promise对象
//传递普通值
Promise.resolve('success').then(function(val){
    // 注意resolve,走得是这里
    console.log(val); // success
},function(err){
    console.log("err:"+ err);
});
//传递数组
Promise.resolve([1,2,3]).then(function(val){
    // 注意resolve,走得是这里
    console.log(val); // [1,2,3]
},function(err){
    console.log(err);
});
//传递一个promise对象
var p1 = Promise.resolve(520);
var p2 = Promise.resolve(p1);
p2.then(function(val){
    //从p1那边传递过来的
    console.log(val); // 520
});

demo2:

// 经典面试题
setTimeout(function() {
  console.log(1)
}, 0);
new Promise(function executor(resolve) {
  console.log(2);
  for( var i=0 ; i<10000 ; i++ ) {
    i == 9999 && resolve();
  }
  console.log(3);
}).then(function() {
  console.log(4);
});
console.log(5);

首先先碰到一个 setTimeout,于是会先设置一个定时,在定时结束后将传递这个函数放到任务队列里面,因此开始肯定不会输出 1 。

然后是一个 Promise,里面的函数是直接执行的,因此应该直接输出 2 3 。

然后,Promise 的 then 应当会放到当前 tick 的最后,但是还是在当前 tick 中。

因此,应当先输出 5,然后再输出 4 。

最后在到下一个 tick,就是 1 。

“2 3 5 4 1”

十、generator (摘呢瑞特):生成器; 生成函数

普通函数-----一路运行到底
1、 generator函数 ---- 中间可以停, 可以理解为 ‘推一下 走一步’

函数跟 * 号,可以跟在function后面 也可以跟在函数名前面。
// 语法

  function *函数(){
       代码...
       yield ajax(xxx);
       代码...
   }
   let gen = 函数();
   gen.next();   //调用

2、yield可以传参

Promise 一次读一堆 适合用
generator 逻辑性的 适合用

3、Generator、yield
生成器( generator)是能返回一个迭代器的函数。生成器函数也是一种函数,最直观的表现就是比普通的function多了个星号*,在其函数体内可以使用yield关键字,有意思的是函数会在每个yield后暂停。

这里生活中有一个比较形象的例子。咱们到银行办理业务时候都得向大厅的机器取一张排队号。你拿到你的排队号,机器并不会自动为你再出下一张票。也就是说取票机“暂停”住了,直到下一个人再次唤起才会继续吐票。

当你调用一个generator时,它将返回一个迭代器对象。这个迭代器对象拥有一个叫做next的方法来帮助你重启generator函数并得到下一个值。next方法不仅返回值,它返回的对象具有两个属性:done和value。value是你获得的值,done用来表明你的generator是否已经停止提供值。继续用刚刚取票的例子,每张排队号就是这里的value,打印票的纸是否用完就这是这里的done。

看个例子:

// 生成器
    function *createIterator() {
        yield 1;
        yield 2;
        yield 3;
    }
    
    // 生成器能像正规函数那样被调用,但会返回一个迭代器
    let iterator = createIterator();
    
    console.log(iterator.next().value); // 1
    console.log(iterator.next().value); // 2
    console.log(iterator.next().value); // 3
    console.log(iterator.next().value); // undefined  因为generator已经停止提供值
//Generator ---> 生成器就是一个函数
//特点:
//1.函数名前面带一个*,和普通函数做区分
//2.内部使用yield语句
//调用方式,如下var res = show(); 与普通函数一样
//value指的是generator函数内容yield定义的值,done:false表示还没遍历完
//直接找到返回值return了,那么此时done才会为true
//console.log(res.next());{value:'值1',done:false}
function* show(){
    yield 'Hello';
    yield 'World';
    yield 'ES6';
    return 'xx';
}
var res = show();
console.log(res.next()); // {value: "Hello", done: false}
console.log(res.next()); // {value: "World", done: false}
console.log(res.next()); // {value: "ES6", done: false}
console.log(res.next()); // {value: "allen", done: true}
// 已经找到return返回值了,继续下去就没有意义了
// console.log(res.next()); // {value: "undefined", done: true}

//yield本身没有返回值,或者可以说每次给你返回的是undefined
function* show(){
    var a = yield 'Hello';
    return a;
}    
var res = show();
console.log(res.next()); // {value: "Hello", done: false}
console.log(res.next()); // {value: "undefined", done: true}

//next方法是可以带参数的,死循环的generator函数
function* fn(){
    for(var i=0;true;i++){
        // 如果里面传了一个值,那么它会把这个参数赋给最近的一个yield
        var a = yield i;
        if(a) i = -1;
    }
} 
var d = fn();
console.log(d.next()); // {value: 0, done: false}
console.log(d.next()); // {value: 1, done: false}
console.log(d.next()); // {value: 2, done: false}
// 如果里面传了一个值,那么它会把这个参数赋最近的一个yield
console.log(d.next(true)); // {value: 0, done: false}
console.log(d.next()); // {value: 1, done: false}
console.log(d.next()); // {value: 2, done: false}
console.log(d.next()); // {value: 3, done: false}

// for..0f循环generator函数
function* fn(){
    yield 1;
    yield 2;
    yield 3;
    yield 4;
    yield 5;
    return 6;
}
//for..0f循环generator函数,可以取值
for(let val of fn()){
document.write(val); // 12345
}

// 对象里使用generator函数的特殊写法,注意下
var json = {
    *show(){
        yield 'a';
        yield 'b';
        return 'c';
    }
};
var res = json.show();
console.log(res.next()); // {value: "a", done: false}
console.log(res.next()); // {value: "b", done: false}
console.log(res.next()); // {value: "c", done: true}

自动调用生成器并启动迭代器的方法:

function run(taskDef) { //taskDef即一个生成器函数

    // 创建迭代器,让它在别处可用
    let task = taskDef();

    // 启动任务
    let result = task.next();

    // 递归使用函数来保持对 next() 的调用
    function step() {

        // 如果还有更多要做的
        if (!result.done) {
            console.log(result.value); //这里就执行该做的事
            result = task.next();
            step();
        }
    }

    // 开始处理过程
    step();

}
//生成器
function *createIterator() {
    yield 1;
    yield 2;
    yield 3;
}
//启动
run(createIterator);

十一、Set():新的数据结构,类似于数组,他本身是个构造函数 使用的时候要new

let set = new Set([1,2,3,4]);
set.add("a").add("b");    // set方法 add 添加值,不能添加相同的值
console.log(set);   // Set{1,2,3,4,"a","b"}

set.size;   // 4

let b = set.delete("1");  // set方法 delete 删除值
console.log(b);  // true
console.log(set);  // Set(5) {2, 3, 4, "a", "b"}

var c = set.has(0);   // set方法 has 判断某个值是否存在,存在true,不存在false
console.log(c)

var d = set.clear();   // set 方法  clear  清空所有值
console.log(d);   // undefined
console.log(set);  // Set(0) {}  

set.forEach((item,index,set) => {   // set 方法  forEach 遍历  set数据结构中item和index是一样的
   console.log(item,index,set)
})

let s = set.keys();   // set 方法  keys(): 返回对象遍历器
console.log(s);
console.log(s.next());   // ,按照set插入的顺序返回值
console.log(s.next());
console.log(s.next());
console.log(s.next());
console.log(s.next());

十二、Map:新的数据结构,强引用类型 置为null,依然存在,他本身是个构造函数 使用的时候要new

let m = new Map([["a",1]]);   // 里面是个二位数组  但是 a是键,1是值
console.log(m);   // Map(1) {"a" => 1}
console.log(m.size);   // 1
console.log(m.set("b",2)); // 添加值  Map(2) {"a" => 1, "b" => 2}
console.log(m.get("b"));  // 获取值  2
console.log(m.has('b'));  // 检测是否存在数据结构里  存在返回true,不存在false
console.log(m.keys());   // 拿到数据结构的key值  //MapIterator {"a", "b"}
console.log(m.delete("b"));  // true   删除数据结构中的值
console.log(m.entries().next());  // 将Map数据变成数组
var map = new Map();
// 设置
// map.set(name,value);
map.set('a','apple');
map.set('b','banana');
// 获取
// map.get(name);
console.log(map.get('a') + ' ' + map.get('b'));
// 删除之前map对象
console.log(map);
// 删除
// map.delete(name);
map.delete('a');
// 删除之后map对象
console.log(map);

// 注意for..in是不能循环map对象的,不报错也无任何反应,所以下一代码无任何输出,稍微注意下
for(var name in map){ 
    console.log(name);
}

// 实体 map对象的循环输出
for(var name of map){
//循环出来的结果就是:a,apple b,banana 循环key,value
console.log(name);
}

//循环出来的结果就是: a,apple b,banana 循环key,value
for(var [key,value] of map.entries()){
    console.log(key,value);
}

//只循环key
for(var key of map.keys()){
    console.log(key);
}

//只循环value
for(var val of map.values()){
    console.log(val);
}

十三、遍历接口

 var arr = [1,2,3,4];
  console.log(arr[Symbol.iterator]);  // 检测是否有遍历接口

十四、WekMap():弱引用类型,避免内存泄漏

十五、模块化 export 和 import

1、script标签 推迟执行
defer:在外链js上使用,会在其他的js的dom节点加载完成之后 在加载,可以改变外连js的执行顺序,多个defer的执行按照先后顺序

2、在外连js中加入script标签 sype="module"就会变成模块化就加载,模块内的代码只能在模块里面使用,不能在模块之外使用。使用了module会默认加上defer 需要在服务器打开

3、export {name1,name2…,nameN} 导出模块中的变量、函数等等 加{}内容
name1 as newName 用上as可以修改原来的名字
export default 1000 默认导出

4、import {} from ‘…/…/xx.js’ 模块导入
import d from ‘…/…/xx.js’ 默认导入不需要加{}

5、模块化的注意事项:
外部的this指向的是undefined

6、import 导入模块、export 导出模块
可以直接在任何变量或者函数前面加上一个 export 关键字,就可以将它导出。
在一个文件中:

export const sqrt = Math.sqrt;
export function square(x) {
    return x * x;
}
export function diag(x, y) {
    return sqrt(square(x) + square(y));
}

然后在另一个文件中这样引用:

import { square, diag } from 'lib';
console.log(square(11)); // 121
console.log(diag(4, 3));

总结:

//mod.js
// 第一种模块导出的书写方式(一个个的导出)
// 导出普通值
export let a = 12;
export let b = 5;
// 导出json
export let json = {
    a,
    b
};
// 导出函数
export let show = function(){
    return 'welcome';
};
// 导出类
export class Person{
    constructor(){
        this.name = 'jam';
    }
    showName(){
        return this.name;
    }
}

//index.js
//导出模块如果用default了,引入的时候直接用,若没有用default,引入的时候可以用{}的形式
// 导入模块的方式
import {
    a,
    b,
    json,
    show,
    Person
} from './mod.js';
console.log(a); // 12
console.log(b); // 5
console.log(json.a); // 12
console.log(json.b); // 5
console.log(show()); // welcome
console.log(new Person().showName()); // jam

//mod1.js
// 第二种模块导出的书写方式
let a = 12;
let b = 5;
let c = 10;
export {
    a,
    b,
    c as cc // as是别名,使用的时候只能用别名,特别注意下
};

//index1.js
// 导入模块的方式
import {
    a,
    b,
    cc // cc是导出的,as别名
} from './mod1.js';
console.log(a); // 12
console.log(b); // 5
console.log(cc); // 10

//mod2.js
// 第三种模块导出的书写方式 ---> default
// default方式的优点,import无需知道变量名,就可以直接使用,如下
// 每个模块只允许一个默认出口
var name = 'jam';
var age = '28';
export default {
    name,
    age,
    default(){
        console.log('welcome to es6 module of default...');
    },
    getName(){
        return 'bb';
    },
    getAge(){
        return 2;
    }
};

//index2.js
// 导入模块的方式
import mainAttr from './mod2.js';
var str = ' ';
// 直接调用
console.log(`我的英文名是:${mainAttr.name}我的年龄是${mainAttr.age}`);
mainAttr.default(); // welcome to es6 module of default...
console.log(mainAttr.getName()); // bb
console.log(mainAttr.getAge()); // 2

//mod3.js
var name = 'jam';
var age = '28';
export function getName(){
    return name;
};
export function getAge(){
    return age;
};

//index3.js
// 导入模块的方式
import * as fn from './mod3.js';
// 直接调用
console.log(fn.getName()); // jam

你可能感兴趣的:(ES6)