Javascript中的with語法

閃現A小兵發表於2020-03-26

這兩天在幹活的時候,遇到了這麼一個業務場景.就是後臺介面返回的資料中有不少欄位是我不需要的,而我只想保留幾個我想要的資料欄位,不想要整個物件賦值.

我們假設後臺返回的資料如下:

{
  "code" : "200",
  "msg" : "操作成功",
  "data" : {
    "name": "zhangsan",
    "age": 12,
    "job": "FE developer",
    "like": "football"
  },
  "hasError" : false
}
複製程式碼

按照以前的做法,管它三七二十一,全給我拿來.直接把整個物件賦值給myData

aInterface().then(res => {
	this.myData = res.data.data
})
複製程式碼

然後在模板中使用:

<div>
    <ul>
      <li v-for="prop in myData">
        {{prop}}
      </li>
    </ul>
</div>
複製程式碼

而我現在只想要 nameage 這兩個欄位,上面的做法會將所有的屬性值全部列印出來.所以,我採用的賦值方法一般是

aInterface().then(res => {
    const data = res.data.data
    this.myData.name = data.name
    this.myData.age = data.age
})
複製程式碼

但是這樣的寫法就顯得有點繁瑣了,每次都要寫 this.myData 這個變數了.此時想起了以前看到過的有個叫 with 的語句,語法是這樣的:

const obj = {
  name:'zhangsan',
  age:12
}
console.log(obj)  // {name: "zhangsan", age: 12}

obj.name = 'lisi'
obj.age = 15
console.log(obj)  // {name: "lisi", age: 15}

with(obj){
  name = 'wangwu'
  age = 18
  job = 'fe'
}
console.log(obj)  // {name: "wangwu", age: 18}
複製程式碼

obj.a = 11 這種是我們最常見的給物件裡面屬性賦值的方式,而 with 後面跟了一個表示式 obj , 花括號裡面的語句就只需要 a = 111 這樣的賦值方式,感覺是可以少寫一點程式碼了. 那麼我們可以看出,其實 with 關鍵字改變了裡面語句的作用域,在 with 程式碼塊內部,變數會先被認為是一個區域性變數,如果某個變數名與表示式 obj 中的一個屬性名是一樣的,那麼這個區域性變數就會指向 obj 物件中同名屬性.但假如 obj 中沒有一個屬性名是與程式碼塊中的區域性變數的名字是一樣的,那麼此時就會發生汙染全域性變數的現象.不知道小夥伴們是否注意到了,上面的程式碼中,在程式碼塊的最後,我加了 job = 'fe' 這樣的一句語句.但是,console 控制檯列印出來的 obj 中並沒有包含這個屬性.這就是因為此時的 job 這個變數已經洩漏到了全域性作用域中了,我們可以繼續在控制檯執行下面程式碼

console.log(job === window.job)  // true
複製程式碼

會發現結果是 true ,證明此時 job 已經是全域性變數了.這就是 with 的其中一個弊端,容易資料洩漏,汙染全域性.而 with 的另外一個缺點就是會導致效能下降.我們來執行如下程式碼,然後在控制檯檢視執行結果:

function test(){
  console.time('test')
  const obj = {
    a:1
  }
  for(let i = 0;i < 99999; i++){
    const tmp = obj.a
  }
  console.timeEnd('test')
}
test()  // test: 1.2958984375ms

function testWith(){
  console.time('testWith')
  const obj = {
    a:1
  }
  with(obj){
    for(let i = 0;i < 99999; i++){
      const tmp = a
    }
  }
  
  console.timeEnd('testWith')
}
testWith()  // testWith: 12.7939453125ms
複製程式碼

注意: console.time() 方法是作為計算器的起始方法, console.timeEnd()方法作為計算器的結束方法 . 兩個方法配合使用,一般是用來測試一段程式執行的時長.所以,這裡兩個函式的執行結果很可能是每次都不一樣的,但是每次偏差應該都不會相差太大.由此,我們可以看出使用了 with 語句的程式碼所需要的時間更長.是沒有使用with語句的好多倍.這是因為JavaScript引擎在編譯階段就進行一些效能優化,比如在執行之前就確定了一些變數的定義位置,然後在程式碼真正的執行過程中可以快速的找到識別符號,而使用了 with 之後,因為無法知道傳遞到with 中的物件是哪個,所以JavaScript引擎它也會很懵逼,然後選擇放棄,不做優化.

總結: 我們程式碼中儘量不推薦使用 with 語句,並且在ES5嚴格模式中已經禁止該標籤.但假如真的很想要用它的話,那麼在 with 程式碼塊的語句中,儘量使用指定物件的屬性作為變數名,至少我們要保證儘量的不汙染全域性.

相關文章