JavaScript有八種內置類型前端
除對象外,其餘統稱爲「基本類型」。
typeof null // 'object' typeof undefined; // "undefined" typeof false; // "boolean" typeof 1; // "number" typeof '1'; // "string" typeof {}; // "object" typeof []; // "object" typeof new Date(); // "object" typeof Symbol; // "Symbol" typeof 123n // 'bigint'
這裏的類型值的是值,變量是沒有類型的,變量能夠隨時持有任何類型的值。JavaScript中變量是「弱類型」的,一個變量能夠如今被賦值爲 字符串類型,隨後又被賦值爲數字類型。es6
typeof
是一個操做符而不是函數,用來檢測給定變量的數據類型。數組
Symbol
是ES6中引入的一種原始數據
類型,表示獨一無二的值。BigInt(大整數)是 ES2020 引入的一種新的數據類型,用來解決 JavaScript中數字只能到 53 個二進制位(JavaScript 全部數字都保存成 64 位浮點數,大於這個範圍的整數,沒法精確表示的問題。(在日常的開發中,數據的id 通常用 string 表示的緣由)。爲了與 Number 類型區別,BigInt 類型的數據必須添加後綴n。1234
爲普通整數,1234n
爲BigInt
。瞭解更多能夠看 《ES6 入門教程》
typeof null
爲何返回 'object'
,稍後會從JavaScript數據底層存儲機制來解釋。瀏覽器
還有一種狀況微信
function foo() {}; typeof foo; // 'function'
這樣看來,function
也是JavaScript
的一個內置類型
。然而查閱規範,就會知道,它其實是 object
的一個"子類型"。具體來講,函數是「可調用對象」,它有一個內部屬性[[call]]
,該屬性使其能夠被調用。typeof
能夠用來區分函數其餘對象。函數
可是使用 typeof
不能 判斷對象具體是哪一種類型。全部 typeof
返回值爲 "object" 的對象(如數組,正則等)都包含一個內部屬性 [[class]]
(咱們能夠把它看作一個內部的分類)。這個屬性沒法直接訪問,通常經過 Object.prototype.toString(...)
來查看。學習
Object.prototype.toString.call(new Date); // "[object Date]" Object.prototype.toString.call([]); // "[object Array]" Object.prototype.toString.call(/reg/ig); // "[object RegExp]"
instanceof
運算符也經常用來判斷對象類型。用法: 左邊的運算數是一個object
,右邊運算數是對象類的名字或者構造函數; 返回true
或false
。this
[] instanceof Array; // true [] instanceof Object; // true [] instanceof RegExp; // false new Date instanceof Date; // true
instanceof
的內部機制是:檢測構造函數的 prototype
屬性是否出如今某個實例對象的原型鏈上。下面會詳解介紹該部分。spa
typeof
原理: 不一樣的對象在底層都表示爲二進制,在Javascript中二進制前(低)三位存儲其類型信息。prototype
typeof null 爲"object", 緣由是由於 不一樣的對象在底層都表示爲二進制,在Javascript中二進制前(低)三位都爲0的話會被判斷爲Object類型,null的二進制表示全爲0,天然前三位也是0,因此執行typeof時會返回"object"。
一個不恰當的例子,假設全部的Javascript對象都是16位的,也就是有16個0或1組成的序列,猜測以下:
Array: 1000100010001000 null: 0000000000000000 typeof [] // "object" typeof null // "object"
由於Array和null的前三位都是000。爲何Array的前三位不是100?由於二進制中的「前」通常表明低位, 好比二進制00000011對應十進制數是3,它的前三位是011。
要想從根本上理解,須要從兩個方面入手:
通俗一些講,instanceof
用來比較一個對象是否爲某一個構造函數的實例。注意,instanceof運算符只能用於對象,不適用原始類型的值。
實例
是否屬於某種類型
function Foo() {}; Foo.prototype.message = ...; const a = new Foo();
function Car(make, model, year) { this.make = make; this.model = model; this.year = year; } const auto = new Car('Honda', 'Accord', 1998); console.log(auto instanceof Car); // expected output: true console.log(auto instanceof Object); // expected output: true
咱們建立的每一個函數都有一個 [[prototype]](原型))屬性,這個屬性是一個指針,指向一個對象,而這個對象的用途是包含能夠由特定類型的全部實例共享的屬性和方法。那麼 prototype 就是調用 構造函數
而建立的那個對象實例
的的原型對象
。使用原型對象的好處是可讓全部對象實例共享它所包含的屬性和方法。
function Person() {}; Person.prototype.name = 'kangkang'; Person.prototype.sayName = function() { console.log(this.name); } const person1 = new Person(); person1.sayName(); // 'kangkang' const person2 = new Person(); person2.sayName(); // 'kangkang' console.log(person1.sayName === person2.sayName); // true
構造函數
都有一個原型對象
原型對象
都包含一個指向構造函數
的指針
實例
都包含一個指向原型對象
的指針
那麼,假如咱們讓原型對象
等於另外一個類型
的實例
,結果會怎麼樣?
顯然,此時的原型對象
將包含一個指向另外一個原型
的指針
,相應地,另外一個原型
中也包含着一個指向指向另外一個構造函數
的指針
。假如另外一個原型
又是另外一個類型
的實例
,那麼上述關係依然成立,如此層層遞進,就構成了實例與原型的鏈條。這就是所謂原型鏈的基本概念。
上面這段話有點繞,若是想不明白的話,這裏能夠停一下,讀三篇,再結合咱們日常寫代碼使用過程當中的實際場景。
[[prototype]]
機制[[prototype]]
機制就是存在與對象中的一個內部連接,它會引用其餘對象。
一般來講,這個連接的做用是:若是在對象上沒有找到須要的屬性或者方法引用,引擎就會繼續在 [[ptototype]]
關聯的對象上進行查找,同理,若是在後者中也沒有找到須要的引用就會繼續查找它的[[prototype]],以此類推。這一系列對象的連接被稱爲「原型鏈」。
全部普通的 [[prototype]]鏈最終都會執行內置的 Object.prototype
。因爲全部的"普通"(內置,不是特定主機的擴展)對象都」源於「(或者說把[[prototype]] 鏈頂端設置爲)這個Object.prototype
對象,因此說它包含JavaScript中許多通用的功能。好比說.toString()
和 .valueOf()
等等。
Object.prototype
是js原型鏈的最頂端,它的__proto__
是null
(有__proto__屬性,但值是 null,由於這是原型鏈的最頂端);
最主要的就是節省內存,若是屬性和方法定義在原型上,那麼全部的實例對象就能共享。
__proto__
絕大多數(不是全部)瀏覽器也支持一種非標準的方法來訪問內部的 [[prototype]]
屬性。
function Foo() {}; const a = new Foo(); a.__proto__ === Foo.prototype; // true
這個奇怪的.__proto__
屬性「神奇地」引用了內部的[[prototype]]
對象。若是你想直接查找(甚至能夠直接經過.__proto__.__proto__ ...來遍歷)原型鏈的話,這個方法很是有用。
和.construtor
同樣,__proto__
實際上並不存在於你正在使用的對象(本例中是a
)。實際上,它和其餘的經常使用函數(.toString()、.isPrototypeOf(...)
,等等 同樣,存在於內置的Object.prototype
中。(它們是不可枚舉的;
此外,.__proto__
看起來很像一個屬性,可是實際上它更像一個 getter/setter
。.__proto__
的實現大體是這樣的
Object.defineProperty(Object.prototype, "__proto__", { get: function() { return Object.getPrototypeOf(this); }, // ES6中的Object.setPrototypeOf set: function(o) { Object.setPrototypeOf(this, o); return o; } })
所以,訪問(獲取值) a.__proto__
時,其實是調用了 a.__proto__()(調用getter函數)
。雖然getter
函數存在於Object.prototype
對象中,可是 它的 this 指向對象 a
,因此和object.getPrototypeOf(a)
結果相同。
.__proto__
是可設置屬性,以前的代碼中使用ES6的Object.setPrototypeOf(...)
進行設置。然而,一般來講你不須要修改已有對象的[[prototype]]
。
function Foo
就是一個方法,好比內置的 Array,String,或者自定義方法。function Object
就是 Object
function Function
就是 Function
__proto__
都是 Function.prototype
String
, Array
, Number
, Object
, Function
這些其實都是 functionfunction Foo() {}; console.log(Object instanceof Object); // true console.log(Function instanceof Function); // true console.log(Function instanceof Object); // true console.log(Foo instanceof Foo); // false console.log(Foo instanceof Object); // true console.log(Foo instanceof Function); // true
你們能夠在控制檯輸出,能夠直觀的看到每一個步驟的輸出,結合instanceof 的規範跟js原型鏈 加深理解。
回過頭來再看instanceof
。
instanceof
的語法:
object instanceof constructor // 等同於 constructor.prototype.isPrototypeOf(object)
instanceof
的代碼實現。
function instanceof(L, R) { //L是表達式左邊,R是表達式右邊 const O = R.prototype; L = L.__proto__; while(true) { if (L === null) return false; if (L === O) // 這裏重點:當 L 嚴格等於 0 時,返回 true return true; L = L.__proto__; } }
instanceof
原理: 檢測 constructor.prototype
是否存在於參數 object的 原型鏈上。instanceof
查找的過程當中會遍歷object
的原型鏈,直到找到 constructor
的 prototype
,若是查找失敗,則會返回false
,告訴咱們,object
並不是是 constructor
的實例。
原型鏈這部分很很差理解,我基本上都是看完過幾天就忘,因此要多看幾遍多理解,花些時間搞明白,搞明白這部分。以後再看相關的東西,就很簡單易懂。這部分是JavaScript很重要的核心。花幾天時間反覆看,弄明白了,之後理解不少問題都是簡單的多。若是你發現我上面哪部分表述的不太準確,記得給我指出來,互相學習。這部分推薦好好看看《JavaScript高級程序設計(第3版)》第六章
的這部分,還有《你不知道的JavaScript(上卷)》第五章
關於這部份內容的講解。
對象的Symbol.hasInstance
屬性,指向一個內部方法。當其餘對象使用instanceof
運算符,判斷是否爲該對象的實例時,會調用這個方法。好比,foo instanceof Foo
在語言內部,實際調用的是Foo[Symbol.hasInstance](foo)
。
class MyClass { [Symbol.hasInstance](foo) { return foo instanceof Array; } } [1, 2, 3] instanceof new MyClass() // true
看完以後,腦子裏能夠把上面的內容串一下;看看下面的幾個問題你是否能夠馬上想出來
JavaScript
有哪幾種數據類型,都有哪些判斷數據類型的操做,返回值是什麼,原理是什麼typeof null
爲何是 」object「
原型
,哪裏是 [[prototype]]
的 」盡頭「,爲何要這麼設計JavaScript
原型鏈的核心是什麼instanceof
的原理是什麼Symbol.hasInstance
又是什麼(或者你本身實現一個instanceof
)最近發起了一個100天前端進階計劃,主要是深挖每一個知識點背後的原理,歡迎關注 微信公衆號「牧碼的星星」,咱們一塊兒學習,打卡100天。