第10章 with語句
with語句是一個強(qiáng)大的窿冯、但經(jīng)常被誤解的、有爭議的js特性确徙。with語句允許我們將一個對象的所有屬性引用到當(dāng)前作用域醒串,允許我們無需使用擁有者對象的前綴,就可以對這些屬性進(jìn)行引用和賦值操作米愿。
重點(diǎn)的一點(diǎn),在js中該語句的存在是暫時的鼻吮,ES5規(guī)范在嚴(yán)格模式下已經(jīng)禁用了該語句育苟。
如果不會閱讀程序代碼,并確保自己知道程序代碼到底要做什么椎木,就沒辦法確保程序會正常工作违柏。出于這個原因博烂,應(yīng)該避免使用with語句∈——Douglas Crockford
10.1 with是怎么回事
with語句會創(chuàng)建一個作用域禽篱,在該作用域內(nèi),在引用特定對象的屬性時馍惹,可以不使用前綴躺率。
10.1.1 在with作用域內(nèi)引用屬性
示例 10.1 利用一個對象創(chuàng)建一個with作用域
test?suite
#results?.pass{color:green;}
#results?.fail{color:red;}
function?assert(value,desc){
var?li?=?document.createElement('li');
li.className?=?value???'pass'?:?'fail';
li.appendChild(document.createTextNode(desc));
document.getElementById('results').appendChild(li);
}
var?use?=?'other';
var?katana?=?{
isSharp:true,
use:function(){
this.isSharp?=?!this.isSharp;
}
};
with(katana){
assert(use?!=?'other'?&&?typeof?use?==?'function','use?is?a?function?from?the?katana?object');
assert(this?!==?katana,'context?isn\'t?changed;?keeps?its?original?value.')
}
assert(use?==?'other','outside?the?with?use?is?unaffected.');
assert(typeof?isSharp?===?'undefined','outside?the?with?the?properties?don\'t?exist.')
注意,在with語句的作用域內(nèi)万矾,對象屬性的優(yōu)先級絕對高于在更高層級作用域內(nèi)定義的同名變量悼吱。這是with被嘲笑的主要原因之一,with作用域內(nèi)的代碼意義可能是含糊不清的良狈。
我們也證明了函數(shù)上下文(this)是不受with語句影響的后添。
101.2 在with作用域內(nèi)進(jìn)行賦值
示例10.2 with作用域內(nèi)的賦值操作
test?suite
#results?.pass{color:green;}
#results?.fail{color:red;}
function?assert(value,desc){
var?li?=?document.createElement('li');
li.className?=?value???'pass'?:?'fail';
li.appendChild(document.createTextNode(desc));
document.getElementById('results').appendChild(li);
}
var?katana?=?{
isSharp:true,
use:function(){
this.isSharp?=?!this.isSharp;
}
};
with(katana){
isSharp?=?false;
assert(katana.isSharp?===?false,'properties?can?be?assigned');
cut?=?function(){
isSharp?=?false;
}
}
assert(typeof?katana.cut?==?'function','new?properties?can?be?created?on?the?scoped?object.')
assert(typeof?window.cut?==?'function','new?properties?are?create?in?the?global?scope.')
如果要在katana上創(chuàng)建新屬性,就需要使用對象引用前綴薪丁,即便是在with作用域內(nèi)也要這樣做遇西,如 katana.cut = function(){isSharp = false;}
10.1.3 性能方面的注意事項(xiàng)
它降低了所包含js代碼的執(zhí)行性能,而不僅僅是局限于與之交互的對象严嗜。
示例10.3 with語句的性能測試
test?suite
#results?.pass{color:green;}
#results?.fail{color:red;}
function?assert(value,desc){
var?li?=?document.createElement('li');
li.className?=?value???'pass'?:?'fail';
li.appendChild(document.createTextNode(desc));
document.getElementById('results').appendChild(li);
}
var?ninja?=?{foo:'bar'},
value,
maxCount?=?1000000,
n,
start,
elapsed;
start?=?new?Date().getTime();
for(n=0;n
value?=?ninja.foo;
}
elapsed?=?new?Date().getTime()-start;
assert(true,'without?with:'+elapsed);
start?=?new?Date().getTime();
with(ninja){
for(n=0;n
value?=?foo;
}
}
elapsed?=?new?Date().getTime()-start;
assert(true,'with?(with?access):'+elapsed);
start?=?new?Date().getTime();
with(ninja){
for(n=0;?n
foo=n;
}
}
elapsed?=?new?Date().getTime()-start;
assert(true,'with?(with?assignment):'?+?elapsed);
start?=?new?Date().getTime();
with(ninja){
for(n=0;?n
value?=?'no?test'
}
}
elapsed?=?new?Date().getTime()-start;
assert(true,'with?(without?access):'?+?elapsed);
without with:31
with (with access):1196
with (with assignment):1211
with (without access):1109
在決定要享受with語句帶來的任何便利時粱檀,我們必須確保適應(yīng)這種程度的額外開銷。
10.2 真實(shí)示例
Prototype中使用的例子:
Object.extend(String.prototype.escapeHTML,{
div:document.createElement('div'),
text:document.createTextNode('')
})
with(String.prototype.escapeHTML) div.appendChild(text);
在這里阻问,Prototype使用with語句梧税,避免了String.prototype.escapeHTML的div和text屬性的引用前綴,否則的話,每個屬性都要使用前綴。
可以實(shí)現(xiàn)相同目標(biāo)而又不必使用with作用域的方法
(function(s){
s.div.appendChild(s.text)
})(String.prototype.escapeHTML)
在即時函數(shù)的作用域內(nèi)偏竟,長引用String.prototype.escapeHTML可以通過一個簡單的函數(shù)參數(shù)s進(jìn)行引用登渣。很多開發(fā)都認(rèn)為,將一個復(fù)雜的引用轉(zhuǎn)換成一個簡單引用艘狭,遠(yuǎn)比完全消除前綴的方式要好。
base2庫的另外一個示例
with(document.body.style){
backgroundRepeat = 'no-repeat';
backgroundImage = 'url(...)';
backgroundAttachment = 'fixed';
}
10.3 導(dǎo)入有命名空間的代碼
YAHOO.util.Event.on(
[YAHOO.util.Dom.get('item'),YAHOO.util.Dom.get('otheritem')],
'click',function(){
YAHOO.util.Dom.setStyle(this,'color','#c00');
}
)
with(YAHOO.util.Dom){
YAHOO.util.Event.on([get('item'),get('otheritem')],'click',function(){setStyle(this,'color','#c00')})
}
額外增加一個with語句,極大地增加了代碼的簡單性尸执。
10.4 測試
new Test.Unit.Runner({
testSliderBasics:function(){
with(this){
var slider = new Control.Slider('handle1','track1');
assertInstanceOf(Control.Slider,slider);
assertEqual('horizontal',slider.axis);
assertEqual(false,slider.disabled);
assertEqual(0,slider.value);
slider.dispose();
}
}
})
通過使用with(this),得到更簡單的代碼缓醋。
10.5 使用with進(jìn)行模板化
模板系統(tǒng)的目標(biāo)通常包括以下功能:
.應(yīng)該有一種運(yùn)行嵌入式代碼和打印數(shù)據(jù)的方法
.應(yīng)該有一種緩存編譯模板的方法
.訪問映射數(shù)據(jù)應(yīng)該很簡單
總的來說如失,一個易于使用的模板系統(tǒng),在很大程度上送粱,取決于with語句的能力褪贵。