js 數(shù)據(jù)類型
js中有六種原始類型:
- number
- boolean
- null
- undefined
- string
- symbol
一種引用類型 - object
引用類型和基本類型有什么區(qū)別,以及深淺拷貝之類就暫時不表了。
注意點:
- undefined和not defined是不同的 。undefined代表的是定義了但是沒有賦值,not defined表示沒有定義。但是typeof 都返回undefined。
- js中的string是不可變的,他的成員函數(shù)不會改變原值,而是創(chuàng)建并返回一個新的字符串。
c = a.toUpperCase();
a === c // false
1 typeof
typeof false // boolean
typeof null // object
typeof undefined // undefined
typeof 2 // number
typeof NaN // number
typeof Infinity // number
typeof 'kimi' // string
typeof Symbol('kimi') // symbol
typeof Date // funtion
typeof [2,2,3] // object
typeof {a:1} // object
- 使用typeof檢測基本類型基本可以正確的判斷出類型的,但是判斷引用類型時就沒有那么準確了。因為數(shù)組、對象、函數(shù)實際上都是引用類型。但function對象和普通的對象相比,其內(nèi)部有一個[[call]]方法,用來表示這個對象是可調(diào)用的。所以返回時function。實際函數(shù)就是一個可調(diào)用對象。如果要準確的判斷各個引用類型就需要使用instanceof。
- null返回object。這是js的一個bug,js最初版本使用的是32位系統(tǒng),為了性能考慮用低位存儲變量的類型信息,000開頭表示對象,然后null為全零。所以返回object。現(xiàn)在內(nèi)部類型判斷代碼已經(jīng)改變。但是這個bug仍然在。所以null其實算做基本類型
null instanceof object // false
2 字面量、String()、new String()
var abc = 'abc',
str1 = String(abc),
str2 = new String(abc);
//判斷下面輸出
str1 === abc //true
str2 === abc //false
typeof str1 // string
typeof str2 // object
String() 和 字面量的方式返回的都是基本類型,String() 用來做類型轉換
而new String()返回object
Q:為什么 'kimi'基本類型可以去調(diào)用length或者toString方法和屬性呢?
在這里,只要引用了字符串'kimi'的屬性或方法,JavaScript就會將字符串值通過調(diào)用new String(s)的方式轉換成對象,這個對象繼承了字符串的方法,并用來處理屬性的作用。一旦屬性引用結束,這個新創(chuàng)建的對象就會銷毀(其實在實現(xiàn)上并不一定創(chuàng)建和銷毀這個臨時對象,然而整個過程看起來就是這樣的)
類似代碼:
var a1 = new String("kimi");
var a2 = a1.substring(0);
a1 = null;
console.log(a2); // kimi
存取字符串,數(shù)字或布爾值的屬性時創(chuàng)建的臨時對象稱作包裝對象
同字符串一樣,數(shù)字和布爾值也具有各自的方法:通過Number和Boolean構造函數(shù)創(chuàng)建一個臨時對象,這些方法的調(diào)用均是來自這個臨時對象.null和undefined沒有包裝對象:訪問它們的屬性會造成一個錯誤。
var s = “test”;
s.len = 4;
console.log(s.len); // undefined
// 由于包裝對象使用完畢會自己銷毀,所以添加的屬性也讀取不到。
Q:為什么2.toString()會報錯?
在這里的 . 它既可以理解為小數(shù)點,也可以理解為對方法的調(diào)用,按照規(guī)范,解釋器把它判斷為一個小數(shù)點。 2.toString() 在解釋器看來其實是:(2.)toString();
如果想解決可以使用如下方式都可以正確輸出
2..toString()
(2).toString();
2 .toString(); //加個空格
2.0.toString();
3 準確類型判斷方法
1. instanceof 配合 typeof
const Person = function() {}
const p1 = new Person()
p1 instanceof Person
let str = 'hello'
str instanceof String // false
str.__proto__ // String
// str字面量有__proto__屬性是因為轉換成包裝對象。但是本身是個基本類型,使用不了instanceof
let str1 = new String('hello')
str1 instanceof String // true
綜上如果是基本類型可以使用typeof,引用類型使用instanceof
2. Object.prototype.toString.call()
Object.prototype.toString.call(2) // [object Number]
Object.prototype.toString.call('kimi') // [object String]
Object.prototype.toString.call(true) // [object Boolean]
Object.prototype.toString.call(Symbol('kimi')) // [object Symbol]
Object.prototype.toString.call(new Date()) // [object Date]
Object.prototype.toString.call(new RegExp()) // [object RegExp]
Object.prototype.toString.call()、toString()、 toString.call()
所有的對象最后都會繼承Object.prototype.toString這個方法。而在每個子類中都會改寫這個方法。Array、Function的原型上都已經(jīng)改寫了這個方法。每個對象上調(diào)用toString方法時會先調(diào)用自身的toString方法,如果找不到會沿著原型鏈往上找,如果一直沒找到最終會找到Object.prototype.toString這個方法
具體看下每個對象調(diào)用toString方法的結果:
1 對象 object
var obj = {a: 1};
obj.toString(); // "[object Object]"
Object.prototype.toString.call(obj); // "[object Object]"
Object.prototype.toString() 在toString方法被調(diào)用時,會執(zhí)行下面的操作步驟
- 獲取this對象的[[Class]]屬性的值。
- 計算出三個字符串"[object ", 第一步的操作結果Result(1), 以及 "]"連接后的新字符串。
- 返回第二步的操作結果Result(2)。
[[Class]]是一個內(nèi)部屬性,所有的對象(原生對象和宿主對象)都擁有該屬性.在規(guī)范中,[[Class]]是這么定義的: 內(nèi)部屬性 描述 [[Class]] 一個字符串值,表明了該對象的類型。其過程簡單說來就是:1、獲取對象的類名(對象類型)。2、然后將[object、獲取的類名、]組合并返回。
那么同時我們可以想是否任何對象object都可以通過this綁定調(diào)用Object.prototype.toString()方法,這樣可以準確的檢測出類型
Object.prototype.toString.call({}); // [object Object]
Object.prototype.toString.call([]); // [object Array]
Object.prototype.toString.call(function(){}); // [object Function]
Object.prototype.toString.call(''); // [object String]
Object.prototype.toString.call(1); // [object Number]
Object.prototype.toString.call(true); // [object Boolean]
Object.prototype.toString.call(null); // [object Null]
Object.prototype.toString.call(undefined); // [object Undefined]
// 相當于Object.prototype.toString.call(undefined);
Object.prototype.toString.call(); // [object Undefined]
Object.prototype.toString.call(new Date()); // [object Date]
Object.prototype.toString.call(/at/); // [object RegExp]
// toString() 方法能識別以下類型是因為引擎為它們設置好了 toStringTag 標簽
Object.prototype.toString.call(new Map()); // "[object Map]"
Object.prototype.toString.call(function* () {}); // "[object GeneratorFunction]"
Object.prototype.toString.call(Promise.resolve()); // "[object Promise]"
// 自己創(chuàng)建的類不能識別,toString() 找不到 toStringTag 屬性時只好返回默認的 Object 標簽
class ValidatorClass {}
Object.prototype.toString.call(new ValidatorClass()); // "[object Object]"
// 可以加上 toStringTag 屬性讓他識別
class ValidatorClass {
get [Symbol.toStringTag]() {
return "Validator";
}
}
Object.prototype.toString.call(new ValidatorClass()); // "[object Validator]"
因為Object是所有子類的父類,所以任何類型的對象object都可以通過this綁定調(diào)用Object.prototype.toString()方法,返回該對象的字符串表示
2 數(shù)組 array
toString():返回由數(shù)組中每個值的字符串形式拼接而成的一個以逗號分隔的字符串
// 如果是多維數(shù)組會遞歸調(diào)用toString()方法
var array = [1, 's', true, {a: 2}];
array.toString();//"1,s,true,[object Object]"
Array.prototype.toString.call(array);//"1,s,true,[object Object]"
那么別的對象是否可以調(diào)用數(shù)組的toString方法呢?
Array.prototype.toString.call({}); // [object Object]
Array.prototype.toString.call(function(){}) // [object Function]
Array.prototype.toString.call(1) // [object Number]
Array.prototype.toString.call('') // [object String]
Array.prototype.toString.call(true) // [object Boolean]
Array.prototype.toString.call(/s/) // [object RegExp]
// 特殊
<!--以下都是 Cannot convert undefined or null to object at toString-->
Array.prototype.toString.call(); // 相當于Array.prototype.toString.call(undefined)
Array.prototype.toString.call(undefined);
Array.prototype.toString.call(null);
數(shù)組對象通過this綁定調(diào)用Array.prototype.toString()方法,返回數(shù)組值的字符串拼接
非數(shù)組對象通過this綁定調(diào)用Array.prototype.toString()方法,返回的是該對象的字符串表示
另外null和undefined不可以通過綁定調(diào)用Array.prototype.toString()方法。
3 函數(shù) function
toString():返回函數(shù)的代碼
function foo(){
console.log('function');
};
foo.toString();
<!--"function foo(){-->
<!-- console.log('function');-->
<!--}"-->
Function.prototype.toString.call(foo);
<!--"function foo(){-->
<!-- console.log('function');-->
<!--}"-->
// Object Function Array本質(zhì)都是構造函數(shù)
Object.toString();
// "function Object() { [native code] }"
Function.toString();
// "function Function() { [native code] }"
Array.toString();
// "function Array() { [native code] }"
非函數(shù)對象是否可以使用函數(shù)的toString方法
Function.prototype.toString.call({})
<!--Function.prototype.toString requires that 'this' be a Function-->
除了上述提到的Object和Array兩種情況,其他類型都不支持非自身實例通過this綁定調(diào)用該Object子類原型對象上的toString()方法,這說明它們在重寫toString()方法時,明確限定了調(diào)用該方法的對象類型,非自身對象實例不可調(diào)用。所以,一般我們只使用Object.prototype.toString.call/apply()方法。
4 日期 Date
toString():返回帶有時區(qū)信息的日期和時間
var date = new Date();
date.toString();
//"Fri May 11 2019 14:55:43 GMT+0800 (中國標準時間)"
Date.prototype.toString.call(date);
//"Fri May 11 2019 14:55:43 GMT+0800 (中國標準時間)"
5 正則 RegExp
toString():返回正則表達式的字面量
var re = /cat/g;
re.toString();// "/cat/g"
RegExp.prototype.toString.call(re);// "/cat/g"
6 字符串 string
toString():返回字符串的一個副本
var str = "a";
str.toString(); //"a"
String.prototype.toString.call(str); //"a"
7 數(shù)字 number
toString():返回字符串形式的數(shù)值
var num = 520;
num.toString(); //"520"
Number.prototype.toString.call(num); //"520"
8 布爾 boolean
toString():返回字符串"true"或"false"
var boo = true;
boo.toString(); //"true"
Boolean.prototype.toString.call(boo); //"true"
9 null和undefined
null和undefined沒有相應的構造函數(shù),所以它們沒有也無法調(diào)用toString()方法,也就是說它們不能訪問任何屬性和方法,只是基本類型而已。
10 全局對象window(Window類)
toString(): 返回對象的字符串表示
window.toString();
<!--"[object Window]"-->
Winodw類并沒有在Window.prototype原型對象上重寫toString()方法,它會順著原型鏈查找調(diào)用Object.prototype.toString()。
所以上述代碼為:
Object.prototype.toString.call(window);
<!--"[object Window]"-->
toString.call()
toString.call()和toString()直接調(diào)用一樣都返回[object Undefined],
toString.call() // [object Undefined]
toString() // [object Undefined]
window.toString.call() // [object Undefined]
window.toString.call(undefined) // [object Undefined]
Object.prototype.toString.call() // [object Undefined]
Object.prototype.toString.call(undefined) // [object Undefined]
所以toString.call()相當于
window.toString.call() -> window.toString.call(undefined) -> Object.prototype.toString.call() ->Object.prototype.toString.call(undefined)
所以用Object.prototype.toString.call() 判斷類型可以簡寫為toString.call()來判斷
但是要注意如果已經(jīng)有定義了toString函數(shù)就不可以使用。
function toString(){
console.log("kimi")
}
toString(); //"kimi"
toString.call({}); //"kimi"
toString.call([]); //"kimi"