1、封裝javascript
原文連接:http://www.ruanyifeng.com/blog/2010/05/object-oriented_javascript_encapsulation.htmlhtml
1.1 原始模型java
var Cat = {//原型 name : "",
color :"" }
var cat1 = {};//實例
cat1.name = "小花";
cat1.color = "花";
var cat2 = {};//實例
cat2.name = "小黑";
cat2.name = "黑";
這就是最簡單的封裝了,把兩個屬性封裝在一個對象中,可是這樣的封裝有兩個缺點jquery
一、若是寫多個實例會很麻煩編程
二、實例也原型之間沒有任何關係數組
1.2 原始模型的改進app
寫一個函數解決代碼重複問題函數
function Cat(name,color){
return {
name : name,
color:color
}
}
var cat1 = Cat("小花",「花」);
var cat2 = Cat("小黑" , "黑");
存在的問題:cat1和cat2之間沒有任何內在關係,不能看出他們是同一原型的實例this
1.3 構造函數模式spa
爲了解決從原型對象生成實例問題,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]);
}
2、繼承
方法1、構造函數的繼承
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
方法四:利用空對象做爲中介
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);//動物
3、非構造函數的繼承
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庫使用的是這種繼承方法
另加:面向對象抽象:原文連接:http://www.cnblogs.com/wangfupeng1988/p/3687346.html
如何更好的解決變化的問題?
提取抽象,隔離具體
什麼是「抽象」? 抽象就是不變的東西,
什麼是「具體」?具體是實際執行的,
咱們應該依賴於抽象編程,而不是依賴於具體編程。應該把程序中的共性抽象出來,而且把具體實現的部分隔離開來,讓他們都依賴於抽象,而且互不影響。這其實就是設計。
2.3 SOLID五大原則
系統設計的5大原則,簡寫分別是S、O、L、I、D。
若是詳細分析這5大原則,其實他們都是圍繞着「提取抽象、隔離具體」來的。