攻读Cocos的继承原理
一、Cocos2d-html5的继承原理借用了jquery作者(John Resig)的代码实现, 这个类的模拟继承技术其实John Resig也是参考了好多相关的javascript继承模拟的技术,如base2和Prototype,并受到启发于2008年3月写下一篇关于继承模拟的博文,到今时今日,它已被cocos2d-html采用了,证明它已经得到承认,并且惊得住考验的,其实我自己在第一家公司的时候,也已经有项目采用过,但当时没能力睇明白,短短25行代码(去除注释),主要运用javascript原型、闭包两种概念写出这么强大的功能,好值得研究拜读,我攻读了一日,整理左自己部分的理解,拿出来分享的。只有攻破其细节,才能更好理解cocos2d-html5!
/* Simple JavaScript Inheritance
* By John Resig
* MIT Licensed.
*/
// Inspired by base2 and Prototype
(function(){
/*
整个继承模拟,有一些约定:
1.所有类的属性和方法都会挂载在原型上,除了最顶级的基类Class
,它的方法extend直接挂在自身。我觉得绑在prototype是合理的原因,属于类的层面的东西,不管
是属性,方法,都应该看做是类的成员变量,类的成员方法,不属于实例的成员变量或者成员方法。
如果挂在自身上面,就算是实例的层面了。
2.模拟与实际的差别,在js中模拟,无法实现重载的,这是js的运行机制决定的,在模拟继承时,
都是采用重写的方式做的,为了最大限度重用,特别对新类的同名方法执行时的上下文this
挂载父类的同名方法指针_super。
3.类的初始化函数定义为init,只要定义了,就会在创建实例的时候先调用init
*/
var initializing = false
, fnTest = /xyz/.test(function(){xyz;}) ? /\b_super\b/ : /.*/;
// 基类的实现 (里面不做任何事)
this.Class = function(){};
// 创建一个继承当前类的扩展类
Class.extend = function(prop) {
/*
this.prototype仅仅包含一个constructor指针(系统会隐藏起来)
,并指向window.Class这个匿名函数(构造函数)
*/
var _super = this.prototype;
initializing = true;
/*
new this() 相当于 new Object(),它们的prototype都为undefined
区别仅限于它们的constructor指向的对象不同(它们的构造函数不同)
new this().constructor指向this,此时上下文的this就是指window.Class构造函数
new Object().constructor指向Object构造函数
补充:new this().constructor永远指向window.Class
*/
var prototype = new this();
initializing = false;
// 将扩展对象属性迁移到prototype
for (var name in prop) {
// 如果当前属性为方法类型,检查该方法是否已经存在于父类
prototype[name] = typeof prop[name] == "function"
&& typeof _super[name] == "function"
&& fnTest.test(prop[name]) ?
// 重写父类的方法
(function(name, fn){
return function() {
/*
this._super为undefined
完全可以写成
this._super = _super[name];
var ret = fn.apply(this, arguments);
this._super = undefined;
*/
var tmp = this._super; // 代码1
/*
_super 父类的pototype
this._super 对应父类中的同名方法
*/
this._super = _super[name]; // 代码2
/*
_super 父类的pototype
this._super 对应父类中的同名方法,让新类
的同名方法的this._super指向父类同名方法
*/
var ret = fn.apply(this, arguments);
/*
this._super任务已完成,进行释放
注意:this貌似此时也没用了 ,想 this = undefined;
这也是错的,this作为上下文载体,这个东西由系统自己负责回收的,当运行完,this
相当于局部变量一样被回收的,也不允许给this赋值的操作。但是可以通过this来操作其指向的对象。
不指定一个undefined给this._super的后果:
新类任何一个方法调用,代码1处this._super必定都会指向最后一个同名方法,
如:
var Person = Class.extend({
init: function(isDancing){
this.dancing = isDancing;
},
dance: function(){
return this.dancing;
}
});
var Ninja = Person.extend({
init: function(){
// 调进去,会进入代码1,而那时this._super指向Person.dance
// 由于代码2每次都会覆盖一次this._super,所以问题不大的,只是新类会多一个
// _super的非空属性,造成资源浪费,鉴于类似cocos2d-html5引擎,类这么多
// 造成浪费就明显大了。
this._super( false );
},
// dance 最后一个同名方法
dance: function(){
return this._super();
},
swingSword: function(){
return true;
}
});
*/
this._super = tmp;
return ret;
};
})(name, prop[name]) :
// 成员变量(新或旧)或者新添加的方法
prop[name];
}
/*
虚拟的构造器
虚拟在什么地方:
1.Class作为一个构造函数并返回暴漏出去,让我们利用其构建扩展类,但它自身prototype将替换成
我们之前精心打磨的prototype(之前的变量),我们之前打磨的才是真正需要的原型,这个就是说
它虚的地方。
为什么要虚拟:
1.只是为了让每个含有init方法的类在创建之时都去执行一次,这是东西相当于模拟了一种机制出来了
,不需要我们自己去主动调一下了。
*/
function Class() {
if ( !initializing && this.init )
/*
this指向的是实例,init的上下文环境必定是实例本身,所以我觉得没必要
this.init.apply(this, arguments);去绑定一下作用域。这个绑定是违反逻辑的
要不就绑一个非this的作为init方法的上下文。
*/
this.init.apply(this,arguments); // this.init(arguments);这样写就ok了!
}
// 我们之前精心打磨的prototype作为Class的新原型
Class.prototype = prototype;
/*
将Class的constructor转为我们定义的
如:var Person = Class.extend({init:function(){}});
那么Person.consructor要应该指向Person才对。否则的话,所有类的prototype.constructor
都指向window.Class,包括顶级基类window.Class
补充:js任何对象都有constructor,但是唯独function的constructor可以改写,其他类型,如数字,字符串
之类的constructor是神圣不可侵犯的,系统不允许修改。这个机制也让继承的模拟创造了机会。
*/
Class.prototype.constructor = Class;
/*
以下一句就是实现类的扩展,把Class看做是子类的话,Class.extend又可以派生出一个孙类出来了
而arguments.callee永远指向的都是 window.Class.extend,每一个派生类都是通过window.Class.extend
来打造的。
*/
Class.extend = arguments.callee;
// 将处理后构造器返回
return Class;
};
})();
本文来源 我爱IT技术网 http://www.52ij.com/jishu/4294.html 转载请保留链接。
- 评论列表(网友评论仅供网友表达个人看法,并不表明本站同意其观点或证实其描述)
-
