JavaScript中沒有類的概念奥此,這給OO
的編程帶來了一些困難款熬,但并不是不能解決的惕虑,使用以下幾種的設(shè)計(jì)模式可以解決JavaScript對象的創(chuàng)建問題庇忌。
工廠模式
工廠模式是創(chuàng)建對象的一種抽象方式义屏。
但由于JavaScript不支持類靠汁,可以用函數(shù)代替,這個(gè)函數(shù)封裝了特定接口創(chuàng)建對象的細(xì)節(jié)闽铐。
function createPerson(name, age, job){
var i=0;
var o = new Object();
o.name = name;
o.age = age;
o.job = job;
o.sayName = function () {
return name;
};
return o;
}
var person1 = createPerson("Nicholas", 29, "Software Engineer");
var person2 = createPerson("Greg", 27, "Doctor");
這樣可以快速地創(chuàng)建相似對象蝶怔,但是用這種方法創(chuàng)建的所有對象都是Object
類,也就是你無法分辨它們的類型兄墅。
function createDog(name, age, owner){
var o = new Object();
o.name = name;
o.age = age;
o.owner = owner;
o.sayName = function () {
return name;
};
return o;
}
var dog = createDog("hali", 2, "Nicholas");
console.log(typeof dog == typeof person1); //true
我覺得你一定不希望狗和你屬于同一個(gè)類型踢星。
構(gòu)造函數(shù)模式
構(gòu)造函數(shù)是很多擁有class
的編程語言的做法。
我們希望隙咸,使用JavaScript也能實(shí)現(xiàn)構(gòu)造函數(shù)沐悦。
function Person(name, age, job){
this.name = name;
this.age = age;
this.job = job;
this.sayName = function () {
console.log(this.name);
};
}
let person1 = new Person("Nicholas", 29, "Software Engineer");
let person2 = new Person("Greg", 27, "Doctor");
console.log(typeof person1); //Person
現(xiàn)在,你創(chuàng)建的類終于有了和函數(shù)同名的類型了扎瓶。
這個(gè)函數(shù)沒有返回值所踊,并且和很多編程語言一樣,使用了new
來創(chuàng)建對象概荷。
任何函數(shù)秕岛,只要使用了new
來創(chuàng)建,那么就是構(gòu)造函數(shù)误证,構(gòu)造函數(shù)本質(zhì)上也是函數(shù)继薛。
但依然有一個(gè)問題,同一個(gè)類之間無法實(shí)現(xiàn)代碼的共用愈捅。
比如sayName
需要被創(chuàng)建兩次遏考,第一次在創(chuàng)建person1
時(shí),第二次在創(chuàng)建person2
時(shí)蓝谨。浪費(fèi)了很多空間灌具。
如何實(shí)現(xiàn)公用呢青团?
原型模式
原型(prototype)是JavaScript很重要的一個(gè)概念。
原型模式使得對象實(shí)例可以共享屬性和方法咖楣。
function Person(){
}
Person.prototype.name = "Nicholas";
Person.prototype.age = 29;
Person.prototype.job = "Software Engineer";
Person.prototype.sayName = function(){
console.log(this.name);
};
var person1 = new Person();
var person2 = new Person();
console.log(person1.sayName() == person2.sayName()); //true
對象的原型是一個(gè)什么概念呢督笆?
Person
有一個(gè)Prototype
,指向Person
的原型诱贿,原型是另外一個(gè)對象娃肿。
當(dāng)JavaScript需要訪問屬性時(shí),首先在Person
找珠十,如果沒找到就回去Prototype
指向的地方找料扰。
原型中還會(huì)存儲(chǔ)構(gòu)造器constructor,指向Person焙蹭,這幅圖里沒有明示出來晒杈,你也需要知道。
console.log(Person.prototype.constructor) //Person
但是這種方式無法讓Person
擁有獨(dú)立的變量壳嚎,也就是說桐智,person1
和person2
都在使用同一個(gè)Prototype指向的對象歉甚,那么當(dāng)person1
試圖改變name的時(shí)候奥秆,person2
的name也會(huì)變刘绣。
一種解決方法是屏蔽。
person1.name = "Greg";
console.log(person1.name); //Greg
只要為person1
重新定義name
屬性郑趁,就能屏蔽原型的屬性,因?yàn)橹罢f過姿搜,搜索變量是從構(gòu)造函數(shù)開始的寡润,如果在構(gòu)造函數(shù)中找到了變量也就不會(huì)去原型中搜索。
另外舅柜,我們還能用對象字面量來簡寫原型的定義梭纹。
比如上面的可以這樣寫:
Person.prototype = {
constructor: Person,
name: "Nicholas",
age: 29,
job: "Software Engineer",
sayName: function(){
console.log(this.name);
}
};
構(gòu)造函數(shù)和原型混成模式
既然如此,干脆把一些屬性在構(gòu)造函數(shù)中定義致份,一些在原型中定義好了变抽。
這也是我們一般推薦的。
function Person(name, age, job){
this.name = name;
this.age = age;
this.job = job;
}
Person.prototype = {
constructor: Person,
sayName: function(){
console.log(this.name);
}
};
動(dòng)態(tài)原型模式
混成模式雖然很好氮块,但是需要把Person和Person.prototype定義分開寫绍载,可能會(huì)遭到一些oo使用者的反對,我們提出一個(gè)更好的模式滔蝉。
function Person(name, age, job){
this.name = name;
this.age = age;
this.job = job;
if( typeof Person.prototype.sayName != "function"){
Person.prototype.sayName = function(){
console.log(this.name);
};
}
}
先用一個(gè)if判斷是否已經(jīng)存在了击儡,是為了防止重載導(dǎo)致的屏蔽行為。
到這一步為止蝠引,終于我們把所有都在Person構(gòu)造函數(shù)內(nèi)完成了阳谍。
寄生構(gòu)造函數(shù)模式
function Person(name, age, job){
var o = new Object();
o.name = name;
o.age = age;
o.job = job;
o.sayName = function () {
console.log(name);
};
return o;
}
var person1 = new Person("Nicholas", 29, "Software Engineer");
person1.sayName(); //Nicholas
通過new
可以看出我們使用了構(gòu)造函數(shù)蛀柴,但有點(diǎn)奇怪的是,我們依然返回了一個(gè)對象矫夯。
其實(shí)這個(gè)對象把構(gòu)造函數(shù)創(chuàng)建的對象覆蓋掉了名扛。
這個(gè)方法創(chuàng)建的對象類型也是不能分辨的。
穩(wěn)妥構(gòu)造函數(shù)模式
雖然我們實(shí)現(xiàn)了共用的屬性和方法茧痒,還實(shí)現(xiàn)了對象自己的屬性和方法肮韧,但擺在我們面前最大的問題是訪問權(quán)限,根據(jù)面向?qū)ο蟮脑O(shè)計(jì)原則旺订,實(shí)例對象的屬性不應(yīng)當(dāng)能直接訪問弄企。
根據(jù)這個(gè),設(shè)計(jì)了一種叫穩(wěn)妥構(gòu)造函數(shù)模式的方法去創(chuàng)建對象区拳。
function Person(name, age, job){
var o = new Object();
o.sayName = function () {
console.log(name);
};
return o;
}
var person1 = Person("Nicholas", 29, "Software Engineer");
person1.sayName(); //Nicholas
無法直接訪問name拘领,但可以通過函數(shù)訪問。
但這個(gè)方法無法查看對象的類型樱调。