语法
with (expression) {
statement
}
描述
JavaScript查找某个未使用命名空间的变量时,会通过作用域链来查找,作用域链是跟执行代码的context或者包含这个变量的函数有关。'with'语句将某个对象添加到作用域链的顶部,如果在statement中有某个未使用命名空间的变量,跟作用域链中的某个属性同名,则这个变量将指向这个属性值。如果沒有同名的属性,则将拋出异常。
作用
with 语句用于设置代码在特定对象中的作用域!!!
性能方面利弊
-
利:
with语句可以在不造成性能损失的情況下,减少变量的长度。其造成的附加计算量很少。使用'with'可以减少不必要的指针路径解析运算。需要注意的是,很多情況下,也可以不使用with语句,而是使用一个临时变量来保存指针,来达到同样的效果。 -
弊:
with语句使得程序在查找变量值时,都是先在指定的对象中查找。所以那些本来不是这个对象的属性的变量,查找起来将会很慢。如果是在对性能要求较高的场合,'with'下面的statement语句中的变量,只应该包含这个指定对象的属性。
语义不明的弊端
- 弊端:
with语句使得代码不易阅读,同时使得JavaScript编译器难以在作用域链上查找某个变量,难以决定应该在哪个对象上来取值。请看下面的例子:
function f(x, o) {
with (o)
print(x);
}
f被调用时,x有可能能取到值,也可能是undefined,如果能取到, 有可能是在o上取的值,也可能是函数的第一个参数x的值(如果o中没有这个属性的话)。如果你忘记在作为第二个参数的对象o中定义x这个属性,程序并不会报错,只是取到另一个值而已。
- 弊端: 使用
with语句的代码,无法向前兼容,特別是在使用一些原生数据类型的时候。看下面的例子:
function f(foo, values) {
with (foo) {
console.log(values)
}
}
如果是在ECMAScript 5环境调用f([1,2,3], obj),则with语句中变量values将指向函数的第二个参数values。但是,ECMAScript 6标准给Array.prototype添加了一个新属性values,所有数组实例将继承这个属性。所以在ECMAScript 6环境中,with语句中变量values将指向[1,2,3].values。
例子
eg1
var a, x, y;
var r = 10;
with (Math) {
a = PI * r * r;
x = r * cos(PI);
y = r * sin(PI / 2);
}
console.log(a, x, y) //314.1592653589793 -10 10
上面的with语句指定Math对象作为默认对象。with语句里面的变量,分別指向Math对象PI、cos和``sin函数,不用在前面添加命名空间。后续所有引用都指向Math对象。
eg2
const testUrl = {
href: 'http://www.lcj.com'
}
function buildUrl() {
let qs = '?debug=true'
with(testUrl) {
var url = href + qs
}
return url
}
const a = buildUrl()
console.log(a) //http://www.lcj.com?debug=true
上面的例子with语句指定testUrl对象添加到作用域链的顶部。
eg3
const testUrl = {
href: 'http://www.lcj.com'
}
function buildUrl() {
let qs = '?debug=true'
with(testUrl) {
let url = href + qs
}
return url
}
const a = buildUrl()
console.log(a) //ERROR: url is not defined
上面例子将var替换成了 let 报错了。因为:在eg2with语句中使用var声明的变量url会成为函数上下文的一部分,可以作为函数的值被返回;eg3这里使用let声明的变量url,因为被限制在块级作用域,所以在with块外没有被找到而报错。
eg4
function buildUrl() {
let qs = '?debug=true'
with(testUrl2) {
var url = href + qs
}
return url
}
const a = buildUrl()
console.log(a) //ERROR: testUrl2 is not defined
参考描述:如果在statement中有某个未使用命名空间的变量,跟作用域链中的某个属性同名,则这个变量将指向这个属性值。如果沒有同名的属性,则将拋出异常。
eg5-1
function buildUrl() {
let qs = '?debug=true'
let href = '123'
var url = href + qs
return url
}
const a = buildUrl()
console.log(a) //123?debug=true
eg5-2
const testUrl = {
href: 'http://www.lcj.com'
}
function buildUrl() {
let qs = '?debug=true'
let href = '123'
with(testUrl) {
var url = href + qs
}
return url
}
const a = buildUrl()
console.log(a) //http://www.lcj.com?debug=true
上面两个例子证明,with确实设置代码在特定对象中的作用域。