「面試指南」解讀JavaScript原始數據類型

JavaScript 有 7 種原始數據類型:

  • String(字符型)
  • Number(數值型)
  • Boolean(布爾值型)
  • Undefined
  • Null
  • Object(對象型)
  • Symbol(符號型,ES6 中新增數據類型)

一般使用 typeof 檢測其類型(Null,Array,Object 除外),返回其數據類型值的字符串;javascript

String
字符型:由一系列字符組成的字符串,屬於值類型。前端

Number
數值型:用來表示數值,屬於值類型,能夠用於算術邏輯計算。java

Boolean
布爾值型:用表示邏輯是,邏輯非:true,false,屬於值類型。程序員

Undefined
未定義:定義未賦值,或未定義(直接使用會引發異常),特殊的數據類原始數據型。es6

Null
空:表明一個空指針,特殊的一種原始數據類型。數組

Object
對象型:以 key-value 的方式表明屬性名與屬性值,由{}組成,不一樣的屬性以,隔開,JS 中一種重要的引用型原始數據類型。安全

Symbol
符號型:ES6 新增的一種原始數據類型,主要用於解決屬性名相同一塊兒的衝突問題,每一個 Symbol()值都不相同。微信


3 種基本數據類型:String、Number、Boolean,屬於值類型

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;app

d; // 報錯:ReferenceError: d is not defined
console.log(d); // 報錯:ReferenceError: d is not defined
console.log(typeof dd); // 不報錯,結果:undefined

2).已定義,可是未賦值,結果:undefined。函數

var e;
console.log(e); // 結果不報錯:undefined
console.log('e ' + typeof e); // 結果:e undefined

Null:

歷史緣由設計缺陷,最初未設計此類型,看成 object 的一種特殊值。後來做爲一種單獨的數據類型獨立出來,爲了兼容之前的代碼,typeof null 返回 object 就無法改變了。

var f = null;
console.log('f ' + typeof f); // 結果: f object

Null 類型的檢測:

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) 來定義。屬性之間由,分隔:

var city = { id: 1, name: '北京', value: 'BeiJing' };

有多種定義/尋址/修改方式:

// 直接定義賦值
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。

// 小小說明
// 使用如下方法能夠精確配置對象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// 可枚舉
});

屬性有兩種尋址方式:

// 點方法
var cityName = city.name;
console.log(cityName); // 北京

// 或者方括號方法
var cityName = city[name];
console.log(cityName); // 北京

對象可使用 for-in、Object.keys()來枚舉其屬性

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 種新的原始數據類型。

var s = Symbol();
console.log('s ', s, typeof s); // s  Symbol()  'symbol'

不可使用 new 來定義一個 Symbol();由於 Symbol()函數生成的是一個原始數據類型的值類型而非引用類型,不是對象,因此不能添加屬性。基本上,它是一種相似於字符串/數值的數據類型;

var s1 = Symbol();
var s2 = Symbol();
console.log(s1 === s2); // false

Symbol 函數能夠接受一個字符串做爲參數,表示對 Symbol 實例的描述,主要是爲了在控制檯顯示,或者轉爲字符串時,比較容易區分。相同描述的兩個 Symbol,返回值依然是不相等的,一樣是兩個不一樣的 Symbol;

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 值能夠顯式轉爲字符串。

var symb = Symbol('a symbol');

String(symb); // 'Symbol(a symbol)'
symb.toString(); // 'Symbol(a symbol)'

另外,Symbol 值也能夠轉爲布爾值,可是不能轉爲數值。

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 的設計初衷之一。

var mySymbol = Symbol();

// 第一種寫法
var a = {};
a[mySymbol] = 'Hello!';

// 第二種寫法
var a = {
  [mySymbol]: 'Hello!'
};

// 第三種寫法
var a = {};
Object.defineProperty(a, mySymbol, { value: 'Hello!' });

// 以上寫法都獲得一樣結果
a[mySymbol]; // "Hello!"

注意,Symbol 值做爲對象屬性名時,不能用點運算符。

var mySymbol = Symbol();
var a = {};

a.mySymbol = 'Hello!';
a[mySymbol]; // undefined
a['mySymbol']; // "Hello!"

在對象的內部,使用 Symbol 值定義屬性時,Symbol 值必須放在方括號之中。

let s = Symbol();

let obj = {
  [s]: function(arg) {
    // do something
  }
};

// 爲了更簡潔,也能夠採用加強的對象寫法
let obj = {
  [s](arg) {
    // do something
  }
};

obj[s](123);

Symbol 的特性使得用來定義一組常量具備很好的優點,從而保證常量的值都是不同的。


Symbol 應用實例:消除魔術字符串

魔術字符串:在代碼之中屢次出現、與代碼造成強耦合的某一個具體的字符串或者數值。風格良好的代碼,應該儘可能消除魔術字符串,改由含義清晰的變量代替。

其中魔術字符串在 switch 條件語句中有大量的體現:

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' 代碼塊中兩次出現,出現了魔術字符串,不利於修改與維護;

通常是把常量變成變量,便可消除魔術字符串,故上述通常做以下處理:

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 能夠以可靠的按照設計的方式執行,以上作以下修改便可。

var shapeType = {
-  triangle: 'Triangle'
+  triangle: Symbol()
  };

對象中屬性包含 Symbol 的遍歷

當 Symbol 做爲對象屬性名時,對象的遍歷就發生了一些小的變化,該屬性就不會再 for-in 、for-of,循環中出現,也不會在 Object.keys()、Object.getOwnProperNames()、JSON.stringify()中做爲屬性返回了。

不過,它也不是私有屬性,有一個 Object.getOwnPropertySymbols 方法,能夠獲取指定對象的全部 Symbol 屬性名。

Object.getOwnPropertySymbols 方法返回一個數組,成員是當前對象的全部用做屬性名的 Symbol 值。

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 屬性);

// 咱們對上述的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。

// 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 值。

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() 方法會有特殊的處理

// 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() 方法會輸出的字符串型的數字值(不管是整數、浮點數仍是科學計數法)

// 默認模式
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 的基模式,默承認以不傳入參數。

// 基模式
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()則輸出正常,通常使用後者,便可解決該問題;
對於變量則不會有上述問題。

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()

console.log(true.toString()); // 'true'
console.log(false.toString()); // 'false'

Null、Undefined 使用 toString(),會拋出異常,與 String()有區別

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()

console.log({ a: 1, b: 'hello' }.toString()); // '[object Object]'

Symbol 使用 toString(),等效於 String()

console.log(Symbol('foo').toString()); // 'Symbol(foo)'
轉換爲 Number

ECMAScript 提供了兩種把非數字的原始值轉換成數字的方法,即 parseInt() 和 parseFloat()。

前者把值轉換成整數,後者把值轉換成浮點數。只有對 String 類型調用這些方法,它們才能正確運行;對其餘類型返回的都是 NaN。

固然做爲 JS 的全局對象 Number(value),也能夠把對象的值轉換爲數字。

經常使用方法:

  • Number();
  • parseInt();
  • parseFloat();

Number():把對象的值轉換爲數字,對於非數字字符串會特殊處理,與後兩種方法有別。

// 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。

若是中間出現不能解析爲數字的,會中止解析,返回已解析的結果。

首尾出現空格會自動忽略。

正常解析爲數值:

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;

指定基數正常解析:

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;

超出基數解析:

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

特殊值解析:

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

首尾空格解析:

console.log(parseInt(' 10')); // 10
console.log(parseInt('10 ')); // 10
console.log(parseInt(' 10 ')); // 10

遇阻解析:

console.log(parseInt('a10')); // NaN
console.log(parseInt('1a0')); // 1
console.log(parseInt('10a')); // 10

parseFloat():將它的字符串參數解析成爲浮點數並返回。若是在解析過程當中遇到了正負號(+ 或 -)、數字 (0-9)、小數點,或者科學記數法中的指數(e 或 E)之外的字符,則它會忽略該字符以及以後的全部字符,返回當前已經解析到的浮點數。同時參數字符串首位的空白符會被忽略。

若是參數字符串的第一個字符不能被解析成爲數字,則 parseFloat 返回 NaN。

// 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 型。

// 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 型。

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 值除外,會拋出異常);

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

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 值參與算術運算,會拋出異常。

// 加運算 +
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(字符串),而後根據+/-對應取正負。

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. 關於各種型之間的隱式轉換

上述彙總了以上列表內容,信息較多,若有紕漏,歡迎指點。

微信公衆號:前端開發那些事兒,歡迎關注!

前端開發那些事兒

相關文章
相關標籤/搜索