前言
在《javascript高级程序设计》中看到延长作用域链小节,一脸茫然。既然好奇心已起,便一探究竟,查缺补漏。
知识点
作用域链
作用域链的作用是用于查找标识符,沿着作用域链向外冒泡查找,找到第一个就停下。
可以在作用域链的前端临时增加一个变量对象,该变量对象会在代码执行后被移除。当执行流进入下列任何一个语句时,作用域链就会得到加长。
with语句
with语法
with (expression) {
statement
}
复制代码
expression
将给定的表达式添加到在评估语句时使用的作用域链上。表达式周围的括号是必需的。statement
要执行多个语句。
作用
- with语句对
statement
的语句的变量进行作用域链查找,首先查找当前的expression
作用域,如果存在相同变量的定义,则这个变量将指向这个属性值。而with语句块中作用域的‘变量对象’是只读的,不能存储标识符,只能存储在其上一层,这就是延长作用域链。
延长作用域链
内部环境可以通过作用域链访问所有的外部环境,但外部环境不能访问内部环境中的任何变量和函数。但有些语句:
- try-catch语句中的catch块;
- with语句;
let Person = {
name: 'Bob'
}
function Run () {
with (Person) {
name = 'Tom'
age = 12
}
}
Run()
console.log(Person.name) // Tom
console.log(Person.age) // undefined
console.log(age) // 12
复制代码
Person
对象中定义了name
属性,而没有定义age
属性,所以在with语句的作用下,name属性
的值被修改,而age
只能存储在作用域的上一层即全局,age
的作用域相对于Person
被延长了。
with的利与弊
- 利:with语句可以在不造成性能损失的情況下,减少变量的长度。其造成的附加计算量很少。使用
with
可以减少不必要的指针路径解析运算。需要注意的是,很多情況下,也可以不使用with语句,而是使用一个临时变量来保存指针,来达到同样的效果。
let Person = {
name: '',
age: '',
sex: ''
}
// 不使用with
Person.name = 'Tom'
Person.age = 22
Person.sex = 'Girl'
// 使用with
with (Person) {
name = 'Tom'
age = 22
sex = 'Girl'
}
// 临时变量
Person = {
name: 'Tom',
age: 22,
sex: 'Girl'
}
复制代码
- 弊 with语句使得程序在查找变量值时,都是先在指定的对象中查找。所以那些本来不是这个对象的属性的变量,查找起来将会很慢。导致性能下降。
对arr进行赋值,让下标值等于下标。
- 不使用with:用时2.849853515625ms
function func() {
console.time("func");
var obj = {
a: [1, 2, 3]
};
for(var i = 0; i < 100000; i++)
{
var v = obj.a[0];
}
console.timeEnd("func"); // func: 2.849853515625ms
}
func();
复制代码
- 使用with:用时67.050048828125ms
function funcWith() {
console.time("funcWith");
var obj = {
a: [1, 2, 3]
};
with(obj) {
for(var i = 0; i < 100000; i++) {
var v = a[0];
}
}
console.timeEnd("funcWith"); // funcWith: 67.050048828125ms
}
funcWith();
复制代码
总结
终上所述,with语句做不到任何优化,还会导致性能下降。所以不推荐使用。而且在严格模式下不支持该语句。