• ES6有何新特性?(下篇)


    目录

    函数参数的默认值设置

    rest参数

    扩展运算符

    Symbol

    迭代器

    生成器

    Promise

    Class

    数值扩展

    对象方法扩展

    模块化


    大家好呀!今天这篇文章继续为大家介绍ES6的新特性,上上上篇文章介绍了一部分,这篇文章会将剩下的部分新增的特性都介绍到。希望对你有帮助!

    函数参数的默认值设置

    ES6允许给函数参数赋值初始值,可以给形参赋初始的值,并且可以给参数指定默认的值,若给参数没有传入相应的值,则会使用默认的值。

    1. function add(a,b,c){
    2. return a+b+c;
    3. }
    4. let result=add(1,2);
    5. console.log(result)//NaN

    以上的这种情况,传入的数值与形参的数量不一样,则最后的一个形参则会传入underfine,因此1+2+underfine则会输出NaN。同时我们可以为第三个形参赋默认的值,具有默认值的参数一般都需要放在最后一个。具体代码如下:

    1. // 形参初始值
    2. function add(a,b,c=10){
    3. return a+b+c;
    4. }
    5. let result=add(1,2);
    6. console.log(result)//13

    除此之外,它还可以与解构赋值进行结合。可以使得获取传入的值更加方便。比如我们为一个函数传入一个对象,在函数的内部输出对象中的属性,我们传统的方式可以如下:

    1. function connect(option){
    2. console.log(option.name)
    3. console.log(option.age)
    4. console.log(option.sex)
    5. }
    6. connect({
    7. name:'N-A',
    8. age:5,
    9. sex:"男"
    10. })

    以上的方式会让我们觉得比较地麻烦,需要反复地使用option来进行调用,若使用函数参数结合对象解构赋值的方式来实现,则会更加地方便,具体实现如下:(同时也可以与上述的功能一样,为参数赋初始的值)

    1. function connect({name,age,sex}){
    2. console.log(name)
    3. console.log(age)
    4. console.log(sex)
    5. }
    6. connect({
    7. name:'N-A',
    8. age:5,
    9. sex:"男"
    10. })

    rest参数

    rest参数主要是用来获取函数的实参。它可以用来替换arguments,与arguments的作用相似。在ES5中使用arguments来获取函数的实参的方法如下:

    1. function date(){
    2. console.log(arguments)
    3. }
    4. date("N-A","5","男")

    输出的结果是一个对象,而使用ES6新增的rest参数输出的结果是一个数组,因此数组更加方便我们对其进行一定的操作。具体的写法如下:

    1. // rest参数
    2. function date(...args){
    3. console.log(args)
    4. }
    5. date("N-A","5","男")
    6. // 若有多个参数,则rest参数必须要放到参数的最后
    7. function fn(a,b,...args){
    8. console.log(a);//1
    9. console.log(b);//2
    10. console.log(args);//[3,4,5,6,7,8]
    11. }
    12. fn(1,2,3,4,5,6,7,8)

    注意:若有多个参数,则rest参数必须放到参数的最后。

    扩展运算符

    扩展运算符能将数组转为逗号分隔的参数序列。他的使用方式也是使用...与rest参数优点相似,但是他们一个是在函数声明时的参数使用,一个是在函数调用时使用。具体用法如下:

    1. const data=[1,2,3,4]
    2. function result(){
    3. console.log(arguments);
    4. }
    5. result(...data);//相当于result(1,2,3,4)

    扩展运算符还有以下的一些用途,数组的合并,在ES5中我们使用concat可以实现数组的合并,在ES6中使用拓展运算符同样可以实现数组的合并,且更加的方便。

    1. const data1=[1,2,3,4]
    2. const data2=[5,6,7,8]
    3. // 传统的方式
    4. const result=data1.concat(data2)//[1,2,3,4,5,6,7,8]
    5. // 扩展运算符
    6. const result2=[...data1,...data2]//[1,2,3,4,5,6,7,8]

    除此之外,扩展运算符还可以实现数组的克隆,但是使用它进行克隆是浅拷贝的克隆。也可以让伪数组变成真正的数组。

    1. // 数组的克隆
    2. const data1=[1,2,3,4]
    3. const data2=[...data1]
    4. console.log(data2)//[1,2,3,4]
    5. // 将伪数组变成真数组
    6. // html部分代码省略
    7. const divs=document.querySelectorAll("div")
    8. const divArr=[...divs];
    9. console.log(divArr)

    Symbol

    ES6 引入了一种新的原始数据类型 Symbol,表示独一无二的值。它是JavaScript 语言的第七种数据类型,是一种类似于字符串的数据型。

    1) Symbol 的值是唯一的,用来解决命名冲突的问题

    2) Symbol 值不能与其他数据进行运算

    3) Symbol 定义 的 对象属 性 不能 使 用 for…in 循 环遍 历 ,但 是可 以 使 用Reflect.ownKeys 来获取对象的所有键名

    1. // 创建Symbol
    2. let s=Symbol();
    3. let s2=Symbol("N-A");
    4. let s3=Symbol("N-A");
    5. console.log(s2==s3);//false
    6. // 使用Symbol.for创=创建
    7. let s4=Symbol.for('N-A');
    8. let s5=Symbol.for('N-A');
    9. console.log(s4==s5);//true

    它可以为对象添加属性和方法,让该属性或者方法是独一无二的,就算原本的对象中有一个同名的方法,它也能够进行添加成功。同时也能够保持其安全性,不会破坏对象原本的属性和方法。

    1. let plan={
    2. name:"N-A",
    3. study(){
    4. console.log("今天学习ES6");
    5. },
    6. sport(){
    7. console.log("今天去游泳");
    8. }
    9. };
    10. // 声明一个对象
    11. let methods={
    12. study:Symbol(),
    13. sport:Symbol()
    14. };
    15. plan[methods.study]=function(){
    16. console.log("今天学习Vue.js")
    17. };
    18. plan[methods.sport]=function(){
    19. console.log("今天去跑步")
    20. };
    21. console.log(plan);
    22. // 方法二
    23. let plan={
    24. name:"N-A",
    25. [Symbol('study')]:function(){
    26. console.log("今天学习ES6");
    27. },
    28. [Symbol('sport')]:function(){
    29. console.log("今天去游泳");
    30. }
    31. };

    除了定义自己使用的 Symbol 值以外,ES6 还提供了 11 个内置的 Symbol 值,指向语言内部使用的方法。可以称这些方法为魔术方法,因为它们会在特定的场景下自动执行。

    以第二个例子,其使用方式如下:

    1. const arr=[1,2,3,4];
    2. const arr2=[5,6,7,8];
    3. // 不可展开
    4. arr2[Symbol.isConcatSpreadable]=false;
    5. console.log(arr.concat(arr2))//[1, 2, 3, 4, Array(4)]

    迭代器

    迭代器是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署 Iterator 接口,就可以完成遍历操作。

    ES6 创造了一种新的遍历命令 for...of 循环,Iterator 接口主要供 for...of 消费。原生具备 iterator 接口的数据(可用 for of 遍历)有:Array、Arguments、Set、Map、String、TypedArray、NodeList。

    其工作原理如下:首先创建一个指针对象,指向当前数据结构的起始位置。第一次调用对象的 next 方法,指针自动指向数据结构的第一个成员。接下来不断调用 next 方法,指针一直往后移动,直到指向最后一个成员。每调用 next 方法返回一个包含 value 和 done 属性的对象。

    1. const study=['ES6','Vue','Webpack','CSS'];
    2. // 使用for...of遍历数组
    3. for(let item of study){
    4. console.log(item)
    5. }
    6. // 原理
    7. let iterator=study[Symbol.iterator]();
    8. // 调用对象的next方法
    9. console.log(iterator.next());//{value: 'ES6', done: false}
    10. console.log(iterator.next());//{value: 'Vue', done: false}
    11. console.log(iterator.next());//{value: 'Webpack', done: false}
    12. console.log(iterator.next());//{value: 'CSS', done: false}
    13. console.log(iterator.next());//{value: undefined, done: true}

    它可以用于自定义遍历数据,以一个实例来进行演示,假如有一个对象,对象中有一个两个属性,一个为name,一个为study。study为一个数组,现在需要使用for...of来进行遍历(不考虑其他方式),该如何实现。

    1. const plan={
    2. name:'N-A',
    3. study:[
    4. 'ES6',
    5. 'Webpack',
    6. 'Vue',
    7. 'CSS'
    8. ],
    9. [Symbol.iterator](){
    10. // 声明索引变量
    11. let index=0;
    12. return{
    13. next:()=>{
    14. if(index<this.study.length){
    15. const result={value:this.study[index],done:false};
    16. index++;
    17. return result;
    18. }else{
    19. return {value:undefined,done:true}
    20. }
    21. }
    22. }
    23. }
    24. }
    25. for(let item of plan){
    26. console.log(item)
    27. }

    生成器

    生成器函数是 ES6 提供的一种异步编程解决方案,语法行为与传统函数完全不同。

    1. function* gen() {
    2. yield '一只没有耳朵';
    3. yield '一只没有尾巴';
    4. yield '真奇怪';
    5. }
    6. let iterator = gen();
    7. console.log(iterator.next());//{value: '一只没有耳朵', done: false}
    8. console.log(iterator.next());//{value: '一只没有尾巴', done: false}
    9. console.log(iterator.next());//{value: '真奇怪', done: false}
    10. console.log(iterator.next());//{value: undefined, done: true}

    以上的代码* 的位置没有限制,可以偏左偏右或者处于中间。生成器函数返回的结果是迭代器对象,调用迭代器对象的 next 方法可以得到yield 语句后的值。yield 相当于函数的暂停标记,也可以认为是函数的分隔符,每调用一次 next方法,执行一段代码。next 方法可以传递实参,作为 yield 语句的返回值。

    1. function * gen(arg){
    2. console.log(arg);//AAAA
    3. let one=yield 111;
    4. console.log(one);//BBB
    5. let two=yield 222;
    6. console.log(two);;//CCC
    7. let three=yield 333;
    8. console.log(three);//DDD
    9. }
    10. let iterator=gen('AAAA');
    11. console.log(iterator.next());
    12. // next方法传入实参
    13. console.log(iterator.next('BBB'));
    14. console.log(iterator.next('CCC'));
    15. console.log(iterator.next('DDD'));

    同时还可以在next()中传入实参,传入的数据会作为上一次yield语句的返回结果。

    应用实例如下:现在我们需要实现1s之后在控制台打印111,然后再过2s之后在控制台打印222,在过3s之后在打印333。可以使用传统的函数回调进行实现,但是代码的可读性以及维护性并不好,同时也会存在回调地狱的问题,因此使用生成器来实现具体如下:

    1. function one(){
    2. setTimeout(()=>{
    3. console.log(111);
    4. iterator.next();
    5. },1000)
    6. }
    7. function two(){
    8. setTimeout(()=>{
    9. console.log(222);
    10. iterator.next();
    11. },2000)
    12. }
    13. function three(){
    14. setTimeout(()=>{
    15. console.log(333);
    16. iterator.next();
    17. },3000)
    18. }
    19. function * gen(){
    20. yield one();
    21. yield two();
    22. yield three();
    23. }
    24. // 调用生成器函数
    25. let iterator=gen();
    26. iterator.next();

    Promise

    Promise 是 ES6 引入的异步编程的新解决方案。语法上 Promise 是一个构造函数,用来封装异步操作并可以获取其成功或失败的结果。

    1. // 实例化Promise对象
    2. const p=new Promise(function(resolve,reject){
    3. setTimeout(function(){
    4. let data='数据库中的数据';
    5. // 调用resolve,让其结果为成功,让p实例的状态为成功,会执行then方法中成功的回调。
    6. // resolve(data);
    7. // 让其状态为失败
    8. let err='请求失败';
    9. reject(err);
    10. })
    11. });
    12. // 调用promise对象的then()方法
    13. p.then(function(value){
    14. console.log(value);
    15. },function(reason){
    16. console.error(reason);
    17. })

    首先需要实例化Promise对象,Promise()接受有个参数,该参数为一个函数,函数有两个参数:resolve以及reject。在后续调用resolve时,promise对象的状态就会变成成功,reject则会失败。当对象的状态发生改变时,就会调用then方法,该方法有两个函数,成功的回调函数以及失败的回调函数。若promise对象的状态为成功则执行成功的对调,否则执行失败的回调。

    使用promise封装ajax请求具体实现如下:

    1. const p = new Promise((resolve, reject)=>{
    2. const xhr = new XMLHttpRequest();
    3. xhr.open("GET", "https://api.apiopen.top/getJoke");
    4. xhr.send();
    5. xhr.onreadystatechange = function () {
    6. if (xhr.readyState === 4) {
    7. if (xhr.status >= 200 && xhr.status < 300) {
    8. resolve(xhr.response);
    9. } else {
    10. reject(xhr.status);
    11. }
    12. }
    13. }
    14. })
    15. p.then(function(value){
    16. console.log(value);
    17. },function(reason){
    18. console.error(reason);
    19. })

    Promise中的then方法,返回的结果是一个Promise对象,对象的状态由回调函数的执行结果决定。如果回调函数中返回的结果是非promise类型的属性,状态为成功,返回的值为对象的成功的值。若没有return返回结果,则同样是成功的状态,只是对应的值为undefined。

    1. const p = new Promise((resolve, reject) => {
    2. setTimeout(() => {
    3. resolve("用户数据");
    4. }, 1000)
    5. });
    6. const result = p.then(value => {
    7. console.log(value);
    8. return 1234;
    9. }, reason => {
    10. console.error(reason);
    11. });
    12. console.log(result);//[[PromiseState]]: "fulfilled" (代表成功) [[PromiseResult]]:1234

    若返回的是一个promise对象,then返回的状态则为return中promise对象的状态,其值也是对应的内部return中promise的值。

    1. const p = new Promise((resolve, reject) => {
    2. setTimeout(() => {
    3. resolve("用户数据");
    4. }, 1000)
    5. });
    6. const result = p.then(value => {
    7. console.log(value);
    8. return new Promise((resolve,reject)=>{
    9. resolve('ok');
    10. });
    11. }, reason => {
    12. console.error(reason);
    13. });
    14. console.log(result);//[[PromiseState]]: "fulfilled" (代表成功) [[PromiseResult]]:ok

    若是抛出错误,则then返回的promise对象的状态为失败的状态,对应的值为抛出的值。

    1. const p = new Promise((resolve, reject) => {
    2. setTimeout(() => {
    3. resolve("用户数据");
    4. }, 1000)
    5. });
    6. const result = p.then(value => {
    7. console.log(value);
    8. throw new Error('出错啦!')
    9. }, reason => {
    10. console.error(reason);
    11. });
    12. console.log(result);//[[PromiseState]]: "rejected" [[PromiseResult]]:Error: 出错啦!

    promise中的catch方法,相当于是then方法中的第二个回调函数,promise实例的状态失败时执行的方法。

    1. const p = new Promise((resolve, reject) => {
    2. setTimeout(() => {
    3. reject("出错啦!");
    4. }, 1000)
    5. });
    6. p.catch(function(reason){
    7. console.log(reason);
    8. })

    Class

    ES6 提供了更接近传统语言的写法,引入了 Class(类)这个概念,作为对象的模板。通过 class 关键字,可以定义类。基本上,ES6 的 class 可以看作只是一个语法糖,它的绝大部分功能,ES5 都可以做到,新的 class 写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已。

    1. class Phone{
    2. constructor(band,price){
    3. this.band=band;
    4. this.price=price;
    5. }
    6. call(){
    7. console.log("我可以电话!!")
    8. }
    9. }
    10. let type=new Phone("Huawei",5999);

    class中的静态成员,使用static声明的属性以及方法属于类,并不属于实例对象。因此使用实例对象无法访问。具体代码如下:

    1. class Phone{
    2. // 声明静态属性
    3. static name='手机';
    4. static change(){
    5. console.log("我可以改变世界");
    6. }
    7. }
    8. let type=new Phone();
    9. console.log(type.name);//undefined
    10. console.log(Phone.name)//手机

    同时还可以使用calss来实现继承,具体代码如下。子类可以对父类的方法进行重写,但是无法使用super来调用父类同名的方法。

    1. class Phone{
    2. constructor(brand,price){
    3. this.brand=brand;
    4. this.price=price;
    5. }
    6. call(){
    7. console.log("我可以打电话");
    8. }
    9. }
    10. class SmartPhone extends Phone{
    11. constructor(brand,price,color){
    12. super(brand,price);//相当于Phone.call(this,brand,price)
    13. this.color=color;
    14. }
    15. phone(){
    16. console.log("拍照")
    17. }
    18. }
    19. const type=new SmartPhone('小米',2987,'白色');

    数值扩展

    1.Number.EPSILON是js表示的最小进度。若两个数的差值小于它,就认为这两个数相等。

    2.ES6 提供了二进制和八进制数值的新的写法,分别用前缀 0b 和 0o 表示。

    3.Number.isFinite() 用来检查一个数值是否为有限的。Number.isNaN() 用来检查一个值是否为 NaN。

    4.Number.parseInt() 与 Number.parseFloat() 。用于将字符串转为整数或者浮点数,若字符串中包含不能转化的字符,则不会在往后转换。

    5.Math.trunc,用于去除一个数的小数部分,返回整数部分。

    6.Number.isInteger() 用来判断一个数值是否为整数。

    7.Math.sign,用于检测一个数为正数,负数或者为零。若为正数则会返回1,若为零则会返回0 ,若会负数则会返回-1。

    对象方法扩展

    1) Object.is 比较两个值是否严格相等,与===行为基本一致,主要区别在于Object.is判断两个NaN是返回的是true,而使用===返回的是false。

    2) Object.assign 对象的合并,将源对象的所有可枚举属性,复制到目标对象。后面的对象会覆盖前面对象中具有同名的属性。若不同名则会合并。

    3) Object.setPrototypeOf、 Object.getPrototypeOf 可以直接设置对象的原型,以及获取对象的原型。

    模块化

    模块化是指将一个大的程序文件,拆分成许多小的文件,然后将小文件组合起来。它可以防止命名冲, 代码复用以及使得代码的维护性更高。

    ES6 之前的模块化规范有:1) CommonJS => NodeJS、Browserify 2) AMD => requireJS 3) CMD => seaJS

    模块功能主要由两个命令构成:export 和 import。export 命令用于规定模块的对外接口。 import 命令用于输入其他模块提供的功能。暴露的方式如下

    方法一:分别暴露,编写一个index.js文件,代码如下:

    1. //分别暴露
    2. export let name='N-A';
    3. export function study(){
    4. console.log("学习ES6新特性")
    5. }

    在主文件中使用通用的引入方式。

    方法二:统一暴露,编写一个index.js文件,在主文件中进行引入,引入方法同上。代码如下:

    1. //统一暴露
    2. let name='N-A';
    3. function study(){
    4. console.log("学习ES6新特性")
    5. }
    6. export {name,study};

    方式三:默认暴露,编写一个index.js文件,引入方法同上。代码如下:

    1. //默认暴露
    2. export default{
    3. let name='N-A';
    4. function study(){
    5. console.log("学习ES6新特性")
    6. }
    7. }

    如果使用该方法进行编写的话,在主文件中想要获取到相应的方法或者属性需要再增加一层default进行调用。

    导入文件的方式除了上面提到的通用方法之外,还可以使用如下的方法。

    1. //解构赋值形式
    2. import {name,study} from "./src/js/index.js";
    3. //若名字重复现需要其别名
    4. import {name as ming,sport} from "./src/js/index2.js";
    5. import {default as def} from "./src/js/index.js";
    6. //简便形式,只能用于默认暴露的方式
    7. import m from './src/js/index.js'

    若引入的文件较多,都写在script标签中会使代码编写不简洁,因此可以单独地创建一个js文件来编写文件的import引入,然后再在主文件中的script标签通过src属性进行引入,然后script标签还需要增加一个type等于“module”的属性。

  • 相关阅读:
    (附源码)springboot手工diy网站 毕业设计 310226
    【FI】财务预制凭证界面隐藏过账按钮
    关于编辑器QScintilla(Scintilla)词法分析器工作原理的分析(实现注释区分)
    DEFORMABLE DETR:用于端到端对象检测的可变形Transformer
    rsync远程同步
    C#值传递与引用传递的区别和用法
    dotnet命令
    (SpringBoot)第七章:SpringBoot日志文件
    运筹说 第79期|论文速读之双目标岛屿旅行商问题
    深入理解Java虚拟机读书笔记--5 JVM工具
  • 原文地址:https://blog.csdn.net/weixin_51735748/article/details/134522615