常常在國內的各大網站博客上看到一句話,叫作JS中萬物皆對象,那是否真是如此?javascript
那麼,咱們先來捋一捋JS中的數據類型,JS中的數據類型有下面幾種java
Undefined函數
Null學習
Boolean網站
Numberspa
String設計
Symbol (ES6中新增)3d
Objectcode
在JS中咱們把前面六種類型稱爲爲基本數據類型,最後一種則是複雜數據類型,也就是對象類型。其實從這裏看貌似已經區分了對象以及其餘。對象
對象能夠動態的添加屬性和方法,而基本類型不行。
以下:
// 基本類型 Number var num1 = 1000; num1.length = 10; console.log(num1.length); //undefinded // 對象類型 Array var arr1 = []; arr1.length = 10; console.log(arr1.length); //10
再進一步看,JS中的數據類型有值類型(基本類型)和引用類型(對象類型)之分(其實其餘不少語言中也有這麼個區別),所謂值類型和引用類型,無非只是實例對象中保存了值或者保存了對象的引用。
值類型:初始化一個值類型實例的時候,其實是給這個值分配了一個內存空間來保存,當進行賦值操做的時候,新的實例會開闢一塊新的內存空間,而後將原來的值copy
到了這個新的內存空間中;
引用類型:初始化一個引用類型實例的時候,僅僅是把這個實例的值所在內存空間的引用賦給這個實例,當copy
給了新的實例對象使,其實是copy
了對這塊內存空間的引用,兩個實例對象本質上共用一塊內存空間。
舉個?:
// 值類型 Number var num1 = 1; var num2 = num1; num2 = num2 + 1 console.log(num1); // 1 // 引用類型 Array var arr1 = []; var arr2 = arr1; arr2.push('oujm') console.log(arr1); // ["oujm"]
其實從上面看,很明顯的能得出JS中並不是萬物皆對象,可爲何仍是有這麼多的人認爲並相信這個觀點是正確的呢?(包括當初懵懂無知的我?)
console.log(typeof null); // object
不少人(可能很少,我瞎猜)都說,連null
都是對象類型,其餘的能不是對象嗎?講道理我以前也很疑惑。直到在看書的時候看到null
只不過是一個空對象引用,這麼說來,它的類型是object
也就沒有那麼奇怪了。
還有些人說這個是JS中的一個
bug
,不一樣的對象在底層都表示爲二進制,在JavaScript
中二進制前三位都爲 0 的話會被判 斷爲object
類型,null
的二進制表示是全 0,天然前三位也是 0,因此執行typeof
時會返回"object"
。是真也好,假也罷。但說這是個bug
其實不必,我不知道底層是怎麼實現,可僅僅是由於null
在底層全是0就返回object
,這種bug
未免顯的過低級了點把... 我更願意相信,JS的設計者就是想把null
表示爲空對象引用
__proto__
以下所示:
var str = "oujm"; console.log(str.__proto__); /* String { anchor:ƒ anchor() at: ƒ at() big: ƒ big() blink: ƒ blink() bold: ƒ bold() charAt: ƒ charAt() charCodeAt: ƒ charCodeAt() codePointAt: ƒ codePointAt() concat: ƒ concat() constructor: ƒ String() ... } */ // Boolean Number 等基本類型打印出來的結論相似
從咱們以前的學習中能知道str
是個基本類型,基本類型怎麼會有屬性呢。但是這裏不但看到了這個基本類型的實例對象有屬性__proto__
,並且很明顯它的構造函數就是String()
,這個時候有些人就會以爲既然有屬性,有構造函數,那說明str
本質上就是個對象。這在表面上看起來好像是沒什麼問題。
那讓咱們再來看一個更直白的?:
var str1 = "oujm"; var str2 = str1.substring(2);
從上面能看出來str1
是有方法的。
OK,宗上所得:基本類型也是對象類型,即萬物皆對象
我以爲大部分人能得出這個結論都基於此。可是他們忽略了,在JS的世界中有一種對象類型叫包裝對象。
咦?,String
,Number
,Boolean
,這三個不是基本類型嗎。其實否則,ECMAScript提供了這三個特殊的引用類型,這三個引用類型和其餘的引用類型類似,但同時也具備於各自的基本類型相應的特殊行爲,實際上,每當讀取一個基本類型的時候,後臺就會建立一個對應的基本包裝類型的對象。
再來看上面那個?,str1
很明顯是一個基本類型實例,問題就出在 str1.substring(2)
字符串怎麼會有方法。其實,爲了讓咱們更好的操做基本類型的實例對象,後臺進行了一系列的操做:
建立String的實例
在實例上調用指定的方法
銷燬這個實例
// var str2 = str1.substring(2) 動做拆解: var tempStr = new String("oujm"); var str2 = tempStr.substring(2); tempStr = null;
從這裏可以看到,通常的引用類型和包裝類型惟一的區別就在於對象的生命週期。包裝類型的對象生命週期很短,只有代碼執行的一瞬間,而後就被銷燬了,因此這也就是爲何咱們不能在運行的時候爲基本類型的值添加屬性和方法。
var str1 = "oujm"; var str1.bf = "ethan"; console.log(str1.bf); // undefined
這也解答了我曾經的一個疑問
var str1 = "oujm"; var str2 = new String("ethan"); console.log(str1.__proto__ === str2.__proto__); // true console.log(str1 instanceof String); // false console.log(str2 instanceof String); // true
一樣的道理,在調用__proto__
屬性的瞬間,也是使用new String()
先來實例化一個對象,因此那一瞬間他們的構造函數以及原型對象是相同的,但也僅僅是那一瞬間。
別再?到有些文章說的,JS的世界很大,並不僅有對象