理解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实现的激活输入框后隐藏初始内容
Jun 29 Javascript
JavaScript 学习笔记(九)call和apply方法
Jan 11 Javascript
JavaScript CSS修改学习第五章 给“上传”添加样式
Feb 19 Javascript
模拟select的代码
Oct 19 Javascript
asp.net中System.Timers.Timer的使用方法
Mar 20 Javascript
jquery中通过父级查找进行定位示例
Jun 28 Javascript
jquery用data方法获取某个元素上的事件
Jun 23 Javascript
html5+javascript实现简单上传的注意细节
Apr 18 Javascript
第一次接触神奇的Bootstrap
Oct 14 Javascript
深入理解JavaScript创建对象的多种方式以及优缺点
Jun 01 Javascript
vue增删改查的简单操作
Jul 15 Javascript
Node.js 基础教程之全局对象
Aug 06 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实现自动登入google play下载app report的方法
2014/09/23 PHP
[原创]网络复制内容时常用的正则+editplus
2006/11/30 Javascript
ExtJS 简介 让你知道extjs是什么
2008/12/29 Javascript
JQuery jsonp 使用示例代码
2009/08/12 Javascript
javascript模拟的Ping效果代码 (Web Ping)
2011/03/13 Javascript
nodejs读取memcache示例分享
2014/01/02 NodeJs
jquery实现submit提交表单
2015/02/03 Javascript
jquery处理页面弹出层查询数据等待操作实例
2015/03/25 Javascript
解决angular的$http.post()提交数据时后台接收不到参数值问题的方法
2015/12/10 Javascript
JS在Chrome浏览器中showModalDialog函数返回值为undefined的解决方法
2016/08/03 Javascript
js实现上下左右弹框划出效果
2017/03/08 Javascript
jQuery初级教程之网站品牌列表效果
2017/08/02 jQuery
微信小程序 获取javascript 里的数据
2017/08/17 Javascript
vue中如何动态绑定图片,vue中通过data返回图片路径的方法
2018/02/07 Javascript
vue-cli webpack 引入swiper的操作方法
2018/09/15 Javascript
原生JS检测CSS3动画是否结束的方法详解
2019/01/27 Javascript
微信小程序登录对接Django后端实现JWT方式验证登录详解
2019/07/29 Javascript
Js实现复选框的全选、全不选反选功能代码实例
2020/02/28 Javascript
extjs图形绘制之饼图实现方法分析
2020/03/06 Javascript
Vue左滑组件slider使用详解
2020/08/21 Javascript
使用vue构建多页面应用的示例
2020/10/22 Javascript
vue element和nuxt的使用技巧分享
2021/01/14 Vue.js
python解决方案:WindowsError: [Error 2]
2016/08/28 Python
Python切片工具pillow用法示例
2018/03/30 Python
Django实现全文检索的方法(支持中文)
2018/05/14 Python
Tensorflow 实现修改张量特定元素的值方法
2018/07/30 Python
PyTorch预训练的实现
2019/09/18 Python
python GUI库图形界面开发之PyQt5中QWebEngineView内嵌网页与Python的数据交互传参详细方法实例
2020/02/26 Python
python正则表达式 匹配反斜杠的操作方法
2020/08/07 Python
详解pycharm连接远程linux服务器的虚拟环境的方法
2020/11/13 Python
python 获取谷歌浏览器保存的密码
2021/01/06 Python
深入了解canvas在移动端绘制模糊的问题解决
2019/04/30 HTML / CSS
美食节目策划方案
2014/05/31 职场文书
2014党员民主评议个人思想剖析发言
2014/09/19 职场文书
2016参观监狱警示教育活动心得体会
2016/01/15 职场文书
Vue.js 带下拉选项的输入框(Textbox with Dropdown)组件
2021/04/17 Vue.js