1. 程式人生 > 程式設計 >JavaScript進階(四)原型與原型鏈用法例項分析

JavaScript進階(四)原型與原型鏈用法例項分析

本文例項講述了JavaScript原型與原型鏈用法。分享給大家供大家參考,具體如下:

一句話說明什麼是原型:原型就是一個JavaScript物件,原型能儲存我們的方法,建構函式創建出來的例項物件能夠引用原型中的方法。

一、傳統建構函式的問題

有如下程式碼

function Foo(){
 this.sayHello = function(){
  }
}

由於物件是呼叫new Foo()所創建出來的,因此每一個物件在建立的時候,函式 sayHello 都會唄建立一次

那麼有沒一個物件都含有一個獨立的,不同的,但是功能邏輯一樣的函式,比如:{} == {}

在程式碼中方法就會消耗效能,最典型的資源就越是記憶體

這裡最好的方法就是將函式放在建構函式之外,那麼在建構函式中引用該函式即可

function sayHello () {}
function Foo () {
 this.say = sayHello;
}

會在開發中變得困難:引入框架危險,程式碼繁冗不好維護。解決方法就是如果外面的函式不佔用其名字,而且在函式名下。

每一個函式在定義的時候,有一個神祕物件(就是原型物件,暫且這麼稱呼)被創建出來。

每一個由建構函式建立的物件都會預設的連線到該神祕物件上。

var f1 = new Foo();
var f2 = new Foo();
f1.sayHello(); //如果f1沒有sayHello那麼就會在Foo.prototype中去找

由建構函式創建出來的眾多物件共享一個物件就是:建構函式.prototype

只需要將共享的東西,重複會多佔用記憶體的東西放到建構函式.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

二、一些相關概念

類class:在JS中就是建構函式

  • 在傳統的面嚮物件語言中,使用一個叫類的東西定義模板,然後使用模板建立物件。
  • 在構造方法中也具有類似的功能,因此也稱其為類

例項(instance)與物件(object)

  • 例項一般是指某一個建構函式創建出來的物件,我們稱為XXXX 建構函式的例項
  • 例項就是物件。物件是一個泛稱
  • 例項與物件是一個近義詞

鍵值對與屬性和方法

  • 在JS中鍵值對的集合稱為物件
  • 如果值為資料(非函式),就稱該鍵值對為屬性
  • 如果值為函式(方法),就稱該鍵值對為方法method

父類與子類(基類和派生類)

  • 傳統的面嚮物件語言中使用類來實現繼承那麼就有父類、子類的概念
  • 父類又稱為基類,子類又稱為派生類
  • 在JS中沒有類的概念,在JS中常常稱為父物件,子物件,基物件,派生物件。

三、認識原型

在JavaScript中,原型也是一個物件,通過原型可以實現物件的屬性繼承,JavaScript的物件中都包含了一個[[Prototype]]內部屬性,這個屬性所對應的就是該物件的原型。

[[Prototype]]作為物件的內部屬性,是不能被直接訪問的。所以為了方便檢視一個物件的原型,Firefox和Chrome中提供了__proto__這個非標準(不是所有瀏覽器都支援)的訪問器(ECMA引入了標準物件原型訪問器"Object.getPrototype(object)")。

下面通過一個例子來看看原型相關概念:

function Person() {}
// 神祕物件就是Person.prototype
//那麼只有使用建構函式才可以訪問它
var o = new Person();
//以前不能直接使用o來訪問神祕物件
//現在有了__proto__後,
o.__proto__也可以直接訪問神祕物件
//那麼o.__proto__ === Person.prototype

神祕物件(原型)中都有一個屬性constructor,翻譯為 構造器 。表示該原型是與什麼建構函式聯絡起來的。

__proto__有什麼用?可以訪問原型。由於在開發中除非特殊要求,不要使用例項去修改原型的成員,因此該屬性開發時使用較少。但是在除錯過程中非常方便,可以輕易的訪問原型進行檢視成員

如果在早期的瀏覽器中使用例項需要訪問原型如何處理?可以使用例項物件訪問構造器,然後使用構造器訪問原型

var o = new Person();
o.constructor.prototype

如果給例項繼承自原型的屬性賦值

function Foo();
Foo.prototype.name = "test";
var o1 = new Foo();
var o2 = new Foo();
o1.name = "張三"; // 不是修改原型中的name而是自己增加了一個name屬性
console.log(o1.name + ','+ o2.name); // 張三,test

四、構造、原型、例項三角結構圖

對於如下程式碼:

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

於是他們的關係圖如下:

JavaScript進階(四)原型與原型鏈用法例項分析

五、物件的原型鏈

凡是物件就有原型,原型也是物件。因此凡是給定一個物件,那麼就可以找到他的原型,原型還有原型,那麼如此下去,就構成一個物件的序列,稱該結構為原型鏈。

問題:

  1. 原型鏈到底到什麼時候是一個頭?
  2. 一個預設的原型鏈結構是怎樣的?
  3. 原型鏈結構對已知語法的修正

5.1 原型鏈的結構

凡是使用建構函式,創建出物件,並且沒有利用賦值的方式修改原型,就說該物件保留預設的原型鏈。

預設原型鏈結構是什麼樣子呢?

function Person(){}
var p = new Person();
//p 具有預設的原型鏈

預設的原型鏈結構就是:當前物件 -> 建構函式.prototype -> Object.prototype -> null

JavaScript進階(四)原型與原型鏈用法例項分析

在實現繼承的時候,有時候會利用替換原型鏈結構的方式實現原型繼承,那麼原型鏈結構就會發送改變

function DunizbCollection(){}
DunizbCollection.prototype = [];
var arr = new DunizbCollection();
// arr -> [] -> Array.prototype -> Object.prototype -> null

JavaScript進階(四)原型與原型鏈用法例項分析

六、函式的建構函式Function

在JS中使用Function可以例項化函式物件 。也就是說在JS中函式與普通物件一樣,也是一個物件型別。函式是JS中的一等公民。

  1. 函式是物件,就可以使用物件的動態特性
  2. 函式是物件,就有建構函式建立函式
  3. 函式是物件,可以建立其它物件
  4. 函式是唯一可以限定變數作用域的結果

要解決的問題

  1. Function 如何使用
  2. Function 與函式的關係
  3. 函式的原型鏈結構

6.1 函式是Function的例項

語法

new Function( arg0,arg1,….argN,body );

Function 中的引數全部是字串

該建構函式的作用是將引數連結起來組成函式

  • 如果引數只有一個,那麼表示函式體
  • 如果引數有多個,最後一個引數表示函式體,前面的所有引數表示函式的引數
  • 如果沒有引數,表示建立一個空函式

舉例:建立一個列印一句話的函式

// 傳統的
function foo () {
 console.log( '你好' );
}
//Function
var func = new Function( 'console.log( "你好" );' );
// 功能上,這裡foo 與 func 等價

再比如,建立一個空函式

//傳統
function foo () {}
//Function
var func = new Function();
func();

傳入函式內一個數字,列印該函式

//傳統
function foo ( num ) {
 console.log( num );
}
//Function
var func = new Function( "num","console.log( num )" );
func();

6.2 函式的原型鏈結構

任意的一個函式,都是相當於Function的例項,類似於{}與new Object()的關係。

function foo () {}

上面的代告訴解析器,有一個物件叫foo,它是一個函式;相當於new Function()得到一個函式物件

  • 函式應該有什麼屬性?答:__proto__
  • 函式的建構函式是什麼?答:Function
  • 函式應該繼承自Function.prototype
  • Function.prototype繼承自Object.prototype

對於Function,我們還必須知道

  • Object函式是Function的一個例項
  • Object作為物件是繼承自Function.prototype的,又“Function.prototype”繼承自Object.prototype

    foo.prototype.__proto__ === Object.prototype // true
  • Function是自己的建構函式
  • 在JS 中任何物件的老祖宗就是Object.prototype
  • 在JS中任何函式的老祖宗就是Function.prototype

下面繪製出 Function 的構造原型例項三角形結構

JavaScript進階(四)原型與原型鏈用法例項分析

6.3 為什麼要使用Function?

Function是使用字串構建函式,那麼就可以在程式執行過程中構建函式.

以前的函式必須一開始就寫好,再經過預解析,一步一步的執行

假定從伺服器裡拿到“[1,2,3,4,5]”,將陣列形式的字串轉換成陣列物件

var arr = ( new Function( 'return ' + str + ' ;' ) )();

感興趣的朋友可以使用線上HTML/CSS/JavaScript程式碼執行工具:http://tools.jb51.net/code/HtmlJsRun測試上述程式碼執行效果。

更多關於JavaScript相關內容可檢視本站專題:《JavaScript常用函式技巧彙總》、《javascript面向物件入門教程》、《JavaScript錯誤與除錯技巧總結》、《JavaScript資料結構與演算法技巧總結》及《JavaScript數學運算用法總結》

希望本文所述對大家JavaScript程式設計有所幫助。