上一篇通过TodoList的练习,目的是为了让大家理解ES6中各种新特性的实际用途。

最好的学习方法就是实践,所以这节课结合实际项目,来更好的理解和掌握ES6的用途和使用场景,达到灵活运用的目的。

1、模块化

以项目中普遍会有的config.js文件为例,实现export导出:

const githubURL = "OUR GITHUB URL HERE";const staticServer = "http://xxx.com";const testsPath = `zaz-${type}-${name}/tests/index.htm?zaz[env]=tests`;const name = "stalker";const type = "mod";const version = "0.0.1";const state = "ok";const description = "JavaScript API to deal with user data";let globalpkg = null; const config = {  _static: {  name,  version,  state,  description,  docs: `${githubURL}/pages/terra/zaz-${type}-${name}`,  source: `${githubURL}/Terra/zaz-${type}-${name}`,  tests: `${staticServer}/fe/${testsPath}`,  dependencies: ['mod.wilson']  }};export default config;

再在其他文件中通过import实现导入:

import config from './config';//导入ES6模块import { globalpkg } from './config';import factory from './factory'; zaz.use((pkg) => {   "use strict";    config.dynamic.globalpkg = pkg;    pkg.require(['modFactory'], (modFactory) => {        modFactory.create(pkg.utils.deepMerge(config._static, factory));    }); });

使用ES6统一的模块化规范,可以提高代码的可读性,更易于维护。

2、数组操作

import React,{Component} from 'react';class RepeatArray extends Component{  constructor() {    super();  }  render(){    const names = ['Alice', 'Emily', 'Kate'];    return (      <div>      {         let p=document.querySelectorAll('p');           let pArr=Array.from(p);           pArr.forEach(function(item){           console.log(item.textContent);         });           Array.from(pArr,function(item){return item + 'ES6'});       }      </div>    );  }}

使用Array.from 同时对每个元素进行操作。

3、模板字符串

常见的使用场景便是写组件模板时使用:

$('#result').append(`   There are <b>${basket.count}</b> items    in your basket, <em>${basket.onSale}</em>   are on sale! `);

可以在模板字符串中任意的嵌入变量, 调用函数等。

4、解构与扩展操作符

扩展操作符在父组件给子组件传递一批属性的情境中更为方便。

下面的例子把className以外的所有属性传递给div标签

class AutoloadingPostsGrid extends React.Component {    render() {        var {            className,            ...others,  // contains all properties of this.props except for className        } = this.props;        return (            <div className={className}>                <PostsGrid {...others} />                <button onClick={this.handleLoadMoreClick}>Load more</button>            </div>        );    }}

使用react开发最常见的问题就是父组件要传给子组件的属性较多时比较麻烦

class MyComponent extends React.Component{//假设MyComponent已经有了name和age属性  render(){    return (      <SubComponent name={this.props.name} age={this.props.age}/>     )  }}

使用扩展操作符可以变得很简单

class MyComponent extends React.Component{//假设MyComponent已经有了name和age属性  render(){    return (      <SubComponent {...this.props}/>     )  }}

上述方式是将父组件的所有属性都传递下去,如果这其中有些属性不需要传递呢?也很简单

class MyComponent extends React.Component{//假设MyComponent有很多属性,而name属性不需要传递给子组件  var {name,...MyProps}=this.props;  render(){    return (      <SubComponent {...Myprops}/>     )  }}

上述方法最常用的场景就是父组件的class属性需要被单独提取出来作为某个元素的class,而其他属性需要传递给子组件。

在构建通用容器时,扩展属性会非常有用。

function App1() {  return <Greeting firstName="Ben" lastName="Hector" />;}function App2() {  const props = {firstName: 'Ben', lastName: 'Hector'};  return <Greeting {...props} />;}

5、Promise

场景一 : 所有图片加载完再显示在页面上,避免页面闪动。

  function loadImg(src) {    return new Promise((resolve, reject) => {       let img = document.createElement('img');       img.src = src;       img.onload = function () {         resolve(img)       }       img.onerror = function (err) {         reject(err)       }    })  }  function showImgs(imgs) {    imgs.forEach(function(img){      document.body.appendChild(img);    })  }  Promise.all([    loadImg('https://example.com/pic1')    loadImg('https://example.com/pic2')    loadImg('https://example.com/pic3')  ]).then(showImgs)

场景二: 多个资源中只要加载了其中一种就可。

{  function loadImg(src) {    return new Promise((resolve, reject) => {       let img = document.createElement('img');       img.src = src;       img.onload = function () {         resolve(img)       }       img.onerror = function (err) {         reject(err)       }    })  }  function showImgs(img) {    document.body.appendChild(img);  }  Promise.race([    loadImg('https://example.com/pic1')    loadImg('https://example.com/pic2')    loadImg('https://example.com/pic3')  ]).then(showImgs)}

6、Generator

在异步编程的解决方案中,Generator比Promise更高级些。

使用场景:抽奖次数逻辑控制、长轮询(服务器请求报错再次请求, 定时发送请求)

  // 以前控制次数是在全局中申明,既不安全又影响性能  let draw = function (count) {    // 具体抽奖逻辑    console.info(`剩余${count}次`)  }  let residue = function* (count) {    while (count > 0) {      count--      yield draw(count)    }  }  let start = residue(5)  let btn = document.createElement('button')  btn.id='start'  btn.textContent = '抽奖'  document.body.appendChild(btn);  document.getElementById('start').addEventListener('click', function() {    start.next()  }, false)}{  // 长轮询  let ajax = function* () {    yield new Promise(function(resolve, reject) {      setTimeout(() => {        resolve({code: 0})      }, 200);    })  }  let pull = function () {    let generator = ajax()    let step = generator.next()    step.value.then(function(d){      if (d.code !=0 ) {        setTimeout(() => {          console.info('wait')          pull()        }, 100);      } else {        console.info(d)      }    })  }  pull()

通过下面这张流程图,再加深下对Generator的理解。

7、await

在项目中,有时会出现需要同时依赖多个接口,而且必须在这几个请求都处理完后,才能开始处理数据的情况。我们可以在一个 async函数 中写多个await 语法的请求,然后逐个处理,但是这样效率太低了。

多个请求是可以并行执行的。这时就可以结合 Promise.all 高级方法来处理,可以同时发起多个请求,然后统一处理接口返回数据。

为了方便,这里演示同时请求2个url,多个的也是一样的, 如下:

export default {  name: 'hello1',  data () {    return {      msg: 'Hello Vue.js',      info: {},      user1: {},      user2: {}    }  },  methods: {    async getUserInfo () {      try {        const res = await this.$http.get('http://aaa.com/userinfo');        this.info = res.data      } catch (e) {        console.log(e);      }    },    async get2UserInfo () {      try {        const res = await Promise.all([          this.$http.get('http://aaa.com/userinfo1'),          this.$http.get('http://aaa.com/userinfo2'),        ])        this.user1 = res[0].data;        this.user2 = res[1].data;      } catch (e) {        console.log(e);      }    }  },  created () {    this.getUserInfo();    this.get2UserInfo();  }}

再次运行项目,可以发现页面在初始化的时候同时发起了3个请求,并正常渲染出了接口数据。

注意:这里的 Promise.all() 的参数是一个函数执行队列,它们会同时发起,然后都请求成功后,会将队列的每个任务的结果组装成一个结果数据,然后返回。


8、类操作

先实战创建一个List类

import Utils from "./Utils.js";class List {    constructor(title = "", items = [], isEditable = true, id = "") {        this.id = (id) ? id : Utils.guid();        this.title = title;        this.items = items;        this.isEditable = isEditable;    }    render() {        var html = `<div class="list" data-id="${this.id}" data-iseditable="${this.isEditable}">                  <h2 class="list-title">${this.title}</h2>                  ${(this.isEditable) ? "<div class='btn delete-list danger' data-action='delete-list'>X</div>" : ""}                  <ul class="items">`;        this.items.forEach(function(item) {            html += item.render();        });        html += `                </ul>                ${(this.isEditable) ? "<div class='btn add-item success' data-action='add-item'>Add Item</div>" : ""}            </div>`;        return html;    }    getItemById(id) {        let item = this.items.filter(i => i.id === id);        return ((item.length) ? item[0] : null);    }    add(item) {        this.items.push(item);    }    remove(item) {        this.items = this.items.filter(i => (i.id !== item.id));    }}export default List;

在app.js中创建List实例:

import List from "./List.js";import Utils from "./Utils.js";import Status from "./Status.js";class App {    constructor(lists = []) {        this.lists = lists;    }    getDueItems() {        let dueItems = [];        this.lists.forEach(function(list) {            list.items.forEach(function(item) {                if (item.date && item.status === Status.PENDING && Utils.dateDiffInDays(new Date(item.date), new Date()) > 0) {                    dueItems.push(item);                }            });        });        return dueItems;    }    getItemById(id) {        const filterById = (function filterById(id1) {            return function(listItem) {                return listItem.id === id1;            };        }(id));        for (let i = 0; i < this.lists.length; i++) {            let item = this.lists[i].items.filter(filterById);            if (item.length) {                return item[0];            }        }        return null;    }    getListById(id) {        let list = this.lists.filter(l => l.id === id);        return ((list.length) ? list[0] : null);    }    render() {        let pastDueList = new List("Past Due Date", this.getDueItems(), false);        let html = `<div class="app">            <div class="btn add-list success" data-action="add-list">[+] Add List</div>          `;        this.lists.forEach(function(list) {            html += list.render();        });        html += pastDueList.render();        html += "</div>";        return html;    }    add(list) {        this.lists.push(list);    }    remove(list) {        this.lists = this.lists.filter(l => (l.id !== list.id));    }}export default App;

ES6中的类能让我们可以用更简明的语法实现继承,也使代码的可读性变得更高。

9、Proxy

Proxy可以让我们根据不同的业务逻辑进行相应的处理, 对原对象进行映射,生成新的对象,操作新对象的同时通过一定的规则修改原对象。

  // 先定义一个函数  function validator(target,validator) {    return new Proxy(target, {      _validator: validator,      set(targer,key,value,proxy){        if (targer.hasOwnProperty(key)) {          let va = this._validator[key];          if (!!va(value)) {            return Reflect.set(target,key,value,proxy)          }else {            throw Error(`不能设置${key}到${value}`)          }        }else {          throw Error(`${key} 不存在`)        }      }    })  }  const personValidator={    name(val){      return typeof val === 'string'    },    age(val){      return typeof val === 'number' && val >18    }  }  class Person{    constructor(name,age) {      this.name = name      this.age = age      return validator(this, personValidator)    }  }  const person  = new Person('lilei', 30)  console.info(person) // Proxy {name: "lilei", age: 30}  // person.name = 48 // Uncaught Error: 不能设置name到48  // console.info(person)  person.name = 'han mei mei'  console.info(person) // Proxy {name: "han mei mei", age: 30}

点评:使用Proxy进行数据校验,将对象和验证分离开,便于后期代码的维护。

总结

本篇主要通过实际项目中的例子回顾ES6的知识点,帮大家梳理重点和难点。学会ES6语法不难,活学活用到项目才是关键。

希望各位小伙伴能充分认识到ES6的强大,通过在实际工作中不断地使用ES6,提升代码质量和工作效率,这样就能多一点喝茶看电影的时间。

最后祝大家都能成为别人眼中的程序猿大牛,O(∩_∩)O哈哈~