2

前言

本篇文章主要记录ES6常用语法API,达到快速查阅的目的,不适合新手学习。现在新版本的Chrome浏览器已经全面支持ES6的语法,大家在测试和学习ES6语法的时候直接使用谷歌浏览器即可,不需要考虑麻烦的babel转换【注意线上代码则必须使用】

let变量

1、声明变量

let a;
let b,c,d;
let e = 100;

2、let变量声明不能重复,但var声明则允许

let name = '悟空';
let name = '悟空'; //报错

3、let只在块儿级作用域内生效,作用域外访问let变量报错,但var声明则允许

 if else while for 
 {
     let name2 = '八戒';
 }
 console.log(name2); //报错

4、不存在变量提升,但var声明则允许

console.log(name3);
let name3 = '唐僧'; //报错,必须先定义name3才能访问

5、作用域链

{
    let name4 = '沙僧';
    function fn(){
        console.log(name4); //可以访问外围作用域的数据
    }
    fn();
}

6、在循环语句中使用let定义循环变量

<body>
    <ul>
        <li>大师兄,师傅被妖怪抓走了!</li>
        <li>大师兄,师傅和二师兄被妖怪抓走了!</li>
        <li>二师兄,师傅被妖怪抓走了!</li>
    </ul>
    <script>
        //获取div元素对象
        let items = document.getElementsByTagName('li');
        //遍历并绑定事件,如果改成var变量定义将报错
        for(let i = 0;i<items.length;i++){
            items[i].onclick = function(){                
                items[i].style.background = 'pink'; //修改当前元素的背景颜色
            }
        }        
    </script>
</body>

const变量

1 、一定要赋初始值,否则报错

const NAME;  //Uncaught SyntaxError: Missing initializer in const declaration

2、 常量使用大写(默认条例)

const NAME2 = 100;

3、常量的值不能修改

const NAME3 = '牛魔王';
const NAME3 = '铁扇公主'; //Uncaught SyntaxError: Identifier 'NAME3' has already been declared

4、 块儿级作用域,作用域外无效

 {
     const NAME4 = '';
 }
console.log(NAME4);    //test.html:41 Uncaught ReferenceError: NAME4 is not defined

5、对于数组和对象的元素修改, 不算做对常量的修改, 不会报错

const NAME5 = ['白骨精','红孩儿'];
NAME5.push('豹子妖'); //成功

模板字符串

模板字符串作用和pre标签一样,是它的增强版,可以在代码里随心所欲的书写js字符串代码格式,并且支持变量拼接。

1、书写特殊格式的js字符串

 let xyj = `
    大师兄,师傅被妖怪抓走了,
    大师兄,师傅和二师兄被妖怪抓走了
    二师兄,师傅被妖怪抓走了
 `    
 console.log(xyj);

2、变量拼接使用${变量名称}

 let name1 = '大师兄,师傅';
 let name2 = '大师兄,师傅和二师兄';
 let name3 = '二师兄,师傅';
 let xyj = `
    ${name1}被妖怪抓走了,
    ${name2}被妖怪抓走了
    ${name3}被妖怪抓走了
 `    
 console.log(xyj);

解构赋值

解构赋值解救按照数组和对象的存储结构顺序,方便的从数组和对象中获取定义的数据。

1、从数组中获取数据

let xyj = ['悟空','八戒','沙僧','唐僧'];
let [wukong, bajie, shasen, tangseng] = xyj;
console.log(wukong);
console.log(bajie);
console.log(shasen);
console.log(tangseng);

2、从对象中获取数据

 let xyj = {
   wukong: '悟空',
   bajie: '八戒',
   other:{
     shaseng: '沙僧'
   }
 };
 
 let {wukong, bajie, other} = xyj;
 console.log(wukong);        
 console.log(bajie);        
 console.log(other.shaseng);

箭头函数

1、es6使用=>格式定义箭头函数,例如如下格式:

 let func1 = () => {
     console.log('我相信离开这个怂货,你一定能找到自己的超人。');
 }        
 func1();

2、不能作为构造实例化对象

let Func2 = () => {
     val:'人他的心里都有一杆称,一边放着他犯的烂事儿,一边放着他应该受的惩罚。'       
 }
        
 let f = new Func2('xiao',30); //Uncaught SyntaxError: Unexpected identifier

3、没有arguments变量

 let Func3 = () => {
     console.log(arguments);     
 }        

 Func3() //test.html:39 Uncaught ReferenceError: arguments is not defined

4、箭头函数简写形式

 //当形参有且只有一个的时候,可以省略小括号
 let Func4 = val => {
     console.log(val);     
 }    
 Func4('傅首尔经典句子盘点')
 
 //当代码体只有一条语句的时候, 可以省略花括号和return
 let sum = n => n + n;
 console.log(sum(1));

5、箭头函数不会改变this指向
箭头函数不会更改this指向,适用于指定回调函数,例如定时器, js数组相关方法等。
箭头函数不适合与this有关的回调, 例如对象的方法。

// 数组回调函数
const arr = [1,20,100,25];
const result = arr.filter(val => val % 2 === 0);
console.log(result);

剩余参数

剩余参数由三个点(...)与一个紧跟着的具名参数指定,用于获取函数的实参,代替arguments

function fn(a,b,...args){
    console.log(a); //曾小贤
    console.log(b); //胡一菲
    console.log(args);    //["吕子乔", "陈美嘉", "张伟"]        
 }
 fn('曾小贤','胡一菲','吕子乔','陈美嘉','张伟');
 

注意点:
1、剩余参数必须要放到参数最后
2、剩余参数是真正的数组,而arguments是类数组结构

扩展运算符

剩余参数允许你把多个独立的参数合并到一个数组中;而扩展运算符则允许将一个数组分割,并将各个项作为分离的参数传给函数。

 function fn(a,b,c){
    console.log(a); //曾小贤
    console.log(b); //胡一菲
    console.log(c); //吕子乔        
 }
 let arr = ['曾小贤','胡一菲','吕子乔'];
 fn(...arr);

扩展运算符除了能将数组中的项分割开并作为函数的分离参数。同样能将扩展运算符用于可迭代对象(例如Set),将它们转换为数组。

let set = new Set([1,2,3,3,3,4,5]);                
let array = [...set];
console.log(array); //[1, 2, 3, 4, 5]

数值扩展

1、Number.EPSILON
Number.EPSILON是JS表示的最小精度,当JS进行小数运算的时候结果会不准确,可以使用Number.EPSILON进行参考。

//JS中比较浮点数方法
function equal(a, b){
     if(Math.abs(a-b) < Number.EPSILON){
         return true;
     }else{
         return false;
     }
}
console.log(0.1 + 0.2 === 0.3);
console.log(equal(0.1 + 0.2, 0.3));

2、Number.isNaN
检测一个数值是否为NaN

console.log(Number.isNaN(11111));

3、Number.isInteger
判断一个数是否为整数

console.log(Number.isInteger(2.5));

4、Number.isFinite
检测一个数值是否为有限数

console.log(Number.isFinite(100/0));
console.log(Number.isFinite(Infinity));

5、Number.parseInt和Number.parseFloat
字符串转整数和字符串转浮点数,并且去掉不能转换的部分

console.log(Number.parseInt('32324sdf'));
console.log(Number.parseFloat('3.1415sdfsd'));

对象方法扩展

1、Object.is
Object.is 判断两个值是否完全相等,几乎和===等价。

console.log(Object.is(NaN, NaN)); //true
console.log(NaN === NaN); //false

var aa = {};
var bb = {};
console.log(aa === bb); //false
console.log(Object.is(aa === bb)); //false

2、Object.assign
Object.assign用于合并对象的,将一个或多个源对象的所有可枚举属性,复制到目标对象 。

let rec = {};
const conf = {
    target:'获取经书',
    person:['唐僧','悟空']
}

//enumerable为false的属性,不会被复制
Object.defineProperty(conf, 'height' ,{
    value: '188',
    enumerable: false
})    

Object.assign(rec,conf);    
console.log(rec); //{target: "获取经书", person: Array(2)}

//引用类型复制的是指针,会修改conf对象的person属性数据
rec.person.push('八戒');
console.log(conf); //{target: "获取经书", person: Array(3), height: "188"}

3、简化对象属性写法
当对象的属性名和属性值同名的时候,可以省略冒号和属性值;函数可以省略:function关键字。

let name = '悟空';
 let age = 19;
 let person = {
    name,
    age
 }
 console.log(person);

symbol

在JS已有的基本类型(字符串数值布尔类型nullundefined)之外(简称UOSNB),ES6引入了一种新的基本类型:符号(Symbol

1、创建Symbol

//创建Symbol
let symbol = Symbol(); 

//创建Symbol,并且带有描述
let symbolDes =  Symbol('desctription'); 
let symbolDes2 = Symbol('desctription');

//尽管有其他方法可以判断一个变量是否为Symbol类型,typeof运算符依然是最准确、最优先的判别手段
console.log(typeof symbol);  

// Symbol('描述字符串')方式创建的字符串,即使描述相同,比较结果也不相同
console.log(symbolDes === symbolDes2);  //false

//另一种创建Symbol的方式,全局唯一,存放于索全局符号注册表,实现共享
let uid =  Symbol.for('uid'); 
let uid2 = Symbol.for('uid'); 

//Symbol.for()方式因为全局唯一,所以比较结果相等
console.log(uid === uid2); //true

//向对象中添加Symbol类型
let uid =  Symbol.for('uid');
var friend = {
    [uid]:123,
    name:'小马哥',
    age:18
};

//其它事项,Symbol类型变量不能与字符串等其它类型变量进行运算

2、全局符号注册表
全局符号注册表类似于全局作用域,是一个共享环境,Symbol.for()方式创建的Symbol变量存放于全局符号注册表中。这意味着我们不确定某些值是否已存在于其中。在使用第三方组件时,为符号的键值使用命名空间能够减少命名冲突的可能性,举个例子:Query代码应当为它的所有键值使用"jquery."的前缀,如"jquery.element"或类似的形式。

3、Symbol内置属性
原型对象包含一些内置属性,可以通过操作对象Symbol属性对原生默认行为进行修改。举例说明:

class MyNumber {        
}

Object.defineProperty(MyNumber, Symbol.hasInstance,{
    value:function(val){
        console.log( '这个函数被调动了。。。' );
        return (val instanceof Number) && (val>=1 && val<=100)
    }
})

let a = new Number(-1);
let b = new Number(2);

// 每调用一次instanceof都会触发一次上述定义的Symbol.hasInstance函数
console.log( a instanceof MyNumber);
console.log( b instanceof MyNumber);
console.log( b instanceof MyNumber);
console.log( b instanceof MyNumber);
//-----------操作结果------------
//这个函数被调动了。。。
//test.html:53 false
//test.html:45 这个函数被调动了。。。
//test.html:54 true
//test.html:45 这个函数被调动了。。。
//test.html:55 true
//test.html:45 这个函数被调动了。。。
//test.html:56 true

上述结果可以说明:我们重新定义了对象Symbol.hasInstance属性,每当我们使用instanceof操作符的时候就会触发Symbol.hasInstance属性上定义的函数,并且判断一个对象是否是么个构造函数的实例完全由我们自定义的规则来判断。

迭代器

1、Symbol.iterator属性

我们先来看下数组新的迭代方式。

const xiyouji = ['唐僧','孙悟空','猪八戒','沙僧'];
    
for(let name of xiyouji){
    console.log(name);
}

为什么数组能够使用for...of...方式循环遍历呢?是因为数组对象原型包含了Symbol.iterator属性。接着上面的数据,我们来看下Symbol.iterator属性到底是什么。

console.log(xiyouji[Symbol.iterator]);  //结果为:ƒ values() { [native code] } ,是一个函数

//调用函数并将结果打印出来
let iterator =  xiyouji[Symbol.iterator]();
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}

结论:
Symbol.iterator符号被用于定义对象的默认迭代器。内置对象与开发者自定义对象都可以使用这个符号,以提供一个能返回迭代器的方法。当Symbol.iterator在一个对象上存在时,该对象就会被认为是可迭代对象。

for-of循环在循环中使用可迭代对象来返回一系列数据。与使用传统for循环进行迭代相比,使用for-of要容易得多,因为你不再需要追踪计数器并控制循环何时结束。for-of循环会自动从迭代器中读取所有数据,直到没有更多数据为止,然后退出循环。

2、自定义迭代器

我们参照上述数组的迭代方式,为自定义的对象添加Symbol.iterator属性,实现用for...of...方式循环遍历(需要自定义遍历数据的时候,要想到迭代器)

const AiQing = {
        name:[
            '曾小贤',
            '胡一菲',
            '路子乔'
        ],
        [Symbol.iterator](){
            let index = 0;
            let _this = this;
            return {
                next:function(){
                    if (index < _this.name.length) {
                        const result = { value: _this.name[index], done: false };
                        index++;
                        return result;
                    }else{
                        return {value: undefined, done: true};
                    }
                }
            }
        }
    }
    
    for(let val of AiQing){
        console.log(val);
    }
    
    //------遍历结果-------------
    //曾小贤
    // 胡一菲
    // 路子乔

为了让for-of更易使用,ES6中的许多类型都具有默认的迭代器。所有的集合类型(也就是数组、Map与Set)都具有迭代器。

生成器

1、生成器定义和调用

生成器其实就是一个特殊的函数,用于实现异步编程,可以使用迭代器的方式实现调用。生成器的定义可以用一个*放在函数名称前表示,生成器函数返回的结果是迭代器对象,调用迭代器对象的next方法可以得到yield语句后的值:

function * generate(){
    console.log(111);
    yield '弟子东胜神洲傲来国花果山水帘洞人氏';
    console.log(222);
    yield '好!好!好!自今就叫做孙悟空也';
    console.log(333);
    yield '决不敢提起师父一字,只说是我自家会的便罢';
    console.log(444);
}

//使用iterator的方式一步一步进行调用
let iterator = generate();
console.log(iterator.next());  
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());

//for ... of ...遍历
 for(let v of generate()){
     console.log(v);
 }
 

2、生成器函数传参

需要注意的是前面yield的执行结果是后面表达式的入参。

function * generate(params){
    console.log(params);
    let one = yield '第一步'; //前面yield的执行结果是后面表达式的入参
    console.log(one);
    let two =  yield '第二步';
    console.log(two);            
}

//第一个参数通过调用函数名后的括号传参
let iterator = generate('悟空学艺111');
console.log(iterator.next());
//其它参数通过next方法传入
console.log(iterator.next('悟空学艺222'));
console.log(iterator.next('悟空学艺333'));        
console.log(iterator.next());

3、解决异步回调的案例

开发项目的时候时常获取商品数据,经常需要先要获取订单数据和用户数据,有一定的依赖关系。这种异步回调的执行方案如下:

//模拟获取  用户数据  订单数据  商品数据 
function getCustomer(params){
    setTimeout(()=>{
        console.log(params);
        let data = '用户数据';                
        //调用 next 方法, 并且将数据传入
        iterator.next(data);
    }, 1000);
}

function getOrders(params){
    setTimeout(()=>{
        console.log(params);
        let data = '订单数据';
        iterator.next(data);
    }, 1000)
}

function getGoods(params){
    setTimeout(()=>{
        console.log(params);
        let data = '商品数据';
        iterator.next(data);
    }, 1000)
}

function * generate(params){
    let customer = yield getCustomer(params);
    let orders = yield getOrders(customer);
    let goods = yield getGoods(orders);
}

//调用生成器函数
let iterator = generate('客户名');
iterator.next();

Promise

1、定义Promise对象

const p = new Promise(function(resolve, reject){
    setTimeout(function(){
        //resolve('异步获取成功时候调用的函数');
        reject('异步获取失败时候调用的函数');  
    }, 1000);
});

p.then(function(value){
    console.log(value);
}, function(err){
    console.error(err);
})

2、用Promise封装读取文件

//引入fs模块
const fs = require('fs');

//使用Promise封装
const p = new Promise(function(resolve, reject){
    fs.readFile("./readme.txt", (err, data)=>{
        //判断如果失败
        if(err) reject(err);
        //如果成功
        resolve(data);
    });
});

p.then(function(value){
    console.log(value.toString());
}, function(err){
    console.log("读取失败!!");
});

3、用Promise封装AJAX

const p = new Promise((resolve, reject) => {
    //1. 创建对象
    const xhr = new XMLHttpRequest();

    //2. 初始化
    xhr.open("GET", "http://url");

    //3. 发送
    xhr.send();

    //4. 绑定事件, 处理响应结果
    xhr.onreadystatechange = function () {
        //判断
        if (xhr.readyState === 4) {
            //判断响应状态码 200-299
            if (xhr.status >= 200 && xhr.status < 300) {
                //表示成功
                resolve(xhr.response);
            } else {
                //如果失败
                reject(xhr.status);
            }
        }
    }
})

//指定回调
p.then(function(value){
    console.log(value);
}, function(reason){
    console.error(reason);
});   

4、Promise异步调用案例

模拟先获取用户数据,拿着用户数据去获取订单数据,最后拿着订单数据数据去获取商品数据。

const p = new Promise((resolve, reject) => {
    setTimeout(()=>{
        let data = '用户数据';   
        console.log(data);
        resolve(data); 
    }, 1000);
});

p.then(value => {
    return new Promise((resolve, reject) => {
        setTimeout(()=>{
            let data = '订单数据';   
            console.log(data);
            resolve(data); 
        }, 1000);
    });
}).then(value => {
    return new Promise((resolve, reject) => {
        setTimeout(()=>{
            let data = '商品数据';   
            console.log(data);
            resolve(data); 
        }, 1000);
    })
}).then(value => {
    console.log('最终的:'+value);
}).catch(err=>{
    console.error(err);
});

Set集合

1、创建一个Set集合

let s = new Set(); //空数据
let s2 = new Set(['路飞','索隆','娜美','山治']); //用数组创建

2、Set集合常用操作

let s2 = new Set(['路飞','索隆','娜美','山治']);        

//元素个数
console.log(s2.size);
//添加新的元素
s2.add('乌索普');
//删除元素
s2.delete('索隆');
//检测是否包含么个元素
console.log(s2.has('索隆'));
//清空
s2.clear();

//循环遍历Set集合
for(let v of s2){
    console.log(v);
}

3、Set集合实践案例

//数组去重,运用扩展运算符和Set集合不存储相同元素的特性
let arr = [1,2,3,4,5,2,3,3,1];
let arr2 = [...new Set(arr)];
console.log(arr2);    

//两个数组求并集,
let arr = [1,2,3,4,5,2,3,3,1];
let arr2 = [3,4,5,6,7];
let res = [...new Set([...arr, ...arr2])]
console.log(res);

//求交集
let arr = [1,2,3,4,5,2,3,3,1];
let arr2 = [3,4,5,6,7];
let s2 = new Set(arr2);
let res = [...new Set(arr)].filter(data => {
    if(s2.has(data)){
        return true;
    }else{
        return false;
    }
});
console.log(res);    

//求差集
let arr = [1,2,3,4,5,2,3,3,1];
let arr2 = [3,4,5,6,7];
let s2 = new Set(arr2);
let res = [...new Set(arr)].filter(data => {
    if(s2.has(data)){
        return false;
    }else{
        return true;
    }
});
console.log(res);

map集合

1、Map创建

let m = new Map();

2、Map操作

//Map添加数据
m.set('key','value');
m.set('arr',[1,2,3]);
m.set('function',function(){
    console.log('map里存储函数')
});
m.set('object',{'obj':'map里存储对象'});

console.log(m.size);

//删除
m.delete('key');
console.log(m);

//获取Map数据
console.log(m.get('arr'));
m.get('function')();
console.log(m.get('object')['obj']);

//清空Map
 m.clear();

//遍历
for(let v of m){
    console.log(v);
}

类声明

1、类的创建

class声明类,constructor定义构造函数,new创建对象初始化的时候调用

class Cat{
    // 通过constructor传入对象属性
    constructor(name,age){
        console.log("new 的时候我会被调用");
        this.name = name;
        this.age = age;
    }

    introduce(){
        console.log("我的名字是:"+this.name+",年龄是:"+this.age);
    }

}    

let cat1= new Cat('ketty',2);
cat1.introduce();

2、继承父类

使用extends继承父类,使用super调用父级构造方法,并且父类的方法可以被覆盖。

//父类
class Animal{
    constructor(name,age){
        console.log("父类constructor被调用");
        this.name = name;
        this.age = age;
    }

    eat(){
        console.log(this.name+"喜欢吃");
    }
}

//子类
class Cat extends Animal{
    // 通过constructor传入对象属性,并且通过super(name,age);将参数传给父类
    constructor(name,age,height){
        super(name,age); 
        console.log("子类constructor被调用");            
        this.height = height;
    }

    //eat(){
    //    console.log("子类"+this.name+"喜欢吃");
    //}
        
    introduce(){
        console.log("我的名字是:"+this.name+",年龄是:"+this.age+",身高是:"+this.height);
    }
}    

//创建子类并且调用父类和子类方法
let cat1= new Cat('ketty',2 , 188);
console.log(cat1);
cat1.introduce();
cat1.eat();

3、类的静态成员

类的静态成员只能通过类名来访问,不能通过类的实例对象来访问

class Animal{        
    static name = 'dog';
    static eat(){
        console.log('想吃火腿肠');
    }
}

console.log(Animal.name);
Animal.eat();

ES6模块化

1、对外暴露数据的方式

//分别暴露
export let name = '西游记';
export function action() {
    console.log("取经");
}

//统一暴露
let name = '西游记';
function action() {
    console.log("取经");
}
export {name, action};

//默认暴露
export default {
    name: '西游记',
    action: function(){
        console.log("取经");
    }
}

2、引入数据

//1. 通用的导入方式
import * as m1 from "./m1.js";

//2. 解构赋值形式
import {school, teach} from "./m2.js";

//3. 默认暴露引入方式
import m3 from "./m3.js";

ES7新特性

1、Array.prototype.includes
Includes方法用来检测数组中是否包含某个元素(没有感觉比indexof好使)

const arr  = ['悟空','八戒']
console.log(arr.includes('悟空')); //true

2、指数操作符**
引入指数运算符**用来实现幂运算,功能与Math.pow()相同

console.log(2 ** 10); //结果为1024

async和await

asyncawait经典应用场景,发送AJAX请求。

// 发送 AJAX 请求, 返回的结果是 Promise 对象
function ajaxUtil(url) {
    return new Promise((resolve, reject) => {
        //1. 创建对象
        const x = new XMLHttpRequest();
        //2. 初始化
        x.open('GET', url);
        //3. 发送
        x.send();
        //4. 事件绑定
        x.onreadystatechange = function () {
            if (x.readyState === 4) {
                if (x.status >= 200 && x.status < 300) {
                    //成功
                    resolve(x.response);
                }else{
                    //如果失败
                    reject(x.status);
                }
            }
        }
    })
}    

// async与await测试  axios
async function test(){
    //发送 AJAX 请求
    let result1 = await ajaxUtil("https://api.apiopen.top/getJoke");
    console.log(result1);
    let result2 = await ajaxUtil("https://api.apiopen.top/getJoke");
    console.log(result2);
}
test();

吴小风
24 声望1 粉丝