ES6的基本知识

变量

ES6中声明变量可以用let声明变量,用const声明常量,即不可改变的量。

let version = '1.0.0';
const weekday = 7;

version = '2.0.0';
weekday = 8; //错误,用const声明的常量,不能修改值

本习惯用大写字母和下划线的组合方式来声明全局的常量

const CONFIG_COLOR = '#FAFAFA';

对象方法属性

小程序的每一个页面都有一个相对应的js文件,里面必不可少的就是Page函数,Page函数接受的参数是一个对象,我们经常使用的写法就是:

Page({
    data: {
        userAvatar: './images/avatar.png',
        userName: 'Oopsguy'
    },
    onLoad: function() {
        //....
    },
    onReady: function() {
        //....
    }
});

现在换做ES6的写法,我们可以这样:

Page({
    data: {
        userAvatar: './images/avatar.png',
        userName: 'Oopsguy'
    },
    onLoad() {
        //....
    },
    onReady() {
        //....
    }
});

我们可以把以前的键值写法省略掉,而且function声明也不需要了。

ES6中拥有了这一概念,声明类的方式很简单,跟其他语言一样,差别不大:

class Animal {
    constructor() {

    }

    eat() {

    }

    static doSomething(param) {
        //...
    }
}

module.exports = Animal;

class关键字用于声明类,constructor是构造函数,static修饰静态方法。不能理解?我们看一下以前的js的简单写法:

var Animal = function() {

};

Animal.prototype.eat = function() {

};

Animal.doSomething = function(param) {

};

module.exports = Animal;

简单的调用示例

let animal = new Animal();
animal.eat();
//静态方法
Animal.doSomething('param');

这里只是简单的展示了一下不同点,更多的只是还是需要读者自己翻阅更多的资料来学习。

解构

其实本人对结构也不太懂怎样解释,简单的来说就是可以把一个数组的元素或者对象的属性分解出来,直接获取,哈哈,解释的比较勉强,还是看看示例吧。

let obj = {
    fullName: 'Xiao Ming',
    gender: 'male',
    role: 'admin'
};

let arr = ['elem1', 1, 30, 'arratElem3'];

let {fullName, role} = obj;
let [elem1, elem2] = arr;

console.log(fullName, role, elem1, elem2);

大家可能猜出了什么,看看输出结果:

> Xiao Ming admin elem1 1

我们只要把需要获取的属性或者元素别名指定解构体中,js会自动获取对应的属性或者下标对应的元素。这个新特性非常有用,比如我们需要在一个Pages data对象中一个属性获取对了属性值:

let year = this.data.year,
    month = this.data.month,
    day = this.data.day;

但是用解构的写法就很简洁:

let {year, month, day} = this.data;

再比如引入一个文件:

function getDate(dateStr) {
    if (dateStr) {
        return new Date(Date.parse(dateStr));
    }
    return new Date();
}

function log(msg) {
    if (!msg) return;
    if (getApp().settings['debug'])
    console.log(msg);
    let logs = wx.getStorageSync('logs') || [];
    logs.unshift(msg)
    wx.setStorageSync('logs', logs)
}

module.exports = {
    getDate: getDate,
    log: log
};

现在引入并调用外部文件的方法:

import {log} from '../../utils/util';

log('Application initialized !!');

import...from...是ES6的引入模块方式,等同于小程序总的require,但import可以选择导入哪些子模块。

箭头函数(Arrow Function)

刚开始我也不知道js的箭头函数到底是什么东西,用了才发现,这特么就是lambda表达式么。箭头函数简化了函数的写法,但是还是跟普通的function有区别,主要是在作用域上。

比如我们需要请求网络:

wx.request({
  url: 'url', 
  header: {
      'Content-Type': 'application/json'
  },
  success: function(res) {
    console.log(res.data)
  }
});

用函数还是可以简化一定的代码量,哈哈哈。

wx.request({
  url: 'url', 
  header: {
      'Content-Type': 'application/json'
  },
  success: (res) => {
    console.log(res.data)
  }
});

注意到那个success指向的回调函数了么,function关键字没了,被醒目的=>符号取代了。看到这里大家是不是认为以后我们写function就用箭头函数代替呢?答案是不一定,而且要非常小心!

function和箭头函数虽然看似一样,只是写法简化了,其实是不一样的,function声明的函数和箭头函数的作用域不同,这是一个不小心就变坑的地方。

Page({
    data: {
        windowHeight: 0
    },
    onLoad() {
        let _this = this;
        wx.getSystemInfo({
            success: function(res) {
                _this.setData({windowHeight: res.windowHeight});
            }
        });
    }
});

一般我们获取设备的屏幕高度差不多是这样的步骤,在页面刚加载的onLoad方法中通过wx.getSystemInfoAPI来获取设备的屏幕高度,由于success指向的回调函数作用域跟onLoad不一样,所以我们无法像onLoad函数体中直接写this.setData来设置值。我们可以定义一个临时变量指向this,然后再回调函数中调用。

哪箭头函数的写法有什么不一样呢?

Page({
    data: {
        windowHeight: 0
    },
    onLoad() {
        let _this = this;
        wx.getSystemInfo({
            success: (res) => {
                _this.setData({windowHeight: res.windowHeight});
            }
        });
    }
});

运行之后好像感觉没什么区别呀,都能正常执行,结果也一样。确实没什么区别,你甚至这样写都可以:

Page({
    data: {
        windowHeight: 0
    },
    onLoad() {
        wx.getSystemInfo({
            success: (res) => {
                this.setData({windowHeight: res.windowHeight});
            }
        });
    }
});

咦?这样写,this的指向的作用域不是不一样么?其实这就是要说明的,箭头函数是不绑定作用域的,不会改变当前this的作用域,既然这样,在箭头函数中的this就会根据作用域链来指向上一层的作用域,也就是onLoad的作用域,所以他们得到的结果都是一样的。

其实我个人的习惯是无论用普通的函数写法还是箭头函数的写法,都习惯声明临时的_this来指向需要的作用域,因为箭头函数没有绑定作用域,写的层次深了,感觉就会很乱,理解起来比较困难,在后面的案例中,我也会延续这个习惯。

Promise

写js经常写的东西除了数组对象就是回调函数,记不记得用jQueryajax用得特别爽,如果是多层嵌套调用的话,那些回调函数简直像盖楼梯一样壮观。现在Promise来了,我们再也不用为这些回调地狱发愁,用Promise来解决回调问题非常优雅,链式调用也非常的方便。

Promise是ES6内置的类,其使用简单,简化了异步编程的繁琐层次问题,比较简单的用法是:

new Promise((resolve, reject) => {
    //success
    //resolve();

    //error 
    //reject();
});

实例化一个Promise对象,它接受一个函数参数,此函数有两个回调参数,resolvereject,如果正常执行使用resolve执行传递,如果是失败或者错误可以用reject来执行传递,其实他们就是一个状态的转换。可以暂时理解为successfail

来看一下简单的示例:

let ret = true;
let pro = new Promise((resolve, reject) => {
    ret ? resolve('true') : reject('false');
}).then((res) => {
    console.log(res);
    return 'SUCCESS';
}, (rej) => {
    console.log(rej);
    return 'ERROR';
}).then((success) => {
    console.log(success);
    let value = 0 / 1;
}, (error) => {
    console.log(error);
}).catch((ex) => {
    console.log(ex);
});

或许我们已经看出些什么了,实例化出一个Promise,根据ret的布尔值决定是否resolve执行正常回调流程还是执行reject回调走意外的流程,显然ret是true,当执行resolve时,传递了一个字符串参数true,可以看到实例化出来的Promise对象后面链式调用了很多then方法,其实then方法同样也是有resolvereject两个回调参数,上层的Promise执行的回调传递到then函数中,Promiseresolve传递到thenresolve,同理reject也一样,之后我们发现最后一个catch函数,这是一个捕抓异常的函数,当流程发生异常,我们可以在catch方法中获取异常并处理。

可能解释的比较羞涩,看看下面例子,发出一个网络请求,获取用户头像,再把用户头像插入DOM中,再睡眠2000ms,再打印出SUCCESS,再睡眠3000ms,在alert出ERROR,再休眠1000ms,最后打印出ERROR。这...看起来有点丧心病狂,但只是举个例子:

$.get('/user/1/avatar', (data) => {
    $('#avatar img').attr('src', data['avatar']);
    setTimeout(() => {
        console.log('SUCCESS');
        setTimeout(() => {
            alert('ERROR');
            setTimeout(() => {
                console.log('ERROR');
            }, 1000);
        }, 3000)
    }, 2000);
});

一共有四个回调函数,也不算多,如果有十几个回调呢?直至是噩梦呀。一层一层的嵌套,看起来已经眼花了。那么Promise能做些什么改变呢?

function sleep(time) {
    return new Promise((resolve) => {
        setTimeout(resolve, time);
    });
}

new Promise((resolve) => {
    $.get('/user/1/avatar', resolve);
}).then((avatar) => {
    $('#avatar img').attr('src', avatar);
}).then(() => {
    return sleep(2000);
}).then(() => {
    console.log('SUCCESS');
    return sleep(3000);
}).then(() => {
    alert('ERROR');
    return sleep(1000);
}).then(() => {
    console.log('ERROR');
});

额...看起来怎么使用Promise代码量比不使用的还多呀。不要介意,嘿嘿,可能是我个人封装不精,但是使用Promise的代码可读性确实比上面的要好很多,而且我们不必写一堆的嵌套回调函数,在享受使用同步写法的待遇,又可以得到异步的功能,两全其美,这样的写法还是比较符合日常的思维方式,哈哈。

看看小程序中怎么应用,在小程序项目的app.js中,我们经常看见这段代码:

App({
    getUserInfo:function(cb){
        var that = this
        if(this.globalData.userInfo){
            typeof cb == "function" && cb(this.globalData.userInfo)
        }else{
            wx.login({
                success: function () {
                    wx.getUserInfo({
                        success: function (res) {
                            that.globalData.userInfo = res.userInfo
                            typeof cb == "function" && cb(that.globalData.userInfo)
                        }
                    })
                }
            })
        }
    }
});

这是个方法是获取当前用户的信息,首先先检查globalData对象中有没有缓存有userInfo对象(存储用户的信息),如果有就返回给用户传进来的回掉函数,否则就请求接口获取用用户信息,获取用户信息之前,微信小程序要求先调用wx.login认证,才能调用wx.getUserInfo接口。

看的出代码的层次已经有点深了,我们可以用Promise来简化一下(-_-|| 说的有点夸张,实际上这点嵌套还是可以的)

wx.getUserInfowx.login这两个接口都用共同的属性successfail,我们可以封装起来:

/**
 * @param {Function} func 接口
 * @param {Object} options 接口参数
 * @returns {Promise} Promise对象
*/
function promiseHandle(func, options) {
  options = options || {};
  return new Promise((resolve, reject) => {
    if (typeof func !== 'function')
        reject();
    options.success = resolve;
    options.fail = reject;
    func(options);
  });
}

App({
    getUserInfo(cb) {
        if (typeof cb !== "function") return;
        let that = this;
        if (that.globalData.userInfo) {
            cb(that.globalData.userInfo);
        } else {    
            promiseHandle(wx.login)
                .then(() => promiseHandle(wx.getUserInfo))
                .then((res) => {
                    that.globalData.userInfo = res.userInfo;
                    cb(that.globalData.userInfo);
                })
                .catch((err) => {
                    log(err);
                });
        }
    }
});  

可以看出,使用了Promise之后,代码简洁了不少,层次深度也降低了不少,好家伙,很管用!

其实本次代码中的回调嵌套很少的,为了尽量使用到ES6的新特性,少量的回调嵌套也使用了Promise处理。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值