本帖最后由 ekmomo 于 2018-3-17 18:31 编辑
基本正确,MV只是为了保持RGSS编码风格所以用了this.initialize.apply(this, arguments)来创造构造函数,其实把initialize方法内容直接写在构造函数内是一样的。
构造函数这个东西在JS里就是一切对象与继承的根本,事实上在早期的JS当中,也只能通过构造函数来实现对象的继承。
让我们打开工程运行游戏,按F8选console(控制台)来创建一个构造函数
function Cat(){ this._name = 'miaomiao'; this._hp = 1; this.eat = function(){ this._hp += 1; console.log(this._hp); }; }
function Cat(){
this._name = 'miaomiao';
this._hp = 1;
this.eat = function(){
this._hp += 1;
console.log(this._hp);
};
}
好了,构造函数创建好了,我们可以通过new操作符来实例化一个对象。
var cat = new Cat; var cat2 = new Cat;
var cat = new Cat;
var cat2 = new Cat;
我们完全可以像自己定义的对象一样来操作被构造的对象。
cat3 = { _name : 'miaomiao', _hp : 1, eat : function(){ this._hp += 1; console.log(this._name + ' hp : ' + this._hp); } }; cat3.eat(); cat3.eat(); cat3.eat();
cat3 = {
_name : 'miaomiao',
_hp : 1,
eat : function(){
this._hp += 1;
console.log(this._name + ' hp : ' + this._hp);
}
};
cat3.eat();
cat3.eat();
cat3.eat();
我们也可以随时修改被实例化的对象的属性。
cat2._name = "wangcai"; cat3._name = "dahuang";
cat2._name = "wangcai";
cat3._name = "dahuang";
我们要实现构造函数的继承也比较容易,其实你已经接触过了。
function SuperCat(){ this._mp = NaN; this._sex = "female"; Cat.apply(this, arguments); }
function SuperCat(){
this._mp = NaN;
this._sex = "female";
Cat.apply(this, arguments);
}
这样新的构造函数不仅有两个新的属性,还有Cat得属性和方法(可以var cat4 = new SuperCat;然后输入cat4自己查看)。但是我们发现一个问题,这喵怎么这么能吃????我们需要修改eat方法。
function Cat(){ this._name = 'miaomiao'; this._hp = 1; this._eatTooMuch = false; this.eat = function(){ if (this._hp > 5) this._eatTooMuch = true; if (this._hp >= 1){ this._hp += (this._eatTooMuch) ? -1 : 1; console.log(this._hp); }else{ console.log(this._name + ' die'); } } }
function Cat(){
this._name = 'miaomiao';
this._hp = 1;
this._eatTooMuch = false;
this.eat = function(){
if (this._hp > 5) this._eatTooMuch = true;
if (this._hp >= 1){
this._hp += (this._eatTooMuch) ? -1 : 1;
console.log(this._hp);
}else{
console.log(this._name + ' die');
}
}
}
当我们把这行代码输入到控制台中,发现了一个问题:!!!已经构造出来的小猫咪还是无限吃!!!
这不仅仅是需要批量修改的问题,细思恐极,用构造函数创造出来的对象方法也是和属性一样不共用的,每只猫都要独立开辟一块内存来存方法。
你可以想象我们在游戏中Game_Item下的isSkill()方法,如果背包里有3000个道具,它们都用自己独立的方法会多么令人难过。
于是乎JS出现了prototype属性,这个属性是伴随着构造函数的声明就已经默认创建的一个对象,你可以在console中直接输入Cat.prototype或者
cat.__proto__来查看构造函数(原型)及实例化对象的原型。
原型的操作非常简单,我们看下面这个例子很直观。
Cat.prototype.age = 1;//返回1 var cat = new Cat; var cat2 = new Cat; cat.age; //返回1 cat.age += 2; //返回3 Cat.prototype.age += 1;//返回2 cat2.age;//返回2 cat.age;//返回3
Cat.prototype.age = 1;//返回1
var cat = new Cat;
var cat2 = new Cat;
cat.age; //返回1
cat.age += 2; //返回3
Cat.prototype.age += 1;//返回2
cat2.age;//返回2
cat.age;//返回3
这就是原型链的作用方式,类似于函数的作用域,如果一个对象自己没有一个属性或者方法,它会去它的原型链上依次查找,如果他自己有这个属性或者方法,它就用自己的。
原型链的继承有很多种方式,MV采用的是圣杯模式
Window_Command.prototype = Object.create(Window_Selectable.prototype); Window_Command.prototype.constructor = Window_Command;
Window_Command.prototype = Object.create(Window_Selectable.prototype);
Window_Command.prototype.constructor = Window_Command;
这样Window_Command就继承了Window_Selectable的原型链啦。比较蠢的是它连constructor一起继承,所以我们需要定义它自己的构造器(其实这是内聚和效率的取舍问题,知道这么写就行没必要较真)。
而Window_Selectable方法继承自Window_Base。所以当Window_Command构造出来的对象调用draw_Text的时候,它现在自己的方法里找》》发现没有》》在去Window_Command原型上找》发现还没有》》去Window_Selectable原型找》》依然没有》》去Window_Base找,找到了就调用,找不到就报错。(这么说不严谨,你可以在控制台var win = new Window_Base;然后输入win查看他的__proto__,其实他还是有原形的)
而当这个对象refresh()的时候,它找到Window_Command就有刷新了,他就用Window_Command的,而不会用Window_Selectable。
比如我们想改变Window_BattleStatus的光标移动规则,我们直接改Window_BattleStatus.prototype.cursorRight就可以了,这样既不会影响到别的窗口还能达到自己的效果。
再比如我们想让装备的帮助窗口显示更多的信息,我们可以新建一个Window_EquipHelp让它继承Window_Help,实例化的时候用Window_EquipHelp,我们只需要重写Window_EquipHelp的refresh()就可以了。
甚至你可以在实例化的对象上直接改,这样对象会直接调用自己的方法而非原型上的。(只能是由对象调用,了解就行)
最后做个总结,一定要学会多用控制台。 |