最近想开发一个web在线的流程设计器,在网上找了很多js绘图框架,后来发现draw2d.js可以满足我的需求。
在使用draw2d的过程中,发现draw2d的Canvas等核心对象都是通过Class.extend()得来的,于是对Class.js(其实就是John Resig的Simple JavaScript Inheritance)进行了详细的分析,demo编写理解。
下面将代码与我的理解(以注释的形式)贴出来,供参考。
//加载后自执行完,向window注册了一个Class函数,这个函数上定义了一个extend函数
(function () {
// /xyz/.test(function () { xyz; })的结果一定是true,为什么不直接写成var fnTest = /\b_super\b/;
// 猜测是作者要通过这种写法告诉读者:/\b_super\b/在后面的代码中也是要这么用,去检测函数中是否有_super
var fnTest = /xyz/.test(function () {
xyz;
}) ? /\b_super\b/ : /.*/;//返回RegExp正则对象/\b_super\b/
this.Class = function () {
}; //相当于window.Class=function(){};
//Class是一个函数,也是一个对象,可以在上面定义函数属性extend
//这个extend不是原型链上的,是直接定义在构造函数上的,有点像java中的静态方法的意思
Class.extend = function (prop) {
var _super = this.prototype; //this指向extend方法的调用者(_super是父类构造函数的prototype,比如Person.prototype)
console.log("1 " + (this == window.Class) + " , " + (this == Person));
//控制init的执行时机
initializing = true;
//创建一个父类对象,作为原型,此时不执行init方法
var prototype = new this(); //第一层子类(直接通过Class.extend()方式创建的类)执行时相当于new window.Class();
console.log("2 " + (prototype.__proto__ === _super));//true,对象的原型指向构造函数的prototype属性
initializing = false;
//向prototype中copy传入对象的属性
for (var name in prop) {
prototype[name] =
typeof prop[name] == "function" && //传入对象的属性是函数
typeof _super[name] == "function" && //父类prototype上有同名函数
fnTest.test(prop[name]) //传入对象的属性是函数,并且函数名或函数体包含_super,说明这个函数要调用父类的函数
?
(function (name, fn) { //闭包的结构,避免始终返回循环的最后一个
console.log("name=" + name);//调用extend时执行
return function () { //调用对象的具体方法时执行
var tmp = this._super; //this指向正在创建的对象
console.log("3 " + (this == s));//false,因为此时的this还没有返回赋值给s
console.log("s=" + s);//undefined
that = this;
console.log("tmp=" + tmp);
this._super = _super[name]; //子类对象上绑定父类的同名函数,命名为_super
var ret = fn.apply(this, arguments);//调用子类对象的函数,在调用过程中可能会调用父类的同名函数,所以需要在上一步中先进行绑定
this._super = tmp; //执行完后还原,一般tmp为undefined
return ret;
};
})(name, prop[name]) : //函数包含_super的情况,特殊处理为了让子类函数中能调用父类的同名函数
prop[name]; //其它的情况,直接copy
}
//傀儡构造函数,实际名称是var指定的,比如Person
function Class() {
//调用extend函数创建类时不会调用init,真正new子类对象时才会执行init
if (!initializing && this.init)
this.init.apply(this, arguments);
}
//指定原型对象
Class.prototype = prototype;
//指定原型对象的构造器
Class.prototype.constructor = Class;
//把extend函数沿着继承层次传递下去,比如Person.extend, Student.extend
Class.extend = arguments.callee;
return Class;
};
})();
附上一个测试的代码demo
var Person = Class.extend({
init: function () {
console.log("Person init");
},
eat: function () {
console.log("Person eat");
},
data1: "data1"
});
console.log("=============================");
var Student = Person.extend({
init: function () {
console.log("Student init");
this._super();
},
eat: function () {
this._super();
},
study: function () {
console.log("Student study");
},
data2: "data2"
});
console.log("=============================");
var that;
var s = new Student();
console.log(that == s);//true
demo的输出结果如下: