對象在js中的定義即為:無序屬性的集合,其屬性可以包含基本值、對象或者函數。
每個對象都是基于一個引用類型創建的,這個引用類型可以是原生類型,也可以是開發人員定義的類型。
- 創建對象最簡單的方式這里也已經介紹。
var person=new Object();
new操作符
1、Object()是一個構造函數,平常我們使用函數時,一個是直接調用Object(),另一種是通過對象的方法。其函數內部的this值指的是其所處的環境對象。
2、在函數前面加上new,可以改變函數的this值,還可以共享函數的原型。
new操作符具體干了什么?參照這
var obj = new Base();
這是一個基本的使用new操作符的代碼。然而一個new操作符卻悄悄的干了這些事。
var obj = {};//第一行創建了一個空對象obj
obj.proto = Base.prototype;
//proto是每個新建對象的內部屬性,指向該實例對象的構造函數的原型對象。
//第二行、將這個空對象的proto成員指向了Base函數對象prototype成員對象
Base.call(obj);//我們將Base函數對象的this指針替換成obj,然后再調用Base函數原始創建對象的方式存在許多不足的地方
使用new操作符,Object構造函數,或是對象字面量。
問題1、導致一個接口創建很多個對象,以至于使得產生大量的重復代碼。
問題2、函數作為特殊的對象,每一次創建對象都會實例化一個同功能的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();如何解決以上存在的問題
解決問題的道路不是馬到成功,也是在提供解決方案后,又進行完善改進的過程。方案1:解決了重復代碼的問題——工廠模式
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();
優點:通過傳遞參數,來使用同一個function實例createPerson,達到節省代碼的效果。
缺點:1、無法知道person是一個怎樣的對象類型。(即對象識別)2、同功能的sayName函數對象需要被重復創立。方案2:解決了對象識別問題以及函數對象重復創立問題——構造函數模式
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();
解釋:采用構造函數模式,函數名即為對象名,且首字母大寫,配合new操作符使用。對象的方法的定義轉移到了構造函數外部,每次實例化對象時,該對象的方法不用在進行實例,而是共享全局作用域中的sayname函數。
缺點:1、全局作用域的函數僅被某個對象調用,不合乎全局對象的設置效果。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();
根據上圖,
解釋:創建一個新的函數對象后,該函數對象就會擁有一個prototype屬性,這個屬性是一個指針,指向一個對象,該對象的用途是包含可以由特定類型的所有實例共享的屬性和方法。包括一個構造函數(constructor)屬性,這個屬性包含一個指向prototype屬性所在函數的指針。其他屬性都是用戶自己建立的。
另外,由該構造函數實例的對象,也包含一個指針[[prototype]],指向構造函數的原型對象。在FF、Safari、Chrome在每個對象上都支持一個屬性
_proto_
。需要知道這個連接存在的是實例與構造函數的原型對象之間,而你不是存在于實例與構造函數之間。缺點:1、原型對象的屬性與方法的添加不夠封裝。2、由于原型對象的共享,導致所有實例共享相同的屬性值。
-
方案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
上面為重寫前,下面為重寫后
以上的代碼實際上重寫了原型對象,導致切斷了構造函數與最初原型之間的關系,需要constructor:Person,
進行重新指定。
缺點:重寫原型對象切斷了現有原型對象與任何之前已經存在的對象實例之間的聯系,他們仍引用的是最初的原型。 -
方案4:解決了原型模式的問題——構造函數與原型混成的模式
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結果,發現構造函數內部的內容不被對象實例共享。這也達到了對象之間個性化的設置。
缺點:還是不夠封裝啊!
-
方案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方法不存在的情況下,才會將他添加在原型中。這段代碼只會在初次調用構造函數的時候才會執行。