摘要:不理解沒關系,下面會結合圖例分析上一篇高級程序設計筆記創建對象下一篇高級程序設計筆記繼承參考之原型鏈的解讀三張圖搞懂的原型對象與原型鏈繼承與原型鏈
文章直接從原型圖解開始的,如果對一些概念不太清除,可以結合后面幾節查看1. 圖解原型鏈 1.1 “鐵三角關系”(重點)
function Person() {}; var p = new Person();
這個圖描述了構造函數,實例對象和原型三者之間的關系,是原型鏈的基礎:
(1)實例對象由構造函數new產生;
(2)構造函數的原型屬性與實例對象的原型對象均指向原型
(3)原型對象中有一個屬性constructor指向對應的構造函數
原型鏈:p --> Person.prototype
描述:實例對象能夠訪問到 Person.prototype 中不同名的屬性和方法
驗證:
p instanceof Person; // true p.__proto__ === Person.prototype; // true Person.prototype.constructor === Person; // true1.2 以原型為構造函數
原型鏈:p --> Person.prototype --> Object.prototype --> null
描述:
(1)由于構造函數的原型也是對象,因此:它也有原型對象,指向Object.__proto__
(2)由于構造函數的原型的原型也是對象,因此:它也有原型對象,指向null(特例)
驗證:
p instanceof Person; // true p instanceof Object; // true Person.prototype instanceof Object; // true1.3 深入研究,引出Function構造函數
1、原型鏈1:見1.2中的原型
2、原型鏈2:Person --> Function.prototype --> Object.prototype --> null
描述:
(1)構造函數Person作為實例對象時,Person = new Function()隱式調用,因此Person --> Function.prototype
(2)由于Function.prototype也是對象,Function.prototype = new Object()隱式調用,因此Function.prototype --> Object.prototype
驗證:
Person instanceof Function; // true Person instanceof Object; // true Function.prototype instanceof Object; // true
3、原型鏈3:Function --> Function.prototype --> Object.prototype --> null
描述:
構造函數Function作為實例對象時,Function = new Function()隱式調用,因此Function --> Function.prototype
Function 這條原型鏈是最為特殊的“鐵三角關系”,理解Function = new Function()就非常好理解了
驗證:
Function.__proto__ === Function.prototype; // true Function instanceof Function; // true Function instanceof Object; // true1.4 完整的原型鏈
圖中新增了Object = new Function()的邏輯
驗證:
Object instanceof Function;// true
幾個結論:
(1)對象都有原型對象,對象默認繼承自其原型對象
(2)所有的函數都是 Function 的實例
(3)所有的原型鏈尾端都會指向Object.prototype
下面提幾個問題:
(1)上圖有幾條原型鏈?分別列出來(上面已給出)
(2)如何在代碼層面驗證原型鏈上的繼承關系?(見第四節)
(3)圖中有幾個“鐵三角”關系?分別列出來
當實例對象被創建時,其原型鏈就已經確定了,當其對應的原型屬性指向改變時,也無法改變原型鏈
function Person({name="小A", age=21}={}) { this.name = name; this.age = age; }; // 情況1:在修改原型屬性前實例化對象 var p1 = new Person(); // 添加原型屬性(方法) Person.prototype.sayName = function() { console.log(this.name); } // Person.prototype.SayHi = function() {} // 情況2:在修改原型屬性后實例化對象 var p2 = new Person(); p1.sayName(); // "小A" p2.sayName(); // "小A"
實例對象p1和實例對象p2的原型鏈相同,為 p1(p2) --> Person.prototype --> Object.prototype
=> 由于是在原有原型對象上添加的方法,相當于對象的擴展,故兩個實例對象均能執行該方法
function Person({name="小A", age=21}={}) { this.name = name; this.age = age; }; // 情況1:在修改原型屬性前實例化對象 var p1 = new Person(); // 重寫原型對象 Person.prototype = { sayName: function() { console.log(this.name); } } // 情況2:在修改原型屬性后實例化對象 var p2 = new Person(); p2.sayName(); // "小A" p1.sayName(); // p1.sayName is not a function
重寫原型對象的方式,會改變實例對象的原型鏈,如下圖所示:
但是,為什么p1的原型鏈沒有變,而p2的原型鏈變了呢?
當實例對象被創建時,其原型鏈就已經確定了,當其對應的原型屬性指向改變時,也無法改變原型鏈
原型鏈是以實例對象為核心的,不能被原型對象的改變而誤導
重寫原型對象的方式會在原型鏈繼承中經常使用到!!!
3. 對象與函數(重點)看到這里,我們可能已經分不清函數與對象了,思考30秒,函數與對象是什么關系?
官方定義: 在Javascript中,每一個函數實際上都是一個函數對象
function fn() {}; var obj = {}; fn instanceof Object; // true fn instanceof Function; // true obj instanceof Object; // true obj instanceof Function; // false
原型鏈解釋:
fn對應的原型鏈:fn --> Function.prototype --> Object.prototype
obj對應的原型鏈:obj --> Object.prototype
從函數的定義來說: 在javascript中一切函數實際都是函數對象,但對象不一定是函數
Function instanceof Object; // true Object instanceof Function; // true Function instanceof Function; // true
原型鏈解釋:
Function對應的原型鏈(Function作為實例對象):Function --> Function.prototype --> Object.prototype
Object對應的原型鏈(Object作為實例對象):Object --> Function.prototype --> Object.prototype
由于Function和Object都是構造函數,在內置對象中,均會調用new Function()的方法
結論:
(1)函數一定是對象,但是對象不一定是函數
(2)對象都是由函數來創建的
針對第一點,這兩個原型鏈可驗證:
fn --> Function.prototype --> Object.prototype
obj --> Object.prototype
針對第二點,可這樣驗證:
var obj = { a: 1, b: 2} var arr = [2, "foo", false] // 實際過程 var obj = new Object() obj.a = 1 obj.b = 2 var arr = new Array() arr[0] = 2 arr[1] = "foo" arr[2] = false //typeof Object === "function" //typeof Array === "function4. 幾個定義 4.1 原型的定義和作用
function Person() {}; var p = new Person();
構造函數的prototype屬性的值(Person.prototype),也可以說成通過調用構造函數而創建出來的那個實例對象的原型對象(p.__proto__)
只要是函數就有 prototype 屬性,即函數的原型屬性(由于對象是由函數創建的,因此對象也有prototype屬性)
函數的原型屬性也是對象(因此,這個對象也有對應的prototype屬性)
由構造函數創建出來的對象會默認鏈接到其構造函數的這個屬性上(constructor)
構造函數的 prototype 屬性的作用是:實現數據共享(繼承)
4.2 幾個術語實例對象中有一個屬性叫 __proto__ ,它是非標準屬性,指向構造函數的原型屬性
Person.prototype 構造函數的原型屬性
p.__proto__ 實例對象的原型對象
構造函數的原型屬性與實例對象的原型對象是一個東西,只是從不同的角度訪問原型
5. 屬性搜索原則和屬性來源判斷 5.1 屬性搜索原則(重點)當代碼讀取某個對象的某個屬性時,都會執行一次搜索,目標是具有給定名字的屬性。搜索先從對象實例本身開始,如果在實例中找到了具有給定名字的屬性,則返回該屬性的值;如果沒有找到,則繼續搜索指針指向的原型對象,在原型對象中查找具有給定名字的屬性。如果在原型對象中找到了這個屬性,則返回這個屬性,如果沒有找到,則繼續在這個原型對象的原型對象中查找,直到找到這個屬性,否則返回undefined
簡言之,沿著對象的原型鏈查找屬性,返回最近的屬性,這就是屬性搜索原則
function Person(){} Person.prototype.name = "Nicholas"; Person.prototype.age = 29; Person.prototype.job = "Software Engineer"; Person.prototype.sayName = function(){ alert(this.name); }; var person1 = new Person(); var person2 = new Person(); person1.name = "Greg"; alert(person1.name); //"Greg" 來自實例 alert(person2.name); //"Nicholas" 來自原型
同樣的,這也是屬性屏蔽的原則
// 接著上面的例子 delete person1.namel; alert(person1.name); // "Nicholas" 來自原型5.2 hasOwnProperty()方法與in操作符
使用hasOwnProperty()方法可以檢測一個屬性是存在于實例中,還是在原型中,這個方法只在給定屬性存在于對象實例中時,才會返回true
function Person(){} Person.prototype.name = "Nicholas"; Person.prototype.age = 29; Person.prototype.job = "Software Engineer"; Person.prototype.sayName = function(){ alert(this.name); }; var person1 = new Person(); var person2 = new Person(); alert(person1.hasOwnProperty("name")); //false person1.name = "Greg"; alert(person1.name); //"Greg" 來自實例 alert(person1.hasOwnProperty("name")); //true alert(person2.name); //"Nicholas" 來自原型 alert(person2.hasOwnProperty("name")); //false delete person1.name; alert(person1.name); //"Nicholas" 來自原型 alert(person1.hasOwnProperty("name")); //false
有兩種方式使用in操作符:多帶帶使用和在for-in循環中使用。在多帶帶使用時,in操作符會在通過對象能夠訪問給定屬性時返回true,無論該屬性存在于實例中還是原型中
因此,同時使用hasOwnProperty()和in操作符,就可以確定某個屬性到底是存在于對象中還是存在于原型中
function hasPrototypeProperty(object, name){ return !object.hasOwnProperty(name) && (name in object); }
順便一提,由于in操作符會在整個原型鏈上查找屬性,處于性能考慮,在使用for-in循環時,建議多加一層判別
function Person(){} Person.prototype.name = "Nicholas"; Person.prototype.age = 29; var p = new Person(); p.sex = "fale"; for(key in p) { console.log(key); // sex name age } // 實際上,我們一般只是在查找實例中的屬性 for(key in p) { if(p.hasOwnProperty(key)) { console.log(key); // sex 屏蔽了原型中的屬性 } }5.3 instanceof操作符
instanceof 用來判斷一個構造函數的prototype屬性所指向的對象是否存在另外一個要檢測對象的原型鏈上
更形象來說,對于 A instanceof B來說,它的判斷規則是:沿著A的__proto__這條線來找,同時沿著B的prototype這條線來找,如果兩條線能找到同一個引用,即同一個對象,那么就返回true。如果找到終點還未重合,則返回false。不理解沒關系,下面會結合圖例分析
function Person() {} var p = new Person(); console.log(p instanceof Object);//true console.log(p instanceof Person);//true
上一篇:《javascript高級程序設計》筆記:創建對象
下一篇:《javascript高級程序設計》筆記:繼承
參考:
JavaScript之原型鏈的解讀
三張圖搞懂JavaScript的原型對象與原型鏈
繼承與原型鏈
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/89571.html
摘要:繼承和前面兩篇文章中的知識非常相關,如果對函數創建原理和原型鏈不熟悉,請猛戳高級程序設計筆記創建對象高級程序設計筆記原型圖解繼承,通俗的說,就是將自身不存在的屬性或方法,通過某種方式為自己所用文章分別介紹原型鏈繼承繼承借用構造函數繼承組合繼 繼承和前面兩篇文章中的知識非常相關,如果對函數創建原理和原型鏈不熟悉,請猛戳:《javascript高級程序設計》筆記:創建對象《javascri...
摘要:上一篇你不知道的筆記寫在前面這是年第一篇博客,回顧去年年初列的學習清單,發現僅有部分完成了。當然,這并不影響年是向上的一年在新的城市穩定連續堅持健身三個月早睡早起游戲時間大大縮減,學會生活。 上一篇:《你不知道的javascript》筆記_this 寫在前面 這是2019年第一篇博客,回顧去年年初列的學習清單,發現僅有部分完成了。當然,這并不影響2018年是向上的一年:在新的城市穩定、...
摘要:因此,所有在方法中定義的變量都是放在棧內存中的當我們在程序中創建一個對象時,這個對象將被保存到運行時數據區中,以便反復利用因為對象的創建成本通常較大,這個運行時數據區就是堆內存。 上一篇:《javascript高級程序設計》筆記:繼承近幾篇博客都會圍繞著圖中的知識點展開 showImg(https://segmentfault.com/img/bVY0C4?w=1330&h=618);...
摘要:探索是如何判斷的表達式如果函數的顯式原型對象在對象的隱式原型鏈上,返回,否則返回是通過自己產生的實例案例案例重要注意的顯示原型和隱式原型是一樣的。面試題測試題測試題報錯對照下圖理解 原型與原型鏈深入理解(圖解) 原型(prototype) 函數的 prototype 屬性(圖) 每個函數都有一個prototype屬性,它默認指向一個Object空對象(即稱為:原型對象) 原型對象中有...
摘要:本期推薦文章從作用域鏈談閉包,由于微信不能訪問外鏈,點擊閱讀原文就可以啦。推薦理由這是一篇譯文,深入淺出圖解作用域鏈,一步步深入介紹閉包。作用域鏈的頂端是全局對象,在全局環境中定義的變量就會綁定到全局對象中。 (關注福利,關注本公眾號回復[資料]領取優質前端視頻,包括Vue、React、Node源碼和實戰、面試指導) 本周開始前端進階的第二期,本周的主題是作用域閉包,今天是第6天。 本...
閱讀 656·2021-11-11 16:55
閱讀 2164·2021-11-11 16:55
閱讀 1956·2021-11-11 16:55
閱讀 2345·2021-10-25 09:46
閱讀 1604·2021-09-22 15:20
閱讀 2287·2021-09-10 10:51
閱讀 1709·2021-08-25 09:38
閱讀 2623·2019-08-30 12:48