理解javascript中的with关键字


Posted in Javascript onFebruary 15, 2016

说起js中的with关键字,很多小伙伴们的第一印象可能就是with关键字的作用在于改变作用域,然后最关键的一点是不推荐使用with关键字。听到不推荐with关键字后,我们很多人都会忽略掉with关键字,认为不要去管它用它就可以了。但是有时候,我们在看一些代码或者面试题的时候,其中会有with关键字的相关问题,很多坑是你没接触过的,所以还是有必要说说with这一个关键字。

一、基本说明

在js高级程序设计中是这样描述with关键字的:with语句的作用是将代码的作用域设置到一个特定的作用域中,基本语法如下:

with (expression) statement;

使用with关键字的目的是为了简化多次编写访问同一对象的工作,比如下面的例子:

var qs = location.search.substring(1);
var hostName = location.hostname;
var url = location.href;

这几行代码都是访问location对象中的属性,如果使用with关键字的话,可以简化代码如下:

with (location){
  var qs = search.substring(1);
  var hostName = hostname;
  var url = href;
}

在这段代码中,使用了with语句关联了location对象,这就以为着在with代码块内部,每个变量首先被认为是一个局部变量,如果局部变量与location对象的某个属性同名,则这个局部变量会指向location对象属性。
注意:在严格模式下不能使用with语句。

二、with关键字的弊端

前面的基本说明中,我们可以看到with的作用之一是简化代码。但是为什么不推荐使用呢?下面我们来说说with的缺点:

1、性能问题
2、语义不明,调试困难

三、性能问题

首先说说性能问题,关于使用with关键字的性能问题,首先我们来看看两段代码:

第一段代码是没有使用with关键字:

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");//0.847ms
}
func();

第二段代码使用了with关键字:

function funcWith() {
  console.time("funcWith");
  var obj = {
    a: [1, 2, 3]
  };
  var obj2 = { x: 2 };
  with (obj2) {
    console.log(x);
    for (var i = 0; i < 100000; i++) {
      var v = obj.a[0];
    }
  }
  console.timeEnd("funcWith");//84.808ms
}
funcWith();

在使用了with关键字后了,代码的性能大幅度降低。第二段代码的with语句作用到了obj2这个对象上,然后with块里面访问的却是obj对象。有一种观点是:使用了with关键字后,在with块内访问变量时,首先会在obj2上查找是否有名为obj的属性,如果没有,再进行下一步查找,这个过程导致了性能的降低。但是程序性能真正降低的原因真的是这样吗?
我们修改一下第二段代码,修改如下:

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");//88.260ms
}
funcWith();

这段代码将with语句作用到了obj对象上,然后直接使用a访问obj的a属性,按照前面说到的观点,访问a属性时,是一次性就可以在obj上找到该属性的,但是为什么代码性能依旧降低了呢。
真正的原因是:使用了with关键字后,JS引擎无法对这段代码进行优化。
JS引擎在代码执行之前有一个编译阶段,在不使用with关键字的时候,js引擎知道a是obj上的一个属性,它就可以静态分析代码来增强标识符的解析,从而优化了代码,因此代码执行的效率就提高了。使用了with关键字后,js引擎无法分辨出a变量是局部变量还是obj的一个属性,因此,js引擎在遇到with关键字后,它就会对这段代码放弃优化,所以执行效率就降低了。
使用with关键字对性能的影响还有一点就是js压缩工具,它无法对这段代码进行压缩,这也是影响性能的一个因素。

四、语义不明,难以调试

前面说到除了性能的问题,with还存在的一个缺点语义不明,难以调试,就是造成代码的不易阅读,而且可能造成潜在的bug。

function foo(obj) {
  with (obj) {
    a = 2;
  }
}

var o1 = {
  a: 3
};
var o2 = {
  b: 3
};

foo(o1);
console.log(o1.a); // 2

foo(o2);
console.log( o2.a ); // undefined
console.log( a ); // 2

这段代码很容易理解了,在foo函数内,使用了with关键字来访问传进来的obj对象,然后修改a属性。当传入o1对象时,因为o1对象存在着a属性,所以这样没有问题。传入o2对象时,在修改a属性时,由于o2对象没有a这个属性,所以被修改的a属性则变成了全局变量。这就造成了潜在的bug。

五、延伸分析

前面说了那么多,相信大家已经理解了为什么不推荐使用with关键字以及可能存在的问题。下面我们来看看一些更复杂的情况,看下面的代码:

var obj = {
  x: 10,
  foo: function () {
    with (this) {
      var x = 20;
      var y = 30;
      console.log(y);//30
    }
  }
};
obj.foo();
console.log(obj.x);//20
console.log(obj.y);//undefined

在这段代码中,分别输出30,20,undefined的。涉及的知识点也比较多:with关键字,this关键字,变量提升等等,我们来一一解释一下。
1、this关键字
关于this关键字的文章google上面相当多,这里不再赘述,我们只需记住一点:this关键字始终指向调用函数的对象。在这里,foo函数中,this指向的就是obj对象。因此在with(this)语句块里面,可以直接通过x变量来访问obj的x属性。
2、变量提升
js中的变量提升也是一个经常遇到的问题,我们可以简单理解成在js中,变量声明会被提升到函数的顶部,尽管有的时候,它是在后面声明的。

所以上面的代码可以解析为:

var obj = {
  x: 10,
  foo: function () {
    var x;//声明局部变量x
    var y;//声明局部变量y
    with (obj) {
      x = 20;//访问变量x,在obj上找到x,则修改为20
      y = 30;//访问变量y,在bojg上找不到y,则进一步查找,找到局部变量y,修改为30
      console.log(y);//30//直接输出局部变量y,
    }
  }
};
obj.foo();
console.log(obj.x);//20,obj.x已被修改为20
console.log(obj.y);//undefined,obj不存在y属性,则为undefined

上面的注释中,解释了代码的执行过程,相信大家已经理解了为什么会出处30,20,undefined的原因。

有兴趣的同学可以看看下面这段代码:

({
x: 10,
foo: function () {
  function bar() {
    console.log(x);
    console.log(y);
    console.log(this.x);
  }
  with (this) {
    var x = 20;
    var y = 30;
    bar.call(this);
  }
}
}).foo();

这段代码会输出什么?为什么呢?

总结

本文总结了with语句的特点和弊端,总的来说,强烈不推荐使用with关键字。其实在日常编码中,我们只需要知道不去使用with就可以了,但是有的时候我们可能会遇到一些关于with的奇奇怪怪的问题,想要找出真正的原因,就要深入理解with关键字,这有助于我们去深入学习JS这门语言,同时也是学习JS的一个乐趣。

Javascript 相关文章推荐
javascript 动态table添加colspan\rowspan 参数的方法
Jul 25 Javascript
js 小贴士一星期合集
Apr 07 Javascript
Jquery实现列表(隔行换色,全选,鼠标滑过当前行)效果实例
Jun 09 Javascript
jquery中的常用事件bind、hover、toggle等示例介绍
Jul 21 Javascript
jQuery实现文本展开收缩特效
Jun 03 Javascript
vue.js初学入门教程(1)
Nov 03 Javascript
jQuery插件zTree实现清空选中第一个节点所有子节点的方法
Mar 08 Javascript
vue获取input输入值的问题解决办法
Oct 17 Javascript
vue2.0 常用的 UI 库实例讲解
Dec 12 Javascript
JavaScript树的深度优先遍历和广度优先遍历算法示例
Jul 30 Javascript
js基础之事件捕获与冒泡原理
Oct 09 Javascript
详解JS深拷贝与浅拷贝
Aug 04 Javascript
使用基于Node.js的构建工具Grunt来发布ASP.NET MVC项目
Feb 15 #Javascript
JavaScript模版引擎的基本实现方法浅析
Feb 15 #Javascript
在ASP.NET MVC项目中使用RequireJS库的用法示例
Feb 15 #Javascript
一道常被人轻视的web前端常见面试题(JS)
Feb 15 #Javascript
获取阴历(农历)和当前日期的js代码
Feb 15 #Javascript
极易被忽视的javascript面试题七问七答
Feb 15 #Javascript
在JavaScript中使用JSON数据
Feb 15 #Javascript
You might like
2020年4月放送!《Princess Connect Re:Dive》制作组 & 角色声优公开!
2020/03/06 日漫
手把手教你使用DedeCms的采集的图文教程
2007/03/11 PHP
php下将多个数组合并成一个数组的方法与实例代码
2011/02/03 PHP
用 Composer构建自己的 PHP 框架之构建路由
2014/10/30 PHP
PHP 实现代码复用的一个方法 traits新特性
2015/02/22 PHP
php如何控制用户对图片的访问 PHP禁止图片盗链
2016/03/25 PHP
PHP获取当前文件的父目录方法汇总
2016/07/21 PHP
PHP实现一个多功能购物网站的案例
2017/09/13 PHP
Javascript调用XML制作连动下拉列表框
2006/06/25 Javascript
JavaScript 高级语法介绍
2009/06/15 Javascript
JavaScript 创建运动框架的实现代码
2013/05/08 Javascript
一个Action如何调用两个不同的方法
2014/05/22 Javascript
最简单的JavaScript图片轮播代码(两种方法)
2015/12/18 Javascript
Node.js服务器开启Gzip压缩教程
2017/08/11 Javascript
js中关于Blob对象的介绍与使用
2019/11/29 Javascript
解决Vue 给mapState中定义的属性赋值报错的问题
2020/06/22 Javascript
使用Python从有道词典网页获取单词翻译
2016/07/03 Python
Python堆排序原理与实现方法详解
2018/05/11 Python
tensorflow实现加载mnist数据集
2018/09/08 Python
Python实现将Excel转换成为image的方法
2018/10/23 Python
python tkinter之顶层菜单、弹出菜单实例
2020/03/04 Python
python简单实现最大似然估计&amp;scipy库的使用详解
2020/04/15 Python
详解Windows下PyCharm安装Numpy包及无法安装问题解决方案
2020/06/18 Python
python 爬取英雄联盟皮肤并下载的示例
2020/12/04 Python
世界领先的艺术图书出版社:TASCHEN
2018/07/23 全球购物
豪华床上用品、床单和浴室必需品:Peacock Alley
2019/09/04 全球购物
char型变量中能不能存贮一个中文汉字
2015/07/08 面试题
如何写毕业求职自荐信
2013/11/06 职场文书
宿舍使用违章电器检讨书
2014/01/12 职场文书
工商管理专业大学生职业生涯规划范文
2014/03/09 职场文书
运动会方队口号
2014/06/07 职场文书
党员教师个人对照检查材料范文
2014/09/25 职场文书
蓬莱阁导游词
2015/02/04 职场文书
歌咏比赛口号大全
2015/12/25 职场文书
golang gopm get -g -v 无法获取第三方库的解决方案
2021/05/05 Golang
win10+RTX3050ti+TensorFlow+cudn+cudnn配置深度学习环境的方法
2022/06/25 Servers