<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
在前端這塊領域,原型與原型鏈是每一個前端 er 必須掌握的概念。多次在面試或者一些技術部落格里面看見這個概念。由此可見,這個玩意對於前端來說有多重要。其實它本身理解起來不難,但是很多剛入行前端的同學,看到prototype
、__proto__
理解起來還是有點吃力,然後腦子裡面就亂成一鍋粥,就像我一樣。 但是這是很正常的事情,沒什麼大不了的,就像我們想要學會跑步,那麼我們就必須先學會走路。任何事情都是有個過程的。所以現在就跟我一起來攻克這個難點吧。
通過這篇文章你將掌握以下知識點:
__proto_
;prototype
;javascript
中物件
的概念;javascript
中類
的概念;new
的實現;instanceof
的實現;javascript
的繼承;javascript
這門語言的理解。這也是本篇文章的寫作思路。
那麼我們就從物件這一概念開始說起,其實物件這一概念相信大家並不陌生。有一種說法是“javasrcript 中萬物皆是物件”,其實這個說法是錯誤的,一個很簡單的例子,javasript
中簡單基本型別(string、boolean、number、null、undefined、symbol)本身就不是物件。其實javasript
中物件主要分為函數物件
和普通物件
。
其中:
String
Number
Boolean
Object
Function
Array
Date
RegExp
Error
這些都是函數物件,他們同時也被稱為內建物件
。函數物件
本身其實就是一個純函數,javascript
用他們來模擬類
。
普通物件就很簡單了,就是我們常見的物件:
const obj = { name: "juefei", desc: "cool", };
可能說到這,你還是無法理解到底啥是函數物件
,啥是普通物件
,那我們就一起來看看下面的程式碼:
const obj1 = {}; const obj2 = new Object(); function func1() {} const obj3 = new func1(); const func2 = new (function () {})(); const func3 = new Function();
接著我們來分別列印一下他們:
console.log(obj1); // object console.log(obj2); // object console.log(obj3); // object console.log(func1); // function console.log(func2); // function console.log(func3); // function
所以可以看見,obj1
、obj2
、,obj3
是普通物件,他們都是Object
的範例,而func1
、func2
、func3
則都是Function
的範例,稱為函數物件
。
我們再看看:
console.log(typeof Object); //f unction console.log(typeof Function); // function
你是不是驚呆了,原來Object
和Function
都是 Function
的範例。
所以我們得出一個結論就是:
Function
的範例,那就是函數物件
,其餘則為普通物件
。同樣我們也可以看出,不僅 Object
是函數物件
,就連 Function
本身也是函數物件,因為我們通過 console.log(typeof Function);
得知 Function
是 Function
的範例。是不是又開始有點繞了?沒事,到這一步你就記住我們剛剛的結論就算完成目標:
Function
的範例,那就是函數物件
,其餘則為普通物件
。那麼說到物件,我們從上面可以看出,一個物件是通過建構函式 new 出來的,這其實跟原型
和原型鏈
有很大的關係,那麼原型
和原型鏈
到底是用來幹嘛的呢?
原型
涉及到這兩個概念,我們就必須先來介紹兩個東西: __proto__
和prototype
,這兩個變數可以說,在 javascript
這門語言裡面隨處可見,我們不管他三七二十一,我們先來看一張表:
物件型別 | __proto__ | prototype |
---|---|---|
普通物件 | ✅ | ❌ |
函數物件 | ✅ | ✅ |
所以,請你先記住以下結論:
函數物件
有 prototype
屬性,普通物件
沒有這個屬性。函數物件
和普通物件
都有 __proto__
這個屬性。prototype
和 __proto__
都是在建立一個函數或者物件會自動生成的屬性。接著我們來驗證一下:
function func() { //func稱為建構函式 } console.log(typeof func.prototype); // object console.log(typeof func.__proto__); // function
const obj = {}; console.log(typeof obj.__proto__); //object console.log(typeof obj.prototype); //undefined (看見了吧,普通物件真的沒有 prototype 屬性)
所以就驗證了我們剛剛的結論:
函數物件
有prototype
屬性,普通物件
沒有這個屬性函數物件
和 普通物件
都有__proto__
這個屬性。prototype
和__proto__
都是在建立一個函數或者物件會自動生成的屬性。你看我又重複寫了一遍,我不是為了湊字數,是為了你加深記憶,這對於我們接下來的篇幅很重要。
接著我們來看看下面的程式碼:
console.log(obj.__proto__ === Object.prototype); // true console.log(func.__proto__ === Function.prototype); // true
所以我們又得出如下結論:
__proto__
屬性主動指向構造的 prototype
;prototype
屬性被__proto__
屬性 所指向。這就是prototype
屬性和__proto__
屬性的區別與聯絡。 這可能又有點繞了,來多看幾遍這一節,多背一下我們的結論。我們繼續。
那麼問題來了,既然func
是一個函數物件
,函數物件是有 prototype
屬性的,那麼func.prototype.__proto__
等於啥呢?
為了解決這個問題,我們來思考一下:
首先,我們看看func.prototype
是啥:
console.log(typeof func.prototype); //object
好,我們知道了,func.prototype
是一個物件,那既然是物件,那 func.prototype
那不就是 Object
的範例嗎?那也就是說,func.prototype.__proto__
屬性肯定是指向Object.prototype
咯! 好,我們來驗證一下:
console.log(func.prototype.__proto__ === Object.prototype); //true
看見沒有,就是這樣的。那看到這裡,我們應該也知道當我們這建立一個建構函式的時候,javascript 是如何幫我們自動生成__proto__
和prototype
屬性的。哈哈沒錯就是這樣:
//我們手動建立func函數 function func() {} //javascript悄悄咪咪執行以下程式碼: func._proto = Function.prototype; //範例的 __proto__ 屬性主動指向構造的 prototype func.prototype = { constructor: func, __proto: Object.prototype, //我們剛剛才在上面驗證的,你別又忘記了 };
我還專門為你畫了個圖(夠貼心不老鐵):
所以prototype
又被稱為顯式原型物件,而__proto__
又被稱為隱式原型物件。
hi,看到這裡,你是不是有種腦子開了光的感覺。哈哈,所以到現在你應該已經理解原型的概念了,如果你還不理解,那就把上述章節再看一遍。最好拿個紙筆出來跟著畫一畫,順便拿出電腦把範例程式碼敲一敲。好,整理一下頭腦,接下來我們來看看什麼又是原型鏈
。
再介紹這個概念之前,我們先來看如下程式碼:
function Person = function(name,desc){ this.name = name; this.desc = desc; } //***1****// Person.prototype.getName = function(){ return this.name; }//***2****// Person.prototype.getDesc = function(){ return this.desc; }//***3****// const obj = new Person('juefei','cool');//***4****// console.log(obj);//***5****// console.log(obj.getName);//***6****//
接下來我們來逐步解析一下:
Person
,此時,Person.portotype
自動建立,其中包含了constructor
和__proto__
兩個屬性;Person.prototype
新增了一個方法 getName
;Person.prototype
新增了一個方法 getDesc
;Person
新建一個範例: obj
(在建立範例的時候,建構函式會自動執行);obj
:{ name: 'juefei', desc: 'cool' }
根據上面一節的結論,我們得出:
obj.__proto__ = Person.prototype;
obj
上面找不到 getName()
這個方法,所以它就會自動去通過自身的 __proto__
繼續向上查詢,結果找到了Person.prototype
,接著它發現,剛好 Person.prototype
上面有getName()
方法,於是找到了這個方法,它就停止了尋找。 怎麼樣,是不是有一種環環相扣的感覺?他們形成一個鏈了,沒錯,這就是原型鏈
。我們得出如下結論:
在存取一個物件(假設這個物件叫 obj)的屬性/方法時,若在當前的物件上面找不到,則會嘗試通過obj.__proto__
去尋找,而 obj.__proto__
又指向其建構函式(假設叫objCreated
)的 prototype
,所以它又自動去 objCreated.prototype
的屬性/方法上面去找,結果還是沒找到,那麼就存取 objCreated.prototype.__proto__
繼續往上面尋找,直到找到,則停止對原型鏈對尋找,若最終還是沒能找到,則返回 undefined
。 一直沿著原型鏈尋找下去,直到找到 Object.prototype.__proto__
,指向 null
,於是返回 undefined
了。
是不是自然而然就理解了。我又給你畫了個圖:
接下來我們再來增加一些概念:
內建函數物件
本身的 __proto__
屬性都指向 Function
的原型物件,即: Function.prototype
;Object.prototype.__proto__
指向 null
,所有的內建函數物件
的原型物件的 __proto__
屬性 ( 內建函數物件.prototype.__proto__
),都指向Object
。我們得出如下終極原型鏈的圖:
針對這個圖,我最終給出我們經常看見那個原型鏈的圖:
好好對比一下,拿出紙和筆畫一畫,根據上面章節的講解,相信你很容易就能明白。
剛剛我們終於明白什麼是 原型
和 原型鏈
。下面我們根據上面的概念來講解一下javascript
中的類
。 我們知道,在物件導向的語言中,類可以被範例化
多次,這個範例化
是指我們可以根據建構函式去獨立複製
多個獨立的範例,這些範例之間是獨立的。但是實際上在 javascript
卻不是這樣的,因為它不是這種複製機制
。我們不能建立一個類的多個範例,我們只能建立這個類的多個物件,因為他們都是通過原型
和原型鏈
關聯到同一個物件。所以在 javascript
中 ,類
都是通過原型
和原型鏈
來實現的,它其實是一種委託方式
。
瞭解了上面javascript
中的類
的概念,那我們應該很容易就理解new
的過程,其核心無非就是執行原型鏈的連結:
function myNew(Cons, ...args) { let obj = {}; obj.__proto__ = Cons.prototype; //執行原型連結 let res = Cons.call(obj, args); return typeof res === "object" ? res : obj; }
那麼學習了原型
和原型鏈
,instanceof
的實現肯定也很簡單了,它也是通過原型
和原型鏈
來實現的:
function myInstanceof(left, right) { let rightProto = right.prototype; let leftValue = left.__proto__; while (true) { if (leftValue === null) { return false; } if (leftValue === rightProto) { return true; } leftValue = leftValue.__proto__; } }
我就不講解過程了,因為我知道你肯定能看懂,哈哈。
我們都知道繼承也是通過原型
和原型鏈
來實現的,那我在這裡介紹兩種常見的繼承方式:
組合繼承:
//組合式繼承 //通過call繼承Parent的屬性,並傳入引數 //將Child的原型物件指向Parent的範例,從而繼承Parent的函數 function Parent(value) { this.val = value; } Parent.prototype.getValue = function () { console.log(this.val); }; function Child(value) { Parent.call(this, value); //繼承Parentd的屬性 } Child.prototype = new Parent();
寄生組合式繼承:
//寄生組合式繼承 //通過call繼承Parent的屬性,並傳入引數 //通過Object.create()繼承Parent的函數 function Parent(value) { this.val = value; } Parent.prototype.getValue = function () { console.log(this.val); }; function Child(value) { //繼承Parentd的屬性 Parent.call(this, value); } Child.prototype = Object.create(Parent.prototype, { constructor: { value: Child, writable: true, configurable: true, enumerable: false, }, });
B.__proto__ = A.prototype
;B.a
,若在 B 中找不到 a,則會在B.__proto__
中,也就是A.prototype
中查詢,若A.prototype
中仍然沒有,則會繼續向上查詢,最終,一定會找到Object.prototype
,倘若還找不到,因為Object.prototype.__proto__
指向null
,因此會返回undefined
;Object.prototype.__proto__
——> null。由此可見,原型
和原型鏈
是如此的強大
到此這篇關於Javascript 原型與原型鏈深入詳解的文章就介紹到這了,更多相關Javascript 原型鏈內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45