刚看到这个东西的时候我是懵的,with是什么玩意,我咋从来没听过,抱着学习的态度,于是开干学习with。查阅了相关资料发现,不推荐使用with关键字,嘿嘿嘿,是不是可以不要学了,no,学无止境,有的底层代码会用到这个,或者面试官可能会问到。所以不管怎么说,还是了解一下的好。
with语句 扩展一个语句的作用域链。
语法:
with (expression) {
statement
}
expression:将给定的表达式添加到在评估语句时使用的作用域链上。表达式周围的括号是必需的。
statement:任何语句。要执行多个语句,请使用一个块语句 ({ ... })对这些语句进行分组
基本用法
var obj = {
a: 1,
b: 2,
c: 3
}
with (obj) {
a = 10
b = 20
c = 30
}
console.log(obj) // {a: 10, b: 20, c: 30}
弊端
1、代码不易阅读,难以在作用域链上查找某个变量,难以决定应该在哪个对象上来取值
function fn(a,b) {
with(b) {
console.log(a);
}
}
打印的这个a可能是形参b对象里的a变量,也可能是形参a的值,所以难以确定是哪个对象上来取值
2、数据泄露
function fn(obj) {
with(obj) {
a = 66
}
}
var obj1 = {
a:1
}
var obj2 = {
b:2
}
fn(obj1)
console.log(obj1.a) // 66
fn(obj2)
console.log(obj2.a) // undefined
console.log(a) // 66 此时a已经泄露到全局变量里去了
首先创建obj1和obj2对象,前者有a属性,后者没有,我们先把obj1传到fn函数中,这个时候with去obj1找a属性,找到后修改a为66,这个时候obj1的a就被修改掉了,这是没问题的。但是我们把obj2传给fn函数,with会去obj2对象中去查找a属性,结果发现没有找到,那么with就会去全局作用域找,如果也没有找到,就会在全局作用域创建一个a变量,并且赋值为66。这就是with泄漏数据的全过程
3、性能下降
function fn1() {
console.time('fn1')
let obj = {
arr: []
}
for(let i=0;i<100000;i++) {
obj.arr[i] = i*2+3
}
console.timeEnd('fn1')
}
fn1()
function fn2() {
console.time('fn2')
let obj = {
arr: []
}
with(obj) {
for(let i=0;i<100000;i++) {
arr[i] = i*2+3
}
}
console.timeEnd('fn2')
}
fn2()
可以看到with用的时间是普通代码逻辑的n倍,为啥with会这么慢呢?原因是 JavaScript 引擎会在编译阶段进行数项的性能优化。其中有些优化依赖于能够根据代码的词法进行静态分析,并预先确定所有变量和函数的定义位置,才能在执行过程中快速找到标识符。
但如果引擎在代码中发现了 with,它只能简单地假设关于标识符位置的判断都是无效的,因为无法知道传递给 with 用来创建新词法作用域的对象的内容到底是什么。