無標題
前言
- 在線音樂戳我呀!
- 音樂博客源碼上線啦!
- 渾渾噩噩在前端領域磕磕碰碰了接近三年,想看看Vue源碼,不知道有沒有最近想看源碼的猿友,如果JS不夠硬,建議跟我一起來重學JS,重學完相信再去看源碼,會事半功倍。
- 尤其清晰的記得畢業期間有一次上課期間手機響起來,接了個面試電話,就問了原型、原型鏈,真的是怕什么來什么,當時對這塊知識較模糊,支支吾吾回答不上來很尷尬。
- 真花了幾天幾夜,只為呈現最好的文章。可能一次性看不完,建議點贊收藏,花再多時間也要硬啃下來,一定拿下原型鏈這塊盲區知識,好嘛!!!
- 接下來我們來看看JS的原型、原型鏈知識點都可以考些什么。
- 請腦子清晰,跟着我的節奏,保證一回徹底啃下,Are you ready ?
每日一面:
面試官:知道什么是對象嗎?
知道,不過我工作努力,上進心強,暫時還沒有對象。但是打算找對象了。
先來問自己六七八道面試題
-
說一下原型?
-
說一下原型鏈過程?
-
Object.prototype.proto 值為啥?
-
什么的顯式原型、隱式原型都指向它自己?
-
任何函數都是new Function創建的?
-
Function的顯式原型是不是都是new Object出來的?
-
所有的函數的__proto__都是一樣的?
-
所有函數的顯式原型指向對象默認是空object實例對象?
想看答案,直接划到最下面。
如果會了,面試官隨便拿捏好吧。
如果不會,我們先來理解原型、原型鏈的概念。
只有知道問題背后的原理知識,解題必然隨手拈來。
原型與原型鏈
-
原型(prototype)
-
顯式原型與隱式原型
-
原型鏈
一、原型
1.1 函數的prototype屬性
1.1.1. 每個函數都有一個prototype屬性, 它默認指向一個Object空對象(即稱為: 原型對象)
上段代碼方便理解這段話:
function Fun () {}
console.log(Fun.prototype) // 默認指向一個Object空對象(沒有我們的屬性)
我們定義的Fun函數有一個prototype屬性,而且打印出來默認指向Object空對象。
❓ 杠精上身:我不信,那為什么Date函數怎么一創建就默認有很多方法?
有圖有真相。
你不是說prototype屬性, 它默認指向一個Object空對象,打印出來這么多方法(揭穿你),不過typeof Date.prototype
確實是object對象類型。
🙋這個問題我可以回答一下:
其實Date原型prototype上的方法是Date初始化自己往原型上添加的,主要是給實例對象使用的,誰把我創建出來,就給誰用。
打開Date函數,從源碼可以看到Date往prototype添加了很多方法。
懂了,原來是Date函數創建的時候自己往prototype上添加屬性方法。
❓ 你說函數的顯式原型指向對象默認是空object實例對象?
console.log(Fn.prototype instanceof Object) // true
console.log(Object.prototype instanceof Object) // false
console.log(Function.prototype instanceof Object) // true
Object除外。
1.1.2. 原型對象中有一個屬性constructor, 它指向函數對象
我們用代碼理解一下:
Date的原型prototype有一個屬性constructor,這個屬性constructor指向Date對象。
Date.prototype.constructor === Date
我們再用畫圖理解一下(說好一定要帶懂你)
構造函數和原型對象有相互引用的關系圖如下:
你里面有個屬性prototype能找到我,我里面有個屬性constructor也能找到你。
1.2 給原型對象添加屬性(一般都是添加方法)
有何作用:
為了給函數的所有實例對象自動擁有原型中的屬性(方法)。
// 給Fun原型添加屬性test
Fun.prototype.test = function () {
console.log('test()')
}
// 你問我有什么用?還不是給你fun實例對象可以調用test方法用的
var fun = new Fun()
fun.test()
二、顯式原型與隱式原型
2.1 每個函數function都有一個prototype,即顯式原型(屬性)
//定義構造函數
function Fn() {
// 內部語句: this.prototype = {}
}
console.log(Fn.prototype) // {}
而函數的prototype屬性在什么時候會被加上?
函數的prototype屬性: 在定義函數時自動添加的, 默認值是一個空Object對象
2.2 每個實例對象都有一個__proto__,可稱為隱式原型(屬性)
//創建實例對象
var fn = new Fn() // 內部語句: this.__proto__ = Fn.prototype
console.log(fn.__proto__) // {}
而對象的__proto__屬性: 創建對象時自動添加的, 默認值為構造函數的prototype屬性值
2.3 對象的隱式原型的值為其對應構造函數的顯式原型的值
其實也確實是相等,因為實例對象的__proto__屬性在創建對象時自動添加的, 默認值為構造函數的prototype屬性值。
二者的地址值都是同一個,自然相等。
function Fn() {}
var fn = new Fn()
console.log(Fn.prototype === fn.__proto__) // true
我們再來上張圖更透底搞懂他們的關系:
⭐請允許我啰嗦兩句(建議根據阿澤以下說的,然后畫下來,畫着畫着就懂了):
-
1.第一行代碼定義了Fn函數,那么棧空間產生Fn,因為函數是引用類型,所以賦予0x123(棧空間命名不是這樣子叫,只不過在這里給它一個名字)
-
Fn是構造函數,內部語句:
this.prototype = {}
,所以有顯式原型prototype屬性,指向空object對象0x234。 -
輸出Fn.prototype,打印
{constructor: ƒ, prototype:{...}}
-
2.創建以Fn實例對象的fn,在棧空間新建fn,指向堆空間的0x3456。
-
實例對象有__proto__屬性,實例對象被創建內部語句是:
this.__proto__ = Fn.prototype
-
對應上方2.3 對象的隱式原型的值為其對應構造函數的顯式原型的值
-
fn的__proto__和Fn.prototype指向同一個值為0x234。
-
3.在Fn構造函數的原型上添加test屬性,即0x234空object對象上添加test屬性。
-
接着執行fn.test(),所謂點.就是去堆空間里面找對應的屬性值,它先去本身0x345找,發現並沒有找到,繼續去它的隱式__proto__原型鏈0x234上找,找到了test屬性,返回!
誤區:
找屬性的時候,不會去顯式原型上找,它是回去找隱式原型__proto__屬性上查找,因為一開始該屬性創建的時候,就是將顯式原型prototype屬性賦給隱式原型__proto__屬性。
❓ 杠精上身:為什么構造函數有prototype屬性了,實例對象還要搞一個__proto__的值為構造函數prototype的值呢?
大概作用就是共享內存,減少方法對內存占用,test方法變成公交車,prototype就是現金,__proto__就是掃碼,只要給錢都能上(都能訪問test)。
2.4 能直接操作顯式原型, 但不能直接操作隱式原型(ES6之前)
我們正常都是直接給Fn原型上添加屬性方法,而不會直接去操作fn實例對象的__proto__屬性去添加方法。
也就是可以直接操作顯式原型,而不要直接去操作隱式原型。(雖然現在ES6時代可以去操作隱式原型,但最好不要)。
function Fn() {}
var fn = new Fn()
// 給顯式原型添加test方法
Fn.prototype.test = function () {
console.log('test()')
}
// 通過實例調用原型的方法
fn.test()
三、原型鏈
⭐建議跟着以下分析,自己把圖畫出來,程序員的動手能力很重要(如果實在懶,再打開一個窗口放把上面的圖,一邊看圖一邊看分析),如果圖、分析不結合,有可能無法GET到點哦 ~
如果暫時沒時間,建議收藏,等有時間慢慢看。
3.1 聊原型鏈前,我們先來看一道面試題目。(請耐下性子,好好看下去,必有收貨)
請輸出以下程序執行的結果。
function Fn() {
this.test1 = function () {
console.log('test1()')
}
}
Fn.prototype.test2 = function () {
console.log('test2()')
}
var fn = new Fn()
fn.test1() // test1()
fn.test2() // test2()
console.log(fn.toString()) // '[object Object]'
fn.test3() // Uncaught TypeError: fn.test3 is not a function
偷笑😁,這面試官也太小看我了吧!
面試官:別急,請根據上述代碼塊畫出函數的棧堆、以及原型鏈指向。
是不是很多面試者突然不知道怎么畫了呢?
其實也正常,對原型鏈概念模模糊糊,畫不出來,正常,不過這也是本篇存在的意義。
記住三句話:
-
每個函數都有一個prototype屬性, 它默認指向一個Object空對象。
-
每個實例對象都有一個__proto__,可稱為隱式原型。
-
實例對象的隱式原型屬性等於構造函數顯式原型屬性的值。
來吧,唐伯虎附體。沖!
-
左邊的程序塊大局觀大概是:
-
Object對象0x567是引擎創建了它,程序再執行的時候也就有了右邊堆空間的產生;
-
無論是Fn函數的顯式原型也好,隱式原型也罷,都是Object的實例對象;
-
然而Object雖然是大家的爸爸,但也是有原型的,是0x345。(爸爸的爸爸叫爺爺)
-
⭐請允許我啰嗦兩句(建議根據阿澤說的,然后畫下來,畫着畫着就懂了):
-
1.第一行代碼定義了Fn函數,那么棧空間產生Fn,因為函數是引用類型,所以賦予0x123(棧空間命名不是這樣子叫,只不過在這里給它一個名字)
-
堆空間也產生0x123的function對象,並有
prototype
屬性。 -
prototype
默認會指向一個Object空對象,我們給這個空對象命名0x234。 -
對應上面 1.1.1 每個函數都有一個prototype屬性, 它默認指向一個Object空對象(即稱為: 原型對象)
-
0x234有一個__proto__屬性。既然都有__prototy__隱式原型,那0x234是Object的實例對象。
-
__proto__指向一個Object對象,我們給這個原型對象命名為0x345。
-
對應上面 2.2 每個實例對象都有一個__proto__,可稱為隱式原型(屬性)
-
要講這個0x345,我們先來引進Object對象。
-
先問大家一個問題,Object對象先創建還是我們代碼塊的Fn函數先創建?
-
Object對象在我們代碼還沒有執行的時候,JS引擎已經創建了Object。
-
驗證下:看圖的0x234實例對象的__proto__隱式原型屬性 = 構造函數Object的顯式原型,那竟然是等於(賦值運算),那肯定是右邊先有的。右先創建才有的左邊。
-
所以Fn之前,Object已經先創建了,我們給Object命名為0x567。
-
0x567的Object函數對象有一個prototype屬性。
-
prototype
指向Object原型對象,我們給這個對象命名0x3456。 -
我們知道
prototype
默認會指向一個Object空對象,那這里Object的prototype也是指向一個空對象? -
總得有個尾吧,這里就是尾了。所以0x3456里面有很多屬性。如:toString()、valueof()、...
-
當然,它還有__proto__屬性,只不過值為null。
-
所以toString方法,我們在函數對象就可以調用它,因為函數也是對象,對象的原型最終為Object,Object的prototype顯式原型就有toString方法。
-
回歸上方的0x234的__proto__指向的就是Object的prototype顯式原型。
-
2.OK,到這里我們畫完代碼塊中的Fn函數的創建(是不是覺得我就定義一個函數,就這么復雜了,其實再走下去就通了),來,我們繼續GO!
-
3.接着
Fn.prototype.test2
給Fn原型添加test2屬性,我們在0x234對象里面添加test2屬性。 -
4.
var fn = new Fn()
,創建了一個Fn的實例對象fn,在棧中創建fn,命名為0x456。 -
堆空間有0x456這塊內存,有着test1屬性。
-
有人可能會問了,test1不是Fn構造函數的嗎?為什么test1寫在fn實例對象里面?
-
請注意:在Fn中的test1是
this.test1 = function(){}
創建的,上篇講到想寫好面向對象的代碼,這篇一定要看講到this指向,誰調用我,this就指向誰,在這里是fn調用的,那this.test1當然是寫在fn里面。 -
當然,0x456是一個實例對象,那就應該有__proto__隱式屬性。
-
該隱式屬性指向Who?
-
對應前面2.3 對象的隱式原型的值為其對應構造函數的顯式原型的值
-
所以,這里的0x456實例對象隱式原型的值指向0x123構造函數顯式原型的值,即指向:0x234。
-
5.OK,左邊代碼塊繼續走,fn.test1方法執行,輸出test1()
-
6.
fn.test2()
方法執行,先找到fn(先在自身屬性中查找,找到返回)。 -
然而並沒有找到,fn里面只有test1、__proto__屬性。(如果沒有, 再沿着__proto__這條鏈向上查找, 找到返回)
-
我們沿着__proto__這條線尋找下去,找到0x234,0x234有test2、__proto__屬性,OK,找到test2,返回,停止查找。輸出test2。
-
7.繼續走,打印
fn.toString()
,先找到fn,並沒有toString,再沿着__proto__這條鏈向上查找,找到0x234,還是沒有找到,再沿着__proto__這條鏈向上查找,來到0x345的Object原型對象,找到了toString屬性,打印[object Object]。 -
8.最后一步,
fn.test3()
,先找到fn,並沒有toString,再沿着__proto__這條鏈向上查找,找到0x234,還是沒有找到,再沿着__proto__這條鏈向上查找,來到0x345的Object原型對象,還是沒有找到,再沿着__proto__這條鏈向上查找,但此時的__proto__為null。 -
說到這里,我們再來聊一個情況。
-
如果是
fn.test3
,到Object的__proto__隱式原型為null,那到最盡頭了,找不到怎么辦呢?會報錯?還是返回null? -
其實都不是,是undefined。
-
所以
fn.test3
輸出undefined,但你fn.test3()
,undefined()執行,把undefined當做函數執行,那不得報錯。
打完收工。
⭐建議跟着以上分析,自己把圖畫出來,程序員的動手能力很重要(如果實在懶,再打開一個窗口放把上面的圖,一邊看圖一邊看分析),如果圖、分析不結合,有可能無法GET到點哦 ~
看完暫時還模模糊糊,請反復看以上分析。
如果暫時沒時間,建議收藏,等有時間慢慢看。
PS:
1.Object.prototype.__proto__
從上圖得知,雖然Object的原型已經是最底層了,但它也是有__proto__,只不過值為null,那其實從某種角度來說,它也是實例對象,因為有__proto__屬性就是實例對象。
2. 最小的孩子為null。
3.關於Object相關知識:
Object在一開始就有一個原型對象0x345,object的函數對象和實例對象都指向了原型對象;確實,不管是我Object的實例對象0x234還是別的函數fn都最終指向了我0x345。
同時object的實例0x234對象也是別的函數的prototype默認指向的一個object空對象。(請結合以上圖,建議打開一個窗口然后把圖拖拉過去,一一對應這些0x某某某,是很清晰明了的)
3.2 再來一條?(易出錯)
請輸出以下程序執行的結果。
function Fn() {
this.test1 = function () {
console.log('test1()')
}
}
console.log(Fn.prototype) // {constructor: ƒ}
Fn.prototype.test2 = function () {
console.log('test2()')
}
console.log(Fn.prototype) // {test2: ƒ, constructor: ƒ}
其實上面輸出的答案是錯的。
啊?
正確答案如下:看圖
可以看到展開前,並沒有test2
(上面的答案是對的),但如果展開后,第一個打印卻又有test2函數。
來,請聽我分析一波。
-
第一個輸出
Fn.prototype
展開有test2這個屬性,但沒有test2的實質內容。 -
JS申明的變量都會提前,所以這里prototype在一開始就知道有個叫test2的屬性的。
-
但是實際的內容prototype不知道,只有當
Fn.prototype.test2 = function () {}
執行完成才知道。可以在添加原型后,打印輸出再看一次prototype。 -
會發現test2的樣子和沒添加原型不一樣了。此時test2正式作為函數加入了prototype中。
-
是開始的時候變量申明提前造成的。即一開始就有test2存在了。
3.3 訪問一個對象屬性時,原型鏈的內心世界
-
訪問一個對象的屬性時:
- 先在自身屬性中查找,找到返回
- 如果沒有, 再沿着__proto__這條鏈向上查找, 找到返回
- 如果最終沒找到, 返回undefined
-
原型鏈的別名:隱式原型鏈
-
其實從上面的例子得知,每次訪問對象屬性,其實都會去尋找__proto__隱式原型鏈上去找,所以原型鏈被人起了別名叫隱式原型鏈。
-
小誤區:
var fn = new Fn()
這段代碼的fn,這個是不是也在原型鏈中找? -
不是,它是在作用域里,變量查找是在作用域中找的;原型鏈查找的是對象的屬性。
-
原型鏈的作用:查找對象的屬性(方法)
都看到這了,再堅持堅持就結束了(后面更精彩,前面那段分析算啥😁,后面才是重頭戲)
3.4 構造函數/原型/實體對象的關系(圖解一)
var o1 = new Object()
var o2 = {}
面試官:這段代碼執行完,棧堆空間是怎樣的?
-
首先:o1、o2都是Object構造函數的實例對象,那實例對象必定有__proto__屬性。
-
其次:實例對象的__proto__隱式原型屬性指向構造函數顯式原型屬性的值。
-
最終:可以看到圖中o1、o2的__proto__屬性與Object的prototype都指向同一個值Object.prototype
3.5 構造函數/原型/實體對象的關系(圖解二)
面試官:相信我,最后一道了。(說出來你可能不信)
最后一道我們就輕松一點,請畫出function Foo(){ }
原型鏈。
直接上圖。
⭐請允許我再多啰嗦兩句:
-
可以看到Foo函數有兩條線,一條是function Foo(),還有一條是__proto__。
-
第一條線為什么會存在?
-
1.
function Foo()
它是一個構造函數,這沒問題吧,在上面2.1 每個函數function都有一個prototype,即顯式原型(屬性) 一開始我們就講到了,所以自然有這條線存在。 -
構造函數有顯式原型屬性prototype。
-
第二條線為什么會存在呢?
-
2.看到了圖中第二條線有__proto__這個屬性,誰有這個屬性,實例對象吧,啥,你不是說
function Foo()
是構造函數嗎?現在又說是實例對象? -
別急,
function Foo(){ }
,其實是一個省略的寫法,它還可以寫成var Foo = new Function()
,這種寫法是相等的,那我這樣一寫,Foo豈不是是Function的實例對象,那也很自然有了第二條線是__proto__。 -
透過本質去看問題,將迎刃而解。
-
換句話說:所有的函數對象都有隱式、顯式原型屬性;隱式原型屬性指向大寫Function的顯式原型屬性。(你怎么不早說.....現在這不是說了嘛)
-
3.Foo的__proto__和
function Function
的顯式原型prototype都指向了Function.prototype。 -
Foo是Function構造函數new出來的吧,Foo是實例對象;function Function是構造函數,構造函數有prototype不過分吧。所以有了
__proto__、function Function()
都指向Function.prototype這條線,再次驗證上面的2.3 對象的隱式原型的值為其對應構造函數的顯式原型的值。(注意結合上圖) -
4.有個奇怪的現象:Function的顯式、隱式原型都指向自己?
-
有隱式原型,那就是實例對象;有prototype顯式原型,那就是構造函數。
-
說明Function,也是自己new出來的,即:
Function = new Function
(這個其實就是先有雞,還是先有蛋的問題) -
只有這樣才能說明顯式、隱式原型都相等,別的函數是沒有這個特點的,如:上面的
var Foo = new Function()
,Foo分了兩條線,顯式原型一條、隱式原型另一條。 -
只有一個顯式、隱式原型屬性都是自己的,就是Function。(⭐畫重點,面試要考的)
-
也就是說:任何函數創建都是new Function出來的,Function自己也不例外。
-
5.函數的顯式原型是new Object。
-
解釋一下:
-
函數是由Function生的,你自己的函數它的__proto__隱式原型指向它父親,也就是Function的prototype顯式原型。
-
Function唯一不同的就是它的__proto__指向它自己。
-
也就是說Function.__proto__找不到它的父親了(這個很關鍵)
-
但是Function的prototype在Function創建后就存在了。
-
Function.prototype是一個對象,是對象,所以它的__proto__當然指向的是Object。
-
-
6.為什么會有Object的__proto__指向Function.prototype這條線?
-
隱式原型怎么有的?(構造函數的顯式原型給的)
-
Object怎么來的?(其實Object也是new Function出來的,哈哈哈😁)
-
又因為Object是Function的實例對象。線是從Object的__proto__指向Function的。
-
一切Function都是new Function,所以Function才是這原型鏈的主謀。
-
可以看到下圖function Object()的隱式原型指向Function的顯式原型,所有對象函數都是Function創建出來的。
-
好,大家不是說一切皆對象嗎?那你這什么都是Function創建的,不是應該一切皆函數嗎?
-
是啊,函數是最底層的,函數可以叫函數對象,函數只是對象的一種特殊表示,所以一切皆對象。這里有一篇不服,老濕說的萬物皆對象,你也信?
-
7.所有函數的__proto__都是一樣的。因為都是new Function產生的。
-
new Function顯式原型是不會變的,那實例對象的__proto__隱式原型指向着我的顯式原型prototype,那大家都是我創建的,我不會變,自然大家也不會變。
聊一聊所有函數的__proto__都是一樣的
1. 其實就是兩條路:函數可以看做是構造函數,這時走的是object,這條顯式原型鏈;
2. 構造函數的顯式原型鏈用prototype走,這條路和他自己的所有實例的__proto__關聯。
3. 另一條路,函數也可以看做是Function的實例,這時走的是Function的這條隱式原型鏈,用自己的__proto__走。
4. 此時所有函數都是一個媽生的,就是new Function()得到的。而Function本身也是函數,所以也應該符合Function通過new Function()得到。
5. 這樣推導出必須滿足Function由new Function()得到,即Function.proto === Function.prototype
6. 通俗的講就是:所有雞都是雞媽生的,而且雞媽也是雞,所以雞媽也是雞媽生的。
雞(fn)有自己的prototype,這只雞(fn)其實又是雞媽生的,所以有__proto__
3.6 原型繼承
構造函數的實例對象自動擁有構造函數原型對象的屬性(方法)
利用的就是原型鏈。
原型上的屬性方法就是為了給實例對象用的,也就是它的孩子,誰創建了我,我就給誰用。
四、面試
現在隨便問你幾道題,應該能對答如流了吧!
Are You OK?
4.1 Object.prototype.proto 值為啥?
🙋:null。最小的孩子為null。
Object的原型對象才是原型鏈的盡頭。
4.2 什么的顯式原型、隱式原型都指向它自己?
🙋:Function。
顯式原型Function是構造函數;隱式原型Function也是實例對象。
要是你能給面試官再畫上【3.5 構造函數/原型/實體對象的關系(圖解二)】這張圖,那明天來上班。
4.3 任何函數都是new Function創建的?
🙋:是的,甚至Function它自己也不例外。
Function.__proto__ === Function.prototype
看,所有的都指向了Function的顯式原型屬性。
不太明白的童鞋可以再看看【3.5 構造函數/原型/實體對象的關系(圖解二)】這張圖。
好,大家不是說一切皆對象嗎?那你這什么都是Function創建的,不是應該一切皆函數嗎?
是啊,無論是內置函數、自定義函數、object都是通過new Function出來的,函數是最底層的,函數可以叫函數對象,函數只是對象的一種特殊表示,所以一切皆對象。這里有一篇不服,老濕說的萬物皆對象,你也信?
4.4 Function的顯式原型是不是都是new Object出來的?
🙋:是的。
-
Function.prototype.__proto__ === Object.prototype
-
函數是由Function生的,你自己的函數它的__proto__隱式原型指向它父親,也就是Function的prototype顯式原型。
-
Function唯一不同的就是它的__proto__指向它自己。
-
也就是說Function.__proto__找不到它的父親了(這個很關鍵)
-
但是Function的prototype在Function創建后就存在了。(A = B,肯定是B先產生才有了A)
-
Function.prototype是一個對象,是對象,所以它的__proto__當然指向的是Object。
4.5 所有的函數的__proto__都是一樣的?
🙋:是的。
因為所有的函數都是通過new Function出來的。
new Function的隱式原型和Function的顯式原型相等。(隱式是實例,誰賦值給它的,構造函數的顯式原型)
Function是不會變的。
所以所有的函數的__proto__都是一樣的。
不太明白的童鞋可以再看看【3.5 構造函數/原型/實體對象的關系(圖解二)】最后的總結。
4.6 所有函數的顯式原型指向對象默認是空object實例對象?
🙋:並不是。
console.log(Fn.prototype instanceof Object) // true
console.log(Object.prototype instanceof Object) // false
console.log(Function.prototype instanceof Object) // true
Object除外。Object內置很多屬性。
最后
想起有一次加班的時候,一個開發五年的大佬再向我講解自己的代碼的時候,說到hasOwnPrototype()這個方法的時候,說這個方法是for in遍歷對象的時候VS Code自動生成的hasOwnPrototype,如下圖:
大佬說我也不知道這個方法是干嘛的,我看別人都有加,最好還是加上吧。
也是笑了笑,其實就是為了嚴謹,確保我這個對象的屬性都是自定義添加的。
很多開發了很多年的大佬,說到底層原理尷尬的摸了摸頭。
不知道,不明了,不重要,重要的是不懂就要學,寫了不止八小時,好幾天拼湊起來的文章。
如果對您有幫助,你的點贊是我前進的潤滑劑。
以往推薦
vue-typescript-admin-template后台管理系統