这是我参与8月更文挑战的第4天,活动详情查看:8月更文挑战
前言
本系列将分多篇文章介绍 js 相关的“冷门知识”,这里的“冷门知识”是指大部分开发者都不曾留意过的知识点,亦或者是意料不到的知识点。这些知识点对现实中的面试、开发也许没有很大的帮助,甚至压根没任何用处,但希望以此博得大家一乐,并额外拓展下知识面。
本系列文章的大部分知识参考于阮一峰写的“网道 - 互联网开发文档 (wangdoc.com)”,强烈建议 web 前端开发者都看下这份文档,既是入门的好帮手,也是拓展知识面的利器。
本系列文章的任何参考,均会在文末标出。
with 语句
我相信大部分人都没有认识过 js 的 with
语句,大概是因为很少有人或教材提起过它,因为它的使用弊端多且严重。
下面我们看看 with
有什么用。
MDN 是这样讲解的。
with语句 扩展一个语句的作用域链。
看完这个定义,大概没人会理解吧。举个栗子,就容易理解了。
比如:
var obj = {
a: 1,
print() {
console.log('a 的值为:', this.a)
}
}
with(obj) {
a = 2
print()
}
上述代码运行后,会将 obj
的 a
属性的值改变为 2
,并且运行 obj
的 print
方法输出 a
的值。也就是说 with
语句里面的 a = 2
其实等同于 obj.a = 2
;with
语句里面的 print()
等同于 obj.print()
我们可以看到 with
语句的设计初衷大概是希望简化对象的调用,省略 obj.
这一前缀。
再举多一个栗子。
with ('abc') {
console.log(toUpperCase()) // 'ABC'
}
上面的代码相当于 console.log('abc'.toUpperCase())
,将 abc
大写化并输出。
有没有注意到传入的 'abc'
是基本类型中的字符串,并非对象,但是 with
语句是针对对象的,为什么这里可以用字符串呢?
这其实与 with
语句无关。我在这里额外提一个知识点:包装对象。
我相信很多人都有这样写过代码。
var str = 'abc'
if (str.length === 3) {
...
}
但是大概很少有人质疑过为什么作为基本类型的字符串 str
可以像对象那样调用属性 length
。
那是因为 js 引擎在某些场合下会自动将基本类型转为对应的包装对象,像上面的字符串 str
就转为一个临时的字符串对象(用完立刻销毁),相当于做了 new String(str)
的操作,因此实际上代码 str.length
在引擎里表现为 new String(str).length
,所以作为基本类型的字符串 str
看起来带有属性 length
,事实上 str
是不带任何属性和方法。具体可以看看“网道”的这一章节“包装对象 - JavaScript 教程 - 网道 (wangdoc.com)”。
回到正题,也就是说上述代码是等同于下面的代码。
with (new String('abc')) {
console.log(toUpperCase()) // 'ABC'
}
优点
这里直接引用 MDN 里的解释。
with 语句可以在不造成性能损失的情況下,减少变量的长度。其造成的附加计算量很少。使用'with'可以减少不必要的指针路径解析运算。需要注意的是,很多情況下,也可以不使用with语句,而是使用一个临时变量来保存指针,来达到同样的效果。
缺点
with 语句看起来还有一点的使用价值,但事实上它存在各种弊端。
意外创建全局变量
我们先看下以下代码。
with(obj) {
a = 2
}
console.log(obj.a) // 输出1
console.log(a) // 输出2
这里的“输出1”和“输出2”的结果要看 obj
是否带有 a
这个属性。
如果 obj
带有属性 a
,那么会预期将 obj
的 a
属性赋予 2
的值,“输出1”的结果为 2
,“输出2”会报错 a is not defined
。
如果 obj
不带有属性 a
,我们可能会认为产生这样的效果 obj.a = 2
,obj
将赋予属性 a
且 a
的值等于 2
,但事实上并不会,而是会在全局作用域里创建一个全局变量 a
,a
的值等于 2
,也就是说,“输出1”的结果为 undefined
,“输出2”的结果为 2
。
这种特性会意外地创建全局变量,从而污染全局作用域。
语义不明
当存在像以下这样的代码,如果我们不知道 b
里面有什么属性和方法,我们怎么知道 a
是 b
里的 a
,还是 function 的入参 a
呢?
function f(a, b) {
with (b) {
console.log(a);
}
}
造成性能损失
这里直接引用 MDN 的相关解释。
with 语句使得程序在查找变量值时,都是先在指定的对象中查找。所以那些本来不是这个对象的属性的变量,查找起来将会很慢。如果是在对性能要求较高的场合,'with'下面的statement语句中的变量,只应该包含这个指定对象的属性。
总结
with
语句就是一个没卵用的语句,w3school 和 MDN 都明确不推荐使用。在 js 的严格模式下,with
语句甚至是完全禁用的。