理解javascript中的with关键字

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

一、基本说明

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

?


1

with (expression) statement;

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

?


1

2

3

var qs = location.search.substring(1);

var hostName = location.hostname;

var url = location.href;

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

?


1

2

3

4

5

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关键字:

?


1

2

3

4

5

6

7

8

9

10

11

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关键字:

?


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

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的属性,如果没有,再进行下一步查找,这个过程导致了性能的降低。但是程序性能真正降低的原因真的是这样吗?
我们修改一下第二段代码,修改如下:

?


1

2

3

4

5

6

7

8

9

10

11

12

13

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。

?


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

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关键字以及可能存在的问题。下面我们来看看一些更复杂的情况,看下面的代码:

?


1

2

3

4

5

6

7

8

9

10

11

12

13

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中,变量声明会被提升到函数的顶部,尽管有的时候,它是在后面声明的。

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

?


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

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的原因。

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

?


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

({

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的一个乐趣。

时间: 2024-10-14 10:27:26

理解javascript中的with关键字的相关文章

转载 深入理解JavaScript中的this关键字

转载原地址: http://www.cnblogs.com/rainman/archive/2009/05/03/1448392.html 深入理解JavaScript中的this关键字 1. 一般用处 2. this.x 与 apply().call() 3. 无意义(诡异)的this用处 4. 事件监听函数中的this 5. 总结 在JavaScript中this变量是一个令人难以摸清的关键字,this可谓是非常强大,充分了解this的相关知识有助于我们在编写面向对象的JavaScript程

深入理解JavaScript中的this关键字

1. 一般用处 2. this.x 与 apply().call() 3. 无意义(诡异)的this用处 4. 事件监听函数中的this 5. 总结 在JavaScript中this变量是一个令人难以摸清的关键字,this可谓是非常强大,充分了解this的相关知识有助于我们在编写面向对象的JavaScript程序时能够游刃有余. 1. 一般用处 对于this变量最要的是能够理清this所引用的对象到底是哪一个,也许很多资料上都有自己的解释,但有些概念讲的偏繁杂.而我的理解是:首先分析this所在

用自然语言的角度理解JavaScript中的this关键字

转自:http://blog.leapoahead.com/2015/08/31/understanding-js-this-keyword/ 在编写JavaScript应用的时候,我们经常会使用this关键字.那么this关键字究竟是怎样工作的?它的设计有哪些好的地方,有哪些不好的地方?本文带大家全面系统地认识这个老朋友. 这里的小明是主语,如果没有这个主语,那么后面的代词『他』将毫无意义.有了主语,代词才有了可以指代的事物. 类比到JavaScript的世界中,我们在调用一个对象的方法的时候

正确理解JavaScript中的this关键字

JavaScript有this关键字,this跟JavaScript的执行上下文密切相关,很多前端开发工程师至今对this关键字还是模棱两可,本文将结合代码讲解下JavaScript的this关键字. this和对象的关系 首先来看下面的代码: var person = { name:'Theo Wong', gender:'male', getName:function(){ console.log(person.name); } }; person.getName(); 定义了一个perso

深入理解javascript中的this 关键字(转载)

在JavaScript中this变量是一个令人难以摸清的关键字,this可谓是非常强大,充分了解this的相关知识有助于我们在编写面向对象的JavaScript程序时能够游刃有余. 1.一般用处 对于this关键字最重要的是明确this所引用的对象是哪一个,也许很多资料上都有自己的解释,但有些概念将的偏繁杂.而我的理解是:首先分析this所在的函数是当做哪个对象的方法调用的,则该对象就是this所引用的对象. Example 一: var obj = {}; obj.x = 100; obj.y

深入理解JavaScript中创建对象模式的演变(原型)

创建对象的模式多种多样,但是各种模式又有怎样的利弊呢?有没有一种最为完美的模式呢?下面我将就以下几个方面来分析创建对象的几种模式: Object构造函数和对象字面量方法 工厂模式 自定义构造函数模式 原型模式 组合使用自定义构造函数模式和原型模式 动态原型模式.寄生构造函数模式.稳妥构造函数模式 第一部分:Object构造函数和对象字面量方法 我之前在博文<javascript中对象字面量的理解>中讲到过这两种方法,如何大家不熟悉,可以点进去看一看回顾一下.它们的优点是用来创建单个的对象非常方

JavaScript大杂烩6 - 理解JavaScript中的this

在JavaScript开发中,this是很常用的一个关键字,但同时也是一个很容易引入bug的一个关键字,在这里我们就专门总结一下页面中可能出现的this关键字(包括几种在其他页面文件中出现的this). JavaScript中的this关键字通常只使用在函数中,它指向当前函数的调用者,这是this关键字的本质,所有的使用方式都是围绕这个展开的,让我们来看一下在各种性质的函数中this的用法.1. 在对象的函数中使用this var person = { name: 'Frank', say: f

深入理解javascript中的立即执行函数(function(){…})()

这篇文章主要介绍了深入理解javascript中的立即执行函数,立即执行函数也叫立即调用函数,通常它的写法是用(function(){-})()包住业务代码,使用jquery时比较常见,需要的朋友可以参考下http://www.jb51.net/article/50967.htm javascript和其他编程语言相比比较随意,所以javascript代码中充满各种奇葩的写法,有时雾里看花,当然,能理解各型各色的写法也是对javascript语言特性更进一步的深入理解. ( function()

深入理解javascript 中的 delete(转)

在这篇文章中作者从<JavaScript面向对象编程指南>一书中关于 delete 的错误讲起,详细讲述了关于 delete 操作的实现, 局限以及在不同浏览器和插件(这里指 firebug)中的表现. 下面翻译其中的主要部分. ...书中声称 “函数就像一个普通的变量那样——可以拷贝到不同变量,甚至被删除” 并附上了下面的代码片段作为说明: >>> var sum = function(a, b) {return a+b;}; >>> var add =