js的兩面-面向過程和麵向物件
阿新 • • 發佈:2019-01-24
js是面向過程還是面向過程?一直以來,頂級大師各有各的說法,這裡不敢妄加評論。 面向過程就是函數語言程式設計,按照傳統流程編寫一個又一個的函式來解決需求的方式。 面向過程適合一個人的專案,如果是團隊合作,別人想修改你的程式碼就不利於維護了。所以下面著重聊聊面向物件。 面向物件就是講你的需求抽象成一個物件,然後針對這個物件分析其屬性和方法。 面向物件的主要特點就是封裝,繼承,多型。 **一,封裝** 封裝就是把所需要的功能都放到一個物件裡。例如 var News=function(id,product,group){ this.id=id; this.product=product; this.group=group; } 也可以通過原型來新增屬性和方法。如: News.prototype.display=function(){ //顯示新聞 } 這樣我們將所需要的屬性和方法都封裝到我們抽象的News類裡面,當使用功能和方法時,我們不能直接使用News這個類,需要使用new關鍵字來例項化 新的物件。例如: var normal_news=new News(1024,"今日頭條","北京事業組"); 上面this新增的屬性和方法是在當前物件上新增的,然而原型建立物件都有prototype,用於指向其繼承的屬性,方法。這樣通過prototype繼承的方法 並不是物件自身的,所以在使用這些方法時,需要通過prototype一級一級查詢來得到。這樣你會發現通過this定義的屬性或者方法是該物件自身擁有的, 所以我們每次通過類建立一個新物件時,this指向的屬性和方法都會得到相應的建立,而通過prototype繼承的屬性或者方法是每個物件通過prototype 訪問到,所以我們每次通過類建立一個新物件時這些屬性和方法不會再次建立。如圖所示:
通過new關鍵字建立的物件實質是對新物件this的不斷賦值,並將prototype指向類的prototype所指向的物件,而類的建構函式外面通過點語法定義的屬性 方法是不會新增到新建立的物件上去的。因此要想在新建立的物件中使用display就得通過News類使用而不能通過this,如News.display,而類得原型 prototype上定義得屬性在新物件裡就可以直接使用,這是因為新物件的prototype和類的prototype指向的是同一個物件。 由於每次例項化物件的時候都需要用 new 關鍵字,所以js例項化有一種安全模式。如下 var News=function(id,product,group){ if(this.instanceof News){ this.id=id; this.product=product; this.group=group; }else{ return new News(id,product,group); } } 這樣就不用擔心忘記使用new關鍵字的問題了。 二,繼承 每個類都有三部分,第一部分是建構函式內的,這是供例項化物件複製用的,第二部分是建構函式外的,直接通過點語法新增的,這是供類使用的看, 例項化物件時訪問不到的,第三部分是類的原型中的,例項化物件可以通過其原型鏈間接地訪問到,也是為供所有例項所共用的。然而在繼承中所涉 及的不僅僅是一個物件。 在JavaScript中沒有繼承這一現有的機制,那怎麼實現呢! 2.1 子類的原型物件--類式繼承 //類式繼承 //宣告父類 function SuperClass(){ this.superValue=true; } //為父類新增共有方法 SuperClass.prototype.getSuperValue=function(){ return this.superValue; } //宣告子類 function SubClass(){ this.subValue=false; } //繼承父類 SubClass.prototype=new SuperClass(); SubClass.prototype.getSubValue=function(){ return this.subValue; }; 繼承很簡單,就是宣告兩個類而已,不過類式繼承需要將第一個類的例項賦值給第二個類的原型。 類的原型物件的作用就是為類的原型新增共有方法,但類不能直接訪問這些屬性和方法,必須通過原型prototype來訪問。而我們例項化一個父類的時候, 新建立的物件複製了父類的建構函式內的屬性與方法並且將原型__proto__指向了父類的原型物件,這樣就擁有了父類的原型物件上的屬性和方法, 並且這個新建立的物件可直接訪問到父類原型物件上的屬性和方法,如果我們將這個新建立的物件賦值給予類的原型,那麼子類的原型就可以訪問到父類 的原型屬性和方法,新建立的物件不僅僅可以訪問父類原型上的屬性和方法,同樣也可訪問從父類建構函式中複製的屬性和方法。你將這個物件賦值給予 類的原型,那麼這個子類的原型同樣可以訪問父類原型上的屬性和方法與從父類建構函式中複製的屬性和方法。 2.2 建立即繼承--建構函式繼承 //建構函式式繼承 //宣告父類 function SuperClass(id){ //引用型別共有屬性 this.books=['JavaScript','html','css']; //值型別共有屬性 this.id=id; } //父類宣告原型方法 SuperClass.prototype.showBooks=function(){ console.log(this.books); } //宣告子類 function SubClass(id){ //繼承父類 SuperClass.call(this,id); } //建立第一個子類的例項 var instance1=new SubClass(10); //建立第二個子類的例項 var instance2=new SubClass(11); instance1.books.push("設計模式"); console.log(instance1.books); //['JavaScript','html','css',‘設計模式’] console.log(instance1.id); //10 console.log(instance2.books);//['JavaScript','html','css'] console.log(instance2.id);//11 instance1.showBooks(); //TypeError 由於call這個方法可以更改函式的作用環境,因此在子類中,對superClass呼叫這個方法就是將子類中的變數在父類中執行一遍,由於父類中是給this 繫結屬性的,因此子類自然也就繼承了父類的共有屬性。由於這種型別的繼承沒有涉及原型的prototype.所以父類的原型方法自然不會被子類繼承, 而如果要想被子類繼承就必須要放在建構函式中,這樣創建出來的每個例項都會單獨擁有一份而不能共用,這樣就違背了程式碼複用的原則。為了綜合這兩 種模式的優點,就有了組合式繼承。 2.3 將優點為我所用--組合式繼承 //組合式繼承 //宣告父類 function SuperClass(name){ //值型別共有屬性 this.name=name; //引用型別共有屬性 this.books=['html','css','javaScript']; } //父類原型共有方法 SuperClass.prototype.getName=function(){ console.log(this.name); }; //宣告子類 function SubClass(name,time){ console.log(this.name); }; //宣告子類 function SubClass(name,time){ //建構函式式繼承父類name屬性 SuperClass.call(this,name); //子類中新增共有屬性 this.time=time; } //類式繼承 子類原型繼承父類 SubClass.prototype=new SuperClass(); //子類原型方法 SubClass.prototype.getTime=function(){ console.log(this.time); } 在子類建構函式中執行父類建構函式,在子類原型上例項化父類就是組合模式這樣就融合了類式繼承和建構函式繼承的有點,並過濾掉其缺點。 三,多型 多型就是同一個方法多種呼叫方式。 //多型 function add(){ var arg=arguments,len=arg.length; switch(len){ case 0: return 10; case 1: return 10+arg[0]; case 2: return arg[0]+arg[1]; } } 對於多型就是根據傳參不同做相應運算。將不同運算方法封裝在類內,這樣程式碼更容易懂。