原型鏈做爲js中的一座大山,算是面試過程當中的必問之一;理解好原型鏈,對於學習js也有很大的幫助。面試
咱們先來看看它的定義瀏覽器
當js在一個對象上查找屬性的時候,首先查找對象自己的屬性(即聲明時候定義的屬性),若是在這些屬性中沒有找到目標屬性,那麼js會找它的__proto__
對象,若是這個對象沒有,那麼繼續找__proto__
對象的__proto__
對象,直到__proto__
爲null
或者找到目標屬性爲止...... 這個__proto__的關係鏈,就是咱們說的原型鏈
其實原型鏈真的很簡單,你只要記住下面這三個點就完了。函數
__proto__
屬性prototype
屬性,這個屬性值也是一個對象__protp__
都會指向它的構造函數的prototype
俗話說實踐出真知,咱們一步步來驗證這幾個特性.學習
你們能夠分段複製個人代碼粘貼到瀏覽器控制檯中this
let obj = {name:"leelei"}; console.log(obj.__proto__); let arr = [1,2,3] console.log(arr.__proto__); let str = "http://www.leelei.info"; console.log(str.__proto__); let num = 100; console.log(num.__proto__);
當你所有打印完之後,你會發現一個問題,誒,個人str和num不是基礎類型嗎?不是對象也有proto
屬性?prototype
這裏咱們要提一下js的隱式轉化規則了,當你訪問基礎類型的一些屬性或者調用一些方法的時候,你的基礎類型會被js給包裝起來,就像下面這樣code
str.__proto__ => new String(str).__proto__; num.__proto__ => new Number(num).__proto__;
那麼這個new String
和new Number
哪裏來的噶?對象
它們都是js原生的構造函數,總共有如下幾種原型鏈
咱們如今已經驗證了第一點了,每一個對象(基礎類型是經過原生構造函數轉成了對象)都有__proto__屬性
請一行一行復制如下代碼到控制檯中get
console.log(Object.prototype); console.log(Number.prototype); console.log(String.prototype); console.log(Boolean.prototype); console.log(RegExp.prototype); console.log(Error.prototype); console.log(Function.prototype); console.log(Array.prototype); console.log(Date.prototype);
若是咱們不是構造函數呢
function fn(){} console.log(fn.prototype);
能夠看到,不管是構造函數,仍是咱們聲明的函數都有prototype
的屬性。
區別只是原生的構造函數他們的prototype
對象上已經自帶了一些方法和屬性。
那麼綜上咱們能夠驗證第二個特性,每個函數都有
prototype
屬性
咱們只要判斷下第一步的__proto__是否指向第二步對應構造函數的prototype便可。
let obj = {name:"leelei"}; obj.__proto__ === Object.prototype; //true let arr = [1,2,3] arr.__proto__ === Array.prototype; //true let str = "http://www.leelei.info"; str.__proto__ === String.prototype; //true let num = 100; num.__proto__ === Number.prototype; //true
結果已經顯而易見了,第三個論點也可獲得論證
咱們根據定義來感覺一下這個鏈條
let obj = {name:"leelei"}; console.log(obj.__proto.valueOf); //有這個方法嗷 obj.valueOf(); //{name:"leelei"};
咱們能夠看到,在咱們聲明obj的時候並無這個屬性方法,可是咱們卻能夠調用,咱們打印了obj的__proto__對象,發現這裏存在這個方法,因此能夠調用。
這裏就是定義所講的當對象自己沒有這個屬性的時候,咱們會去它的__proto__對象上找
這裏才只有一層噶?若是隔多幾層會不會調用不了了阿?
咱們能夠編寫一個構造函數,而後給它的原型對象prototype添加自定義方法來試驗。
function Foo(age){this.age = age}; Foo.prototype.say = function(){console.log('hello')}; let foo = new Foo(18);
根據特性2
,對象的__proto__對象指向構造函數的prototype,因此這樣是沒什麼問題的
foo.say() => foo.__proto__.say() //hello 至關於=> Foo.prototype.say() //hello
那咱們還能不能使用Object上面的方法呢?固然能夠
foo.valueOf() => foo.__proto__.valueOf 至關於=> Foo.prototype.valueOf 怎麼沒有嗷? //第一層找不到喔,那就往上一層 //不要忘了prototype也是對象! //因此它也有__proto__屬性 ==> Foo.prototype.__proto__.valueOf 至關於==> Object.prototype.valueOf 找到啦
你可能會有點好奇爲何這兩個會等於?Foo.prototype.__proto__ === Object.prototype
由於Foo.prototype
是對象,Foo
是函數
Foo.__proto__ === Function.prototype; Foo.prototype.__proto__ === Object.prototype;
typeof (Function.prototype); //function
這個竟然不是對象?!驚了
根據特性2
,每一個函數都有一個prototype屬性。
既然Function.prototype
不是對象而是方法,那麼它也有prototype屬性纔對
Function.prototype.prototype //undefined
驚了!它竟然沒有prototype屬性!
莫慌,除這個以外,都是適用的。記住這個特例便可。
其實原型鏈並不複雜,可是單純簡單記憶的話確實比較難記,咱們能夠本身在控制檯多搗鼓一下,按照它的定義來走,更方便咱們理解和記憶。
謝謝你們,若是有錯誤,請在評論區指出。
打個廣告,李雷的博客