如果我說你對 new 操作符可能不是很瞭解。很多人可能會很不屑,不就是 new 一個構造函數嗎,來我給你背一下 new 的過程,巴拉巴拉巴拉……
然鵝 new 操作符真的那麼簡單嗎?我們看下面的幾個問題:
- new 一個構造函數發生了什麼?
- 如果我們的構造函數是一個箭頭函數,得到的結果是什麼?
- 如果構造函數 return 一個普通對象,new 此構造函數得到的實例對象是什麼?隱式原型指向什麼?
- 如果構造函數 return 一個基本類型的值,此時 new 此構造函數得到的實例對象是什麼?隱式原型指向什麼?
- 如果構造函數 return 一個Array 類型的值,此時 new 此構造函數得到的實例對象是什麼?隱式原型指向什麼?
- 如果構造函數 return 一個基本包裝類型的值(Number、String、Boolean),此時 new 此構造函數得到的實例對象是什麼?隱式原型指向什麼?
- 如果構造函數 return null 呢,此時 new 此構造函數得到的實例對象又是什麼?隱式原型指向什麼?
是不是有點懷疑自己了。
如果你可以全部正確回答,那可以關掉本篇博客去看其他知識點了,因爲你對 new 已經瞭解的很透徹了,但是如果你一知半解,請繼續往下看,看完本文之後相信你對 new 會有一個更深入的瞭解。
首先第一個問題,new 一個構造函數發生了什麼?
Q:new 一個構造函數發生了什麼?
老生常談,很多人都可以說的很詳細。以下是 MDN 給出的答案
- 創建一個空的簡單JavaScript對象(即{});
- 鏈接該對象(即設置該對象的構造函數)到另一個對象 ;
- 將步驟1新創建的對象作爲this的上下文 ;
- 如果該函數沒有返回對象,則返回this。
我們用代碼分解一下以上步驟
// 示例代碼
function Parent (name) {
this.name = name
}
Parent.prototype.getName = function() {
return this.name
}
const people = new Parent('張三')
上面的代碼看起來很簡單,其實卻做了很多的隱式操作,const people = new Parent('張三')
其實相當於做了如下操作
// 首先會創建一個對象,並且將此對象的隱式原型對象(__proto__)指向原型函數的顯示原型對象(prototype)
const obj = Object.create(Parent.prototype)
// 將新創建的對象作爲 this 的上下文
this = obj
// 執行構造函數中的代碼
this.name = name
// 如果構造函數返回一個對象,則得到一個對象,否則得到我們隱式創建的對象
if (此構造函數返回一個對象) {
return 這個對象
} else {
return obj
}
如果我們的構造函數是一個箭頭函數呢?
Q:如果我們的構造函數是一個箭頭函數,得到的結果是什麼?
// 示例代碼
const Parent = (name) => {
this.name = name
}
Parent.prototype.getName = function() {
return this.name
}
const people = new Parent('張三')
首先提示不可以給 undefined 設置 getName
好,那我們去掉原型的設置
結果提示 Parent 不是一個構造函數
???,why?
我們看一下普通函數和箭頭函數的區別
可以看到箭頭函數是沒有顯示原型的,同樣我們可以在 MDN 中找到答案。
箭頭函數不能用作構造器,和 new一起用會拋出錯誤。
箭頭函數沒有prototype屬性。
箭頭函數不會創建自己的this,它只會從自己的作用域鏈的上一層繼承this。
由於 箭頭函數沒有自己的this指針,通過 call() 或 apply() 方法調用一個函數時,只能傳遞參數(不能綁定this),他們的第一個參數會被忽略。
等 ……
所以我們的第二個問題也有了答案
A:箭頭函數是不可以做爲構造函數的,因爲箭頭函數沒有 this,且與普通函數相比缺少了 prototype,和 new 操作符一起使用會報錯。
那如果我們的構造函數 return 一個普通對象呢?
Q:如果構造函數 return 一個普通對象,new 此構造函數得到的實例對象是什麼?隱式原型指向什麼?
// 示例代碼
function Parent (name) {
this.name = name
return { like: '遊戲', name: '李四' }
}
Parent.prototype.getName = function() {
return this.name
}
const people = new Parent('張三')
如上示例,我們看一下 people 是什麼
爲什麼會這樣呢?觀察 MDN 文檔中 new 的過程
如果該函數
沒有返回對象
,則返回this。
所以這個問題我們也有了答案
A:如果構造函數返回一個對象,得到的實例對象就是我們構造函數 return 的實例對象本身,實例對象的原型也和構造函數的原型一致。
如果構造函數 return 一個基本類型
Q:如果構造函數 return 一個基本類型的值,此時 new 此構造函數得到的實例對象是什麼?隱式原型指向什麼?
// 示例代碼
function Parent (name) {
this.name = name
return `李四愛玩遊戲`
}
Parent.prototype.getName = function() {
return this.name
}
const people = new Parent('張三')
此時,我們得到的實例對象又是什麼樣子的呢?
可能有些同學已經猜到了,我們會得到構造函數隱式創建的對象。
還是可以在那句話中找到線索
如果該函數
沒有返回對象
,則返回this。
我們得到結論:
A:如果構造函數 return 的值是一個基本類型,那我們得到的實例對象是構造函數隱式創建的對象。隱式原型指向構造函數的顯示原型。
如果構造函數 return 一個 Array 類型
Q:如果構造函數 return 一個 Array 類型的值,此時 new 此構造函數得到的實例對象是什麼?隱式原型指向什麼?
// 示例代碼
function Parent (name) {
this.name = name
return [ '李四', '遊戲' ]
}
Parent.prototype.getName = function() {
return this.name
}
const people = new Parent('張三')
我們在控制檯輸出一下結果,看實例對象是什麼
這裏不難理解爲什麼會得到數組,因爲 Array 本身就是 Object 的子集,JS 裏數組是用 Object 實現的。
A:如果構造函數 return 一個數組,實例對象就是我們 return 的數組,隱式原型指向也與其相同。
如果構造函數 return 一個基本包裝類型的值
Q:如果構造函數 return 一個基本包裝類型的值(Number、String、Boolean),此時 new 此構造函數得到的實例對象是什麼?隱式原型指向什麼?
// 示例代碼
function Parent (name) {
this.name = name
return new String(`李四愛玩遊戲`)
}
Parent.prototype.getName = function() {
return this.name
}
const people = new Parent('張三')
看到這裏相信大家都已經知道答案了,得到的實例對象就是我們 return 的值
也不難理解,基本包裝類型也是 Object 的子集,也是一個對象。也不難推導出,其他的像 Date、BigInt 等也是與此相同。
A:如果構造函數 return 一個基本包裝類型,實例對象就是我們 return 的值,隱式原型指向也與其相同。
如果構造函數 return null
Q:如果構造函數 return null 呢,此時 new 此構造函數得到的實例對象又是什麼?隱式原型指向什麼?
// 示例代碼
function Parent (name) {
this.name = name
return null
}
Parent.prototype.getName = function() {
return this.name
}
const people = new Parent('張三')
這裏爲什麼會單獨把 null 拿出來呢?因爲 null 在 js 裏的位置很尷尬,它是一種基本類型的值,但是 typeof null 的結果卻是 object,所以爲了更清晰,我們單獨測試一下他的結果,如下
可以看到 typeof 的檢測結果對其本質沒有影響,還是和基本類型得到的結果一致
A:如果構造函數 return 的值是 null ,typeof 不影響其結果,和基本類型得到的結果相同
總結
到此我們把所有的情況都測試了一遍,是不是對 new 有了更清晰的認識呢
最後我們回顧上面的問題,發現其實可以總結爲三種情況
- 箭頭函數是特殊的函數,不可以使用 new 操作符。
- 如果構造函數返回基本類型的值,則我們得到的實例對象是構造函數隱式創建的對象,隱式原型指向構造函數的顯式原型。這裏我們觀察沒有 return 值的構造函數,發現其實就相當於 return undefined,所以也可以歸類爲返回基本類型得值
- 如果構造函數返回的是一個對象類型的值(包括 Object 的子集,Array、Number 等)則實例對象是我們 return 的值,且隱式原型的指向與 return 的值的隱式原型相同
到此,我們詳細解析了 new 一個構造函數的過程