首頁 >web前端 >js教程 >簡單理解JavaScript中的封裝與繼承特性_基礎知識

簡單理解JavaScript中的封裝與繼承特性_基礎知識

WBOY
WBOY原創
2016-05-16 15:10:021333瀏覽

JavaScript中的封裝
封裝簡單地說就是讓外界只能存取物件的共有變數和函數,隱藏細節和資料。
js中有三種方法創建對象,分別為門戶大開型、用命名規範區分私有變數、閉包創建真正的私有變數三種。
1.門戶大開型,是實現物件的最基礎的方法,所有方法與變數都是共有的外界可以存取。

var Book = function(name){ 
  if(this.check(name)){ 
    console.log("error"); 
    throw new Error("name null"); 
  } 
  this.name = name; 
} 
Book.prototype = { 
  check:function(name){ 
    if(!name){ 
      return true; 
    } 
  }, 
  getName:function(){ 
    return this.name; 
  } 
} 
 
var book = new Book("哈哈"); 
//output:哈哈 哈哈 
console.log(book.name,book.getName()); 

這個例子是門戶大開型的典型,外界能直接存取物件的屬性和方法。可以注意到屬性和變數都有"this"來建立。
 
2.用命名規範區分私有變量,該方法是門戶大開型的最佳化版本,只不過是在私有變數或方法前面用"_"區分​​,如果有程式設計師有意使用_getName()的方法來呼叫方法,還是無法阻止的,不是真正將變數隱藏。
 
3.閉包創建真正的私有變量,該方法利用js中只有函數具有作用域的特性,在構造函數的作用域中定義相關變量,這些變量可以被定義域該作用域中的所有函數訪問。

var Book2 = function(name){ 
  if(check(name)){ 
    console.log("error"); 
    throw new Error("name null"); 
  } 
  name = name; 
  function check(name){ 
    if(!name){ 
      return true; 
    } 
  } 
  this.getName = function(){ 
    return name; 
  } 
} 
Book2.prototype = { 
  display:function(){ 
    //无法直接访问name 
    return "display:"+this.getName(); 
  } 
} 
var book2 = new Book2("哈哈"); 
//output:undefined "哈哈" "display:哈哈" 
console.log(book2.name,book2.getName(),book2.display()); 

 可以看到,這個範例中的結果,直接存取name會回傳undefined的結果。可以看到這個例子與門戶大開型的區別,門戶大開型中的變數使用"this"來創建,而這個例子中使用var來創建,check函數也是如此,使得name與check函數只能在構造函數的作用域中訪問,外界無法直接訪問。
此方法解決了前兩種方法的問題,但是也有一定的弊端。在門戶大開型物件建立模式中,所有方法都建立在原型物件中,因此不管產生多少物件實例,這些方法在記憶體中只存在一份,而採用該方法,每產生一個新的物件都會為每個私有變數和方法建立一個新的副本,故會耗費更多的記憶體。

JavaScript中的繼承
Book基類:

var Book = function(name){ 
  if(this.check(name)){ 
    console.log("error"); 
    throw new Error("name null"); 
  } 
  this.name = name; 
} 
Book.prototype = { 
  check:function(name){ 
    if(!name){ 
      return true; 
    } 
  }, 
  getName:function(){ 
    return this.name; 
  } 
} 

繼承方法:

function extend(subClz,superClz){ 
var F = function(){} 
F.prototype = superClz.prototype; 
subClz.prototype = new F(); 
subClz.prototype.constructor = subClz; 
subClz.superClass = superClz.prototype; 
if(superClz.prototype.constructor == Object.prototype.constructor){ 
  superClz.prototype.constructor = superClz; 
} 

 
使用空函數F作為橋接,可以避免直接實例化父類別時呼叫父類別的建構函式帶來額外開銷,而且當父類別的建構函式有參數時,想直接透過subClass.prototype = new superClass();實現父類別建構函式的呼叫和原型鏈的繼承是不行的。

subClz.superClass = superClz.prototype; 
if(superClz.prototype.constructor == Object.prototype.constructor){ 
  superClz.prototype.constructor = superClz; 
} 

 
加入這三句可以避免子類別繼承父類別寫Book.call(this,name);而是簡單地寫ArtBook.superClass.Constructor.call(this,name)便能實現。
並且在子類別重寫父類別方法的時候,可以呼叫到父類別的方法:

ArtBook.prototype.getName = functiion(){ 
  return ArtBook.superClass.getName.call(this) + "!!!"; 
} 

ArtBook子類別:

var ArtBook = function(name,price){ 
  ArtBook.superClass.Constructor.call(this,name); 
  this.price = price; 
} 
extend(ArtBook,Book); 
ArtBook.prototype.getPrice = function(){ 
    return this.price; 
} 
ArtBook.prototype.getName = function(){ 
   return ArtBook.superClass.getName.call(this)+"!!!"; 
 } 

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn