最初javascript没有class的概念,我们使用的类是以function模拟,继承的实现手段一般依靠原型链,继承的使用也是评价一个jser的重要指标。每个函数都会包含一个原型对象prototype。原型对象prototype包含一个指向构造函数的指针constructor。实例对象包含一个内部属性__proto__指针指向原型对象prototype。
这是他们之间的三角关系:
(function () {
var Person = function (name) {
this.name = name;
};
//Person.prototype = {};//这句将影响十分具有constructor属性
Person.prototype.getName = function () {
return this.name;
};
var Student = function (name, sex, id) {
this.name = name || '无名氏';
this.sex = sex || '不明';
this.id = id || '未填'; //学号
};
//相当于将其prototype复制了一次,若是包含constructor的话将指向Person
Student.prototype = new Person();
Student.prototype.getId = function () {
return this.id;
}
var y = new Person();
var s = new Student;
var s1 = y instanceof Person;
var s2 = s instanceof Student;
var s3 = s instanceof Person;
var s4 = Student.prototype.constructor === Person;
var s5 = Student.constructor === Person;
var s6 = Student.constructor === Function;
var s = '';
})();
一般形式的继承方式如上,偶尔我们会这样干:
Student.prototype = {}
但是这样会导致prototype对象的constructor对象丢失,所以需要找回来,另外一个问题是,这里继承需要执行父类的构造方法,这样是有问题的
比如,父类的构造函数中有一些事件绑定什么的与子类无关,便会导致该类继承无用,所以很多时候我们需要自己实现继承,比较优雅的是prototype的做法,我这里对其进行了一定改造。
var arr = [];
var slice = arr.slice;
function create() {
if (arguments.length == 0 || arguments.length > 2) throw '参数错误';
var parent = null;
//将参数转换为数组
var properties = slice.call(arguments);
//如果第一个参数为类(function),那么就将之取出
if (typeof properties[0] === 'function')
parent = properties.shift();
properties = properties[0];
function klass() {
this.initialize.apply(this, arguments);
}
klass.superclass = parent;
klass.subclasses = [];
if (parent) {
var subclass = function () { };
subclass.prototype = parent.prototype;
klass.prototype = new subclass;
parent.subclasses.push(klass);
}
var ancestor = klass.superclass && klass.superclass.prototype;
for (var k in properties) {
var value = properties[k];
//满足条件就重写
if (ancestor && typeof value == 'function') {
var argslist = /^\s*function\s*\(([^\(\)]*?)\)\s*?\{/i.exec(value.toString())[1].replace(/\s/i, '').split(',');
//只有在第一个参数为$super情况下才需要处理(是否具有重复方法需要用户自己决定)
if (argslist[0] === '$super' && ancestor[k]) {
value = (function (methodName, fn) {
return function () {
var scope = this;
var args = [function () {
return ancestor[methodName].apply(scope, arguments);
} ];
return fn.apply(this, args.concat(slice.call(arguments)));
};
})(k, value);
}
}
klass.prototype[k] = value;
}
if (!klass.prototype.initialize)
klass.prototype.initialize = function () { };
klass.prototype.constructor = klass;
return klass;
}
首先,继承时使用一个空构造函数实现,这样不会执行原构造函数的实例方法,再规范化必须实现initialize方法,保留构造函数的入口,这类实现比较优雅,建议各位试试。