1. 甚么是旋量群?
非官方基本概念:旋量群是指无权出访另两个表达式codice中的表达式的表达式
标识符块:
function fn1() {
var name = Fuleny
return function() {
console.log(name)
}
}
var fn2 = fn1()
fn2()
// Fuleny
在前述标识符中,旋量群指的是return function() { console.log(name) }那个表达式。
那个表达式在fn1表达式作用域外部,因此能出访到表达式外部的表达式name,fn1表达式外部的codice由自上而下codice下的表达式fn2转交,初始化fn2就能同时实现在自上而下表达式下出访局部表达式中的表达式的值。
两个经典之作的循环式点选的范例:
<ul>
<li>li_1</li>
<li>li_2</li>
<li>li_3</li>
<li>li_4</li>
</ul>
var elements = document.getElementsByTagName(li),
length = elements.length
for (var i = 0; i < length; i++) {
elements[i].onclick = function() {
console.log(i);
}
}
// 4 4 4 4
解答:
给每两个li标签绑定了click事件,但绑定的表达式的codice中没有表达式i,此时i为undefined,则解析引擎就会寻找父级的codice,父级codice中有i,但此时的i已经循环式完i已经赋值为4,所以每个li标签被点选时,控制台都会打印出4。这是codice的问题。
旋量群只能取得包含表达式中任何表达式的最后两个值。因为别忘了旋量群所保存的是整个表达式对象,而不是某个特殊的表达式。
接下来采用旋量群解决那个问题:
var elements = document.getElementsByTagName(li),
length = elements.length
for (var i = 0; i < length; i++) {
elements[i].onclick = logLiIndex(i)
}
function logLiIndex(num) {
return function() {
console.log(num)
}
}
// 0 1 2 3
// 也能直接采用匿名旋量群表达式for (var i = 0; i < length; i++) {
elements[i].onclick = function(num){
return function() {
console.log(num)
}
}(i)
}
for (var i = 0; i < length; i++) {
(function() {
element(i).onclick = function() {
console.log(i)
}
})()
}
如果不想采用过多的旋量群,当然采用块级codice也可解决:
for (let i = 0; i < length; i++) {
elements[i].onclick = function() {
console.log(i)
}
}
// 0 1 2 3// 块级codice的到来,也让令人诟病的JS有了一丝生机,补上了JS的短板
2. 旋量群可被利用的常用情景
2.1 采用表达式引用方式的setTimeout初始化
原生的setTimeout传递的第两个表达式不能带参数
setTimeout(function(params) {
console.log(params)
}, 1000)
// undefined
如果一段标识符想要通过setTimeout来初始化,那么它的第两个参数需要传递两个表达式对象的引用,第二个参数是需要延迟的毫秒数,但那个表达式对象的引用无法为将要被执行的对象提供参数。
但是能初始化两个表达式来返回两个外部表达式的初始化,将那个外部表达式对象的引用传递给setTimeout表达式。外部表达式需要的参数,外部表达式为传递给它。
function log(num) {
return function() {
console.log(`第${num}秒后打印`)
}
}
setTimeout(log(2), 2000)
// 第2秒后打印
2.2 回调
大部分我们所写的JS标识符中都是基于事件的——定义某种行为,然后添加到用户触发的事件之上(比如点选或者按键),我们的标识符通常作为回调:为响应事件而执行的表达式。
例如在页面上添加一种能调整字号的按钮:
body {
font-family: Helvetica, Arial, sans-serif;
font-size: 12px;
}
h1 {
font-size: 1.5em;
}
h2 {
font-size: 1.2em;
}
由于采用的是相对单位,只需修改body中的font-size属性,其他元素也会相对调整。
function makeSizer(size) {
return function() {
document.body.style.fontSize = size + px
}
}
var size12 = makeSizer(12)
var size14 = makeSizer(14)
var size16 = makeSizer(16)
// 分别将他们添加到按钮的点选事件上
document.getElementById(size_12).onclick = size12
document.getElementById(size_14).onclick = size14
document.getElementById(size_16).onclick = size16
<a href=“#” id=“size_12”></a>
<a href=“#” id=“size_14”></a>
<a href=“#” id=“size_16”></a>
2.3 用旋量群模拟私有方法
编程语言中JAVA是支持将方法声明为私有的,而JavaScript没有这种原生支持,但能采用旋量群来模拟私有方法。私有方法不仅仅有利于限制对标识符的出访:还提供了管理自上而下命名空间的强大能力,避免了非核心的标识符弄乱了标识符的公共接口。
var Counter = (function() {
var privateCounter = 0 // 私有表达式
function changBy(val) {
privateCounter += val
}
return {
intcrement: function() { // 三个旋量群共用两个词法语境
changBy(1)
},
delcrement: function() {
changBy(–1)
},
value: function() {
return privateCounter
}
}
})()
console.log(Counter.value()) // 0
Counter.intcrement()
Counter.intcrement()
console.log(Counter.value()) // 2
Counter.delcrement()
console.log(Counter.value()) // 1
能把那个表达式储存在另两个表达式makeCounter中并用它来创建多个计数器。
var makeCounter = (function() {
var privateCounter = 0
function changBy(val) {
privateCounter += val
}
return {
intcrement: function() {
changBy(1)
},
delcrement: function() {
changBy(–1)
},
value: function() {
return privateCounter
}
}
})()
var Counter1 = makeCounter()
var Counter2 = makeCounter() // 每个旋量群都是引用自己词法codice内的表达式privateCounter
console.log(Counter1.value(), Counter2.value()) // 0 0
Counter1.intcrement()
console.log(Counter1.value(), Counter2.value()) // 1 0
两个旋量群内对表达式的修改,不会影响另两个旋量群的表达式
以这种方式采用旋量群,提供了许多面向对象编程的相关好处——特别是数据隐藏和封装3. 旋量群的优劣
优点:
表达式长期驻扎在内存中
避免自上而下表达式的污染
能定义私有属性和私有方法旋量群的典型框架应该是jQuery,旋量群是JavaScript的一大特点,主要应用旋量群场合是为了:设计私有的方法和表达式。
这在做框架的时候体现更明
缺点:
常驻内存 会增大内存的采用量 采用不当会造成内存泄露
能改变父表达式外部表达式的值旋量群有两个非常严重的问题,那是内存浪费问题,那个内存浪费不仅仅因为它常驻内存,更重要的是,对旋量群的采用不当会造成无效内存的产生