最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

javascript 面向?qū)ο笕吕砭氈^承與多態(tài)

 更新時(shí)間:2009年12月03日 19:18:22   作者:  
前面我們討論了如何在 JavaScript 語言中實(shí)現(xiàn)對私有實(shí)例成員、公有實(shí)例成員、私有靜態(tài)成員、公有靜態(tài)成員和靜態(tài)類的封裝。這次我們來討論一下面向?qū)ο蟪绦蛟O(shè)計(jì)中的另外兩個(gè)要素:繼承與多態(tài)。
1 又是幾個(gè)基本概念
為什么要說又呢?
在討論繼承時(shí),我們已經(jīng)列出了一些基本概念了,那些概念是跟封裝密切相關(guān)的概念,今天我們要討論的基本概念,主要是跟繼承與多態(tài)相關(guān)的,但是它們跟封裝也有一些聯(lián)系。
1.1 定義和賦值
變量定義是指用
var a;
這種形式來聲明變量。
函數(shù)定義是指用
function a(...) {...}
這種形式來聲明函數(shù)。
var a = 1;
是兩個(gè)過程。第一個(gè)過程是定義變量 a,第二個(gè)過程是給變量 a 賦值。
同樣
var a = function(...) {};
也是兩個(gè)過程,第一個(gè)過程是定義變量 a 和一個(gè)匿名函數(shù),第二個(gè)過程是把匿名函數(shù)賦值給變量 a。
變量定義和函數(shù)定義是在整個(gè)腳本執(zhí)行之前完成的,而變量賦值是在執(zhí)行階段完成的。
變量定義的作用僅僅是給所聲明的變量指明它的作用域,變量定義并不給變量初始值,任何沒有定義的而直接使用的變量,或者定義但沒有賦值的變量,他們的值都是 undefined。
函數(shù)定義除了聲明函數(shù)所在的作用域外,同時(shí)還定義函數(shù)體結(jié)構(gòu)。這個(gè)過程是遞歸的,也就是說,對函數(shù)體的定義包括了對函數(shù)體內(nèi)的變量定義和函數(shù)定義。
通過下面這個(gè)例子我們可以更明確的理解這一點(diǎn):
復(fù)制代碼 代碼如下:

alert(a);
alert(b);
alert(c);
var a = "a";
function a() {}
function b() {}
var b = "b";
var c = "c";
var c = function() {}
alert(a);
alert(b);
alert(c);

猜猜這個(gè)程序執(zhí)行的結(jié)果是什么?然后執(zhí)行一下看看是不是跟你想的一樣,如果跟你想的一樣的話,那說明你已經(jīng)理解上面所說的了。
這段程序的結(jié)果很有意思,雖然第一個(gè) alert(a) 在最前面,但是你會發(fā)現(xiàn)它輸出的值竟然是 function a() {},這說明,函數(shù)定義確實(shí)在整個(gè)程序執(zhí)行之前就已經(jīng)完成了。
再來看 b,函數(shù) b 定義在變量 b 之前,但是第一個(gè) alert(b) 輸出的仍然是 function b() {},這說明,變量定義確實(shí)不對變量做什么,僅僅是聲明它的作用域而已,它不會覆蓋函數(shù)定義。
最后看 c,第一個(gè) alert(c) 輸出的是 undefined,這說明 var c = function() {} 不是對函數(shù) c 定義,僅僅是定義一個(gè)變量 c 和一個(gè)匿名函數(shù)。
再來看第二個(gè) alert(a),你會發(fā)現(xiàn)輸出的竟然是 a,這說明賦值語句確實(shí)是在執(zhí)行過程中完成的,因此,它覆蓋了函數(shù) a 的定義。
第二個(gè) alert(b) 當(dāng)然也一樣,輸出的是 b,這說明不管賦值語句寫在函數(shù)定義之前還是函數(shù)定義之后,對一個(gè)跟函數(shù)同名的變量賦值總會覆蓋函數(shù)定義。
第二個(gè) alert(c) 輸出的是 function() {},這說明,賦值語句是順序執(zhí)行的,后面的賦值覆蓋了前面的賦值,不管賦的值是函數(shù)還是其它對象。
理解了上面所說的內(nèi)容,我想你應(yīng)該知道什么時(shí)候該用 function x(..) {…},什么時(shí)候該用 var x = function (…) {…} 了吧?
最后還要提醒一點(diǎn),eval 中的如果出現(xiàn)變量定義和函數(shù)定義,則它們是在執(zhí)行階段完成的。所以,不到萬不得已,不要用 eval!另外,即使要用 eval,也不要在里面用局部變量和局部方法!
1.2 this 和執(zhí)行上下文
在前面討論封裝時(shí),我們已經(jīng)接觸過 this 了。在對封裝的討論中,我們看到的 this 都是表示 this 所在的類的實(shí)例化對象本身。真的是這樣嗎?
先看一下下面的例子吧:
復(fù)制代碼 代碼如下:

var x = "I'm a global variable!";
function method() {
alert(x);
alert(this.x);
}
function class1() {
// private field
var x = "I'm a private variable!";
// private method
function method1() {
alert(x);
alert(this.x);
}
var method2 = method;
// public field
this.x = "I'm a object variable!";
// public method
this.method1 = function() {
alert(x);
alert(this.x);
}
this.method2 = method;
// constructor
{
this.method1(); // I'm a private variable!
// I'm a object variable!
this.method2(); // I'm a global variable!
// I'm a object variable!
method1(); // I'm a private variable!
// I'm a global variable!
method2(); // I'm a global variable!
// I'm a global variable!
method1.call(this); // I'm a private variable!
// I'm a object variable!
method2.call(this); // I'm a global variable!
// I'm a object variable!
}
}
var o = new class1();
method(); // I'm a global variable!
// I'm a global variable!
o.method1(); // I'm a private variable!
// I'm a object variable!
o.method2(); // I'm a global variable!
// I'm a object variable!

為什么是這樣的結(jié)果呢?
那就先來看看什么是執(zhí)行上下文吧。那什么是執(zhí)行上下文呢?
如果當(dāng)前正在執(zhí)行的是一個(gè)方法,則執(zhí)行上下文就是該方法所附屬的對象,如果當(dāng)前正在執(zhí)行的是一個(gè)創(chuàng)建對象(就是通過 new 來創(chuàng)建)的過程,則創(chuàng)建的對象就是執(zhí)行上下文。
如果一個(gè)方法在執(zhí)行時(shí)沒有明確的附屬于一個(gè)對象,則它的執(zhí)行上下文是全局對象(頂級對象),但它不一定附屬于全局對象。全局對象由當(dāng)前環(huán)境來決定。在瀏覽器環(huán)境下,全局對象就是 window 對象。
定義在所有函數(shù)之外的全局變量和全局函數(shù)附屬于全局對象,定義在函數(shù)內(nèi)的局部變量和局部函數(shù)不附屬于任何對象。
那執(zhí)行上下文跟變量作用域有沒有關(guān)系呢?
執(zhí)行上下文與變量作用域是不同的。
一個(gè)函數(shù)賦值給另一個(gè)變量時(shí),這個(gè)函數(shù)的內(nèi)部所使用的變量的作用域不會改變,但它的執(zhí)行上下文會變?yōu)檫@個(gè)變量所附屬的對象(如果這個(gè)變量有附屬對象的話)。
Function 原型上的 call 和 apply 方法可以改變執(zhí)行上下文,但是同樣不會改變變量作用域。
要理解上面這些話,其實(shí)只需要記住一點(diǎn):
變量作用域是在定義時(shí)就確定的,它永遠(yuǎn)不會變;而執(zhí)行上下文是在執(zhí)行時(shí)才確定的,它隨時(shí)可以變。
這樣我們就不難理解上面那個(gè)例子了。this.method1() 這條語句(注意,這里說的還沒有進(jìn)入這個(gè)函數(shù)體)執(zhí)行時(shí),正在創(chuàng)建對象,那當(dāng)前的執(zhí)行上下文就是這個(gè)正在創(chuàng)建的對象,所以 this 指向的也是當(dāng)前正在創(chuàng)建的對象,在 this.method1() 這個(gè)方法執(zhí)行時(shí)(這里是指進(jìn)入函數(shù)體),這個(gè)正在執(zhí)行的方法所附屬的對象也是這個(gè)正在創(chuàng)建的對象,所以,它里面 this.x 的 this 也是同一個(gè)對象,所以你看的輸出就是 I'm a object variable! 了。
而在執(zhí)行 method1() 這個(gè)函數(shù)時(shí)(是指進(jìn)入函數(shù)體后),method1() 沒有明確的附屬于一個(gè)對象,雖然它是定義在 class1 中的,但是他并沒有不是附屬于 class1 的,也不是附屬于 class1 實(shí)例化后的對象的,只是它的作用域被限制在了 class1 當(dāng)中。因此,它的附屬對象實(shí)際上是全局對象,因此,當(dāng)在它當(dāng)中執(zhí)行到 alert(this.x) 時(shí),this.x 就成了我們在全局環(huán)境下定義的那個(gè)值為 “I'm a global variable!” 的 x 了。
method2() 雖然是在 class1 中定義的,但是 method() 是在 class1 之外定義的,method 被賦值給 method2 時(shí),并沒有改變 method 的作用域,所以,在 method2 執(zhí)行時(shí),仍然是在 method 被定義的作用域內(nèi)執(zhí)行的,因此,你看到的就是兩個(gè) I'm a global variable! 輸出了。同樣,this.method2() 調(diào)用時(shí),alert(x) 輸出 I'm a global variable! 也是這個(gè)原因。
因?yàn)?call 會改變執(zhí)行上下文,所以通過 method1.call(this) 和 method2.call(this) 時(shí),this.x 都變成了 I'm a object variable!。但是它不能改變作用域,所以 x 仍然跟不使用 call 方法調(diào)用時(shí)的結(jié)果是一樣的。
而我們后面執(zhí)行 o.method1() 時(shí),alert(x) 沒有用 this 指出 x 的執(zhí)行上下文,則 x 表示當(dāng)前執(zhí)行的函數(shù)所在的作用域中最近定義的變量,因此,這時(shí)輸出的就是 I'm a private variable!。最后輸出 I'm a object variable! 我想不用我說大家也知道為什么了吧?
2 繼承和多態(tài)
2.1 從封裝開始
前面我們說了,封裝的目的是實(shí)現(xiàn)數(shù)據(jù)隱藏。
但是更深一層來說,在 javascript 中進(jìn)行封裝還有以下幾個(gè)好處:
1、隱身實(shí)現(xiàn)細(xì)節(jié),當(dāng)私有部分的實(shí)現(xiàn)完全重寫時(shí),并不需要改變調(diào)用者的行為。這也是其它面向?qū)ο笳Z言要實(shí)現(xiàn)封裝的主要目的。
2、javascript 中,局部變量和局部函數(shù)訪問速度更快,因此把私有字段以局部變量來封裝,把私有方法以局部方法來封裝可以提高腳本的執(zhí)行效率。
3、對于 javascript 壓縮混淆器(據(jù)我所知,目前最好的 javascript 分析、壓縮、混淆器就是 JSA)來說,局部變量和局部函數(shù)名都是可以被替換的,而全局變量和全局函數(shù)名是不可以被替換的(實(shí)際上,對于 javascript 腳本解析器工作時(shí)也是這樣的)。因此,不論對于開源還是非開源的 javascript 程序,當(dāng)私有字段和私有方法使用封裝技術(shù)后,編寫代碼時(shí)就可以給它們定義足夠長的表意名稱,增加代碼的可讀性,而發(fā)布時(shí),它們可以被替換為一些很短的名稱(一般是單字符名稱),這樣就可以得到充分的壓縮和混淆。及減少了帶寬占用,又可以真正實(shí)現(xiàn)細(xì)節(jié)的隱藏。
所以,封裝對于 javascript 來說,是非常有用的!
那么在 javascript 實(shí)現(xiàn)繼承是為了什么呢?
2.2 為什么要繼承
在其它面向?qū)ο蟪绦蛟O(shè)計(jì)語言中,繼承除了可以減少重復(fù)代碼的編寫外,最大的用處就是為了實(shí)現(xiàn)多態(tài)。尤其是在強(qiáng)類型語言中,尤為如此:
1、在強(qiáng)類型語言中,一個(gè)變量不能夠被賦予不同類型的兩個(gè)值,除非這兩種類型與這個(gè)變量的類型是相容的,而這個(gè)相容的關(guān)系就是由繼承來實(shí)現(xiàn)的。
2、在強(qiáng)類型語言中,對一個(gè)已有的類型無法直接進(jìn)行方法的擴(kuò)充和改寫,要擴(kuò)充一個(gè)類型,唯一的方法就是繼承它,在它的子類中進(jìn)行擴(kuò)充和改寫。
因此,對于強(qiáng)類型的面向?qū)ο笳Z言,多態(tài)的實(shí)現(xiàn)是依賴于繼承的實(shí)現(xiàn)的。
而對于 javascript 語言來說,繼承對于實(shí)現(xiàn)多態(tài)則顯得不那么重要:
1、在 javascript 語言中,一個(gè)變量可以被賦予任何類型的值,且可以用同樣的方式調(diào)用任何類型的對象上的同名方法。
2、在 javascript 語言中,可以對已有的類型通過原型直接進(jìn)行方法的擴(kuò)充和改寫。
所以,在 javascript 中,繼承的主要作用就是為了減少重復(fù)代碼的編寫。
接下來我們要談的兩種實(shí)現(xiàn)繼承的方法可能大家已經(jīng)都很熟悉了,一種是原型繼承法,一種是調(diào)用繼承法,這兩種方法都不會產(chǎn)生副作用。我們主要討論的是這兩種方法的本質(zhì)和需要注意的地方。
2.3 原型繼承法
在 javascript 中,每一個(gè)類(函數(shù))都有一個(gè)原型,該原型上的成員在該類實(shí)例化時(shí),會傳給該類的實(shí)例化對象。實(shí)例化的對象上沒有原型,但是它可以作為另一個(gè)類(函數(shù))的原型,當(dāng)以該對象為原型的類實(shí)例化時(shí),該對象上的成員就會傳給以它為原型的類的實(shí)例化對象上。這就是原型繼承的本質(zhì)。
原型繼承也是 javascript 中許多原生對象所使用的繼承方法。
復(fù)制代碼 代碼如下:

function parentClass() {
// private field
var x = "I'm a parentClass field!";
// private method
function method1() {
alert(x);
alert("I'm a parentClass method!");
}
// public field
this.x = "I'm a parentClass object field!";
// public method
this.method1 = function() {
alert(x);
alert(this.x);
method1();
}
}
parentClass.prototype.method = function () {
alert("I'm a parentClass prototype method!");
}
parentClass.staticMethod = function () {
alert("I'm a parentClass static method!");
}
function subClass() {
// private field
var x = "I'm a subClass field!";
// private method
function method2() {
alert(x);
alert("I'm a subClass method!");
}
// public field
this.x = "I'm a subClass object field!";
// public method
this.method2 = function() {
alert(x);
alert(this.x);
method2();
}
this.method3 = function() {
method1();
}
}
// inherit
subClass.prototype = new parentClass();
subClass.prototype.constructor = subClass;
// test
var o = new subClass();
alert(o instanceof parentClass); // true
alert(o instanceof subClass); // true
alert(o.constructor); // function subClass() {...}
o.method1(); // I'm a parentClass field!
// I'm a subClass object field!
// I'm a parentClass field!
// I'm a parentClass method!
o.method2(); // I'm a subClass field!
// I'm a subClass object field!
// I'm a subClass field!
// I'm a subClass method!
o.method(); // I'm a parentClass prototype method!
o.method3(); // Error!!!
subClass.staticMethod(); // Error!!!

上面這個(gè)例子很好的反映出了如何利用原型繼承法來實(shí)現(xiàn)繼承。

相關(guān)文章

  • javascript 面向?qū)ο缶幊袒A(chǔ):繼承

    javascript 面向?qū)ο缶幊袒A(chǔ):繼承

    "繼承是面向?qū)ο箝_發(fā)的又一個(gè)重要概念,它可以將現(xiàn)實(shí)生活的概念對應(yīng)帶程序邏輯中"?!? 雖然在JavaScript中沒有專門的機(jī)制來實(shí)現(xiàn)類的繼承,但可以通過拷貝一個(gè)類的prototype 到另外一個(gè)類來實(shí)現(xiàn)繼承”。
    2009-08-08
  • jabsorb筆記_幾個(gè)小例子

    jabsorb筆記_幾個(gè)小例子

    研究了一下 jabsorb,寫了幾個(gè)簡單的例子,希望能夠幫助菜鳥快速入門。
    2008-10-10
  • javascript中最常用的繼承模式 組合繼承

    javascript中最常用的繼承模式 組合繼承

    組合繼承避免了原型鏈和借用構(gòu)造函數(shù)的缺陷,成為JavaScript中最為常用的繼承模式
    2010-08-08
  • 關(guān)于js類的定義

    關(guān)于js類的定義

    這兩天幫同學(xué)做秒殺網(wǎng),打算將前臺和后臺徹底分離,僅用ajax通信。這樣就免不了大量的使用javascript,于是發(fā)現(xiàn),js果然是難學(xué)難用。
    2011-06-06
  • javascript面向?qū)ο缶幊檀a

    javascript面向?qū)ο缶幊檀a

    javascript面向?qū)ο缶幊檀a,學(xué)習(xí)js面向?qū)ο缶幊痰呐笥芽梢詤⒖枷隆?/div> 2011-12-12
  • JavaScript 繼承使用分析

    JavaScript 繼承使用分析

    繼承,通俗地說,之前你寫過一些類,這些類中有一些是而你現(xiàn)在要寫的類的功能的子集或者基本相同,那么你不用完全重新寫一個(gè)新的類,你可以把之前寫的類拿過來使用.這樣的一種代碼重用過程就叫做繼承
    2011-05-05
  • JS 繼承實(shí)例分析

    JS 繼承實(shí)例分析

    主要有三種方法: 1. this.method=Parent; this.method=Parent's constructor 2. Parent.call(this,arg,arg,arg.....);3.Parent.apply(this,arg.arg...) //for Array 還是來點(diǎn)實(shí)際的吧...
    2008-11-11
  • Riot.js 快速的JavaScript單元測試框架

    Riot.js 快速的JavaScript單元測試框架

    Riot是一個(gè)快速,富有表現(xiàn)力,上下文驅(qū)動 的單元測試框架。最初是用于Ruby的單元測試,最近作者Alex Young又實(shí)現(xiàn)了Riot的JavaScript版- Riot.js。
    2009-11-11
  • JavaScript 工具庫 Cloudgamer JavaScript Library v0.1 發(fā)布

    JavaScript 工具庫 Cloudgamer JavaScript Library v0.1 發(fā)布

    研究了一年多的js,也差不多寫一個(gè)自己的js庫了。 我寫這個(gè)不算框架,只是一個(gè)小型的js工具庫,所以我用的名字是Library。
    2009-10-10
  • Javascript 面向?qū)ο?對象(Object)

    Javascript 面向?qū)ο?對象(Object)

    Javascript 面向?qū)ο?對象(Object)
    2010-05-05

最新評論

亳州市| 双牌县| 司法| 兰坪| 江永县| 克拉玛依市| 西安市| 娱乐| 耿马| 略阳县| 改则县| 霍城县| 读书| 迭部县| 秭归县| 洛宁县| 登封市| 洛南县| 共和县| 德格县| 信阳市| 沈阳市| 历史| 丘北县| 九龙坡区| 高唐县| 文水县| 隆昌县| 丰镇市| 波密县| 政和县| 建湖县| 商南县| 宜春市| 石嘴山市| 临湘市| 榆社县| 长沙市| 广河县| 崇阳县| 临洮县|