不要用with
一、with 语句的使用
with
的初衷为了避免冗余的对象调用
foo.bar.data.x = 1;
foo.bar.data.y = 2;
foo.bar.data.z = 3;
with (foo.bar.data){ // 改变块内作用域
x = 1;
y = 2;
z = 3;
}
但其实用变量替换的方法也可以轻松解决
var o = foo.bar.data;
o.x = 1;
o.y = 2;
o.z = 3;
所以with似乎本来就没有存在的必要。到了如今,会去用with的人才真的是罕见。到了strict模式里,使用with会直接报错:
(function (){
'use strict';
var o = {};
with(o){
x = 1;
}
})()
// VM132:4 Uncaught SyntaxError: Strict mode code may not include a with statement
二、with 语法中的 this 指向
with 中的 this 与with外部的this指向相同
// 全局函数
function foo(_obj){
with(_obj){
console.log(this === globalThis, this.x);
}
}
foo({x: 1}); // true undefined
// 构造函数
function Bar(){
this.x = 'bar';
this.withFunc = function(_obj){
with(_obj){
console.log(this instanceof Bar, this.x)
}
}
}
let bar = new Bar();
bar.withFunc({x: 1}); // true "bar"
三、为什么不要使用 with 语句
-
性能问题
做个简单的测试:对对象中的某个属性连续赋值100万次,观察使用with与不使用with的时间开销
// chrome v86.0.4240.111 var o = { x: 1 }; function useWith(){ with(o){ for(let i=0; i<100*10000; i++){ x = i; } } } function noWith(){ for(let i=0; i<100*10000; i++){ o.x = i; } } console.time('useWith'); useWith(); console.timeEnd('useWith'); // useWith: 168.0419921875 ms console.time('noWith'); noWith(); console.timeEnd('noWith'); // noWith: 2.031982421875 ms
-
不可预测
使用with语句后代码产生的不可预测性是废弃with的根本原因。with强行割裂了词法作用域,将对象临时性地插入到了作用域链中。这使得出现了难以捉摸的代码。
-
栗子1
function printInput(_input){ /** * 函数的本意是将输入的对象打印出来 */ with(_input){ console.log(_input); } } printInput("正常") // "正常" printInput({output: "正常"}) // { output: "正常" } printInput({_input: "不正常"}) // "不正常"
但当传入的参数是带有同名
_input
属性的_input
对象时,with
强行访问了_input._input
-
栗子2
function func(){ with({}){ x = '插入with引入的新对象' } console.log(x) } func(); // "插入with引入的新对象"
在
with
中引入的新变量实际上是被添加到外层的function
上的
-
代码难以优化
由于无法进行预测,不同的调用,或者即使相同的调用也会因为运行时的变化而出现偏差,代码含义只能在运行时才能被确定,从而使得代码无法在编译阶段被优化。
优化指两方面
- 解析和运行变得缓慢,指的就是之前已经谈到的性能。
- 对于代码优化和压缩工具来说,无法确定是变量还是属性(例如刚刚的
_input
),也就不能进行重命名(因为属性无法被重命名)。