這幾天又好好讀了JavaScript高級程序設(shè)計限府。其中在理解對象和創(chuàng)建對象這塊有了很多新的見解和認(rèn)知
JavaScript的對象
由于我最開始學(xué)習(xí)的是Java秽荤,所以很容易就會把JavaScript和Java聯(lián)系到一起赂弓。不同于Java中橙依,ES5還是沒有類這個概念的喘漏。沒辦法通過類來實(shí)例化對象,更別說什么構(gòu)造函數(shù)繼承這些了椒功。
ECMA-262把對象定義:無序?qū)傩缘募希鋵傩钥梢园局抵鞘玻瑢ο蠡蛘吆瘮?shù)动漾。
簡單地創(chuàng)建對象
JavaScript創(chuàng)建對象很簡單,最基本的創(chuàng)建方法:
var obj = new Object(); // 通過Object構(gòu)造函數(shù)來創(chuàng)建對象
// 或者
var obj2 = {//....balabala} // 通過對象字面量來創(chuàng)建對象
創(chuàng)建一個對象后,我們來了解一下對象的屬性類型:
對象的屬性類型
數(shù)據(jù)屬性:
數(shù)據(jù)屬性說的簡單點(diǎn)就是對象中具有具體數(shù)據(jù)值的屬性。如下中荠锭,name旱眯,age均為person的數(shù)據(jù)屬性。
var person = {
name: 'www',
age: 18
}
每一個數(shù)據(jù)屬性呢证九,又包含四個屬性值删豺。顯示在對象中的是其value屬性±⒘可以由Object.defineProperty來定義它的這四個屬性呀页。
四個屬性:
1、configurable : 表示這個屬性是否是可配置的拥坛。默認(rèn)為true蓬蝶,如果修改為false,那么不可再修改這個屬性的configurable屬性和enumerable屬性猜惋,也不可用delete來刪除這個屬性丸氛。
2、enumerabel : 表示這個屬性是否是可枚舉的著摔。默認(rèn)為true缓窜。在比如Array這種引用類型中,它的那些例如sort()谍咆,push()等等的方法的這個屬性均為false禾锤,這樣我們就沒辦法用for in 來枚舉處其屬性愚臀。
3先慷、writable: 表示是否可修改惹恃。默認(rèn)為true。如果改為false港粱,那么就沒辦法再修改這個屬性值。
4旦签、value : 表示這個屬性的屬性值查坪。上面的name的value屬性就死 'www'。
Object.defineProperty方法的使用:
Object.defineProperty(person,name,{
// 這里我就取默認(rèn)情況
configurable: true,
enumerable: true,
writable: true,
value: 'www'
})
訪問器屬性
訪問器屬性沒有具體的數(shù)據(jù)值宁炫,它提供一個getter和setter方法偿曙。
?這個就有點(diǎn)繞了。因?yàn)槲覀冊诙x對象的時候羔巢,像上面person這種望忆,它的name屬性我們是可以看得到的罩阵,很明顯就容易理解數(shù)據(jù)屬性。那么具體什么叫做訪問器屬性呢启摄?
?這里通過一個例子來說明一下稿壁。首先我們定義一個girl對象。女孩子的年齡都是保密的歉备,所以我們給它設(shè)置一個_age屬性傅是。那我們可以通過一個ask訪問器屬性來獲取到這個女孩的年齡。
var girl = {
_age:18, // 下劃線_開頭的屬性是一種約定俗成蕾羊,一般不作為公開屬性喧笔, 只能通過對象方法訪問。
mood: "happy"
}
// 定義一個ask訪問器屬性龟再。
Object.defineProperty(girl,"ask",{
// 我們可以通過girl.ask 來獲取到這個女孩18歲了书闸。
get: function () {
return this._age;
}
// 要是我們給這個女孩設(shè)置一個 girl.ask = 20; 就像詢問這個女孩,你是不是20了吸申?女孩就會很生氣梗劫。
她的mood屬性就會變成 angry。
set: function(val) {
if(val > 18) {
this.mood = "angry";
}
}
})
這就是訪問器屬性截碴∈崆龋可以通過這個屬性獲取到其他屬性,也可以在設(shè)置這個屬性的時候日丹,導(dǎo)致別的屬性發(fā)生變化走哺。同時,這個屬性除了get和set兩個屬性哲虾,還有configurable和enumerable兩個屬性丙躏,類似于數(shù)據(jù)屬性。
關(guān)于讀取屬性和設(shè)置多個屬性等束凑,這些都是基本知識的介紹晒旅,可以閱讀《JavaScript高級程序設(shè)計》
創(chuàng)建對象
上面說了很簡單通過Object構(gòu)造函數(shù)和對象字面量來創(chuàng)建對象。接下來說幾個逼格稍微高一點(diǎn)的汪诉。
工廠模式
JavaScript工廠模式創(chuàng)建對象就像是無限調(diào)用一個方法來創(chuàng)建Object對象废恋。這個方法就像是一個工廠一樣,如下代碼所示:
function factory(name,age,sex) {
var o = new Object();
o.name = name;
o.age = age;
o.sex = sex;
o.say = function () {
console.log(o.name+'--'+o.age+'--'+o.sex);
}
return o;
}
// 創(chuàng)建實(shí)例對象
var p1 = factory("wyh",18,"man");
p1.say(); // wyh--18--man
缺點(diǎn):缺點(diǎn)太明顯了扒寄,我創(chuàng)造的對象全是Object的鱼鼓,沒有標(biāo)識度。實(shí)例化的p1,p2,p3也沒有任何的聯(lián)系该编。
所以迄本,讓我們來看構(gòu)造函數(shù)模式。
構(gòu)造函數(shù)模式
還是沒辦法摒棄Java的學(xué)習(xí)思想课竣。所以這里我很容易就聯(lián)想到Java的構(gòu)造函數(shù)嘉赎。JavaScript的構(gòu)造函數(shù)模式創(chuàng)建對象還真的很像Java的構(gòu)造函數(shù)置媳。如下代碼:
// 與Java的構(gòu)造函數(shù)何其相似。函數(shù)內(nèi)部沒有顯示的創(chuàng)建對象的動作曹阔。
function Animal(name,leg) {
this.name = name;
this.leg = leg;
this.sayName = function () {.....};
}
// 實(shí)例化兩個對象:通過new 來創(chuàng)建半开。
var tiger = new Animal("tiger",4);
var lion = new Animal("lion",4);
console.log(tiger.constructor); // Function: Animal。
好像很不錯赃份〖挪穑可是有嚴(yán)重的問題所在。每當(dāng)實(shí)例化一個Animal對象抓韩,就會給sayName開辟一個內(nèi)存空間纠永,如果這個對象有很多函數(shù)屬性,如果要實(shí)例化很多的Animal對象谒拴,就會很占用內(nèi)存空間了尝江。當(dāng)然,我們可以把這個對象的函數(shù)屬性放在全局變量中英上,但是同時也會污染了全局環(huán)境炭序。
所以,來看原型模式:
原型模式
由于我們創(chuàng)建的每個函數(shù)都是有一個叫做prototype(原型)的屬性苍日,該屬性作為一個指針指向一個對象惭聂。
所以我們可以利用這個prototype來進(jìn)行創(chuàng)建對象。這里僅僅介紹簡單的原型模式創(chuàng)建相恃,更具體的可以閱讀《JavaScript高級程序設(shè)計》辜纲。
代碼:
function Person() {
}
Person.prototype.name = "wyh";
Person.prototype.age = 18;
Person.prototype.list = [],
Person.prototype.sayName = function () {
console.log(this.name);
}
var p1 = new Person();
var p2 = new Person();
p1.list.push("a");
console.log(p2.list) // ['a']
console.log(p1.constructor) // Person
console.log(p1.sayName == p2.sayName) // true;
解決了上面構(gòu)造函數(shù)模式中的函數(shù)占用內(nèi)存的問題了。因?yàn)樗袑?shí)例化的對象的函數(shù)指向的地址是一樣的拦耐。
缺點(diǎn):沒有辦法自定義初值耕腾,不具變通能力。
如上面所示杀糯,我在p1中操作了數(shù)組list扫俺,p2的也發(fā)生改變,這是不想看到的固翰。原型模式一個實(shí)例化對象更改引用類型狼纬,所有的都會改變。
組合使用構(gòu)造函數(shù)模式和原型模式
既然構(gòu)造函數(shù)和原型模式都各自有問題倦挂,而且還可以互補(bǔ)畸颅。那么我們就把這兩種模式結(jié)合到一起使用担巩。
代碼如下所示:
function Person(name,age,sex) {
this.name = name;
this.age = age;
this.sex = sex;
this.shoplist = [];
}
Person.prototype = {
sayName: function () {
console.log(this.name);
},
constructor: Person
}
var p1 = new Person("wyh",18,"man");
var p2 = new Person("MJ",16,"wm");
p1.shoplist.push("apple");
p2.shoplist.push("shoes");
console.log(p1.sayName == p2.sayName); // true
console.log(p1.constructor) // Person
console.log(p2.shoplist) // ['shoes']
我們把公用的方法用原型模式構(gòu)造方援,省去內(nèi)存消耗。把不公用的引用類型用構(gòu)造函數(shù)模式構(gòu)造涛癌,避免混淆犯戏。
同時送火,也可以通過傳入初始化數(shù)據(jù)來自定義我們需要的對象的樣子。
這種方法(模式)是使用度最普遍的一種方法先匪。
動態(tài)原型模式
把上面那種模式中种吸,所有的信息都封裝在構(gòu)造函數(shù)中。但是又為了防止重復(fù)的初始化原型呀非,于是就有了動態(tài)原型模式坚俗。話不多說,請看代碼:
function Person(name,age) {
// 屬性
this.name = name;
this.age = age;
// 動態(tài)地進(jìn)行原型的構(gòu)造方法
if(typeof this.sayName != "function") {
console.log('----1------');
Person.prototype.sayName = function () {
console.log(this.name);
}
// 其他的需要原型處理的屬性
}
}
var p1 = new Person("wyh",18); // 1
p1.sayName(); // wyh
var p2 = new Person("mj",16); // 什么都沒有
p2.sayName(); // mj
當(dāng)我們第一次實(shí)例化Person的時候岸裙,會動態(tài)地創(chuàng)建原型屬性猖败,并且只創(chuàng)建這一次。
寄生構(gòu)造函數(shù)模式
這個降允。恩闻。。我看不太出來和工廠模式的區(qū)別剧董。要說實(shí)例化的時候用了new操作符的話幢尚,那我還是不明白這個模式的作用所在。
穩(wěn)妥構(gòu)造函數(shù)模式
同上面這個寄生構(gòu)造函數(shù)模式翅楼,我暫時看不出來它的用處所在尉剩。