類, 類的實(shí)例太示,類的構(gòu)造函數(shù)处坪,類的成員函數(shù)/類原型
1: 類: 具有相同類型的一類實(shí)例的邏輯描述;
2: 類的實(shí)例: 被構(gòu)造函數(shù)出來的具有這個(gè)類的實(shí)例特征的一個(gè)表;
3: 類的成員函數(shù): 完成對(duì)應(yīng)的邏輯凝危,通過this來操作不通的實(shí)例;
// “function Person”叫做類的構(gòu)造函數(shù)
function Person(name, age) {
this.name = name;
this.age = age;
}
// “.get_age”叫做類的方法,又叫做類的成員函數(shù)
// 用 this 操作類的實(shí)例來完成特定的邏輯;
Person.prototype.get_age = function() {
return this.age;
}
Person.prototype.get_name = function() {
return this.name;
}
// end
var xiaoming = new Person("xiaoming", 12);
//new 函數(shù)的作用相當(dāng)于手寫了下面這樣的一個(gè)表:
var my_person = {
//數(shù)據(jù)
name: "xxxxx",
age: 12,
//成員函數(shù)
__proto__: {
get_name: Person.prototype.get_name,
get_age: Person.prototype.get_age,
}
};
//“xiaohong”叫做類的實(shí)例
var xiaohong = new Person("xiaohong", 13);
var xiaotian = new Person("xiaotian", 13);
console.log(xiaotian);
console.log(xiaohong);
console.log(xiaoming);
xiaotian.get_name();
xiaoming.get_name();
xiaotian.get_name();
// xiaotian, xiaoming, xiaohong 者三個(gè)實(shí)例都有相同的結(jié)構(gòu):
// 1:每個(gè)人實(shí)例 都有 name, age,
// 2: 每個(gè)實(shí)例都有一個(gè) (函數(shù))方法get_name, get_age;
// 他們這三個(gè)實(shí)例,屬于同一個(gè)類型;
// 或者說他們?nèi)齻€(gè)實(shí)例潜必,都是Person類
// 把Person看作是一個(gè)類, xiaotian, xiaoming,xiaohong可以看作是類的 3個(gè)實(shí)例;
舉一個(gè)例子:新建一個(gè)Enemy.js文件磺平,寫入
// 類
function Enemy(name, level) {
this.name = name;
this.level = level;
}
// “Enemy.prototype”這個(gè)表我們又把他叫做“類原型”;
Enemy.prototype.attack_player = function() {
console.log("attack_player", this);
}
// 類結(jié)束
module.exports = Enemy;
在 main.js 文件中
var Enemy = require("./Enemy")
var e1 = new Enemy("e1", 1);
e1.attack_player();
var e2 = new Enemy("e2", 2);
e2.attack_player();
繼承
為何要用繼承魂仍?
為了擴(kuò)展“類的原型”拐辽,類的原型不需要構(gòu)造函數(shù)里的實(shí)例
例1:繼承機(jī)制: 貓的代碼---> 波斯貓的代碼(在貓的代碼的基礎(chǔ)上擴(kuò)展波斯貓的代碼
例2:敵人--> 擴(kuò)展成特殊的敵人。在原來敵人的基礎(chǔ)上--> 繼承原來的代碼, --> 再添加;
實(shí)現(xiàn)繼承擦酌,例如:
- new 機(jī)制使得 Cat 的空表"__ proto __"被創(chuàng)建俱诸,并攜帶參數(shù) name "Black Cat"
- 然后化身為函數(shù) Cat 中的 this
- 接著被 .call 顯式傳遞給函數(shù) Animal 中的 this,并攜帶參數(shù)name "Black Cat"
- 執(zhí)行函數(shù) Animal赊舶,意味著向 Cat 表中添加和函數(shù) Animal相同地 key 和 value
- 打印出 "Black Cat"
function Animal(name){
this.name = name;
this.showName = function(){
console.log(this.name);
}
}
function Cat(name){
Animal.call(this, name);
}
var cat = new Cat("Black Cat");
cat.showName();
// step1: 創(chuàng)建一個(gè)BossEnemy的構(gòu)造函數(shù);
function BossEnemy(name, level) {
--------------------------------------------------------------------------------
// 調(diào)用基類的構(gòu)造函數(shù)
// 如何繼承“構(gòu)造函數(shù)里的成員”睁搭?
// new 一個(gè) "BossEnemy",就是在表 BossEnemy 下新建一個(gè)類原型__proto__空表
// 因?yàn)樵?new 機(jī)制下笼平,函數(shù) BossEnemy 下的 this介袜,就是類原型__proto__空表
// 接著被 .call 顯式傳遞給構(gòu)造函數(shù) Enemy,構(gòu)造函數(shù) Enemy 下的 this出吹,就被傳遞為表 BossEnemy 下的類原型__proto__空表
// 執(zhí)行構(gòu)造函數(shù) Enemy遇伞,將構(gòu)造函數(shù) Enemy 下的實(shí)例,添加到表 BossEnemy 下的類原型__proto__空表
// 又因?yàn)樵?new 機(jī)制捶牢,會(huì)返回表 BossEnemy 下的類原型__proto__表
// 當(dāng) var boss = new BossEnemy("通天教主", 10) 時(shí)
// 意思是表 BossEnemy 下的類原型__proto__空表鸠珠,攜帶著由參數(shù)name, level 分別傳過來的 "通天教主", 10
// 被傳入到構(gòu)造函數(shù) Enemy 中,執(zhí)行的內(nèi)容是向類原型__proto__空表里添加數(shù)據(jù)
// key = name秋麸,value = 參數(shù)name
// 所以最終返回的表渐排,就是 attack_player Enemy { name: '通天教主', level: 10, blood: 100 }
Enemy.call(this, name, level) //這里的this 是 new BossEnemy() 的空表
// 如何擴(kuò)展自己的成員?
this.blood = 100;
}
// step2: 把 Enemy 里面所有的原型方法灸蟆,都復(fù)制過來;
// 注意:不能直接這樣寫驯耻。當(dāng)我們修改BossEnemy.prototype原型的時(shí)候,同時(shí)也會(huì)修改到Enemy.prototype炒考。
// 因?yàn)樗麄儍蓚€(gè)變量都指向同一個(gè)“表的實(shí)例”可缚,所以要新建一個(gè)表
// BossEnemy.prototype = Enemy.prototype;
--------------------------------------------------------------------------------
// 如何繼承“構(gòu)造函數(shù) Enemy 的表 prototype”?
// 寫法1
BossEnemy.prototype = {};
for(var i in Enemy.prototype) {
BossEnemy.prototype[i] = Enemy.prototype[i];
}
// “規(guī)范的”寫法2
// 通過下面這兩行斋枢,var a 就相當(dāng)于構(gòu)造函數(shù)
// 要繼承基類 Enemy.prototype帘靡,只繼承 prototype。
// 如果直接 new 一個(gè)Enemy瓤帚,除了繼承它的 prototype
// 還會(huì)把函數(shù) Enemy 里的數(shù)據(jù)(this.xxx) 全都拿過來描姚。
var a = function() {}
//a.prototype 指向 Enemy.prototype
a.prototype = Enemy.prototype;
// 用 new 函數(shù)4個(gè)步驟,把類的原型 Enemy.prototype 復(fù)制到表__proto__中戈次。
BossEnemy.prototype = new a(); // {__proto__: Enemy原型}
--------------------------------------------------------------------------------
// 如何擴(kuò)展原型轩勘?
// 獲取到原型以后,就繼承了 Enemy 的原型方法;
BossEnemy.prototype.boss_attack = function() {
console.log("boss_attack");
}
var boss = new BossEnemy("通天教主", 10);
// 表的訪問
boss.boss_attack();
boss.attack_player();
--------------------------------------------------------------------------------
// 重載
// 根據(jù)優(yōu)先找到表 BossEnemy.prototype.attack_player 的怯邪,會(huì)把Enemy.prototype.attack_player 沖掉
BossEnemy.prototype.attack_player = function() {
//調(diào)用基類的 prototype
Enemy.prototype.attack_player.call(this);
// 這里的 this绊寻,還是 new BossEnemy 的表
console.log("BossEnemy get name");
return this.name;
}
boss.attack_player();
/*
打印出:
attack_player Enemy { name: '通天教主', level: 10, blood: 100 }
BossEnemy get name
*/
Class函數(shù)
javascript 沒有類,繼承語(yǔ)法的, new, this, 模擬,有些地方些了一個(gè)繼承函數(shù)榛斯。這個(gè)Class函數(shù)观游,能幫我們得到新的類
function Class(class_desic) {
var new_class = function(name, level) {
if (class_desic.extend) { // 有基類
// 調(diào)用基類的構(gòu)造函數(shù)
class_desic.extend.call(this, name, level);
}
// 加一個(gè)初始化的接口
if (class_desic.init) {
class_desic.init.call(this);
}
}
if (class_desic.extend) {
var a = function() {};
a.prototype = class_desic.extend.prototype;
new_class.prototype = new a();
}
else {
new_class.prototype = {};
}
for(var i in class_desic) {
if (i == "extend") {
continue;
}
new_class.prototype[i] = class_desic[i];
}
return new_class;
}
var BossEnemy2 = Class({
// 定義寫死的關(guān)鍵字 extend 是繼承自那個(gè)基類
extend: Enemy, // 對(duì)象的名字
init: function(){
},
boss_attack: function() {
},
add_blood: function() {
console.log("add_blood", this);
/*
這里的 this 指的也是 b2 的 this
b2 的實(shí)例顯式的綁定過來
這里的 this 也是指后面調(diào)用的實(shí)例
*/
},
});
var b2 = new BossEnemy2("boss2", 1111);
console.log(b2);
b2.add_blood();