变量
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.getSystemInfo
API来获取设备的屏幕高度,由于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经常写的东西除了数组对象就是回调函数,记不记得用jQuery
的ajax
用得特别爽,如果是多层嵌套调用的话,那些回调函数简直像盖楼梯一样壮观。现在Promise
来了,我们再也不用为这些回调地狱发愁,用Promise
来解决回调问题非常优雅,链式调用也非常的方便。
Promise
是ES6内置的类,其使用简单,简化了异步编程的繁琐层次问题,比较简单的用法是:
new Promise((resolve, reject) => {
//success
//resolve();
//error
//reject();
});
实例化一个Promise
对象,它接受一个函数参数,此函数有两个回调参数,resolve
和reject
,如果正常执行使用resolve
执行传递,如果是失败或者错误可以用reject
来执行传递,其实他们就是一个状态的转换。可以暂时理解为success
和fail
。
来看一下简单的示例:
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
方法同样也是有resolve
和reject
两个回调参数,上层的Promise
执行的回调传递到then
函数中,Promise
的resolve
传递到then
的resolve
,同理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.getUserInfo
和wx.login
这两个接口都用共同的属性success
和fail
,我们可以封装起来:
/**
* @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
处理。