多圖詳解,一次性啃懂原型鏈(上萬字)


無標題

前言

  • 在線音樂戳我呀!
  • 音樂博客源碼上線啦!
  • 渾渾噩噩在前端領域磕磕碰碰了接近三年,想看看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空對象(沒有我們的屬性)

1.png

我們定義的Fun函數有一個prototype屬性,而且打印出來默認指向Object空對象。

❓ 杠精上身:我不信,那為什么Date函數怎么一創建就默認有很多方法?

有圖有真相。

2.png

你不是說prototype屬性, 它默認指向一個Object空對象,打印出來這么多方法(揭穿你),不過typeof Date.prototype確實是object對象類型。

🙋這個問題我可以回答一下:

其實Date原型prototype上的方法是Date初始化自己往原型上添加的,主要是給實例對象使用的,誰把我創建出來,就給誰用。

3.png

打開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

我們再用畫圖理解一下(說好一定要帶懂你)

構造函數和原型對象有相互引用的關系圖如下:

1.png

你里面有個屬性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

我們再來上張圖更透底搞懂他們的關系:

13.jpg

⭐請允許我啰嗦兩句(建議根據阿澤以下說的,然后畫下來,畫着畫着就懂了):

  • 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__,可稱為隱式原型。

  • 實例對象的隱式原型屬性等於構造函數顯式原型屬性的值。

來吧,唐伯虎附體。沖!

16.png

  • 左邊的程序塊大局觀大概是:

    • Object對象0x567是引擎創建了它,程序再執行的時候也就有了右邊堆空間的產生;

    • 無論是Fn函數的顯式原型也好,隱式原型也罷,都是Object的實例對象;

    • 然而Object雖然是大家的爸爸,但也是有原型的,是0x345。(爸爸的爸爸叫爺爺)

7.jpg

⭐請允許我啰嗦兩句(建議根據阿澤說的,然后畫下來,畫着畫着就懂了):

  • 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: ƒ}

5.png

其實上面輸出的答案是錯的。

啊?

正確答案如下:看圖

Video_2021-09-13_202026.gif

可以看到展開前,並沒有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 = {}

面試官:這段代碼執行完,棧堆空間是怎樣的?

8.png

  • 首先:o1、o2都是Object構造函數的實例對象,那實例對象必定有__proto__屬性。

  • 其次:實例對象的__proto__隱式原型屬性指向構造函數顯式原型屬性的值。

  • 最終:可以看到圖中o1、o2的__proto__屬性與Object的prototype都指向同一個值Object.prototype

3.5 構造函數/原型/實體對象的關系(圖解二)

面試官:相信我,最后一道了。(說出來你可能不信)

9.png

最后一道我們就輕松一點,請畫出function Foo(){ }原型鏈。

直接上圖。

10.png

⭐請允許我再多啰嗦兩句:

  • 可以看到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的顯式、隱式原型都指向自己?

11.png

  • 有隱式原型,那就是實例對象;有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這條線?

17.png

  • 隱式原型怎么有的?(構造函數的顯式原型給的)

  • 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的顯式原型屬性。

15.png

不太明白的童鞋可以再看看【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,如下圖:

14.png

大佬說我也不知道這個方法是干嘛的,我看別人都有加,最好還是加上吧。

也是笑了笑,其實就是為了嚴謹,確保我這個對象的屬性都是自定義添加的。

很多開發了很多年的大佬,說到底層原理尷尬的摸了摸頭。

不知道,不明了,不重要,重要的是不懂就要學,寫了不止八小時,好幾天拼湊起來的文章。

如果對您有幫助,你的點贊是我前進的潤滑劑。

以往推薦

尤大大說我的代碼全部不加分號

老濕說的萬物皆對象,你也信?

Vue-Cli3搭建組件庫

Vue實現動態路由(和面試官吹項目亮點)

項目中你不知道的Axios騷操作(手寫核心原理、兼容性)

VuePress搭建項目組件文檔

koa2+vue+nginx部署

vue-typescript-admin-template后台管理系統

原文鏈接

https://juejin.cn/post/7010942653915201543/


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM