學習javascript面向?qū)ο?理解javascript原型和原型鏈
先看一張圖,梳理梳理。
一、基本概念
【原型鏈】每個構(gòu)造函數(shù)都有一個對象,原型對象都包含一個指向構(gòu)造函數(shù)的指針,而實例都包含一個指向原型對象的內(nèi)部指針。那么,如果原型對象等于另一個原型的實例,此時的原型對象將包含一個指向另一個原型的指針,相應地,另一個原型中也包含著一個指向另一個構(gòu)造函數(shù)的指針。如果另一個原型又是另一個原型的實例,那么上述關(guān)系依然成立。如此層層遞進,就構(gòu)成了實例與原型的鏈條。
【原型對象】這個對象包含可以由特定類型的所有實例共享的屬性和方法。所有引用類型默認都繼承了Object,而這個繼承也是通過原型鏈實現(xiàn)的。所有函數(shù)的默認原型都是Object的實例,因此默認原型都會包含一個內(nèi)部指針,指向Object.prototype,這也正是所有自定義類型都會繼承toString()、valueOf()方法的原因
【構(gòu)造函數(shù)】構(gòu)造函數(shù)與其他函數(shù)的區(qū)別在于調(diào)用它們的方式不同。一般來說,函數(shù)只要通過new操作符來調(diào)用,那它就可以作為構(gòu)造函數(shù);如果不通過new操作符來調(diào)用,那它跟普通函數(shù)也不會有什么兩樣。
[注意]用戶自定義的函數(shù)和javascript中內(nèi)置的構(gòu)造函數(shù)可以當成構(gòu)造函數(shù)使用
【構(gòu)造函數(shù)的寫法】構(gòu)造函數(shù)始終應該以一個大寫字母開頭,而非構(gòu)造函數(shù)以一個小寫字母開頭。這個做法借鑒自其他OO語言,主要是為了區(qū)別于ECMAScript中的其他函數(shù);因為構(gòu)造函數(shù)本身也是函數(shù),只不過可以用來創(chuàng)建對象而已
【構(gòu)造函數(shù)的三種使用場景】
[a]當作構(gòu)造函數(shù)使用
var person = new Person("Nicholas",29,"software Engineer"); person.sayName();
[b]當作普通函數(shù)調(diào)用
Person("greg",27,"doctor");//添加到window window.sayName();//"Greg"
[c]在另一個對象的作用域中調(diào)用
var o = new Object(); Person.call(o,"Kristen",25,"Nurse"); o.sayName();//"Kristen"
【prototype屬性】只要創(chuàng)建了一個新函數(shù),就會根據(jù)一組特定的規(guī)則為該函數(shù)創(chuàng)建一個prototype屬性,這個屬性指向函數(shù)的原型對象。
[注意]只有函數(shù)才有prototype屬性,object沒有prototype屬性
【constructor屬性】在默認情況下,所有原型對象都會自動獲得一個constructor(構(gòu)造函數(shù))屬性,這個屬性包含一個指向prototype屬性所在函數(shù)的指針
[注意]創(chuàng)建了自定義的構(gòu)造函數(shù)之后,其原型對象默認只會取得constructor屬性,至于其他方法則都是從Object繼承而來的
【_proto_和[[prototype]]】當調(diào)用構(gòu)造函數(shù)創(chuàng)建一個新實例后,該實例的內(nèi)部將包含一個指針(內(nèi)部屬性),指向構(gòu)造函數(shù)的原型對象。ECMA-262第5版管這個指針叫[[prototype]]。雖然在腳本中標準的方式訪問[[prototype]],但firefox\safari\chrome在每個對象上都支持一個屬性_proto_;而在其他實現(xiàn)中,這個屬性對腳本則是完全不可見的。這個連接存在于實例與構(gòu)造函數(shù)的原型對象之間,而不是存在于實例與構(gòu)造函數(shù)之間
二、基本操作
【原型鏈查詢】每當代碼讀取某個對象的某個屬性時,都會執(zhí)行一次搜索,目標是具有給定名字的屬性。搜索首先從對象實例本身開始,如果在實例中找到了具有給定名字的屬性,則返回該屬性的值;如果沒有找到,則繼續(xù)搜索指針指向的原型對象,在原型對象中查找具有給定名字的屬性,如果找到了這個屬性,則返回該屬性的值。
【添加實例屬性】當為對象實例添加一個屬性時,這個屬性就會屏蔽原型對象中保存的同名屬性;換句話說,添加這個屬性只會阻止我們訪問原型中的那個屬性,但不會修改那個屬性,即使將這個屬性設置為null,也只會在實例中設置這個屬性,而不會恢復其指向原型的連接。不過,使用delete操作符則可以完全刪除實例屬性,從而讓我們能夠重新訪問原型中的屬性。
【原型的動態(tài)性】由于在原型中查找值的過程是一次搜索,因此我們對原型對象所做的任何修改都能立即從實例上反映出來,即使是先創(chuàng)建了實例后修改原型也照樣如此。
[注意]不推薦在產(chǎn)品化的程序中修改原生對象的原型
function Person(){}; var friend = new Person(); Person.prototype.sayHi = function(){ alert('hi'); } friend.sayHi();//"hi"
【重寫原型】調(diào)用構(gòu)造函數(shù)時會為實例添加一個指向最初原型的[[prototype]]指針,而把原型修改為另外一個對象就等于切斷了構(gòu)造函數(shù)與最初原型之間的聯(lián)系。實例中的指針僅指向原型,而不指向構(gòu)造函數(shù)。
三、基本方法
[1]isPrototypeOf():判斷實例對象和原型對象是否存在于同一原型鏈中,只要是原型鏈中出現(xiàn)過的原型,都可以說是該原型鏈所派生的實例的原型
function Person(){}; var person1 = new Person(); var person2 = new Object(); console.log(Person.prototype.isPrototypeOf(person1));//true console.log(Object.prototype.isPrototypeOf(person1));//true console.log(Person.prototype.isPrototypeOf(person2));//false console.log(Object.prototype.isPrototypeOf(person2));//true
[2]ECMAScript5新增方法Object.getPrototypeOf():這個方法返回[[Prototype]]的值
function Person(){}; var person1 = new Person(); var person2 = new Object(); console.log(Object.getPrototypeOf(person1)); //Person{} console.log(Object.getPrototypeOf(person1) === Person.prototype); //true console.log(Object.getPrototypeOf(person1) === Object.prototype); //false console.log(Object.getPrototypeOf(person2)); //Object{}
[3]hasOwnProperty():檢測一個屬性是否存在于實例中
function Person(){ Person.prototype.name = 'Nicholas'; } var person1 = new Person(); //不存在實例中,但存在原型中 console.log(person1.hasOwnProperty("name"));//false //不存在實例中,也不存在原型中 console.log(person1.hasOwnProperty("no"));//false person1.name = 'Greg'; console.log(person1.name);//'Greg' console.log(person1.hasOwnProperty('name'));//true delete person1.name; console.log(person1.name);//"Nicholas" console.log(person1.hasOwnProperty('name'));//false
[4]ECMAScript5的Object.getOwnPropertyDescriptor():只能用于取得實例屬性的描述符,要取得原型屬性的描述符,必須直接在原型對象上調(diào)用Object.getOwnPropertyDescription()方法
function Person(){ Person.prototype.name = 'Nicholas'; } var person1 = new Person(); person1.name = 'cook'; console.log(Object.getOwnPropertyDescriptor(person1,"name"));//Object {value: "cook", writable: true, enumerable: true, configurable: true} console.log(Object.getOwnPropertyDescriptor(Person.prototype,"name"));//Object {value: "Nicholas", writable: true, enumerable: true, configurable: true}
[5]in操作符:在通過對象能夠訪問給定屬性時返回true,無論該屬性存在于實例還是原型中
function Person(){} var person1 = new Person(); person1.name = 'cook'; console.log("name" in person1);//true console.log("name" in Person.prototype);//false var person2 = new Person(); Person.prototype.name = 'cook'; console.log("name" in person2);//true console.log("name" in Person.prototype);//true
[6]同時使用hasOwnProperty()方法和in操作符,來確定屬性是否存在于實例中
//hasOwnProperty()返回false,且in操作符返回true,則函數(shù)返回true,判定是原型中的屬性 function hasPrototypeProperty(object,name){ return !object.hasOwnProperty(name) && (name in object); } function Person(){ Person.prototype.name = 'Nicholas'; } var person1 = new Person(); console.log(hasPrototypeProperty(person1,'name'));//true person1.name = 'cook'; console.log(hasPrototypeProperty(person1,'name'));//false delete person1.name; console.log(hasPrototypeProperty(person1,'name'));//true delete Person.prototype.name; console.log(hasPrototypeProperty(person1,'name'));//false
[7]ECMAScript5的Object.keys()方法:接收一個對象作為參數(shù),返回一個包含所有可枚舉屬性的字符串數(shù)組
[注意]一定要先new出實例對象再使用該方法,否則為空
function Person(){ Person.prototype.name = 'Nicholas'; Person.prototype.age = 29; Person.prototype.job = 'Software Engineer'; Person.prototype.sayName = function(){ alert(this.name); } }; var keys = Object.keys(Person.prototype); console.log(keys);//[] var p1 = new Person(); p1.name = "Rob"; p1.age = 31; var keys = Object.keys(Person.prototype); console.log(keys);//["name","age","job","sayName"] var p1Keys = Object.keys(p1); console.log(p1Keys);//["name","age"]
[8]ECMAScript5的Object.getOwnPropertyNames()方法:接收一個對象作為參數(shù),返回一個包含所有屬性的字符串數(shù)組
[注意]一定要先new出實例對象再使用該方法,否則只有constructor
function Person(){ Person.prototype.name = 'Nicholas'; Person.prototype.age = 29; Person.prototype.job = 'Software Engineer'; Person.prototype.sayName = function(){ alert(this.name); } }; var keys = Object.getOwnPropertyNames(Person.prototype); console.log(keys);//["constructor"] var p1 = new Person(); var keys = Object.getOwnPropertyNames(Person.prototype); console.log(keys);//["constructor", "name", "age", "job", "sayName"]
希望本文所述對大家學習javascript程序設計有所幫助。
相關(guān)文章
把文本中的URL地址轉(zhuǎn)換為可點擊鏈接的JavaScript、PHP自定義函數(shù)
這篇文章主要介紹了把文本中的URL地址轉(zhuǎn)換為可點擊鏈接的JavaScript、PHP自定義函數(shù),需要的朋友可以參考下2014-07-07JavaScript encodeURI 和encodeURIComponent
encodeURI和encodeURIComponet函數(shù)都是javascript中用來對URI進行編碼,將相關(guān)參數(shù)轉(zhuǎn)換成UTF-8編碼格式的數(shù)據(jù)。URI在進行定位跳轉(zhuǎn)時,參數(shù)里面的中文、日文等非ASCII編碼都會進行編碼轉(zhuǎn)換2015-12-12JavaScript進階教程之函數(shù)的定義、調(diào)用及this指向問題詳解
這篇文章主要給大家介紹了關(guān)于JavaScript進階教程之函數(shù)的定義、調(diào)用及this指向問題的相關(guān)資料,文中通過實例代碼介紹的非常詳細,對大家學習或者使用js具有一定的參考學習價值,需要的朋友可以參考下2022-09-09JavaScript學習筆記之取值函數(shù)getter與取值函數(shù)setter詳解
這篇文章主要介紹了JavaScript取值函數(shù)getter與取值函數(shù)setter,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2022-08-08動態(tài)加載外部javascript文件的函數(shù)代碼分享
動態(tài)加載外部javascript文件的函數(shù)代碼分享,做個記錄備忘,方便查找。2011-07-07JS實現(xiàn)常見的TAB、彈出層效果(TAB標簽,斑馬線,遮罩層等)
這篇文章主要介紹了JS實現(xiàn)常見的TAB、彈出層效果,包括TAB標簽,斑馬線,遮罩層等.以完整實例總結(jié)分析了JavaScript實現(xiàn)tab切換、隔行變換及彈出遮罩層的完整實現(xiàn)技巧,需要的朋友可以參考下2015-10-10