天天看點

【JavaScript精華荟萃】精華彙總+易混淆知識(上篇)

前言:

因為我目前是一名大二學生,是以可能有些内容是站在學生角度了解的,是以如有不當,還請各位前輩指點。

寫此文是想梳理一下JS的知識,以及一些易混淆的内容,順便進行回顧,因為确實有一段時間沒真正地在JS上花精力了,同時我也将我的JS經曆寫在了文章最後。

文章目錄

  • ​​0.JS的Debug​​
  • ​​1.資料類型具體取值+Undefined+Null​​
  • ​​2.===與NaN問題​​
  • ​​3.浮點數底層誤差問題​​
  • ​​4.同一數組可存不同類型​​
  • ​​5.嚴格檢查模式​​
  • ​​6.轉義符 \ 的另一種用途​​
  • ​​7.随機數Math.random()取值問題​​
  • ​​8.全局變量與局部變量​​
  • ​​9.多行字元串與模闆字元串​​
  • ​​10.slice()與substring()​​
  • ​​11.Number(),parseInt()和parseFloat()彙總​​
  • ​​12.forEach()函數​​
  • ​​13.閉包​​

0.JS的Debug

浏覽器中自帶原生的JS Debug功能,非常友善

【JavaScript精華荟萃】精華彙總+易混淆知識(上篇)

1.資料類型具體取值+Undefined+Null

【基本資料類型】:

  • 數字(Number)

    (1)整數:123

    (2)浮點數:123.1

    (3)科學計數法1.123e3

    (4)負數:-99

    (5)NaN:不是一個數(但本身歸屬于數字類型)

    (6)Infinity:無窮大的結果

  • 字元串(String)

    (1)單引号:‘abc’

    (2)雙引号:“abc”

  • 布爾值(Boolean)

    (1)true

    (2)false

  • 未定義值(Undefined)
  • 【JavaScript精華荟萃】精華彙總+易混淆知識(上篇)
    【JavaScript精華荟萃】精華彙總+易混淆知識(上篇)
  • 空值(Null)
  • 【JavaScript精華荟萃】精華彙總+易混淆知識(上篇)

【引用資料類型】:

對象:(注:JS中所有事物都是對象,此處分類是按引用型資料類型進行分類後的對象,可了解為對象的子集)

  • 字元串對象(String)
  • 【JavaScript精華荟萃】精華彙總+易混淆知識(上篇)
  • 數組對象(Array)
  • 【JavaScript精華荟萃】精華彙總+易混淆知識(上篇)
  • 日期對象(Date)
  • 【JavaScript精華荟萃】精華彙總+易混淆知識(上篇)
  • 數值對象(Math)
  • 【JavaScript精華荟萃】精華彙總+易混淆知識(上篇)

2.===與NaN問題

1.盡量不要使用==比較,因為==隻比較值,不要求類型一緻,判斷相等,盡量用===

2.NaN與任何數都不相等,包括它與自己

【JavaScript精華荟萃】精華彙總+易混淆知識(上篇)
【JavaScript精華荟萃】精華彙總+易混淆知識(上篇)

3.隻能通過 isNaN() 的方式,判斷某變量結果是否為NaN

【JavaScript精華荟萃】精華彙總+易混淆知識(上篇)

3.浮點數底層誤差問題

(1/3) 不等于(1 - 2/3)

【JavaScript精華荟萃】精華彙總+易混淆知識(上篇)

此問題涉及計算機底層硬體中,運算器的三個寄存器(現代計算機可能已不是三個了),ACC累加器、MQ乘商寄存器、X操作數寄存器,它們與算術邏輯單元共同完成運算。對于浮點數,它們的計算都是有限精度,無法達到絕對精度,存儲器和寄存器内部能存的精度都是有限的,是以導緻上述問題。(注:這裡如果解釋的不當,還請前輩指正)同樣,相似的精度問題例子還有:sinx,它在計算機底層是這樣得出的結果:

【JavaScript精華荟萃】精華彙總+易混淆知識(上篇)

計算機也不能直接求解開放x:

【JavaScript精華荟萃】精華彙總+易混淆知識(上篇)

那該如何比較上述類似結果的浮點數呢?

可用顯示容許誤差法:允許一定範圍的誤差

【JavaScript精華荟萃】精華彙總+易混淆知識(上篇)

4.同一數組可存不同類型

1.JS中同一數組可存不同類型:

【JavaScript精華荟萃】精華彙總+易混淆知識(上篇)

2.為保證可讀性,數組盡量用[ ](少用new Array()),而對象盡量用{ }3.取數組下标溢出時,結果為undefined,(在對象操作中,也是如此,通路對象不存在的屬性時,結果也為undefined)

【JavaScript精華荟萃】精華彙總+易混淆知識(上篇)

5.嚴格檢查模式

在<script>标簽内的第一行,加上’use strict’,進而開啟嚴格檢查,嚴格檢查可以預防因JavaScript的随意性而導緻産生的一些問題

(注:如果’use strict’本身标了紅色波浪線,那麼需要設定IDEA支援ES6的文法)

圖中,a标紅,滑鼠懸浮上去也會提示錯誤資訊

【JavaScript精華荟萃】精華彙總+易混淆知識(上篇)

同時在console中報錯

【JavaScript精華荟萃】精華彙總+易混淆知識(上篇)

删去後,a不再标紅

【JavaScript精華荟萃】精華彙總+易混淆知識(上篇)

6.轉義符 \ 的另一種用途

平時也可在浏覽器的Console中,随手測試不熟練的Unicode和ASCII。

【JavaScript精華荟萃】精華彙總+易混淆知識(上篇)
【JavaScript精華荟萃】精華彙總+易混淆知識(上篇)
【JavaScript精華荟萃】精華彙總+易混淆知識(上篇)

7.随機數Math.random()取值問題

  • Math.random():[0, 1)
  • Math.random()*m:[0, m)
  • Math.random()*m+n:[n, m+n)
  • Math.random()*m-n:[-n, m-n)
  • Math.random()*m-m:[-m, 0)

8.全局變量與局部變量

全局變量:有兩種表示方法:

1.在函數外定義的變量(必須用var關鍵字定義才可,let與const則仍是局部變量,隻不過是作用域較大的局部變量,這個結論可以利用全局變量一定屬于window對象這個特性測試出來);

2.在函數内但沒有使用 var 關鍵字聲明的變量。

表示法1例子:

var carName = " Volvo";
 
// 此處可調用 carName 變量
function myFunction() {
    // 函數内可調用 carName 變量
}      

表示法2例子:

// 此處可調用 carName 變量
 
function myFunction() {
    carName = "Volvo";
    // 此處可調用 carName 變量
}      

注:1)全局變量有全局作用域: 網頁中所有腳本和函數均可使用。

2)全局變量在 HTML 中都屬于window對象。

3)全局變量可以覆寫 window 對象的變量或者函數。

局部變量:在函數内使用 var 關鍵字或 let 關鍵字聲明的變量。(在ES6中,用let關鍵字定義局部變量)

例子:

// 這裡不能使用 carName 變量
 
function myFunction() {
    var carName = "Volvo";
    // 這裡可以使用 carName 變量
}
 
// 這裡不能使用 carName 變量      

9.多行字元串與模闆字元串

都是利用符号:` `

多行字元串:

【JavaScript精華荟萃】精華彙總+易混淆知識(上篇)

模闆字元串:

【JavaScript精華荟萃】精華彙總+易混淆知識(上篇)
【JavaScript精華荟萃】精華彙總+易混淆知識(上篇)

而且,JS中的字元串與Java中的字元串一樣都是不可變的,測試如下:

【JavaScript精華荟萃】精華彙總+易混淆知識(上篇)

10.slice()與substring()

slice(start, end) :[start, end)

substring(from, to):[start, end)

【JavaScript精華荟萃】精華彙總+易混淆知識(上篇)

但不同的是,slice()可支援參數取負數,如-1表示最後一個下标

【JavaScript精華荟萃】精華彙總+易混淆知識(上篇)

11.Number(),parseInt()和parseFloat()彙總

【JavaScript精華荟萃】精華彙總+易混淆知識(上篇)

12.forEach()函數

将數組中的每個元素映射給forEach()函數中的參數函數,逐一調用。

(我在大二上學期做遊戲開發的時候,正是用的這個函數,首先用一個數組接收使用者輸入的方向鍵,例如單純的正上向,或者是複合的左上方向,把鍵碼用數組收容,并用forEach函數不斷周遊存方向鍵的數組中的每個元素傳入這個函數的參數函數,進而進一步完成模型位置的更新)

【JavaScript精華荟萃】精華彙總+易混淆知識(上篇)

13.閉包

(這裡放一個菜鳥教程裡的經典例子)

var add = (function () {
    var counter = 0;
    return function () {return counter += 1;}
})();
 
add();
add();
add();
 
// 計數器為 3      

但我認為這樣會更好了解(這是菜鳥教程上一個大佬的評論):

var add =function () {
    var counter = 0;
    window.alert("父方法"); // 隻有在 add 指派時執行一次 
    return function () {
        window.alert("子方法");  // 每次執行 add() 都會執行
        return counter += 1;

    }    
    // counter 作用域在父函數中, 自然在其子函數中也能使用,但因為
    // 子函數還需要使用了count, 是以 count 不随着父函數一起釋放。    
    // 利用在 function(){}() 的形式自動執行一遍父匿名函數, 賦給 add 子方法。
}();

function myFunction(){
    document.getElementById("demo").innerHTML = add();  //這裡add()執行的就是子方法
}      

因為閉包會持有父方法的局部變量并且不會随父方法銷毀而銷毀, 是以這個counter其實就是來自于第一次function執行時建立的變量,這個function自運作一遍之後,其實最後指派給add的是return counter += 1 這段代碼。

是以後面每次調用add() 其實都是在調用return counter += 1

變量 add 指定了函數自我調用的傳回字值。

自我調用函數隻執行一次。設定計數器為 0。并傳回函數表達式。

add變量可以作為一個函數使用。非常棒的部分是它可以通路函數上一層作用域的計數器。

這個叫作 JavaScript 閉包。它使得函數擁有私有變量變成可能。

計數器受匿名函數的作用域保護,隻能通過 add 方法修改。

閉包是一種保護私有變量的機制,在函數執行時形成私有的作用域,保護裡面的私有變量不受外界幹擾。

因為閉包會持有父方法的局部變量并且不會随父方法銷毀而銷毀,是以直覺的說就是形成一個不銷毀的棧環境。

繼續閱讀