JavaScript prototype原型鏈介紹

javascript 是一種基於原型的編程 (prototype based programming) 的語言, 而與咱們一般的基於類編程 (class based programming) 有很大的區別,我列舉重要的幾點以下:javascript

  1. 函數是first class object,也就是說函數與對象具備相同的語言地位
  2. 沒有類,只有對象
  3. 函數也是一種對象,所謂的函數對象
  4. 對象是按引用來傳遞的

那麼這種 prototype based programming 的語言如何實現繼承呢(OO的一大基本要素), 這也即是 prototype 的由來。java

看下面的代碼片段:web

function foo(a, b, c)
{
	return a*b*c;
}
alert(foo.length);
alert(typeof foo.constructor);
alert(typeof foo.call);
alert(typeof foo.apply);
alert(typeof foo.prototype);  

對於上面的代碼,用瀏覽器運行後你會發現:編程

  1. length: 提供的是函數的參數個數
  2. prototype: 是一個object
  3. 其它三個都是function

而對於任何一個函數的聲明,它都將會具備上面所述的5個property(方法或者屬性)。瀏覽器

下面咱們主要看下prototype。app

// prototype
function Person(name, gender)
{
	this.name = name;
	this.gender = gender;
	this.whoAreYou = function()
    {//這個也是所謂的closure, 內部函數能夠訪問外部函數的變量
	var res = "I'm " + this.name + " and I'm a " + this.gender +".";
	return res;
	};
}
// 那麼在由Person建立的對象便具備了下面的幾個屬性
Person.prototype.age = 24;
Person.prototype.getAge = function()
{
	return this.age;
};
flag = true;
if (flag)
{
	var fun = new Person("Tower", "male");
	alert(fun.name);
	alert(fun.gender);
	alert(fun.whoAreYou());
	alert(fun.getAge());
}
Person.prototype.salary = 10000;
Person.prototype.getSalary = function()
{
	return this.name + " can earn about " + this.salary + "RMB each month." ;
};
// 下面就是最神奇的地方, 咱們改變了Person的prototype,而這個改變是在建立fun以後
// 而這個改變使得fun也具備了相同的屬性和方法
// 繼承的意味即此
if (flag)
{
	alert(fun.getSalary());
	alert(fun.constructor.prototype.age);//而這個至關於你直接調用 Person.prototype.age
	alert(Person.prototype.age);
}  

 從上面的示例中咱們能夠發現,對於prototype的方法或者屬性,咱們能夠動態地增長, 而由其建立的對象自動會繼承相關的方法和屬性。ide

另外,每一個對象都有一個 constructor 屬性,用於指向建立其的函數對象,如上例中的 fun.constructor 指向的就是 Person。函數

那麼一個疑問就天然產生了, 函數對象中自身聲明的方法和屬性與prototype聲明的對象有什麼差異?post

有下面幾個差異:this

  1. 自身聲明的方法和屬性是靜態的,也就是說你在聲明後,試圖再去增長新的方法或者修改已有的方法,並不會對由其建立的對象產生影響,也即繼承失敗。
  2. 而prototype能夠動態地增長新的方法或者修改已有的方法,從而是動態的一旦父函數對象聲明瞭相關的prototype屬性,由其建立的對象會自動繼承這些prototype的屬性。

繼續上面的例子:

flag = true;
// 函數內部聲明的方法是靜態的,沒法傳遞的
Person.school = "ISCAS";
Person.whoAreYou = function(){
return "zhutao";
};//動態更改聲明期的方法,並不會影響由其建立的對象的方法, 即所謂的靜態
if (flag)
{
	alert(Person.school);
	alert(fun.school);//輸出的是 "undefined"
	alert(Person.whoAreYou()); //輸出 zhutao
	alert(fun.whoAreYou()); // I'm Tower and I'm a male.
}
Person.prototype.getSalary = function(){
return "I can earn 1000000 USD";
};
if (flag)
{
	alert(fun.getSalary());//已經繼承了改變, 即所謂的動態
}  

既然有函數對象自己的屬性,也有prototype的屬性,那麼是由其建立的對象是如何搜索相應的屬性的呢?

基本是按照下面的流程和順序來進行。

  1. 先去搜索函數對象自己的屬性,若是找到當即執行
  2. 若是1沒有找到,則會去搜索prototype屬性,有2種結果,找到則直接執行,不然繼續搜索父對象的父對象的prototype,直至找到,或者到達 prototype chain 的結尾(結尾會是Object對象)

上面也回答若是函數對象自己的屬性與prototype屬性相同(重名)時的解決方式,函數自己的對象優先。

再看一個多重prototype鏈的例子:

// 多重prototype鏈的例子
function Employee(name)
{
	this.name = "";
	this.dept = "general";
	this.gender = "unknown";
}
function WorkerBee()
{
	this.projects = [];
	this.hasCar = false;
}
WorkerBee.prototype = new Employee; // 第一層prototype鏈
function Engineer()
{
	this.dept = "engineer"; //覆蓋了 "父對象"
	this.language = "javascript";
}
Engineer.prototype = new WorkerBee; // 第二層prototype鏈
var jay = new Engineer("Jay");
if (flag)
{
	alert(jay.dept);    //engineer, 找到的是本身的屬性
	alert(jay.hasCar);  // false, 搜索到的是本身上一層的屬性
	alert(jay.gender);  // unknown, 搜索到的是本身上二層的屬性
}  

 上面這個示例的對象關係以下:

javascript 的prototype給語言自己增長了很強的靈活性,但與 class based programming 相比整個思惟邏輯仍是有很大的不一樣,因此須要更多地思考和揣摩。

而javascript是披着c語言外衣的函數式語言的理解天然也須要更多地思考。

window.onload = function()
{   
	/*  
	每一個對象實例都有個屬性成員用於指向到它的instanceof 對象(暫稱爲父對象)的原型(prototype)  
	咱們把這種層層指向父原型的關係稱爲[原型鏈 prototype chian]  
	原型也具備父原型,由於它每每也是一個對象實例,除非咱們人爲地去改變它  
	在JavaScript中,"一切都是對象,函數是第一型。"  
	Function和Object都是函數的實例。  
	Function的父原型指向到Function的原型,Function.prototype的父原型是Object的原型  
	Object的父原型也指向到Function的原型,Object.prototype是全部父原型的頂層  
	在spiderMonkey引擎中,父原型能夠經過 __proto__ 進行訪問  
  
	你們在看的時候最後能反覆的讀幾篇,能加深理解,尤爲是原型,父原型,還有原型鏈的意思.  
           
	*  prototype  訪問的是原型  
	*  __proto__  訪問的父原型  
	*  instanceof 原型鏈的父類     
	*/   
	Function.prototype.hi = function(){alert('hi Function');}   
	Object.prototype.hi = function(){alert('hi Object');}   
       
	var a = function()
    {   
		this.txt = 'a';   
	};   
  
	a.prototype = {   
		say:function(){alert('a');}   
	};   
        
	alert(a instanceof Function);//a是Function的實例;   
	alert(a.__proto__ === Function.prototype);   
	
    //a的父原型指向到Function的原型;   
	//a.__proto__父原型 Function   
	//Function.prototype 父原型 Function   
	alert(Function instanceof Object);   
	
    //Function是Object的實例;   
	alert(Function.__proto__ === Function.prototype);   
   
	//Function的父原型指向到Function的原型;   
	alert(Function.prototype.__proto__ === Object.prototype);   
    
	//Function的原型的父原型指向到Object的原型   
	alert(Object.__proto__ === Function.prototype);   
    
	//Object的父原型指向到Function的原型;   
	alert(Object.prototype.__proto__);   
	//Object的原型是全部父原型的頂端,它再也不具備父原型,因此結果爲null;       
	alert(a.prototype instanceof Object);   
	//a的原型也是一個對象   
	alert(a.prototype.__proto__ === Object.prototype);   
	//a的原型的父原型指向Object的原型   
};   

轉載隨意,但請帶上本文地址:

http://www.nowamagic.net/librarys/veda/detail/1238

相關文章
相關標籤/搜索