ES6新特性介绍

本文深入探讨了JavaScript的ES6新特性,包括变量声明的const和let,模板字符串的使用,解构赋值的灵活性,以及异步编程中的箭头函数、Promise和async/await。通过实例展示了这些特性的应用及其优势,如简化代码结构、提高可读性和处理复杂异步操作的能力。
摘要由CSDN通过智能技术生成

1、ES6与JavaScript的关系

        JavaScript 由 Brendan Eich 于 1995 年发明,并于 1997 年成为 ECMA 标准。

        ECMAScript 是该语言的官方名称。

        从 2015 年起,ECMAScript 按年命名(ECMAScript 2015)。

        ES6 指的是 ECMAScript 的第六代版本 因发布时间为2015年 又可称为 ECMAScript 2015。

        ECMAScript 主要版本:

        浏览器支持情况:

 

2、变量声明const和let

   ES6 新增了 const 关键字用于声明常量, let 关键字声明变量。

        const 声明的变量为常量,常量在声明时必须赋值,声明后不能被修改:

const PI = 3.14;
PI = 3.1415;  //报错 Uncaught TypeError: Assignment to constant variable.

        如果 const 声明的常量是一个对象,对象所包含的属性值是可以被修改的,但不能替换为一个新的对象即对象的引用地址不能修改:

const car = {color:'red'};
car.color = 'black';     //可以修改  111027110181937
car = {color:'black'};   //报错 Uncaught TypeError: Assignment to constant variable.

        const 为块级作用域,即在所属的大括号范围内有效。超出范围程序将抛出错误:

{
    var i = 1;
 	 const j = 2;
}
console.log("i=" + i);   //输出 i=1
console.log("j=" + j);   //报错 Uncaught ReferenceError: j is not defined

        与 const 一样, let 声明的变量也是块级作用域,即在所属的大括号范围内有效。超出范围程序将抛出错误:

{
    var i = 1;
 	 let j = 2;
}
console.log("i=" + i);   //输出 i=1
console.log("j=" + j);   //报错 Uncaught ReferenceError: j is not defined

        区别于 var,let 关键词声明的变量不具备变量提升(hoisting)特性:

console.log("i=" + i);  //输出 i=undefined
var i = 1;
console.log("j=" + j);  //报错 VM320:3 Uncaught ReferenceError: j is not defined
let j = 2;

        区别于 var,同一作用域内,let 变量不能重复声明:

var i = 1;
var i = 2;   //可以重复声明
let j = 3;
let j = 4;   //报错 VM287:4 Uncaught SyntaxError: Identifier 'j' has already been declared

        var 的变量提升(hoisting)特性:

var i = 1;             // 全局变量i
f();                   // f是函数,虽然定义在调用的后面,但是函数声明会提升到作用域的顶部。 
console.log(i);        // i = 1,  当前为全局变量i
function f() {
  console.log(i);      // 当前i是下一行的i的声明提升后的默认值undefined
  var i = 2;           //局部变量
  console.log(i);      // i = 2
}

        运行结果:

        let 在 for 循环中的作用域:

for (var i = 0; i < 10; i++) {
   setTimeout(function () {
    console.log("i=" + i);
  }, 0);
}      // 输出 10次 i=10
for (let j = 0; j < 10; j++) {
   setTimeout(function () {
    console.log("j=" + j);
  }, 0);
}     // 循环输出 j=0 到 j=9

        运行结果:

3、模板字符串

 模板字符串(template string)是传统的javascript字符串的加强版。用反引号 `` 标识。它可以当作普通字符串使用,也可以用来方便的处理多行字符串和在字符串中嵌入变量。

        普通字符串。与传统javascript字符串使用一致:

 let hello = `hello world`;

        字符串中如果需要使用反引号,则需要在前面添加反斜杠进行转义:

let hi = `\`hi\`, dude`;

        处理多行字符串。使用传统字符串需要使用换行和缩进的时候使用 + 或者 concat() 进行拼接是一件复杂而痛苦的事情。而模板字符串允许在反引号范围内任意的换行和缩进,所有的换行和缩进格式都能得到保留,非常方便:

let table = `
        <table>
            <tr>
                <td>cell</td>
                <td>cell</td>
            </tr>
        </table>
        `;
console.log(table);

        打印结果:

         在字符串中嵌入变量。传统字符串可以使用 + 进行拼接从而将变量嵌入字符串中,模板字符串使用 ${ } 形式将变量嵌入字符串中:

let job = "程序员", salary = "100";

//传统字符串拼接拼接
let say = "我的工作是" + job + ", 我每月可以挣" + salary + "大洋,真开心!";  

//模板字符串嵌入
say = `我的工作是${job}, 我每月可以挣${job}大洋,真开心!`;                   

        ${ }内部可以直接放入字符串也可以放入放入变量、表达式、对象、方法。如果大括号中的值不是字符串,将按照一般的规则转为字符串。比如,大括号中是一个对象,将默认调用对象的 toString() 方法。

4、解构赋值

        解构赋值是对赋值运算符的扩展。他是一种针对数组或者对象进行模式匹配,然后对其中的变量进行赋值。

        数组的解构赋值:

let [a1, a2, a3] = [1, 2, 3];
console.log(`a1=${a1},a2=${a2},a3=${a3}`);
let [b1, , b2] = [1, 2, 3];
console.log(`b1=${b1},b2=${b2}`);
let [c1, c2, c3, c4, c5] = 'hello';
console.log(`c1=${c1},c2=${c2},c3=${c3},c4=${c4},c5=${c5}`);

        输出结果:

        对象的解构赋值:

let a1 = { x, y } = { x: 'aaa', y: 'bbb' };
console.log(a1);  //a1.x='aaa', a1.y='bbb'
let a2 = { x: ['hello', { z: 'world' }] };
let a3 = { x: [y, { z }] } = a2;
console.log(a3);  //a3.x[0]='hello', a3.x[1].z='world'

         输出结果:

        解构默认值,当解构的对象的属性没有值甚至不存在时可以设置默认值:

let [x = 1, y = 2, z = 3] = [4, undefined, 6];
console.log(`x=${x},y=${y},z=${z}`);
function fun({ x = 1, y = 2, z }) {
    console.log(`x=${x},y=${y},z=${z}`);
}
fun({});
fun({ x: 3 });
fun({ x: 4, z: 5 });
fun({ x: 6, y: 7, z: 8 });
fun({ z: 9 });

         输出结果:

 5、展开运算符

        展开运算符是三个点 ... ,能将数组转换为参数序列:

function add(x, y) {
    return x + y;
}
let numbers = [1, 2];
console.log(add(...numbers));

        输出结果:

        与数组解构表达式结合,... 也可称为剩余运算符:

let [a, b, ...c] = [1, 2, 3, 4, 5, 6];
console.log(a, b, c);

         输出结果:

        即 ...c 匹配数组除去前面确定的变量 a 和 b 后剩余的部分。

        注意:与解构表达式结合时剩余运算符只能位于最后一个变量前,放在之前的变量前会抛出异常:

 let [...a, b, c] = [1, 2, 3, 4, 5, 6]; 
//报错 Uncaught SyntaxError: Rest element must be last element
 let [x, ...y, z] = [1, 2, 3, 4, 5, 6]; 
//报错 Uncaught SyntaxError: Rest element must be last element

        与对象解构表达式结合:

let { a, b, ...rest } = { a: 1, b: 2, c: 3, d: 4 };
console.log(a, b, rest);

        输出结果:

         同样剩余运算符放在之前的变量前会抛出异常:

let { ...rest, a, b } = { a: 1, b: 2, c: 3, d: 4 }; 
//报错 Uncaught SyntaxError: Rest element must be last element

        数组的拼接,使用 ... 运算符可以进行数组的拼接操作:

 let a = [1, 2], b = [4, 5];
 let c = [...a, ...b];
 let d = 3;
 //单个元素加入拼接,对应变量无需使用 ... 运算符
 let e = [...a, d, ...b];
 console.log(c,e);

        输出结果:

         对象的拷贝赋值。使用 ... 运算符可以将源对象拷贝到目标对象,拷贝后需注意字段值修改的作用范围:

let src = { user: 'admin', password: '123456', job: { title: 'lawyer', salary: 1000 } };
let tar = { ...src, tel: '110' };
src.password = '888888';      //源单独修改
tar.user = 'user';            //目标单独修改
src.job.title = 'teacher';    //源和目标同步修改
tar.job.salary = 200;         //源和目标同步修改
console.log(src, tar);

        输出结果:

        可以看到非对象的属性修改,源和目标是独立互不影响的;对象属性的修改源和目标同步变化,因为对象的索引地址未发生变化。 

let tar = {...src };  //可以当做是src的浅拷贝

        这种拷贝赋值的方法效果与 Object.assign() 相同:

let src = { user: 'admin', password: '123456', job: { title: 'lawyer', salary: 1000 } }, tar={ tel: '110' };
Object.assign(tar, src);
src.password = '888888';
tar.user = 'user';
src.job.title = 'teacher';
tar.job.salary = 200;
console.log(src, tar);

        输出结果:

 6、箭头函数

        ES6之前,使用普通函数把数组每个字符串转换为大写形式:

const upperCaseStr = ['Hello', 'World'].map(function (src) {
    return src.toUpperCase();
});

        使用箭头函数:

const upperCase = ['Hello', 'World'].map(str => str.toUpperCase());

        通过对比可以发现代码书写上有了极大地简便。函数的参数只有一个,不需要使用 () 包起来,但是超过一个, 则必须需要将参数列表放在圆括号内:

const add = (a, b) => a + b;

        一般箭头函数都只有一个表达式作为函数主体,因此没有花括号 {} 且自动返回表达式,如果箭头函数内部需要多行代码,则需使用常规语法。按照常规语法上述箭头函数可按如下格式书写:

const upperCaseStr = ['Hello', 'World'].map(str => {
    return str.toUpperCase();
});
const add = (a, b) => {
    return a + b;
};

        箭头函数对this的影响:对于普通函数, this 的值基于函数如何被调用:

function Counter() {
    this.count = 0;
}
Counter.prototype.addCount = function () {
   setTimeout(function () {
        this.count++;
        console.log(`count: ${this.count}`);
    }, 100);
}

const counter = new Counter();
counter.addCount();             //输出 count: NaN

        传递给 setTimeout() 的函数被调用时没用到 new、call() 或 apply(),也没用到上下文对象。意味着函数内的 this 的值是全局对象,而不是 counter 对象。实际上发生的情况是,在setTimeout调用的函数内部创建了新的 count 变量(默认值为 undefined),然后自增(undefined + 1 结果为 NaN)。

        对于箭头函数,this 的值基于函数周围的上下文,this 的值和函数外面的 this 的值是一样的:

function Counter() {
    this.count = 0;
}
Counter.prototype.addCount = function () {
    setTimeout(() => {
        this.count++;
        console.log(`count: ${this.count}`);
    }, 100);
}
const counter = new Counter();
counter.addCount();             //输出 count: 1

        以上箭头函数中的 this 与外部的 this 值相同,都是指向调用的 counter 对象。

7、Promise

        Promise是ES6中提供异步编程的解决方案,相比传统的通过回调函数和事件的方案更具有优越性,可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数。

        Promise共有三种状态:Pending(进行中),Resolved(已完成又称为Fulfilled),Rejected(已失败)。先看一个简单的使用示例:

new Promise(
    function (resolve, reject) {
        let score = Math.round(Math.random() * 100);  //通常是耗时较长的异步操作。
        console.log(`score: ${score}`);
        if (score >= 60) {
            resolve('pass');
        } else {
            reject("fail");
        }
    }
).then(function resolveCallback(data) {
    console.log(`resolved data : ${data}`);
}, function rejectCallback(data) {
    console.log(`reject data : ${data}`);
});

        运行结果如下:

        new Promise() 构造时的函数 function(resolve, reject){} 用于处理异步操作。根据异步操作的结果通过调用 resolve() 和 reject() 设置 Promise 状态。then() 方法里第一个参数为处理 Resolved 状态的回调方法,第二个参数为处理 Rejected 状态的回调方法。

        Promise 允许只设置 Resolved 状态,对应 then() 方法中也只需一个处理 Resolved 状态的回调方法:

new Promise(
    (good) => {
        // 一些异步操作
        good('pass');
    }
).then((data) => {
    console.log(`resolved data : ${data}`);
});

         运行结果:

        Promise 的状态只能被设置一次,多次的设置不会生效:

new Promise(
    (pass, fail) => {
        // 一些异步操作 
        pass('pass');  // 设置状态为 Resolved  成功设置
        fail('fail');  // 设置状态为 Rejected  不能成功设置
    }
).then((data) => {
    console.log(`resolved data : ${data}`);
}, (data) => {
    console.log(`reject data : ${data}`);
});

         运行结果:

         Promise 提供了 Promise.all 和 Promise.race 方法来处理多个 Promise 的问题。

        Promise.all 将多个 Primise 实例合并成一个实例,只有当所有实例都变成 Resolved 状态才会执行回调:

const p1 = new Promise((get) => {
    setTimeout(() => {
        console.log('大王');
        get("大王");
    }, 200);
});
const p2 = new Promise((get) => {
    setTimeout(() => {
        console.log('小王');
        get();
    }, 100);
});
Promise.all([p1, p2]).then(data => {
    console.log(data);
    console.log(' 王炸!');
});

        运行结果:

        其中 then() 里边的 data 为 Promise 实例数组 [p1,p2] 对应传递的参数数组 [data1,data2],data1 为 '大王' ,p2 没有传递参数 data2 为 undefined。

        Promise.race 同样是将多个 Promise 实例包装成一个新的 Promise 实例, 多个实例中只要有一个实例状态改变,实例状态就改变了,返回的第一个改变的实例状态:

const pTimeout = new Promise((resolve) => {
    setTimeout(() => {
        console.log('pTimeout resolve');
        resolve({ code: 'TIMEOUT' });
    }, 100);
});
const pRequest = new Promise((resolve) => {
    setTimeout(() => {
        console.log('pRequest resolve');
        resolve({ code: 'OK', data: {} });
    }, 200);
});
Promise.race([p1, p2]).then(res => {
    if (res.code === 'OK') {
        console.log('request ok');
    } else {
        console.log('request timeout');
    }
});

         运行结果:

         以上代码模拟接口数据请求处理请求超时的情况,pTimeout 设置超时时间 100 ms, pRequest 模拟数据请求200ms后返回数据,两个异步操作 pTimeout先状态改变, 打包的 Promise实例跟着改变,打印请求超时, 如果 pRequest 先于 pTimeout 改变状态则打印请求成功。

8、async, await

        async 和 await 是用来处理异步的。即你需要异步像同步一样执行,需要异步返回结果之后,再往下依据结果继续执行。

        async 是“异步”的简写,而 await 可以认为是 async wait 的简写。async 用于申明一个 function 是异步的,而 await 用于等待一个异步方法执行完成。

        相比传统的回调方法和 Promise 写法,async-await 让我们书写代码时更加流畅,也增强了代码的可读性。

        传统回调方式处理多异步流程:

    const getUp = function (callback) {
            setTimeout(() => {
                console.log('get up at 7:00.')
                callback();
            }, 100);
        }
        const washFace = function (callback) {
            setTimeout(() => {
                console.log('wash face at 7:10.')
                callback();
            }, 100);
        }
        const brushTeeth = function (callback) {
            setTimeout(() => {
                console.log('brush teeth at 7:20.')
                callback();
            }, 100);
        }
        const haveBreakfast = function (callback) {
            setTimeout(() => {
                console.log('have breakfast at 7:30.')
                callback();
            }, 100);
        }
        const goToWork = function () {
            setTimeout(() => {
                console.log('go to work at 8:00.')
            }, 100);
        }
        const happyDay = function () {
            getUp(function () {
                washFace(function () {
                    brushTeeth(function () {
                        haveBreakfast(function () {
                            goToWork();
                        });
                    });
                });
            });
        }
        happyDay();

        运行结果:

 Promise 处理多异步流程:

const getUp = function () {
            return new Promise((resolve) => {
                setTimeout(() => {
                    console.log('get up at 7:00.')
                    resolve();
                }, 100);
            });
        }
        const washFace = function () {
            return new Promise((resolve) => {
                setTimeout(() => {
                    console.log('wash face at 7:10.')
                    resolve();
                }, 100);
            });
        }
        const brushTeeth = function () {
            return new Promise((resolve) => {
                setTimeout(() => {
                    console.log('brush teeth at 7:20.')
                    resolve();
                }, 100);
            });
        }
        const haveBreakfast = function () {
            return new Promise((resolve) => {
                setTimeout(() => {
                    console.log('have breakfast at 7:30.')
                    resolve();
                }, 100);
            });
        }
        const goToWork = function () {
            return new Promise((resolve) => {
                setTimeout(() => {
                    console.log('go to work at 8:00.')
                    resolve();
                }, 100);
            });
        }
        const happyDay = function () {
            getUp().then(washFace()).then(brushTeeth()).then(haveBreakfast()).then(goToWork());
        }
        happyDay();

        运行结果:

        注意:因为Promise在构造的时候就会被立即执行,所以以上代码采用 const p = function(){return new Promise()} 的方式进行定义,才能满足在需要调用时才执行的需求。

        async, await 处理多异步流程:

const getUp = function () {
            return new Promise((resolve) => {
                setTimeout(() => {
                    console.log('get up at 7:00.')
                    resolve();
                }, 100);
            });
        }
        const washFace = function () {
            return new Promise((resolve) => {
                setTimeout(() => {
                    console.log('wash face at 7:10.')
                    resolve();
                }, 100);
            });
        }
        const brushTeeth = function () {
            return new Promise((resolve) => {
                setTimeout(() => {
                    console.log('brush teeth at 7:20.')
                    resolve();
                }, 100);
            });
        }
        const haveBreakfast = function () {
            return new Promise((resolve) => {
                setTimeout(() => {
                    console.log('have breakfast at 7:30.')
                    resolve();
                }, 100);
            });
        }
        const goToWork = function () {
            return new Promise((resolve) => {
                setTimeout(() => {
                    console.log('go to work at 8:00.')
                    resolve();
                }, 100);
            });
        }
        const happyDay = async function () {
            await getUp();
            await washFace();
            await brushTeeth();
            await haveBreakfast();
            await goToWork();
        }
        happyDay();

        运行结果:

        对比一下代码方式,明显async, await的方式要更简洁易读。 

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

_老杨_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值