javascript面向對象基礎

1  關於對象的靜態屬性、私有屬性、局部變量、原型變量 的使用與區別
1)局部變量:是指在函數中用 var 聲明過的變量,在函數運行完之後內存被自動回收!
2)私有屬性:是指在函數中用 this 標記過的變量,在實例對象後一直被對象所獨有!
3)靜態屬性:是指函數(類)本身所獨有的屬性,他是不能被實例對象所調用的,這與java不同。
4)原型變量:是指通過類或者對象本身的原型所所創建的變量,他能被所有的實例對象以及類本身原型所共享。
具體如下:
function Person( ){ 
this.name = 'zhangsan' ;                        // 私有屬性
var value = 'wuyun' ;                             // 局部變量
this.process  = function(){                    // 使用方法
      console.info(this.name + value ); // 如果局部變量和私有變量同名,那麼會使用局部變量,所以使用this作爲標記! 
}
}
var person = new Person( );
console.info(   person.__proto__  == Person.prototype   );  // true,說明二者可以通過原型相互共享屬性
Person.age = 21 ;                                          // Person的靜態屬性,person是無法調用的
person.age = 22 ;                                         // person的靜態屬性,Person是無法調用的
Person.prototype.sex = 'man' ;                  // 原型變量,二者共享
person.__proto__.height = 185 ;               // 原型變量,二者共享



2 關於prototype  __proto__ 的調用
1)prototype 是Function對象獨有的屬性,也就是說只有類才能夠調用這個屬性
2)__proto__  是實例對象獨有的屬性,也就是用new,或者{ } 聲明過的變量才能調用
3)以上二者如果是對應類的實例的話,那麼他們引用的是同一個原型,所以 person.__proto__ == Person.prototype  爲 true

3 關於原型鏈繼承(必不可少的兩步
1)子類的原型指向父類的實例,SubClass.prototype = new SuperClass( );
2)子類的原型構造函數指向子類本身,SubClass.prototype.constructor = SubClass ; 
3 ) 由於js使用原型繼承導致,構造函數也別繼承,然而面向對象來說這是錯誤的,所以纔有第二步重新指向構造函數。
4 ) 個人理解 Person 與 Person.prototype 的差異是:前者是指構造函數或類本身,後者其實是一個所有實例都共享的實例對象。
5)如果單純只是繼承某一個函數那麼可以使用 call 函數進行處理,而無需繼承。 Class.method.call(Self , param) ; 
具體如下:
function extends( SubClass , SuperClass){
/*第一步 : 構建橋樑類Bridge,他的作用就是完全替代父類本身,包括構造方法*/
var Bridge = function( ){ } ;
Bridge.prototype = new SuperClass( ); // Bridge.prototype.constructor = SuperClass ;這一步原型鏈默認完成 
/*第二步 : 使用子類的原型鏈繼承橋樑父類*/
SubClass.prototype = new Bridge( );  
SubClass.prototype.constructor = SubClass ;  
/*第三步 : 擴展子類屬性,把父類的引用作爲子類的共享屬性,爲子類中所調用 */
SubClass.prototype.superClass = SuperClass.prototype ;        //  這裏必須是prototype,而不能是函數本身
/*第四步 : 爲保證程序正常運行機制,做個小判斷*/
if( SuperClass.prototype.constructor == Object.prototype.constructor  ){
SuperClass.prototype.constructor = SuperClass ;
} 
}


6)javascript是單繼承的,但如果想在一個類中擁有多個類的方法,那麼就要使用聚合(摻元類,把其他類的方法爲自己所用)。
具體如下:
function mixin(ReceivingClass,GivingClass){
    for(var method in GivingClass.prototype ){
        if(ReceivingClass.prototype[method] == undefined){
  /* 這裏特別注意使用prototype而不使用原因就是 靜態屬性 與 原型屬性的差異 */
            ReceivingClass.prototype[method]= GivingClass.prototype[method];
        }
    }
}



4 關於接口的三種模仿方式
1)註釋式模仿接口(其實就是用註釋語句把接口給解釋,說白了就是通過規範文檔來說明某給類就是一個接口),如下
/*  這裏就是接口的註釋聲明文檔,這個是核心
interface Composite {
    function add(child);
    function remove(child);
    function getChild(index);
}
interface FormItem {
    function save();
} */
var CompositeForm = function(id, method, action) { //自定義任意一個類,用來模擬實現接口
    ...
};
CompositeForm.prototype.add = function(child) { // 通過實現方法說明實現接口
    ...
};


2)屬性檢查式模仿接口(同樣使用註釋,但是它與上面的唯一不同點就是在方法的實現中,對該類有個判斷是否實現了該接口,所謂判斷,就是該類有一個屬性用來保存他實現類的名稱,所以稱之爲屬性檢查)
function addForm(formInstance) {
    if(!implements(formInstance, 'Composite', 'FormItem')) {
        throw new Error("Object does not implement a required interface.");
    }
    ...
}


3)鴨式辨型式模仿接口(它採用的思想是說只要在實現類中實現了接口中的所有方法就等同於實現了接口,那麼他的核心就是判斷是否實現了接口中的方法,否則報錯)

5 創建對象的模式(成員變量的保護)
1)門戶大開型(使用 this.property 的方式定義成員變量,但是爲了成員數據的安全性,我們使用接口實現,只能調用接口來訪問成員變量,不過這必須自覺遵守,因爲本身是可以直接通過instance.property 調研的)
function Book( id , name ){
this.setId(id); //使用接口方法對屬性賦值,而不是直接賦值屬性
this.setName(name);
}
Book.prototype = {  // 通過字面量方式註冊實現接口的方法,同時還使用檢查規則,這樣就避免了自己賦值屬性導致的問題
setId:function(id){
this.checkId(id); // 這個纔是核心
this.id = id ;
}
check:function(id){
// some code
}
.......
}


2)用戶命名規範區別私有成員(這種方法其實和上面的方法是一模一樣的,唯一的差異就在於對象的成員變量名稱都用下劃線開頭,一些不希望外部調用的方法也使用下劃線開頭,說白了這就是約定俗成,並不是真正的達到目的)如下:
Book.prototype = { // 通過字面量方式註冊實現接口的方法,同時還使用檢查規則,這樣就避免了自己賦值屬性導致的問題
setId:function(id){
this._checkId(id); // 這個纔是核心
this._id = id ;
}
_check:function(id){
// some code
}
.......
}


3)使用閉包函數實現真正的私有成員(這種做法就是充分借用函數作用域以及閉包原來達到私有成員變量的保護)
1)函數作用域:在javascript中只有函數作用域沒有塊級作用域,換句話說,如果A函數內部有個B函數,那麼B函數是能夠調用A函數的所有成員的。
2)閉包:對以上的A B , 如果在B中使用了A的成員變量,不管B在哪裏執行,是在A內部執行,或是在A外部的任意函數作用域中執行,B所執行的作用域還是A,也就是說不管A是否執行完,A都會爲B保留一份執行空間的內存,那麼B使用A的成員變量將會被保留。這就是說 在不同函數作用域中也能調用之前作用域的成員變量,爲閉包!
3)使用函數作用域和閉包的概念完成私有成員的保護:
var Book = function(newIsbn, newTitle, newAuthor) { // implements Publication
  // Private attributes. 私有成員變量
  var isbn, title, author;
  // Private method. 私有方法
  function checkIsbn(isbn) {
    ...
  }
  // Privileged methods. 公共方法
  this.getIsbn = function() {
    return isbn;
  };
  this.setIsbn = function(newIsbn) {
    if(!checkIsbn(newIsbn)) throw new Error('Book: Invalid ISBN.');
    isbn = newIsbn;
  };
  // Constructor code. 初始化
  this.setIsbn(newIsbn);
};
// Public, non-privileged methods. 公共共享方法
Book.prototype = {
  display: function() {
    ...
  }
};


這裏很值得注意的是:使用 var 聲明的變量就是爲私有的屬性,因爲沒有使用this標記所以是無法再外部調用的,但是通過閉包達到私有變量的保護有個很大的缺點就是:公共方法(爲了閉包)寫在了類裏面,從而導致每個對象實例化後都會保存這個方法的副本,那麼導致佔了大量內存,如果把方法寫在prototype中,即成爲公共共享的方法,只保存一份副本在內存,但是在公共共享的方法中只能使用this來調用變量,而不能調用用var聲明的變量。所以這就是這種方法的弊端!而且還有一個弊端就是:繼承該類之後的子類是不能訪問到該類的私有成員的,因爲只能使用閉包訪問了!!!

6 關於屬性和方法的相關概念
1)實例公共屬性: this.name
2)實例私有屬性: person.name(單個實例);var name ( 所有實例 )
3)類公共屬性:Person.name ; 
4 ) 靜態屬性:Person.prototype.name ; person.__proto__.name ;
5)使用閉包重新定義屬性的概念:
var Person = ( function( ){
var name = ''; //類的靜態私有屬性
this.sex = '';   // 類的靜態共有屬性
var ocr = function( ){
var age = ''; //實例私有屬性
this.height = ''; //實例共享屬性
}
ocr.getName = function( ){ //定義類的方法
return name ;
}
return ocr ; //返回的是類本身
} )( )
alert(Person.getName( ));


這裏說說執行流程:1)使用小括號就是爲了讓他自動執行,這裏直接使用Person,實際上已經把Person定義的function執行完成了,等價於沒有小括號時的,Person();
                               2)返回一個function,實際上就是閉包的原理,也可以看成就是返回一個類,是這個類本身而不是類實例,所以我們定義方法是用類名定義類的屬性,而不是定義實例屬性,不然的話無法在外面調用。

7 常量(就是使用屬性的定義以及閉包原理)
var Class = (function() {
   // Private static attributes.
    var constants = {
   UPPER_BOUND: 100,
   LOWER_BOUND: -100
    }
    // Return the constructor.
    var ctor = function( ) {
    }
    ctor.getConstant=function(name) {
   return constants[name];
    }
    return ctor;
  })( );
  /* Usage. */
  alert(Class.getConstant('UPPER_BOUND'));



8 函數的鏈式調用
1)所謂鏈式調用不過就是可以person.fn( ).process( ).property ;
2)實現很簡單,就是在每一個方法的返回值爲當前實例,即 this ; 
3)關於Jquery 的鏈式調用,如下:
//爲了類(Function)擴展函數,我們頂一個他的靜態函數
Function.prototype.method = function(name,fn){ // Function.prototype是爲所有的Function對象增加屬性,原因是繼承
 this.prototype[name] = fn;  // 這裏的this是指函數類本身,所以要用prototype,這樣才能使屬性共享 
 return this;  // 返回this,達到鏈式調用
};
(function(){
 function _$(els){ };	  // 聲明一個類
 _$.onready = function(obj,fn){
  if(obj){  //  如果對象存在,把$對象綁定在對象身上
   obj.$ = function(){
    return new _$(arguments);
   }
  }else{   // 對象(_$)註冊到window上
   window.$ = function(){
    return new _$(arguments);
   }
  }
  fn();
 }  

 _$.method("addEvent",function(type,fn){ //註冊方法
  fn();
 }).method("getEvent",function(fn,e){        //註冊方法
  fn();
 });

_$.onready(com,function(){
  com.$("div01").addEvent("click",function(){
   alert("click Event");
  com.$(this).getEvent(function(){
    alert("click getEvent");
   })
  })
 })
)()



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章