咱們經常使用的兩種編程模式javascript
POP--面向過程編程(Process-oriented programming)java
面向過程編程是以功能爲中心來進行思考和組織的一種編程方法,它強調的是系統的數據被加工和處理的過程,在程序設計中主要以函數或者過程爲程序的基本組織 方式,系統功能是由一組相關的過程和函數序列構成。面向過程強調的是功能(加工),數據僅僅做爲輸入和輸出存在。這種過程化的思想是一種很樸素和廣泛的思 想和方法,人類不少活動都是這種組織模式,好比工廠生產,企業服務等。面向過程以數據的加工處理過程爲主線,忽略了過程的所屬、邊界和環境,混淆了服務功 能和自我功能(好比人能夠砍樹,這就是一種服務功能,有輸入也有輸出;它能夠提供給外部,而行走,則是自我功能,沒有輸入也沒有輸出),外部環境和內部組 織,以及環境數據和原料性數據之間的區別。從思惟上來說,面向過程更強調細節,忽視了總體性和邊界性,但這與現實世界有很大的出入,由於現實世界中,這種過程都不是孤立存在的,而是從屬於某個對象,所以,面向過程雖然反映了現實世界的而一個方面(功能),但沒法更加形象的模擬或者表示現實世界。好比以下這種寫法:編程
function A(){ } function B(){ A(); }
感受互相之間獨立存在的安全
OOP--面向對象編程(Object Oriented Programming)app
世界是由一個個對象組成的,所以面向對象的思惟方式更加接近現實世界,面向對象編程的組織方式也更加貼近現實世界。面向對象以對象爲中心,將對象的內部組織與外部環境區分開來,將表徵對象的內部屬性數據與外部隔離開來,其行爲與屬性構成一個總體,而系統功能則表現爲一系列對象之間的相互做用的序列,能更加 形象的模擬或表達現實世界。在編程組織中,對象的屬性與方法再也不像面向過程那樣分開存放,而是視爲一個總體(程序的最終實現其實仍是分離的,但這僅僅是物 理實現上的,不影響將對象的這兩個部分視爲一個總體),所以具備更好的封裝性和安全性(表徵內部的屬性數據須要經過對象的提供的方法來訪問)。面向對象強 調的是總體性,所以面向對象與面向過程在不少方面是能夠互補的。同時因爲對象繼承和多態技術的引入,使得面向對象具備更強、更簡潔的對現實世界的表達能 力。從而加強了編程的組織性,重用性和靈活性。好比以下這種寫法:ide
var obj={ default:{}, config:{}, init:function(){ this.A(); this.B(); }, A:function(){ this.config.name=’A’; }, B:function(){ this.config.name=’B’; } }
這種看起來就有點OO的感受了,把屬性和方法封裝在一個對象裏面。面向對象的開發模式是逐漸流行起來,且被開發者們普遍推廣的模式。函數
Javascript是一門基於對象的語言,但它不是一種真正的面向對象編程(OOP)語言,對象的屬性都是以鍵值對的形式存在的,就是平時咱們所說的數據字典。把對象的屬性和方法封裝在一個對象裏面,通常經過四種方式:原始對象,構造函數,原型模式,構造函數和原型混合模式。我我的認爲面向對象的寫法讓系統更具備可維護性,可擴展性,可重用性,還有可配置性,功能模塊也讓人感受一目瞭然。性能
下面來說一下關於javascript對應的OOP的封裝,繼承,多態三大特性學習
(1)對象字面量或者實例化對象模式this
var Obj = { Id: '', Width: '', Height: '', init: function() { }, eventsBind: function() { }, renderView: function() { } }
或者
Var obj=new Object();
Obj.id=’’;….
Obj. renderView….
這種封裝方式比較簡單, 最經常使用的一種模式,簡潔明瞭,適用於簡單的封裝.這兩種寫法,推薦字面量的方式。
(2) 構造函數模式
function MyPlugin(name, pwd) { this.name = name; this.pwd = pwd; this.Init = function() { }; this.renderView = function() { }; this.bindEvent = function() { } }
這種方式和C#的構造函數方式相似,每次實例化,全部的元素和方法都被從新建立,從新分配內存,互不影響 ,缺點在於實例化時公有的方法指向不一樣的地址,形成沒必要要的浪費,性能欠佳 ,方法應該共享纔對,下面的混合模式會講到。
(3)原型prototype
var MyPlugin = function(){ } MyPlugin.prototype = { obj: { name: ’aaaaa’ }, Init: function() { }, renderView: function() { }, bindEvent: function() { } }
這種方式的特色是全部的在原型鏈上的方法都是共享的,而且指向同一個地址。這裏須要注意,若是原型鏈上面一個屬性對應的是object對象會有一個問題,就是在一個實例對象裏面給這個對象的屬性賦值會影響另外一個實例對象.
var a=new MyPlugin();
var b= new MyPlugin();
a.obj.name=’bbbbbbbb’
這樣在b中的obj對象的name值會被改變。緣由是這裏的obj是引用類型,a.obj和b.obj指向的是同一個地址,若是是值類型則不會存在這樣的問題
(4)構造函數和原型混合模式
這種模式的意義在於實例化的時候保持屬性的相互獨立,只共享方法.在作封裝的時候推薦使用這種方式.剛纔咱們所說的構造函數的缺點和原型模式的缺點在這裏獲得改善。
var MyPlugin = function(name) { this.name = name; } MyPlugin.prototype = { Show: function(){ Console.log(this.name); } }
(5) 其餘寫法
var MyPlugin = function(config) { var fn1 = function() { } var fn2 = function() { } return { test1: fn1, test2: fn2 } }
或者
var MyPlugin = function(config) { var obj = new Object(); obj.fn1 = function() { } obj.fn2 = function() { } return obj; }
剛纔咱們提到構造函數模式實例化以後對象的方法地址指向不同,而原型模式,地址指向一致的說法。
咱們來看一下:
var funcDemo=function (name) { } funcDemo.prototype.init=function(){ } var a=new funcDemo('aaa'); var b=new funcDemo('bbb'); console.log(a.init===b.init);
輸出結果是true.
再來看看構造函數:
var funcDemo=function (name) { this. Init=function(){ } } var a=new funcDemo('aaa'); var b=new funcDemo('bbb'); console.log(a.init===b.init);
輸出結果是false.
1.call ,apply
var funcA = function() { this.show = function() { console.log('funcA'); } } var funcB = function() { funcA.call(this); } var b = new funcB(); b.show();
在這裏 funcA.call(this);這句至關因而在funB的內部裏面執行了
this.show=function(){
console.log('funcA');
}
而當前的做用域在funcB內部,this指向的是funB的實例化對象,也就是把show方法賦值給了funcB的實例化對象
有一點須要注意,若是是直接執行funcB(),那麼當前做用域就是window了,至關於把show方法賦值給了window。等同於
window.show==function(){ console.log('funcA'); }
2.原型繼承
var funcA = function() { this.show = function() { console.log('funcA'); } } var funcB = function() { } funcB.prototype = new funcA(); var b = new funcB(); b.show();
這一句 funcB.prototype=new funcA();
至關於把funB的原型指向了funcA的實例
等同於
funcB. Prototype={ Show:function(){ console.log('funcA'); } }
咱們能夠寫一個函數來實現繼承
var extend = function(fn, newfn) { var F = function () { }; F.prototype = fn.prototype; newfn.prototype = new F(); newfn.prototype.constructor = newfn; newfn.prototype.superClass =fn.prototype }
3.屬性拷貝
咱們先實現屬性拷貝的代碼:
var DeepCopy = function(newobj, obj) { for(var prop in obj) { if(obj.hasOwnProperty(prop)) { var item = obj[prop]; if(Object.prototype.toString.call(item) == '[object Object]') { newobj[prop] = {}; arguments.callee(newobj[prop], item); } else if(Object.prototype.toString.call(item) == '[object Array]') { newobj[prop] = []; arguments.callee(newobj[prop], item); } else newobj[prop] = item; } } return newobj; }
而後將A對象裏的屬性賦給B對象 :
var A = { obj: { name: 'AAAA' }, Arr: [ { name: 'AAAA' }, { name: 'BBBB' } ] } var B = { name: ’BBBBBBBBBB’ } DeepCopy(B, A)
和其餘語言同樣 ,先定義一個基類,js不存在類的概念,這裏我只是類比的說法.
var baseClass=function(){ this.init=function(){ this.test.apply(this,arguments); } }
這裏執行init實際上是執行了子類的test方法
咱們再定義A類,B類
var A=function(){ this.test=function(){ alert('AAAAAAAAAA'); } } var B=function(){ this.test=function(){ alert('BBBBBBBBBBBBB'); } }
而後將A類,B類的原型指向baseClass的實例,就是咱們剛纔說的原型繼承
A.prototype=new baseClass(); B.prototype=new baseClass();
最後咱們實例化
var a=new A(); a.init(); var b=new B(); b.init();
分別輸出'AAAAAAAAAA','BBBBBBBBBBBBB'
咱們所實現的js這種多態和強類型語言的多態感受有所不一樣,顯得不那麼直觀,由於強類型的語言大都是經過抽象類或者接口聲明方法,而後經過子類實現,調用方法的時候實際上是實例化抽象類或者接口,指向子類對象的實例。而咱們這裏的實現其實是經過父類聲明方法調用子類對父類聲明方法的實現。
Js 面向對象的這部分東西我也是大體的講了一下,講解的不恰當或者不完善的地方,還請詳細指出。若是有興趣交流學習,請看最上角,加入個人qq技術羣207058575.