ES6/ES7/ES8常用新增语法

本文参考了阮一峰老师的ES6教程,部分示例代码来源于ECMAScript 6 入门。


块级作用域

ES6中针对块级作用域进行了一些规范化的定义。

  • 使用let关键字来创建块级作用域变量,该方式声明的变量只在 let 所在的代码块有效。

    • 比如for循环中的i,就可以使用 let 来声明,这样i只在for循环体内有效。

    • for循环中,每一轮循环,i都是一个新声明的变量,并且只在本次循环的代码块中生效。

    • 注意for循环中,设置循环变量的那部分是父作用域,循环体内部是一个单独的子作用域,因此在 for 循环的代码块中再次用 let 声明i会创建一个新的变量。

      for (let i = 0; i < 3; i++) {
        let i = 'abc';
        console.log(i);
      }
      // abc
      // abc
      // abc
      //输出了 3 次abc。这表明函数内部的变量i与循环变量i不在同一个作用域,有各自单独的作用域。
      
  • 使用 const 关键字来创建块级作用域常量,常量在创建后不能被重新赋值。

  • 不存在变量提升,如果在let声明之前使用或对该变量赋值,会报错ReferenceError

  • 不允许在相同作用域内,重复声明同一个变量。

    ES5规定中不允许在块级作用域中声明函数,但浏览器却可以这样做。而ES6中明确规定可以在块级作用域中声明函数,相当于let声明,同时为了兼容历史规则,允许浏览器有自己的行为。这样一来,在不同浏览器中,块级作用域声明函数的规则具有较大的差异。为了避免不必要的bug,我们在块级作用域中,要使用函数表达式,而不是函数声明语句。


模板字面量

模板字面量是允许嵌入表达式的字符串字面量,它是增强版的字符串,采用反引号(键盘上ESC下面的那个键)来标识。

  • 可以用模板字面量来创建多行字符串,空格和换行都会被视为字符串的一部分。

    let msg = `
    

    hello

    world

    `.trim(); console.log(msg); //输出 <h2>hello<h2> <h2>world<h2>

  • 在模板字面量中使用变量占位符${变量}

    let name = "Tom", message = `Hello, ${name}.`;
    console.log(message); // "Hello, Tom."
    
    //占位符可以是运算符或者函数调用等构成表达式
    function fn() {
      return "Hello World";
    }
    `foo ${fn()} bar` // foo Hello World bar
    
    let count = 10, price = 0.25,
    message = `${count} items cost $${(count * price).toFixed(2)}.`;
    console.log(message); // "10 items cost $2.50."
    

函数的扩展

函数的默认参数:

允许在调用时没有值或undefined被传入时使用指定的默认参数值。

function fun(name='world'){
  return 'hello ' + b;
}
fun(); //"hello world"

函数的rest参数:

延展运算符和一个变量名搭配使用,生成一个数组,用于获取函数多余的参数:

let sum = (a, b, ...m)=>{
  let total = 0;
  for(var i of m){
    total += i;
  }
  console.log(`total:${total}`);
}

sum(1,2,3,4); // total:7

箭头函数:

使用箭头函数可以创建语法更为简洁的函数。

箭头函数不会创建自己的this参数,它将继承使用执行上下文的this值。

const values = [0, 3, 2, 5, 7, 4, 8, 1];

//函数的参数为空或者不止一个时,必须使用括号
values.sort((v1, v2) => v1 - v2);
//当函数的语句不止一行时,必须给代码块加上大括号,并使用return语句返回
//等同于
values.sort((v1, v2) => {return v1 - v2;});

//如果直接返回一个对象,则需要给对象加上括号,否则会报错
let getTempItem = id => ({ id: id, name: "Temp" });

//箭头函数和变量解构一起使用
const full = ({ first, last }) => first + ' ' + last;
// 等同于
function full(person) {
  return person.first + ' ' + person.last;
}

变量解构赋值及扩展运算符

三点运算符 ... 的应用:rest参数及扩展运算符。

rest参数在上面已经演示过,下面作为扩展运算符使用:

数组的解构赋值:

let [a, b, c] = [1, 2, 3];

console.log(...[1,2,3]);
// 1 2 3

// 用来实现concat的效果
let arr1=[1,2]; let arr2=[3,4];
console.log(...[...arr1, ...arr2]);
// 1 2 3 4
let [m, ...n] = [1,2,3,4];
console.log(n); //Array(3)

let [a,b,c] = 'ES6';
console.log(a,b,c); //E S 6

//字符串转化为数组
let arr = [...'ES6'];
console.log(arr); //Array(3)

Promise使用

以前我们通过callback函数和事件来实现异步操作,层层嵌套,金字塔式,使代码非常臃肿。

ES6提供了Promise对象,里面保存着某个未来才会结束的事件,从它可以获取异步操作的消息。

这里只介绍一下常见用法,详情请看阮一峰ES6详解。

  • 一个利用Promise异步加载图片的例子:

    let loadImageAsync(url) {
      //返回一个Promise实例对象
      return new Promise(function(resolve, reject){
        const image = new Image();
        image.onload = function() {
          resolve(image);
          //resolve函数的作用是,将Promise对象的状态从“未完成”变为“成功”
          //在异步操作成功时调用,并将异步操作的结果,作为参数传递出去
        };
        image.onerror = function {
          reject(new Error('Could not load image at' + url));
          //reject函数的作用是,将Promise对象的状态从“未完成”变为“失败”
          //在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去
        };
        image.src = url;
      });
    };
    
    //使用then方法来为Promise实例添加状态改变时的回调函数
    //第一个参数是resolved状态的回调函数,第二个参数(可选)是rejected状态的回调函数。
    loadImageAsync(url).then((res) => {
      document.body.appendChild(res);
      //此处还可以return一个新的Promise,后面继续指定then
    }).catch((err) => {
      console.log("rejected:", err);
    });
    //catch用来捕获rejected状态,也可以把该回调写入then(resolve回调, rejected回调)
    
  • 如果使用then方法,依次指定了多个回调函数。第一个回调函数完成以后,会将返回结果作为参数,传入第二个回调函数。

  • 前一个回调函数可能返回一个Promise对象,后一个回调函数就会等待该Promise对象状态发生变化,才会被调用。

    例如:

    loadImageAsync(url_1).then((res)=>{
      return loadInfo(url_2);
    }).then(function funcA(resA) {
      console.log("resolved: ", resA);
    }, function funcB(err){
      console.log("rejected: ", err);
    });
    

    第一个then方法指定的回调函数,返回的是另一个Promise对象。这时,第二个then方法指定的回调函数,就会等待这个新的Promise对象状态发生变化。如果变为resolved,就调用funcA,如果状态变为rejected,就调用funcB

  • 如果要同时使用多个Promise,还可以用Promise.all():

    Promise.all([checkLogin(), getUserInfo()]).then(([res1, res2]) => {
      console.log(res1, res2);
    });
    

ES6的模块化

之前已经在模块化规范里详细介绍过:ES6模块化规范。


后续新增特性

ES6新增的内容很多,大部分常用的不再赘述,有一些容易混淆需注意的,如:

  • Object.keys()(ES5新增)

    • 返回对象所有的可枚举属性,不包括继承过来的属性或方法
    • 注意:如果是数组,遍历时返回的是索引属性(即0、1、2…)
    • for...in(非新增)是遍历一个对象的可枚举属性,包括继承过来的属性或方法
  • for...of(ES6新增)遍历可迭代对象定义要迭代的数据

    • 可迭代对象包括 Array,Map,Set,String,TypedArray,arguments 对象等等
    • 也就是说普通的对象是不能使用for...of
  • for…in和for…of的区别

ES6以后,基本每年发布一次新的ES,所以特性增加的较少。

ES7新增

  • Array.prototype.includes()判断数组是否包含指定值

  • 指数操作符 **

ES8新增

  • async/await 让异步函数写得更加流畅自然

  • Object.values()
    返回对象自身的所有属性的值的数组,不包括继承的值

  • Object.entries()
    返回一个给定对象自身可枚举属性的键值对的数组,包含的键值对也是数组形式
    不包括继承值

  • 函数参数后允许添加逗号
    主要是用于多人协作开发减少不必要的行变更

你可能感兴趣的:(JavaScript)