javascript中的with關鍵字

提及js中的with關鍵字,不少小夥伴們的第一印象可能就是with關鍵字的做用在於改變做用域,而後最關鍵的一點是不推薦使用with關鍵字。聽到不推薦with關鍵字後,咱們不少人都會忽略掉with關鍵字,認爲不要去管它用它就能夠了。可是有時候,咱們在看一些代碼或者面試題的時候,其中會有with關鍵字的相關問題,不少坑是你沒接觸過的,因此仍是有必要說說with這一個關鍵字。面試

基本說明

在js高級程序設計中是這樣描述with關鍵字的:with語句的做用是將代碼的做用域設置到一個特定的做用域中,基本語法以下:express

1 with (expression) statement;

使用with關鍵字的目的是爲了簡化屢次編寫訪問同一對象的工做,好比下面的例子:函數

1 var qs = location.search.substring(1);
2 
3 var hostName = location.hostname;
4 
5 var url = location.href;

這幾行代碼都是訪問location對象中的屬性,若是使用with關鍵字的話,能夠簡化代碼以下:工具

1 with (location){
2 
3     var qs = search.substring(1);
4 
5     var hostName = hostname;
6 
7     var url = href;
8 
9 }

在這段代碼中,使用了with語句關聯了location對象,這就覺得着在with代碼塊內部,每一個變量首先被認爲是一個局部變量,若是局部變量與location對象的某個屬性同名,則這個局部變量會指向location對象屬性。
注意:在嚴格模式下不能使用with語句。post

with關鍵字的弊端

前面的基本說明中,咱們能夠看到with的做用之一是簡化代碼。可是爲何不推薦使用呢?下面咱們來講說with的缺點:
一、性能問題
二、語義不明,調試困難性能

性能問題

首先說說性能問題,關於使用with關鍵字的性能問題,首先咱們來看看兩段代碼:學習

第一段代碼是沒有使用with關鍵字:優化

 1 function func() {
 2 
 3     console.time("func");
 4 
 5     var obj = {
 6 
 7         a: [1, 2, 3]
 8 
 9     };
10 
11 
12     for (var i = 0; i < 100000; i++) {
13         var v = obj.a[0];
14     }
15 
16     console.timeEnd("func");//0.847ms
17 }
18 
19 func();

 

第二段代碼使用了with關鍵字:this

 1 function funcWith() {
 2 
 3     console.time("funcWith");
 4 
 5     var obj = {
 6 
 7         a: [1, 2, 3]
 8 
 9     };
10 
11     var obj2 = { x: 2 };
12 
13     with (obj2) {
14 
15         console.log(x);
16 
17         for (var i = 0; i < 100000; i++) {
18 
19             var v = obj.a[0];
20 
21         }
22     }
23 
24     console.timeEnd("funcWith");//84.808ms
25 
26 }
27 
28 funcWith();

在使用了with關鍵字後了,代碼的性能大幅度下降。第二段代碼的with語句做用到了obj2這個對象上,而後with塊裏面訪問的倒是obj對象。有一種觀點是:使用了with關鍵字後,在with塊內訪問變量時,首先會在obj2上查找是否有名爲obj的屬性,若是沒有,再進行下一步查找,這個過程致使了性能的下降。可是程序性能真正下降的緣由真的是這樣嗎?
咱們修改一下第二段代碼,修改以下:google

 1 function funcWith() {
 2 
 3     console.time("funcWith");
 4 
 5     var obj = {
 6 
 7         a: [1, 2, 3]
 8 
 9     };
10 
11     with (obj) {
12 
13         for (var i = 0; i < 100000; i++) {
14 
15             var v = a[0];
16 
17         }
18 
19     }
20 
21     console.timeEnd("funcWith");//88.260ms
22 
23 }
24 
25 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 function foo(obj) {
 2 
 3     with (obj) {
 4 
 5         a = 2;
 6 
 7     }
 8 
 9 }
10 
11 var o1 = {
12 
13     a: 3
14 
15 };
16 
17 var o2 = {
18 
19     b: 3
20 
21 };
22 
23 foo(o1);
24 console.log(o1.a); // 2
25 
26 foo(o2);
27 console.log( o2.a ); // undefined
28 console.log( a ); // 2

 這段代碼很容易理解了,在foo函數內,使用了with關鍵字來訪問傳進來的obj對象,而後修改a屬性。當傳入o1對象時,由於o1對象存在着a屬性,因此這樣沒有問題。傳入o2對象時,在修改a屬性時,因爲o2對象沒有a這個屬性,因此被修改的a屬性則變成了全局變量。這就形成了潛在的bug。

延伸分析

前面說了那麼多,相信你們已經理解了爲何不推薦使用with關鍵字以及可能存在的問題。下面咱們來看看一些更復雜的狀況,看下面的代碼:

 1 var obj = {
 2     x: 10,
 3     foo: function () {
 4 
 5         with (this) {
 6 
 7             var x = 20;
 8             var y = 30;
 9 
10             console.log(y);//30
11         }
12     }
13 };
14 
15 obj.foo();
16 console.log(obj.x);//20
17 console.log(obj.y);//undefined

 

在這段代碼中,分別輸出30,20,undefined的。涉及的知識點也比較多:with關鍵字,this關鍵字,變量提高等等,咱們來一一解釋一下。
一、this關鍵字
關於this關鍵字的文章google上面至關多,這裏再也不贅述,咱們只需記住一點:this關鍵字始終指向調用函數的對象。在這裏,foo函數中,this指向的就是obj對象。所以在with(this)語句塊裏面,能夠直接經過x變量來訪問obj的x屬性。
二、變量提高
js中的變量提高也是一個常常遇到的問題,咱們能夠簡單理解成在js中,變量聲明會被提高到函數的頂部,儘管有的時候,它是在後面聲明的。

因此上面的代碼能夠解析爲:

 1 var obj = {
 2     x: 10,
 3     foo: function () {
 4         var x;//聲明局部變量x
 5         var y;//聲明局部變量y
 6         with (obj) {
 7             x = 20;//訪問變量x,在obj上找到x,則修改成20
 8             y = 30;//訪問變量y,在obj上找不到y,則進一步查找,找到局部變量y,修改成30
 9             console.log(y);//30//直接輸出局部變量y,
10         }
11     }
12 };
13 
14 obj.foo();
15 console.log(obj.x);//20,obj.x已被修改成20
16 console.log(obj.y);//undefined,obj不存在y屬性,則爲undefined

 

上面的註釋中,解釋了代碼的執行過程,相信你們已經理解了爲何會出處30,20,undefined的緣由。

有興趣的同窗能夠看看下面這段代碼:

 1 ({
 2     x: 10,
 3     foo: function () {
 4         function bar() {
 5             console.log(x);
 6             console.log(y);
 7             console.log(this.x);
 8         }
 9 
10         with (this) {
11             var x = 20;
12             var y = 30;
13 
14             bar.call(this);
15         }
16     }
17 }).foo();

這段代碼會輸出什麼?爲何呢?

總結

本文總結了with語句的特色和弊端,總的來講,強烈不推薦使用with關鍵字。其實在平常編碼中,咱們只須要知道不去使用with就能夠了,可是有的時候咱們可能會遇到一些關於with的奇奇怪怪的問題,想要找出真正的緣由,就要深刻理解with關鍵字,這有助於咱們去深刻學習JS這門語言,同時也是學習JS的一個樂趣。歡迎小夥伴們來交流!!

 

原文地址:http://luopq.com/2016/02/14/js-with-keyword/

相關文章
相關標籤/搜索