前端學習手冊

Undefined、Nulljavascript

一、爲何有的編程規範要求用 void 0 代替 undefined?java

  Undefined 類型表示未定義,它的類型只有一個值,就是 undefined。任何變量在賦值前是 Undefined 類型、值爲 undefined,通常咱們能夠用全局變量 undefined(就是名爲 undefined 的這個變量)來表達這個值,或者 void 運算來把任意一個表達式變成 undefined 值。由於 JavaScript 的代碼 undefined 是一個變量,而並不是是一個關鍵字,這是 JavaScript 語言公認的設計失誤之一,因此,咱們爲了不無心中被篡改,我建議使用 void 0 來獲取 undefined 值。Undefined 跟 Null 有必定的表意差異,Null 表示的是:「定義了可是爲空」。因此,在實際編程時,咱們通常不會把變量賦值爲 undefined,這樣能夠保證全部值爲 undefined 的變量,都是從未賦值的天然狀態。Null 類型也只有一個值,就是 null,它的語義表示空值,與 undefined 不一樣,null 是 JavaScript 關鍵字,因此在任何代碼中,你均可以放心用 null 關鍵字來獲取 null 值。算法

String編程

一、string最大長度瀏覽器

String 用於表示文本數據。String 有最大長度是 2^53 - 1,這在通常開發中都是夠用的,可是有趣的是,這個所謂最大長度,並不徹底是你理解中的字符數。由於 String 的意義並不是「字符串」,而是字符串的 UTF16 編碼,咱們字符串的操做 charAt、charCodeAt、length 等方法針對的都是 UTF16 編碼。因此,字符串的最大長度,其實是受字符串的編碼長度影響的。函數

Note:現行的字符集國際標準,字符是以 Unicode 的方式表示的,每個 Unicode 的碼點表示一個字符,理論上,Unicode 的範圍是無限的。
UTF 是 Unicode 的編碼方式,規定了碼點在計算機中的表示方法,常見的有 UTF16 和 UTF8。
Unicode 的碼點一般用 U+??? 來表示,其中 ??? 是十六進制的碼點值。
0-65536(U+0000 - U+FFFF)的碼點被稱爲基本字符區域(BMP)。

Number性能

值得注意的是,JavaScript 中有 +0 和 -0,在加法類運算中它們沒有區別,可是除法的場合則須要特別留意區分,「忘記檢測除以 -0,而獲得負無窮大」的狀況常常會致使錯誤,而區分 +0 和 -0 的方式,正是檢測 1/x 是 Infinity 仍是 -Infinity。this

根據雙精度浮點數的定義,Number 類型中有效的整數範圍是 -0x1fffffffffffff 至 0x1fffffffffffff,因此 Number 沒法精確表示此範圍外的整數。一樣根據浮點數的定義,非整數的 Number 類型沒法用 ==(=== 也不行) 來比較,一段著名的代碼,爲何在 JavaScript 中,0.1+0.2 不能 =0.3:這是浮點運算的特色,也是不少同窗疑惑的來源,浮點數運算的精度問題致使等式左右的結果並非嚴格相等,而是相差了個微小的值。因此實際上,這裏錯誤的不是結論,而是比較的方法,正確的比較方法是使用 JavaScript 提供的最小精度值:編碼

console.log( Math.abs(0.1 + 0.2 - 0.3) <= Number.EPSILON);

  檢查等式左右兩邊差的絕對值是否小於最小精度,纔是正確的比較浮點數的方法。這段代碼結果就是 true 了。spa

Symbol

Symbol 是 ES6 中引入的新類型,它是一切非字符串的對象 key 的集合,在 ES6 規範中,整個對象系統被用 Symbol 重塑。Symbol 能夠具備字符串類型的描述,可是即便描述相同,Symbol 也不相等。咱們建立 Symbol 的方式是使用全局的 Symbol 函數。例如:

var mySymbol = Symbol("my symbol");

  一些標準中提到的 Symbol,能夠在全局的 Symbol 函數的屬性中找到。例如,咱們可使用 Symbol.iterator 來自定義 for…of 在對象上的行爲:

var o = new Object;
o[Symbol.iterator] = function() {
   var v = 0
   return {
    next: function() {
       return { value: v++, done: v > 10 }
    }
  }
};
for(var v of o)
   console.log(v); // 0 1 2 3 ... 9

  代碼中咱們定義了 iterator 以後,用 for(var v of o) 就能夠調用這個函數,而後咱們能夠根據函數的行爲,產生一個 for…of 的行爲。這裏咱們給對象 o 添加了 Symbol.iterator 屬性,而且按照迭代器的要求定義了一個 0 到 10 的迭代器,以後咱們就能夠在 for of 中愉快地使用這個 o 對象啦。這些標準中被稱爲「衆所周知」的 Symbol,也構成了語言的一類接口形式。它們容許編寫與語言結合更緊密的 API。

Object

爲何給對象添加的方法能用在基本類型上?

在 JavaScript 中,對象的定義是「屬性的集合」。屬性分爲數據屬性和訪問器屬性,兩者都是 key-value 結構,key 能夠是字符串或者 Symbol 類型。由於 C++ 和 Java 的成功,在這兩門語言中,每一個類都是一個類型,兩者幾乎等同,以致於不少人經常會把 JavaScript 的「類」與類型混淆。事實上,JavaScript 中的「類」僅僅是運行時對象的一個私有屬性,而 JavaScript 中是沒法自定義類型的。

JavaScript 中的幾個基本類型,都在對象類型中有一個「親戚」。它們是:Number;String;Boolean;Symbol。

因此,咱們必須認識到 3 與 new Number(3) 是徹底不一樣的值,它們一個是 Number 類型, 一個是對象類型。Number、String 和 Boolean,三個構造器是兩用的,當跟 new 搭配時,它們產生對象,當直接調用時,它們表示強制類型轉換。Symbol 函數比較特殊,直接用 new 調用它會拋出錯誤,但它仍然是 Symbol 對象的構造器。

JavaScript 語言設計上試圖模糊對象和基本類型之間的關係,咱們平常代碼能夠把對象的方法在基本類型上使用,好比:

console.log("abc".charAt(0)); //a

  甚至咱們在原型上添加方法,均可以應用於基本類型,好比如下代碼,在 Symbol 原型上添加了 hello 方法,在任何 Symbol 類型變量均可以調用。

Symbol.prototype.hello = () => console.log("hello");

var a = Symbol("a");
console.log(typeof a); //symbol,
a並不是對象 a.hello(); //hello,有效

  點運算符提供了裝箱操做,它會根據基礎類型構造一個臨時對象,使得咱們能在基礎類型上調用對應對象的方法

類型轉換

 

   在不傳入第二個參數的狀況下,parseInt 只支持 16 進制前綴「0x」,並且會忽略非數字字符,也不支持科學計數法。在一些古老的瀏覽器環境中,parseInt 還支持 0 開頭的數字做爲 8 進制前綴,這是不少錯誤的來源。因此在任何環境下,都建議傳入 parseInt 的第二個參數,而 parseFloat 則直接把原字符串做爲十進制來解析,它不會引入任何的其餘進制。多數狀況下,Number 是比 parseInt 和 parseFloat 更好的選擇。

在較小的範圍內,數字到字符串的轉換是徹底符合你直覺的十進制表示。當 Number 絕對值較大或者較小時,字符串表示則是使用科學計數法表示的。這個算法細節繁多,咱們從感性的角度認識,它其實就是保證了產生的字符串不會過長。

裝箱轉換

每一種基本類型 Number、String、Boolean、Symbol 在對象中都有對應的類,所謂裝箱轉換,正是把基本類型轉換爲對應的對象,它是類型轉換中一種至關重要的種類。

前文提到,全局的 Symbol 函數沒法使用 new 來調用,但咱們仍能夠利用裝箱機制來獲得一個 Symbol 對象,咱們能夠利用一個函數的 call 方法來強迫產生裝箱。咱們定義一個函數,函數裏面只有 return this,而後咱們調用函數的 call 方法到一個 Symbol 類型的值上,這樣就會產生一個 symbolObject。咱們能夠用 console.log 看一下這個東西的 type of,它的值是 object,咱們使用 symbolObject instanceof 能夠看到,它是 Symbol 這個類的實例,咱們找它的 constructor 也是等於 Symbol 的,因此咱們不管從哪一個角度看,它都是 Symbol 裝箱過的對象:

var symbolObject = (function(){
  return this;
 }).call(Symbol("a"));

console.log(typeof symbolObject); //object 
console.log(symbolObject instanceof Symbol); //true console.log(symbolObject.constructor == Symbol); //true

  裝箱機制會頻繁產生臨時對象,在一些對性能要求較高的場景下,咱們應該儘可能避免對基本類型作裝箱轉換。

使用內置的 Object 函數,咱們能夠在 JavaScript 代碼中顯式調用裝箱能力。

var symbolObject = Object(Symbol("a")); 

console.log(typeof symbolObject); //object 
console.log(symbolObject instanceof Symbol); //true console.log(symbolObject.constructor == Symbol); //true

  每一類裝箱對象皆有私有的 Class 屬性(舊版本規定的),這些屬性能夠用 Object.prototype.toString 獲取:

var symbolObject = Object(Symbol("a")); 

console.log(Object.prototype.toString.call(symbolObject)); //[object Symbol]

  在 JavaScript 中,沒有任何方法能夠更改私有的 Class 屬性,所以 Object.prototype.toString 是能夠準確識別對象對應的基本類型的方法,它比 instanceof 更加準確。但須要注意的是,call 自己會產生裝箱操做,因此須要配合 typeof 來區分基本類型仍是對象類型。

拆箱轉換

在 JavaScript 標準中,規定了 ToPrimitive 函數,它是對象類型到基本類型的轉換(即,拆箱轉換)。對象到 String 和 Number 的轉換都遵循「先拆箱再轉換」的規則。經過拆箱轉換,把對象變成基本類型,再從基本類型轉換爲對應的 String 或者 Number。拆箱轉換會嘗試調用 valueOf 和 toString 來得到拆箱後的基本類型。若是 valueOf 和 toString 都不存在,或者沒有返回基本類型,則會產生類型錯誤 TypeError。

    var o = {
        valueOf : () => {console.log("valueOf"); return {}},
        toString : () => {console.log("toString"); return {}}
    }

    o * 2
    // valueOf
    // toString
    // TypeError

  咱們進行 o*2 這個運算的時候,你會看見先執行了 valueOf,接下來是 toString,最後拋出了一個 TypeError,這就說明了這個拆箱轉換失敗了。到 String 的拆箱轉換會優先調用 toString。咱們把剛纔的運算從 o*2 換成 String(o),那麼你會看到調用順序就變了。

var o = {
     valueOf : () => {console.log("valueOf"); return {}},
     toString : () => {console.log("toString"); return {}}
}

 String(o) 
// toString
// valueOf 
// TypeError        

  在 ES6 以後,還容許對象經過顯式指定 @@toPrimitive Symbol 來覆蓋原有的行爲。

    var o = {
        valueOf : () => {console.log("valueOf"); return {}},
        toString : () => {console.log("toString"); return {}}
    }

    o[Symbol.toPrimitive] = () => {console.log("toPrimitive"); return "hello"}


    console.log(o + "")
    // toPrimitive
    // hello

類型」在 JavaScript 中是一個有爭議的概念。一方面,標準中規定了運行時數據類型; 另外一方面,JavaScript 語言中提供了 typeof 這樣的運算,用來返回操做數的類型,但 typeof 的運算結果,與運行時類型的規定有不少不一致的地方。咱們能夠看下錶來對照一下。

 

 在表格中,多數項是對應的,可是請注意 object——Null 和 function——Object 是特例,咱們理解類型的時候須要特別注意這個區別。

相關文章
相關標籤/搜索