设为首页收藏本站|繁體中文

Project1

 找回密码
 注册会员
搜索
12
返回列表 发新帖
楼主: Yosokay
打印 上一主题 下一主题

[有事请教] 关于菜单窗口

[复制链接]

Lv3.寻梦者

梦石
0
星屑
3399
在线时间
461 小时
注册时间
2013-12-7
帖子
333
11
发表于 2018-3-17 18:11:02 | 只看该作者
本帖最后由 ekmomo 于 2018-3-17 18:31 编辑
Yosokay 发表于 2018-3-17 15:09
尝试回答您的问题:
1.构造函数以大写字母开头,普通函数以小写字母开头,用作区分。
2.this做引用,取决 ...


基本正确,MV只是为了保持RGSS编码风格所以用了this.initialize.apply(this, arguments)来创造构造函数,其实把initialize方法内容直接写在构造函数内是一样的。
构造函数这个东西在JS里就是一切对象与继承的根本,事实上在早期的JS当中,也只能通过构造函数来实现对象的继承。
让我们打开工程运行游戏,按F8选console(控制台)来创建一个构造函数
JAVASCRIPT 代码复制
  1. function Cat(){
  2.         this._name = 'miaomiao';
  3.         this._hp = 1;
  4.                 this.eat = function(){
  5.                         this._hp += 1;
  6.                         console.log(this._hp);
  7.                 };
  8. }


好了,构造函数创建好了,我们可以通过new操作符来实例化一个对象。
JAVASCRIPT 代码复制
  1. var cat = new Cat;
  2. var cat2 = new Cat;


我们完全可以像自己定义的对象一样来操作被构造的对象。
JAVASCRIPT 代码复制
  1. cat3 = {
  2.         _name : 'miaomiao',
  3.         _hp : 1,
  4.                 eat : function(){
  5.                         this._hp += 1;
  6.                         console.log(this._name + ' hp : ' + this._hp);
  7.                 }
  8. };
  9. cat3.eat();
  10. cat3.eat();
  11. cat3.eat();

我们也可以随时修改被实例化的对象的属性。
JAVASCRIPT 代码复制
  1. cat2._name = "wangcai";
  2. cat3._name = "dahuang";

我们要实现构造函数的继承也比较容易,其实你已经接触过了。
JAVASCRIPT 代码复制
  1. function SuperCat(){
  2.         this._mp = NaN;
  3.         this._sex = "female";
  4.         Cat.apply(this, arguments);
  5. }

这样新的构造函数不仅有两个新的属性,还有Cat得属性和方法(可以var cat4 = new SuperCat;然后输入cat4自己查看)。但是我们发现一个问题,这喵怎么这么能吃????我们需要修改eat方法。

JAVASCRIPT 代码复制
  1. function Cat(){
  2.         this._name = 'miaomiao';
  3.         this._hp = 1;
  4.         this._eatTooMuch = false;
  5.                 this.eat = function(){
  6.                         if (this._hp > 5) this._eatTooMuch = true;
  7.                         if (this._hp >= 1){
  8.                                 this._hp += (this._eatTooMuch) ? -1 : 1;
  9.                                 console.log(this._hp);
  10.                         }else{
  11.                                 console.log(this._name + ' die');
  12.                         }
  13.                 }
  14. }

当我们把这行代码输入到控制台中,发现了一个问题:!!!已经构造出来的小猫咪还是无限吃!!!
这不仅仅是需要批量修改的问题,细思恐极,用构造函数创造出来的对象方法也是和属性一样不共用的,每只猫都要独立开辟一块内存来存方法。
你可以想象我们在游戏中Game_Item下的isSkill()方法,如果背包里有3000个道具,它们都用自己独立的方法会多么令人难过。

于是乎JS出现了prototype属性,这个属性是伴随着构造函数的声明就已经默认创建的一个对象,你可以在console中直接输入Cat.prototype或者
cat.__proto__来查看构造函数(原型)及实例化对象的原型。

原型的操作非常简单,我们看下面这个例子很直观。
JAVASCRIPT 代码复制
  1. Cat.prototype.age = 1;//返回1
  2. var cat = new Cat;
  3. var cat2 = new Cat;
  4. cat.age; //返回1
  5. cat.age += 2; //返回3
  6. Cat.prototype.age += 1;//返回2
  7. cat2.age;//返回2
  8. cat.age;//返回3


这就是原型链的作用方式,类似于函数的作用域,如果一个对象自己没有一个属性或者方法,它会去它的原型链上依次查找,如果他自己有这个属性或者方法,它就用自己的。
原型链的继承有很多种方式,MV采用的是圣杯模式
JAVASCRIPT 代码复制
  1. Window_Command.prototype = Object.create(Window_Selectable.prototype);
  2. 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()就可以了。
甚至你可以在实例化的对象上直接改,这样对象会直接调用自己的方法而非原型上的。(只能是由对象调用,了解就行)

最后做个总结,一定要学会多用控制台。
回复 支持 反对

使用道具 举报

Lv2.观梦者

梦石
0
星屑
269
在线时间
24 小时
注册时间
2017-11-1
帖子
15
12
 楼主| 发表于 2018-3-18 15:05:50 | 只看该作者
ekmomo 发表于 2018-3-17 18:11
基本正确,MV只是为了保持RGSS编码风格所以用了this.initialize.apply(this, arguments)来创造构造函数, ...

多谢讲解,但还是有地方不清楚

1.在mv里自带的脚本,如Scnen_Base为例:
  1. function Scene_Base() {
  2.     this.initialize.apply(this, arguments);
  3. }
  4. Scene_Base.prototype.initialize = function() {
  5.     Stage.prototype.initialize.call(this);
  6.     this._active = false;
  7.     this._fadeSign = 0;
  8.     this._fadeDuration = 0;
  9.     this._fadeSprite = null;
  10.     this._imageReservationId = Utils.generateRuntimeId();
  11. };
复制代码

可以去掉this.initialize.apply(this, arguments);吗,为什么要加这一行

2.
  1. Cat.prototype.age = 1;//返回1
  2. var cat = new Cat;
  3. var cat2 = new Cat;
  4. cat.age; //返回1
  5. cat.age += 2; //返回3
  6. Cat.prototype.age += 1;//返回2
  7. cat2.age;//返回2
  8. cat.age;//返回3
复制代码

为什么在Cat.prototype.age += 1后cat.age是3而不是4?

3.关于mv脚本运行是不是先把默认脚本全部执行,然后再依次执行插件脚本?
回复 支持 反对

使用道具 举报

Lv3.寻梦者

梦石
0
星屑
3399
在线时间
461 小时
注册时间
2013-12-7
帖子
333
13
发表于 2018-3-18 21:56:40 | 只看该作者
本帖最后由 ekmomo 于 2018-3-18 22:02 编辑

我们先来将你的第三个问题
关于mv脚本运行是不是先把默认脚本全部执行,然后再依次执行插件脚本?

这句话本身没错,但是我觉得你问出这个问题是没有理解解释器跑代码的基本原理。如:
JAVASCRIPT 代码复制
  1. function fn(){
  2.   你好这里是一行错误的代码啦啦啦
  3. }

我们把代码在控制台里输入一遍,不会报错。是因为我们即使在函数体(花括号内)里写一万行代码,解释器都不会读到函数体里面去(预编译阶段会抛出基本语法错误,语法错误是什么了解一下就行)。只有在我们调用函数(使用fn()来调用它)的时候,函数才会被执行
这个例子里的语句被称作函数声明语句,它包括两个部分:函数的声明和函数的定义。它就好像是变量声明和赋值一样(var a = 1),解释器再读到函数声明的时候,只是把函数放入内存。函数体内的所有代码是什么解释器根本不关心。
然后我们讲下函数的重写,它就好像是变量的覆盖。
JAVASCRIPT 代码复制
  1. function fn(){//1
  2.   你好这里是一行错误的代码啦啦啦//未执行时被忽略
  3. }
  4. function fn(){//2
  5.   console.log("你好!这里是一行正确的代码啦啦啦。")//未执行时被忽略
  6. }
  7. fn();

控制台复制以上代码运行,我们发现可以正确打印文本。这说明函数被重写以后,存放之前函数的内存就被释放了。
这里要说一点,JS跑代码的时候是从上到下 逐行执行的。在下方定义的函数或者赋值的变量总会覆盖上方的。这也是我们为什么可以在插件里直接重写MV自带的函数以及为什么有些插件需要按顺序排放原因。
JAVASCRIPT 代码复制
  1. fn1();
  2. function fn1(){
  3. console.log("JS逆天");
  4. }

最后这个例子是预编译阶段声明提前的示例,我们可以先调用再定义一个函数。(了解就行)


然后我们再回答第二个问题
JAVASCRIPT 代码复制
  1. var a = 1;
  2. var b = 2;
  3. var c = 3;
  4. function mrScope(){
  5.         var a = 4;
  6.         b = 5;//自己没申请,直接写全局
  7.         function msScope(){
  8.                 var c = 6;
  9.                 console.log(a);//mrScope有
  10.                 console.log(b);//全局有
  11.                 console.log(c);//自己有
  12.                 console.log("-----------无情分割-----------");
  13.         }
  14.         msScope();
  15. }
  16.  
  17. mrScope();
  18. console.log(a);
  19. console.log(b);
  20. console.log(c);


我们讲原型链的时候顺带就把作用域讲了好了。仔细看代码msScope在最里层,他外层有mrScope和全局作用域。我们现在全局定义了a,b,c然后在mrScope作用域里定义了自己的a,和重写了全局的b。(它自己没b所以用了全局的)msScope中,他定义了自己的c然后输出了mrScope的a全局的b(自己和mrScope都没有)和自己的c。所以结果是4,5,6。

而在全局执行的时候,它只能用自己的变量。简而言之就是里层函数作用域没有一个变量时它会依次向上级作用域查找,里可以找外,而外不能找里。

这里还有一个重点就是
JAVASCRIPT 代码复制
  1. funciton er(){
  2. b = 5;
  3. }
  4. console.log(b);

我们在如果在一个函数里不用var 声明就直接使用一个变量的话,它会变成全局变量。我们回答第一个问题的时候就说过,函数体只有在函数执行的时候才有意义,函数的作用域也是一样,当函数执行完毕时,它的作用域和作用域里面的变量都会被自动销毁(闭包除外)但是如果不用var这个变量提升到全局的话,只有在游戏结束了它才销毁。

原型链的使用原理和作用域相似,原型可以依次查找原型链上的属性。cat.age += 2;等价于

JAVASCRIPT 代码复制
  1. cat.age/*定义过自己就有了,以后原型就管不了了*/ = cat.age/*这里自己还没有就调原型的*/ + 2;


第一个问题 为什么要加this.initialize.apply(this, arguments);
你可以理解为apply和call都是复制代码用的,这俩方法除了写参数的形式不一样以外没区别。如:
JAVASCRIPT 代码复制
  1. function say(){
  2. console.log("5555");
  3. }
  4. function cry(){
  5. var tears =tears || 0;
  6. tears += 1;
  7. say.call(this);//相当于把say的函数体直接复制到这里。
  8. }
  9. cry()
回复 支持 反对

使用道具 举报

Lv2.观梦者

梦石
0
星屑
269
在线时间
24 小时
注册时间
2017-11-1
帖子
15
14
 楼主| 发表于 2018-3-19 20:53:39 | 只看该作者
ekmomo 发表于 2018-3-18 21:56
我们先来将你的第三个问题

这句话本身没错,但是我觉得你问出这个问题是没有理解解释器跑代码的基本原理。 ...

蟹蟹

但是感觉对initialize还是难理解,譬如:
  1. function Scene_Base() {
  2.     //this.initialize.apply(this, arguments);
  3. }
  4. Scene_Base.prototype.initialize = function() {
  5.     Stage.prototype.initialize.call(this);
  6.     this._active = false;
  7.     this._fadeSign = 0;
  8.     this._fadeDuration = 0;
  9.     this._fadeSprite = null;
  10.     this._imageReservationId = Utils.generateRuntimeId();
  11. };
复制代码

这样的话会出现错误:undefined is not a function,其原因是因为Stage.prototype.initialize.call(this);这个没有调用进来,导致一些变量和方法的失联嘛?

那假如这是一个独立的方法,是不是可以在这个构造函数省去this.initialize.apply(this,arguments)?譬如:
  1. function Independent_Scene_Base() {
  2.     //this.initialize.apply(this, arguments);
  3. }
  4. Independent_Scene_Base.prototype.initialize = function() {
  5.     //Stage.prototype.initialize.call(this);
  6.     this._active = false;
  7.     this._fadeSign = 0;
  8.     this._fadeDuration = 0;
  9.     this._fadeSprite = null;
  10.     this._imageReservationId = Utils.generateRuntimeId();
  11. };
复制代码

是不是就不会报错了呢?
回复 支持 反对

使用道具 举报

Lv3.寻梦者

梦石
0
星屑
3399
在线时间
461 小时
注册时间
2013-12-7
帖子
333
15
发表于 2018-3-20 00:25:40 | 只看该作者
本帖最后由 ekmomo 于 2018-3-20 00:41 编辑

你再仔细看下我前面的回复,其实我有解释的。
关键不是initialize,而是apply。apply和call的作用你可以理解成复制代码。
JAVASCRIPT 代码复制
  1. function Scene_Base() {
  2.     //this.initialize.apply(this, arguments);
  3.    //等同于Scene_Base.initialize.apply(this, arguments);
  4.    /*等同于*/
  5.     Stage.prototype.initialize.call(this);
  6.     this._active = false;
  7.     this._fadeSign = 0;
  8.     this._fadeDuration = 0;
  9.     this._fadeSprite = null;
  10.     this._imageReservationId = Utils.generateRuntimeId();
  11. }

JAVASCRIPT 代码复制
  1. //Scene_Base.initialize.apply(obj, arguments); 等同于
  2.     Stage.prototype.initialize.call(obj);
  3.     obj._active = false;
  4.     obj._fadeSign = 0;
  5.     obj._fadeDuration = 0;
  6.     obj._fadeSprite = null;
  7.     obj._imageReservationId = Utils.generateRuntimeId();

你把initialize.apply注释了相当于对象在构造时少了很多属性和方法。

JAVASCRIPT 代码复制
  1. //arguments是一个伪数组对象,是函数(或构造函数)执行时的参数列表,如:
  2. function fn(){
  3. console.log(arguments[0]);
  4. console.log(arguments[1]);
  5. }
  6. fn(5, 2, 8);//输出5 2


apply 和 call唯一的区别就是,call接受多个参数,apply接受两个参数。
apply和call里第一个参数是一个对象,它用来替换原先函数里的this(就是让这个函数明白是谁调用的它),apply的第二个参数是一个数组,写法是apply(this, [a,b,c])而call得写法是call(this, a, b, c)。
回复 支持 反对

使用道具 举报

Lv2.观梦者

梦石
0
星屑
269
在线时间
24 小时
注册时间
2017-11-1
帖子
15
16
 楼主| 发表于 2018-3-21 13:30:17 | 只看该作者
ekmomo 发表于 2018-3-20 00:25
你再仔细看下我前面的回复,其实我有解释的。
关键不是initialize,而是apply。apply和call的作用你可以理 ...

十分感谢,能理解这一点了,下次有问题继续请教
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册会员

本版积分规则

拿上你的纸笔,建造一个属于你的梦想世界,加入吧。
 注册会员
找回密码

站长信箱:[email protected]|手机版|小黑屋|无图版|Project1游戏制作

GMT+8, 2024-4-25 07:17

Powered by Discuz! X3.1

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表