javascript
《javascript高级程序设计》笔记:原型图解
1. 圖解原型鏈
1.1 “鐵三角關系”(重點)
function Person() {}; var p = new Person();
這個圖描述了構造函數,實例對象和原型三者之間的關系,是原型鏈的基礎:
(1)實例對象由構造函數new產生;
(2)構造函數的原型屬性與實例對象的原型對象均指向原型
(3)原型對象中有一個屬性constructor指向對應的構造函數
原型鏈:p --> Person.prototype
描述:實例對象能夠訪問到 Person.prototype 中不同名的屬性和方法
驗證:
1.2 以原型為構造函數
原型鏈:p --> Person.prototype --> Object.prototype --> null
描述:
(1)由于構造函數的原型也是對象,因此:它也有原型對象,指向Object.__proto__
(2)由于構造函數的原型的原型也是對象,因此:它也有原型對象,指向null(特例)
驗證:
1.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
驗證:
3、原型鏈3:Function --> Function.prototype --> Object.prototype --> null
描述:
構造函數Function作為實例對象時,Function = new Function()隱式調用,因此Function --> Function.prototype
Function 這條原型鏈是最為特殊的“鐵三角關系”,理解Function = new Function()就非常好理解了
驗證:
1.4 完整的原型鏈
圖中新增了Object = new Function()的邏輯
驗證:
Object instanceof Function;// true幾個結論:
(1)對象都有原型對象,對象默認繼承自其原型對象
(2)所有的函數都是 Function 的實例
(3)所有的原型鏈尾端都會指向Object.prototype
下面提幾個問題:
(1)上圖有幾條原型鏈?分別列出來(上面已給出)
(2)如何在代碼層面驗證原型鏈上的繼承關系?(見第四節)
(3)圖中有幾個“鐵三角”關系?分別列出來
2. 原型鏈改寫(重點)
當實例對象被創建時,其原型鏈就已經確定了,當其對應的原型屬性指向改變時,也無法改變原型鏈 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; // trueobj 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; // trueFunction 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 = 2var 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__)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")); //falseperson1.name = "Greg"; alert(person1.name); //"Greg" 來自實例 alert(person1.hasOwnProperty("name")); //truealert(person2.name); //"Nicholas" 來自原型 alert(person2.hasOwnProperty("name")); //falsedelete 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的原型對象與原型鏈
繼承與原型鏈
總結
以上是生活随笔為你收集整理的《javascript高级程序设计》笔记:原型图解的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: inotify_add_watch使用注
- 下一篇: C++ STL 常用查找算法