JavaScript面向?qū)ο缶幊倘腴T教程
盡管面向?qū)ο驤avaScript與其他語言相比之下存在差異,并由此引發(fā)了一些爭(zhēng)論,但毋庸置疑,JavaScript具有強(qiáng)大的面向?qū)ο缶幊棠芰?br>
本文先從介紹面向?qū)ο缶幊涕_始,然后回顧JavaScript對(duì)象模型,最后演示JavaScript中的面向?qū)ο缶幊谈拍睢?/P>
JavaScript回顧
如果你對(duì)諸如變量(variables)、類型(types)、函數(shù)(functions)、以及作用域(scope)等JavaScript概念覺得心里沒底,那么你可以閱讀重新介紹JavaScript中的這些主題。你還可以查閱JavaScript 1.5核心指南
面向?qū)ο缶幊?/STRONG>
面向?qū)ο缶幊淌且环N編程范式(paradigm),即使用抽象來創(chuàng)建基于真實(shí)世界的模型。它使用了幾種以前建立的范式技術(shù),包括模塊化(modularity)、多態(tài)(polymorphism)、和封裝(encapsulation)。今天,許多流行的編程語言(比如Java、JavaScript、C#、C++、Python、PHP、Ruby、以及Objective-C)都支持面向?qū)ο缶幊蹋∣OP)。
面向?qū)ο缶幊炭梢暈槭褂脜f(xié)作對(duì)象集合來進(jìn)行軟件設(shè)計(jì),這與傳統(tǒng)觀點(diǎn)相反,傳統(tǒng)觀點(diǎn)把程序視為函數(shù)集合,或者簡化為計(jì)算機(jī)指令列表。在面向?qū)ο缶幊讨?,每個(gè)對(duì)象都具有以下能力:接收消息、處理數(shù)據(jù)、以及給其他對(duì)象發(fā)送消息。每個(gè)對(duì)象都可以視為一個(gè)獨(dú)立的具有不同角色或責(zé)任的小機(jī)器。
面向?qū)ο缶幊讨荚跒榫幊烫嵘蟮撵`活性和可維護(hù)性,并在大規(guī)模軟件工程中廣泛流行。由于其非常重視模塊化,因此面向?qū)ο蟠a旨在讓開發(fā)更簡單、稍后理解起來更容易,而且相對(duì)于較少采用模塊化的編程方法,使得對(duì)于復(fù)雜情況及步驟的分析、編碼和理解更加直接。
專用術(shù)語
類(Class)
~ 定義對(duì)象的特征(characteristics)。
對(duì)象(Object)
~ 類的實(shí)例(Instance)。
屬性(Property)
~ 某一對(duì)象特征(characteristic),例如顏色。
方法(Method)
~ 某種對(duì)象能力,例如行走。
構(gòu)造函數(shù)(Constructor)
~ 實(shí)例化(instantiation)時(shí)所調(diào)用的方法。
繼承(Inheritance)
~ 一個(gè)類可以繼承來自另一個(gè)類的特征。
封裝(Encapsulation)
~ 一個(gè)類只定義該對(duì)象的特征,一個(gè)方法只定義該方法如何執(zhí)行。
抽象(Abstraction)
~ 將某一對(duì)象的復(fù)雜繼承、方法、屬性結(jié)合在一起,而且必須能夠模擬某一現(xiàn)實(shí)模型。
多態(tài)(Polymorphism)
~ 不同類可能會(huì)定義相同的方法或?qū)傩浴?BR>對(duì)于面向?qū)ο缶幊痰倪M(jìn)一步描述,參閱維基百科的面向?qū)ο缶幊淘~條。
基于原型的編程
基于原型的編程(Prototype-based programming)是一種面向?qū)ο缶幊田L(fēng)格,其中類(classes)并不存在,并且行為重用(在基于類的語言中稱為繼承)是通過粉飾充當(dāng)原型的現(xiàn)存對(duì)象來完成的。這種模式也稱為無類的(class-less)、面向原型的(prototype-oriented)、或基于實(shí)例(instance-based)的編程。
關(guān)于基于原型語言的最初(且非常規(guī)范的)示例就是由David Ungar和Randall Smith開發(fā)的Self編程語言。然而,這種無類編程風(fēng)格最近越來越受歡迎,并且已被一些編程語言采用,例如avaScript、Cecil、NewtonScript、Io、MOO、REBOL、Kevo、Squeak(當(dāng)使用Viewer框架來操縱Morphic組件時(shí))、及其他幾種語言。
JavaScript面向?qū)ο缶幊?/STRONG>
核心對(duì)象(Core Objects)
JavaScript有幾個(gè)包含在其核心中的對(duì)象;例如,Math、Object、Array、以及String等對(duì)象。下面的示例演示了如何使用Math對(duì)象的random()方法獲取隨機(jī)數(shù)。
alert(Math.random());
提示:本例和所有其他示例都假設(shè)已在全局范圍內(nèi)定義了函數(shù)名alert(正如包含在web瀏覽器中的alert一樣)。alert函數(shù)實(shí)際上不是JavaScript本身的一部分。
JavaScript核心對(duì)象列表,參閱JavaScript 1.5核心參考:全局對(duì)象(Global Objects)。
JavaScript中的每個(gè)對(duì)象都是一個(gè)Object對(duì)象的實(shí)例,并因此繼承其所有屬性和方法。
自定義對(duì)象(Custom Objects)
類(The Class)
JavaScript是基于原型的語言,其中不包含可在如 C++或Java中找到的類聲明(class statement)。有時(shí)這會(huì)讓一些習(xí)慣于具有類聲明語言(languages with a class statement)的程序員感到困惑。不過,JavaScript用函數(shù)(functions)作為類。定義一個(gè)類簡單到就是定義一個(gè)函數(shù)。在下例中,我們定義了名為Person(人)的新類。
對(duì)象(類實(shí)例)(The Object (Class Instance))
要?jiǎng)?chuàng)建obj對(duì)象的一個(gè)新實(shí)例,我們使用語句new obj,同時(shí)將結(jié)果(其類型是obj)賦給某個(gè)變量(variable),以便稍后訪問。
在下例中,我們首先定義名為Person的類,然后創(chuàng)建兩個(gè)實(shí)例(person1和person2)。
var person1 = new Person();
var person2 = new Person();
還可參閱新的實(shí)例化替代方法Object.create。
構(gòu)造函數(shù)(The Constructor)
當(dāng)實(shí)例化時(shí)(創(chuàng)建對(duì)象實(shí)例的瞬間)是會(huì)調(diào)用構(gòu)造函數(shù)。構(gòu)造函數(shù)是類的一個(gè)方法。而在JavaScript中,會(huì)函數(shù)(function)作為作為該對(duì)象的構(gòu)造函數(shù);因此,也就無需顯式定義一個(gè)構(gòu)造函數(shù)方法。類中聲明的每個(gè)行為在實(shí)例化時(shí)都會(huì)執(zhí)行。
構(gòu)造函數(shù)用于設(shè)置對(duì)象屬性或調(diào)用方法為使用該對(duì)象做準(zhǔn)備。本文稍后會(huì)介紹,通過使用一種不同的語法來添加類方法及其定義 。
在下例中,當(dāng)實(shí)例化Person時(shí),Person類的構(gòu)造函數(shù)會(huì)顯示一個(gè)警告框。
alert('Person instantiated');
}
var person1 = new Person();
var person2 = new Person();
屬性(對(duì)象屬性)(The Property (object attribute))
屬性是包含在類中的變量;每個(gè)對(duì)象實(shí)例都有這些屬性。屬性應(yīng)設(shè)置在類(函數(shù))的原型(prototype)屬性中,以便繼承正常工作。
在類中操作屬性是通過this關(guān)鍵字實(shí)現(xiàn)的,this引用當(dāng)前對(duì)象。在類外部訪問(讀或?qū)懀┠硞€(gè)屬性要通過以下語法:InstanceName.Property;這與C++、Java、以及其他一些語言所用語法相同。(在類內(nèi)部使用this.Property的語法來獲取或設(shè)置屬性值)。
在下例中,我們?yōu)镻erson類定義gender(性別)屬性,然后在初始化時(shí)定義該屬性。
this.gender = gender;
alert('Person instantiated');
}
var person1 = new Person('Male'); // Male: 男
var person2 = new Person('Female'); // Female: 女
//顯示person1的性別
alert('person1 is a ' + person1.gender); // person1 is a Male
方法(The methods)
方法遵循與屬性相同的邏輯;區(qū)別在于它們是函數(shù)而且被定義為函數(shù)。調(diào)用方法與訪問屬性相似,不過你要在方法名末尾添加(),可能會(huì)有參數(shù)(arguments)。定義一個(gè)方法,就是為該類prototype屬性上的某個(gè)命名屬性指定一個(gè)函數(shù);函數(shù)被分配到的那個(gè)名稱就是在對(duì)象上調(diào)用該方法的名稱。
在下例中,我們?yōu)镻erson類定義并使用sayHello()方法。
this.gender = gender;
alert('Person instantiated');
}
Person.prototype.sayHello = function() {
alert('hello');
};
var person1 = new Person('Male');
var person2 = new Person('Female'); // 調(diào)用Person的sayHello方法。
person1.sayHello(); // hello
在JavaScript中,方法是作為屬性被綁定到某個(gè)類/對(duì)象的普通函數(shù)對(duì)象,這意味著,可以“脫離上下文(out of the context)”來調(diào)用它們。考慮如下示例代碼:
function Person(gender) {
this.gender = gender;
}
Person.prototype.sayGender = function() {
alert(this.gender);
};
var person1 = new Person('Male');
var genderTeller = person1.sayGender;
person1.sayGender(); // alerts 'Male'
genderTeller(); // alerts undefined
alert(genderTeller === person1.sayGender); // alerts true
alert(genderTeller === Person.prototype.sayGender); // alerts true
此示例一次演示了多個(gè)概念。這表明,在JavaScript中沒有“基于對(duì)象的方法(per-object methods)”,因?yàn)樵摲椒ǖ乃幸枚贾赶蛲耆嗤暮瘮?shù),即我們起初在原型上定義的那個(gè)函數(shù)。當(dāng)某個(gè)函數(shù)被作為方法(或確切地說是屬性)調(diào)用時(shí),JavaScript會(huì)將當(dāng)前的“對(duì)象上下文(object context)”“綁定”到特定的“this”變量。這與調(diào)用該函數(shù)對(duì)象的“call”方法等效,如下所示:
genderTeller.call(person1); //alerts 'Male'e
更多相關(guān)信息,請(qǐng)參閱Function.call和Function.apply
繼承(Inheritance)
繼承是一種方法,用于創(chuàng)建作為一個(gè)或多個(gè)類專用版本的類。(JavaScript僅支持單類繼承)。這個(gè)專用類通常被稱為子類(child),而其他類通常被稱為父類(parent)。在JavaScript中,你要完成繼承,需將父類的實(shí)例賦給子類,然后將子類特化(specializing)。
提示:由于JavaScript不檢測(cè)的子類的prototype.constructor(原型的構(gòu)造函數(shù)),參閱Core JavaScript 1.5核心參考:Global Objects:Object:prototype屬性,因此我們必須手動(dòng)指定該值。
在下例中,我們定義Student類作為Person的子類。然后我們重新定義sayHello()方法,并添加sayGoodBye()方法。
// 定義Person類
function Person() {}
Person.prototype.walk = function() {
alert('I am walking!');
};
Person.prototype.sayHello = function() {
alert('hello');
};
// 定義Student類
function Student() {
//調(diào)用父類構(gòu)造函數(shù)
Person.call(this);
}
// 繼承Person
Student.prototype = new Person(); // 修正構(gòu)造函數(shù)指針,由于它指向Person
Student.prototype.constructor = Student; // 替換sayHello方法
Student.prototype.sayHello = function() {
alert('hi, I am a student');
}
// 添加sayGoodBye方法
Student.prototype.sayGoodBye = function() {
alert('goodBye');
}
var student1 = new Student();
student1.sayHello();
student1.walk();
student1.sayGoodBye(); // 檢驗(yàn)繼承
alert(student1 instanceof Person); // true
alert(student1 instanceof Student); // true
封裝
在上例中,Student無須知曉Person類的walk()方法是如何實(shí)現(xiàn)的,但仍可使用該方法;Student類無須顯式定義該方法,除非我們想改變它。這稱為封裝(encapsulation),這樣每個(gè)類繼承其父類的方法,并且只需定義它所希望改變的東西。
抽象
抽象是一種機(jī)制(mechanism),允許對(duì)處理中的問題的當(dāng)前部分進(jìn)行建模。這可以通過繼承(特化)或組合(composition)來實(shí)現(xiàn)。JavaScript通過繼承實(shí)現(xiàn)特化(specialization),通過讓類實(shí)例成為其他對(duì)象的屬性值實(shí)現(xiàn)組合。
JavaScript的Function類繼承自O(shè)bject類(這說明模型的特化),并且Function.prototype屬性是Object的實(shí)例(這說明了組合)。
alert('foo is a Function: ' + (foo instanceof Function));
alert('foo.prototype is an Object: ' + (foo.prototype instanceof Object));
多態(tài)
就像所有的方法和屬性被定義在原型屬性內(nèi)部一樣,不同的類可以定義具有相同名稱的方法;方法的作用域限于定義它們的類之內(nèi)。這僅當(dāng)兩個(gè)類之間沒有父子關(guān)系(當(dāng)一個(gè)類沒有從繼承鏈中的其他類繼承時(shí))時(shí)才為真。
提示
本文中所提出的面向?qū)ο缶幊虒?shí)現(xiàn)技術(shù)不僅適用于JavaScript,因?yàn)榫腿绾芜M(jìn)行面向?qū)ο缶幊潭?,這是非常靈活的。
同樣,這里展示的技術(shù)既沒有使用任何語言技巧(language hacks),也沒有模仿其他語言的對(duì)象理論實(shí)現(xiàn)。
在JavaScript中,還有其他更高級(jí)的面向?qū)ο缶幊痰募夹g(shù),但是那些內(nèi)容已超出了這篇介紹性文章的范圍。
- JavaScript面向?qū)ο笕齻€(gè)基本特征實(shí)例詳解【封裝、繼承與多態(tài)】
- JS面向?qū)ο蠡A(chǔ)講解(工廠模式、構(gòu)造函數(shù)模式、原型模式、混合模式、動(dòng)態(tài)原型模式)
- Javascript 面向?qū)ο螅ㄒ唬?共有方法,私有方法,特權(quán)方法)
- 面向?qū)ο蟮腏avascript之二(接口實(shí)現(xiàn)介紹)
- js面向?qū)ο笾R妱?chuàng)建對(duì)象的幾種方式(工廠模式、構(gòu)造函數(shù)模式、原型模式)
- javascript 面向?qū)ο笕吕砭氈當(dāng)?shù)據(jù)的封裝
- javascript 面向?qū)ο缶幊袒A(chǔ) 多態(tài)
- 徹底理解js面向?qū)ο笾^承
- JS 面向?qū)ο蟮?鐘寫法
- js面向?qū)ο笾o態(tài)方法和靜態(tài)屬性實(shí)例分析
- JavaScript面向?qū)ο笾叽蠡驹瓌t實(shí)例詳解
相關(guān)文章
javascript對(duì)象之內(nèi)置對(duì)象Math使用方法
Math對(duì)象的一些方法能實(shí)現(xiàn)我們課本上的某些數(shù)學(xué)計(jì)算,比較常用的方法有如下幾個(gè)2010-04-045個(gè)最頂級(jí)jQuery圖表類庫插件【jquery插件庫】
這篇文章主要介紹了5個(gè)最頂級(jí)jQuery圖表類庫插件【jquery插件庫】,需要的朋友可以參考下2016-05-05JavaScript實(shí)現(xiàn)cookie的操作
這篇文章介紹了JavaScript操作Cookie的方法,文中通過示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-05-05a標(biāo)簽的href與onclick事件的區(qū)別詳解
對(duì)于a標(biāo)簽的href與onclick事件,大家都經(jīng)常見到,也經(jīng)常使用,可它們有什么區(qū)別呢?下面就讓小編來給大家詳細(xì)介紹下,感興趣的朋友可以學(xué)習(xí)下,不用謝了,哈哈2014-11-11JavaScript中splice與slice的區(qū)別
本文給大家分享的是JavaScript中的splice和slice的用法和區(qū)別,slice()方法和splice()方法都是原生js中對(duì)數(shù)組操作的方法,下面我們來詳細(xì)探討下2017-05-05