天天看點

怎麼了解js的面向對象程式設計

面向對象的語言有一個标志,即擁有類的概念,抽象執行個體對象的公共屬性與方法,基于類可以建立任意多個執行個體對象,一般具有封裝、繼承、多态的特性!但JS中對象與純面向對象語言中的對象是不同的,ECMA标準定義JS中對象:無序屬性的集合,其屬性可以包含基本值、對象或者函數。可以簡單了解為JS的對象是一組無序的值,其中的屬性或方法都有一個名字,根據這個名字可以通路相映射的值(值可以是基本值/對象/方法)。

官方解釋

原型模式如類模式一樣,都是是一種程式設計泛型,即程式設計的方法論。另外最近大紅大紫的函數程式設計也是一種程式設計泛型。JavaScript之父Brendan Eich在設計JavaScript時,從一開始就沒打算為其加入類的概念,而是借鑒了另外兩門基于原型的的語言:Self和Smalltalk。

  既然同為面向對象語言,那就得有建立對象的方法。在類語言中,對象基于模闆來建立,首先定義一個類作為對現實世界的抽象,然後由類來執行個體化對象;而在原型語言中,對象以克隆另一個對象的方式建立,被克隆的母體稱為原型對象。

一、了解對象:

第一種:基于Object對象

怎麼了解js的面向對象程式設計
var person = new Object();
person.name = 'My Name';
person.age = 18;
person.getName = function(){
return this.name;
}
           
怎麼了解js的面向對象程式設計

第二種:對象字面量方式(比較清楚的查找對象包含的屬性及方法)

怎麼了解js的面向對象程式設計
var person = {
    name : 'My name',
    age : 18,
    getName : function(){
        return this.name;
    }
}
           
怎麼了解js的面向對象程式設計

JS的對象可以使用‘.’操作符動态的擴充其屬性,可以使用’delete’操作符或将屬性值設定為’undefined’來删除屬性。如下:

person.newAtt=’new Attr’;//添加屬性
alert(person.newAtt);//new Attr
delete person.age;
alert(person.age);//undefined(删除屬性後值為undefined);
           

二、對象屬性類型

ECMA-262第5版定義了JS對象屬性中特征(用于JS引擎,外部無法直接通路)。ECMAScript中有兩種屬性:資料屬性和通路器屬性

1、資料屬性:

資料屬性指包含一個資料值的位置,可在該位置讀取或寫入值,該屬性有4個供述其行為的特性:

[[configurable]]:表示能否使用delete操作符删除進而重新定義,或能否修改為通路器屬性。預設為true;

[[Enumberable]]:表示是否可通過for-in循環傳回屬性。預設true;

[[Writable]]:表示是否可修改屬性的值。預設true;

[[Value]]:包含該屬性的資料值。讀取/寫入都是該值。預設為undefined;如上面執行個體對象person中定義了name屬性,其值為’My name’,對該值的修改都反正在這個位置

要修改對象屬性的預設特征(預設都為true),可調用Object.defineProperty()方法,它接收三個參數:屬性所在對象,屬性名和一個描述符對象(必須是:configurable、enumberable、writable和value,可設定一個或多個值)。

如下:(浏覽器支援:IE9+、Firefox 4+、Chrome、Safari5+)

怎麼了解js的面向對象程式設計
var person = {};
Object.defineProperty(person, 'name', {
configurable: false,
writable: false,
value: 'Jack'
});
alert(person.name);//Jack
delete person.name;
person.name = 'lily';
alert(person.name);//Jack
           
怎麼了解js的面向對象程式設計

可以看出,delete及重置person.name的值都沒有生效,這就是因為調用defineProperty函數修改了對象屬性的特征;值得注意的是一旦将configurable設定為false,則無法再使用defineProperty将其修改為true(執行會報錯:can't redefine non-configurable property);

2、通路器屬性:

它主要包括一對getter和setter函數,在讀取通路器屬性時,會調用getter傳回有效值;寫入通路器屬性時,調用setter,寫入新值;該屬性有以下4個特征:

[[Configurable]]:是否可通過delete操作符删除重新定義屬性;

[[Numberable]]:是否可通過for-in循環查找該屬性;

[[Get]]:讀取屬性時調用,預設:undefined;

[[Set]]:寫入屬性時調用,預設:undefined;

通路器屬性不能直接定義,必須使用defineProperty()來定義,如下:

怎麼了解js的面向對象程式設計
var person = {
_age: 18
};
Object.defineProperty(person, 'isAdult', {
get: function () {
if (this._age >= 18) {
return true;
} else {
return false;
}
}
});
           
怎麼了解js的面向對象程式設計

alert(person.isAdult?'成年':'未成年');//成年

從上面可知,定義通路器屬性時getter與setter函數不是必須的,并且,在定義getter與setter時不能指定屬性的configurable及writable特性;

此外,ECMA-262(5)還提供了一個Object.defineProperties()方法,可以用來一次性定義多個屬性的特性:

怎麼了解js的面向對象程式設計
var person = {};
Object.defineProperties(person,{
_age:{
value:19
},
isAdult:{
get: function () {
if (this._age >= 18) {
return true;
} else {
return false;
}
}
}
});
           
怎麼了解js的面向對象程式設計

alert(person.isAdult?'成年':'未成年');//成年

上述代碼使用Object.defineProperties()方法同時定義了_age及isAudlt兩個屬性的特性

此外,使用Object.getOwnPropertyDescriptor()方法可以取得給定屬性的特性:

var descriptor = Object.getOwnPropertyDescriptor(person,'_age');

alert(descriptor.value);//19

對于資料屬性,可以取得:configurable,enumberable,writable和value;

對于通路器屬性,可以取得:configurable,enumberable,get和set

三、建立對象

使用Object構造函數或對象字面量都可以建立對象,但缺點是建立多個對象時,會産生大量的重複代碼,是以下面介紹可解決這個問題的建立對象的方法

1、工廠模式

怎麼了解js的面向對象程式設計
function createPerson(name, age, job) {
var o = new Object();
o.name = name;
o.age = age;
o.job = job;
o.getName = function () {
return this.name;
}
return o;//使用return傳回生成的對象執行個體
}
var person = createPerson('Jack', 19, 'SoftWare Engineer');
           
怎麼了解js的面向對象程式設計

建立對象交給一個工廠方法來實作,可以傳遞參數,但主要缺點是無法識别對象類型,因為建立對象都是使用Object的原生構造函數來完成的。

2、構造函數模式

怎麼了解js的面向對象程式設計
function Person(name,age,job){
this.name = name;
this.age = age;
this.job = job;
this.getName = function () {
return this.name;
}
}
var person1 = new Person('Jack', 19, 'SoftWare Engineer');

var person2 = new Person('Liye', 23, 'Mechanical Engineer');
           
怎麼了解js的面向對象程式設計

使用自定義的構造函數(與普通函數一樣,隻是用它來建立對象),定義對象類型(如:Person)的屬性和方法。它與工廠方法差別在于:

沒有顯式地建立對象

直接将屬性和方法指派給this對象;

沒有return語句;

此外,要建立Person的執行個體,必須使用new關鍵字,以Person函數為構造函數,傳遞參數完成對象建立;實際建立經過以下4個過程:

建立一個對象

将函數的作用域賦給新對象(是以this指向這個新對象,如:person1)

執行構造函數的代碼

傳回該對象

上述由Person構造函數生成的兩個對象person1與person2都是Person的執行個體,是以可以使用instanceof判斷,并且因為所有對象都繼承Object,是以person1 instanceof Object也傳回真:

alert(person1 instanceof Person);//true;
alert(person2 instanceof Person);//true;
alert(person1 instanceof Object);//true;
alert(person1.constructor === person2.constructor);//ture;
           

雖然構造函數方式比較不錯,但也存在缺點,那就是在建立對象時,特别針對對象的屬性指向函數時,會重複的建立函數執行個體,以上述代碼為基礎,可以改寫為:

怎麼了解js的面向對象程式設計
function Person(name,age,job){
this.name = name;
this.age = age;
this.job = job;
this.getName = new Function () {//改寫後效果與原代碼相同,不過是為了友善了解
return this.name;
}
}
           
怎麼了解js的面向對象程式設計

上述代碼,建立多個執行個體時,會重複調用new Function();建立多個函數執行個體,這些函數執行個體還不是一個作用域中,當然這一般不會有錯,但這會造成記憶體浪費。當然,可以在函數中定義一個getName = getName的引用,而getName函數在Person外定義,這樣可以解決重複建立函數執行個體問題,但在效果上并沒有起到封裝的效果,如下所示:

怎麼了解js的面向對象程式設計
function Person(name,age,job){
this.name = name;
this.age = age;
this.job = job;
this.getName = getName;
}
function getName() {//到處是代碼,看着亂!!
return this.name;
}
           
怎麼了解js的面向對象程式設計

3、原型模式

JS每個函數都有一個prototype(原型)屬性,這個屬性是一個指針,指向一個對象,它是所有通過new操作符使用函數建立的執行個體的原型對象。原型對象最大特點是,所有對象執行個體共享它所包含的屬性和方法,也就是說,所有在原型對象中建立的屬性或方法都直接被所有對象執行個體共享。

怎麼了解js的面向對象程式設計
function Person(){
}
Person.prototype.name = 'Jack';//使用原型來添加屬性
Person.prototype.age = 29;
Person.prototype.getName = function(){
return this.name;
}
var person1 = new Person();
alert(person1.getName());//Jack
var person2 = new Person();
alert(person1.getName === person2.getName);//true;共享一個原型對象的方法
           
怎麼了解js的面向對象程式設計

原型是指向原型對象的,這個原型對象與構造函數沒有太大關系,唯一的關系是函數的prototype是指向這個原型對象!而基于構造函數建立的對象執行個體也包含一個内部指針為:[[prototype]]指向原型對象。

執行個體屬性或方法的通路過程是一次搜尋過程:

首先從對象執行個體本身開始,如果找到屬性就直接傳回該屬性值;

如果執行個體本身不存在要查找屬性,就繼續搜尋指針指向的原型對象,在其中查找給定名字的屬性,如果有就傳回;

基于以上分析,原型模式建立的對象執行個體,其屬性是共享原型對象的;但也可以自己執行個體中再進行定義,在查找時,就不從原型對象擷取,而是根據搜尋原則,得到本執行個體的傳回;簡單來說,就是執行個體中屬性會屏蔽原型對象中的屬性;

原型與in操作符

一句話:無論原型中屬性,還是對象執行個體的屬性,都可以使用in操作符通路到;要想判斷是否是執行個體本身的屬性可以使用object.hasOwnProperty(‘attr’)來判斷;

原生對象中原型

原生對象中原型與普通對象的原型一樣,可以添加/修改屬性或方法,如以下代碼為所有字元串對象添加去左右空白原型方法:

String.prototype.trim = function(){
return this.replace(/^\s+/,'').replace(/\s+$/,'');
}
var str = ' word space ';
alert('!'+str.trim()+'!');//!word space!
           

原型模式的缺點,它省略了為構造函數傳遞初始化參數,這在一定程式帶來不便;另外,最主要是當對象的屬性是引用類型時,它的值是不變的,總是引用同一個外部對象,所有執行個體對該對象的操作都會其它執行個體:

怎麼了解js的面向對象程式設計
function Person() {
}
Person.prototype.name = 'Jack';
Person.prototype.lessons = ['Math','Physics'];
var person1 = new Person();
person1.lessons.push('Biology');
var person2 = new Person();
alert(person2.lessons);//Math,Physics,Biology,person1修改影響了person2
           
怎麼了解js的面向對象程式設計

4、組合構造函數及原型模式

目前最為常用的定義類型方式,是組合構造函數模式與原型模式。構造函數模式用于定義執行個體的屬性,而原型模式用于定義方法和共享的屬性。結果,每個執行個體都會有自己的一份執行個體屬性的副本,但同時又共享着對方方法的引用,最大限度的節約記憶體。此外,組合模式還支援向構造函數傳遞參數,可謂是集兩家之所長。

怎麼了解js的面向對象程式設計
function Person(name, age, job) {
this.name = name;
this.age = age;
this.job = job;
this.lessons = ['Math', 'Physics'];
}
Person.prototype = {
constructor: Person,//原型字面量方式會将對象的constructor變為Object,此外強制指回Person
getName: function () {
return this.name;
}
}
var person1 = new Person('Jack', 19, 'SoftWare Engneer');
person1.lessons.push('Biology');
var person2 = new Person('Lily', 39, 'Mechanical Engneer');
alert(person1.lessons);//Math,Physics,Biology
alert(person2.lessons);//Math,Physics
alert(person1.getName === person2.getName);//true,//共享原型中定義方法
           
怎麼了解js的面向對象程式設計

在所接觸的JS庫中,jQuery類型的封裝就是使用組合模式來執行個體的!!!

5、動态原型模式

組合模式中執行個體屬性與共享方法(由原型定義)是分離的,這與純面向對象語言不太一緻;動态原型模式将所有構造資訊都封裝在構造函數中,又保持了組合的優點。其原理就是通過判斷構造函數的原型中是否已經定義了共享的方法或屬性,如果沒有則定義,否則不再執行定義過程。該方式隻原型上方法或屬性隻定義一次,且将所有構造過程都封裝在構造函數中,對原型所做的修改能立即展現所有執行個體中:

怎麼了解js的面向對象程式設計
function Person(name, age, job) {
this.name = name;
this.age = age;
this.job = job;
this.lessons = ['Math', 'Physics'];
}
if (typeof this.getName != 'function') {//通過判斷執行個體封裝
  Person.prototype = {
    constructor: Person,//原型字面量方式會将對象的constructor變為Object,此外強制指回Person
    getName: function () {
      return this.name;
    }
  }
}
var person1 = new Person('Jack', 19, 'SoftWare Engneer');
person1.lessons.push('Biology');
var person2 = new Person('Lily', 39, 'Mechanical Engneer');
alert(person1.lessons);//Math,Physics,Biology
alert(person2.lessons);//Math,Physics
alert(person1.getName === person2.getName);//true,//共享原型中定義方法
           
怎麼了解js的面向對象程式設計

轉載自:https://www.cnblogs.com/shenjp/p/6517743.html