第一次尝试写技术文章,文笔比较渣。内容不是不少,但仍是花费了很多时间。javascript
先前去某互联网公司面试,面试官问能不能说一下怎样用ES5模拟实现面向对象的三大特征:封装、继承、多态,记得当时只是balabala的说了一下js继承的6种方式。java
最近有时间,因而就抽空大概封装整理了一下...面试
关于js的继承,网上有不少大佬写的文章,这里就再也不赘述了。数据结构
下面会用到寄生组合式继承
的方式来封装咱们的Class方法,首先我认为有两点是须要考虑的:app
一、命名空间namespace的问题函数
就是我声明的这个类须要挂载到哪一个父级下面,就像java里的输入流类InputStream
、输出流类OutputStream
都是位于java.io.*
包(package)下的,日期类Date
、数据结构类HashMap
/HashSet
都是位于java.util.*
包(package)下的ui
二、实现相似ES6中在子类构造函数里面调用父类的方法(super)this
二话很少说,直接上代码spa
/** * 挂载Class到指定的命名空间下 * @param { String } namespace 以点链接的字符串 * @param { Function } Ctor 被挂载的Class * @param { Object } rootNamespace 顶级命名空间 */
let Namespace = function(namespace, Ctor, rootNamespace) {
let nsName, nsNames = namespace.split("."),
max = nsNames.length - 1,
index = 0;
if (!rootNamespace) try {
if (!new RegExp("^[a-zA-Z_$][a-zA-Z0-9_$]*$").test(nsNames[0]))
throw "";
rootNamespace = new Function("return " + nsNames[0])(), index = 1
} catch (i) {
rootNamespace = window
}
for (; max > index; index++)
nsName = nsNames[index], rootNamespace[nsName] || (rootNamespace[nsName] = {}), rootNamespace = rootNamespace[nsName];
rootNamespace[nsNames[max]] || (rootNamespace[nsNames[max]] = Ctor)
};
复制代码
let SuperClass = function() {},
_proto_ = new Object;
_proto_.superclass = Object, _proto_.__NAME__ = "Object", _proto_.superinstance = new Object;
_proto_.callsuper = function(a) {
let self = this;
// 传入a为父类的方法名
if (this._realsuper = this._realsuper ? this._realsuper.prototype.superclass : this.superclass, "string" == typeof a) {
let args = Array.prototype.slice.call(arguments, 1);
self._realsuper.prototype[a].apply(self, args)
} else {
let args = Array.prototype.slice.call(arguments, 0);
self._realsuper.apply(self, args)
}
this._realsuper = null // 调用后置空,便于下次再次调用
}, SuperClass.prototype = _proto_;
复制代码
let Class = function(className, oArgs) {
let ns = oArgs.ns && oArgs.ns + "." + className;
if (ns) try {
let clazz = new Function("return " + ns)();
if (clazz) return clazz // 存在则直接返回
} catch (g) {}
let superClass = oArgs.extend || SuperClass,
fn = function() {},
plugins = oArgs.plugins || [];
fn.prototype = superClass.prototype;
let construct = oArgs.construct || function() {},
properties = oArgs.properties || {},
methods = oArgs.methods || {},
statics = oArgs.statics || {},
_proto_ = new fn;
for (let property in _proto_) _proto_.hasOwnProperty(property) && delete _proto_[property];
for (let prop in properties) _proto_[prop] = properties[prop];
for (let methodName in methods) _proto_[methodName] = methods[methodName];
for (let index = 0; index < plugins.length; index++) {
let plugin = plugins[index];
for (let p in plugin) _proto_[p] = plugin[p]
}
_proto_.constructor = construct, _proto_.superclass = superClass, _proto_.superinstance = new fn, _proto_.__NAME__ = className, construct.prototype = _proto_;
for (let p in statics) construct[p] = statics[p];
return ns && Namespace(ns, construct), construct
};
复制代码
至此,封装完成prototype
let myClass = Class("Test", {
ns: "", // 命名空间
extend: SuperClass, // 父类、可选
plugins: [], // 插件类
construct: function(e) { // 构造函数,能够在这里执行callsuper
...
this.callsuper(e)
},
statics: {}, // 静态属性和方法
methods: {} // 实例属性和方法
});
复制代码