javascript中的with關鍵字

来源:http://www.cnblogs.com/lrzw32/archive/2016/02/14/5189450.html
-Advertisement-
Play Games

說起js中的with關鍵字,很多小伙伴們的第一印象可能就是with關鍵字的作用在於改變作用域,然後最關鍵的一點是不推薦使用with關鍵字。聽到不推薦with關鍵字後,我們很多人都會忽略掉with關鍵字,認為不要去管它用它就可以了。但是有時候,我們在看一些代碼或者面試題的時候,其中會有with關鍵字的...


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

基本說明

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

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語句。

with關鍵字的弊端

前面的基本說明中,我們可以看到with的作用之一是簡化代碼。但是為什麼不推薦使用呢?下麵我們來說說with的缺點:
1、性能問題
2、語義不明,調試困難

性能問題

首先說說性能問題,關於使用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關鍵字:

 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的屬性,如果沒有,再進行下一步查找,這個過程導致了性能的降低。但是程式性能真正降低的原因真的是這樣嗎?
我們修改一下第二段代碼,修改如下:

 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關鍵字,變數提升等等,我們來一一解釋一下。
1、this關鍵字
關於this關鍵字的文章google上面相當多,這裡不再贅述,我們只需記住一點:this關鍵字始終指向調用函數的對象。在這裡,foo函數中,this指向的就是obj對象。因此在with(this)語句塊裡面,可以直接通過x變數來訪問obj的x屬性。
2、變數提升
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,在bojg上找不到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/


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • ssh username@IP 即可
  • ViewData ViewBag TempData 類型 字典 Dynamic TempDataDictionary 出生時間 MVC1 MVC3 框架版本 .net3.5 .net4.0 是否需要拆裝箱 需要 不需要 存儲數據周期 一次HTTP請求 一次HTTP請求 一次HTTP請求 前後臺調用數
  • 有的時候打開下載的別人的項目時,會出現如下圖的提醒: 將Web.config中“<add key="webpages:Version" value="3.0.0.0" />”修改為“<add key="webpages:Version" value="4.0.0.0" />” 效果如下圖: 修改前:
  • 下載別人的範例,出現由於Nuget套件不存在而無法啟動時: 效果如下圖: 步驟如下: 1.點擊 項目-》啟用NuGet程式包還原 2.點擊下圖中的是 3.點擊下圖中的確定 4.效果如圖: . 5.右擊 引用-》 點擊 管理NuGet程式包 6.點擊左上角的 “還原” 7.效果如下圖:
  • 作者:竹竿 這章我們講Java Spring的Ioc控制反轉, DI依賴註入。 閱讀此文之前,必須深入理解Java反射原理。 1、 Ioc控制反轉 原來類對象實例的創建都是有程式員自己通過new進行的。控制反轉之後,我們把創建對象的功能交給Spring,到時候我們直接用就可以了。 2、 DI註入 c
  • jsp文件解析成class文件過程需要利用tomcat的jasper組件。 Jasper是tomcat中使用的JSP引擎,在Tomcat 6中使用的是Jasper 2,相對於原來的版本作了不少的改進,比如:JSP的標簽緩衝池、後臺編譯、頁面改變時自動重新編譯、Eclipse中JDT編譯等等。 簡單的
  • 代碼示例: 1 package com.shejimoshi.create.Prototype; 2 3 4 /** 5 * 功能:用原型實例指定創建對象的種類,並且通過拷貝這些原型創建新的對象 6 * 適用:當一個系統應該獨立於她得產品創建、構成和表示時,要使用Prototype模式 7 * 實例
  • HTML在佈局上, 有一個非常重要的模型, 那就是盒子模型, 在盒子模型中把標簽內容理解為一個物品, 而css樣式理解為包容著這個物品的盒子, 一般的塊級標簽都具有盒子模型的特征, 你可以在css中對這個盒子進行設置, 以達到自己佈局的目的, 我這裡繪製了個簡圖, 只寫了內填充和外邊距, 因為我感覺
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...