Javascript中的with關鍵字

2021-07-26 15:17:14 字數 3794 閱讀 7580

鏈結   

說起js中的with關鍵字,很多小夥伴們的第一印象可能就是with關鍵字的作用在於改變作用域,然後最關鍵的一點是不推薦使用with關鍵字。聽到不推薦with關鍵字後,我們很多人都會忽略掉with關鍵字,認為不要去管它用它就可以了。但是有時候,我們在看一些**或者面試題的時候,其中會有with關鍵字的相關問題,很多坑是你沒接觸過的,所以還是有必要說說with這乙個關鍵字。

在js高階程式設計中是這樣描述with關鍵字的:with語句的作用是將**的作用域設定到乙個特定的作用域中,基本語法如下:

1

with (expression) statement;

使用with關鍵字的目的是為了簡化多次編寫訪問同一物件的工作,比如下面的例子:

1

23

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

var hostname = location.hostname;

var url = location.href;

這幾行**都是訪問location物件中的屬性,如果使用with關鍵字的話,可以簡化**如下:

123

45

with (location)

在這段**中,使用了with語句關聯了location物件,這就以為著在with**塊內部,每個變數首先被認為是乙個區域性變數,如果區域性變數與location物件的某個屬性同名,則這個區域性變數會指向location物件屬性。

注意:在嚴格模式下不能使用with語句。

效能問題

首先說說效能問題,關於使用with關鍵字的效能問題,首先我們來看看兩段**:

第一段**是沒有使用with關鍵字:

123

4567

891011

function func

() ;

for (var i = 0; i < 100000; i++)

console.timeend("func");

}func

();

第二段**使用了with關鍵字:

123

4567

891011

1213

1415

function

funcwith() ;

var obj2 = ;

with (obj2)

}console.timeend("funcwith");

}funcwith();

在使用了with關鍵字後了,**的效能大幅度降低。第二段**的with語句作用到了obj2這個物件上,然後with塊裡面訪問的卻是obj物件。有一種觀點是:使用了with關鍵字後,在with塊內訪問變數時,首先會在obj2上查詢是否有名為obj的屬性,如果沒有,再進行下一步查詢,這個過程導致了效能的降低。但是程式效能真正降低的原因真的是這樣嗎?

我們修改一下第二段**,修改如下:

123

4567

891011

1213

function

funcwith() ;

with (obj)

}console.timeend("funcwith");

}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。

123

4567

891011

1213

1415

1617

1819

function

foo(obj)

}var o1 = ;

var o2 = ;

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。

123

4567

891011

1213

var obj = }};

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中,變數宣告會被提公升到函式的頂部,儘管有的時候,它是在後面宣告的。

所以上面的**可以解析為:

123

4567

891011

1213

1415

var obj = }};

obj.foo();

console.log(obj.x);//20,obj.x已被修改為20

console.log(obj.y);//undefined,obj不存在y屬性,則為undefined

有興趣的同學可以看看下面這段**:

123

4567

891011

1213

1415

(

with (this)

}}).foo();

這段**會輸出什麼?為什麼呢?

本文總結了with語句的特點和弊端,總的來說,強烈不推薦使用with關鍵字。其實在日常編碼中,我們只需要知道不去使用with就可以了,但是有的時候我們可能會遇到一些關於with的奇奇怪怪的問題,想要找出真正的原因,就要深入理解with關鍵字,這有助於我們去深入學習js這門語言,同時也是學習js的乙個樂趣。歡迎小夥伴們來交流!!

JavaScript中的this詳解

this屬於js的底層知識,了解this之後,能夠實現一些基本的功能,但是感覺最重要的是,this是物件導向必不可少的組成部分,如果希望能夠逐漸的掌握物件導向,this必然是不可少的。檢視this指向的一句話法則 永遠指向其所在函式的所有者如果沒有所有者時,指向window。理解this的要點 關鍵...

JavaScript中的setInterval用法

setinterval function,interval arg1,arg2,argn setinterval object,methodname,interval arg1,arg2,argn 第一種格式是標準動作面板中setinterval函式的預設語法,第二種格式是在專家模式動作中使用的方法...

JavaScript中的陣列

陣列 引數 返回值原陣列是否改變 1 向陣列尾部新增push 引數可以是乙個或多個 返回值是新增後陣列的長度 原陣列改變 2 刪除陣列的最後一項pop 沒有引數 返回值是刪除的那個數 原陣列改變 3 向陣列的頭部新增unshift 引數可以是乙個或多個 返回值是新增後陣列的長度 原陣列改變 4 刪除...