從Object.prototype.toString聊到原型

一般在 JavaScript 裏使用 typeof 來判斷數據類型,只能區分基本類型,即 「number」,」string」,」undefined」,」boolean」,」object」,「function」,「symbol」 (ES6新增)七種。 可是仍是有些邊界狀況咱們沒法判斷的,好比:javascript

console.log(typeof null) // object
 console.log(typeof [1,2,3]) // object
複製代碼

很明顯上述得出來的結果不符合咱們的要求,那麼就不得不使用Object.prototype.toString方法去解決咱們的技術痛點java

Object.prototype.toString實現類型檢測

首先咱們先來介紹toString方法,這個方法就是轉爲字符串的方法,跟Object.prototype.toString對好比下:面試

let arr=[1,2,3];

//直接對一個數組調用toString()
 console.log(arr.toString()) // "1,2,3"
 console.log(Object.prototype.toString()) //[object Object]
複製代碼

你會發現Object.prototype.toString方法返回的結果有咱們須要的,你能夠經過Object.prototype.toString()=="[object Object]"來判斷是否是object類型,那麼有同窗就說我要檢測的是Array類型呢,是這樣寫嗎:數組

console.log(Array.prototype.toString()) 
複製代碼

結果是打印出來的爲空的,爲何會這樣這樣子呢,Object.prototype中的toString方法是確實被繼承下來了,可是數組重寫了toString方法,因此直接調用數組對象上面的toString方法調用到的實際是重寫後的方法,並非Object.prototype中的toString方法,因此要明確是隻有Object.prototype.toString方法返回的結果是"[...]",也就是只有Object.prototype上的toString才能用來進行復雜數據類型的判斷,而其餘類型可能內部有他們本身的寫法,那咱們若是想要判斷其餘類型如何調用這個方法呢,聰明的同窗可能想到去改變Object.prototype.toString上下文的執行環境,那麼天然而然想到call方法(apply方法):app

let arr=[1,2,3];
 console.log(arr.toString()) // "1,2,3"
// 經過call指定arr數組爲Object.prototype對象中的toString方法的上下文
 console.log(Object.prototype.toString.call(arr)) // "[object Array]"
複製代碼

那麼咱們的問題就解決了,很精確判斷出類型,實際上咱們是經過Object.prototype的原型方法去實現的,那麼這裏就不得不聊下原型以及原型鏈方面的知識了函數

深刻了解原型及原型鏈

在面試題或者開發中常常會遇到prototype,_proto,constructor等一系列名詞,好比:測試

打印的這個對象裏面有__proto__屬性,屬性下包含着constructor屬性和__proto__屬性等,看起來很複雜那今天咱們就好好剖析這個知識點吧!!!

構造函數和原型的關係

prototype(翻譯爲原型):每一個函數都有一個 prototype 屬性,那麼構造函數和prototype是怎麼樣的一種指向關係ui

舉個例子:this

function Animal() {

}
// prototype是函數纔會有的屬性
Animal.prototype.name = '旺財'
let animal1 = new Animal()
let animal2 = new Animal()
console.log(animal1.name) // 旺財
console.log(animal2.name) // 旺財
複製代碼

這裏有個問題:咱們Animal沒有聲明name字段,爲何咱們的實例化對象animal1和animal2會具備name字段?還有構造函數跟原型之間又有什麼關係?spa

其實,函數的prototype屬性指向了一個對象,這個對象正是調用該構造函數而建立的實例的原型,當咱們進行new操做(new操做下面會重點講到)的時候返回的animal1對象字段至關於由Animal和Animal.prototype實例原型組成,你能夠看到toString方法,可是咱們Animal構造函數並無聲明這個方法這是繼承於Animal.prototype原型對象,name字段也是,其構造函數和原型的關係圖以下:

這裏咱們有必要弄清楚一些叫法,由於概念有點多容易混淆

  • 只有(構造)函數才具備prototype屬性
  • prototype是屬性不是原型,經過該屬性才能找到原型
  • Animal.prototype纔是咱們所說的原型,也叫實例原型

如何經過實例化對象指向實例原型(proto)

每個JavaScript對象(除了 null )都具備的一個屬性,叫__proto__,這個屬性會指向該對象的原型

舉個例子:

function Animal() {

}
let animal = new Animal()
console.log(animal.__proto__ === Animal.prototype) // true
複製代碼

因而構造函數、實例原型和__proto__屬性之間的關係圖以下:

咱們上述講到的都是指向實例原型,那麼咱們可不能夠經過原型指向構造函數,答案確定是能夠的

constructor屬性指向構造函數

有同窗會說實例原型能夠指向實例化對象,那是不行的由於咱們new不少個實例對象,可是要經過constructor指向實例化對象是不能的,那麼咱們就來看看constructor如何指向構造函數 走下代碼:

function Animal() {

}
let animal = new Animal()
console.log(animal)
console.log(Animal === Animal.prototype.constructor) // true
console.log(animal.__proto__.constructor=== Animal) // true
複製代碼

那麼咱們能夠更新下關係圖:

以上效果圖也就是咱們常說的原型鏈,咱們花了大量時間去講原型和原型鏈,那麼哪些地方用到這方面的知識呢? 接下來咱們來看看new操做符是如何實例對象

new操做符是如何工做的

其實new操做符就幹了三件事情

例子一:

function Animal() {
  console.log("發出聲音")
}
let animal = new Animal()
複製代碼

等同於

例子二:

function Animal() {
  console.log("發出聲音")
}
let animal  = {};
animal.__proto__ = Animal.prototype;
Animal.call(animal) // // 這一步的操做是改變this指向,指向實例化對象 而且將其構造函數this關聯的屬性綁定給實例化對象
複製代碼
  • 第一行,咱們建立了一個空對象animal
  • 第二行,咱們將這個空對象的__proto__成員指向了Animal函數對象prototype成員對象
  • 第三行,咱們將Animal函數對象的this指針(上下文環境)替換成animal,而後再調用Animal函數

這就是new操做內部所作的事,可能細心的同窗會發現例子一執行的時候會打印出來:

也就是執行了一次Animal函數,實際就是至關於Animal.call(animal)這樣執行了,這樣解釋了爲何打印出來了

還有一個問題:假設咱們在構造函數加上個return會發生什麼?

function TestAnimal(){
     this.name="TestAnimal"
     console.log("我是測試TestAnimal")
 }
 function Animal() {
  console.log("發出聲音")
  return TestAnimal
}
let animal = new Animal()
console.log(animal)
複製代碼

效果圖以下:

實際上new 操做符調用構造函數的時候,函數內部實際上發生如下變化:

1.建立一個空對象,而且 this 變量引用該對象,同時還繼承了該函數的原型。

2.屬性和方法被加入到 this 引用的對象中。

3.新建立的對象由 this 所引用,而且最後隱式的返回 this.

function Animal() {
  console.log("發出聲音")
  console.log(this)
  return TestAnimal
}
// let animal = new Animal()
console.log(new Animal()) // 結果跟this打印的同樣,這樣就解釋最後隱式的返回 this
// 而且當你使用new關鍵字的時候Animal被當作了構造函數:當Animal中包含return的時候 
// 且return的是一個對象而不是number這樣的值,則會返回return後的對象,return後不是對象,則被忽略,返回的是Animal中的this對象
複製代碼
相關文章
相關標籤/搜索