JavaScript strict模式与with关键字:为何应避免使用
本文介绍ECMAScript 5引入的strict模式及其对with关键字的限制。详细解析with语句因变量作用域不明确导致的歧义问题,并提供使用局部变量或立即执行函数等替代方案,帮助开发者编写更清晰、高性能的JavaScript代码。
ECMAScript 5与strict模式的引入
2009年12月,ECMAScript发布了ECMAScript 5标准,距离上一个ECMAScript 3版本发布已过去十年。在此期间,尽管JavaScript在Web编程中广泛应用,但由于各大厂商和组织对语言复杂性(即是否增加大量特性以扩展功能)存在分歧,ECMAScript 4标准最终夭折,导致新标准的制定滞后于编程实践。ECMAScript 5的目标更为务实,除了新增对JSON的支持和对反射的更完整控制外,一项重要改进是引入了“严格模式”(strict mode)。在该模式下,ECMAScript的语法变得更加严格,禁止了许多常见的易错代码,包括强制变量声明和禁止使用with语句。启用该模式非常简单,只需在脚本文件或函数的首行添加"use strict";字符串即可。
笔者后知,2010年曾撰写一篇短文讨论with关键字的缺陷,内容如下。
楔子
很久以前,神笔马良的家乡为纪念他,拟将一条马路以他的名字命名。马良未加推辞,但提出了四字意见。多年后,一位外地人来到此地,在街上拦住当地人问路。
请问这是神马路?
对,这是神马路。
你也不知道吗?
我就是这的人,怎么会不知道。
那这是神马路?
你知道了还问什么。
我就是不知道这是神马路。
那我不是已经告诉你这是神马路了吗?
你能不能再说一遍这是神马路?
……
事后,这位当地人想起当年神笔马良的意见,恍然大悟。马良说的是——勿用简称。
A Question
One day Tom said to Wang Er, his Chinese friend,“I have a dream. I want to show myself on CCTV.”The next day Tom broke into a neighbor shop. The police didn't take much time to identify and arrest Tom because he was captured very clearly by the shop's CCTV.
The question is when Tom said his dream, he is
A) ambitious B) not ambitious C)ambiguous D) unambiguous
The right answer is B) and C).
正文
with关键字的歧义问题
以上两个古今中外的例子说明,简写有时会引起歧义。这在JavaScript中也存在。有时候反复引用一个名字很长的变量是很麻烦的,比如:
objectWithLongName1.propty1=value1;
objectWithLongName1.propty2=value2;
objectWithLongName1.propty3=value3;
objectWithLongName1.method1();
但是一个清晰的名字对于程序的可读性又是很重要的。所以JavaScript提供了with语句。上面的例子可以改写成:
代码如下:
with (objectWithLongName1){
propty1=value1;
propty2=value2;
propty3=value3;
method1();
} 这样省去不少敲打键盘的功夫,而且程序的结构也变得更加清晰。但是这样的简写引入了歧义,我们如何知道大括号内的名称,哪些是objectWithLongName1的属性和方法,哪些是外部变量和函数。JavaScript的解析规则是,先在objectWithLongName1上查找这些名称的属性,如果没有找到,则认为它们是外部变量。用代码说明就是这样:
代码如下:
if(objectWithLongName1.property1!==undefined){
if(objectWithLongName1.value1!==undefined){
objectWithLongName1.property1=objectWithLongName1.value1; //可能1
}else{
objectWithLongName1.property1=value1;//可能2
}
}else{
if(objectWithLongName1.value1!==undefined){
property1=objectWithLongName1.value1; //可能3
}else{
property1=value1;//可能4
}
} 我们希望的是这四种可能性之一,但是一不小心,程序执行的就会是另外一种可能。而且,这样的写法对于程序的读者来说也非常难解。另一方面,对于JavaScript解释器,这种不确定性也影响了语言的性能。
其实只要一个小小的改进,就可以祛除这些缺陷。我们可以在省略了对象的属性前面加上点号,这样就在属性和外部变量之间加上了直观的区分,有不少其他语言就是这样做的。我们最初的例子会变成这样:
代码如下:
with (objectWithLongName1){
.propty1=value1;
.propty2=value2;
.propty3=value3;
.method1();
} 避免使用with的替代方案
在JavaScript做这样的改进之前,两害相权取其轻,要尽量避免使用with语句。我们仍然可以采用一些变通的方法。
代码如下:
var o1= objectWithLongName1;
o1.propty1=value1;
o1.propty2=value2;
o1.propty3=value3;
o1.method1(); 或者对于这样的情况:
objectWithLongName1.propty1= objectWithLongName2.propty1;
objectWithLongName1.propty2= objectWithLongName2.propty2;
……
objectWithLongName1.propty10= objectWithLongName2.propty10;
可以写成:
代码如下:
(function(o1, o2, pl){
pl.forEach(function(item){o1[item]=o2[item];});
})( objectWithLongName1,objectWithLongName2, [‘propty1', ‘propty2', … , ‘propty10']);JavaScript入门书籍避坑:这些书不适合初学者
2026-11-05 16:31:15
JS使用Math.round实现保留两位小数的原理与代码
2026-11-05 15:35:55
JavaScript 原生 Date 对象怎么实现日期加减
2026-11-05 14:30:00
JavaScript函数定义的三种方式:声明、表达式与箭头函数
2026-11-05 13:50:55
JavaScript中escape、encodeURI和encodeURIComponent对空格的处理差异
2026-11-05 12:47:23
