類的使用和面向對象
大家都知道在cocos2d-x 底層是C++編寫的,那么就有類的概念和繼承機制。
但是在JS中,是沒有類這個概念的,沒有提供類,沒有C++的類繼承機制。
那么JS是通過什么方式實現簡單的繼承呢?JS是通過對象的原型實現繼承。
我們來看一下這段代碼:
var baselayer = cc.Layer.extend({ ctor:function(){ this._super(); cc.log("baselayer ctor read"); }, init:function(){ this._super(); cc.log("baselayer init read"); } });
我們申明了baseLayer對象 ,並且利用cc.Layer.extend,繼承了CClayer。
那么問題來了?他究竟是怎么實現的呢?我們按住crtl跟進去看看cc.Layer.extend的實現;

ClassManager.compileSuper.ClassManager = ClassManager; /* Managed JavaScript Inheritance * Based on John Resig's Simple JavaScript Inheritance http://ejohn.org/blog/simple-javascript-inheritance/ * MIT Licensed. * 在這里申明了,實現JS繼承的方式 是參考了 John Resig's 的一個例子來實現的;並且有原文地址,有興趣的同學可以去看看原版實現方式 */ (function () { var fnTest = /\b_super\b/; var config = cc.game.config; var releaseMode = config[cc.game.CONFIG_KEY.classReleaseMode]; if(releaseMode) { console.log("release Mode"); } /** * The base Class implementation (does nothing) * @class */ cc.Class = function () { }; /** * Create a new Class that inherits from this Class * @static * @param {object} props * @return {function} */ cc.Class.extend = function (props) { //聲明_super對象,並賦值為原型 var _super = this.prototype; // Instantiate a base Class (but only create the instance, // don't run the init constructor) //實例化創建prototype這個基類,只是創建實例,並沒有跑init構造函數 var prototype = Object.create(_super); //給這個class復制ID標識,並且將_super對象添加到ClassManager類管理器中 var classId = ClassManager.getNewID(); ClassManager[classId] = _super; // Copy the properties over onto the new prototype. We make function // properties non-eumerable as this makes typeof === 'function' check // unneccessary in the for...in loop used 1) for generating Class() // 2) for cc.clone and perhaps more. It is also required to make // these function properties cacheable in Carakan. //進行函數的驗證檢測,以及設置他使用基本設置 var desc = { writable: true, enumerable: false, configurable: true }; //單例模式的基礎申明 prototype.__instanceId = null; // The dummy Class constructor //創建Class這個類 function Class() { this.__instanceId = ClassManager.getNewInstanceId(); // All construction is actually done in the init method //如果這個類他存在.ctor方法,那么就默認的使用執行這個方法 //ctor在JS中就相當於構造函數 if (this.ctor) this.ctor.apply(this, arguments); } //給ID復制 Class.id = classId; // desc = { writable: true, enumerable: false, configurable: true, // value: XXX }; Again, we make this non-enumerable. desc.value = classId; Object.defineProperty(prototype, '__pid', desc); // Populate our constructed prototype object //把我們原型對象賦值 Class.prototype = prototype; // Enforce the constructor to be what we expect //將整個類賦值給desc.value desc.value = Class; //並且將類里構造的對象賦值 Object.defineProperty(Class.prototype, 'constructor', desc); // Copy getter/setter //模擬get/set的方式,使用cc.clone函數來拷貝 this.__getters__ && (Class.__getters__ = cc.clone(this.__getters__)); this.__setters__ && (Class.__setters__ = cc.clone(this.__setters__)); for(var idx = 0, li = arguments.length; idx < li; ++idx) { var prop = arguments[idx]; for (var name in prop) { var isFunc = (typeof prop[name] === "function"); var override = (typeof _super[name] === "function"); var hasSuperCall = fnTest.test(prop[name]); if (releaseMode && isFunc && override && hasSuperCall) { desc.value = ClassManager.compileSuper(prop[name], name, classId); Object.defineProperty(prototype, name, desc); } else if (isFunc && override && hasSuperCall) { desc.value = (function (name, fn) { return function () { var tmp = this._super; // Add a new ._super() method that is the same method // but on the super-Class //如果在新的對象方法里面添加._super(),他會繼承父類的_super方法 //並且實現方法里面的所有對象及方法的賦值 this._super = _super[name]; // The method only need to be bound temporarily, so we // remove it when we're done executing var ret = fn.apply(this, arguments); this._super = tmp; return ret; }; })(name, prop[name]); Object.defineProperty(prototype, name, desc); } else if (isFunc) { desc.value = prop[name]; Object.defineProperty(prototype, name, desc); } else { prototype[name] = prop[name]; } if (isFunc) { // Override registered getter/setter //如果是方法,那么重載里面的屬性,並且實現get,set方法可以直接使用 var getter, setter, propertyName; if (this.__getters__ && this.__getters__[name]) { propertyName = this.__getters__[name]; for (var i in this.__setters__) { if (this.__setters__[i] === propertyName) { setter = i; break; } } cc.defineGetterSetter(prototype, propertyName, prop[name], prop[setter] ? prop[setter] : prototype[setter], name, setter); } if (this.__setters__ && this.__setters__[name]) { propertyName = this.__setters__[name]; for (var i in this.__getters__) { if (this.__getters__[i] === propertyName) { getter = i; break; } } cc.defineGetterSetter(prototype, propertyName, prop[getter] ? prop[getter] : prototype[getter], prop[name], getter, name); } } } } // And make this Class extendable // 可以使用Class.extend來實現類的繼承 Class.extend = cc.Class.extend; //add implementation method //添加要實現的方法 Class.implement = function (prop) { for (var name in prop) { prototype[name] = prop[name]; } }; return Class; }; })();
重點看3個點:
// The dummy Class constructor //創建Class這個類 function Class() { this.__instanceId = ClassManager.getNewInstanceId(); // All construction is actually done in the init method //如果這個類他存在.ctor方法,那么就默認的使用執行這個方法 //ctor在JS中就相當於構造函數 if (this.ctor) this.ctor.apply(this, arguments); }
第一,這個是在JS中的實現構造函數的方法,如果在自定義類中,存在有ctor:function()這個方法,那么他會
默認執行,默認成為構造函數;
desc.value = (function (name, fn) { return function () { var tmp = this._super; // Add a new ._super() method that is the same method // but on the super-Class //如果在新的對象方法里面添加._super(),他會繼承父類的_super方法 //並且實現方法里面的所有對象及方法的賦值 this._super = _super[name]; // The method only need to be bound temporarily, so we // remove it when we're done executing var ret = fn.apply(this, arguments); this._super = tmp; return ret; }; })(name, prop[name]);
第二,desc.value在這個for循環中的賦值,實現了this._super()的原理,它會為派生類實完成對父類的實現;
通俗點來說,就是,如果我們想要繼承並實現父類的方法,那么就需要在方法里面調用this._super()這個方法!
// And make this Class extendable // 可以使用Class.extend來實現類的繼承 Class.extend = cc.Class.extend;
第三,講cc.Class.extend賦值給Class.extend,就可以使用Class.extend來實現自定義類的繼承;
OK,梳理完畢下面看看我們來學習一下怎么實現自定義類和自定義的繼承:
var myLayer = baselayer.extend({ ctor:function(){ this._super(); cc.log("myLayer ctor read"); }, init:function(){ this._super(); cc.log("myLayer init read"); } });
這段代碼中我從myLayer繼承了父類baselayer,注意用法就是剛才我們Review底層代碼時看到的
var myLayer = baselayer.extend({});
然后繼續續實現ctor構造方法函數,和自定義方法函數init;
並且日志輸出一下;
最終我們需要在MainScene中調用;
如下:
var MainScene = cc.Scene.extend({ onEnter:function(){ this._super(); var layer = new myLayer(); this.addChild(layer); } });
我們首先只調用
var layer = new myLayer();
看看最終輸出是什么?
從輸出可以看出他先調用了baselayer,再調用了myLayer;
那么就可以理解為我們直接new myLayer() 會直接自動實現調用我們寫的ctor構造函數方法;
而且是先調用父類,然后再調用我們的派生類自定義類;
他並沒有主動調用init:function()這個方法,因為他是我們自定義的,所以需要我們手動去調用
layer.init();
OK,我們加上手動調用后再來看一下輸出是什么?
我們可以看到了前兩行輸出都是 ctor 先執行;
init 函數后執行;
而且調用也是 先執行baseLayer 我們的父類的init函數 再執行我們的自定義init方法!
現在就非常清晰了,我們的myLayer.init方法繼承了baseLayer.init的屬性方法;
而且實現的原理就是通過this._super()來實現的!
我們再修改一下代碼來看看是不是這樣,將myLayer類里面的init方法里面的this._super();這句話去掉!
看看我們的baseLayer.init方法會不會被調用
var myLayer = baselayer.extend({ ctor:function(){ this._super(); cc.log("myLayer ctor read"); }, init:function(){ cc.log("myLayer init read"); } });
看看輸出: