首頁  >  文章  >  web前端  >  理解javascript中的with關鍵字_javascript技巧

理解javascript中的with關鍵字_javascript技巧

WBOY
WBOY原創
2016-05-16 15:15:291112瀏覽

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

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn