Javascript中的類javascript
類是對象的模板,用於建立共享一系列屬性和方法的相似對象。java
使用new關鍵字調用函數,能夠建立對象實例。編程
1 function Accommodation(){}; 2 3 var house = new Accommodation(); 4 5 var apartment = new Accommodation();
同一模板的對象實例之間互無關聯,這些對象實例是徹底獨立的變量,只不過共享同一個模板結構而已。瀏覽器
類的擴充:閉包
1.找出對象的構造器app
經過new關鍵字建立的對象的實例,有一個額外的屬性:constructor,該屬性指向建立該對象時所使用的JavaScript構造函數。編程語言
1 house.constructor === Accommodation;//true 2 3 apartment.constructor === Accommodation;//true
關鍵字instanceof也能夠檢查對象是不是某個構造函數的實例。函數
house instanceof Accommodation; apartment instanceof Accommmodation;
2.經過原型添加屬性和方法this
Javascript中的每一個函數(構造器)都有一個叫prototype的屬性,這個屬性指向一個對象,咱們用關鍵字new來建立一個「類」的對象實例時,實例中包含的屬性和方法都來自prototype所指向的這個對象。spa
//定義構造函數 function Accommodation(){} //爲這個類添加屬性 Accommodation.prototype.floors = 0; Accommodation.prototype.rooms = 0; Accommodation.prototype.sharedEntrance = false; //爲這個類添加方法 Accommodation.prototype.lock = function(){}; Accommodation.prototype.unlock = function(){}; //建立對象實例 var house = new Accommodation(); var apartment = new Accommodation(); //讀取對象實例的屬性 console.log(house.floors);//0 console.log(apartment.rooms);//0 //修改對象屬性的值 house.floors = 2; apartment.sharedEntrance = true; //調用對象實例的方法 house.unlock(); apartment.lock();
prototype屬性自己是一個對象,這個對象被關聯在扮演「類」這個角色的函數身上,所以,還能夠用對象直接量標記法爲構造函數添加屬性和方法。
//定義構造函數 function Accommodation(){} //爲這個類添加屬性和方法 Accommodation.prototype = { floors: 0, rooms: 0, sharedEntrance: false, lock: function(){}, unlock: function(){} }
prototype這個關鍵字有一個強大的特性:容許在對象實例已經被建立後繼續添加屬性和方法,新增屬性和方法會自動添加到全部對象實例中,不論是已建立的仍是將要建立的。
3.經過做用域添加屬性和方法
javascript函數體內定義的變量和函數,做用域都限於函數體內,在該函數體外沒法訪問這些變量和函數——對這些函數和變量來講,包裹他們的外層函數提供了一個沙箱般的編程環境,或者說一個閉包。
4.上下文和this關鍵字
javascript中this關鍵字表明的是一個函數的上下文環境,這個上下文環境大多數狀況下指的是函數運行時封裝這個函數的那個對象。
//在全部函數以外,this表示的是全局window對象 console.log(this === window);//true //由於doSomething函數在對象外部被調用,this指向的是瀏覽器中的window對象 function doSomething(){ console.log(this === window); //true } doSomething(); var house = { floors: 2, isLocked = false, lock: function(){ console.log(this === house);//true 由於this關鍵字表明的是包含這個方法的那個對象 //也能夠把this看作house對象的替身,可使用點標記法 this.isLocked = true; }, } house.lock(); console.log(house.isLocked);//true
對象中的嵌套函數其上下文環境是全局的window對象,而非包含它的那個對象。但咱們能夠在this指向包含這個函數的對象時,將this的值保存在一個變量中,在用到該對象時用這個變量代替。
var apartment = { islocked: false, lock: function(){ var that = this; //設置isLocked屬性 this.isLocked = true; function doSomething(){ console.log(this === apartment);//false console.log(this === window);//false console.log(that === apartment);//false //經過that變量來修改apartment對象的isLocked屬性 that.isLocked = false; } doSomething(); }, } apartment.lock(); console.log(apartment.isLocked);//false
在使用new關鍵字建立對象時,this指向的值和通常狀況下又有區別,這種狀況下this指向的是經過構造函數所建立的那個對象實例。也由於這個特性,得以在構造函數中經過this來設置全部對象實例的屬性和方法,而非像以前那樣使用prototype關鍵字。
//定義一個新的構造函數來表示一種住宅 function Accommodation(){ //this關鍵字指向的是經過這個「類」建立的對象實例 this.floors = 0; this.rooms = 0; this.sharedEntrance = false; this.isLocked = false; this.lock = function(){ //函數中的this通常指向包含函數的那個對象,本例中的this指向的是建立的對象實例,由於這個函數是經過這個被建立的對象實例來調用的 this.isLocked = true; }; this.unlock = function(){ this.isLocked = false; } } //經過構造函數來建立對象實例 var house = new Accommodation(); var apartment = new Accommodation(); //讀取和修改屬性值,調用方法等操做都和普通對象同樣 console.log(house.floors); house.floors = 2; apartment.lock();
開發者通常會結合使用prototype和this關鍵字來定義對象實例的屬性和方法,其中前者用來定義方法,後者用來定義屬性。每次經過構造器建立一個新的對象實例,構造函數都會被執行一次。之因此結合使用這兩個關鍵字,是爲了不每次初始化一個對象實例時都要執行那些對方法進行初始化的代碼。經過prototype關鍵字定義的方法只需定義一次,而後就能夠爲全部經過這個構造函數建立的對象所用,這使得對象的建立變得更加高效;
開發者們喜歡在構造函數中使用this關鍵字來設置屬性的另外一個緣由是能夠給構造函數傳遞參數,這樣咱們就能在調用構造函數時經過傳遞參數來對某些屬性進行初始化了。
咱們也能夠向構造函數傳遞一個對象直接量做爲惟一的參數,這個對象直接量包含了進行屬性設置所需的全部初始值。
5.方法的鏈式調用
要實現鏈式調用,只需在「類」中的每一個方法最後經過this關鍵字返回對象實例的引用便可。
6.繼承
傳統編程語言的一項關鍵功能就是能夠建立一些新的類來繼承或者擴展某個父類的屬性和方法,這些新的類和該父類都有某種相似的邏輯關聯,這些新的類被稱爲子類。
Javascript中也能夠實現這種繼承,不過是經過Javascript的原型鏈來實現,被稱爲原型繼承。
//定義一個有兩個方法的類 function Accommodation(){} Accommodation.prototype.lock = function(){} Accommodation.prototype.unlock = function(){} //定義一個構造函數,它將成爲咱們的子類 function House(defaults) { defaults = defaults || {}; //將本類全部實例的floors屬性初始化爲2 this.floors = 2; this.rooms = defaults.rooms ||7; } //將House類的原型設爲Accommodation「類」的一個實例,使用關鍵字new來調用Accommodation的構造函數,這樣就能建立並返回一個包含全部屬性和方法的對象。這個對象被傳遞給House"類"的原型,這樣House"類"就得以繼承Accommodation 的全部內容 House.prototype = new Accommodation(); //對象實例的constructor屬性指向建立該對象的那個構造函數,然而因爲House繼承了Accommodation的全部內容,constructor值也被複制了,因此咱們如今須要重設constructor值,使其指向新的子類。若是沒有這一步,經過House"類"建立的對象就會報告說他們是經過Accommodation「類」建立的。 House.prototype.constructor = House;
封裝:
當經過繼承對已有的類進行改變或特殊化時,父類的全部屬性和方法對子類都是可用的,在子類中不須要額外聲明或定義任何東西就能使用父類的屬性和方法,這種特性被稱爲封裝。子類之須要定義那些在父類基礎上新增的屬性和方法便可;
多態:
在構造一個新的子類來繼承並擴展一個「類」的時候,你可能須要將某個方法替換爲一個同名的新方法,新方法和原方法功能相似,但對子類作出了針對性的改變,這就是多態,Javascript中實現多態,只需重寫一個函數並給它一個和原方法形同的方法便可;
call和apply
arguments對象
公有,私有以及受保護的屬性和方法
在構造函數中經過var定義的變量其做用域侷限於該構造函數內——在prototype上定義的方法沒法訪問這個變量,由於這些方法有本身的做用域。要想經過公有的方法來訪問私有的變量,須要建立一個同時包含兩個做用域的新做用域,爲此,咱們能夠建立一個自我執行的函數,稱爲閉包。該函數徹底包含了類的定義,包括全部私有變量以及原型方法。
javascript有一個非強制性的但頗有用的編程慣例,就是對全部私有變量或函數名加一個下劃線(_)做爲前綴,以標識他們是私有的,這有助於你及項目組的其餘成員更好的理解每一個類的做者的意圖。
// 咱們將類定義包在一個自我執行的函數裏,這個函數返回咱們所建立的類並將其保存在一個變量中,一邊在後面的代碼中使用 var Accommodation = (function(){ //定義類的構造函數,由於處於一個新的函數內,咱們也切換到了一個新的做用域中,因此可使用與保存函數返回值得那個變量相同的名字 function Accommodation(){ //此處定義的變量都是私有的,這些變量在當前做用域以外不可用,能夠經過變量名添加下劃線前綴來標識這一點 var _isLocked = false, _isAlarmed = false, _alarmMessage = "Alarm activated!"; //僅在當前做用域中定義的函數(未在構造函數上定義的)也是私有的 function _alarm(){ _isAlarmed = true; alert(_alarmMessage); } function _disableAlarm(){ _isAlarmed = false; } //全部定義在原型上的方法都是公有的,當咱們在此處建立愛你的類在閉包結束處被返回後,就能夠在當前做用域以外訪問這些方法了 Accommodation.prototype.lock =function(){ _isLocked = true; _alarm(); } Accommodation.prototype.unlock = function(){ _isLocked = false; _disableAlarm(); } //定義一個getter函數來對私有變量_islocked的值進行只讀訪問——至關於把該變量定義爲了受保護的 Accommodation.prototype.getIsLocked = function(){ return _isLocked; } //定義一個setter函數來對私有變量_alarmMessage進行只寫訪問——至關於將其定義爲了受保護的 Accommodation.prototype.setAlarmMessage = function(message){ _alarmMessage = message; } //返回在這個做用域中建立的類,使之在外層做用域中即後面的代碼中的全部位置均可用,只有公有的屬性和方法是可用的 return Accommodation; } }());