1. 程式人生 > 實用技巧 >Javascript高階之資料型別

Javascript高階之資料型別

資料型別

typeof操作符

  • 對一個值使用typeof操作符會返回下列字串之一
    • undefined 表示值已定義,未賦值
    • boolean 表示值為布林值
    • string 表示值為字串
    • number 表示值為數值
    • object 表示值為物件(不是函式)或null
    • function 表示值為函式
    • symbol 表示值為符號
let message= 'some string';
console.log(typeof message);    // string
console.log(typeof(message));    // string
console.log(typeof(null));    // object

資料型別檢測

  • 資料型別的檢測方法
    • Object.prototype.toString.call() 全型別檢測
    • instanceof Array[,Function,Date,RegExp,Object] 只能檢測物件型別
    • typeof 只能檢測除null外的值型別
      • 常用於(typeof value == "string") ? "'" + value + "'" : value;
    • Array.isArray() 僅只檢測Array型別
    • isFinite() 用於檢測數值是否在安全範圍之內
    • isNaN() 用於檢測資料是否為空
    • valueOf() 用於返回物件的原始數值
let value= '9';
console.log((typeof value == "string") ? "'" + value + "'" : value);

資料型別的初始化

  • 定義一個空物件
let obj= null;
console.log(typeof obj);
  • 定義一個空函式
let fun= Function.prototype;
console.log(typeof fun);
  • 定義一個空陣列
let arr= [];
console.log(Array.isArray(arr));
  • 定義一個空字串
let str= '';
console.log(typeof str);
  • 定義一個空符號
let sym= Symbol();
console.log(typeof sym);

強制型別轉換

  • 強制型別轉換
    • 任意型別 --> 字串
      • x.toString()
        - x不能是undefined或null
      • String(x) - 相當於隱式轉換
    • 任意型別 --> 數字
      • Number(x) - 專用於將非字串型別轉數字,相當於隱式轉換
      • parseInt(str) - 將字串轉為整型數字,不認識小數點,碰上第一個非數字字元就停止,自動跳過開頭的空字元
      • parseFloat(str) - 將字串轉為浮點型數字,認識第一個小數點
    • 任意型別 --> Boolean
      • Boolean(x) - 相當於隱式轉換
      • 只有5個值轉為false - "" NaN undefined null 0,其餘都轉為true
    • 快速轉換
      • 任意型別轉String - x + ""
      • 任意型別轉Boolean - !!x
      • 任意型別轉Number - 除”+”以外的其他運算
    // Number(x)  VS  parseInt(str)
    let b = true;
    console.log(Number(b)); 			     //1
    console.log(parseInt(String(b)));	 //NaN

	let width="60px";
	console.log(Number(width)); 		     //NaN
    console.log(parseInt(width)); 		 //60
  // 十進位制轉十六進位制
  let num1 = 125;
  console.log(num1.toString(16));  //輸出7d
  console.log(num1.toString(8));  // 輸出175
  // 十六進位制轉十進位制
  let num2 = '7d';
  console.log(parseInt(num2,16));  //輸出125
  // 十進位制轉十六進位制
  let array = [170, 15, 19, 0, 0, 0, 0, 0, 0, 2, 1, 1, 0, 0, 0, 218];
	let newArray = array.map(item =>{
		return item.toString(16);
	});
	console.log(newArray);
			
	// 十六進位制轉十進位制
	let arr1 = ["aa", "f", "13", "0", "0", "0", "0", "0", "0", "2", "1", "1", "0", "0", "0", "da"];
	let arr2 = arr1.map(item =>{
		return parseInt(item,16);
	})
	console.log(arr2);

轉換成字串型別

  • toString()
let num = 5;
console.log(num.toString());
  • String()
    • String()函式存在的意義:有些值沒有toString()
    • 這個時候可以使用String()。比如:undefined和null
let s = null;
console.log(s.toString());
console.log(String(s));
  • 拼接字串方式
    • num + ""
    • 當+兩邊一個操作符是字串型別,一個操作符是其它型別的時候
    • 會先把其它型別轉換成字串再進行字串拼接,返回字串

轉換成數值型別

  • Number()
    • Number()可以把任意值轉換成數值,如果要轉換的字串中有一個不是數值的字元,返回NaN
let a = Number('1');
let b = Number(1);
let c = Number('c');
let d = Number(null);
let e = Number(undefined);

console.log(a,b,c,d,e); // 1 1 NaN 0 NaN
  • parseInt()
    • 如果第一個字元是數字會解析,直到遇到非數字結束
    • 如果第一個字元不是數字或者符號就返回NaN
let a = parseInt('1.2df');
let b = parseInt(1);
let c = parseInt('c12');
let d = parseInt(null);
let e = parseInt(undefined);

console.log(a,b,c,d,e); //1 1 NaN NaN NaN
  • parseFloat()
    • parseFloat() 把字串轉換成浮點數
    • parseFloat()和parseInt非常相似
    • 不同之處在與parseFloat會解析第一個 . 遇到第二個.或者非數字結束
    • 如果解析的內容裡只有整數,解析成整數
let a = parseFloat('1.2df');
let b = parseFloat('1.3.4');
let c = parseFloat('c12');
let d = parseFloat(null);
let e = parseFloat(undefined);

console.log(a,b,c,d,e); //1.2 1.3 NaN NaN NaN
  • + - -0 等運算
let str = '500';
console.log(+str);		// 取正
console.log(-str);		// 取負
console.log(str - 0);   

轉換成布林型別

  • Boolean()
    • 0、''(空字串) 、null、 undefined 、NaN 會轉換成false 其它都會轉換成true
let a = Boolean('0');
let b = Boolean(0);
let c = Boolean('1');
let d = Boolean(null);
let e = Boolean(undefined);
let f = Boolean(NaN);

console.log(a,b,c,d,e,f); //true false true false false false

undefined型別

  • 概念
    • undefined型別只有一個值,就是特殊值undefined
    • 對於未宣告的變數,只能執行一個有用的操作,就是對它呼叫typeof
    • 永遠不要顯式的將變數值設定為undefined
let message;
console.log(message);              // undefined
console.log(age);                  // 報錯

console.log(typeof message);       // undefined
console.log(typeof age);           // undefined

if(message){
    console.log('1號程式執行了');   // 不執行
}

if(!message){
    console.log('2號程式執行了');   // 執行
}

if(age){
    console.log('3號程式執行了');   // 報錯
}

Null型別

  • 概念
    • null型別只有一個值,就是特殊值null
    • 邏輯上來講,null值表示一個空物件指標
    • 在定義將來要儲存的物件值變數時,建議使用null來初始化
    • undefined值是由null值派生而來的
console.log(undefined==null);   // true

let message= null;
if(message){
    console.log('1號程式執行了');   // 不執行
}
if(!message){
    console.log('2號程式執行了');   // 執行
}

Boolean型別

  • 概念

    • 布林值字面量區分大小寫
    • 其他型別的值可以轉化為布林值
  • 不同型別與布林值之間的轉換規則

    • 轉換為true的值
      • 非空字串
      • 非零數值(包括無窮值)
      • 任意物件
      • N/A(不存在)
    • 轉化為false的值
      • ""(空字串)
      • 0、NaN
      • null
      • undefined
let message='你好,中國!';
if(message){
  console.log(message);
}

Number型別

  • 概念
    • 使用八進位制和十六進位制格式建立的數值在所有的數學操作中都被視為十進位制數值
    • 八進位制
      • 字首新增0
      • 若字面量超出範圍,則當成十進位制數
    • 十六進位制
      • 字首新增0x(區分大小寫)
      • 十六進位制數字中的字母不區分大小寫
      • 正零和負零在所有情況下都認定為等同的

浮點數

  • 概念
    • 對於非常大或非常小的數值,浮點數可以用科學計數法來表示
    • 浮點值的精確度最高可達17位小數,但在算術計算中遠不如整數精確
let num1= 3.14e5;
console.log(num1);      // 314000
console.log(0.1+0.2);   // 0.30000000000000004

值得範圍

  • 概念
    • JS的下限值儲存在 Number.MIN_VALUE
    • JS的上限值儲存在 Number.MAX_VALUE
    • 超出範圍,返回Infinity
    • 檢測資料是否在範圍之內 isFinite()
// JS最小數值
console.log(Number.MIN_VALUE);

// JS最大數值
console.log(Number.MAX_VALUE);

console.log(Number.MAX_VALUE*2);                // Infinity

console.log(isFinite(Number.MAX_VALUE));        // true
console.log(isFinite(Number.MAX_VALUE*2));      // false

NaN

  • 概念
    • NaN表示不是數值
    • 用於表示本來要返回數值的操作失敗了
    • NaN不等於任何值,包括自己
    • 檢測資料是否為空 isNaN()
console.log(0/0);           // NaN
console.log(5/0);           // Infinity
console.log(NaN == NaN);    // false

console.log(isNaN(NaN));    // true
console.log(isNaN(10));     // false
console.log(isNaN('10'));   // false
console.log(isNaN('blue')); // true
console.log(isNaN(true));   // false

String型別

  • 概念
    • 字串可以用雙引號(")單引號(')反引號(`)標示

字元字面量

  • 字面量
    • \n 換行
    • \t 製表
    • \b 退格
    • \r 回車
    • \f 換頁
    • \\ 反斜槓
    • \' 單引號
    • \" 雙引號
    • ``` 反引號
    • \xnn 以十六進位制編碼nn表示的字元
    • \unnnn 以十六進位制編碼nnnn表示的Unicode字元

字串的特點

  • 字串是不可變的

模板字面量

let strTemp1= '你好,中國!\n我的祖國!';
console.log(strTemp1);

let strTemp2= `你好,中國!
我的祖國!`;
console.log(strTemp2);

字串插值

  • 概念

    • 模板字面量最常用的一個特性是支援字串插值
    • 字元換插值通過在${}中使用一個Javascript表示式實現
    • 所有插入的值都會使用toString()強制轉型為字串
    • 任何Javascript表示式都可以用於插值
  • 巢狀的模板字串無須轉義

console.log(`Hello,${`world`}!`);
  • 將表示式轉換為字串時會呼叫toString()
let foo= {toString: ()=>'world'};
console.log(`Hello,${foo}!`);
  • 在插值表示式中可以呼叫函式和方法
function capitalize(word){
  return `${word[0].toUpperCase()}${word.slice(1)}`;
}
console.log(`${capitalize('hello')},${capitalize('world')}!`);
  • 模板可以插入自己之前的值
let value= '';
function append(){
  value= `${value}哈哈!`;
  console.log(value);
}
append();
append();
append();

模板字面量標籤函式

  • 概念
    • 標籤函式會接收被插值記號分隔後的模板和對每個表示式求值的結果
    • 因為表示式引數的數量是可變的,所以通常應該使用剩餘操作符將它們收集到一個數組中
let a= 18;
let b= 27;
function simpleTag(strings, a, b, sum){
  console.log(strings);
  console.log(a);
  console.log(b);
  console.log(sum);

  return 'over';
}

let res= `${a}+${b}=${a+b}`;
let resTag= simpleTag`${a}+${b}=${a+b}`;

console.log(res);
console.log(resTag);
let a= 18;
let b= 27;
function simpleTag(strings, ...exps){
  console.log(strings);
  console.log(exps);
  let arr= exps.map(item=>{
    return item;
  })
  console.log(arr);
  for(exp of exps){
    console.log(exp);
  }
  return 'over';
}

let resTag= simpleTag`${a}+${b}=${a+b}`;
console.log(resTag);
let a= 18;
let b= 27;
function simpleTag(strings, ...exps){
  return strings[0]+ exps.map((e, i)=>`${e}${strings[i+1]}`).join('');
}
let resTag= simpleTag`${a}+${b}=${a+b}`;
console.log(resTag);

原始字串

  • 概念
    • 使用模板字面量可以直接獲取原始的模板字面量內容
    • 可以通過標籤函式的第一個引數,即字串陣列的.raw屬性取得每個字串的原始內容
console.log(`\u00A9`);
console.log(String.raw`\u00A9`);

function printRaw(strings){
  console.log('模板字面量:');
  for(const string of strings){
    console.log(string);
  }

  console.log('原始字面量:');
  for(const stringRaw of strings.raw){
    console.log(stringRaw);
  }
}
printRaw`\u00A9${'and'}\n`;

Symbol型別

  • 概念
    • 符號是原始值,且符號例項是唯一、不可變的
    • 符號的用途是確保物件屬性使用唯一識別符號,不會發生屬性衝突的危險

符號的基本用法

  • 概念
    • 符號需要使用Symbol()函式初始化
    • Symbol()函式不能與new關鍵字一起作為建構函式使用
let sym= Symbol();
console.log(typeof sym);

let genericSymbol= Symbol();
let otherGenericSymbol= Symbol();
console.log(genericSymbol== otherGenericSymbol);    // false

let fooSymbol= Symbol('foo');
let otherFooSymbol= Symbol('foo');
console.log(fooSymbol== otherFooSymbol);            // false

使用全域性符號登錄檔

  • 概念

    • 如果執行時的不同部分需要共享和重用符號例項
    • 可以用一個字串作為鍵,在全域性符號登錄檔中建立並重用符號
  • Symbol.for()方法

    • 對每個字串鍵都執行冪等操作
    • 全域性登錄檔中的符號必須使用字串鍵來建立
    • 登錄檔中使用的鍵同時也會被用作符號描述
let fooGlobalSymbol= Symbol.for('foo');
let otherGlobalFooSymbol= Symbol.for('foo');
console.log(fooGlobalSymbol=== otherGlobalFooSymbol);  // true
let emptyGlobalSymbol= Symbol.for();
console.log(emptyGlobalSymbol);   // Symbol(undefined)
  • Symbol.keyFor()方法
    • 查詢全域性登錄檔
    • 接受符號,返回該全域性符號對應的字串鍵
    • 如果查詢的不是全域性符號,則返回undefined
let s= Symbol.for('foo');
console.log(Symbol.keyFor(s));  // foo
let s= Symbol('foo');
console.log(Symbol.keyFor(s));  // undefined

使用符號作為屬性

  • 概念
    • 凡是可以使用字串或數值作為屬性的地方,都可以使用符號
    • 包括物件字面量屬性和Object.defineProperty() Object.defineProperties()定義的屬性
    • 物件字面量只能在計算屬性語法中使用符號作為屬性
let s1= Symbol('Sunny'),
    s2= Symbol('Jerry'),
    s3= Symbol('Marry'),
    s4= Symbol('Tommy');

let o= {
  [s1]: 'programmer'
};
console.log(o);     // {Symbol(Sunny): "programmer"}

Object.defineProperty(o, s2, {value: 'Photographer'});
console.log(o);

Object.defineProperties(o, {
  [s3]: {value: 'Designer'},
  [s4]: {value: 'Operation'}
});
console.log(o);

常用內建符號

  • 概念
    • ES6引入了一批常用內建符號,用於暴露語言的內部行為
    • 開發者可以直接訪問、重寫或模擬這些行為
    • 這些內建符號都以Symbol工廠函式字串屬性的形式存在
    • 所有內建符號屬性都是不可寫、不可列舉、不可配置的
    • 在ES規範中,經常引用符號在規範中的名稱,字首為@@
      • @@iterator指的就是Symbol.iterator

Symbol.asyncIterator

  • 概念
    • 這個符號作為一個屬性表示
      • 一個方法,該方法返回物件預設的AsyncIterator
      • for-await-of語句使用
    • 這個符號表示實現非同步迭代器API的函式
      • 返回的物件是實現該API的AsyncGenerator
class Foo{
  async *[Symbol.asyncIterator](){}
}
let f= new Foo();
console.log(f[Symbol.asyncIterator]()); // AsyncGenerator{<suspended>}
class Emitter{
  constructor(max){
    this.max= max;
    this.asyncIdx= 0;
  }

  async *[Symbol.asyncIterator](){
    while(this.asyncIdx < this.max){
      yield new Promise((resolve)=>resolve(this.asyncIdx++));
    }
  }
}
async function asyncCount(){
  let emitter= new Emitter(5);
  for await(const x of emitter){
    console.log(x);
  }
}
asyncCount();

Object型別

  • 概念

    • 物件就是一組資料和功能的集合
  • 每個Object例項都有如下的屬性和方法

    • constructor 用於建立當前物件的函式
    • hasOwnProperty(propertyName) 用於判斷當前物件例項(不是原型)上是否存在給定的屬性
    • isPrototypeOf(object) 用於判斷當前物件是否為另一個物件的原型
    • propertyIsEnumerable(propertyName) 用於判斷給定的屬性是否可以使用for-in語句列舉
    • toLocaleString() 返回物件的字串表示,該字串反映物件所在的本地化執行環境
    • toString() 返回物件的字串表示
    • valueOf() 返回物件對應的字串、數值或布林值表示,通常與toString()的返回值相同