JavaScript面向物件(OOP)
前 言
JRedu
面向物件程式設計(簡稱OOP)是現在最流行的程式設計方法,這種方法有別於基於過程的程式設計方法。在寫面向物件的WEB應用程式方面JavaScript是一種很好的選擇.它能支援OOP.因為它通過原型支援繼承的方式和通過屬性和方法的方式一樣好.很多開發者試圖拋棄JS,試著用C#或JAVA僅是因為JS不是他認為合適的面向物件的語言.許多人還沒有認識到javascript支援繼承.當你寫面向物件的程式碼時.它能給你很強大的能量.你也可以使用它寫出可複用,可封裝的程式碼.
一、什麼是面向物件程式設計(OOP)? |
在瞭解什麼是面向物件程式設計之前,我們來看看語言的分類。總體可以分為三類:面向機器
1面向過程與面向物件
1)面向過程:面向過程專注於如何去解決一個問題的過程步驟。程式設計特點是由一個個函式去實現每一步的過程步驟,沒有類和物件的概念。 2)面向物件:專注於由哪一個物件來解決這個問題,程式設計特點是出現了一個類,從類中拿到物件,由這個物件去解決具體問題。 對於呼叫者來說,面向過程需要呼叫者自己去實現各種函式。而面向物件,只需要告訴呼叫者,物件中具體方法的功能,而不需要呼叫者瞭解方法中的實現細節。
2面向物件的三大特徵
這個對於計算機專業的人來說,相信大家已經很熟悉啦,那我就再說一遍吧~
面向物件的三大特徵是繼承、封裝、多型。JS可以模擬實現繼承和封裝,但是無法模擬實現多型,所以我們說JS是一門基於物件的語言,而非是面向物件的語言。
3類和物件
1、類:一類具有相同特徵(屬性)和行為(方法)的集合。比如,人類具有身高、體重等屬性,吃飯、大笑等行為,所以,我們可以把人劃分為一類。
2、物件:從類中,拿出具有確定屬性值和方法的個體。比如,張三-->屬性:身高180體重180 方法:說話-->我叫張三,身高180 3、類和物件的關係: ①類是抽象的,物件是具體的(類是物件的抽象化,物件是類的具體化) ②類是一個抽象的概念,只能說類有屬性和方法,但是不能給屬性賦具體的。比如,人類有姓名,但是不能說人類的姓名叫什麼。 物件是一個具體的個例,是將類中的屬性進行具體賦值而來的個體。 比如,張三是一個人類的個體。可以說張三的姓名叫張三。也就是張三對人類的每一個屬性進行了具體的賦值,那麼張三就是由人類產生的一個物件。 4、使用類和物件的步驟:
1)建立一個類(建構函式):類名必須使用大駝峰法則。即每個單詞首字母都要大寫
function 類名(屬性1){ this.屬性1=屬性1; this.方法=function(){ //方法中要呼叫自身屬性,必須使用this.屬性 } }
2)通過類例項化(new)出一個物件。
var obj=new 類名(屬性1的具體值); obj.屬性; 呼叫屬性 obj.方法; 呼叫方法
3)注意事項: ①通過類名,new出一個物件的過程,叫做"類的例項化"。 ②類中的this,會在例項化的時候,指向新new出的物件。 所以,this.屬性 this.方法實際上是將屬性和方法繫結在即將new出的物件上面。 ③在類中,要呼叫自身屬性,必須使用this.屬性名。如果直接使用變數名,則無法訪問對應的屬性。
function Person(name,age){ this.name=name; this.age=age; this.say=function(content){ //在類中,訪問類自身的屬性,必須使用this.屬性呼叫。 alert("我叫"+this.name+",今年"+this.age+"歲,我說了一句話:"+content); } } var zhangsan=new Person("姐姐",18); zhangsan.say("你好呀");
④類名必須使用大駝峰法則,注意與普通函式區分。
4面向物件的兩個重要屬性
1)constructor:返回當前物件的建構函式。 >>>zhangsan.constructor==Person; ( true) 2)instanceof:檢測一個物件是不是一個類的例項; >>>lisi instanceof Person √ lisi是通過Person類new出的 >>>lisi instanceof Object √ 所有物件都是Object的例項 >>>Person instanceof Object √ 函式本身也是物件
5廣義物件與狹義物件
1)狹義物件:只有屬性和方法,除此之外沒有任何其他內容。
var obj={}; //用{}宣告的物件 var obj=new Object(); //用new宣告的物件
2)廣義物件:除了用字面量宣告的基本資料型別之外,JS中萬物皆物件。換句話說,只要能新增屬性和方法的變數,都可以稱為物件。
var s="123"; //不是物件 s.name="aaa"; console.log(typeof(s)); //String console.log(s.name); //undfined 字面量宣告的字串不是物件,不能新增屬性 var s=new String("123"); //是物件 s.name="aaa"; console.log(typeof(s)); //Object console.log(s.name); //"aaa" 使用new宣告的字串是物件,能新增屬性和方法。
二、 成員屬性、靜態屬性和私有屬性 |
1、在建構函式中,使用this.屬性宣告。或者在例項化出物件以後,使用"物件.屬性"追加的,都屬於成員屬性或成員方法。也叫例項屬性或例項方法。 成員屬性/方法,是屬於由類new出的物件的。 需要使用"物件名.屬性名"呼叫。
【靜態屬性與靜態方法】 2、通過“類名.屬性名”、“類名.方法名”宣告的屬性和方法,稱為靜態屬性、靜態方法。也叫類屬性和類方法。 類屬性/類方法,是屬於類的(屬於建構函式的) 通過"類名.屬性名"呼叫。 3、成員屬性是屬於例項化出的物件的,只能使用物件呼叫。 靜態屬性是屬於建構函式的,只能使用類名呼叫。 [私有屬性和私有方法] 4、在建構函式中,使用var宣告的變數稱為私有屬性; 在建構函式中,使用function宣告的函式,稱為私有方法;
function Person(){ var num=1;//私有屬性 function func(){}//私有方法 }
私有屬性和私有方法的作用域,只在建構函式內容有效。即只能在建構函式內部使用,在建構函式外部,無論使用物件名還是類名都無法呼叫。
function Person(name){ this.name=name; //宣告成員屬性 var sex="男";//私有屬性 } var zhangsan=new Person("張三"); zhangsan.age=14; //追加成員屬性 alert(zhangsan.name); //呼叫成員屬性 Person.count="60億"; //宣告靜態屬性 alert(Person.count); //呼叫靜態屬性 var lisi=new Person("李四"); console.log(lisi.count); //undefined 靜態屬性是屬於類的,只能用類名呼叫。
三、 JavaScript模擬實現封裝 |
1、什麼叫封裝? ①方法的封裝:將類內部的函式進行私有化處理,不對外提供介面,無法在類外部使用的方法,稱為私有方法,即方法的封裝。 ②屬性的封裝:將類中的屬性進行私有化處理,對外不能直接使用物件名訪問(私有屬性)。同時,需要提供專門用於設定和讀取私有屬性的set/get方法,讓外部使用我們提供的方法,對屬性進行操作。這就叫屬性的封裝。 2、注意:封裝不是拒絕訪問,而是限制訪問。要求呼叫者,必須使用我們提供的set/get方法進行屬性的操作,而不是直接拒絕操作。 因此,單純的屬性私有化,不能稱為封裝!必須要有私有化後,提供對應的set/get方法。
function Person(name,age1){ this.name=name; // this.age=age; var age=0; this.setAge=function(ages){ if(ages>0&&ages<=120){ age=ages; }else{ alert("年齡賦值失敗"); } } // 當例項化類拿到物件時,可以直接通過類名的()傳入年齡,設定私有屬性 if(age1!=undefined) this.setAge(age1); this.getAge=function(){ return age; } this.sayTime=function(){ alert("我說當前時間是:"+getTime()); } this.writeTime=function(){ alert("我寫了當前時間是:"+getTime()); } /*私有化的方法,只能在類內部被其他方法呼叫,而不能對外提供功能。這就是方法的封裝*/ function getTime(){ return new Date(); } } var zhangsan=new Person("張三",99); zhangsan.setAge(99); alert("張三的年齡是"+zhangsan.getAge()); var lisi=new Person("李四",99); lisi.setAge(110); alert("李四的年齡是:"+lisi.getAge());
四、JavaScript中的this指向詳解 |
1、誰最終呼叫函式,this最終指向誰(記住!) ①this指向誰,不應考慮函式在哪宣告,而應該考慮函式在哪呼叫!!! ②this指向的永遠只可能是物件,而不可能是函式。 ③this指向的物件,叫做函式的上下文context,也叫函式的呼叫者。
2、this指向的規律!!!(跟函式的呼叫方式息息相關,記住這點,相信你一定會分清this指向噠) ①通過函式名()呼叫的,this永遠指向window ②通過物件.方法呼叫的,this指向這個物件。 ③函式作為陣列中的一個元素,用陣列下標呼叫的,this指向這個陣列 ④函式作為window內建函式的回撥函式使用,this指向window。 setInterval setTimeout 等。 ⑤函式作為建構函式,使用new關鍵字呼叫,this指向新new出的物件。
function func(){ console.log(this); } var obj={ name:"zhangsan", func:func } //①通過函式名()呼叫的,this永遠指向window。 func(); //②通過物件.方法呼叫的,this指向這個物件 obj.func();//狹義物件 window.onclick=function(){ document.getElementById("div1").onclick=function(){ func();//最終還是使用()呼叫,所以指向window } document.getElementById("div1").onclick=func;//廣義物件,指向div } //③函式作為陣列中的一個元素,用陣列下標呼叫的,this指向這個陣列 var arr=[1,2,3,func,4,5,6]; arr[3](); //④函式作為window內建函式的回撥函式使用,this指向window。 setTimeout(func,1000); //⑤函式作為建構函式,使用new關鍵字呼叫,this指向新new出的物件。 var obj1=new func();
現在,你一定分清了this指向了吧,下面我們來看一個綜合案例:
var obj1={ name:"obj1", arr:[func,1,{name:"obj2",func:func},3,4], } obj1.arr[0]();//最終的呼叫者是陣列。this-->obj.arr setTimeout(obj1,arr[0],2000);//obj.arr[0]僅僅是取到函式賦給setTimeout,但並沒有呼叫。函式的最終呼叫者是setTimeout。這個式子相當於setTimeout(func,2000); obj1.arr[2].func();//最終呼叫者是{name:"obj2",func:func} setTimeout(obj1.arr[2].func,2000);//最終呼叫者是setTimeout //↓最終的呼叫者是div1 //document.getElementById("div1").onclick=obj1.arr[0]; //document.getElementById("div1").onclick=arr[2].func;
最後,我們再來看一道面試題:
var fullname = 'John Doe'; var obj = { fullname: 'Colin Ihrig', prop: { fullname: 'Aurelio De Rosa', getFullname: function() { return this.fullname; } } }; console.log(obj.prop.getFullname()); // 函式的最終呼叫者 obj.prop var test = obj.prop.getFullname; console.log(test()); // 函式的最終呼叫者 test() this-> window obj.func = obj.prop.getFullname; console.log(obj.func()); // 函式最終呼叫者是obj var arr = [obj.prop.getFullname,1,2]; arr.fullname = "JiangHao"; console.log(arr[0]()); // 函式最終呼叫者陣列