对于面向对象语言来说,继承机制是代码复用的基础,很不幸的是javascript作为一个基于原型继承的语言,并没有在本身语言层面上直接作出对类继承的支持。

但是js语言拥有很强大的表现力。所以一般是js的使用者自行设计一套继承机制,这个机制必须包括几个点,对私有访问权限的模拟,对属性和类属性的不同实现,对方法覆盖的支持,对父类被覆盖方法的访问等。

cocos2d-x中,整合了两套继承机制,看《MoonWarriors》例子中的源码SysMenu.js文件

varSysMenu=cc.Layer.extend({_ship:null,ctor:function(){cc.associateWithNative(this,cc.Layer);},init:function(){varbRet=false;if(this._super()){winSize=cc.Director.getInstance().getWinSize();varsp=cc.Sprite.create(s_loading);sp.setAnchorPoint(cc.p(0,0));this.addChild(sp,0,1);varlogo=cc.Sprite.create(s_logo);logo.setAnchorPoint(cc.p(0,0));logo.setPosition(0,250);this.addChild(logo,10,1);varnewGameNormal=cc.Sprite.create(s_menu,cc.rect(0,0,126,33));varnewGameSelected=cc.Sprite.create(s_menu,cc.rect(0,33,126,33));varnewGameDisabled=cc.Sprite.create(s_menu,cc.rect(0,33*2,126,33));vargameSettingsNormal=cc.Sprite.create(s_menu,cc.rect(126,0,126,33));vargameSettingsSelected=cc.Sprite.create(s_menu,cc.rect(126,33,126,33));vargameSettingsDisabled=cc.Sprite.create(s_menu,cc.rect(126,33*2,126,33));varaboutNormal=cc.Sprite.create(s_menu,cc.rect(252,0,126,33));varaboutSelected=cc.Sprite.create(s_menu,cc.rect(252,33,126,33));varaboutDisabled=cc.Sprite.create(s_menu,cc.rect(252,33*2,126,33));varnewGame=cc.MenuItemSprite.create(newGameNormal,newGameSelected,newGameDisabled,function(){this.onButtonEffect();flareEffect(this,this,this.onNewGame);}.bind(this));vargameSettings=cc.MenuItemSprite.create(gameSettingsNormal,gameSettingsSelected,gameSettingsDisabled,this.onSettings,this);varabout=cc.MenuItemSprite.create(aboutNormal,aboutSelected,aboutDisabled,this.onAbout,this);varmenu=cc.Menu.create(newGame,gameSettings,about);menu.alignItemsVerticallyWithPadding(10);this.addChild(menu,1,2);menu.setPosition(winSize.width/2,winSize.height/2-80);this.schedule(this.update,0.1);vartmp=cc.TextureCache.getInstance().addImage(s_ship01);this._ship=cc.Sprite.createWithTexture(tmp,cc.rect(0,45,60,38));this.addChild(this._ship,0,4);varpos=cc.p(Math.random()*winSize.width,0);this._ship.setPosition(pos);this._ship.runAction(cc.MoveBy.create(2,cc.p(Math.random()*winSize.width,pos.y+winSize.height+100)));if(MW.SOUND){cc.AudioEngine.getInstance().setMusicVolume(0.7);cc.AudioEngine.getInstance().playMusic(s_mainMainMusic,true);}bRet=true;}returnbRet;},onNewGame:function(pSender){varscene=cc.Scene.create();scene.addChild(GameLayer.create());scene.addChild(GameControlMenu.create());cc.Director.getInstance().replaceScene(cc.TransitionFade.create(1.2,scene));},onSettings:function(pSender){this.onButtonEffect();varscene=cc.Scene.create();scene.addChild(SettingsLayer.create());cc.Director.getInstance().replaceScene(cc.TransitionFade.create(1.2,scene));},onAbout:function(pSender){this.onButtonEffect();varscene=cc.Scene.create();scene.addChild(AboutLayer.create());cc.Director.getInstance().replaceScene(cc.TransitionFade.create(1.2,scene));},update:function(){if(this._ship.getPosition().y>480){varpos=cc.p(Math.random()*winSize.width,10);this._ship.setPosition(pos);this._ship.runAction(cc.MoveBy.create(parseInt(5*Math.random(),10),cc.p(Math.random()*winSize.width,pos.y+480)));}},onButtonEffect:function(){if(MW.SOUND){vars=cc.AudioEngine.getInstance().playEffect(s_buttonEffect);}}});SysMenu.create=function(){varsg=newSysMenu();if(sg&&sg.init()){returnsg;}returnnull;};SysMenu.scene=function(){varscene=cc.Scene.create();varlayer=SysMenu.create();scene.addChild(layer);returnscene;};

这个extend继承写法由John Resig创造,John Resig是JS领域的大神,而且网上有很多粉丝给他编的段子,非常有趣。

例子中使用父类cc.Layer.extend方法来启动继承,传入一个对象字面量{},这个字面量可以包含对象属性和对象方法,最终由extend来完成接口绑定,返回一个构造函数赋值给SysMenu。

对于类方法(也就是通常意义上的静态方法),使用的是js最传统的方式,直接给构造函数指定属性即可。

这种编写代码的方式非常简单,而且也很优美。更重要的是,这种写法,非常符合C++或java程序员的排版审美。

关于继承的理解。js里面的原型继承和基于类的继承方式截然不同,内部是在维护一个原型链,链上的节点与节点之间是链接关系(注意:不是赋值,也不是拷贝)。可以先看一下《权威指南》那本书是怎么讲的,不过很遗憾,那本书关于原型继承的图解画的不太好……千万不要搞代数式的替换和死记硬背,那样你很难掌握原型链的本质。

另外,强烈推荐三生石上的系列文章《JavaScript继承详解》

JavaScript继承详解

JavaScript继承详解(二)

JavaScript继承详解(三)

JavaScript继承详解(四)

JavaScript继承详解(五)

JavaScript继承详解(六)

有时间的话,我会把三生石上的文章配一些详细的原型链描述图,这样就可以很容易的掌握js的原型链了。