JavaScript中的with關鍵字
JS中的with關鍵字有什麼作用麼?下面YJBYS小編帶大家一起來了解!
説起js中的with關鍵字,很多小夥伴們的第一印象可能就是with關鍵字的作用在於改變作用域,然後最關鍵的一點是不推薦使用with關鍵字。聽到不推薦with關鍵字後,我們很多人都會忽略掉with關鍵字,認為不要去管它用它就可以了。但是有時候,我們在看一些代碼或者面試題的時候,其中會有with關鍵字的相關問題,很多坑是你沒接觸過的,所以還是有必要説説with這一個關鍵字。
基本説明
在js高級程序設計中是這樣描述with關鍵字的:with語句的作用是將代碼的作用域設置到一個特定的作用域中,基本語法如下:
1 | with (expression) statement; |
使用with關鍵字的目的是為了簡化多次編寫訪問同一對象的工作,比如下面的例子:
123 | var qs = tring(1);var hostName = name;var url = ; |
這幾行代碼都是訪問location對象中的屬性,如果使用with關鍵字的話,可以簡化代碼如下:
12345 | with (location){ var qs = tring(1); var hostName = hostname; var url = href;} |
在這段代碼中,使用了with語句關聯了location對象,這就以為着在with代碼塊內部,每個變量首先被認為是一個局部變量,如果局部變量與location對象的某個屬性同名,則這個局部變量會指向location對象屬性。注意:在嚴格模式下不能使用with語句。
with關鍵字的`弊端
前面的基本説明中,我們可以看到with的作用之一是簡化代碼。但是為什麼不推薦使用呢?下面我們來説説with的缺點:
1、性能問題
2、語義不明,調試困難
性能問題
首先説説性能問題,關於使用with關鍵字的性能問題,首先我們來看看兩段代碼:
第一段代碼是沒有使用with關鍵字:
1234567891011 | function func() { ("func"); var obj = { a: [1, 2, 3] }; for (var i = 0; i < 100000; i++) { var v = obj.a[0]; } End("func");//0.847ms}func(); |
第二段代碼使用了with關鍵字:
123456789101112131415 | function funcWith() { ("funcWith"); var obj = { a: [1, 2, 3] }; var obj2 = { x: 2 }; with (obj2) { (x); for (var i = 0; i < 100000; i++) { var v = obj.a[0]; } } End("funcWith");//84.808ms}funcWith(); |
在使用了with關鍵字後了,代碼的性能大幅度降低。第二段代碼的with語句作用到了obj2這個對象上,然後with塊裏面訪問的卻是obj對象。有一種觀點是:使用了with關鍵字後,在with塊內訪問變量時,首先會在obj2上查找是否有名為obj的屬性,如果沒有,再進行下一步查找,這個過程導致了性能的降低。但是程序性能真正降低的原因真的是這樣嗎?我們修改一下第二段代碼,修改如下:
12345678910111213 | function funcWith() { ("funcWith"); var obj = { a: [1, 2, 3] }; with (obj) { for (var i = 0; i < 100000; i++) { var v = a[0]; } } End("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。
12345678910111213141516171819 | function foo(obj) { with (obj) { a = 2; }}var o1 = { a: 3};var o2 = { b: 3};foo(o1);(o1.a); // 2foo(o2);( o2.a ); // ( a ); // 2 |
這段代碼很容易理解了,在foo函數內,使用了with關鍵字來訪問傳進來的obj對象,然後修改a屬性。當傳入o1對象時,因為o1對象存在着a屬性,所以這樣沒有問題。傳入o2對象時,在修改a屬性時,由於o2對象沒有a這個屬性,所以被修改的a屬性則變成了全局變量。這就造成了潛在的bug。
延伸分析
前面説了那麼多,相信大家已經理解了為什麼不推薦使用with關鍵字以及可能存在的問題。下面我們來看看一些更復雜的情況,看下面的代碼:
12345678910111213 | var obj = { x: 10, foo: function () { with (this) { var x = 20; var y = 30; (y);//30 } }};();(obj.x);//(obj.y);//undefined |
在這段代碼中,分別輸出30,20,undefined的。涉及的知識點也比較多:with關鍵字,this關鍵字,變量提升等等,我們來一一解釋一下。
1、this關鍵字關於this關鍵字的文章google上面相當多,這裏不再贅述,我們只需記住一點:this關鍵字始終指向調用函數的對象。在這裏,foo函數中,this指向的就是obj對象。因此在with(this)語句塊裏面,可以直接通過x變量來訪問obj的x屬性。
2、變量提升js中的變量提升也是一個經常遇到的問題,我們可以簡單理解成在js中,變量聲明會被提升到函數的頂部,儘管有的時候,它是在後面聲明的。
所以上面的代碼可以解析為:
123456789101112131415 | var obj = { x: 10, foo: function () { var x;//聲明局部變量x var y;//聲明局部變量y with (obj) { x = 20;//訪問變量x,在obj上找到x,則修改為20 y = 30;//訪問變量y,在obj上找不到y,則進一步查找,找到局部變量y,修改為30 (y);//30//直接輸出局部變量y, } }};();(obj.x);//20,obj.x已被修改為(obj.y);//undefined,obj不存在y屬性,則為undefined |
上面的註釋中,解釋了代碼的執行過程,相信大家已經理解了為什麼會出處30,20,undefined的原因。有興趣的同學可以看看下面這段代碼:
123456789101112131415 | ({x: 10,foo: function () { function bar() { (x); (y); (this.x); } with (this) { var x = 20; var y = 30; (this); }}})(); |
這段代碼會輸出什麼?為什麼呢?
總結
本文總結了with語句的特點和弊端,總的來説,強烈不推薦使用with關鍵字。其實在日常編碼中,我們只需要知道不去使用with就可以了,但是有的時候我們可能會遇到一些關於with的奇奇怪怪的問題,想要找出真正的原因,就要深入理解with關鍵字,這有助於我們去深入學習JS這門語言,同時也是學習JS的一個樂趣。歡迎小夥伴們來交流!!
-
dreamweaver的學習心得
當我們備受啟迪時,寫心得體會是一個不錯的選擇,這樣我們可以養成良好的總結方法。那麼要如何寫呢?下面是小編收集整理的dreamweaver的學習心得,歡迎閲讀與收藏。dreamweaver的學習心得1目前,互聯網是最快捷的信息傳播渠道之一,學習網頁製作成了學生必須掌握的一項技...
-
如何理解Javascript的caller,callee,call,apply區別
在提到上述的概念之前,首先想説説javascript中函數的隱含參數:argumentsarguments該對象代表正在執行的函數和調用它的函數的參數。[function.]arguments[n]參數function:選項。當前正在執行的Function對象的名字。n:選項。要傳遞給Function對象的從0開始的參數值...
-
Dreamweaver技巧詳解
Dreamweaver由MX版本開始使用Opera軟件公司的排版引擎“Presto”作為網頁預覽。下面給大家介紹Dreamweaver技巧,一起來學習吧!Dreamweaver技巧詳解1、靈活運用樣式熟悉網頁設計的網友就知道,調用Style的方法很多,我們可以單擊鼠標右鍵選擇CustonStyle來調用Style...
-
javascript閉包的定義及應用實例分析
官方解釋“閉包”是一個擁有許多變量和綁定了這些變量的環境表達式(通常是一個函數),因而這些變量也是環境表達式的一部分。通俗解釋Javascript中所有的函數都是一個閉包。不過一般來説,嵌套的function產生的閉包更為強大,也是大部分時候我們所説的“閉包”。看如下...