//以下以實例說明javascript的物件導向繼承運作方式
//定義動物類別
function Animal (weight, height, numLegs) {
this.weight = weight;
this.height = height;
this.moveable = true;
}
//定義狗類別
function Dog (name) {
this.name = name;
this.setName = function (name){
this.name = name;
};
}
//創造一個狗類別實體
var laifu = new Dog('laifu');
//透過類別的prototype新增吠叫方法,這可以直接讓既存的實體擁有吠叫的能力
Dog.prototype.bark = function (){
console.log('woof!');
};
laifu.bark();
//可使用bark方法
//因為實體會透過prototype chain"向上"從祖先的prototype中尋找可用的方法與屬性
//實體的__proto__屬性儲存著所屬類別的prototype內容,
//所屬類別的prototype的__proto__屬性儲存著父類別的prototype內容
//向上延伸直至原始類別Object的prototype,再往上就是null
//實際運作過程是使用new創建實體時會run一遍prototype chain,取得可用的屬性和方法,
//相同的屬性或方法以血緣近者優先取用
//可透過右側方式探索prototype chain: laigu.__proto__.__proto__...
//讓狗類別繼承Animal類別
Dog.prototype = new Animal();
//此時Dog.prototype指向的記憶體位置已不同,已是指向Animal類別的一個實體,值已從原本的{bark:function(){}}變成{weight:undefined, height:undefined, ...},而Dog.prototype.__proto__指向的是其父類別的prototype,也就是Animal.prototype
laifu.bark();
//laifu.__proto__指向的仍是原本的記憶體位置,也就是原本的Dog.prototype,值是{bark:function(){}},所以仍然可使用bark方法
//創造另一個狗類別實體
var littleBlack = new Dog('littleBlack');
//littleBlack.bark();已無法使用這個方法,因為littleBlack.__proto__指向的Dog.prototype已是新的位置,有新的值{weight:undefined, height:undefined, ...}
console.log(littleBlack.moveable);//繼承後新建的實體依循新的prototype chain取得moveable這個屬性
console.log(laifu.moveable);//繼承前既存的實體仍是依循原本的prototype chain,其中沒有moveable這個屬性,所以輸出undefined
//定義一個新的黃金獵犬類別
function GoldenRetriever() {
this.hunt = function () {
console.log('It\'s hunting');
};
}
//繼承狗類別
GoldenRetriever.prototype = new Dog();
var littleGold = new GoldenRetriever();
//實體littleGold透過littleGold.__proto__,也就是GoldenRetriever.prototype獲得name屬性並習得setName方法
//以下內容值驗證,後續的console.log都是在驗證內容值
//以下內容值驗證,後續的console.log都是在驗證內容值
console.log(littleGold.__proto__);
console.log(GoldenRetriever.prototype);
littleGold.setName('littleGold');
console.log(littleGold.name);
//透過littleGold.__proto__.__proto__,也就是Dog.prototype獲得moveable屬性
console.log(littleGold.__proto__.__proto__);
console.log(Dog.prototype);
console.log(littleBlack.__proto__);
console.log(littleGold.moveable);
//使用自己的方法
littleGold.hunt();
//使用prototype動態新增屬性與方法,會即時套用至prototype chain上所有既存實體
Animal.prototype.p1 = 'p1';
Dog.prototype.p2 = function (){console.log('p2')};
GoldenRetriever.prototype.p3 = 'p3';
console.log(littleGold.p1);
littleGold.p2();
console.log(littleGold.p3);
//相同的屬性或方法,血緣近者優先取用
Animal.prototype.lol = '789';
Dog.prototype.lol = '456';
GoldenRetriever.prototype.lol = '123';
console.log(littleGold.lol);
//輸出prototype chain尾端類別實體擁有的全部屬性和方法
for(prop in littleGold){
console.log(prop);
}
//分界線
console.log('-------------------------------');
//private 屬性和方法,物件中以this定義的屬性和方法為public,以var定義者為private
//private 屬性和方法不會在for in迴圈中顯示
function Person(name, age, weight){
//public
this.name = name;
this.say = function (words){
console.log(this.name + ' says ' + words + '.');
};
//private
var age = age;
var weight = weight;
var think = function(question){
return ('I think ' + question + ' is a great thing.');
};
//使用public方法存取private屬性和方法
this.setAge = function (num){
age = num;
};
this.tellingAge = function (friendship){
if(friendship === 'good') return age;
else return 'this is secret.';
};
this.tellingWeight = function (friendship){
if(friendship === 'good') return weight;
else return 'this is secret.';
};
this.advise = function (friendship){
if(friendship === 'good') return think;
else return 'sorry I have no time.';
};
}
var Ryu = new Person('Ryu',36,75);
for(p in Ryu){
console.log(p);
}
console.log(Ryu.name);
Ryu.say('hello!');
console.log(Ryu.tellingAge('good'));
console.log(Ryu.tellingWeight('normal'));
var thinkingMethod = Ryu.advise('good');
var thought = thinkingMethod('OOP');
console.log( thought );
console.log( thinkingMethod('OOP') );
console.log( Ryu.advise('good')('OOP') );
console.log('------------------------------');
//定義rosyPerson類別
function rosyPerson (mentality) {
this.mentality = mentality;
var hardshipExp = 'many years ago...';
this.tellingStory = function (){
return hardshipExp;
};
}
//繼承Person類別,private屬性和方法一樣會繼承
rosyPerson.prototype = new Person();
var rosyKen = new rosyPerson('positive');
for(p in rosyKen){
console.log(p);
}
console.log( rosyKen.advise('good')('programming') );
rosyKen.setAge(33);
console.log( rosyKen.tellingAge('good') );
//所有物件皆繼承原始物件Object,因此擁有Object.prototype中的所有屬性和方法,例如hasOwnProperty方法
//可透過console.log(Object.prototype)檢視有哪些通用的方法和屬性
console.log(Object.prototype);
//hasOwnProperty方法確認是否擁有指定的屬性或方法,傳回true或false,繼承的屬性或方法會傳回false,
//private 屬性和方法也會傳回false
console.log(rosyKen.hasOwnProperty('mentality'));
console.log(rosyKen.hasOwnProperty('name'));
console.log(rosyKen.hasOwnProperty('hardshipExp'));
console.log(rosyKen.hasOwnProperty('tellingStory'));
console.log(rosyKen.tellingStory());
//打完收工
沒有留言:
張貼留言