對象在js中的定義即為:無序?qū)傩缘募喜蹋鋵傩钥梢园局怠ο蠡蛘吆瘮?shù)览芳。
每個對象都是基于一個引用類型創(chuàng)建的斜姥,這個引用類型可以是原生類型,也可以是開發(fā)人員定義的類型沧竟。
- 創(chuàng)建對象最簡單的方式這里也已經(jīng)介紹铸敏。
var person=new Object();
new操作符
1、Object()是一個構(gòu)造函數(shù)悟泵,平常我們使用函數(shù)時杈笔,一個是直接調(diào)用Object(),另一種是通過對象的方法糕非。其函數(shù)內(nèi)部的this值指的是其所處的環(huán)境對象蒙具。
2、在函數(shù)前面加上new朽肥,可以改變函數(shù)的this值禁筏,還可以共享函數(shù)的原型。
new操作符具體干了什么衡招?參照這
var obj = new Base();
這是一個基本的使用new操作符的代碼篱昔。然而一個new操作符卻悄悄的干了這些事。
var obj = {};//第一行創(chuàng)建了一個空對象obj
obj.proto = Base.prototype;
//proto是每個新建對象的內(nèi)部屬性始腾,指向該實例對象的構(gòu)造函數(shù)的原型對象州刽。
//第二行、將這個空對象的proto成員指向了Base函數(shù)對象prototype成員對象
Base.call(obj);//我們將Base函數(shù)對象的this指針替換成obj窘茁,然后再調(diào)用Base函數(shù)原始創(chuàng)建對象的方式存在許多不足的地方
使用new操作符怀伦,Object構(gòu)造函數(shù),或是對象字面量山林。
問題1、導(dǎo)致一個接口創(chuàng)建很多個對象邢羔,以至于使得產(chǎn)生大量的重復(fù)代碼驼抹。
問題2、函數(shù)作為特殊的對象拜鹤,每一次創(chuàng)建對象都會實例化一個同功能的function對象框冀,浪費。
var person1={
name:"xiaoming",
age:16,
sayname:function(){
alert(this.name);
}
}
var person2={
name:"xiaohong",
age:18,
sayname:function(){
alert(this.name);
}
}
person1.sayname();
person2.sayname();如何解決以上存在的問題
解決問題的道路不是馬到成功敏簿,也是在提供解決方案后明也,又進(jìn)行完善改進(jìn)的過程宣虾。方案1:解決了重復(fù)代碼的問題——工廠模式
function createPerson(name,age,job)
{
var obj=new Object();
obj.name=name;
obj.age=age;
obj.job=job;
obj.saynaName=function(){
alert(this.name);
};
return obj;
}
var person1=createPerson("dudu",22,"Painter");
person1.saynaName();
優(yōu)點:通過傳遞參數(shù),來使用同一個function實例createPerson温数,達(dá)到節(jié)省代碼的效果绣硝。
缺點:1、無法知道person是一個怎樣的對象類型撑刺。(即對象識別)2鹉胖、同功能的sayName函數(shù)對象需要被重復(fù)創(chuàng)立。方案2:解決了對象識別問題以及函數(shù)對象重復(fù)創(chuàng)立問題——構(gòu)造函數(shù)模式
function Person(name,age,job){
this.name=name;
this.age=age;
this.job=job;
this.sayName=sayname;
}
function sayname(){
alert(this.name);
}
var person1=new Person("dudu",22,"Painter");
person1.sayName();
解釋:采用構(gòu)造函數(shù)模式够傍,函數(shù)名即為對象名甫菠,且首字母大寫,配合new操作符使用冕屯。對象的方法的定義轉(zhuǎn)移到了構(gòu)造函數(shù)外部寂诱,每次實例化對象時,該對象的方法不用在進(jìn)行實例安聘,而是共享全局作用域中的sayname函數(shù)痰洒。
缺點:1、全局作用域的函數(shù)僅被某個對象調(diào)用搞挣,不合乎全局對象的設(shè)置效果带迟。2、違背了對象的封裝特性囱桨。方案3:解決了對象方法定義的封裝問題——原型模式
function Person(){}
Person.prototype.name="Nicholas";
Person.prototype.age=29;
Person.prototype.job="Softwar Enginear"
Person.prototype.sayName=function(){
alert(this.name);
};
var person1=new Person();
person1.sayName();
根據(jù)上圖仓犬,
解釋:創(chuàng)建一個新的函數(shù)對象后,該函數(shù)對象就會擁有一個prototype屬性舍肠,這個屬性是一個指針搀继,指向一個對象,該對象的用途是包含可以由特定類型的所有實例共享的屬性和方法翠语。包括一個構(gòu)造函數(shù)(constructor)屬性叽躯,這個屬性包含一個指向prototype屬性所在函數(shù)的指針。其他屬性都是用戶自己建立的肌括。
另外点骑,由該構(gòu)造函數(shù)實例的對象,也包含一個指針[[prototype]]谍夭,指向構(gòu)造函數(shù)的原型對象黑滴。在FF、Safari紧索、Chrome在每個對象上都支持一個屬性
_proto_
袁辈。需要知道這個連接存在的是實例與構(gòu)造函數(shù)的原型對象之間,而你不是存在于實例與構(gòu)造函數(shù)之間珠漂。缺點:1晚缩、原型對象的屬性與方法的添加不夠封裝尾膊。2、由于原型對象的共享荞彼,導(dǎo)致所有實例共享相同的屬性值冈敛。
-
方案3.1:解決了原來原型模式的問題1——封裝的原型模式
function Person(){}
var friend=new Person();
Person.prototype={
constructor:Person,
name:"Nichoas",
age:29,
job:"software ENgineer",
sayName:function(){
alert(this.name);
}
};
friend.sayName();//error
以上的代碼實際上重寫了原型對象卿泽,導(dǎo)致切斷了構(gòu)造函數(shù)與最初原型之間的關(guān)系莺债,需要constructor:Person,
進(jìn)行重新指定。
缺點:重寫原型對象切斷了現(xiàn)有原型對象與任何之前已經(jīng)存在的對象實例之間的聯(lián)系签夭,他們?nèi)砸玫氖亲畛醯脑汀?p> -
方案4:解決了原型模式的問題——構(gòu)造函數(shù)與原型混成的模式
function Person(name,age,job){
this.name=name;
this.age=age;
this.job=job;
this.friends=["bobo","xixi"];
}
Person.prototype={
constructor:Person,
asyName:function(){
alert(this.name);
}
};
var person1=new Person("du",22,"painter");
var person2=new Person("susu",22,"singer");person1.friends.push("wawa"); alert(person1.friends);//bobo,xixi,wawa alert(person2.friends);//bobo,xixi
解釋:對比兩次alert結(jié)果齐邦,發(fā)現(xiàn)構(gòu)造函數(shù)內(nèi)部的內(nèi)容不被對象實例共享。這也達(dá)到了對象之間個性化的設(shè)置第租。
缺點:還是不夠封裝按肽础!
-
方案4.1:彌補不夠封裝
function Person(name,age,job){
this.name=name;
this.age=age;
this.job=job;
this.friends=["bobo","xixi"];
//
if(typeof this.sayName!= "function")
{
Person.prototype.sayNmae=function(){
alert(this.name);
};
}
}
只有在sayName方法不存在的情況下慎宾,才會將他添加在原型中丐吓。這段代碼只會在初次調(diào)用構(gòu)造函數(shù)的時候才會執(zhí)行。