JavaScript進(jìn)階(四)原型與原型鏈用法實(shí)例分析
本文實(shí)例講述了JavaScript原型與原型鏈用法。分享給大家供大家參考,具體如下:
一句話說明什么是原型:原型就是一個(gè)JavaScript對象,原型能存儲(chǔ)我們的方法,構(gòu)造函數(shù)創(chuàng)建出來的實(shí)例對象能夠引用原型中的方法。
一、傳統(tǒng)構(gòu)造函數(shù)的問題
有如下代碼
function Foo(){
this.sayHello = function(){
}
}
由于對象是調(diào)用new Foo()所創(chuàng)建出來的,因此每一個(gè)對象在創(chuàng)建的時(shí)候,函數(shù) sayHello 都會(huì)唄創(chuàng)建一次
那么有沒一個(gè)對象都含有一個(gè)獨(dú)立的,不同的,但是功能邏輯一樣的函數(shù),比如:{} == {}。
在代碼中方法就會(huì)消耗性能,最典型的資源就越是內(nèi)存
這里最好的方法就是將函數(shù)放在構(gòu)造函數(shù)之外,那么在構(gòu)造函數(shù)中引用該函數(shù)即可
function sayHello () {}
function Foo () {
this.say = sayHello;
}
會(huì)在開發(fā)中變得困難:引入框架危險(xiǎn),代碼繁冗不好維護(hù)。解決方法就是如果外面的函數(shù)不占用其名字,而且在函數(shù)名下。
每一個(gè)函數(shù)在定義的時(shí)候,有一個(gè)神秘對象(就是原型對象,暫且這么稱呼)被創(chuàng)建出來。
每一個(gè)由構(gòu)造函數(shù)創(chuàng)建的對象都會(huì)默認(rèn)的連接到該神秘對象上。
var f1 = new Foo(); var f2 = new Foo(); f1.sayHello(); //如果f1沒有sayHello那么就會(huì)在Foo.prototype中去找
由構(gòu)造函數(shù)創(chuàng)建出來的眾多對象共享一個(gè)對象就是:構(gòu)造函數(shù).prototype
只需要將共享的東西,重復(fù)會(huì)多占用內(nèi)存的東西放到構(gòu)造函數(shù).prototype中,那么所有的對象就可以共享了。
function Foo(){}
Foo.prototype.sayHello = function(){
console.log("….");
}
var f1 = new Foo();
f1.sayHello();
var f2 = new Foo();
f2.sayHello();
console.log(f1.sayHello === f2.sayHello); // true
二、一些相關(guān)概念
類class:在JS中就是構(gòu)造函數(shù)
- 在傳統(tǒng)的面向?qū)ο笳Z言中,使用一個(gè)叫類的東西定義模板,然后使用模板創(chuàng)建對象。
- 在構(gòu)造方法中也具有類似的功能,因此也稱其為類
實(shí)例(instance)與對象(object)
- 實(shí)例一般是指某一個(gè)構(gòu)造函數(shù)創(chuàng)建出來的對象,我們稱為XXXX 構(gòu)造函數(shù)的實(shí)例
- 實(shí)例就是對象。對象是一個(gè)泛稱
- 實(shí)例與對象是一個(gè)近義詞
鍵值對與屬性和方法
- 在JS中鍵值對的集合稱為對象
- 如果值為數(shù)據(jù)(非函數(shù)),就稱該鍵值對為屬性
- 如果值為函數(shù)(方法),就稱該鍵值對為方法method
父類與子類(基類和派生類)
- 傳統(tǒng)的面向?qū)ο笳Z言中使用類來實(shí)現(xiàn)繼承那么就有父類、子類的概念
- 父類又稱為基類,子類又稱為派生類
- 在JS中沒有類的概念,在JS中常常稱為父對象,子對象,基對象,派生對象。
三、認(rèn)識(shí)原型
在JavaScript中,原型也是一個(gè)對象,通過原型可以實(shí)現(xiàn)對象的屬性繼承,JavaScript的對象中都包含了一個(gè)[[Prototype]]內(nèi)部屬性,這個(gè)屬性所對應(yīng)的就是該對象的原型。
[[Prototype]]作為對象的內(nèi)部屬性,是不能被直接訪問的。所以為了方便查看一個(gè)對象的原型,F(xiàn)irefox和Chrome中提供了__proto__這個(gè)非標(biāo)準(zhǔn)(不是所有瀏覽器都支持)的訪問器(ECMA引入了標(biāo)準(zhǔn)對象原型訪問器"Object.getPrototype(object)")。
下面通過一個(gè)例子來看看原型相關(guān)概念:
function Person() {}
// 神秘對象就是Person.prototype
//那么只有使用構(gòu)造函數(shù)才可以訪問它
var o = new Person();
//以前不能直接使用o來訪問神秘對象
//現(xiàn)在有了__proto__后,
o.__proto__也可以直接訪問神秘對象
//那么o.__proto__ === Person.prototype
神秘對象(原型)中都有一個(gè)屬性constructor,翻譯為 構(gòu)造器 。表示該原型是與什么構(gòu)造函數(shù)聯(lián)系起來的。
__proto__有什么用?可以訪問原型。由于在開發(fā)中除非特殊要求,不要使用實(shí)例去修改原型的成員,因此該屬性開發(fā)時(shí)使用較少。但是在調(diào)試過程中非常方便,可以輕易的訪問原型進(jìn)行查看成員
如果在早期的瀏覽器中使用實(shí)例需要訪問原型如何處理?可以使用實(shí)例對象訪問構(gòu)造器,然后使用構(gòu)造器訪問原型
var o = new Person(); o.constructor.prototype
如果給實(shí)例繼承自原型的屬性賦值
function Foo(); Foo.prototype.name = "test"; var o1 = new Foo(); var o2 = new Foo(); o1.name = "張三"; // 不是修改原型中的name而是自己增加了一個(gè)name屬性 console.log(o1.name + ','+ o2.name); // 張三,test
四、構(gòu)造、原型、實(shí)例三角結(jié)構(gòu)圖
對于如下代碼:
function Person(){}
var p = new Person()
console.log(Person.prototype.constructor); //function Person(){}
console.log(Person.prototype.constructor.name); //Person
console.log(typeof Person.prototype.constructor); //function
console.log(p.__prop__);
console.log(p.__prop__ === Person.prototype);//true
于是他們的關(guān)系圖如下:

五、對象的原型鏈
凡是對象就有原型,原型也是對象。因此凡是給定一個(gè)對象,那么就可以找到他的原型,原型還有原型,那么如此下去,就構(gòu)成一個(gè)對象的序列,稱該結(jié)構(gòu)為原型鏈。
問題:
- 原型鏈到底到什么時(shí)候是一個(gè)頭?
- 一個(gè)默認(rèn)的原型鏈結(jié)構(gòu)是怎樣的?
- 原型鏈結(jié)構(gòu)對已知語法的修正
5.1 原型鏈的結(jié)構(gòu)
凡是使用構(gòu)造函數(shù),創(chuàng)建出對象,并且沒有利用賦值的方式修改原型,就說該對象保留默認(rèn)的原型鏈。
默認(rèn)原型鏈結(jié)構(gòu)是什么樣子呢?
function Person(){}
var p = new Person();
//p 具有默認(rèn)的原型鏈
默認(rèn)的原型鏈結(jié)構(gòu)就是:當(dāng)前對象 -> 構(gòu)造函數(shù).prototype -> Object.prototype -> null

在實(shí)現(xiàn)繼承的時(shí)候,有時(shí)候會(huì)利用替換原型鏈結(jié)構(gòu)的方式實(shí)現(xiàn)原型繼承,那么原型鏈結(jié)構(gòu)就會(huì)發(fā)送改變
function DunizbCollection(){}
DunizbCollection.prototype = [];
var arr = new DunizbCollection();
// arr -> [] -> Array.prototype -> Object.prototype -> null

六、函數(shù)的構(gòu)造函數(shù)Function
在JS中使用Function可以實(shí)例化函數(shù)對象 。也就是說在JS中函數(shù)與普通對象一樣,也是一個(gè)對象類型。函數(shù)是JS中的一等公民。
- 函數(shù)是對象,就可以使用對象的動(dòng)態(tài)特性
- 函數(shù)是對象,就有構(gòu)造函數(shù)創(chuàng)建函數(shù)
- 函數(shù)是對象,可以創(chuàng)建其它對象
- 函數(shù)是唯一可以限定變量作用域的結(jié)果
要解決的問題
- Function 如何使用
- Function 與函數(shù)的關(guān)系
- 函數(shù)的原型鏈結(jié)構(gòu)
6.1 函數(shù)是Function的實(shí)例
語法
new Function( arg0,arg1,arg1,….argN, body );
Function 中的參數(shù)全部是字符串
該構(gòu)造函數(shù)的作用是將參數(shù)鏈接起來組成函數(shù)
- 如果參數(shù)只有一個(gè),那么表示函數(shù)體
- 如果參數(shù)有多個(gè),最后一個(gè)參數(shù)表示函數(shù)體,前面的所有參數(shù)表示函數(shù)的參數(shù)
- 如果沒有參數(shù),表示創(chuàng)建一個(gè)空函數(shù)
舉例:創(chuàng)建一個(gè)打印一句話的函數(shù)
// 傳統(tǒng)的
function foo () {
console.log( '你好' );
}
//Function
var func = new Function( 'console.log( "你好" );' );
// 功能上,這里foo 與 func 等價(jià)
再比如,創(chuàng)建一個(gè)空函數(shù)
//傳統(tǒng)
function foo () {}
//Function
var func = new Function();
func();
傳入函數(shù)內(nèi)一個(gè)數(shù)字,打印該函數(shù)
//傳統(tǒng)
function foo ( num ) {
console.log( num );
}
//Function
var func = new Function( "num" ,"console.log( num )" );
func();
6.2 函數(shù)的原型鏈結(jié)構(gòu)
任意的一個(gè)函數(shù),都是相當(dāng)于Function的實(shí)例,類似于{}與new Object()的關(guān)系。
function foo () {}
上面的代告訴解析器,有一個(gè)對象叫foo,它是一個(gè)函數(shù);相當(dāng)于new Function()得到一個(gè)函數(shù)對象
- 函數(shù)應(yīng)該有什么屬性?答:
__proto__ - 函數(shù)的構(gòu)造函數(shù)是什么?答:Function
- 函數(shù)應(yīng)該繼承自Function.prototype
- Function.prototype繼承自O(shè)bject.prototype
對于Function,我們還必須知道
- Object函數(shù)是Function的一個(gè)實(shí)例
-
Object作為對象是繼承自Function.prototype的,又“Function.prototype”繼承自O(shè)bject.prototype
foo.prototype.__proto__ === Object.prototype // true
- Function是自己的構(gòu)造函數(shù)
- 在JS 中任何對象的老祖宗就是Object.prototype
- 在JS中任何函數(shù)的老祖宗就是Function.prototype
下面繪制出 Function 的構(gòu)造原型實(shí)例三角形結(jié)構(gòu)

6.3 為什么要使用Function?
Function是使用字符串構(gòu)建函數(shù),那么就可以在程序運(yùn)行過程中構(gòu)建函數(shù).
以前的函數(shù)必須一開始就寫好,再經(jīng)過預(yù)解析,一步一步的運(yùn)行
假定從服務(wù)器里拿到“[1,2,3,4,5]”,將數(shù)組形式的字符串轉(zhuǎn)換成數(shù)組對象
var arr = ( new Function( 'return ' + str + ' ;' ) )();
感興趣的朋友可以使用在線HTML/CSS/JavaScript代碼運(yùn)行工具:http://tools.jb51.net/code/HtmlJsRun測試上述代碼運(yùn)行效果。
更多關(guān)于JavaScript相關(guān)內(nèi)容可查看本站專題:《JavaScript常用函數(shù)技巧匯總》、《javascript面向?qū)ο笕腴T教程》、《JavaScript錯(cuò)誤與調(diào)試技巧總結(jié)》、《JavaScript數(shù)據(jù)結(jié)構(gòu)與算法技巧總結(jié)》及《JavaScript數(shù)學(xué)運(yùn)算用法總結(jié)》
希望本文所述對大家JavaScript程序設(shè)計(jì)有所幫助。
- 圖解JS原型和原型鏈實(shí)現(xiàn)原理
- 一文秒懂JavaScript構(gòu)造函數(shù)、實(shí)例、原型對象以及原型鏈
- JS原形與原型鏈深入詳解
- javascript 原型與原型鏈的理解及應(yīng)用實(shí)例分析
- JS原型和原型鏈原理與用法實(shí)例詳解
- JavaScript原型繼承和原型鏈原理詳解
- JavaScript對象原型鏈原理解析
- JS學(xué)習(xí)筆記之原型鏈和利用原型實(shí)現(xiàn)繼承詳解
- JavaScript中構(gòu)造函數(shù)與原型鏈之間的關(guān)系詳解
- 深入淺析js原型鏈和vue構(gòu)造函數(shù)
- JavaScript原型鏈與繼承操作實(shí)例總結(jié)
- 詳解JavaScript原型與原型鏈
相關(guān)文章
利用ES6實(shí)現(xiàn)單例模式及其應(yīng)用詳解
單例是在程序設(shè)計(jì)非?;A(chǔ)的東西,這篇文章主要給大家介紹了關(guān)于利用ES6實(shí)現(xiàn)單例模式及其應(yīng)用的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考借鑒,下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧。2017-12-12
分頁欄的web標(biāo)準(zhǔn)實(shí)現(xiàn)
分頁欄是網(wǎng)頁上最常見不過的一個(gè)組件,本博文給出分頁欄的一個(gè)web2.0標(biāo)準(zhǔn)示例,并作簡要分析2011-11-11
JavaScript實(shí)現(xiàn)文字與圖片拖拽效果的方法
這篇文章主要介紹了JavaScript實(shí)現(xiàn)文字與圖片拖拽效果的方法,涉及javascript操作文字與圖片的技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-02-02
基于JavaScript實(shí)現(xiàn)的折半查找算法示例
這篇文章主要介紹了基于JavaScript實(shí)現(xiàn)的折半查找算法,結(jié)合實(shí)例形式分析了折半查找的原理、操作步驟及javascript實(shí)現(xiàn)折半查找的相關(guān)操作技巧與注意事項(xiàng),需要的朋友可以參考下2017-04-04
微信JS SDK接入的幾點(diǎn)注意事項(xiàng)(必看篇)
下面小編就為大家?guī)硪黄⑿臞S SDK接入的幾點(diǎn)注意事項(xiàng)(必看篇)。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-06-06
javascript自定義滾動(dòng)條實(shí)現(xiàn)代碼
這篇文章主要為大家詳細(xì)介紹了javascript自定義滾動(dòng)條實(shí)現(xiàn)代碼,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-02-02

