ES6笔记(完整详细版)

第三阶段 企业级开发

第一章 环境搭建

一、Node介绍

    Node也叫NodeJS,Node.js,由Ryan-Dahl于2009年5月在GitHub发布了第一版。
    Node是一个JavaScript运行环境(runtime)。实际上他是对Google V8引擎进行了封装。
    官网介绍:一个搭建在ChromJavaScript运行时上的平台,用于构建高速、可伸缩的网络程序。
    Node采用的事件驱动、非阻塞I/O模型,使它既轻量又高效,并成为构建运行在分布式设备上的数据密集型实时程序的完美选择。
    目前Node在实际开发中主要作为开发基础环境存在,用于进行模块管理,编译es6代码,编译sass文件,运行各种Js脚本。

二、Node安装

1、下载

进入官网http://nodejs.org/en/,选取合适的版本进行下载

2、安装

Linux:先解将安装包压,然后 进行环境变量的配置即可。
windows:直接点击安装即可。

三、Node使用

1、基本使用

1)执行js脚本

 $ node demo.js

2)REPL环境

在命令行键入node命令,后面没有文件名,就进入一个Node.js的REPL环境(Read-eval-print loop,"读取-求值-输出循环"),可以直接运行各种JavaScript命令。

 $ node
 > 1+1
 2
 >

四、模块化结构

1)模块化

  Node.js采用模块化结构,按照CommonJS规范定义和使用模块。在Node中,以模块为单位划分所有功能,并且提供一个完整的模块加载机制,使得我们可以将应用程序划分为各个不同的部分,并且对这些部分进行很好的协同管理。通过将各种可重用的代码编写在各种模块中的方法,我们可以大大减少应用程序的代码量,提高应用程序开发效率以及应用程序的可读性。通过模块加载机制,我们也可以将各种第三方模块引入到我们的应用程序中。

2)CommonJS

   JavaScript是一种功能强大的面向对象语言,具有一些最快速的动态语言解释器。官方JavaScript规范定义了一些用于构建基于浏览器应用程序的对象的API。但是,规范并没有定义一个用于对构建更广泛的应用程序的标准库。CommonJS API将通过定义处理许多常见应用程序需求的API来填补这一空白,最终提供与Python、Ruby和Java一样丰富的标准库。其意图是应用程序开发人员能够使用CommonJS API编写应用程序,然后在不同的JavaScript解释器和主机环境中运行该程序。在兼容CommonJS的系统中,可以使用JavaScript程序开发:
     服务器端JavaScript应用程序
     命令行工具
     图形界面应用程序
     混合应用程序(如Titanium或Adobe AIR)
   Node应用由模块组成,采用CommonJS模块规范。

3)模块作用域

    每一个文件就是一个模块,有自己的作用域。在一个文件里面定义的变量、函数、类,都是私有的,对其他文件不可见。

  私有属性:

    //example.js
    var x = 5;
    var addX = function(value){
         return value+x;
    };

    上面代码中,变量x和函数addX,是当前文件example.js私有的,其他文件不可见。

  全局属性:

    如果想在多个文件分享变量,必须定义为global对象的属性。

    global.warning = true;

4)模块交互

     CommonJS规范规定,每个模块内部,module变量代表当前模块。这个变量是一个对象,它的export属性(即module.exports)是对外的接口。加载某个模块,其实是加载该模块的module.exports属性。

   定义模块:

     var x = 5;
     var addX = function(value){
         return value+x;
     };
     module.export.x = x;
     module.export.addX = addX;


   模块加载:

       require方法用于加载模块。
       var example = require('./example.js');
       console.log(example.x); //5
       console.log(example.addX(1)); //6

5)模块对象

   Node内部提供一个Module构建函数。所有模块都是Module的实例。每个模块内部都有一个module对象,代表当前模块。它有以下属性:

   module.id 模块的识别符,通常是带有绝对路径的模块文件名。
   module.filename 模块的文件名,带有绝对路径。
   module.loaded 返回一个布尔值,表示模块是否已经加载完成。
   module.parent 返回一个对象,表示调用该模块的模块。
   module.children 返回一个数组,表示该模块要用到的其他模块。
   module.exports 表示模块对外输出的值。

6)exports变量

   为了方便,Node为每个模块提供一个exports变量,指向module.exports。这等同于在每个模块头部,有这样一行命令:
   var export = module.exports;

五、核心模块

1)path模块

  path模块提供了一些工具函数,用于处理文件与目录的路径,使用如下方法引用: 
  
  var path = require('path');
  path.basename() 该方法返回一个参数路径的最后一个部分
  path.dirname() 该方法返回一个path的目录名
  path.extname() 该方法返回path的扩展名,即从path的最后一部分中的最后一个。(句号)字符到字符串结束。
  path.isAbsolute() 该方法判定path是否为一个绝对路径。
  path.join() 该方法使用平台特定的分隔符把全部给定的path片段连接到一起,并规范化生成的路径。
  path.nomalize() 该方法会规范化给定的path,并解析'..'和'.'片段。
  path.delimiter  该属性提供平台特定的路径分隔符。

 另外:

   __filename:指向当前运行的脚本文件名
   __dirname:指向当前运行的脚本所在目录

2)querystring模块

   querystring模块提供了一些实用函数,用于解析与格式化URL查询字符串。使用如下方法引用:
   
   var querystring = require('querystring');
   querystring.stringify(obj[,sep[,eq]])  将对象转换为查询字符串
         obj 要序列化成URL查询字符串的对象。
         sep 用于界定查询字符串中的键值对的子字符串。默认为'&'。
         eq 用于界定查询字符串中的键与值的子字符串。默认为'='。
   querystring.parse(str[,sep[,eq]])  将查询字符串转换为对象。      

3)url模块

   提供了一些实用函数,用于URL处理与解析。可以通过以下方式使用:
   
   var url = require('url');
   url.parse() 将一个url地址转换为一个对象
   url.resolve() 该方法会以一种Web浏览器解析超链接的方式把一个目标URL解析成相对于一个基础URL
        url.resolve('/one/two/three','four'); // '/one/two/four'
        url.resolve('http://example.com/','/one'); //'http://example.com/one'
        url.resolve('http://example.com/one','/two'); //'http://example.com/two'
        (替换最后一个)

六、npm

1)介绍

npm是Js开发者能够更方便的分享和服用以及更新代码,被复用的代码被称为包或者模块,一个模块中包含了一到多个js文件。
在模块中一般还会包含一个package.json的文件,该文件中包含了该模块的配置信息。一个完整的项目需要依赖多个模块,

一个完整的npm包含三部分:
    npm网站---用于预览npm管理的包
    注册机制---用于上传包,使用数据库来维护包与上传者的信息
    客户端---用于安装包

2)创建一个模块

    Node.js的模块是一种能够被发布到npm上的包。
    创建模块从创建package.json文件开始,package.json是模块的配置文件。

       可以使用npm init命令来初始化package.json文件。
         $ npm init
             name 模块名称           version  模块版本
             description 描述信息    main   指定模块入口文件
             Dependencies 依赖关系   engines  指定node版本
             devDependencies 环境依赖或测试依赖
             optionalDependencies 可选择依赖
             script 定义当前模块脚本,使用npm run来运行所定义的脚本  

       使用-y参数创建默认package.json文件。
         $ npm init -y  

3)安装npm

  npm会随着Node一起被安装到本地。可以使用以下命令来更新npm:

  $ npm install npm@latest -g

  安装淘宝镜像:

      由于默认npm的仓库在国外,下载起来很慢,可以使用淘宝镜像来加快下载速度。

    $ npm install -g cnpm --register=https://register.npm.taobao.org

      Registry注册中心:
        官方:https://registry.npmjs.org
        淘宝:https://registry.npm.taobao.org
        私有:http://localIP:port  

  修改npm权限:

     执行npm的时候有时会遇到权限不足的情况,可以通过以下方式进行修正:

     $ npm config get prefix
     $ sudo chown -R $(whoami) $(npm config get prefix)/{lib/node_modules,bin,share}

4)模块安装

   如果想要仅在当前模块中使用某个第三方模块,就可以使用npm install的默认安装,默认安装即是本地安装;如果想要在命令行中使用模块,就需要进行全局安装。安装时,如果当前目录中没有node_modules目录,npm就会创建一个该目录。
   $ npm install <package_name>
       $ npm install 
          安装所有项目依赖的模块,依赖的模块定义在package.json中
       $ npm install
           安装模块时,默认会将所安装的模块写入到package.json中的dependencies属性,通过添加一些参数改变这个特性。 

           -S,--save
           -D,--save-dev:Package will appear in your devDenpendencies.
           -O,--save-optional:Package will appear in your optionalDependencies.
           --no--save:Prevents saving to dependencies.
           -E,--save-exact:Saved dependencies will be configured with an exact version rather than using npm's default semver range operator.   

5)模块更新

   全局更新以来的模块。

   $ npm update <modules_name>

6)模块删除

    从node_modules中删除不需要的模块。

    $ npm uninstall -g <package_name>

    不仅删除node_modules中的依赖,还需要删除package.json中的信息,可以使用--save参数。

    $ npm uninstal --save -g <package_name>

7)搭建本地npm仓库(sinopia)

   安装  $ npm install -g sinopia
   配置  $ npm set registry http://localhost:4873/
   添加用户  $ npm adduser --registry http://localhost:4873/
   发布模块  $ npm publish <module_name>
   启动  $ sinopia

七、Babel

1、命令行转码babel-cli

全局环境下进行Babel转码。这意味着,如果项目要运行,全局环境必须有Babel,也就是说项目产生了对环境的依赖。

1)安装

  $ npm install --global babel-cli

2)安装预设并且添加配置文件配置.babelrc 
   在当前项目的根目录下创建该文件。

   $ npm install --save-dev babel-preset-es2015
   {"presets":["es2015"]}

3)使用

   转码结果输出到标准输出
   $ babel example.js
   转码结果写入一个文件,--out-file或-o参数指定输出文件
   $ babel example.js --out-file compiled.js
   整个目录转码--out-dir或-d参数指定输出目录
   $ babel src --out-dir lib   

2、配置文件

Babel的配置文件是.babelrc,存放在项目的根目录下。
使用Babel的第一步,就是配置这个文件。该文件用来设置转码规则和插件,基本格式如下:
{"presets":[],"plugins":[]}

1)presets字段设定转码规则,官方提供以下的规则集,你可以根据需要安装。

  ES2015转码规则
  $ npm install --save-dev babel-preset-es2015  =>es2015
  最新转码规则
  $ npm install --save-dev babel-preset-latest  =>latest
  不会过时的转码规则
  $ npm install --save-dev babel-preset-nev  =>env

2)然后,将这些规则加入.babelrc。

  {"presets":["es2015"],"plugins":[],"ignore":[]}

3、将babel-cli安装到项目中

1)安装babel-cli及预设

  $ npm install --save-dev babel-cli
  $ npm install --save-dev babel-preset-env

2)配置文件.babelrc

   $ vim .babelrc
       {"presets":["env"]}    

3)在package.json中添加脚本

   {
      //...
      "script":{"build":"babel src -d lib"}
   }          

4)运行脚本,执行转码操作

   $ npm run build

八、babel-polyfill

 Babel默认只转换新的JavaScript句法(syntax),而不转换新的API,比如Iterator、Generator、Set、Maps、Proxy、Reflect、Symbol、Promise等全局对象,以及一些定义在全局对象上的方法(Object.assign)都不会转码。
 举例来说,ES6在Array对象上新增了Array.from方法。Babel就不会转码这个方法。如果想让这个方法运行,必须使用babel-polyfill,为当前环境提供一个垫片。

1、安装

  $ npm install -save babel-polyfill

2、在js文件中引用并且使用

  import "babel-polyfill";//或者require("babel-polyfill");

第二章 基础知识

一、let命令

1.基本用法

ES6新增了let命令,用来声明变量。用法类似于var,但是也存在新特性:

1)let所声明的变量,只在let命令所在的代码块有效。适用于for循环。

   {let a = 10;var b = 1;}
   //a  a is not defined
   //b  1

2)不存在变量提升。

   console.log{bar};
   let bar = 2;
   //报错ReferenceError:bar is not defined

3)暂时性死区。

   在代码块内,使用let命令变量之前,该变量都是不可用的。

   var tmp = 123;
   if(true){
        tmp = 'abc'; //ReferenceError
        let tmp;
   }     

4)不允许重复声明。

    function(){let a =10;let a = 1;} //报错

2.块级作用域

 let实际上为JavaScript新增了块级作用域。外层作用域无法读取内层作用域的变量,内层作用域可以定义外层作用域的同名变量。

 let foo = 1;
 {
    let foo = 2; //定义同名变量
    let bar = "one";
 }      
 console.log{foo}; //1
 console.log(bar); //报错

 块级作用域的出现,实际上使得广泛应用的立即执行函数表达式(IIFE)不再必要了。    

二、const命令

1. 基本用法

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

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

1)const声明的变量不得改变值,这意味着,const一旦声明变量,就必须立即初始化,不能留到以后赋值。

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

2)块级作用域

  只在声明所在的块级作用域内有效。

3)暂时性死区

  在代码块内,使用let命令声明变量之前,该变量是不可用。

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

4)不允许重复声明

   var message = 'Hello!';
   let age = 25;
   //以下两行都会报错
   const message = 'Goodbye!';
   const age = 30;

三、解构赋值

1.解构

 ES6允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构(Destructing)。例如:let [a,b,c] = [1,2,3];
 本质上,这种写法属于“模式匹配”,只要等号两边的模式相同,左边的变量就会被赋予对应的值。
 如果结构不成功,变量的值就等于undefined。另一种情况是不完全解构,即等号左边的模式只匹配一部分的等号右边的数组。这种情况下,解构依然可以成功。

2.数组的解构赋值

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

1)不完全解构:

  let [a,[b],d] = [1,[2,3],4]; //a=1;b=2;d=4

2)集合解构:

  let[head,...tail] = [1,2,3,4]; //head=1;tail=[2,3,4]

3)默认值(当匹配值严格等于undefined时,默认值生效):

  let [x,y='b'] = ['a']; //x = 'a',y = 'b'

4)默认值也可以为函数:

  function f(){ console.log('aaa'); }
  let [x=f()] = [1];

3.对象的解构赋值

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

    let {foo,bar} = {foo:'aaa',bar:'bbb'}; //foo='aaa';bar='bbb'

 2)如果变量名与属性名不一致,必须写成下面这样。

     var {foo:baz} = {foo:'aaa',bar:'bbb'}; // baz='aaa'    

 3)这实际上说明,对象的解构赋值是下面形式的简写。

     let {foo:foo,bar:bar} = {foo:'aaa',bar:'bbb'};

 4)嵌套结构

     let obj = {p:['Hello',{y:'World'}]};
     let {p:[x,{y}]} = obj;  // x='Hello';y='World'  

 5)默认值(默认值生效的条件是,对象的属性值严格等于undefined)

     var {x:y=3} = {}; // y=3

4.字符串的解构赋值

 1)解构时,字符串被转换成了一个类似数组的对象。

   const [a,b,c,d,e] = 'hello'; //a=h;b=e;c=l;d=l;e=o

 2)也可以对数组的属性解构

   let {length:len} = 'hello'; // len=5 

5.数值和布尔值的解构赋值

  解构时,如果等号右边是数值和布尔值,则会先转为对象。

  let {toString:s} = 123; // s===Number.prototype.toString true
  let {toString:s} = true; // s===Boolean.prototype.toString true

6.函数参数的解构赋值

 1)基本语法

   function add([x,y]){return x+y;}
   add([1,2]);

 2)默认值

   function move({x=0,y=0}){
       return [x,y];
   }  
   move({x:3,y:8}); //[3,8]
   move({x:3}); //[3,0]
   move({}); //[0,0]
   move(); //[0,0]

7.常见用途

 1)交换变量的值

    let x = 1; 
    let y = 2; 
    [x,y] = [y,x];

 2)从函数返回多个值

    function example(){
       return [1,2,3];
    }
    let [a,b,c] = example();   

 3)函数参数的定义
    
    function f([x,y,z]){...}
    f([1,2,3]);

 4)提取JSON数据
 
    let jsonData = {id:42,status:'OK',data:[867,5309]};
    let {id,status,data:number} = jsonData;   

 5)输入模块的指定方法

    const {SourceMapConsumer,SourceNode} = require("source-map");

 6)函数参数的默认值         

    jQuery.ajax = function(url,{
      async = true,cache = true,global = true,
      beforeSend = function(){},
      complete = function(){},
      //...more config
    }){//...do stuff};

    指定参数的默认值,就避免了在函数体内部再写var foo = config.foo||'default foo';这样的语句。

 7)遍历map结构
 
    var map = new Map();
    map.set('first','hello');
    map.set('second','world');
    for(let [key,value] of map){
       console.log(key + "is" + value);
    }

第三章 对象、函数、数组的扩展

学习目标:
1)属性简写方式
2)方法简写方式
3)Object方法的扩展
4)函数默认值
5)箭头函数
6)扩展运算符
7)Array.from()
8)Arra.of()
9)数组实例的find(),findIndex()
10)数组实例的fill()
11)数组实例的entries(),keys(),values()
12)数组实例的includes()

一、对象扩展

1.属性简写

ES6允许直接写入变量和函数,作为对象的属性和方法。这时,属性名为变量名,属性值为变量的值。

   var foo = 'bar';
   var baz = {foo};
   =>
   var baz = {foo:foo}; 

2.方法简写

   var o = {method(){return "hello!";}};
   =>
   var o = {method:function(){return "hello!";}};   

3.属性名表达式

 ES6允许字面量定义对象时,可以把表达式放在方括号内。

   let propKey = 'foo';
   let obj = {[propKey]:true,['a'+'bc']:123};

4.方法的name属性

 函数的name属性,返回函数名。

   const person = {sayName(){console.log('hello!');}};
   person.sayName.name; //"sayName" 

5.Object.is(value1,value2)

 同值相等,与===类似,不同之处在于:+0不等于-0;NaN等于自身
 
 Object.is('foo','foo'); //true
 Object.is({},{}); //false            

二、函数的扩展

1.函数参数的默认值

 ES6允许为函数的参数设置默认值,即直接写在参数定义的后面。

 function log(x,y='World'){
     console.log(x,y);
 }

 通常情况下,定义了默认值的参数,应该是函数的尾参数。
 函数的length属性,将返回没有指定默认值的参数个数。

2.与解构赋值默认值结合使用

 参数默认值可以与解构赋值的默认值结合起来使用。

 function foo({x,y=5}){
     console.log(x,y);
 }
 foo({}); // undefined 5
 foo({x:1}); //1  5
 foo({x:1,y:2}); // 1 2

3.rest参数

 ES6引入rest参数(形式为"...变量名"),用于获取函数的多余参数,这样就不需要使用arguments对象了。
 rest参数搭配的变量是一个数组,该变量将多余的参数放入数组中。

 function add(...values){
     let sum = 0;
     for(var val of values){
         sum += val;
     }
     return sum;
 }
 add(2,3,5); //10

4.箭头函数

 ES6允许使用“箭头”(=>)定义函数

1)基本用法:

 var f = v => v;
 等价于
 var f = function(v){
    return v;
 }

 如果箭头函数不需要参数或需要多个参数,就使用一个圆括号代表参数部分。
 如果箭头函数的代码块部分多于一条语句,就要使用大括号将他们括起来,并且使用return语句返回。

 2)this

 箭头函数里面没有自己的this,而是引用外层的this。

 //ES6
 function foo(){
    setTimeout(()=>{
       console.log('id:',this.id);
    },1000);
 }

 //ES5
 function foo(){
    var _this = this;
    setTimeout(function(){
       console.log('id:',_this.id);
    },1000);
 }

 不能作为构造函数,没有内部属性arguments。

5.扩展运算符

 扩展运算符(spread)是三个点(...)。它好比rest参数的逆运算,将一个数组转为用逗号分隔的参数序列。

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

1) 函数的调用

  function add(x,y){
     return x+y;
  }
  add(...[1,3]);
  Math.max(...[14,3,77]);

2) 将字符串转换为数组
  
  [...'hello']   // ['h','e','l','l','o']  

三、数组的扩展

1. Array.from

用于将两类对象转为真正的数组:类似数组的对象(array-like object)和可遍历(iteratble)的对象(包括ES6新增的数据结构Set和Map)。

let arrayLike = {'0':'a','1':'b','2':'c',length:3};

//ES6的写法

let arr2 = Array.from(arrayLike); //['a','b','c']

只要是部署了Iterator接口的数据结构,Array.from都能将其转为数组。

Array.from('hello');  //将字符串转换为数组['h','e','l','l','o'] 
let namesSet = new Set(['a','b']);
Array.from(namesSet);  //['a','b']

2. Array.of()

 用于将一组值转换为数组。这个发的主要目的是弥补数组构造函数Array()的不足。

 Array.of(3,18,8); //[3,18,8]

3.数组实例的find()和findIndex()

 find()方法用于找出第一个符合条件的数组成员。
 它的参数是一个回调函数,所有数组成员依次执行该回调函数,直到找出第一个返回值为true的成员,然后返回该成员。如果没有符合条件的成员,则返回undefined。
 find()方法的回调函数可以接受三个参数,依次为当前的值、当前的位置和原数组。

 [1,4,-5,10].find((n)=>n<0); //-5

 findIndex()方法与find()方法非常类似,更换第一个符合条件的数组成员的位置。如果所有成员都不符合条件,则返回-1.

 [1,5,10,15].findIndex(function(value,index,arr){
       return value > 9;
 }); // 2

4.数组实例的fill()

 fill()方法使用给定的值填充一个数组。

 ['a','b','c'].fill(7); // [7,7,7]
 new Array(3).fill(7); //[7,7,7]

5.数组实例的entries(),keys()

  这两个方法用于遍历数组。它们都返回一个遍历器对象,可以用for...of循环进行遍历,唯一的区别是:keys()是对键名的遍历,entries()是对键值对的遍历。

  for(let index of ['a','b'].keys()){
      console.log(index);
  }
  for(let [index,elem] of ['a','b'].entries()){
      console.log(index,elem);
  }

6.数组实例的includes()

  该方法返回一个布尔值,表示某个数组是否包含给定的值,与字符串的includes方法类似。ES6引入了该方法。

  [1,2,3].includes(2);  // true
  [1,2,3].includes(4);  // false
  [1,2,NaN].includes(NaN);  // true

第四章 Set和Map数据结构及Promise

一、Set

1.set实例的创建

 它类似于数组,但是成员的值都是唯一的,没有重复的值。set本身是一个构造函数,用来生成set数据结构。

 const s = new Set();
 [2,3,5,4,5,2,2].forEach(x => s.add(x));
 console.log(s);  //2 3 5 4

 Set函数可以接收一个数组(具有iterable接口的其他数据结构)作为参数,用来初始化。

 [...new Set(array)]  //去除数组的重复成员

2.set实例的属性和方法

 Set结构的实例有以下属性:

   1) Set.prototype.constructor:构造函数,默认就是Set函数。
   2) Set.prototype.size:返回Set实例的成员总数。  
 
 Set结构的实例有以下方法:

   1) add(value):添加某个值,返回Set结果本身。
   2) delete(value):删除某个值,返回一个布尔值,表示删除是否成功。
   3) has(value):返回一个布尔值,表示该值是否为Set的成员。
   4) clear():清除所有成员,没有返回值。
   5) keys():返回键名的遍历器。
   6) values():返回键值的遍历器。
   7) entries():返回键值对的遍历器。
   8) forEach():使用回调函数遍历每个成员

二、Map

1.Map实例的属性和方法

 Map类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各类型的值(包括对象)都可以当做键。
 也就是说,Object结构提供了“字符串--值”的对应,Map结构提供了“值--值”的对应,是一种更完善的Hash结构的实现。如果你需要“键值对”的数据结构,Map比Object更合适。

 Map可以接受一个数组作为参数。该数组的成员是一个个表示键值对的数组。

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

 Map结构的实例有以下属性:

   Set.prototype.size:返回Map实例的成员总数。 

 Map结构的实例有以下方法:

   1) set(key,value):set方法设置键名key对应的键值为value,然后返回整个Map结构。如果key已经有值,则键值会被更新,否则就新生成该键。 
   2) get(key):get方法读取对应的键值,如果找不到key,返回false。
   3) has(key):has方法返回一个布尔值,表示某个键是否在当前Map对象之中。
   4) delete(key):delete方法删除某个键,返回true。如果删除失败,返回false。
   5) clear():清除所有成员,没有返回值。
   6) keys():返回键名的遍历器。 
   7) values():返回键值的遍历器。
   8) entries():返回键值对的遍历器。
   9) forEach():使用回调函数遍历每个成员   

三、Interator

1.Interator(遍历器)的概念

 遍历器(Iterator)就是这样一种机制。它是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署Iterator接口,就可以完成遍历操作(即依次处理该数据结构的所有成员)。

 Iterator的作用有三个:

    1)为各种数据结构提供一个统一的、简便的访问接口。
    2)使得数据结构的成员能够按某种次序排列。
    3)ES6创造了一种新的遍历命令for...of循环,Iterator接口主要供for...of消费。

 Iterator的遍历过程是这样的:

   1)创建一个指针对象,指向当前数据结构的起始位置。也就是说,遍历器对象本质上就是一个指针对象。
   2)第一次调用指针对象的next方法,可以将指针指向数据结构的第一个成员。
   2)第二次调用指针对象的next方法,指针就指向数据结构的第二个成员。
   3)不断调用指针对象的next方法,直到它指向数据结构的结束位置。

2.默认Interator接口

 Iterator接口的目的,就是为所有数据结构提供了一种统一的访问机制,即for...of循环。当使用for...of循环遍历某种数据结构时,该循环会自动去寻找Iterator接口。一种数据结构只要部署了Iterator接口,我们就称这种数据结构是“可遍历的”。可以通过如下方法访问Iterator对象:

 var iterator = iterObj[Symbol.iterator]();

 原生具备Iterator接口的数据结构如下:

   Array
   Map
   Set
   String
   TypedArray
   函数的arguments对象
   NodeList对象

四、Promise

1.promise介绍

 Promise是异步编程的一种解决方案,比传统的解决方案(回调函数和事件)更合理更强大。它由社区最早提出和实现,ES6将其写进了语言标准,统一了用法,原生提供了Promise对象。

 所谓Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。从语法上说,Promise是一个对象,它可以获取异步操作的消息。Promise提供统一的API,各种异步操作都可以用同样的方法进行处理。

 有了Promise对象,就可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数。此外,Promise对象提供统一的接口,使得控制异步操作更加容易。

2.基本用法

 Promise构造函数接收一个函数作为参数,该函数的两个参数分别是reolve和reject。它们是两个函数,有JavaScript引擎提供,不用自己部署。

 resolve函数的作用是,将Promise对象的状态从“未完成”变为“成功”(即从Pending变为Resolved),在异步操作成功时调用,并将异步操作的结果作为参数传递出去。

 reject函数的作用是,将Promise对象的状态从“未完成”变为“失败”(即从Pending变为Rejected),在异步操作失败时调用,并将异步操作的结果作为参数传递出去。

 如果调用resolve和reject函数时带有参数,那么它们的参数会被传递给回调函数。

 Promise实例生成以后,可以调用then方法分别指定Resolved状态和Rejected状态的回调函数。

 .then(function(){
        //success
     },
     function(){
        //error
     });

3. promise.prototype.then()

 Promise实例具有then方法,也就是说,then方法是定义在原型对象Promise.prototype上的。它的作用是为Promise实例添加状态改变时的回调函数。
 then方法的第一个参数是Resolved状态的回调函数,第二个参数(可选)是Rejected状态的回调函数。then方法返回的是一个新的Promise实例(注意:不是原来的Promise实例)。
 因此可以采用链式写法,即then方法后面再调用另一个then方法。如果使用两个then方法,第一个回调函数完成以后,会将返回结果作为参数,传入第二个回调函数。

4. promise.prototype.catch()

 Promise.prototype.catch方法是.then(null,rejection)的别名,用于指定发生错误时的回调函数。
 一般来说,不要在then方法里面定义Reject状态的回调函数(即then的第二个参数),总是使用catch方法。

 Promise对象的错误具有“冒泡”性质,会一直向后传递,直到被捕获为止。也就是说,错误总是会被下一个catch语句捕获。

5. promise.all()

 Promise.all方法用于将多个Promise实例包装成一个新的Promise实例。

 var p = Promise.all([p1,p2,p3]);

 上面代码中,Promise.all方法接收一个数组作为参数,p1,p2,p3都是Promise实例,p的状态由p1,p2,p3决定,分成两种情况:
 1)只有p1,p2,p3的状态都变成fulfilled,p的状态才会变成fulfilled,此时p1,p2,p3的返回值组成一个数组,传递给p的回调函数。
 2)只要p1,p2,p3之中有一个被rejected,p的状态就会变成rejectedd,此时第一个被rejected的实例的返回值,会传递给p的回调函数。

6. promise.race()

 promise.race方法同样是将多个Promise实例包装成一个新的Promise。下面代码中,只要p1,p2,p3之中有一个实例率先改变状态,p的状态就跟着改变。那个率先改变的Promise实例的返回值,就传递给p的回调函数。

 var p = Promise.race([p1,p2,p3]);

7. promise.resolve()

 promise.resolve方法将现有对象转为Promise对象,例如:
 var jsPromise = promise.resolve($.ajax('/whatever.json'));

 1) 参数是一个Promise实例
    promise.resolve将不做任何修改、原封不动地返回这个实例。
 2) 参数是一个thenable对象
    thenable对象指的是具有then方法的对象,promise.resolve方法会将这个对象转为Promise对象,然后立即执行thenable对象的then方法。
 3) 参数不是具有then方法的对象,或根本就不是对象
    如果参数是一个原始值,或者是一个不具有then方法的对象,则promise.resolve方法返回一个新的Promise对象,状态为resolved。
 4) 不带有任何参数
    直接返回一个resolve状态的Promise对象。需要注意的是,立即resolve的Promise对象,是在本轮“事件循环”(event loop)的结束时,而不是在下一轮的“事件循环”的开始时。  

8. promise.reject()

 promise.reject方法也会返回一个新的Promise实例,该实例的状态为rejected。

 var p = promise.reject("出错了");
 =>
 var p = new Promise((resolve,reject)=>reject('出错了'));

9. finally()

 finally方法用于指定不管Promise对象最后状态如何都会执行的操作。它接收一个普通的回调函数作为参数,该函数不管怎样都必须执行。

 下面是一个例子,服务器使用Promise处理请求,然后使用finally方法关掉服务器。
 
 server.listen(0).then(function(){
     // run test
 }).finallly(server.stop);

第五章 ES6模块

1、介绍
历史上,JavaScript一直没有模块(modules)体系,无法将一个大程序拆分成互相依赖上午小文件,再用简单的方法拼接装起来。
在ES6之前,社区制定了一些模块加载方案,最主要的有CommonJS和AMD两种。前者用于服务器,后者用于浏览器。
ES6在语言标准的层面上,实现了模块功能,而且实现的相当简单,完全可以取代CommonJS和AMD规范,成为浏览器和服务器通用的模块解决方案。

2、export命令

模块功能主要由两个命令构成:export和import。export命令用于规定模块的对外接口,import命令用于输入其他模块提供的功能。
一个模块就是一个独立的文件。该文件内部的所有变量,外部无法获取。
如果你希望外部能够读取模块内部的某个变量,就必须适应使用export关键字输出该变量。下面是一个JS文件。里面使用export命令输出变量。

var firstName = 'Michael';
var lastName = 'Jackson';
var year = 1958;
function multiply(x,y){return x * y;};
export{firstName,lastName,year,multiply};

需要特别注意的是,export命令规定的是对外的接口,必须与模块内部的变量建立一一对应关系,不能直接导出一个值。

export var m = 1;
或 var m = 1; export{m};
或 var n = 1; export{n as m};
在一个模块中,export可以调用多次。

3、import命令

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

1)解构导入

  import {firstName,lastName,year} from './profile';

2)重命名变量

   import {lastName as surname} from './profile';

3)重复导入

    import {name} from './module1';
    import {age} from './module1';

   如果多次重复执行同一import语句,那么只会执行一次模块代码。
   
4)模块的整体加载
   
    import * as persom from './module1';    

4、export default命令

使用import命令的时候,用户需要知道所要加载的变量名或函数名,否则无法加载。
但是,用户肯定希望快速上手,未必愿意阅读文档,去了解模块有哪些属性和方法。
为了给用户提供方便,让他们不用阅读文档就能加载模块,就要用到export default命令为模块指定默认输出。

export default function(){
    console,log('foo');
}

其他模块加载该模块时,import命令可以为该匿名函数指定任意名字。

import customName from './export-default';
customName(); //'foo'

export default命令用于指定模块的默认输出。显然,一个模块只能有一个默认输出,因此export default命令只能使用一次。所以,import命令后面才不用加大括号,因为只可能对应一个方法或者对象。

5、export与import的复合写法

如果在一个模块之中,先输入后输出同一个模块,import语句可以与export语句写在一起。

export {foo,bar} from './my_module';
=>
import {foo,bar} from 'my_module';
export {foo,bar};

第六章 Class

1、介绍

JavaScript语言中,生成实例对象的传统方法是通过构造函数ES6提供了更接近传统语言的写法,引入了class(类)概念,作为对象的模板。通过class关键字,可以定义类。
基本上,ES6的class可以看作只是一个语法糖,它的绝大部分功能,ES5都可以做到,新的class写法只是让对象原型的写法更加清晰更像面向对象编程的语法而已。所以ES6的类,完全可以看作构造函数的另一种写法。

class Point{
constructor(x,y){
this.x = x;
this.y = y;
}
toString(){
return ‘(’ + this.x + ‘,’ + this.y + ‘)’;
}
}

2、方法

在类中可以直接定义方法,实际上类的所有方法都定义在类的prototype属性上面。在类的实例上面调用方法,其实就是调用原型上的方法。

class Point{
  constructor(){//...}
  toString(){//...}
  toValue(){//...}
}

由于类的方法都定义在prototype对象上面,所以类的新方法可以添加在prototype对象上面。Object.assign方法可以很方便地向类添加多个方法。

class Point{
   constructor(){//...}
}
Object.assign(Point.prototype,{
  toString(){},
  toValue(){}
});

3、constructor方法

constructor方法是类的默认方法,通过new命令生成对象实例时,自动调用该方法。一个类必须有constructor方法,如果没有显示定义,一个空的constructor方法会被默认添加。

class Point{}
=>
class Point {costructor(){}}

类必须使用new调用,否则会报错。这是它跟普通构造函数的一个主要区别,后者不用new也可以执行。

4、静态方法

类相当于实例的原型,所有在类中定义的方法,都会被实例继承。如果在一个方法前,加上static关键字,就表示该方法不会被实例继承,而是直接通过类来直接调用,这就称为“静态方法”。

class Foo{
   static classMethod(){return 'hello';}
}
Foo.classMethod(); //'hello'

如果静态方法包含this关键字,这个this值得是类,而不是实例。

5、实例属性

类的实例属性可以定义在构造函数中。

class Person{
   constructor(id,name,age){
       this.id = id;
       this.name = name;
       this.age = age;
   }
}

6、静态属性

直接在类上定义的属性是静态属性。

class Foo{
   //...
}
Foo.prop = 1;
Foo.prop; //1

目前,只有这种写法可行,因为ES6明确规定,class内部只有静态方法,没有静态属性。

7、继承

class可以通过extends关键字实现继承,这比ES5的通过修改原型链实现继承要清晰和方便很多。

class Animal{
   constructor(name){
      this.name = name;
   }
   sayName(){
      console.log("my name is",this.name);
   }
}
class Dog extends Animal{
    //...
}

子类必须在constructor方法中调用super方法,否则新建实例时会报错。
这是因为子类没有自己的this对象,而是继承父类的this对象,然后对其进行加工。
如果不调用super方法,子类就得不到this对象。子类构造函数可以省略。
在子类构造函数中,只有调用super之后,才可以使用this关键字,否则会报错。

8、super

super这个关键字,既可以当做函数使用,也可以当做对象使用。在这两种情况下,它的用法完全不同。

1)函数

  子类B的构造函数之中的super(),代表调用父类的构造函数。super虽然代表了父类A的构造函数,但是返回的是子类B的实例,即super内部的this指的是B,因此super()在这里相当于A.prototype.constructor.call(this)。

2)对象
    
   在普通方法中,指向弗雷德原型对象;在静态方法中,指向父类。由于super指向父类的原型对象,所以定义在父类实例上上午方法或属性,是无法通过super调用的。

   ES6规定,通过super调用父类的方法时,super会绑定子类的this。

   super.print();
   =>
   super.print.call(this);

   不能直接打印super,因为无法得知super到底是函数还是对象。

9、类的prototype属性和__proto__属性

 class作为构造函数的语法糖,同时有prototype属性和__proto__属性,因此同时存在两条继承链:
 1)子类的__proto__属性,表示构造函数的继承,总是指向父类。
 2)子类prototype属性的__proto__属性,表示方法的继承,总是指向父类的prototype属性。

 class A {}
 class B extends A {}
 B.__proto__ === A; //true
 B.prototype.__proto__ === A.prototype; //true

 类的继承是按照下面的模式实现的。

 class A {}
 class B {}
  //B的实例继承A的实例
 Object.setPrototypeOf(B.prototype,A.prototype);
  //B的实例继承A的静态属性
 Object.setPrototypeOf(B,A);
 const b = new B();

猜你喜欢

转载自blog.csdn.net/qq_37957971/article/details/83146250