Javascript中的‘類’
Javascript中并沒有嚴格意義上的類。ES5中通過使用首字母大寫的方法來模擬類。這個首字母大寫的方法也被稱為構(gòu)造函數(shù),其是一個函數(shù)。
構(gòu)造函數(shù)前面用關(guān)鍵字new,則創(chuàng)建一個新的空對象,并用這個新對象調(diào)用這個函數(shù)。
function Person (name) {
this.name = name;
}
var per1 = new Person('bob');
其實方法也是對象,方法都是由Function構(gòu)造函數(shù)創(chuàng)建的,F(xiàn)unction由它本身創(chuàng)建。
三個概念
- 構(gòu)造函數(shù):也是一個函數(shù),一般和關(guān)鍵字new配合使用,你單獨調(diào)用也會不報錯。其本身也是一個對象。
- 原型對象 : 構(gòu)造函數(shù)的prototype屬性指向的就是原型對象。
- 實例對象 : new 關(guān)鍵字新創(chuàng)建的對象,new關(guān)鍵字和構(gòu)造函數(shù)一起用,新創(chuàng)建的對象調(diào)用構(gòu)造函數(shù),構(gòu)造函數(shù)里通過this關(guān)鍵字引用實例對象,
通過this關(guān)鍵字初始化實例對象。
如果構(gòu)造函數(shù)里顯示的返回一個對象,我們聲明的實例對象就是返回的這個
function OBJ(){
return {name : 1}
};
var other = new OBJ();
other.name; // 1
//other的構(gòu)造函數(shù)為Object , 如果返回的是一個別的構(gòu)造函數(shù)創(chuàng)建的實例對象,other就是指向這個實例對象
一個例子
function Person(name){
this.name = name
}
var per = new Person("someone");
在這個例子中,Person是構(gòu)造函數(shù),是一個函數(shù)對象,是對象就會有構(gòu)造函數(shù)(Object.prototype對象除外,它是唯一沒有原型和構(gòu)造函數(shù)的對象)
Object.prototype instanceof Object // false 結(jié)果很有趣
函數(shù)對象的構(gòu)造函數(shù)都是Function , Object也是函數(shù)對象,所以 Object的構(gòu)造函數(shù)也是Function.
- 對象的constructor屬性指向其對應的構(gòu)造函數(shù)
Person.constructor // ? Function() { [native code] }
Object.constructor // ? Function() { [native code] }
Function也是構(gòu)造函數(shù)對象,所以 Function instanceof Function 為true 同時 Function instanceof Object 也為true 而Object instanceof Function也為true 結(jié)果很有趣。 - 實例對象的proto屬性是瀏覽器實現(xiàn)的一個指向該對象原型對象的屬性。
Function.____proto____ === Function.prototype
Function的____proto____指向創(chuàng)建Function的函數(shù)的prototype,F(xiàn)unction由它自己創(chuàng)建。
Objecet.prototype.___proto____ === null
上面的例子,解釋器會為Person創(chuàng)建一個初始的prototype,這個原型對象中有個constructor屬性指向構(gòu)造函數(shù),在我們給Person指定自定義的對象時,自定義的對象是沒有自己的constructor屬性的(會繼承),除非我們手動添加。
實現(xiàn)繼承
給實例對象的原型對象即構(gòu)造函數(shù)的prototype添加方法,或?qū)傩?一般不建議添加屬性),所有的實例對象都會繼承這些方法和屬性,而且這些方法和屬性在實例對象中都是可訪問的。給構(gòu)造函數(shù)添加方法和屬性能模擬實現(xiàn)類方法和類字段
當通過構(gòu)造函數(shù)創(chuàng)建對象時,對象所有的屬性值將會被復制給新的對象。但由于prototype是一個引用類型的變量,存儲的是對象的地址,因此prototype中的所有屬性值將會被該函數(shù)創(chuàng)建的所有對象共享,若一個對象修改了prototype中的某個屬性,那所有對象prototype屬性都會發(fā)生修改。
因此,prototype屬性適合存儲不變的屬性,如函數(shù);構(gòu)造函數(shù)的普通屬性適合存儲基本類型的屬性;而對象無論存儲在哪里效果都一樣。此外,將函數(shù)存儲在prototype中可以達到節(jié)約內(nèi)存的功效,不論創(chuàng)建多少對象,該原型對象的函數(shù)在內(nèi)存中只有一份。
在實例對象中通過obj.繼承來的方法/屬性 = somevalue 是無法修改原型對象的方法或?qū)傩缘模驗樵谡{(diào)用對象中的屬性或者方法時,首先會在當前對象中查找是否有該屬性,若無則依次在該對象的原型鏈中找,如果到最后還找不到則返回undefined。obj.繼承來的方法/屬性 = somevalue 會給當前對象創(chuàng)建一個和繼承來的屬性同名的屬性,并且在當前對象中調(diào)用會覆蓋掉繼承來的屬性。
function Student(id){
this.id = id
}
根據(jù)現(xiàn)實邏輯,Student是Person的一類
所以Student的實例應該有Person的特征
繼承是通過prototype表現(xiàn)的,所有類的實例對象都會繼承類(構(gòu)造函數(shù))protptype的屬性和方法。prototype一定是個對象
Student.prototype = inhert(Person.prototype) || new Person();
Student.prototype.constructor = Student //這一句是指定Student實例的構(gòu)造函數(shù)的,如果不指定,不會默認生成,而是使用繼承來的(這里就是Person)
function inherit(obj){
if(obj == null || (typeof obj !== "object" && typeof obj !== "function")){
throw TypeError();
}
if(Object.create) return Object.create(obj); //新建一個對象,對象構(gòu)造函數(shù)的prototype為obj
var f = function(){};
f.prototype = obj;
return new f();
}
關(guān)于實例對象的constructor
① var obj = {}; || var obj = new Object();
obj的構(gòu)造函數(shù)是Object , obj.constructor: Object
obj的constructor是繼承Object.prototype的,obj.hasOwnProperty(constructor) //false② function Person(){
}
var obj2 =new Person();
obj2.constructor : Person
obj的constructor是繼承Person.prototype的。
Person.prototype : {constructor : Person} //解釋器創(chuàng)建
解釋器會為Person創(chuàng)建一個初始的prototype,這個原型對象中有個constructor屬性指向構(gòu)造函數(shù),在我們給Person的prototype指定自定義的對象時,自定義的對象會有一個繼承自它的原型對象的constructor屬性,如果要Person的實例對象的constructor正確的指向它的構(gòu)造函數(shù),需要我們手動添加Person.prototype.constructor = Person。
例如:如果Person.prototype = {name : 'otherobj'};
var otherobj = new Person(); otherobj.constructor : Object③ var obj3 = Object.create(obj2) // 創(chuàng)建一個對象,obj3的原型對象為obj2
obj3.constructor //Person
obj3.constructor.prototype // obj2
obj3.constructor.prototype.constructor.prototype //{constructor : Person}
obj3.constructor.prototype.constructor.prototype.constructor.prototype //Object.prototype④function Student(){
}
Student.prototype = new Person();
var obj4 =new Student();
obj4.constructor //Person
Student.prototype.constructor = Studnet;//手動設(shè)置
//下面兩種錯誤的繼承
Student.prototype = Person.prototype //Student和Person一樣了
Student.prototype = Person //Person這個函數(shù)對象被當作prototype了
var otherCon = Person;
obj4 instanceof otherCon //true instanceof是檢測對象的繼承關(guān)系而不是構(gòu)造函數(shù)
函數(shù)有個name屬性,可以用來看構(gòu)造函數(shù)的name