js物件導向

來源:互聯網
上載者:User

標籤:script   使用   佔用   ret   缺點   struct   基本類型   意思   1.3   

一、封裝

  原文連結:http://www.ruanyifeng.com/blog/2010/05/object-oriented_javascript_encapsulation.html

  1.1 原始模型

var Cat = {//原型   name : "",
  color :""}

var cat1 = {};//執行個體
cat1.name = "小花";
cat1.color = "花";

var cat2 = {};//執行個體
cat2.name = "小黑";
cat2.name = "黑";

  這就是最簡單的封裝了,把兩個屬性封裝在一個對象中,但是這樣的封裝有兩個缺點

   1、如果寫多個執行個體會很麻煩

  2、執行個體也原型之間沒有任何關係

1.2 原始模型的改進

  寫一個函數解決代碼重複問題

  

function Cat(name,color){
  return {
    name : name,
    color:color
  }
}
var cat1 = Cat("小花",“花”);
var cat2 = Cat("小黑" , "黑");

  存在的問題:cat1和cat2之間沒有任何內在關係,不能看出他們是同一原型的執行個體

1.3 建構函式模式

  為瞭解決從原型對象產生執行個體問題,js提供一個建構函式模式。

  所謂的建構函式就是普通的函數,內部使用this對象,對建構函式內部使用new運算子就可以產生執行個體,並且this變數會綁定在執行個體對象上

var Cat = function(name,color){
  this.name = name;
  this.color = color;
}
var cat1 = new Cat("小花",“花”);
var cat2 = new Cat("小黑",“黑”);

js提供了一個instanceof 運算子,驗證原型對象和執行個體對象之間的關係
alert( cat1 instanceof Cat);//ture
alert( cat2 instanceof Cat);//true

  存在的問題:如果存在不變的屬性或方法時,比如var Cat = function(name,color){  this.name = name;

  this.color = color;
  this.style = "貓科"
  this.eat = function(){
    alert("吃老鼠");
  }
}

var cat1 = new Cat("小黑",“黑”);
var cat2 = new Cat("小花",“花”) ;
alert(cat1.style);//貓科
cat1.eat();//吃老鼠
alert(cat2.style);//貓科
cat2.eat();//吃老鼠

  對於每一個執行個體對象,type和eat都是一樣的內容,每一次產生一個執行個體,都必須為重複內容,多佔用一些記憶體,既不環保也缺乏效率

解決辦法:prototype模式

每一個建構函式都有一個prototype屬性,指向prototype對象,這個對象的所有屬性和方法都被建構函式的執行個體繼承

這意味著,我們可以把那些不變的屬性和方法直接定義在prototype上

var Cat = function(name,color){
  this.name = name;
  this.color = colr;
}
Cat.prototype = {
  constructor : Cat,
  style : "貓科",
  eat : function(){
    alert("吃老鼠");
  }
}
var cat1 = new Cat("小黑",“黑”);
cat1.eat();
alert(cat1.style);

var cat2 = new Cat("小花",“花”);
alert(cat2.style);
cat2.eat();

 prototype驗證方法

 isPrototypeOf方法判斷,某個prototype對象和執行個體之間的關係

  

alert( Cat.prototype isPrototypeOf (cat1) ) ;//true
alert(Cat.prototype isPrototypeOf( cat2 ) );//true

 instanceof 判斷執行個體和父類之間的關係

alert( cat1 instanceof Cat);//true
alert(cat2 instanceof Cat);//true

  每一個執行個體對象都有hasOwnProperty()方法,判斷屬性是區域屬性還是繼承prototype的屬性

alert( cat1 hasOwnProperty(name) );//true 為區域屬性
alert( cat1 hasOwnProperty(eat) );//false 為繼承屬性

  in 運算子用於判斷執行個體是否有某個屬性,無論是區域屬性還是繼承屬性

alert( "name" in cat1 );//true
alert("age" in cat1);//false

in還可以變數對象中的所有屬性
for( var pro in cat1){
  alert("cat1的”+pro +"屬性值為:"+cat1[pro]);
}

二、繼承

方法一、建構函式的繼承

function Animal (){
  this.species = "動物";
}

function Cat(name,color){
  this.name = name;
  this.color = color;
}
怎樣才能是貓繼承動物呢?
方法一:建構函式綁定
function Animal(){
  this.species = "動物";
}
function Cat(name,color){
  Animal.apply(this,arguments);
  this.name = name;
  this.color = color;
}
var cat1 = new Cat("小黑",“黑”);
alert( cat1.species );//“動物”

 方法二:原型模式

如果Cat的prototype成為Animal的執行個體,cat就可以繼承Animal了
Cat.prototype = new Animal();Cat.prototype.constructor = Cat;var cat1 = new Cat();alert( cat1.species );//"動物"

  

任何一個建構函式的原型對象都有一個constructor屬性,此屬性都指向這個建構函式。
當沒有Cat.prototype = new Animal();的時候,Cat.prototype.constructor指向的是Cat,但是繼承了Animal後,Cat.prototype.constructor = Animal;
所以要進行更正,Cat.prototype.constructor = Cat;
更重要的是執行個體也有constructor屬性,指向的是建構函式原型對象的constructor屬性
alert( cat1.constructor == Cat.prototype.constructor);//true
因此當Cat.prototype = new Animal();後 cat1.constructor = Animal,cat1明明是使用建構函式Cat產生的,但此時確是Animal,導致了原型鏈的混亂,所以要手動更正constructor,
Cat.prototype.constructor = Cat

方法三:直接繼承prototype
方法三是方法二的改進:由於Animal對象中,不變的屬性都可以直接寫入Animal.prototype。所以,我們也可以讓Cat()跳過 Animal(),直接繼承Animal.prototype。function Animal(){}
Animal.prototype.species = "動物";

然後將Cat的prototype指向Animal.prototype
Cat.prototype = Animal.prototype ;
Cat.prototype.constructor = Cat;
var cat1 = new Cat("小黑",“黑”);
alert(cat1.species);//動物

  

與前一種方法相比,這樣做的優點是效率比較高(不用執行和建立Animal的執行個體了),比較省記憶體。缺點是 Cat.prototype和Animal.prototype現在指向了同一個對象,那麼任何對Cat.prototype的修改,都會反映到Animal.prototype。

所以,上面這一段代碼其實是有問題的。請看第二行

  Cat.prototype.constructor = Cat;

這一句實際上把Animal.prototype對象的constructor屬性也改掉了!

  alert(Animal.prototype.constructor); // Cat

方法四:利用Null 物件作為中介
var F = function(){};
F.prototype = Animal.prototype;
Cat.prototype = new F()
Cat.prototype.constructor = Cat;
F作為空白對象,幾乎不佔記憶體,此時修改Cat的prototype對象,就不會修改Animal的prototype對象了
將上面方法封裝成一個函數方便使用
function extend(Child,Parent){
  var F = function(){};
  F.prototype = Parent.prototype;
  Child.prototype = new F();
  Child.prototype.constructor = Child;
  Child.uber = Parent.prototype;
}

extend(Cat,Animal);
var cat1 = new Cat();
alert( cat1.species);//動物

這個是YUI庫實現繼承的方法

另外,說明一點,函數體最後一行

  Child.uber = Parent.prototype;

意思是為子物件設一個uber屬性,這個屬性直接指向父物件的prototype屬性。(uber是一個德語詞,意思是"向上"、"上一層"。)

這等於在子物件上開啟一條通道,可以直接調用父物件的方法。這一行放在這裡,只是為了實現繼承的完備性,純屬備用性質。

方法五:拷貝繼承
將父物件的所有屬性和方法都拷貝到子物件中
function Animal(){}
Anima.prototype.species = "動物";

function extend2( Child,Parent){
  var p = Parent.prototype;
  var c = Child,prototype;
  for(var i in p){
    c[i] = p[i];
  }
  Child.uber = p
}

extend2( Cat,Animal );
var cat1 = new Cat("小花",“花”);
alert(cat1.species);//動物

 三、非建構函式的繼承

var Chines  = {
  nation : "中國"
};
var Doctor = {
  career:"醫生";
}
讓醫生繼承中國,成為中國醫生。

  方法一:object()方法

function object(parent){
  var F = function(){};
  F.prototype = parent;
  return new F();
}

var Doctor = object(Chinese);
Doctor.career = "醫生";
alert(Doctor.nation);//中國

  方法二:淺拷貝

function extend3(p){
  var c = {};
  for(var i in p){
    c[i] = p[i];
  }
  c.uber = p;
  return c;
}

var Doctor = extend3(Chines);
Doctor.career = "醫生";
alert(Doctor.nation);//中國

 

但是,這樣的拷貝有一個問題。那就是,如果父物件的屬性等於數組或另一個對象,那麼實際上,子物件獲得的只是一個記憶體位址,而不是真正拷貝,因此存在父物件被篡改的可能。

請看,現在給Chinese添加一個"出生地"屬性,它的值是一個數組。

  Chinese.birthPlaces = [‘北京‘,‘上海‘,‘香港‘];

通過extendCopy()函數,Doctor繼承了Chinese。

  var Doctor = extendCopy(Chinese);

然後,我們為Doctor的"出生地"添加一個城市:

  Doctor.birthPlaces.push(‘廈門‘);

發生了什麼事?Chinese的"出生地"也被改掉了!

  alert(Doctor.birthPlaces); //北京, 上海, 香港, 廈門

  alert(Chinese.birthPlaces); //北京, 上海, 香港, 廈門

所以,extendCopy()只是拷貝基本類型的資料,我們把這種拷貝叫做"淺拷貝"。這是早期jQuery實現繼承的方式。

方法三:深拷貝

 

function deepCopy(parent,c){
  var c = c || {};
  for( var i in parent){
    if( typeof(parent[i] == "object") ){
      c[i] = (parent[i].constructor == Array ) ? [] : {};
      deepCopy(parent[i] , c[i]);
    }else{
      c[i] = parent[i];
    }
  }
  return c;
}

var Doctor = deepCopy(Chinese);
Doctor.carrer = "醫生";
Chinese.brithPlace = ["上海",“北京”,“南京”]
alert(Doctor.nation);//"中國"
Doctor.brithPlace.push ("內蒙古");
alert(Doctor.brithPlace);//["上海",“北京”,“南京”,“內蒙古”]

 

  目前jquery庫使用的是這種繼承方法

 

 

 

 



  

 

 


 

  

 

js物件導向

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.