JS中對象的定義:無序?qū)傩缘募现锌瘢鋵傩灾悼梢园局担瑢ο蠡蛘吆瘮?shù)扑毡。我們可以把js的對象想象成散列表胃榕,無非就是一組鍵值對,其中的值可以是數(shù)據(jù)或函數(shù)瞄摊。
屬性有兩種類型:數(shù)據(jù)屬性和訪問器屬性勋又。
數(shù)據(jù)屬性:包含一個數(shù)據(jù)值的位置,在這個位置可以讀取和寫入值换帜。數(shù)據(jù)屬性有四個特性:
configurable 屬性能否delete掉楔壤,屬性特性能否修改,能否把數(shù)據(jù)屬性變?yōu)樵L問器屬性
enumerable 能否通過for in循環(huán)返回屬性
writable 能否修改屬性的值
-
value 屬性的數(shù)據(jù)值
var person = {}; //person的name屬性值為Jack 不可修改值 Object.defineProperty(person,"name",{ writable:false, value:"Jack" }); alert(person.name); person.name="Grey"; alert(person.name); Object.defineProperty(person,"age",{ configurable:false, value:22 }) alert(person.age); delete person.age;//configurable為false惯驼,不能修改 alert(person.age);
訪問器屬性:不包含數(shù)據(jù)值蹲嚣,它們包含一對getter和setter函數(shù)递瑰。
configurable 屬性能否delete掉,屬性特性能否修改隙畜,能否把數(shù)據(jù)屬性變?yōu)樵L問器屬性
enumerable 能否通過for in循環(huán)返回屬性
get 讀取屬性時調(diào)用的函數(shù)
-
set 寫入屬性時調(diào)用的函數(shù)
訪問器屬性不能直接定義抖部,需通過defineProperty來定義。在調(diào)用該方法時议惰,如果不指定慎颗,configurable,enumerable换淆,writable特性的默認值都是falsevar book={ _year:2004, edition:1 }; Object.defineProperty(book,"year",{ get:function(){ return this._year; }, set:function(newValue){ if(newValue>2004){ this._year=newValue; this.edition+=newValue-2004; } } }); book.year=2006; alert(book.edition); book._year=2007;//直接設(shè)置下劃線屬性的值也會起作用
_year前面的下劃線是一種常用的記號哗总,用于表示只能通過對象方法定義的屬性。_year是數(shù)據(jù)屬性倍试,year是訪問器屬性
定義多個屬性 通過描述符一次定義多個屬性讯屈,第一個參數(shù)是要修改的對象,第二個對象的屬性與第一個對象中要添加或修改的屬性一一對應(yīng)
var book={};
Object.defineProperties(book,{
_year:{
value:2004
},
edition:{
value:1
},
year:{
get:function(){
return this._year;
},
set:function(){
if(arguments[0]>2004){
this._year=arguments[0];
this.edition+=arguments[0]-2004;
}
}
}
})
var des=Object.getOwnPropertyDescriptor(book,"_year");
console.log(des);//讀取屬性描述符
6.2 創(chuàng)建對象
工廠模式
function createPerson(name,age,job){
var o=new Object();
o.name=name;
o.age=age;
o.job=job;
o.sayname=function(){
alert(this.name);
};
return o;
}
var person1=createPerson("jack",22,"worker");
person1.sayname();
console.log(person1);
alert(person1 instanceof Object);//工廠模式的壞處县习,無法確定對象類型涮母,都是Object
構(gòu)造函數(shù)模式
function Person(name,age,job){
this.name=name;
this.age=age;
this.job=job;
this.sayname=function(){
alert(this.name);
}
}
var person1 = new Person("jack",33,"workers")
person1.sayname();
console.log(person1);
alert(person1 instanceof Person);//構(gòu)造函數(shù)可以確定對象類型 但是實例上每個方法都要在實例上創(chuàng)建一遍
原型模式
我們創(chuàng)建的每個函數(shù)都有一個prototype屬性,這個屬性是一個指針躁愿,指向一個對象叛本,這個對象包含特定類型的所有實例共享的屬性和方法。
function Person(){
}
Person.prototype.name="jack";
Person.prototype.age=29;
Person.prototype.job="coder";
Person.prototype.sayname=function(){
alert(this.name);
}
var person1 = new Person();
person1.sayname();//jack
var person2 = new Person();
person2.name="lucy";
person2.sayname();//lucy
person1.sayname();//jack
無論什么時候彤钟,只要創(chuàng)建一個新函數(shù)来候,就會為該函數(shù)創(chuàng)建一個prototype屬性,這個屬性指向函數(shù)的原型對象逸雹。而原型對象里也有一個constructor指針指向該函數(shù)营搅。當用函數(shù)創(chuàng)建一個新實例以后,這個實例的內(nèi)部的proto指針將指向原型梆砸。
console.log(Object.getPrototypeOf(person1));//實例對應(yīng)的原型
每當讀取某個對象的某個屬性時转质,搜索首先從對象實例本身開始,如果找到了則返回對應(yīng)的值帖世,找不到到原型對象里找休蟹。
我們在實例對象里添加一個屬性,即使和原型里的某個屬性重名日矫,也不會覆蓋原型里的屬性赂弓。
person1.city="beijing";
alert(person1.hasOwnProperty('city'));//屬性在實例中返回true true
alert(person1.hasOwnProperty('name'));//false
in 只要屬性能找到就返回true
alert("job" in person1);
for(var p in person1){//遍歷所有屬性,包括原型中的
alert(p);
}
alert(Object.keys(person1));//返回所有屬性組成的字符串哪轿,不包含原型
字面量重寫原型對象
function Person(){}
//字面量重寫原型對象
Person.prototype={
constructor:Person//重寫設(shè)定指針
name:"jack",
age:29,
job:"coder",
sayName:function(){
alert(this.name);
}
}
原型中查找值的過程是一次搜索拣展,因此我們對原型對象所做的任何修改都能立即從實例上反應(yīng)出來,即使先創(chuàng)建實例后修改原型也照樣如此缔逛。
原型模式的缺點:如果屬性是引用類型备埃,一個實例的修改會導(dǎo)致所有實例的值都被修改姓惑。
創(chuàng)建自定義類型的最常見方式是組合使用構(gòu)造函數(shù)模式和原型模式。(實例屬性用構(gòu)造按脚,方法和共享屬性用原型)
function Person(name,age,job){
this.name=name;
this.age=age;
this.job=job;
this.friends=["lucy","bob"];
}
Person.prototype={
constructor:Person,
sayName:function(){
alert(this.name);
}
}
var person1=new Person("jack",22,"worker");
繼承
主要依靠原型鏈來完成
讓某個實例的原型對象等于另一類型的實例于毙。
function Person(name,age,job){
this.name=name;
this.age=age;
this.job=job;
this.friends=["lucy","bob"];
}
Person.prototype={
constructor:Person,
sayName:function(){
alert(this.name);
}
}
function Student(school){
this.school=school;
}
Student.prototype=new Person();
Student.prototype.saySchhol=function(){
alert(this.school);
};
var s1=new Student("ustc");
s1.name="jack";
s1.age=12;
s1.sayName();
console.log(s1);
原型鏈的問題:一 父類的引用屬性,子類一但修改辅搬,所有子類實例的值都會變化唯沮,二 無法向父類傳遞構(gòu)造參數(shù)。
借用構(gòu)造函數(shù)
在子類的構(gòu)造函數(shù)內(nèi)部調(diào)用父類構(gòu)造函數(shù)
function Student(school){
Person.call(this);//每個子類都會有父類屬性的拷貝副本
this.school=school;
}
組合繼承
使用原型鏈實現(xiàn)對原型屬性和方法的繼續(xù)堪遂,而借用構(gòu)造函數(shù)來實現(xiàn)對實例屬性的繼承介蛉。
function Person(name){
this.name=name;
}
Person.prototype.sayname=function(){
alert(this.name);
}
function Student(name,school,score){
Person.call(this,name);
this.school=school;
this.score=score;
}
Student.prototype=new Person("Jack");
Student.saySChool=function(){
alert(this.school);
}
var s1=new Student("lucy","ustc",99);
var s2=new Student("mike","autc",80);
console.log(s1);
console.log(s2);
s1.sayname();
s2.sayname();