感谢谢帅shawn分享的canvas动画框架,我再来分一次

//动画框架

http://neekey.net/blog/2011/05/11/canvas-%E7%AE%80%E5%8D%95%E5%8A%A8%E7%94%BB%E5%AE%9E%E7%8E%B0%E6%80%9D%E8%B7%AF/

//使用JavaScript和Canvas开发游戏

http://www.cn-cuckoo.com/2011/08/10/game-development-with-javascript-and-the-canvas-element-2554.html

之前学过的OC是纯粹的面向对象语言,所以我一直很想知道JS里的难道没有“类”的概念吗?直到小新大神讲到了构造函数和原型链的时候,我明白了——在JS中,没有传说中的“类”,而是利用“构造函数+原型链”来模拟其他语言中“类”和“继承”的思路。

初看第一个链接中的动画框架,本以为有之前的OC基础,可以看懂,结果完败。无奈之下只能翻书,把JS红皮书第六章《面向对象的程序设计》通读了一遍,练看带琢磨,2天时间,看完直接低血糖了。。。在此要再次重点感谢小新大神之前的讲解,要不2个月也看不明白。

本章的知识结构是递进完善式的,所以建议想要去看书的同学,不要像我一样,卡在某个你认为不太合理的概念上苦苦纠结,因为你总是会发现,后面一节的概念,是用来完善或代替前一节的概念的(TMD!)。下面的总结,也将采用递进完善式,具体代码请看书P144。

构建对象的方式:

工厂模式:很基础的创建方式,问题是,不能识别对象的爹是谁,总是window。

构造函数模式:完善了工厂模式,可以识别对象是属于哪类。使用new操作符,来确保this的指向,不用new也可以创建,但是this还是window。问题是,方法应当被共享,而不是被每个新实例重复的创建。如果提到外面去声明方法,就失去了封装性。

原型模式:每个构造函数被声明时,会产生一个他的原型对象prototype,可以将需要共享的属性和方法放在这个对象里,就不会被重复创建了。问题是,对于基本数据类型的属性,不能改。对于引用类型的属性,牵一发而动全身。

组合使用构造函数和原型:将需要改的,放在构造函数中,不需要改的即可以被共享的,放在原型中。

动态原型模式:基于上面的组合式,再次优化,将原型的初始化放在构造函数中,只不过要加个if判断,当this.xxx != 'function'时,将this.xxx初始化到 prototype中。这样可以防止原型中的xxx被修改后,牵一发而动全身。

(寄生构造函数模式:这个方式一般不用!原理是把工厂模式封装在构造函数模式里。没看出有毛好处。)

(稳妥构造函数模式:安全模式,不用this不用new,除了用显式声明的方法来访问属性之外,别无他法。)

层层递进有木有!!

  继承:存在一个“类”(父类),我要构造一个新的类,让他除了有自己的特有的属性和方法之外,还具有老类的一切。其实就是你想扩展一个类的功能,但是又不想直接去改他的原始代码,因为他可能很重要,别人还要用。那么就继承一个子类吧!

  原型链:真是个相当逻辑的东东,在挠墙的同时不得不佩服这种设计思路,谁想的啊这是。。。

  书上的概念,简而言之就是另一个指向另一个,而另一个又指向另一个。。。

  我的理解就是:每个“类”(即构造函数)都会伴随着一个原型对象,构造函数和原型中都可以放属性和方法。只不过,构造函数中放的是未来很有可能被修改的属性(私家厕所),而原型里放的都是共享的(公共厕所)。上面的大段中提到,创建对象的合理方法是组合式,也就是说,构造函数+原型,构成了“类”的一个整体。当我想继承一个“新类”的时候,我就在新类的构造函数中声明新加入的属性,然后让新类的原型直接等于一个老类的new实例。此时,新类的构造函数+原型构建完成了,也就成为了一个新的整体。这样一来,新类不但具有自己的新属性(在构造函数中),还包含老类的所有属性和方法(在原型中)。当然,你依旧可以在新类的原型中,继续加入那些适合被共享的新方法。

原型链的问题:牵一发而动全身。还有,不敢给超类的构造函数传参,主要是因为,超类的构造函数现在是子类的原型,你传参了之后怕是会影响子类的所有实例。

  借用构造函数:又是完善。。。这里第三次感谢小新大神,因为他讲了this,call和apply。利用call可以改变作用域来控制this指向,所以在子类的构造函数中,可以用superClass.call(this,x,y,z)来调用父类的构造函数,从而解决了子类new时传参的问题。问题是,这样继承,完全没用上prototype共享的特性,还是会出现重复声明方法的现象。

组合继承:继续完善。。。用call的方式在子类的构造函数中继承,再将子类的原型等于父类的new实例。这样就是在借用构造函数的基础上,用上了prototype的共享特性。问题是,子类的构造函数中继承了一次父类的构造函数,在子类的原型中又继承了一次,相当于父类的构造函数部分被继承了俩次,也就是父类构造函数中包含的属性在子类的构造函数中有一份,在子类的原型中还有一份,而调用属性的时候,JS引擎会先找构造函数再找原型,所以原型中的那一份相当于被屏蔽掉了,浪费了。看到这的时候,我卡了,因为书上没有提到重复调用的问题,是我自己瞎琢磨的,卡了很久我才决定还是先往下看吧。而且我还在想另一个问题,为啥不能让子类的原型=父类的原型呢?这样不是一样继承了吗?后来明白了,如果这样,你给子类的原型添加新方法的时候,父类的原型也会被添加新方法,那也就是跟直接改父类源代码没区别了,失去了继承的意义。而等于new实例的话,你给子类原型添加新方法的时候,相当于是给这个new实例添加方法,并不会影响到父类的原型。

原型式继承:可以这么理解,把一个老对象,包装成一个新对象,把老对象作为新对象的原型。这是一种浅复制的过程。ECMAScript5标准自带这种继承的函数。

var anotherPerson = Object.create(person,自定义描述符); 这个继承方法是个伏笔,是为了后面的终极方法做铺垫的,过!

寄生式继承:就是把原型式继承的语句,和给新对象添加新方法语句,封装在一个新的函数中,依旧是蛋疼的伏笔!

寄生组合式继承(终极境界):

看到这的时候,确实低血糖了。让我比较欣慰的是,这个终极方法,正是解决之前我卡住的那个问题的——“俩次调用问题”。回想一下,刚才我们说的,不希望拥有俩份父类的构造函数中的属性,那么,我们就需要减少一次调用。而在子类中用call调用父类的构造函数这个是不能被删掉的,因为他解决了传参的问题。所以,只能拿subClass.prototype = new superClass()这个开刀了。其实我们就是想用这个语句,让子类的原型=一个新的实例,这个实例并不需要是父类的new实例,而只要包含父类的原型就够了,因为我们已经有一份父类的构造函数中的属性了。童鞋,有没有一个疑问——那让子类的原型=父类的原型不是正好吗?呵呵,这不就是我刚才卡的时候的第二个问题么。。。请翻上去再看一下解释吧。换言之,如果我们能够构造一个对象obj,让他只拥有父类的原型,而不包含父类的构造函数,再让subClass.prototype = obj; 这样就解决问题了。这就用到了原型式继承的浅复制原理,以及寄生式继承的封装方式增强对象(就是给对象添加新东西)。实现这个终极目标,我们需要创造一个函数,引用书上的函数名

  function inheritPrototype(子类,父类){

    var obj = object(父类.prototype) ;//这是刚才说到的ECMAScript5支持的原型式继承方法,与object.create()类似。这一步就是复制一个父类的原型。

    obj.constructor = 子类;      //由于下一句代码属于重写子类原型,将丢失原型的constructor的默认指向,所以先修复一下。

    子类.prototype = obj;      //把这个只拥有父类原型,而不包含父类构造函数的新对象,赋给子类的原型,实现继承。

  }

  有了这个函数后,当我们继承时,需要写 subClass.prototype = new superClass()的时候,就调用 inheritPrototype(subClass,superClass)来代替。            

  看完了这些知识,我又去看那个游戏框架了,并且顺着写一遍,因为是凭感觉临摹,所以其中有我自己的修改。写完了,调试调了1个小时,各种大小写错误,语法小错误,其中还有俩处都用到了闭包来解决this指向问题,问题原因是当一个函数作为参数传入setInterval()时,this会变window。

setInterval((function(self){

      return function(){

          alert(self.xxx);

          }

      })(this),30);

总之,由于临摹欲望而触发的学习过程,涉及到的知识还是非常全面的,关于canvas的相关操作,在这篇文章里就不写了。

Ps:JS代码写很多之后,找错误真是个麻烦事。由于我这编辑器不会报错,我就得一行一行的用alert('1')去测,看到底是哪行之后不弹出了。

下面附上我的临摹框架代码:

<body style="margin:0;padding:0;">
<canvas id='canvas' width="800" height="800"></canvas>
<script>
//寄生组合式继承方式(复制函数+寄生继承函数)
function copy(o){
var Temp = function(){};
Temp.prototype = o;
return new Temp();
}
//构建独立继承父类原型的函数
function inheritPrototype(subclass,superclass){
var proto = copy(superclass.prototype);
proto.constructor = subclass;
subclass.prototype = proto;
}

//精灵(动画元素的抽象类)
var Sprite = function(){
this.speed = {
x : 1,
y : 1
};
}

Sprite.prototype = {
move : function(){
setInterval((function(self){
return function(){
self.x += self.speed.x;
self.y += self.speed.y;
}
})(this),30);
},
draw : function(){}
};

//帧(控制画布刷新的类)
var Fps = function(){
//所有需要重绘的精灵数组
this.sprites = [];

//重绘所需的定时器
this.render = function(){
setInterval((function(self){
//清空画布
return function(){
self.ctx.clearRect(0, 0, 800, 800);
//重绘所有
for(var i in self.sprites){
self.sprites[i].draw();
}
}
})(this),30);//1000/30 = 33帧/秒
}
}

//动画实体
var Circle = function(ctx,x,y,radius){
Sprite.call(this);
this.ctx = ctx;
this.x = x;
this.y = y;
this.radius = radius;
this.strokeStyle = 'rgba('+Math.floor(Math.random()*225)+','+Math.floor(Math.random()*225)+','+Math.floor(Math.random()*225)+','+Math.floor(Math.random()*5+5)/10+')';
this.fillStyle = 'rgba('+Math.floor(Math.random()*225)+','+Math.floor(Math.random()*225)+','+Math.floor(Math.random()*225)+','+Math.floor(Math.random()*5+5)/10+')';
this.lineWidth = Math.floor(Math.random()*10);
}

inheritPrototype(Circle,Sprite);

Circle.prototype.draw = function(){
this.ctx.beginPath();
this.ctx.lineWidth = this.lineWidth;
this.ctx.arc(this.x,this.y,this.radius,0,Math.PI*2,true);
this.ctx.strokeStyle = this.strokeStyle;
this.ctx.fillStyle = this.fillStyle;
this.ctx.stroke();
this.ctx.fill();
};

var ctx = document.getElementById('canvas').getContext('2d');
//创建画布刷新类Fps
var fps = new Fps();
fps.ctx = ctx;
fps.render();//启动

for (var i=0; i<50; i++){
var circle = new Circle(ctx,400,600,Math.random()*60+10);
circle.speed = {x:Math.random()*10-5,y:Math.random()*10-10};
circle.move();
fps.sprites.push(circle);
}
</script>

</body>

转载于:https://www.cnblogs.com/GeekHacker/archive/2012/06/25/2560660.html

构造函数 + 原型链继承 + 临摹面向对象模式的canvas动画框架相关推荐

  1. 记录--JS精粹,原型链继承和构造函数继承的 “毛病”

    这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助 先从面向对象讲起,本瓜认为:面向对象编程,它的最大能力就是:复用! 咱常说,面向对象三大特点,封装.继承.多态. 这三个特点,以" ...

  2. JS中对象的四种继承方式:class继承、原型链继承、构造函数继承、组合继承(构造函数和原型链继承的结合)

    前言 才发现之前没有对JavaScript中的继承做过总结,不过看得到是不少,接下来就对这几种继承方式做一下总结. class继承 class继承是ES6引入的标准的继承方式. ES6引入了class ...

  3. 原型链继承和构造函数继承

    //原型链继承function Father(name,age){//构造器this.name=name;this.age=age;this.say=function(){console.log(th ...

  4. JavaScript进阶学习(二)—— 基于原型链继承的js工具库的实现方法

    文章来源:小青年原创 发布时间:2016-07-03 关键词:JavaScript,原型链,jQuery类库 转载需标注本文原始地址: http://zhaomenghuan.github.io... ...

  5. 继承方式之原型链继承

    继承方式之原型链继承: 今天实践了一下原型链继承,与之前也是有了更深一点的理解和体会吧.顺便写点东西,加深一下印象,有哪里说得不对的,请大家评论留言,本人一定洗耳恭听,虚心学习. 原型链继承,顾名思义 ...

  6. 继承方式一:原型链继承

    继承是OO语言中的一个最为人津津乐道的概念.许多OO语言都支持两种继承方式:接口继承和实现继承.接口继承只继承方法和签名,而实现继承则继承实际的方法.由于函数没有签名,所以在ECMAScript中无法 ...

  7. 原型和原型链,原型继承和原型链继承

    一.对象 js中的基本数据类型分为:- 内置基本类型:number string 布尔 null undined:- 引用类型(对象):objectjs中万物皆为对象- 对象:String.Array ...

  8. ES6 继承(复习原型链继承)

    2019独角兽企业重金招聘Python工程师标准>>> 原型链继承 <script type="text/javascript">/* 原型链 继承 ...

  9. Javascript 对象继承 原型链继承 对象冒充 call 混合方式

    一.原型链继承 function ClassA() {} ClassA.prototype.color = "blue"; ClassA.prototype.sayColor = ...

最新文章

  1. 被批伪开源!刚刚融资6千万美元的Redis怎么了?
  2. 新装WINDOWS XP系统 必须安装的十大高危漏洞补丁
  3. SpringBoot 项目使用 SLF4J+logback 进行日志记录,来增强可维护性
  4. AjaxControlToolkit控件效果演示
  5. PopupWindow在项目中的使用 并指定位置及加入动画效果
  6. 重新拎一遍js的正则表达式
  7. java list集合运算
  8. 【渝粤题库】国家开放大学2021春2605经济法律基础题目
  9. Java关键字transient
  10. python如何删除对象_Python-从列表中删除对象
  11. mysql purge进程_MySQL数据库之Purge死锁问题解析
  12. 为什么有的PLC需要上电停止功能,能防止PLC变砖,可编程控制器 上电停止 串口通信 梯形图
  13. 大数据工程师简历_大数据毕业生简历该怎么写?
  14. 架空线路的基本结构及组成
  15. [除一波线段树和平衡树的草]
  16. Androidnbsp;滑动屏幕效果学习之Gestur…
  17. Google 工程主管:AIGC 将在三年内终结编程!
  18. 百度云盘秒传链接的建立
  19. 关于参加“兆易创新杯”第十三届中国研究生电子设计竞赛,国赛二等奖的总结(fishing_5)
  20. 基于FRP反向代理工具实现内网穿透攻击

热门文章

  1. No execution.target specified in your configuration file.
  2. Django的静态文件路径设置对比
  3. hexo的yelee主题使用katex引擎(markdown渲染加速)
  4. 关于softmax loss这个概念
  5. cython编码报错
  6. html 弹出层插件,jQuery弹出层插件(原创)
  7. python的基本原理_Python函数基本使用原理详解
  8. linux是否有免安装程序,在线Ubuntu Linux系统,免安装体验Linux系统
  9. python3 案例分享--Jupyter Notebook Demo
  10. 如何提高天猫入驻成功率?掌握这两点即可