关于 JavaScript 的 with 语句

May 17th, 2010 Add Comment

JavaScript 有个 with 关键字, with 语句的原本用意是为逐级的对象访问提供命名空间式的速写方式. 也就是在指定的代码区域, 直接通过节点名称调用对象.

用过 Java 和 .NET 的同学对包或命名空间的概念应该不会陌生, 正因为有这个概念, 使代码的简洁易读得到了保证. 不知 JavaScript 设计之初是如何定位 with 语句的, 个人觉得它们之间有一定的相似度. 如:

apple.banana.candy.dog.egg.fog.god.huh.index = 0;
doSomething(apple.banana.candy.dog.egg.fog.god.huh.index);

利用 with 语句, 可以写为以下代码.

with(apple.banana.candy.dog.egg.fog.god.huh) {
	c = 0;
	doSomething(index);
}

看起来很美妙, 却存在致命的缺陷. 下面我们来进行一些小测试吧.

1. 在 with 语句内部通过内部变量修改数值

var root = {
	branch: {
		node: 1
	}
};
 
with(root.branch) {
	node = 0;
	// 显示 0, 正确!
	alert(node);
}
// 显示 0, 正确!
alert(root.branch.node);

2. 在 with 语句内部通过对象节点修改数值

var root = {
	branch: {
		node: 1
	}
};
 
with(root.branch) {
	root.branch.node = 0;
	// 显示 0, 正确!
	alert(node);
}
// 显示 0, 正确!
alert(root.branch.node);

经过测试 1 和测试 2, 乍看没什么问题, 但是... 请看测试 3.

3. 在 with 语句内部通过对象父节点修改数值

var root = {
	branch: {
		node: 1
	}
};
 
with(root.branch) {
	root.branch = {
		node: 0
	};
	// 显示 1, 错误!
	alert(node);
}
// 显示 0, 正确!
alert(root.branch.node);

由上面的测试 3 可知, with 语句内部的节点父节点修改后, 不会同步到节点本身. 也就是说, 不能保证内外数值的一致性. 这是可能成为项目里面隐藏性很高的 bug.
那我们该怎么办呢? 接受那很长的一串逐级访问, 还是另有他法?

方法是有的. 我们可以通过别名引用父节点的方式来调用节点对象, 如:

var root = {
	branch: {
		node: 1
	}
};
 
var quote = root.branch;
quote.node = 0;
// 显示 0, 正确!
alert(root.branch.node);

我相信很少人会用 with 语句, 也不会有很多人知道这个关键字, 但我觉得这是个有问题的语句, 压根就不应该使用, 所以写个小文记录一下.

声明: 本文采用 BY-NC-SA 协议进行授权. 转载请注明转自: 关于 JavaScript 的 with 语句

  1. http://0.gravatar.com/avatar/e0cf41235fb32bfffbe62e4b1a86751e?s=32&d=http%3A%2F%2F0.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D32&r=G

    Hi, 我最近正在温习javascript语法,针对您的例子二,我对“bug”的解释如下:
    进入with语句块的时候,js会创建一个临时的with对象,暂且称之为withobj:withobj的parent为当前的scope chain,withobj的__proto__为with语句块的参数root.branch。

    with(root.branch) {
    root.branch = {
    node: 0
    };
    // 显示 1, 错误!
    alert(node);
    }
    with语句块中将root.branch修改成另外一个对象,此时withobj的__proto__还是原来的对象{node:1}.于是便出现了例子二的"bug".

    所以with语句并没有bug,而是一个正常的语法现象。

    with语句并不可怕,它有它存在理由。:)

  2. http://1.gravatar.com/avatar/b296cd51fec6c36a6cd0d590ba3446ac?s=32&d=http%3A%2F%2F1.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D32&r=G

    是因为作用域的原因吧

  3. http://0.gravatar.com/avatar/497ea247993bf5d350c9aa7796c6eb81?s=32&d=http%3A%2F%2F0.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D32&r=G

    1.避免使用eval或者Function构造函数
    2.避免使用with
    3.不要在性能要求关键的函数中使用try-catch-finally
    4.避免使用全局变量

  4. http://0.gravatar.com/avatar/497ea247993bf5d350c9aa7796c6eb81?s=32&d=http%3A%2F%2F0.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D32&r=G

    第一次见 学到了

  5. http://0.gravatar.com/avatar/c505a1320f0f1664a47e667406299afd?s=32&d=http%3A%2F%2F0.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D32&r=G

    dismory :犀牛书里也是不推荐使用with语句的一是代码难优化, 同比不用with的代码低二就是这个问题了, 函数定义和变量初始化可能会产生奇怪的问题

    +1

  6. http://0.gravatar.com/avatar/67345fbddbfac18bef1df05048a19f0a?s=32&d=http%3A%2F%2F0.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D32&r=G
  7. http://0.gravatar.com/avatar/a6d14de05d7b2c3cf4fae7ae14cfa7f3?s=32&d=http%3A%2F%2F0.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D32&r=G

    果然内涵。。。

  8. http://1.gravatar.com/avatar/df0fe5448aea816246ef96c5c4a2623d?s=32&d=http%3A%2F%2F1.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D32&r=G

    犀牛书里也是不推荐使用with语句的
    一是代码难优化, 同比不用with的代码低
    二就是这个问题了, 函数定义和变量初始化可能会产生奇怪的问题

  9. http://0.gravatar.com/avatar/c961dc648c06e1d2126cc799fde9f62e?s=32&d=http%3A%2F%2F0.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D32&r=G

    关注你有一段时间了,还以为你不再更新了 :cry: 没想到最近更新还很勤,希望继续坚持啊,让我们这些菜鸟有个寻找答案的地方。

  10. http://0.gravatar.com/avatar/28ea04650153a2dc99acd9b8d1b6fc4e?s=32&d=http%3A%2F%2F0.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D32&r=G

    mg12 哥最近高产啊~

    我大概10月份考虑去杭州找工作. 有机会一定要亲眼目睹一下 mg12 哥的芳容... 哈哈 :razz:

  11. http://1.gravatar.com/avatar/93e82f5c662d768de7683d7ca1f9f766?s=32&d=http%3A%2F%2F1.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D32&r=G

    :lol: 一般只会用到测试1的情况。
    with(apple.banana.candy.dog.egg.fog.god.huh) {
    index= 0; //打错了哦
    doSomething(index);
    }

  12. http://0.gravatar.com/avatar/8e8fd58a39fba32e893ea7e289e36d9f?s=32&d=http%3A%2F%2F0.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D32&r=G

    不容易。。居然更新了

  13. http://1.gravatar.com/avatar/3ac75a81bdde38a0e6eb97e8ab3e62ed?s=32&d=http%3A%2F%2F1.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D32&r=G

    是ajax留言么 测试下 看代码好像不像

  14. http://0.gravatar.com/avatar/483afefc2ac42399bc24b75b881917b2?s=32&d=http%3A%2F%2F0.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D32&r=G

    好不容易看到你更新, 而更新滴东东我却看不懂,呵呵

  15. http://0.gravatar.com/avatar/659f2cbefe67feeb4b839ac74ac0e71c?s=32&d=http%3A%2F%2F0.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D32&r=G

    有时间更新了……

  16. http://1.gravatar.com/avatar/fa7f6e086d0164f7a5c41a79df6bd3ee?s=32&d=http%3A%2F%2F1.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D32&r=G

    没抢到沙发,

  17. http://0.gravatar.com/avatar/490cf262668eebb0f0f1a50d9d48d702?s=32&d=http%3A%2F%2F0.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D32&r=G

    @Kars
    欢迎

    @bolo
    因为最近在整理以前的项目, 也看一下 JavaScript 的语法规则.

  18. http://0.gravatar.com/avatar/2a9c4f525050372a6fe0b10ce4fe8957?s=32&d=http%3A%2F%2F0.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D32&r=G

    少见,一天两更

  19. http://1.gravatar.com/avatar/b42237c4385431164af14b2ecd71a312?s=32&d=http%3A%2F%2F1.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D32&r=G

    额、、小白一只、、坐沙发、、

  1. Loading...