关于ES6的介绍及使用

  • Post author:
  • Post category:其他



本章知识梳理图

ECMAScript 6.0(以下简称 ES6)是 JavaScript 语言的下一代标准,已经在 2015 年 6 月正式发布了。它的目标,是使得 JavaScript 语言可以用来编写复杂的大型应用程序,成为企业级开发语言。

1.ECMAScript 和 JavaScript 的关系



ECMAScript 和 JavaScript 到底是什么关系?

1996 年 11 月,JavaScript 的创造者 Netscape 公司,决定将 JavaScript 提交给标准化组织 ECMA,希望这种语言能够成为国际标准。次年,ECMA 发布 262 号标准文件(ECMA-262)的第一版,规定了浏览器脚本语言的标准,并将这种语言称为 ECMAScript,这个版本就是 1.0 版。

该标准从一开始就是针对 JavaScript 语言制定的,但是之所以不叫 JavaScript,有两个原因。一是商标,Java 是 Sun 公司的商标,根据授权协议,只有 Netscape 公司可以合法地使用 JavaScript 这个名字,且 JavaScript 本身也已经被 Netscape 公司注册为商标。二是想体现这门语言的制定者是 ECMA,不是 Netscape,这样有利于保证这门语言的开放性和中立性。

因此,

ECMAScript 和 JavaScript 的关系是:

前者是后者的规格,后者是前者的一种实现。

2.ES6基本语法

ES标准中不包含 DOM 和 BOM的定义,只涵盖基本数据类型、关键字、语句、运算符、内建对象、内建函数等通用语法。

任务一:let和const命令


一、let命令

ES6新增了let命令,用来声明变量。它的用法类似于var,但其声明的变量只在let命令所在的代码块内有效。

1.let命令作用域只局限于当前代码块


ES6新增了let命令,用来声明变量。它的用法类似于var,但其声明的变量只在let命令所在的代码块内有效。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>let命令作用域</title>
</head>
<body>
    
</body>
</html>
<script>
    {
        let a=10;
        var b=1;
    }
    console.log(a);//ReferenceError: a is not defined
    console.log(b);
</script>

2.使用let声明的变量作用域不会被提前


var命令支持“变量提升”,即变量可以在声明之前使用,值为undefined。let命令不支持“变量提升”,声明的变量一定要在声明之后才可以使用,否则会报错。

//var的情况
    console.log(foo);
    var foo=2;
//let的情况,会报错
    console.log(bar);
    let bar=2;

3.在相同的作用域下不能声明相同的变量


let命令不允许在相同作用域内重复声明同一个变量。也不能在函数内部重复声明参数,声明的参数也不能与形参同名,如果声明的参数是在另一个作用域下,则可以进行重复声明。

//会报错
    function func(){
        let a=10;
        var a=1;
    }
//报错
    function func(){
        let a=10;
        let a=1;
    }
//报错
    function func(arg){
        let arg;
    }
//不报错
    function func(arg){
        {
            let arg;
        }
    }

4.for循环体中let的父子作用域

有5个按钮,当单击某个按钮时,控制台可以打印输出当前单机的时第几个按钮,按照常规实现思路,代码实现如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>for循环体中let命令</title>
</head>
<body>
   <button>按钮</button> 
   <button>按钮</button> 
   <button>按钮</button> 
   <button>按钮</button> 
   <button>按钮</button> 
</body>
</html>
<script>
   var btns=document.querySelectorAll('button');
   for(var i=0;i < btns.length;i++){
       btns[i].onclick=function(){
           console.log("这是第"+i+"个按钮");
       }
   }
</script>


运行效果图:


无论单击哪一个按钮,最后打印的都是“这是第5个按钮”。因为for是同步操作,直接将i++到i=5,而for循环内部的函数执行的是异步操作,当函数执行的时候找不到i的值,就会往上面的作用域查找i的值为5。


上机训练1——let命令使用:解决上面按钮例子,最终实现页面效果图如下:



实现代码:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>for循环体中let命令</title>
</head>
<body>
   <button>按钮</button> 
   <button>按钮</button> 
   <button>按钮</button> 
   <button>按钮</button> 
   <button>按钮</button> 
</body>
</html>
<script>
   var btns=document.querySelectorAll('button');
   for(let i=0;i < btns.length;i++){
       btns[i].onclick=function(){
           console.log("这是第"+i+"个按钮");
       }
   }
</script>

二、const命令

基本用法



1.const


声明一个只读的常量。一旦声明,常量的值就不能改变。

const PI = 3.1415;
PI // 3.1415

PI = 3;
// TypeError: Assignment to constant variable.

2.


const


一旦声明变量,就必须立即初始化,不能留到以后赋值。

const foo;
// SyntaxError: Missing initializer in const declaration

3.


const


的作用域与


let


命令相同:只在声明所在的块级作用域内有效。

if (true) {
  const MAX = 5;
}

MAX // Uncaught ReferenceError: MAX is not defined

4.


const


命令声明的常量也是不提升,同样存在暂时性死区,只能在声明的位置后面使用。

下面代码在常量

MAX

声明之前就调用,结果报错。

if (true) {
  console.log(MAX); // ReferenceError
  const MAX = 5;
}

5.


const


声明的常量,也与


let


一样不可重复声明。

var message = "Hello!";
let age = 25;

// 以下两行都会报错
const message = "Goodbye!";
const age = 30;

6.const定义复合类型的数据(对象或数组)时,常量指向的内存地址保存的只是一个指针。

   const foo={};
   //为foo添加一个属性,可以成功
   foo.prop=123;
   console.log(foo.prop);
   //将foo指向另一个对象,会报错
   foo={};



常量foo存储的是一个地址,这个地址指向一个对象。地址不可更改,对象本身内容可以改变。

   const a=[];
   a.push('Hello');  //可执行
   a.length=0;  //可执行
   a=['Dave'];  //报错



常量a是一个数组,数组本身是可以更改,但是将另一个数组赋值给a,修改了地址,就会报错。



小结:



let、const命令的使用场景:const一般在需要一个模块的时候使用或者定义一些全局常量时用。




而let限制了变量的作用域,保证变量不会去影响全局变量,所以尽量将var改为let。


任务二:变量的解构赋值



一、数组的解构赋值

ES6 允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为

解构

(Destructuring)。以前,为变量赋值,只能直接指定值。

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


ES6 允许写成下面这样:

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


本质上,这种写法属于“

模式匹配

”,只要等号两边的模式相同,左边的变量就会被赋予对应的值。



不完全解构赋值:

   let[x,y]=['a'];
   console.log(x);  //a
   console.log(y);  //undefined



数组解构赋值:

var arr = [1,2,3];

// 传统的js
let a = arr[0];
let b = arr[1];
let c = arr[2];
console.log(a,b,c);

// es6的解构
var [x,y,z] = arr;
console.log(x,y,z);


总结:

  • 解构赋值是对赋值运算符的扩展。
  • 它针对

    数组

    或者

    对象

    进行模式匹配,然后对其中的变量进行赋值。
  • 解构,顾名思义,就是将集合型数据进行分解,拆分,把里面的值逐一遍历获取。
  • 在代码书写上简洁且易读,语义更加清晰明了;也方便了复杂对象中数据字段的获取。




二、对象的解构赋值



解构不仅可以用于数组,还可以用于对象。


对象


的解构与数组的解构有一个重要的不同:



数组



的元素是按次序排列的,变量的取值由它的位置决定;而



对象



的属性没有次序,变量必须与属性同名,才能取到正确的值。

var user = {
    username : "吕布",
    weapon:"方天画戟",
    horse:"赤兔马"
};

// 传统的js
let mingzi = user.username;
let wuqi = user.weapon;
let zuoji = user.horse;
console.log("姓名:"+mingzi+",武器:"+wuqi+",坐骑:"+zuoji);

// es6的解构
let {username,weapon,horse} = user;  // 注意:解构的变量名必须是对象中的属性
console.log("姓名:"+username+",武器:"+weapon+",坐骑:"+horse);



对象的解构赋值:

let {bar,foo}={foo:"aaa",bar:"bbb"}
console.log(foo); //aaa
console.log(bar);  //bbb

let {baz}={foo:"aaa",bar:"bbb"}
console.log(baz);  //赋值失败,没有对应的属性名,变量的值等于undefined



对象的解构赋值简写:

let obj={first:'hello',last:'world'};
let{first:f,last:l}=obj;
console.log(f); //hello
console.log(l); //world



等同于:


let{first:f,last:l}={first:’hello’,last:’world’}


上机训练2——对象的解构赋值使用


需求

:假设有一段Json对象数据,利用对象的解构赋值来快速提取JSON数据的值。

var jsonData={
    id:42,
    status:"OK",
    data:[123,542]
};



实现代码:

var jsonData={
    id:42,
    status:"OK",
    data:[123,542]
};
let {id,status,data}=jsonData;
console.log(id);
console.log(status);
console.log(data);


任务三:使用箭头函数


1.箭头函数的起因


示例代码:

const Person={
        username:"小暖",
        age:18,
        sayHello:function(){
            setInterval(function(){
                console.log("我叫"+this.username+"我今年"+this.age+"岁")
            },1000)
        }
    };
    Person.sayHello();


没有输出“我叫小暖我今年18岁”的原因:

setInterval函数是在全局作用域下执行的(

对象的花括号是不能封闭作用域的

),所以此时this指向的是全局window,window对象没有username、age,所以输出的是undefined。



是否可以用箭头函数来解决这个问题??




2.箭头函数的定义


ES6使用 => 箭头 定义函数

2.1.函数没有参数


传统函数写法:

   var say=function(){

        console.log("Hello World!");
    }


箭头函数写法

:当花括号里面只有一句话时可以去掉花括号。

  let say=()=>{

        console.log("Hello World!");
    }

    let say=()=>console.log("Hello World!");

    say();

2.2 函数有1个参数


传统函数写法:

  var hello=function(name){

        console.log("Hello,"+name);
    }
    hello("小莉");


箭头函数写法:

只有一个参数时,可以去掉圆括号

   let hello=name=>console.log("Hello,"+name);

    hello("小莉");

2.3函数有多个参数


传统写法:

var sum=function(a,b,c){
        console.log(a+b+c);
    }


箭头函数写法:

let sum=(a,b,c)=>console.log(a+b+c);
sum(1,2,3);

3.箭头函数和普通函数的区别

3.1不绑定this

箭头函数的this在定义的时候就确定了,箭头函数的this是静态的,始终指向函数声明时所在作用域下的this的值,以后不管如何调用箭头函数,箭头函数的this始终为定义时的this。

const Person={
        username:"小暖",
        age:18,
        sayHello:function(){
            setInterval(()=>{
                console.log("我叫"+this.username+"我今年"+this.age+"岁")
            },1000)
        }
    };



效果图:


解决了开头的问题

3.2不绑定arguments

例如  打印arguments参数的长度,会报错。

传统函数:

let arrowfunc=function(){console.log(arguments.length)}
arrowfunc();

箭头函数:

let arrowfunc=()=>console.log(arguments.length);  
arrowfunc(); //报错

4.箭头函数不适用场景

4.1 不建议在对象的方法中使用

在对象的方法中不建议使用箭头函数。例如:

const Person={
        username:"小暖",
        age:18,
        sayHello:()=>{
            setInterval(()=>{
                console.log("我叫"+this.username+"我今年"+this.age+"岁")
            },1000)
        }
    };
    Person.sayHello();



原因:


因为方法写在了对象里面,而对象的括号是不能封闭作用域的,所以此时的this还是指向全局作用域对象window,而全局对象没有username和age属性。

4.2 不能作为构造函数

由于箭头函数的this具有


不绑定


的特点,不能使用箭头函数作为构造函数,否则会报错。

传统构造函数:

 let Person=function(name,age){
        this.name=name;
        this.age=age;
    }

箭头函数进行构造:


报错!

 let Person=(name,age)=>{
        this.name=name;
        this.age=age;
    }
 let p1=new Person("夏明",23);
 console.log("我叫"+p1.name+",今年"+p1.age);

4.3 定义原型方法时不建议使用

function Person(){
        this.username="小暖"
    }
Person.prototype.sayHello=()=>{
        console.log(this.username)
    }
var p1=new Person();
p1.sayHello();



运行效果图:




总结:


箭头函数由于代码的简洁性和不绑定调用者this的特点,建议在非方法函数中使用,而在方法函数中使用,需要特别注意它的this绑定问题,如果需要动态地修改this,建议不要使用箭头函数。

任务四:Map数据结构


ES6 提供了 Map 数据结构。它类似于对象,也是键值对的集合。但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。

1.如何创建Map

创建一个空Map:

const map=new Map();

创建一个非空Map:

const map=new Map([
        ['name','张三'],
        ['title','Author']
    ]);

2.Map常用属性及方法

2.1 size属性:返回Map结构的成员总数

console.log(map.size); 

2.2 set方法和get方法


set方法:

增加一个新元素,设置键名key对应的键值value,返回当前 Map。

map.set('friends',['大花','小花']);
//键可以为对象类型
let key={
        school:'咸宁职业技术学院'
        };
map.set(key,'咸宁市')


get方法:

读取key对应的键值,如果找不到key,则返回undefined。

 const map=new Map([
        ['name','张三'],
        ['title','Author']
    ]);
console.log(map.get('name'));//张三
console.log(map.get('sex'));  //undefined

2.3 has方法:返回一个布尔值,判断某个键是否在当前Map对象中。

const map=new Map([
        ['name','张三'],
        ['title','Author']
    ]);
console.log(map.has('title'));   //true
console.log(map.has('year'));   //false

2.4 delete方法:删除某个键,如果删除成功返回true,否则返回false。

const map=new Map([
        ['name','张三'],
        ['title','Author']
    ]);
console.log(map.delete('title'));  //true

2.5 遍历方法

  • keys() :返回键名的遍历器;
  • values():返回键值的遍历器;
  • entries():返回所有成员的遍历器;
  • forEach()遍历Map所有的成员;
 const map=new Map([
        ['name','张三'],
        ['title','Author']
    ]);
// keys():返回键名的遍历器
    for (let key of map.keys()){
        console.log(key);
    }
// values():返回键值的遍历器
    for (let value of map.values()){
        console.log(value);
    }
// entries():返回所有成员的遍历器
    for (let[k,v] of map.entries()){
        console.log(k,v);
    }
// forEach() 遍历Map的所有成员
    map.forEach(function(value,index){
        console.log(index+":"+value);
    })

任务五:Module(模块化)的语法


模块化是指将一个大的程序文件,拆分成许多小的文件,然后将小文件组合起来。


模块化的优势有以下几点:


1. 防止命名冲突;

2. 代码复用;

3. 高维护性;


模块功能主要由两个命令构成:

export





import




export 命令用于规定模块的对外接口(

导出模块

);

import 命令用于输入其他模块提供的功能(

导入模块

);

1.export命令

一个模块就是一个独立的文件,文件内部的所有变量外部无法获取。如果希望外部能够读取模块内部的某个变量,就必须使用export关键字输出该变量。

1.1 输出变量


第一种写法:

export var firstName='Michael';
export var lastName='Jackson';
export var year=1958;


第二种写法:

var firstName='Michael';
var lastName='Jackson';
var year=1958;
export {firstName,lastName,year};

1.2 输出函数


输出单个函数:

export function multiply(x,y){
    return x*y;
  };


输出多个函数:

function v1(){ };
function v2(){ };
export{
    v1,v2
 };

2. import 命令

使用export命令定义模块的对外接口以后,其他JS文件就可以通过import命令加载这个模块,并从中输入变量。


示例:

//main.js
import {firstName,lastName,year} from './profile.js';
function setName(element){
element.textContent=firstName+''+lastName;
  }



注意:


import命令输入的变量都是

只读

的,因为它的本质是输入接口,不允许在加载模块的脚本中改写接口内容。


3.export default命令

从上面可以看出,使用import命令时,用户需要知道所要加载的变量名或函数名,否则无法加载。为了给用户提供方便,不用阅读文档就可以加载模块,此时需要用到export default命令,为模块指定默认输出。


示例:

//export-default.js
export default function(){
    console.log('foo');
}
//import-default.js
import customName from './export-default';
customName();

export-default.js是一个模块文件,它的默认输出是一个函数,import-default.js为另一个模块,在加载export-default.js模块时,import命令可以为匿名函数指定任意名字(customName),而不需要知道原模块输出的函数名。


注意:

  • 一个模块只能有一个默认输出,export default只能使用一次;
  • import命令后面可以不用加大括号;
  • export default命令用在非匿名函数前也是可以的;

任务六:Promise对象


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


Promise对象有3种状态

:pending(进行中)、fulfilled(已成功)、rejected(已失败)。



基本用法示例:

//实例化promise对象
    const p=new Promise(function(resolve,reject){
        setTimeout(function(){
            //成功,则调用resolve方法,promise对象的状态就会是成功
            // let data='用户的数据';
            // resolve(data);
            //失败,则调用reject方法,promise对象的状态就会是失败
            let err='数据读取失败';
            reject(err);
        },1000)
    });

    //根据promise对象的状态分别调用 Promise 对象的then方法,两个参数为函数
    p.then(function(value){ //promise对象的状态为成功
        console.log(value);
    },function(reason){//promise对象的状态为失败
        console.error(reason);
    });

promise构造函数接受一个函数作为形参,该函数的两个参数分别是resolve和reject。他们是两个函数,由JavaScript引擎提供,不用我们部署。


resolve函数的作用

是将promise对象的状态变为成功,并将异步操作的结果作为参数传递出去,然后调用then方法的一个回调函数;


reject函数的作用

是将promise对象的状态变为失败,并将异步操作的报错作为参数传递出去,然后调用then方法的第二个回调函数;



版权声明:本文为wanglingli95原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。