1. 程式人生 > >JavaScript面向物件(OOP)

JavaScript面向物件(OOP)

 前  言

JRedu

       面向物件程式設計(簡稱OOP)是現在最流行的程式設計方法,這種方法有別於基於過程的程式設計方法。在寫面向物件的WEB應用程式方面JavaScript是一種很好的選擇.它能支援OOP.因為它通過原型支援繼承的方式和通過屬性和方法的方式一樣好.很多開發者試圖拋棄JS,試著用C#或JAVA僅是因為JS不是他認為合適的面向物件的語言.許多人還沒有認識到javascript支援繼承.當你寫面向物件的程式碼時.它能給你很強大的能量.你也可以使用它寫出可複用,可封裝的程式碼.

一、什麼是面向物件程式設計(OOP)?

        在瞭解什麼是面向物件程式設計之前,我們來看看語言的分類。總體可以分為三類:面向機器

面向過程還有面向物件。面向物件的語言主要有C++、Java、PHP等。

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]());
// 函式最終呼叫者陣列