玩轉(zhuǎn)JavaScript OOP - 類(lèi)的實(shí)現(xiàn)詳解
概述
當(dāng)我們?cè)谡務(wù)撁嫦驅(qū)ο缶幊虝r(shí),我們?cè)谡務(wù)撌裁矗?/p>
我們首先談?wù)摰氖且恍└拍睿簩?duì)象、類(lèi)、封裝、繼承、多態(tài)。
對(duì)象和類(lèi)是面向?qū)ο蟮幕A(chǔ),封裝、繼承和多態(tài)是面向?qū)ο缶幊痰娜筇匦浴?/p>
JavaScript提供了對(duì)象卻缺乏類(lèi),它不能像C#一樣能顯式地定義一個(gè)類(lèi)。
但是JavaScript的函數(shù)功能非常靈活,其中之一就是構(gòu)造函數(shù),結(jié)合構(gòu)造函數(shù)和原型對(duì)象可以實(shí)現(xiàn)”類(lèi)”。
對(duì)象和類(lèi)的概念
對(duì)象
“對(duì)象”是面向?qū)ο缶幊讨蟹浅V匾囊粋€(gè)概念,一個(gè)對(duì)象是一個(gè)“東西”(某個(gè)人或某件事)的描述。
人和事都來(lái)源于現(xiàn)實(shí)生活,我們對(duì)現(xiàn)實(shí)世界的認(rèn)知就是對(duì)人和事的認(rèn)知。
在編程的領(lǐng)域,代碼對(duì)于常人來(lái)說(shuō)是抽象的,代碼構(gòu)成的應(yīng)用是為了更好地解決現(xiàn)實(shí)世界的問(wèn)題。
在分析和設(shè)計(jì)階段,使用“對(duì)象”的概念能夠更好地反應(yīng)現(xiàn)實(shí)世界的問(wèn)題。
反過(guò)來(lái)說(shuō),代碼是包含一些邏輯的,這些邏輯用于描述業(yè)務(wù),業(yè)務(wù)是包含一些業(yè)務(wù)知識(shí)的,業(yè)務(wù)知識(shí)是通過(guò)對(duì)現(xiàn)實(shí)世界的理解和分析總結(jié)出來(lái)的,這些問(wèn)題是由現(xiàn)實(shí)世界的“對(duì)象”構(gòu)成的。
對(duì)象包含特征和行為,用OOP的術(shù)語(yǔ)來(lái)說(shuō),特征是對(duì)象的屬性,行為是對(duì)象的方法。
類(lèi)
在現(xiàn)實(shí)世界中,相似的對(duì)象可以按照一定的標(biāo)準(zhǔn)來(lái)分組。例如“蜂鳥(niǎo)”和“老鷹”都被劃分到鳥(niǎo)類(lèi),鳥(niǎo)類(lèi)不是一個(gè)具體的對(duì)象,它是人們根據(jù)“蜂鳥(niǎo)”、“老鷹”等那些具體的鳥(niǎo)分析出相似的特征和行為后,歸納出來(lái)的一個(gè)概念。類(lèi)相當(dāng)于一個(gè)模板,我們可以基于這個(gè)模板創(chuàng)建不同的具體的對(duì)象。
在C#中,我們可以定義一個(gè)鳥(niǎo)類(lèi)。
/// <summary> /// 鳥(niǎo)類(lèi) /// </summary> public class Bird { public void Fly() { Console.WriteLine("I can fly!"); } }
雖然JavaScript是一門(mén)面向?qū)ο缶幊陶Z(yǔ)言,但它沒(méi)有提供class的語(yǔ)法支持。
在JavaScript中,一切都是基于對(duì)象的,即使后面要講的“原型”也都是對(duì)象,JavaScript的繼承和重用也都是通過(guò)原型來(lái)實(shí)現(xiàn)的。
但是結(jié)合構(gòu)造函數(shù)和原型對(duì)象可以實(shí)現(xiàn)JavaScript的“類(lèi)”。
構(gòu)造函數(shù)
之前我們使用new Array()創(chuàng)建一個(gè)數(shù)組,使用new Object()創(chuàng)建一個(gè)對(duì)象,Array()和Object()是JavaScript內(nèi)置的兩個(gè)構(gòu)造函數(shù),盡管JavaScript沒(méi)有提供類(lèi),但我們可以將Array和Object理解為“類(lèi)”的概念。
需要注意的是,JavaScript的“類(lèi)”是由構(gòu)造函數(shù)實(shí)現(xiàn)的。
定義構(gòu)造函數(shù)
構(gòu)造函數(shù)也是函數(shù),定義構(gòu)造函數(shù)和其他函數(shù)并沒(méi)有語(yǔ)法上的區(qū)別。
唯一的區(qū)別是構(gòu)造函數(shù)的首字母應(yīng)該大寫(xiě),這也是JavaScript的編程規(guī)范。
以下定義了一個(gè)Person()構(gòu)造函數(shù),我們可以將它理解為Person類(lèi)。
function Person(){ console.log('I am keepfool.'); }
JavaScript的“類(lèi)”和構(gòu)造函數(shù)是同時(shí)被定義的,在JavaScript中定義“類(lèi)”時(shí),就同時(shí)定義了構(gòu)造器。
使用構(gòu)造函數(shù)
JavaScript使用類(lèi)的方式和C#一樣,new關(guān)鍵字后面跟著構(gòu)造函數(shù)。
var p = new Person();
定義屬性和方法
現(xiàn)在我們已經(jīng)定義好了Person類(lèi),可以為Person類(lèi)添加一些屬性和方法。
定義屬性
在講JavaScript對(duì)象時(shí),我們講了對(duì)象的屬性設(shè)置和訪問(wèn)。
這段代碼展示了定義對(duì)象屬性的兩種方式:
var cat = { color: 'black' }; cat.name = 'Tom'; console.log(cat.color); console.log(cat.name);
使用this定義屬性
JavaScript類(lèi)的屬性定義方式則有些不同,在構(gòu)造函數(shù)中使用this關(guān)鍵字定義屬性:
function Person(name){ this.name = name; }
•第一行代碼,定義了Person類(lèi),并定義了構(gòu)造函數(shù)。
•第二行代碼,定義了name屬性。
創(chuàng)建并使用對(duì)象
以下2行代碼創(chuàng)建了兩個(gè)Person類(lèi)的對(duì)象
var p1 = new Person('James'); var p2 = new Person('Cury');
在Chrome控制臺(tái)中輸出p1.name和p2.name
p1和p2是兩個(gè)不同的對(duì)象,修改p1.name不會(huì)影響p2.name。
p1.name = 'Lebron James';
定義方法
首先,我們區(qū)分一下術(shù)語(yǔ)“函數(shù)”和“方法”,“函數(shù)”是獨(dú)立的單元,而“方法”是依賴(lài)于類(lèi)這個(gè)主體存在的。
使用this定義方法
在JavaScript中,類(lèi)的方法是定義在構(gòu)造函數(shù)中的函數(shù),在構(gòu)造函數(shù)中使用this關(guān)鍵字定義方法:
function Person(name) { // 定義屬性 this.name = name; // 定義方法 this.sayHello = function() { return 'Hello, I am ' + this.name; } }
使用方法
在Chrome控制臺(tái)分別調(diào)用p1和p2對(duì)象的sayHello()方法
constructor屬性
當(dāng)創(chuàng)建一個(gè)對(duì)象時(shí),一個(gè)特殊的屬性被JavaScript自動(dòng)地分配給對(duì)象了,這個(gè)屬性就是constructor屬性。
在chrome控制臺(tái)輸入p1.constructor
,可以看到p1對(duì)象的constructor屬性指向一個(gè)函數(shù)。
瞧瞧這個(gè)函數(shù)的內(nèi)容,這不正是Person()構(gòu)造函數(shù)嗎?
這表示我們也可以通過(guò)p1.constructor屬性創(chuàng)建對(duì)象,
var p3 = new p1.constructor('Steve Nash');
這行代碼闡述了一句話(huà):“我不關(guān)心p1對(duì)象是怎么創(chuàng)建的,但我想讓另一個(gè)對(duì)象如p1一樣創(chuàng)建!”
在Chrome控制臺(tái)使用instanceof操作符,可以看到p1、p2、p3都是Person類(lèi)的實(shí)例
另外,當(dāng)我們以{}
方式創(chuàng)建對(duì)象時(shí),實(shí)際上也調(diào)用了Object()構(gòu)造函數(shù)。
var o = {};
這行代碼聲明了一個(gè)對(duì)象,盡管我們沒(méi)有設(shè)置任何屬性和方法,但JavaScript引擎默認(rèn)給它設(shè)置了constructor屬性。
o.constructor
指向的是Object()構(gòu)造函數(shù),[native code]
顯示了Object()是JavaScript內(nèi)置的函數(shù)。
原型對(duì)象
在JavaScript中,定義一個(gè)函數(shù)時(shí),函數(shù)就會(huì)擁有prototype屬性,構(gòu)造函數(shù)也不例外。
下圖說(shuō)明了Person()構(gòu)造函數(shù)的prototype屬性是一個(gè)對(duì)象,它是屬于函數(shù)的,我們稱(chēng)這個(gè)屬性為原型對(duì)象。
從Person類(lèi)的角度出發(fā),我們也可理解為prototype屬性是屬于Person類(lèi)的。
同時(shí)Person類(lèi)的實(shí)例是沒(méi)有prototype屬性的,上圖的p1.prototype是undefined,這說(shuō)明prototype屬性是共享的,這有點(diǎn)像C#中的靜態(tài)屬性。
設(shè)置prototype
既然prototype是一個(gè)對(duì)象,那就可以為它添加屬性和方法。
在函數(shù)的protpotype屬性上定義屬性和方法,與設(shè)置普通對(duì)象的屬性和方法沒(méi)什么區(qū)別。
下面的代碼為Person.prototype定義了屬性和方法。
function Person(name){ this.name = name; this.sayHello = function() { return 'Hello, I am ' + this.name; } } // 在構(gòu)造函數(shù)的prototype對(duì)象上定義屬性和方法 Person.prototype.height = 176; Person.prototype.run = function(){ return 'I am ' + this.name + ', I am running!'; } var p1 = new Person('James');
使用prototype
在Person.prototype中定義的屬性和方法,可以直接被Person類(lèi)的實(shí)例使用,仍然是以object.property
的方式使用。
需要特別注意的是,name
和sayHello()
是屬于Person類(lèi)的實(shí)例,而height
和run()
是不屬于Person類(lèi)的實(shí)例。
小技巧:通過(guò)hasOwnProperty方法可以查看對(duì)象是否包含某個(gè)屬性或方法。
自有屬性 vs. prototype的屬性
Person類(lèi)的實(shí)例既可以使用Person類(lèi)中的屬性,又可以使用Person.prototype中的屬性。
那么Person類(lèi)的屬性和Person.prototype的屬性有什么差別呢?
首先,我們可以將Person類(lèi)中的屬性和方法理解為“實(shí)例屬性”。
由于prototype是共享的,我們可以將prototype中的屬性和方法理解為“共享屬性”。
“實(shí)例屬性”和“共享屬性”的差別主要體現(xiàn)在性能上。
每創(chuàng)建一個(gè)Person的實(shí)例,就會(huì)產(chǎn)生一個(gè)name屬性和sayHello()方法的副本,而height屬性和run()方法則是所有實(shí)例共享一個(gè)副本。
既然如此,這意味著sayHello()方法可以提到prototype中。
另外,不同的Person實(shí)例height可能會(huì)不一樣,應(yīng)將它放到Person類(lèi)中更合理。
function Person(name,height){ this.name = name; this.height = height; } Person.prototype.sayHello = function(){ return 'Hello, I am ' + this.name + ', my height is ' + this.height + 'cm.'; } Person.prototype.run = function(){ return 'I am ' + this.name + ', I am running!'; } var p1 = new Person('James',203); var p2 = new Person('Cury',190);
類(lèi)的實(shí)現(xiàn)總結(jié)
JavaScript沒(méi)有類(lèi),但構(gòu)造函數(shù)可以實(shí)現(xiàn)“類(lèi)”。
按照J(rèn)avaScript編程規(guī)范,構(gòu)造函數(shù)的首字母應(yīng)該大寫(xiě)。
“類(lèi)”的屬性和方法是用this.property方式定義在構(gòu)造函數(shù)中的。
在對(duì)象創(chuàng)建時(shí)JavaScript分配了constructor屬性給對(duì)象,constructor屬性是對(duì)象構(gòu)造函數(shù)的一個(gè)引用。
函數(shù)在定義時(shí)就已經(jīng)有了prototype屬性,prototype屬性也是一個(gè)對(duì)象。
prototype是共享的,定義在prototype上的屬性和方法可以被“類(lèi)”的實(shí)例使用。
如果屬性或方法能夠定義在prototype上,就不要定義在構(gòu)造函數(shù)上,使用prototype可以減少內(nèi)存開(kāi)銷(xiāo)。
以上這篇玩轉(zhuǎn)JavaScript OOP - 類(lèi)的實(shí)現(xiàn)詳解就是小編分享給大家的全部?jī)?nèi)容了,希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
- Javascript OOP之面向?qū)ο?/a>
- JavaScript OOP面向?qū)ο蠼榻B
- Nodejs全棧框架StrongLoop推薦
- javascript延時(shí)重復(fù)執(zhí)行函數(shù) lLoopRun.js
- JavaScript運(yùn)行機(jī)制之事件循環(huán)(Event Loop)詳解
- javascript oop開(kāi)發(fā)滑動(dòng)(slide)菜單控件
- Node.js事件循環(huán)(Event Loop)和線(xiàn)程池詳解
- JS OOP包機(jī)制,類(lèi)創(chuàng)建的方法定義
- javascript 原型模式實(shí)現(xiàn)OOP的再研究
- 延時(shí)重復(fù)執(zhí)行函數(shù) lLoopRun.js
- JavaScript OOP類(lèi)與繼承
- javascript基于prototype實(shí)現(xiàn)類(lèi)似OOP繼承的方法
- Javascript oop設(shè)計(jì)模式 面向?qū)ο缶幊毯?jiǎn)單實(shí)例介紹
相關(guān)文章
微信小程序開(kāi)發(fā)數(shù)據(jù)緩存基礎(chǔ)知識(shí)辨析及運(yùn)用實(shí)例詳解
這篇文章主要介紹了微信小程序開(kāi)發(fā)數(shù)據(jù)緩存基礎(chǔ)知識(shí)辨析及運(yùn)用實(shí)例詳解,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-11-11刪除javascript所創(chuàng)建子節(jié)點(diǎn)的方法
這篇文章主要介紹了刪除javascript所創(chuàng)建子節(jié)點(diǎn)的方法,涉及javascript針對(duì)頁(yè)面節(jié)點(diǎn)元素的操作技巧,需要的朋友可以參考下2015-05-05JavaScript使用canvas繪制坐標(biāo)和線(xiàn)
這篇文章主要為大家詳細(xì)介紹了JavaScript使用canvas繪制坐標(biāo)和線(xiàn),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-04-045個(gè)可以幫你理解JavaScript核心閉包和作用域的小例子
這篇文章主要介紹了5個(gè)可以幫你理解JavaScript核心閉包和作用域的小例子,本文是翻譯自國(guó)外的一篇文章,短小精悍,需要的朋友可以參考下2014-10-10如何用JS模擬實(shí)現(xiàn)數(shù)組的map方法
這篇文章主要介紹了如何用JS模擬實(shí)現(xiàn)數(shù)組的map方法,文中講解非常細(xì)致,代碼幫助大家更好的理解和學(xué)習(xí),感興趣的朋友可以了解下2020-07-07javascript仿京東導(dǎo)航左側(cè)分類(lèi)導(dǎo)航下拉菜單效果
這篇文章主要為大家詳細(xì)介紹了javascript仿京東導(dǎo)航左側(cè)分類(lèi)導(dǎo)航下拉菜單效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-03-03javascript生成不重復(fù)的隨機(jī)數(shù)
這篇文章主要介紹了javascript在指定范圍內(nèi)生成不重復(fù)的隨機(jī)數(shù)的方法和相關(guān)實(shí)例,有需要的小伙伴可以參考下。2015-07-07在JS中如何把毫秒轉(zhuǎn)換成規(guī)定的日期時(shí)間格式實(shí)例
本篇文章主要介紹了在JS中如何把毫秒轉(zhuǎn)換成規(guī)定的日期時(shí)間格式實(shí)例,非常具有實(shí)用價(jià)值,需要的朋友可以參考下2017-05-05javascript實(shí)現(xiàn)添加附件功能的方法
這篇文章主要介紹了javascript實(shí)現(xiàn)添加附件功能的方法,在我們編輯信息時(shí),有時(shí)候需要附加文件、圖片實(shí)現(xiàn)上傳功能,通過(guò)本文了解javascript是如何實(shí)現(xiàn)附加功能的,請(qǐng)閱讀。2015-11-11