JScript建立物件與繼承方式--紅寶書第六章
一. 建立物件
1. 工廠模式
解決了建立多個類似物件的問題,但是通過工廠模式建立的物件無法識別是一個什麼樣的物件型別
function objFactory(name,age){ var o = new Object(); //適合於原生建構函式建立例項 o.name=name; o.age=age; o.getAction=function(){console.log(this.name)}; return o; } var person1=objFactory("jasson",10); var person2=objFactory("jim",8);
2. 建構函式模式
//自定義一類特性的型別
function Animal(name,age){
this,name=name;
this.age=age;
this.getAction=function(){
console.log(this.name);
}
}
var dog = new Animal("DOG",3);
var cat=new Animal("cat",2);
建構函式可以用於建立特定型別的物件。除了Object、Array原生建構函式,還可以自定義建構函式。自定義建構函式可以用於將它的例項標識為一種特定的型別,如Dog、Cat。
問題:每個方法都會在每個例項上建立一遍。在ECMAscript中函式是物件,每定義一個函式相當於例項化了一個Function物件
即 this.getAction=function(){……}
邏輯上等價於 this.getAction=new Function(){……}
所以不同例項上的同名函式是不相等的如下:
console.log(dog.getAction==cat.getAction);
VM649:1 false
但是,建立兩個可以完成同樣任務的Function例項是沒有必要的。大可將函式移到建構函式體外去,如下:
function Animal(name,age){ this,name=name; this.age=age; //此時this.getAction屬性設為等於全域性函式getAction,而getAction是一個指向函式的指標,因此解決了兩個函式做同一件事的問題。 this.getAction=getAction; } function getAction(){ console.log(this.name); } var dog = new Animal("DOG",3); var cat=new Animal("cat",2);
問題:如果物件需要定義很多方法,那麼會存在很多全域性函式,那麼自定義引用型別的封裝性無法得以體現了。這一點可以用原型模式解決。
3. 原型模式
可以讓所有物件例項共享它所包含的屬性和方法。
function Animal(){
}
Animal.prototype.name="dog";
Animal.prototype.age=2;
**Animal.prototype.getAction=function(){
console.log(this.name);
}**
var dog = new Animal();
var cat=new Animal();
4. 組合構造模式
建構函式模式+原型模式的方法
使例項可以擁有各自的例項屬性,而原型模式可以用於定義共同的屬性和方法。
function Animal(name,age){
this,name=name;
this.age=age;
}
*Animal.prototype.getAction=function(){
console.log(this.name);
}
var dog = new Animal("DOG",3);
var cat=new Animal("cat",2);
5. 動態原型模式
將所有資訊封裝在建構函式中,在必要情況下初始化原型。
function Animal(name,age){
this,name=name;
this.age=age;
**if(typeof this.getAction != "function"){
Animal.prototype.getAction=function(){
console.log(this.name);
}
}**
}
var dog = new Animal("DOG",3);
var cat=new Animal("cat",2);
dog.getAction();
6. 寄生建構函式
function Animal(name,age){ //寄生的宿主建構函式
var o= new Object(); //寄生者
o.name=name;
o.age=age;
o.getAction=function(){console.log(this.name)};
return o;
}
var dog = new Animal("DOG",3);
var cat=new Animal("cat",2);
二. 繼承
例項、建構函式、例項的關係
1. 原型鏈繼承
定義一個父類Animal
function Animal(){
this.name="animal";
}
Animal.prototype.getName=function(){return this.name};
//定義子類
function Dog(){
this.age=2;
}
Dog.prototype = new Animal(); //原型繼承
var instance = new Dog(); //子類的例項
alert(instance.getName()); //輸出為animal,繼承了父類的方法
console.log(instance.constructor==Animal); //VM2342:1 true,
使用原型繼承後,instance.constructor會指向父類Animal,因為Dog.prototype指向了Animal的原型,所以constructor指向了animal。
問題:1.建立子型別的例項時,無法在不影響其他物件例項的情況下向超型別的建構函式傳遞引數。2.無法實現多繼承
特點:1.簡單易於實現
2. 借用建構函式繼承
function Animal(){
this.names=["animal"];
}
function Dog(){
**Animal.call(this);** // 借調了父類的建構函式
}
//每當建立一個例項時,都會利用call方法借調animal的建構函式,在例項物件上會執行animal建構函式中定義的初始化程式碼,因此Dog的每個例項上都會有names屬性的副本。所以instance1和instance1的names打印出來結果會不一樣
var instance1 = new Dog();
instance1.names.push("dog");
console.log(instance1.names); //(2) ["animal", "dog"]
var instance2 = new Dog();
console.log(instance2.names); //VM2406:12 ["animal"]
借用建構函式的另一點----子型別建構函式可以向父類建構函式傳遞引數
function Animal(name){
this.names=name;
}
function Dog(){
**Animal.call(this,"dog");** // 借調了父類的建構函式同時傳遞引數
this.age=2;
}
var instance = new Dog();
console.log(instance.name); //VM2406:12 "dog"
問題:1.函式無法複用。因此需要用組合繼承 。 2.只能繼承父類的例項和方法,無法繼承原型屬性和方法
特點:1.可以實現多繼承。2.建立子類例項時可以向父類傳參
3. 組合繼承
借用建構函式繼承+原型繼承
function Animal(name){
this.name=name;
this.colors=["red","blue","green"];
}
Animal.prototype.getName=function(){
console.log(this.name);
}
function Dog(name,age){
Animal.call(this,name); //**第二次呼叫超類animal的建構函式**
this.age=age;
}
Dog.prototype=new Animal(); //**第一次呼叫超類animal建構函式**
Dog.prototype.constructor=Dog; //使得dog.prototype.constructor仍然指向dog
Dog.prototype.getAge=function(){console.log(this.age);};
var instance1 = new Dog("JIM",2);
instance1.colors.push("yellow");
console.log(instance1.colors); //(4) ["red", "blue", "green", "yellow"]
instance1.getName(); // JIM
instance1.getAge(); //2
var instance2 = new Dog("tom",11);
console.log(instance2.colors); //VM2911: (3) ["red", "blue", "green"]
instance2.getName(); // tom
instance2.getAge(); //11
如果註釋掉Dog.prototype.constructor=Dog; 這句,那麼
console.log(Dog.prototype.constructorDog);.//false
console.log(Dog.prototype.constructorAnimal); //true
問題:1.無論什麼情況下,都會呼叫兩次超類的建構函式,導致有兩組超類中的屬性name和colors,一組在例項上,一組在Dog原型中。------解決方案為寄生組合式繼承
特點:1.可以繼承父類的屬性方法,也可以繼承原型屬性方法。2.可以傳參 。 3.函式可以複用
4. 寄生式繼承
利用createAnimal()函式封裝繼承過程,類似於工廠模式和寄生建構函式的思維。
function createAnimal(original){
var o=new Object(original); //original初始化的引數
o.getName=function(){console.log(original.name);};
return o;
}
var animal={
name: "JIm",
colors: ["red","bluer"]
};
var obj = createAnimal(animal); //利用函式封裝繼承過程,返回的結果是個物件
obj.getName(); //JIM
5. 寄生組合式繼承
寄生式繼承+組合式繼承
通過借用建構函式繼承屬性,利用原型鏈的混成形式繼承方法。
//借用建構函式方法
function Animal(name){
this.name=name;
this.colors=["red","blue","green"];
}
Animal.prototype.getName=function(){
console.log(this.name);
}
function Dog(name,age){
Animal.call(this,name); //**第二次呼叫超類animal的建構函式**
this.age=age;
}
Dog.prototype.getAge=function(){
console.log(this.age);
};
//寄生組合繼承的重點!!!!!
function inheritPrototype(dog, animal){
var o = new Object(animal.prototype);
o.constructor = dog;
dog.prototype = o;
}
inheritPrototype(Dog,Animal);
var instance1 = new Dog("JIM",2);
console.log(instance1.colors); // ["red", "blue", "green"]
instance1.getName(); // JIM
instance1.getAge(); //2
重點部分!!!
**function inheritPrototype(dog, animal){
var o = new Object(animal.prototype);
o.constructor = dog;
dog.prototype = o;
}
inheritPrototype(Dog,Animal);**
問題:1. 實現較為複雜
特點: 1.整體實現較為理想