整理了5個JavaScript怪異行爲及其緣由

若是你用 JavaScript 寫過項目或者參加過面試,那必定遇到過很多使人匪夷所思的問題。JavaScript 早期的規範不統一,也沒有嚴格的標準,再加上它的語法靈活多樣,有些看起來就不正確的代碼卻能正常執行,一些看起來符合邏輯的代碼,運行結果卻相差十萬八千里。這些問題在平常開發中常常會致使 BUG,更重要的是,不少面試官會把它們拿出來當考驗我們 JS 工程師的能力。那麼這篇文章就總結了 5 個 JavaScript 比較坑的問題,以及它們出現的緣由和解決方法。前端

一、可選分號

問題:web

function foo({
 return 
  {
     value1
  };
}
console.log(typeof foo());

你可能會認爲它的輸出結果是 "Object",可是結果倒是 undefined。乍一看代碼好像沒什麼問題,可是細心一點能夠看到 return 語句返回的對象放到了下一行,那麼問題就來了:JavaScript 的分號是可選的,return 語句在換行後,JavaScript 會自動給它的結尾加上分號,而在 return 以後的代碼都不會執行,因此 foo() 的返回結果是 undefined。解決方法是在每行結尾都寫上分號,這樣就能清楚的知道代碼在哪裏結束。面試

二、this 指向

問題:數組

var a = 5;
var obj = {
 a3,
  foofunction({
    console.log(this.a);
  }
}

var objFoo = obj.foo;
objFoo();

答案爲 5。在調用函數時,它內部的 this 指向的是調用對象,例如 obj.foo() this 指向的是 obj 對象。若是在全局調用函數時, this 指向的是全局對象,在瀏覽器中爲 window。objFoo 至關因而在獲取了 obj 對象的 foo 方法引用後,在全局進行調用,因此 this 指向的是 window 對象。使用  var 在頂級做用域中定義的變量會添加到 window 中,因此 objFoo() 調用打印的是全局中的 a,即 5。瀏覽器

三、數組長度

問題:微信

const arr = [1234];
arr.length = 0;
console.log(arr[0]);

結果爲 undefined, 由於 array 的 length 屬性同時也能反過來控制數組的元素數量,在給 arr.length 設置爲 0 時,arr 就變成了空數組,再訪問裏邊的元素就都是 undefined 了。前端工程師

四、提高(Hoisting)

問題:閉包

function bar({
  return foo;
  foo = 10;
  function foo({}
  var foo = '11';
}
console.log(typeof bar());

結果爲 function。使用 var 聲明的變量和使用 funtion 定義的函數會提高到當前做用域的頂部,因此變量能夠先賦值,後使用 var 進行聲明,而函數則能夠先調用後定義。「可是要注意的是,使用 var 定義(指在聲明的同時進行賦值)的變量,只會提高聲明部分,賦值部分不會被提高,例如示例中的」 var foo = '11' 會提高 var foo,但 foo = 11 保留在原位。在定義 bar() 函數時,同時會建立一個做用域,提高會把相關變量和函數放到 bar() 函數的第一行。綜合上邊的規則,能夠知道 foo() 函數和 foo 變量的聲明進行了提高,由於 foo 變量與同名,可是隻有聲明,因此不會覆蓋函數的值,foo 仍然指向的是函數。以後就直接使用 return 語句返回告終果,後邊的代碼就不會再執行了。bar() 中的代碼實際上是下邊這種形式:編輯器

function bar({
  function foo({}
  var foo;
  return foo;
  foo = 10;
  var foo = '11';
}

五、做用域與閉包

問題:函數

for(var i = 0; i < 3; i++) {
  setTimeout(() => {
    console.log(i);
  });
}

你可能會認爲上方代碼的結果爲 0 1 2,但其實是 3 3 3,這是由於使用 var 關鍵字定義的變量沒有塊級做用域,在 for 循環中定義的 i 至關因而全局變量,它會添加到 window 變量中,即便在 for 循環退出後也能訪問 i 的值。這樣就致使了一個問題,使用 setTimeout() 推遲的函數會在 for 循環結束後才執行,此時 i 的值已經變成 3 了,因此 3 個 setTimeout() 中的函數都會打印出 3。要解決這個問題有兩種方法。

第 1 種是使用 let 關鍵字定義變量 i,這樣在每次循環時,都會建立一個新的做用域,所以每一個做用域中的 i 是相互獨立的,這樣就能打印出 0 1 2

第 2 種方法是使用自執行函數,例以下邊代碼這樣:

for(var i = 0; i < 3; i++) {
 (function(i{
    setTimeout(() => {
      console.log(i);
    })
  })(i)
}

這時,i 經過參數傳遞給了匿名的自執行函數,同時自執行函數建立了一個閉包,因此它會捕獲 i 的值,至關於在內部複製了參數 i 的值,因此不管外邊的 i 怎麼變化,它內部的值都不會發生改變。這樣也能打印出 0 1 2

小結

這 5 個問題揭露了 JavaScript 中常見的一些坑,稍微不注意就會留下隱患,而且難以察覺,例如一個簡單的換行、 this 指向的改變、意外修改數組的長度、變量和函數提高、做用域的建立都有可能出現異外的狀況。這些問題可能在平常開發中並很少見,可是常常會出如今 JS 筆試和麪試中,用於考察面試者對 JS 的熟悉程度。另外, JS 中的坑遠不止這些,因此須要在平常中多積累,另外也可關注本博客,我會不定時的更新 JavaScript 使用上的問題,感謝!

本文分享自微信公衆號 - 峯華前端工程師(qiantu_me)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。

相關文章
相關標籤/搜索