第一次嘗試寫技術文章,文筆比較渣。內容不是不少,但仍是花費了很多時間。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: {} // 實例屬性和方法
});
複製代碼