在微信小程序示例代码里面使用到了非常多的for+i+function+function这种组合代码,有时看了头晕,到底是什么意思?起什么作用或者说解决什么问题?它跟javascript的closure(闭包)有什么关联?今天我们就来学习一下它。
下面使用一个作为例子:
var toastNum = 3
var pageData ={}
pageData.data ={}
for(var i =0; i <= toastNum; ++i) {
pageData.data['toast'+i+'Hidden'] = true;
(function (index) {
pageData['toast'+index+'Change'] = function(e) {
var obj = {}
obj['toast'+index+'Hidden'] = true;
this.setData(obj)
}
pageData['toast'+index+'Tap'] = function(e) {
var obj = {}
obj['toast'+index+'Hidden'] = false
this.setData(obj)
}
})(i)
}
Page(pageData)
这段代码来自微信小程序示例代码: page/component/component-pages/wx-toast/wx-toast.js
这段代码的代码的主要意思是通过一个for循环,把object对象pageData里的object变量data的toast0Hidden,toast1Hidden,toast2Hidden,toast3Hidden变量赋值都赋值成true,把object对象pageData里toast0Change,toast1Change,toast2Change,toast3Change变量分别赋值一个匿名回调函数,此回调函数如下:
function(e) {
var obj = {}
obj['toast'+index+'Hidden'] = true;
this.setData(obj)
}
把object对象pageData里toast0 Tap,toast1 Tap,toast2C Tap,toast3Tap变量分别赋值一个匿名回调函数,此回调函数如下:
function(e) {
var obj = {}
obj['toast'+index+'Hidden'] = false
this.setData(obj)
}
但是为什么for循环里面还有一个
(function (index) {
……
})(i)
这样的东西呢?是不是多此一举,让代码复杂化,能不能去掉,如果不是不能,哪又起什么作用呢?
为了理解这个东西,我们先学习一点点javascript的作用域的知识
在javascript的世界里,只有全局作用域和函数作用域,没有块作用域,那么什么是全局作用域,什么又是函数作用域呢?全局作用域就是在全局范围都起作用的一些,其中囊括在函数之外定义的变量和函数内没有声明为var的变量,如果上面的toastNum,pageData,特别指出的是i它也是,而函数作用域就是在函数以内的作用范围,包括参(),函数体{}内,比如上面代码的var obj={}中的obj和函数参数index。
为了看到效果,我们把去掉,调整代码如下:
for(var i = 0; i<= toastNum; ++i) {
pageData.data['toast'+i+'Hidden'] = true;
pageData['toast'+i+'Change']= function(e) {
var obj = {}
obj['toast'+i+'Hidden'] = true;
this.setData(obj)
}
pageData['toast'+i+'Tap'] = function(e) {
var obj = {}
obj['toast'+i+'Hidden'] = false
this.setData(obj)
}
}
调试时看变量初始化,好像都正常,该有的都有了
那咱们继续,点击界面上其中的某个按钮,调试结果:
调试的i,怎么变成4,进而形成toast4Hidden,从上上图我们知道,我们最高是3,怎么跑出4来了?哦,原来,i由于javascript里没有区块作用域,所以i现在全局作用域,并且只有一个i,当for循环完后i就变成了4,所以当回调函数使用它时就是4了,那么跟我们想的不一样,我们想的是当点击第一个btn时,就是相应的toast1Hidden,那怎么办?一种通用的就办法就引了函数作用域,就是javascript所说的closure(闭包),就是把i当参数传入一个函数,然后被传入的函数有一个参数是用来复制保存当时的i的,如下代码:
(function (index) {
……
})(i)
所以现在只有4个index变量(for循环以后),复制缓存i的四个状态:0,1,2,3,而这4个index的作用是函数作用域,在上面function(){}的{}里头有效,所以就可以实现我们之前想的目标,即函数里的index是对应的。
最后代码我贴一下,大家自己去体会下之间的不同:
Js文件代码:
var toastNum = 3
var pageData ={}
pageData.data ={}
for(var i = 0; i<= toastNum; ++i) {
pageData.data['toast'+i+'Hidden'] = true;
(function (index) {
pageData['toast'+index+'Change'] =function(e) {
var obj = {}
obj['toast'+index+'Hidden'] = true;
this.setData(obj)
}
pageData['toast'+index+'Tap'] = function(e){
var obj = {}
obj['toast'+index+'Hidden'] = false
this.setData(obj)
}
})(i)
}
Page(pageData)
Wxml文件代码:
<viewclass="page">
<view class="page__hd">
<textclass="page__title">toast</text>
<textclass="page__desc">toast提示</text>
</view>
<view class="page__bd">
<view class="btn-area">
<view class="body-view">
<toasthidden="{{toast1Hidden}}" bindchange="toast1Change">
默认
</toast>
<button type="default"bindtap="toast1Tap">点击弹出默认toast</button>
</view>
<view class="body-view">
<toasthidden="{{toast2Hidden}}" icon="warn"bindchange="toast2Change">
设置icon
</toast>
<button type="default" bindtap="toast2Tap">点击弹出设置icon的toast</button>
</view>
<view class="body-view">
<toasthidden="{{toast3Hidden}}" duration="3000"bindchange="toast3Change">
设置duration
</toast>
<button type="default"bindtap="toast3Tap">点击弹出设置duration的toast</button>
</view>
</view>
</view>
</view>