正文
- 每一個函數(shù)都有一個prototype屬性洪橘,它是一個指針指向?qū)ο蠹找В@個對象就是通過調(diào)用構(gòu)造函數(shù)而創(chuàng)建的的對象實例的原型對象。
Person.prototype == man
- 所有的原型對象(Person.prototype)里面有一個屬性(constructor)指向prototype屬性所在的函數(shù)指針芝此,即構(gòu)造函數(shù)(Person)搀暑。
Person.prototype.constructor == Person
- 構(gòu)造函數(shù)創(chuàng)建的實例對象(man),默認(rèn)會包含一個內(nèi)部屬性(_proto_)哟忍,指向構(gòu)造函數(shù)的原型對象(Person.prototype)
man._proto_ == Person.prototype
原型的語法:
function Person(){}
Person.prototype.name = 'NIKE';
Person.prototype.size = 29;
var man = new Person();
也可以用對象字面量來重寫:
function Person(){}
Person.prototype = {
constructor : Person,
name : 'NIKE',
size : 29
}
注意:重寫原型對象的時候梅屉,該原型對象的constructor指向的不再是它對象的構(gòu)造函數(shù)筐带,而是object的構(gòu)造函數(shù),所以必須在重寫中顯示定義constructor為Person
通過原型可以給原生的引用類型增加方法:
String.prototype.test = function(){}
var str = 'sssssss';
str.test();
原型模式的缺點:原型中所有的屬性都是共享的寂呛,所有實例對象都會共享
解決辦法:構(gòu)造函數(shù)+原型對象
function Person(name,size){
this.name = name;
this.size = size;
}
Person.prototype.say = function(){
return this.name;
}
另外兩種構(gòu)造模式:
- 寄生構(gòu)造模式:(類似于工廠模式)
function Person(name,size){
var o = new object();
o.name = name;
o.size = size;
o.say = function(){
return this.name;
}
return o;
}
- 穩(wěn)妥構(gòu)造模式:(沒有公共屬性)
function Person(name,size){
var o = new object();
o.say = function(){
return name;
}
}
這兩種模式的問題是:創(chuàng)建的對象跟構(gòu)造函數(shù)沒有一毛錢關(guān)系怎诫。
繼承
1.原型鏈繼承
function Super(){}
Super.prototype.say = function(){
return 1;
}
function Sub(){}
Sub.prototype = new Super();
//覆蓋
Sub.prototype.say = function(){
return 2;
}
原理:創(chuàng)建Super的實例,然后把該實例賦給Sub.prototype贷痪,那么Sub.prototype則擁有Sup的所有屬性和方法幻妓,當(dāng)通過Sub構(gòu)造函數(shù)實例一個對象后,該對象不僅包含Sub的屬性和對象劫拢,同時還包含Sup的肉津,即實現(xiàn)了繼承
注意:通過原型鏈實現(xiàn)繼承的時候,不能使用對象字面量舱沧,會重寫原型鏈妹沙。
缺點:原型屬性會被所有實例共享,并且不能想超類行的構(gòu)造函數(shù)(Super)傳遞參數(shù)熟吏。
2. 借用構(gòu)造函數(shù)
function Super(name){
this.name = name;
}
function Sub(name){
Super.call(this,name)
}
優(yōu)點:可以傳遞參數(shù)
缺點:方法都在構(gòu)造函數(shù)里面定義距糖,沒有函數(shù)復(fù)用的意義
3.組合繼承
function Super(name){
this.name = name;
}
Super.prototype.say = function(){
return this.name;
}
function Sub(name,age){
//繼承屬性
Super.call(this,name);
this.age = age;
}
//繼承方法
Sub.prototype = new Super();
Sub.prototype.constructor = Sub;
缺點:需要調(diào)用兩次構(gòu)造函數(shù)(call和實例化)
4.原型式繼承
var person = {
name : 'NIKE',
age : 29
}
var anotherPerson = Object.create(person);
優(yōu)點:不需要創(chuàng)建構(gòu)造函數(shù)
缺點:跟原型模式一樣,會共享屬性題外:Object.create的實現(xiàn):
function object(o){
function F(){}
F.prototype = o;
return new F();
}
5.寄生組合式繼承(最理想的繼承方式)
function inheritPrototype(sub,sup){
var prototype = Object.create(sup.prototype);
prototype.constructor = sub;
sub.prototype = prototype;
}
function Super(name){
this.name = name;
}
Super.prototype.say = function(){
return this.name;
}
function Sub(name,age){
Super.call(this,name);
this.age = age;
}
inheritPrototype(Sub,Super);
優(yōu)點:不必為了子類型的原型而調(diào)用超類型的構(gòu)造函數(shù)(我們所需要的無非是超類型原型的一個副本)
面向?qū)ο笾袪砍兜降姆椒ǎ?/h4>
- Object.defineProperty() 定義對象數(shù)據(jù)的屬性
var person = {}
Object.defineProperty(person,"name",{
writable : false, //只讀
configurable : false, //不可配置牵寺,一旦設(shè)置為false,不可再進(jìn)行配置
enumerable : false, //不可枚舉悍引,不能通過for..in循環(huán)
value : 'NIKE'
})
var person = {}
Object.defineProperty(person,"name",{
writable : false, //只讀
configurable : false, //不可配置牵寺,一旦設(shè)置為false,不可再進(jìn)行配置
enumerable : false, //不可枚舉悍引,不能通過for..in循環(huán)
value : 'NIKE'
})
定義多個屬性 Object.defineProperties
Object.defineProperties(person,{
name : 'NIke',
_year:{ //_命名規(guī)則,表示該屬性屬于私有屬性
writable : true,
value : 2004
},
year:{
get:function(){
return this._year;
},
set:function(value){
this._year = value;
}
}
});
- A instanceof B 判斷A是否是B的實例
man instanceof Person == true
man instanceof Object == true
- Object.getPrototypeOf() 返回實例對象的內(nèi)部屬性(_proto_)
Object.getPrototypeOf(man) == Person.prototype
- A.prototype.isPrototypeOf(B) 判斷A.prototype是否是B的原型對象
Person.prototype.isPrototypeOf(man)
- A.hasOwnProperty(B) 檢測一個屬性B是存在于實例A中(true)帽氓,還是存在于原型中(false)
man.hasOwnProperty('name')
- hasPrototypeProperty(A,B) 與方法5正好相反趣斤,只不過使用方法略有不同
- Object.keys(A) 返回對象A中所有可枚舉屬性,以字符串?dāng)?shù)組返回