理解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 控制弹出窗口
Apr 10 Javascript
基于jQuery的获得各种控件Value的方法
Nov 19 Javascript
javascript写的简单的计算器,内容很多,方法实用,推荐
Dec 29 Javascript
ie支持function.bind()方法实现代码
Dec 27 Javascript
在JavaScript中正确引用bind方法的应用
May 11 Javascript
Javascript中的return作用及javascript return关键字用法详解
Nov 05 Javascript
基于jquery编写的放大镜插件
Mar 23 Javascript
微信小程序 参数传递实例代码
Mar 20 Javascript
认识less和webstrom的less配置方法
Aug 02 Javascript
angular4 如何在全局设置路由跳转动画的方法
Aug 30 Javascript
JavaScript表格隔行变色和Tab标签页特效示例【附jQuery版】
Jul 11 jQuery
Net微信网页开发 使用微信JS-SDK获取当前地理位置过程详解
Aug 26 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
PHP $_FILES函数详解
2011/03/09 PHP
php表单提交问题的解决方法
2011/04/12 PHP
PHP使用DES进行加密与解密的方法详解
2013/06/06 PHP
8个必备的PHP功能实例代码
2013/10/27 PHP
php新浪微博登录接口用法实例
2014/12/23 PHP
php使用crypt()函数进行加密
2017/06/08 PHP
JavaScript中this关键字使用方法详解
2007/03/08 Javascript
用javascript实现计算两个日期的间隔天数
2007/08/14 Javascript
extjs fckeditor集成代码
2009/05/10 Javascript
Javascript中 关于prototype属性实现继承的原理图
2013/04/16 Javascript
jquery中ajax处理跨域的三大方式
2016/01/05 Javascript
BootStrapTable服务器分页实例解析
2016/12/20 Javascript
详解Vue.js组件可复用性的混合(mixin)方式和自定义指令
2017/09/06 Javascript
原生JS控制多个滚动条同步跟随滚动效果
2017/12/22 Javascript
Vue入门之animate过渡动画效果
2018/04/08 Javascript
微信小程序使用wxParse解析html的方法示例
2019/01/17 Javascript
js尾调用优化的实现
2019/05/23 Javascript
vue  elementUI 表单嵌套验证的实例代码
2019/11/06 Javascript
vant实现购物车功能
2020/06/29 Javascript
零基础写python爬虫之抓取糗事百科代码分享
2014/11/06 Python
python实现简单聊天应用 python群聊和点对点均实现
2017/09/14 Python
Django视图和URL配置详解
2018/01/31 Python
详解从Django Allauth中进行登录改造小结
2019/12/18 Python
Python 改变数组类型为uint8的实现
2020/04/09 Python
Python调用C/C++的方法解析
2020/08/05 Python
python实现取余操作的简单实例
2020/08/16 Python
HTML5给汉字加拼音收起展开组件的实现代码
2020/04/08 HTML / CSS
Sneaker Studio罗马尼亚网站:购买运动鞋
2018/11/04 全球购物
写给学生的新学期寄语
2014/01/18 职场文书
幼儿园元旦亲子活动方案
2014/02/17 职场文书
民主评议党员自我评价材料
2014/09/18 职场文书
2014年安全员工作总结
2014/11/13 职场文书
2014年科技工作总结
2014/11/26 职场文书
如何写一份成功的商业计划书
2019/06/25 职场文书
Python中Numpy和Matplotlib的基本使用指南
2021/11/02 Python
Redis超详细讲解高可用主从复制基础与哨兵模式方案
2022/04/07 Redis