1. 程式人生 > >「面試指南」解讀JavaScript原始資料型別

「面試指南」解讀JavaScript原始資料型別

#### **JavaScript 有 7 種原始資料型別:** - **String(字元型)** - **Number(數值型)** - **Boolean(布林值型)** - **Undefined** - **Null** - **Object(物件型)** - **Symbol(符號型,ES6 中新增資料型別)** > 通常使用 typeof 檢測其型別(Null,Array,Object 除外),返回其資料型別值的字串; **String** 字元型:由一系列字元組成的字串,屬於值型別。 **Number** 數值型:用來表示數值,屬於值型別,可以用於算術邏輯計算。 **Boolean** 布林值型:用表示邏輯是,邏輯非:true,false,屬於值型別。 **Undefined** 未定義:定義未賦值,或未定義(直接使用會引起異常),特殊的資料類原始資料型。 **Null** 空:代表一個空指標,特殊的一種原始資料型別。 **Object** 物件型:以 key-value 的方式代表屬性名與屬性值,由{}組成,不同的屬性以,隔開,JS 中一種重要的引用型原始資料型別。 **Symbol** 符號型:ES6 新增的一種原始資料型別,主要用於解決屬性名相同一起的衝突問題,每個 Symbol()值都不相同。 --- #### **3 種基本資料型別:String、Number、Boolean,屬於值型別** ```javascript var a = '10', b = 10, c = true; console.log('a ' + typeof a); // 結果:a string console.log('b ' + typeof b); // 結果:b number console.log('c ' + typeof c); // 結果:c boolean ``` --- #### **2 種特殊資料型別:Undefined、Null;** **Undefined:** 1).未定義,直接使用,報錯;但是直接輸出一個不存在變數的 typeof ,返回 undefined; ```javascript d; // 報錯:ReferenceError: d is not defined console.log(d); // 報錯:ReferenceError: d is not defined console.log(typeof dd); // 不報錯,結果:undefined ``` 2).已定義,但是未賦值,結果:undefined。 ```javascript var e; console.log(e); // 結果不報錯:undefined console.log('e ' + typeof e); // 結果:e undefined ``` **Null:** 歷史原因設計缺陷,最初未設計此型別,當作 object 的一種特殊值。後來作為一種單獨的資料型別獨立出來,為了相容以前的程式碼,typeof null 返回 object 就沒法改變了。 ```javascript var f = null; console.log('f ' + typeof f); // 結果: f object ``` Null 型別的檢測: ```javascript function isNull(value) { if (!value && typeof value === 'object') return true; return false; } console.log(isNull(null)); // 結果:true console.log(isNull('a')); // 結果:false console.log(isNull(123)); // 結果:false console.log(isNull(true)); // 結果:false ``` --- #### **1 種引用資料型別:Object;** Object(物件型):一般是基本資料型別的組合,由{}分隔。在{}內部,物件的屬性以 key-value 鍵值對的形式 (name : value) 來定義。屬性之間由,分隔: ```javascript var city = { id: 1, name: '北京', value: 'BeiJing' }; ``` 有多種定義/定址/修改方式: ```javascript // 直接定義賦值 var city = { id: 1, name: '北京', value: 'BeiJing' }; // 關鍵字new定義並賦值 var city = new Object(); city.id = 1; city.name = '北京'; city.value = 'BeiJing'; // defineProperty單個新增或修改某個屬性值 var city = {}; Object.defineProperty(city, 'id', { value: 1, writable: true, configurable: true, enumerable: true });// 等價於 city[id]=1,或citys.id=1; Object.defineProperty(city, 'name', { value: '北京', writable: true, configurable: true, enumerable: true });// 等價於 city[name]='北京',或citys.name='北京'; Object.defineProperty(city, 'value', { value: 'BeiJing', writable: true, configurable: true, enumerable: true });// 等價於 city[value]='BeiJing',或citys.value='BeiJing'; console.log(city); // {id: 1, name: '北京', value: 'BeiJing'} // defineProperties批量新增或修改屬性值 var city = {}; Object.defineProperties(city, { id: { value: 1, writable: true, configurable: true, enumerable: true }, name: { value: '北京', writable: true, configurable: true, enumerable: true }, value: { value: 'BeiJing', writable: true, configurable: true, enumerable: true } }); // 等價於 直接賦值 city={id: 1, name: '北京', value: 'BeiJing'} console.log(city); // {id: 1, name: '北京', value: 'BeiJing'} ``` > defineProperty:精確地新增或修改物件的屬性這個方法允許修改預設的額外選項(或配置)。 > 一般具有配置屬性:value(屬性值)、writable(是否可修改)、configurable(是否可刪除)、enumerable(是否可列舉), > 預設情況下,使用 Object.defineProperty() 新增的屬性值是不可修改的, > 預設值配置:writable:false,configurable:false,enumerable:false。 ```javascript // 小小說明 // 使用以下方法可以精確配置物件obj: Object.defineProperty(obj,obj_name,{ value:name_value, writable:false,// 預設,配置後不可直接賦值修改 configurable:false,// 預設,配置後不可使用delete刪除 enumerable:false// 預設,配置後不可在for-in、Object.keys()中列舉 }); // 示例說明: var obj={}; // 使用以下方法操作obj,等價於obj={name:'hello'}; Object.defineProperty(obj,name,{ value:'hello', writable:true,// 預設 configurable:true,// 可刪除,可以使用delete obj.name,來刪除name屬性; enumerable:true// 可列舉 }); ``` 屬性有兩種定址方式: ```javascript // 點方法 var cityName = city.name; console.log(cityName); // 北京 // 或者方括號方法 var cityName = city[name]; console.log(cityName); // 北京 ``` 物件可以使用 for-in、Object.keys()來列舉其屬性 ```javascript var city = { id: 1, name: '北京', value: 'BeiJing' }; // for-in列舉屬性 for (const key in city) { if (city.hasOwnProperty(key)) { let value = city[key]; // 或者let value = city.key; console.log(key, value); } } // id 1 // name 北京 // value BeiJing // Object.keys()列舉屬性 for (let i = 0; i < Object.keys(city).length; i++) { const key = Object.keys(city)[i]; let value = city[key]; // 或者let value = city.key; console.log(key, value); } // id 1 // name 北京 // value BeiJing ``` **_物件的原型、原型鏈、方法繼承另篇總結。_** --- #### **1 種 ES6 新增資料型別:Symbol。** > 為什麼引入 Symbol? > ES5 的物件屬性名都是字串,這容易造成屬性名的衝突。比如,你使用了一個他人提供的物件,但又想為這個物件新增新的方法(mixin 模式),新方法的名字就有可能與現有方法產生衝突。如果有一種機制,保證每個屬性的名字都是獨一無二的就好了,這樣就從根本上防止屬性名的衝突。這就是 ES6 引入 Symbol 的原因。 Symbol 是一種用來解決因物件屬性名衝突,來確保每個屬性名全域性唯一的第 7 種新的原始資料型別。 ```javascript var s = Symbol(); console.log('s ', s, typeof s); // s Symbol() 'symbol' ``` 不可以使用 new 來定義一個 Symbol();因為 Symbol()函式生成的是一個原始資料型別的值型別而非引用型別,不是物件,所以不能新增屬性。基本上,它是一種類似於字串/數值的資料型別; ```javascript var s1 = Symbol(); var s2 = Symbol(); console.log(s1 === s2); // false ``` Symbol 函式可以接受一個字串作為引數,表示對 Symbol 例項的描述,主要是為了在控制檯顯示,或者轉為字串時,比較容易區分。相同描述的兩個 Symbol,返回值依然是不相等的,同樣是兩個不同的 Symbol; ```javascript var s1 = Symbol('s1'); var s2 = Symbol('s2'); console.log(s1, s2); // Symbol(s1) Symbol(s2) console.log(s1.toString(), s2.toString()); // "Symbol(s1)" "Symbol(s2)" var _s1 = Symbol('s1'); console.log(_s1 === s1); // false ``` 但是,Symbol 值可以顯式轉為字串。 ```javascript var symb = Symbol('a symbol'); String(symb); // 'Symbol(a symbol)' symb.toString(); // 'Symbol(a symbol)' ``` 另外,Symbol 值也可以轉為布林值,但是不能轉為數值。 ```javascript var symb = Symbol(); Boolean(symb); // true !symb; // false if (symb) { console.log('if symb be true,do somethine.'); // if symb be true,do somethine. } Number(symb); // Uncaught TypeError: Cannot convert a Symbol value to a number symb + 2; // Uncaught TypeError: Cannot convert a Symbol value to a number ``` 當 Symbol 作為屬性名: 由於每一個 Symbol 值都是不相等的,這意味著 Symbol 值可以作為識別符號,用於物件的屬性名,就能保證不會出現同名的屬性。這對於一個物件由多個模組構成的情況非常有用,能防止某一個鍵被不小心改寫或覆蓋。這也是 Symbol 的設計初衷之一。 ```javascript var mySymbol = Symbol(); // 第一種寫法 var a = {}; a[mySymbol] = 'Hello!'; // 第二種寫法 var a = { [mySymbol]: 'Hello!' }; // 第三種寫法 var a = {}; Object.defineProperty(a, mySymbol, { value: 'Hello!' }); // 以上寫法都得到同樣結果 a[mySymbol]; // "Hello!" ``` _注意,Symbol 值作為物件屬性名時,不能用點運算子。_ ```javascript var mySymbol = Symbol(); var a = {}; a.mySymbol = 'Hello!'; a[mySymbol]; // undefined a['mySymbol']; // "Hello!" ``` 在物件的內部,使用 Symbol 值定義屬性時,Symbol 值必須放在方括號之中。 ```javascript let s = Symbol(); let obj = { [s]: function(arg) { // do something } }; // 為了更簡潔,也可以採用增強的物件寫法 let obj = { [s](arg) { // do something } }; obj[s](123); ``` > Symbol 的特性使得用來定義一組常量具有很好的優勢,從而保證常量的值都是不一樣的。 --- #### **Symbol 應用例項:消除魔術字串** 魔術字串:在程式碼之中多次出現、與程式碼形成強耦合的某一個具體的字串或者數值。風格良好的程式碼,應該儘量消除魔術字串,改由含義清晰的變數代替。 其中魔術字串在 switch 條件語句中有大量的體現: ```javascript function getArea(shape, options) { var area = 0; switch (shape) { case 'Triangle': // 魔術字串 area = 0.5 * options.width * options.height; break; // other shape } return area; } getArea('Triangle', { width: 100, height: 100 }); // 常量'Triangle' 程式碼塊中兩次出現,出現了魔術字串,不利於修改與維護; ``` 一般是把常量變成變數,即可消除魔術字串,故上述一般作如下處理: ```javascript var shapeType = { triangle: 'Triangle' }; function getArea(shape, options) { var area = 0; switch (shape) { case shapeType.triangle: area = 0.5 * options.width * options.height; break; } return area; } getArea(shapeType.triangle, { width: 100, height: 100 }); ``` 不過上述的 shapeType.triangle 依然存在無法確保唯一的可能,如果使用 Symbol,就會消除這樣可能,使得 switch 可以以可靠的按照設計的方式執行,以上做如下修改即可。 ```diff var shapeType = { - triangle: 'Triangle' + triangle: Symbol() }; ``` --- #### **物件中屬性包含 Symbol 的遍歷** 當 Symbol 作為物件屬性名時,物件的遍歷就發生了一些小的變化,該屬性就不會再 for-in 、for-of,迴圈中出現,也不會在 Object.keys()、Object.getOwnProperNames()、JSON.stringify()中作為屬性返回了。 不過,它也不是私有屬性,有一個 Object.getOwnPropertySymbols 方法,可以獲取指定物件的所有 Symbol 屬性名。 Object.getOwnPropertySymbols 方法返回一個數組,成員是當前物件的所有用作屬性名的 Symbol 值。 ```javascript var obj = {}, a = Symbol('a'), b = Symbol('b'); obj[a] = 'Hello'; obj[b] = 'World'; obj['c'] = "I'm"; obj['d'] = 'a FE Developer!'; // 4種遍歷方式的對比: // 1.使用for-in遍歷物件 console.error('for-in遍歷物件:'); for (const key in obj) { console.log('key:' + key); } // 2.使用Object.keys遍歷物件 var objKeys_keys = Object.keys(obj); console.error('Object.keys()遍歷物件:'); console.log(objKeys_keys); // ["c","d"] // 3.使用Object.getOwnProperNames(),遍歷物件 var objKeys_ownProper = Object.getOwnPropertyNames(obj); console.error('Object.getOwnPropertyNames()遍歷物件:'); console.log(objKeys_ownProper); // ["c","d] // 4.使用Object.getOwnPropertySymbols,遍歷物件 var objectKeys_symbol = Object.getOwnPropertySymbols(obj); console.error('Object.getOwnPropertySymbols遍歷物件:'); console.log(objectKeys_symbol); // [Symbol(a), Symbol(b)] // 對比輸出結果: // for-in遍歷物件:["c","d"] // Object.keys遍歷物件:["c","d"] // Object.getOwnPropertyNames()遍歷物件:["c","d"] // Object.getOwnPropertySymbols遍歷物件:[Symbol(a), Symbol(b)] ``` 上述使用 for-in、Object.keys()、Object.getOwnPropertyNames()、Object.getOwnPropertySymbols()對物件的遍歷做了對比。那麼,有沒有一種方法可以可以列舉一個物件的所有的屬性呢? 答案是有的:Reflect.keys(); --- #### **Reflect.keys()遍歷物件屬性,不會受 enumerable 影響** 什麼是 Reflect? Reflect 是一個物件,所有屬性和方法都是靜態的,類似於 Math()函式,提供以下靜態方法: > Reflect.apply():對一個函式進行呼叫操作,同時可以傳入一個數組作為呼叫引數。和 Function.prototype.apply() 功能類似。 > > Reflect.construct():對建構函式進行 new 操作,相當於執行 new target(...args)。 > > Reflect.defineProperty():和 Object.defineProperty() 類似。 > > Reflect.deleteProperty():作為函式的 delete 操作符,相當於執行 delete target[name]。 > > Reflect.enumerate():該方法會返回一個包含有目標物件身上所有可列舉的自身字串屬性以及繼承字串屬性的迭代器,for...in 操作遍歷到的正是這些屬性。 > > Reflect.get():獲取物件身上某個屬性的值,類似於 target[name]。 > > Reflect.getOwnPropertyDescriptor():類似於 Object.getOwnPropertyDescriptor()。 > > Reflect.getPrototypeOf():類似於 Object.getPrototypeOf()。 > > Reflect.has():判斷一個物件是否存在某個屬性,和 in 運算子 的功能完全相同。 > > Reflect.isExtensible():類似於 Object.isExtensible(). > > Reflect.ownKeys():返回一個包含所有自身屬性(不包含繼承屬性)的陣列。(類似於 Object.keys(), 但不會受 enumerable 影響). > > Reflect.preventExtensions():類似於 Object.preventExtensions()。返回一個 Boolean。 > > Reflect.set():將值分配給屬性的函式。返回一個 Boolean,如果更新成功,則返回 true。 > > Reflect.setPrototypeOf():類似於 Object.setPrototypeOf()。 在此我們只討論 Reflect.ownKeys(); 因為它類似於 Object.keys(),但是不受 enumerable 影響,所有它會返回一個物件下面的所有屬性(不包括繼承屬性,但包括 Symbol 屬性); ```javascript // 我們對上述的obj,使用此方法,來遍歷它的屬性 var obj = {}, a = Symbol('a'), b = Symbol('b'); obj[a] = 'Hello'; obj[b] = 'World'; obj['c'] = "I'm"; obj['d'] = 'a FE Developer!'; var objKeys_reflect = Reflect.ownKeys(obj); console.log(objKeys_reflect); // ["c", "d", Symbol(a), Symbol(b)] ``` 這樣就遍歷了一個物件下的所有屬性了。 --- #### **Symbol 的一個重要方法:Symbol.for();** 在前面介紹 Symbol 在 ES6 的引入初衷時,我們提到了它是為了避免物件屬性被汙染,而設計的一種新的資料型別。 Symbol 作為物件的屬性,優秀的解決了物件屬性被汙染的問題,但是這樣還不夠。 因為我們有時候需要在某些地方重新使用已經定義的 Symbol,所以 Symbol();就不滿足不了這個需求了。 ES6 中就設計了 Symbol 的一個全域性登記機制:使用 Symbol.for(key)方法會生成指定 key 的會被登記在全域性環境中,在當前執行時具有全域性有效性的 Symbol。同時,Symbol.keyFor 方法可以返回一個已登記的 Symbol 型別值的 key。 ```javascript // Symbol.for登記一個全域性的Symbol const symbol = Symbol.for('foobar'); Symbol.keyFor(symbol); // "foobar" // Symbol.keyFor(),返回一個已登記的 Symbol 型別值的key var s1 = Symbol.for('foo'); Symbol.keyFor(s1); // "foo" var s2 = Symbol('foo'); Symbol.keyFor(s2); // undefined ``` > Symbol.for()與 Symbol()的區別: > > Symbol.for()會根據傳入的 key 在全域性作用域中註冊一個 Symbol 值,如果某個 key 值從未被註冊到全域性作用域中,便會建立一個 Sybmol 值並根據 key 只註冊到全域性環境中。如果是該 key 已被註冊,就會返回一個與第一次使用所建立的 Symbol 值等價的 Symbol 值。 ```javascript const symbol = Symbol.for('foo'); const obj = {}; obj[symbol] = 'bar'; // ... const anotherSymbol = Symbol.for('foo'); console.log(symbol === anotherSymbol); // true console.log(obj[anotherSymbol]); // bar ``` 這在大型的系統開發中可以用於一些全域性的配置資料中或者用於需要多處使用的資料中。 --- #### **各型別之間的轉換** > String(value)、value.toString()、Boolean(value)、Number(value)、parseInt(value) 可以把對應的 value 轉化為相應的型別。 ##### **轉化為字串** 兩種方式將其他型別可以轉化為字串,不過稍有區別。 - String()方法 - toString() 方法 1.使用 String():將其它物件轉化為字串,可以被認為是一種更加安全的做法,雖然該方法底層使用的也是 toString() 方法,但是針對 null/undefined/symbols,String() 方法會有特殊的處理 ```javascript // Number-->String console.log(String(10)); // '10' console.log(String(0)); // '0' console.log(String(1)); // '1' console.log(String(NaN)); // 'NaN' // Boolean --> String console.log(String(true)); // 'true' console.log(String(false)); // 'false' // Undefined --> String console.log(String(undefined)); // 'undefined' // Null --> String console.log(String(null)); // 'null' // Object-->String console.log(String({a:1,b:"hello"})); // '[object Object]' // String-->Symbol console.log(String(Symbol('foo'));// 'Symbol(foo)' ``` 2.使用 toString():會呼叫該型別下的 toString()方法,但是對於 Undefined、Null 型別的會丟擲異常。對於 Object 型別的會返回該物件的原始字串表示:'[object,Object]',與 JSON.stringify(),返回結果有別。 **Number 使用 toString(),有兩種模式:** - **1.預設模式 toString()** - **2.基模式 toString(radix),只對於 Number 有效** 採用預設模式:直接使用 toString() 方法會輸出的字串型的數字值(無論是整數、浮點數還是科學計數法) ```javascript // 預設模式 var iNum1 = 10, iNum2 = 10.0, iNum3 = 10.12; console.log(iNum1.toString()); //輸出 "10" console.log(iNum2.toString()); //輸出 "10" console.log(iNum3.toString()); //輸出 "10.12" ``` 採用基模式:使用 toString(radix) 可以指定傳入不同的基數 radix 輸出該數字對應基數的字串型,例如二進位制的基是 2,八進位制的基是 8,十進位制的基是 10,十六進位制的基是 16。radix 範圍 2 ~ 36 之間的整數,超出返回丟擲異常, 預設模式是基數為 10 的基模式,預設可以不傳入引數。 ```javascript // 基模式 var iNum = 10; console.log(iNum.toString(2)); //輸出 "1010" console.log(iNum.toString(8)); //輸出 "12" console.log(iNum.toString(10)); //輸出 "10" 等價於:iNum.toString(); console.log(iNum.toString(16)); //輸出 "A" // 基數超出範圍,丟擲異常 console.log(iNum.toString(0)); // Uncaught RangeError: toString() radix argument must be between 2 and 36 console.log(iNum.toString(1)); // Uncaught RangeError: toString() radix argument must be between 2 and 36 console.log(iNum.toString(37)); // Uncaught RangeError: toString() radix argument must be between 2 and 36 // NaN使用基模式指定基數無效,結果返回'NaN' console.log(NaN.toString(2)); //輸出 "NaN" console.log(NaN.toString(8)); //輸出 "NaN" console.log(NaN.toString(10)); //輸出 "NaN" console.log(NaN.toString(16)); //輸出 "NaN" ``` > 對於常量,數字.toString(),數字為整數時,會報錯,因為預設會把後面的.解析成整數的一部分,小數.toString()則正常,我們使用數字..toString()、(數字).toString()則輸出正常,一般使用後者,即可解決該問題; > 對於變數則不會有上述問題。 ```javascript console.log(10.toString()); // Uncaught SyntaxError: Invalid or unexpected token console.log(10.67867.toString()); // '10.67867' 小數.toString(),不報錯 console.log(10.67867..toString()); // Uncaught SyntaxError: Unexpected token '.' console.log(10..toString()); // '10' 雖然同樣可以輸出結果,一般不使用,以免引起不可控異常 // 一般使用(數字).toString()的寫法,避免.解析引起的異常 console.log((10).toString()); // '10' console.log((10.67867).toString()); // '10.67867' ``` **Boolean 使用 toString(),等價於 String()** ```javascript console.log(true.toString()); // 'true' console.log(false.toString()); // 'false' ``` **Null、Undefined 使用 toString(),會丟擲異常,與 String()有區別** ```javascript console.log(null.toString()); // Uncaught TypeError: Cannot read property 'toString' of null console.log(undefined.toString()); // Uncaught TypeError: Cannot read property 'toString' of undefined ``` **Object 使用 toString(),等效於 String()** ```javascript console.log({ a: 1, b: 'hello' }.toString()); // '[object Object]' ``` **Symbol 使用 toString(),等效於 String()** ```javascript console.log(Symbol('foo').toString()); // 'Symbol(foo)' ``` ##### **轉換為 Number** ECMAScript 提供了兩種把非數字的原始值轉換成數字的方法,即 parseInt() 和 parseFloat()。 前者把值轉換成整數,後者把值轉換成浮點數。只有對 String 型別呼叫這些方法,它們才能正確執行;對其他型別返回的都是 NaN。 當然作為 JS 的全域性物件 Number(value),也可以把物件的值轉換為數字。 常用方法: - Number(); - parseInt(); - parseFloat(); Number():把物件的值轉換為數字,對於非數字字串會特殊處理,與後兩種方法有別。 ```javascript // Number(value) console.log(Number('a')); // NaN console.log(Number('10')); // 10 console.log(Number('')); // 0 console.log(Number('0')); // 0 console.log(Number('1')); // 1 console.log(Number(true)); // 1 console.log(Number(false)); // 0 console.log(Number(undefined)); // NaN console.log(Number(null)); // 0 console.log(Number({ a: 1, b: 'hello' })); // NaN console.log(Number(Symbol('foo'))); // Uncaught TypeError: Cannot convert a Symbol value to a number ``` parseInt(string, [radix]):可解析一個字串,並返回一個整數。 > radix 表示要解析的數字的基數。該值介於 2 ~ 36 之間。 > > 傳入該值按照指定的基數解析,為 0 或不傳入會根據 string 來判斷數字的基數 > > 如果它以 “0x” 或 “0X” 開頭,將以 16 為基數。 > > 如果該引數小於 2 或者大於 36,則 parseInt() 將返回 NaN。 > > 舉例,如果 string 以 "0x" 開頭,parseInt() 會把 string 的其餘部分解析為十六進位制的整數。 > > 如果 string 以 0 開頭,那麼 ECMAScript v3 允許 parseInt() 的一個實現把其後的字元解析為八進位制或十六進位制的數字。 > > 如果 string 以 1 ~ 9 的數字開頭,parseInt() 將把它解析為十進位制的整數。 > > 如果字串的第一個字元不能被轉換為數字,那麼 parseFloat() 會返回 NaN。 > > 如果中間出現不能解析為數字的,會停止解析,返回已解析的結果。 > > 首尾出現空格會自動忽略。 > 正常解析為數值: ```javascript console.log(parseInt('0')); // 0 console.log(parseInt('1')); // 1 console.log(parseInt('10')); // 10 console.log(parseInt('010')); // 10 或 8 按照10進位制或者是8進位制解析,結果不定,但是控制檯一般都是輸出10; console.log(parseInt('0xa')); // 10 按照16進位制解析,16^0*10 =10; console.log(parseInt('0Xa')); // 10 按照16進位制解析,16^0*10 =10; console.log(parseInt('0xf')); // 15 按照16進位制解析,16^0*15 =15; console.log(parseInt('0x0f')); // 15 按照16進位制解析,16^0*15 =15; console.log(parseInt('0x1f')); // 31 按照16進位制解析 16^1*1 + 16^0*15 =31; console.log(parseInt('0xef')); // 239 按照16進位制解析 16^1*14 + 16^0*15 =239; ``` > 指定基數正常解析: ```javascript console.log(parseInt('10', 2)); // 2 按照2進位制解析,按照8421碼的速讀,為2; console.log(parseInt('010', 2)); // 2 按照2進位制解析,忽略0; console.log(parseInt('1100', 2)); // 12 按照2進位制解析,按照8421碼的速讀,為8+4=12; console.log(parseInt('11', 2)); // 3 按照2進位制解析,按照8421碼的速讀,為2+1=3; console.log(parseInt('11111', 2)); // 31 按照2進位制解析,2^4*1 + 2^3*1 + 2^2*1 + 2^1*1 + 2^0*1=31; console.log(parseInt('10', 8)); // 8 按照8進位制解析,8^1*1 + 8^0*0 =8; console.log(parseInt('010', 8)); // 8 按照8進位制解析,忽略進位制識別符號0,8^1*1 + 8^0*0 =8; console.log(parseInt('199', 10)); // 199 按照10進位制解析; console.log(parseInt('0199', 10)); // 199 按照10進位制解析,忽略0; console.log(parseInt('a', 16)); // 10 按照16進位制解析,16^0*10 =10; console.log(parseInt('0xa', 16)); // 10 按照16進位制解析,忽略進位制識別符號0x,16^0*10 =10; ``` > 超出基數解析: ```javascript console.log(parseInt('10', 0)); // 10 按照10進位制解析; console.log(parseInt('010', 0)); // 10 按照10進位制解析; console.log(parseInt('10', 1)); // NaN console.log(parseInt('010', 1)); // NaN console.log(parseInt('10', 68)); // NaN console.log(parseInt('010', 68)); // NaN ``` > 特殊值解析: ```javascript console.log(parseInt('a')); // NaN console.log(parseInt('')); // NaN console.log(parseInt(true)); // NaN console.log(parseInt(false)); // NaN console.log(parseInt(undefined)); // NaN console.log(parseInt(null)); // NaN console.log(parseInt({ a: 1, b: 'hello' })); // NaN console.log(parseInt(Symbol('foo'))); // Uncaught TypeError: Cannot convert a Symbol value to a number ``` > 首尾空格解析: ```javascript console.log(parseInt(' 10')); // 10 console.log(parseInt('10 ')); // 10 console.log(parseInt(' 10 ')); // 10 ``` > 遇阻解析: ```javascript console.log(parseInt('a10')); // NaN console.log(parseInt('1a0')); // 1 console.log(parseInt('10a')); // 10 ``` parseFloat():將它的字串引數解析成為浮點數並返回。如果在解析過程中遇到了正負號(+ 或 -)、數字 (0-9)、小數點,或者科學記數法中的指數(e 或 E)以外的字元,則它會忽略該字元以及之後的所有字元,返回當前已經解析到的浮點數。同時引數字串首位的空白符會被忽略。 > 如果引數字串的第一個字元不能被解析成為數字,則 parseFloat 返回 NaN。 ```javascript // parseFloat() console.log(parseFloat('10.322')); // 10.322 console.log(parseFloat('0')); // 0 console.log(parseFloat('0x1')); // 0 console.log(parseFloat('0xf')); // 0 console.log(parseFloat(Math.PI)); // 3.141592653589793 console.log(parseFloat('a')); // NaN console.log(parseFloat('')); // NaN console.log(parseFloat(true)); // 1 console.log(parseFloat(false)); // 0 console.log(parseFloat(undefined)); // NaN console.log(parseFloat(null)); // NaN console.log(parseFloat({ a: 1, b: 'hello' })); // NaN console.log(parseFloat(Symbol('foo'))); // Uncaught TypeError: Cannot convert a Symbol value to a number ``` ##### 轉換為 Boolean 一般使用 JS 全域性物件 Boolean(),可以把指定型別轉換為 Boolean 型。 ```javascript // Boolean(value) console.log(Boolean('a')); // true console.log(Boolean('')); // false console.log(Boolean('10')); // true console.log(Boolean(10)); // true console.log(Boolean(0)); // false console.log(Boolean(1)); // true var a; console.log(Boolean(a)); // false console.log(Boolean(null)); // false console.log(Boolean({ a: 1, b: 'hello' })); // true console.log(Boolean(Symbol('foo'))); // true ``` 同樣的,取反運算子!、!!也可以把特定型別的轉換為 Boolean 型。 ```javascript console.log(!''); // true console.log(!!''); // false console.log(!'a'); // false console.log(!!'a'); // true console.log(!0); // true console.log(!!0); // false console.log(!!1); // true console.log(!1); // false console.log(!undefined); // true console.log(!!undefined); // false console.log(!null); // true console.log(!!null); // false console.log(!{ a: 1, b: 'hello' }); // false console.log(!!{ a: 1, b: 'hello' }); // true console.log(!Symbol('foo')); // false console.log(!!Symbol('foo')); // true ``` ##### **關於各型別之間的隱式轉換** 上述的型別轉換都是我們主動轉換的,屬於顯式轉換,但是在 js 的運算中,當運算子在運算時,如果兩邊資料不統一,CPU 就無法計算,這時我們編譯器會自動將運算子兩邊的資料做一個數據型別轉換,轉成一樣的資料型別再計算,這種無需程式設計師手動轉換,而由編譯器自動轉換的方式就稱為隱式轉換。 常見的隱式轉換: - 字串連線 - 比較運算 - 算術運算 - 特殊表示式 字串連線:連線之前會將非字串轉換為字串,再做連線運算(Symbol 值除外,會丟擲異常); ```javascript console.log('a' + 10); // 'a10' console.log('a' + 0); // 'a0' console.log('a' + 1); // 'a1' console.log('a' + NaN); // 'aNaN' console.log('a' + true); // 'atrue' console.log('a' + false); // 'afalse' console.log('a' + undefined); // 'aundefined' console.log('a' + null); // 'anull' console.log('a' + { a: 1, b: 'foo' }); // 'a[object Object]' console.log('a' + Symbol('foo')); // Uncaught TypeError: Cannot convert a Symbol value to a string ``` 比較運算:'>'、'<' 、'==',會呼叫 Number()方法,轉換數值後再做比較。 '==='不會隱式轉換型別,同時比較數值與其型別。 > 特列: > 字串直接的大小比較:從首位逐個對比其對應的 Unicode 編碼值的大小; > 可以使用 charCodeAt()獲取字串中某個字元的 Unicode 值 > null===null 為 true,任何兩個 null 都是相等的 > NaN===NaN 為 false,任何兩個 NaN 都是不相等的 > > undefined==null 為 true ```javascript console.log('a' > 'b'); // false 比較Unicode值,97>98 console.log('a' < 10); // false console.log('a' < 10); // false console.log('a' == 10); // false console.log('a' === 10); // false console.log('5' > '10'); // true 按照字串比較來判斷 console.log('10' > 10); // fasle console.log('10' < 10); // fasle console.log('10' == 10); // true console.log('10' === 10); // false // 特殊比較 console.log(null === null); // true console.log(undefined === undefined); //true console.log(NaN == NaN); //false console.log(NaN === NaN); //false console.log(undefined == null); // true console.log(undefined === null); // false console.log(NaN == undefined); //false console.log(NaN == null); // false ``` 算術運算:會隱式的使用 Number()轉換為數值型後,再做運算。 > 特例: > 非數字的字串與數值型數字相加,不會使用此規則,會使用字串連線規則; > Object 與數字相加,不會使用此規則,會使用字串連線規則; > 陣列與數字相加,不會使用此規則,會使用字串連線規則; > Symbol 值參與算術運算,會丟擲異常。 ```javascript // 加運算 + console.log('a' + 10); // 'a10' 特例,直接使用字串連線原則 console.log('10' + 10); // 20 10 + 10 console.log(true + 10); // 11 1 + 10 console.log(false + 10); // 10 0 + 10 console.log(undefined + 10); // NaN NaN + 10 console.log(null + 10); // 10 0 + 10 console.log({ a: 1, b: 'hello' } + 10); // '[object Object]10' 特例:使用了字串連線原則 console.log(['a', 'b'] + 10); // 'a,b10' 特例:使用了字串連線原則 console.log(Symbol('foo') + 10); // Uncaught TypeError: Cannot convert a Symbol value to a number // 減運算 - console.log('a' - 10); // NaN console.log('10' - 10); // 0 10 - 10 console.log(true - 10); // -9 1 - 10 console.log(false - 10); // -10 0 - 10 var a; console.log(a - 10); // NaN NaN - 10 console.log(null - 10); // -10 0 - 10 console.log({ a: 1, b: 'hello' } - 10); // NaN console.log(['a', 'b'] - 10); // NaN console.log(Symbol('foo') - 10); // Uncaught TypeError: Cannot convert a Symbol value to a number // 除運算子 / console.log('a' / 10); // NaN console.log('10' / 10); // 1 console.log(true / 10); // 0.1 console.log(false / 10); // 0 console.log(undefined / 10); // NaN console.log(null / 10); // 0 console.log({ a: 1, b: 'hello' } / 10); // NaN console.log(['a', 'b'] / 10); // NaN console.log(Symbol('foo') / 10); // Uncaught TypeError: Cannot convert a Symbol value to a number // 取餘運算子 % console.log('a' % 10); // NaN console.log('10' % 10); // 0 console.log(true % 10); // 1 console.log(false % 10); // 0 console.log(undefined % 10); // NaN console.log(null % 10); // 0 console.log({ a: 1, b: 'hello' } % 10); // NaN console.log(['a', 'b'] % 10); // NaN console.log(Symbol('foo') % 10); // Uncaught TypeError: Cannot convert a Symbol value to a number // += -= /= %= 也是在上述規則上,做運算 ``` 特殊表示式: +字串、-字串、會把當前字串轉換為 Number,等價於 Number(字串),然後根據+/-對應取正負。 ```javascript console.log(); console.log(+'a'); // NaN console.log(+'10'); // 10 console.log(+true); // 1 console.log(+false); // 0 console.log(+undefined); // NaN console.log(+null); // 0 console.log(+{ a: 1, b: 'hello' }); // NaN console.log(+['a', 'b']); // NaN console.log(+Symbol('foo')); // Uncaught TypeError: Cannot convert a Symbol value to a number console.log(-'a'); // NaN console.log(-'10'); // -10 console.log(-true); // -1 console.log(-false); // -0 與0等價:-false===0 為true console.log(-undefined); // NaN console.log(-null); // -0 與0等價: -null===0 為true console.log(-{ a: 1, b: 'hello' }); // NaN console.log(-['a', 'b']); // NaN console.log(-Symbol('foo')); // Uncaught TypeError: Cannot convert a Symbol value to a number ``` **總結:** 1. JavaScript 主要有 7 種原始資料型別: 2. 3 種基本資料型別:String、Number、Boolean,屬於值型別 3. 2 種特殊資料型別:Undefined、Null; 4. 1 種引用資料型別:Object; 5. 1 種 ES6 新增資料型別:Symbol。 6. Symbol 應用例項:消除魔術字串 7. 物件中屬性包含 Symbol 的遍歷 8. Reflect.keys()遍歷物件屬性,不會受 enumerable 影響 9. Symbol 的一個重要方法:Symbol.for(); 10. 各型別之間的轉換 1. 轉化為字串 2. 轉換為 Number 3. 轉換為 Boolean 4. 關於各型別之間的隱式轉換 上述彙總了以上列表內容,資訊較多,如有紕漏,歡迎指點。 微信公眾號:前端開發那些事兒,歡迎關注! ![前端開發那些事兒](https://user-gold-cdn.xitu.io/2020/3/29/1712491e8008d375?w=900&h=500&f=gif&s=