javascript必知必會之prototype

作者: Tower Joo  來源: 博客園  發布時間: 2009-10-16 17:56  閱讀: 1511 次  推薦: 0   原文鏈接   [收藏]  

  摘要

  本系列博文主要談一些在 javascript 使用中經常會混淆的高級應用,包括: prototype, closure, scope, this關鍵字. 對于一個需要提高自己javascript水平的程序員,這些都是必須要掌握的.

  本節主要介紹prototype.

  起由

  最近在做一個項目,里面大量地使用 javascript 作為頁面的動態生成腳本, 使用 json 與服務器進行通信. 在讀之前遺留的代碼時, 經常會弄不清楚, 作用域, this關鍵字在當前context下的指向等,于是便開始專門學習了 相關的知識,記錄下來與大家分享.

  下面的內容中會有一些代碼,建議大家也去嘗試修改和理解,這樣更容易掌握.

  prototype

  javascript 是一種 prototype based programming 的語言, 而與我們通常的 class based programming 有很大 的區別,我列舉重要的幾點如下:

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

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

  看下面的代碼片斷:

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.

// 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的方法或者屬性,我們可以動態地增加, 而由其創建的 對象自動會繼承相關的方法和屬性.
  另外,每個對象都有一個 constructor 屬性,用于指向創建其的函數對象,如上例中的 fun.constructor 指向的 就是 Person.
  那么一個疑問就自然產生了, 函數對象中自身聲明的方法和屬性與prototype聲明的對象有什么差別?

  有下面幾個差別:

  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語言外衣的函數式語言 的理解自然也需要更多地思考.

0
0
 
 
 

文章列表

arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()